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;
faileddraw : Boolean;
_inIME: Boolean;
public
callback: ICommonCallback;
auxMouseByParent: Boolean;
@ -234,9 +236,19 @@ type
function stringValue: NSString; 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 doCommandBySelector (aSelector: SEL); override;
procedure setMarkedText_selectedRange_replacementRange (aString: id; selectedRange: NSRange; replacementRange: NSRange);
procedure unmarkText;
function selectedRange: NSRange;
@ -246,6 +258,7 @@ type
function validAttributesForMarkedText: NSArray;
function firstRectForCharacterRange_actualRange (aRange: NSRange; actualRange: NSRangePointer): NSRect;
function characterIndexForPoint (aPoint: NSPoint): NSUInteger;
procedure doCommandBySelector (aSelector: SEL); override;
end;
{ ICocoaIMEControl }
@ -585,6 +598,14 @@ end;
{ 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);
begin
if Assigned(fstr) then fstr.release;
@ -617,25 +638,78 @@ begin
end;
end;
procedure TCocoaCustomControl.insertText_replacementRange(aString: id;
replacementRange: NSRange);
procedure TCocoaCustomControl.keyDown(theEvent: NSEvent);
var
view: NSView;
isFirst: Boolean;
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;
procedure TCocoaCustomControl.doCommandBySelector(aSelector: SEL);
procedure TCocoaCustomControl.insertText_replacementRange(aString: id;
replacementRange: NSRange);
var
textView: NSTextView;
nsText: NSString;
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;
procedure TCocoaCustomControl.setMarkedText_selectedRange_replacementRange(
aString: id; selectedRange: NSRange; replacementRange: NSRange);
var
textView: NSTextView;
nsText: NSString;
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;
procedure TCocoaCustomControl.unmarkText;
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;
function TCocoaCustomControl.selectedRange: NSRange;
@ -648,11 +722,6 @@ begin
Result := NSMakeRange(0,0);
end;
function TCocoaCustomControl.hasMarkedText: LCLObjCBoolean;
begin
Result := false;
end;
function TCocoaCustomControl.attributedSubstringForProposedRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
begin
@ -664,18 +733,17 @@ begin
Result := nil;
end;
function TCocoaCustomControl.firstRectForCharacterRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSRect;
begin
Result := NSMakeRect(0,0,0,0);
end;
function TCocoaCustomControl.characterIndexForPoint(aPoint: NSPoint
): NSUInteger;
begin
Result := 0;
end;
procedure TCocoaCustomControl.doCommandBySelector(aSelector: SEL);
begin
inherited doCommandBySelector(ASelector);
end;
procedure TCocoaCustomControl.dealloc;
begin
if Assigned(fstr) then fstr.release;
@ -916,14 +984,6 @@ begin
Result := not hasMarkedText();
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;
begin
Result:= false;