Browse Source

Handle replacementRange in AvnView (#21608)

* macOS: ensure native selectedRange is up-to-date

* macOS: handle replacementRange in setMarkedText and insertText

* macOS: handle unexpected ranges in attributedSubstringForProposedRange
pull/21624/head
Julien Lebosquain 3 months ago
committed by GitHub
parent
commit
b3014ccf27
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      native/Avalonia.Native/src/OSX/AvnTextInputMethod.h
  2. 8
      native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm
  3. 35
      native/Avalonia.Native/src/OSX/AvnView.mm
  4. 22
      src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs
  5. 5
      src/Avalonia.Native/avn.idl

4
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<IAvnTextInputMethodClient> Client;

8
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];
}

35
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<uint64_t>([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<uint64_t>([NSDate timeIntervalSinceReferenceDate] * 1000);

22
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());

5
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)]

Loading…
Cancel
Save