| 
					
				 | 
			
			
				@@ -1,14 +1,13 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using System; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using System.Runtime.InteropServices; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-using System.Runtime.Versioning; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using Avalonia.Controls.Presenters; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-using Foundation; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-using ObjCRuntime; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-using Avalonia.Input.TextInput; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using Avalonia.Input; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using Avalonia.Input.Raw; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using Avalonia.Input.TextInput; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using Avalonia.Logging; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using CoreGraphics; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using Foundation; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+using ObjCRuntime; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 using UIKit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // ReSharper disable InconsistentNaming 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // ReSharper disable StringLiteralTypo 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -25,6 +24,9 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     [Adopts("UIKeyInput")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     partial class TextInputResponder : UIResponder, IUITextInput 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private static AvaloniaEmptyTextPosition? _emptyPosition; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private static AvaloniaEmptyTextPosition EmptyPosition => _emptyPosition ??= new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private class AvaloniaTextRange : UITextRange, INSCopying 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             private UITextPosition? _start; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -67,6 +69,15 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             public NSObject Copy(NSZone? zone) => new AvaloniaTextPosition(Index); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private class AvaloniaEmptyTextPosition : UITextPosition, INSCopying 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            public AvaloniaEmptyTextPosition() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            public NSObject Copy(NSZone? zone) => this; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         public TextInputResponder(AvaloniaView view, ITextInputMethodClient client) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             _view = view; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -93,7 +104,25 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         public override NSString TextInputContextIdentifier => new NSString(Guid.NewGuid().ToString()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public override UITextInputMode TextInputMode => UITextInputMode.CurrentInputMode; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public override UITextInputMode TextInputMode 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                var mode = UITextInputMode.CurrentInputMode; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // Can be empty see https://developer.apple.com/documentation/uikit/uitextinputmode/1614522-activeinputmodes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (mode is null && UITextInputMode.ActiveInputModes.Length > 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    mode = UITextInputMode.ActiveInputModes[0]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // See: https://stackoverflow.com/a/33337483/20894223 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (mode is null) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    using var tv = new UITextView(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    mode = tv.TextInputMode; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return mode; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         [DllImport("/usr/lib/libobjc.dylib")] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private static extern void objc_msgSend(IntPtr receiver, IntPtr selector, IntPtr arg); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -105,8 +134,8 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private readonly AvaloniaView _view; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private string? _markedText; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private void SurroundingTextChanged(object? sender, EventArgs e) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "SurroundingTextChanged"); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -153,9 +182,9 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 switch (ReturnKeyType) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     case UIReturnKeyType.Done: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        case UIReturnKeyType.Go: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        case UIReturnKeyType.Send: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        case UIReturnKeyType.Search: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    case UIReturnKeyType.Go: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    case UIReturnKeyType.Send: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    case UIReturnKeyType.Search: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         ResignFirstResponder(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -164,7 +193,7 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             TextInput(text); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         void IUIKeyInput.DeleteBackward() => KeyPress(Key.Back); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         bool IUIKeyInput.HasText => true; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -176,8 +205,8 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "IUIKeyInput.TextInRange {start} {end}", r.StartIndex, r.EndIndex); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             string result = ""; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if(string.IsNullOrEmpty(_markedText)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                result = s.Text[r.StartIndex .. r.EndIndex]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (string.IsNullOrEmpty(_markedText)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                result = s.Text[r.StartIndex..r.EndIndex]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var span = new CombinedSpan3<char>(s.Text.AsSpan().Slice(0, s.CursorOffset), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -214,7 +243,7 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         void IUITextInput.UnmarkText() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Logger.TryGet(LogEventLevel.Debug, ImeLog)?.Log(null, "IUIKeyInput.UnmarkText"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if(_markedText == null) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (_markedText == null) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var commitString = _markedText; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             _markedText = null; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -239,15 +268,15 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Logger.TryGet(LogEventLevel.Debug, ImeLog) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 ?.Log(null, "IUIKeyInput.GetPosition {start} {offset}", pos.Index, (int)offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             var res = GetPositionCore(pos, offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             Logger.TryGet(LogEventLevel.Debug, ImeLog) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 ?.Log(null, $"res: " + (res == null ? "null" : (int)res.Index)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             return res!; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var res = GetPositionCore(pos, offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Logger.TryGet(LogEventLevel.Debug, ImeLog) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ?.Log(null, $"res: " + (res == null ? "null" : (int)res.Index)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return res!; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private AvaloniaTextPosition? GetPositionCore(AvaloniaTextPosition pos, nint offset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var end = pos.Index + (int)offset; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (end < 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return null!; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -261,14 +290,14 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var pos = (AvaloniaTextPosition)fromPosition; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Logger.TryGet(LogEventLevel.Debug, ImeLog) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                ?.Log(null, "IUIKeyInput.GetPosition {start} {direction} {offset}", pos.Index,  inDirection, (int)offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ?.Log(null, "IUIKeyInput.GetPosition {start} {direction} {offset}", pos.Index, inDirection, (int)offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var res = GetPositionCore(pos, inDirection, offset); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Logger.TryGet(LogEventLevel.Debug, ImeLog) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 ?.Log(null, $"res: " + (res == null ? "null" : (int)res.Index)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return res!; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-         
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private AvaloniaTextPosition? GetPositionCore(AvaloniaTextPosition fromPosition, UITextLayoutDirection inDirection, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             nint offset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -348,7 +377,7 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         CGRect IUITextInput.GetFirstRectForRange(UITextRange range) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-             
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             Logger.TryGet(LogEventLevel.Debug, ImeLog)? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 .Log(null, "IUITextInput:GetFirstRectForRange"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             // TODO: Query from the input client 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -377,11 +406,11 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (presenter is { }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 var hitResult = presenter.TextLayout.HitTestPoint(new Point(point.X, point.Y)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 return new AvaloniaTextPosition(hitResult.TextPosition); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return EmptyPosition; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         UITextPosition IUITextInput.GetClosestPositionToPoint(CGPoint point, UITextRange withinRange) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -440,7 +469,7 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         NSDictionary? IUITextInput.MarkedTextStyle 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             get => null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            set {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            set { } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         UITextPosition IUITextInput.BeginningOfDocument => _beginningOfDocument; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -478,7 +507,7 @@ partial class AvaloniaView 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var res = base.ResignFirstResponder(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if (res && ReferenceEquals(CurrentAvaloniaResponder, this)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 Logger.TryGet(LogEventLevel.Debug, "IOSIME") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     ?.Log(null, "Resigned first responder"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 _client.SurroundingTextChanged -= SurroundingTextChanged; 
			 |