diff --git a/native/Avalonia.Native/src/OSX/AvnTextInputMethod.h b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.h index 4e5116ee71..dd10ae28b4 100644 --- a/native/Avalonia.Native/src/OSX/AvnTextInputMethod.h +++ b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.h @@ -38,7 +38,9 @@ public: virtual void SetCursorRect (AvnRect rect) override; - virtual void SetSurroundingText (char* text, int anchorOffset, int cursorOffset) override; + virtual void SetSurroundingText (char* text, int start, int end) override; + + virtual void SetSelectionInSurroundingText (int start, int end) override; public: ComPtr Client; diff --git a/native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm index 8c3ae080fa..d99fef7833 100644 --- a/native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm +++ b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm @@ -31,11 +31,15 @@ HRESULT AvnTextInputMethod::SetClient(IAvnTextInputMethodClient *client) { void AvnTextInputMethod::Reset() { } -void AvnTextInputMethod::SetSurroundingText(char* text, int anchorOffset, int cursorOffset) { +void AvnTextInputMethod::SetSurroundingText(char* text, int start, int end) { [_inputMethodDelegate setText:[NSString stringWithUTF8String:text]]; - [_inputMethodDelegate setSelection: anchorOffset : cursorOffset]; + [_inputMethodDelegate setSelection: start:end]; } void AvnTextInputMethod::SetCursorRect(AvnRect rect) { [_inputMethodDelegate setCursorRect: rect]; } + +void AvnTextInputMethod::SetSelectionInSurroundingText(int start, int end) { + [_inputMethodDelegate setSelection: start:end]; +} diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 600dca865f..35c636819e 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -775,10 +775,21 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt) markedText = (NSString*) string; } - _markedRange = NSMakeRange(_selectedRange.location, [markedText length]); auto parent = _parent.tryGet(); - if(parent->InputMethod->IsActive()){ + // Delete any replaced range + if (replacementRange.location != NSNotFound && parent != nullptr && parent->InputMethod->IsActive()) + { + parent->InputMethod->Client->SelectInSurroundingText((int)replacementRange.location, (int)(replacementRange.location + replacementRange.length)); + uint64_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000); + parent->TopLevelEvents->RawKeyEvent(KeyDown, timestamp, AvnInputModifiersNone, AvnKeyBack, AvnPhysicalKeyNone, "\b"); + parent->TopLevelEvents->RawKeyEvent(KeyUp, timestamp, AvnInputModifiersNone, AvnKeyBack, AvnPhysicalKeyNone, "\b"); + } + + _markedRange = NSMakeRange(_selectedRange.location, [markedText length]); + + if (parent != nullptr && parent->InputMethod->IsActive()) + { parent->InputMethod->Client->SetPreeditText((char*)[markedText UTF8String]); } } @@ -807,9 +818,16 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt) if(actualRange){ range = *actualRange; } + + // From the docs: an implementation of this method should be prepared for aRange to be out of bounds. + // In this case, you should return the intersection of the document's range and aRange. + // If the location of aRange is completely outside of the document's range, return nil. + auto finalRange = NSIntersectionRange(range, NSMakeRange(0, _text.length)); - NSAttributedString* subString = [_text attributedSubstringFromRange:range]; + if (finalRange.length == 0) + return nil; + NSAttributedString* subString = [_text attributedSubstringFromRange:finalRange]; return subString; } @@ -819,9 +837,9 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt) if(parent == nullptr){ return; } - + NSString* text; - + if([string isKindOfClass:[NSAttributedString class]]) { text = [string string]; @@ -831,6 +849,13 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt) text = (NSString*) string; } + if (replacementRange.location != NSNotFound && + ![self hasMarkedText] && + parent->InputMethod->IsActive()) + { + parent->InputMethod->Client->SelectInSurroundingText((int)replacementRange.location, (int)(replacementRange.location + replacementRange.length)); + } + [self unmarkText]; uint64_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000); diff --git a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs index 3df0a0ef4a..5889313ceb 100644 --- a/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs +++ b/src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs @@ -32,22 +32,25 @@ namespace Avalonia.Native { _client.SurroundingTextChanged -= OnSurroundingTextChanged; _client.CursorRectangleChanged -= OnCursorRectangleChanged; - + _client.SelectionChanged -= OnSelectionChanged; + _nativeClient?.Dispose(); } - + _nativeClient = null; _client = client; - + if (_client != null) { _nativeClient = new AvnTextInputMethodClient(_client); OnSurroundingTextChanged(this, EventArgs.Empty); OnCursorRectangleChanged(this, EventArgs.Empty); + // Note: OnSelectionChanged isn't called, it's already up-to-date thanks to OnSurroundingTextChanged _client.SurroundingTextChanged += OnSurroundingTextChanged; _client.CursorRectangleChanged += OnCursorRectangleChanged; + _client.SelectionChanged += OnSelectionChanged; } _inputMethod.SetClient(_nativeClient); @@ -92,7 +95,7 @@ namespace Avalonia.Native { return; } - + var surroundingText = _client.SurroundingText; var selection = _client.Selection; @@ -103,6 +106,17 @@ namespace Avalonia.Native ); } + private void OnSelectionChanged(object? sender, EventArgs e) + { + if (_client is null) + { + return; + } + + var selection = _client.Selection; + _inputMethod.SetSelectionInSurroundingText(selection.Start, selection.End); + } + public void SetCursorRect(Rect rect) { _inputMethod.SetCursorRect(rect.ToAvnRect()); diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index f01430a42a..32d3f4286e 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -872,7 +872,7 @@ interface IAvnWindowEvents : IAvnWindowBaseEvents interface IAvnTextInputMethodClient : IUnknown { void SetPreeditText(char* preeditText); - void SelectInSurroundingText(int start, int length); + void SelectInSurroundingText(int start, int end); } [uuid(1382a29f-e260-4c7a-b83f-c99fc72e27c2)] @@ -881,7 +881,8 @@ interface IAvnTextInputMethod : IUnknown HRESULT SetClient(IAvnTextInputMethodClient* client); void Reset(); void SetCursorRect(AvnRect rect); - void SetSurroundingText(char* text, int anchorOffset, int cursorOffset); + void SetSurroundingText(char* text, int start, int end); + void SetSelectionInSurroundingText(int start, int end); } [uuid(e34ae0f8-18b4-48a3-b09d-2e6b19a3cf5e)]