Cocoa: IME: implements a base NSTextInputClient for non-editable LCL CustomControl

implements a base NSTextInputClient for non-editable LCL CustomControl,
like Form, Grid, ListView, that are not system control and not FullEditControl.
1. when using IME in these controls, a temporary and one-time editor is shown at the bottom of the control, supporting IME such as Chinese.
2. refers to MacOS Finder, when using IME in the file list view, a small window will pop up at the bottom of the screen for input. the text can then be used for filename starting character match.
3. it is useful for implementing IME support for controls that do not have a text input window.
This commit is contained in:
rich2014 2023-07-04 21:36:41 +08:00
parent 873c352755
commit 7031b6b752

View File

@ -203,6 +203,8 @@ type
isdrawing : integer; isdrawing : integer;
faileddraw : Boolean; faileddraw : Boolean;
_inIME: Boolean;
public public
callback: ICommonCallback; callback: ICommonCallback;
auxMouseByParent: Boolean; auxMouseByParent: Boolean;
@ -234,9 +236,19 @@ type
function stringValue: NSString; override; function stringValue: NSString; override;
procedure addSubView(aview: NSView); override; procedure addSubView(aview: NSView); override;
// this is parts of public
// NSTextInputClientProtocol related.
// implements a base NSTextInputClient for non-editable LCL CustomControl,
// like Form, Grid, ListView, that are not system control and not FullEditControl.
// 1. when using IME in these controls, a temporary and one-time editor is shown
// at the bottom of the control, supporting IME such as Chinese.
// 2. refers to MacOS Finder, when using IME in the file list view,
// a small window will pop up at the bottom of the screen for input.
// the text can then be used for filename starting character match.
// 3. it is useful for implementing IME support for controls that do not
// have a text input window.
procedure keyDown(theEvent: NSEvent); override;
procedure insertText_replacementRange (aString: id; replacementRange: NSRange); procedure insertText_replacementRange (aString: id; replacementRange: NSRange);
procedure doCommandBySelector (aSelector: SEL); override;
procedure setMarkedText_selectedRange_replacementRange (aString: id; selectedRange: NSRange; replacementRange: NSRange); procedure setMarkedText_selectedRange_replacementRange (aString: id; selectedRange: NSRange; replacementRange: NSRange);
procedure unmarkText; procedure unmarkText;
function selectedRange: NSRange; function selectedRange: NSRange;
@ -246,6 +258,7 @@ type
function validAttributesForMarkedText: NSArray; function validAttributesForMarkedText: NSArray;
function firstRectForCharacterRange_actualRange (aRange: NSRange; actualRange: NSRangePointer): NSRect; function firstRectForCharacterRange_actualRange (aRange: NSRange; actualRange: NSRangePointer): NSRect;
function characterIndexForPoint (aPoint: NSPoint): NSUInteger; function characterIndexForPoint (aPoint: NSPoint): NSUInteger;
procedure doCommandBySelector (aSelector: SEL); override;
end; end;
{ ICocoaIMEControl } { ICocoaIMEControl }
@ -585,6 +598,14 @@ end;
{ TCocoaCustomControl } { TCocoaCustomControl }
function getNSStringObject( const aString: id ) : NSString;
begin
if aString.isKindOfClass( NSAttributedString.classClass ) then
Result:= NSAttributedString( aString ).string_
else
Result:= NSString( aString );
end;
procedure TCocoaCustomControl.setStringValue(avalue: NSString); procedure TCocoaCustomControl.setStringValue(avalue: NSString);
begin begin
if Assigned(fstr) then fstr.release; if Assigned(fstr) then fstr.release;
@ -617,25 +638,78 @@ begin
end; end;
end; end;
procedure TCocoaCustomControl.insertText_replacementRange(aString: id; procedure TCocoaCustomControl.keyDown(theEvent: NSEvent);
replacementRange: NSRange); var
view: NSView;
isFirst: Boolean;
begin begin
lclGetCallback.InputClientInsertText(NSStringToString(NSString(astring))); isFirst:= not _inIME;
inputContext.handleEvent(theEvent);
if _inIME and isFirst then
begin
view:= self.window.fieldEditor_forObject(true, nil);
view.setFrameSize( NSMakeSize(self.frame.size.width,16) );
self.addSubView( view );
end
else if not _inIME then
inputContext.discardMarkedText;
end; end;
procedure TCocoaCustomControl.doCommandBySelector(aSelector: SEL); procedure TCocoaCustomControl.insertText_replacementRange(aString: id;
replacementRange: NSRange);
var
textView: NSTextView;
nsText: NSString;
begin begin
inherited doCommandBySelector(ASelector); if not _inIME then exit;
textView:= NSTextView(self.window.fieldEditor_forObject(false,nil));
if Assigned(textView) then
textView.removeFromSuperview;
nsText:= getNSStringObject(aString);
lclGetCallback.InputClientInsertText(nsText.UTF8String);
unmarkText;
end; end;
procedure TCocoaCustomControl.setMarkedText_selectedRange_replacementRange( procedure TCocoaCustomControl.setMarkedText_selectedRange_replacementRange(
aString: id; selectedRange: NSRange; replacementRange: NSRange); aString: id; selectedRange: NSRange; replacementRange: NSRange);
var
textView: NSTextView;
nsText: NSString;
begin begin
nsText:= getNSStringObject(aString);
if nsText.length > 0 then
begin
_inIME:= true;
textView:= NSTextView(self.window.fieldEditor_forObject(false,nil));
if Assigned(textView) then
textView.setMarkedText_selectedRange_replacementRange(aString,selectedRange,replacementRange);
end
else
unmarkText;
end;
function TCocoaCustomControl.hasMarkedText: LCLObjCBoolean;
begin
Result := _inIME;
end; end;
procedure TCocoaCustomControl.unmarkText; procedure TCocoaCustomControl.unmarkText;
begin begin
_inIME:= false;
end;
function TCocoaCustomControl.firstRectForCharacterRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSRect;
var
point: NSPoint;
rect: NSRect;
begin
point:= self.convertPoint_toView(NSZeroPoint, nil);
rect:= NSMakeRect(point.x, point.y, 0, 16);
Result:= self.window.convertRectToScreen(rect);
end; end;
function TCocoaCustomControl.selectedRange: NSRange; function TCocoaCustomControl.selectedRange: NSRange;
@ -648,11 +722,6 @@ begin
Result := NSMakeRange(0,0); Result := NSMakeRange(0,0);
end; end;
function TCocoaCustomControl.hasMarkedText: LCLObjCBoolean;
begin
Result := false;
end;
function TCocoaCustomControl.attributedSubstringForProposedRange_actualRange( function TCocoaCustomControl.attributedSubstringForProposedRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSAttributedString; aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
begin begin
@ -664,18 +733,17 @@ begin
Result := nil; Result := nil;
end; end;
function TCocoaCustomControl.firstRectForCharacterRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSRect;
begin
Result := NSMakeRect(0,0,0,0);
end;
function TCocoaCustomControl.characterIndexForPoint(aPoint: NSPoint function TCocoaCustomControl.characterIndexForPoint(aPoint: NSPoint
): NSUInteger; ): NSUInteger;
begin begin
Result := 0; Result := 0;
end; end;
procedure TCocoaCustomControl.doCommandBySelector(aSelector: SEL);
begin
inherited doCommandBySelector(ASelector);
end;
procedure TCocoaCustomControl.dealloc; procedure TCocoaCustomControl.dealloc;
begin begin
if Assigned(fstr) then fstr.release; if Assigned(fstr) then fstr.release;
@ -916,14 +984,6 @@ begin
Result := not hasMarkedText(); Result := not hasMarkedText();
end; end;
function getNSStringObject( const aString: id ) : NSString;
begin
if aString.isKindOfClass( NSAttributedString.classClass ) then
Result:= NSAttributedString( aString ).string_
else
Result:= NSString( aString );
end;
function isIMEDuplicateCall( const newParams, currentParams: TCocoaIMEParameters ) : Boolean; function isIMEDuplicateCall( const newParams, currentParams: TCocoaIMEParameters ) : Boolean;
begin begin
Result:= false; Result:= false;