Cocoa: split BaseInputClient responsibility from TCocoaCustomControl into TCocoaCustomControlWithBaseInputClient

This commit is contained in:
rich2014 2024-08-23 00:33:23 +08:00
parent a0aa06e92c
commit 224719b67b
5 changed files with 189 additions and 161 deletions

View File

@ -15,17 +15,12 @@ uses
type
{ TCocoaCustomControl }
TCocoaCustomControl = objcclass(NSControl, NSTextInputClientProtocol)
TCocoaCustomControl = objcclass(NSControl)
private
fstr : NSString;
isdrawing : integer;
faileddraw : Boolean;
_inIME: Boolean;
private
function getWindowEditor(): NSTextView; message 'getWindowEditor';
procedure DoCallInputClientInsertText(nsText:NSString); message 'DoCallInputClientInsertText:';
public
callback: ICommonCallback;
auxMouseByParent: Boolean;
@ -56,7 +51,17 @@ type
procedure setStringValue(avalue: NSString); override;
function stringValue: NSString; override;
procedure addSubView(aview: NSView); override;
procedure doCommandBySelector (aSelector: SEL); override;
end;
{ TCocoaCustomControlWithBaseInputClient }
TCocoaCustomControlWithBaseInputClient = objcclass(TCocoaCustomControl, NSTextInputClientProtocol)
private
_inIME: Boolean;
private
function getWindowEditor(): NSTextView; message 'getWindowEditor';
procedure DoCallInputClientInsertText(nsText:NSString); message 'DoCallInputClientInsertText:';
public
// NSTextInputClientProtocol related.
// implements a base NSTextInputClient for non-editable LCL CustomControl,
@ -75,22 +80,17 @@ type
function selectedRange: NSRange;
function markedRange: NSRange;
function hasMarkedText: LCLObjCBoolean;
function firstRectForCharacterRange_actualRange (aRange: NSRange; actualRange: NSRangePointer): NSRect;
function attributedSubstringForProposedRange_actualRange (aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
function validAttributesForMarkedText: NSArray;
function firstRectForCharacterRange_actualRange (aRange: NSRange; actualRange: NSRangePointer): NSRect;
function characterIndexForPoint (aPoint: NSPoint): NSUInteger;
procedure doCommandBySelector (aSelector: SEL); override;
end;
implementation
{ TCocoaCustomControl }
function TCocoaCustomControl.getWindowEditor(): NSTextView;
begin
Result:= NSTextView( self.window.fieldEditor_forObject(true,nil) );
end;
procedure TCocoaCustomControl.setStringValue(avalue: NSString);
begin
if Assigned(fstr) then fstr.release;
@ -129,142 +129,6 @@ begin
end;
end;
procedure TCocoaCustomControl.keyDown(theEvent: NSEvent);
var
textView: NSView;
isFirst: Boolean;
begin
if (not _inIME) and (theEvent.keyCode in
[kVK_Return, kVK_ANSI_KeypadEnter, kVK_Escape, kVK_Space]) then
begin
inherited;
exit;
end;
isFirst:= not _inIME;
inputContext.handleEvent(theEvent);
if _inIME and isFirst then
begin
textView:= getWindowEditor();
textView.setFrameSize( NSMakeSize(self.frame.size.width,16) );
self.addSubView( textView );
end
else if not _inIME then
inputContext.discardMarkedText;
end;
procedure TCocoaCustomControl.DoCallInputClientInsertText(nsText:NSString);
begin
if Assigned(callback) then
callback.InputClientInsertText(nsText.UTF8String);
nsText.release;
end;
// in TCocoaCustomControl, such as Form, Grid, ListView,
// after inputting text, another control may be focused.
// in insertText_replacementRange(), Cocoa/InputContext doesn't like it,
// so calling InputClientInsertText() asynchronously.
procedure TCocoaCustomControl.insertText_replacementRange(aString: id;
replacementRange: NSRange);
var
nsText: NSString;
begin
if not _inIME then exit;
unmarkText;
nsText:= getNSStringObject(aString).copy;
performSelector_withObject_afterDelay(ObjCSelector('DoCallInputClientInsertText:'), nsText, 0 );
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:= getWindowEditor();
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;
var
textView: NSTextView;
begin
_inIME:= false;
textView:= getWindowEditor();
if Assigned(textView) then
textView.removeFromSuperview;
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;
var
textView: NSText;
begin
textView:= getWindowEditor();
if not Assigned(textView) then
Result:= NSMakeRange( NSNotFound, 0 )
else
Result:= textView.selectedRange;
end;
function TCocoaCustomControl.markedRange: NSRange;
var
textView: NSTextView;
begin
textView:= getWindowEditor();
if not Assigned(textView) then
Result:= NSMakeRange( NSNotFound, 0 )
else
Result:= textView.markedRange;
end;
function TCocoaCustomControl.attributedSubstringForProposedRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
begin
Result := nil;
end;
function TCocoaCustomControl.validAttributesForMarkedText: NSArray;
begin
Result := nil;
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;
@ -439,5 +303,148 @@ begin
inherited otherMouseDragged(event);
end;
procedure TCocoaCustomControl.doCommandBySelector(aSelector: SEL);
begin
inherited doCommandBySelector(ASelector);
end;
{ TCocoaCustomControlWithBaseInputClient }
function TCocoaCustomControlWithBaseInputClient.getWindowEditor(): NSTextView;
begin
Result:= NSTextView( self.window.fieldEditor_forObject(true,nil) );
end;
procedure TCocoaCustomControlWithBaseInputClient.DoCallInputClientInsertText(nsText:NSString);
begin
if Assigned(callback) then
callback.InputClientInsertText(nsText.UTF8String);
nsText.release;
end;
procedure TCocoaCustomControlWithBaseInputClient.keyDown(theEvent: NSEvent);
var
textView: NSView;
isFirst: Boolean;
begin
if (not _inIME) and (theEvent.keyCode in
[kVK_Return, kVK_ANSI_KeypadEnter, kVK_Escape, kVK_Space]) then
begin
inherited;
exit;
end;
isFirst:= not _inIME;
inputContext.handleEvent(theEvent);
if _inIME and isFirst then
begin
textView:= getWindowEditor();
textView.setFrameSize( NSMakeSize(self.frame.size.width,16) );
self.addSubView( textView );
end
else if not _inIME then
inputContext.discardMarkedText;
end;
// in TCocoaCustomControl, such as Form, Grid, ListView,
// after inputting text, another control may be focused.
// in insertText_replacementRange(), Cocoa/InputContext doesn't like it,
// so calling InputClientInsertText() asynchronously.
procedure TCocoaCustomControlWithBaseInputClient.insertText_replacementRange(aString: id;
replacementRange: NSRange);
var
nsText: NSString;
begin
if not _inIME then exit;
unmarkText;
nsText:= getNSStringObject(aString).copy;
performSelector_withObject_afterDelay(ObjCSelector('DoCallInputClientInsertText:'), nsText, 0 );
end;
procedure TCocoaCustomControlWithBaseInputClient.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:= getWindowEditor();
if Assigned(textView) then
textView.setMarkedText_selectedRange_replacementRange(aString,selectedRange,replacementRange);
end
else
unmarkText;
end;
function TCocoaCustomControlWithBaseInputClient.hasMarkedText: LCLObjCBoolean;
begin
Result := _inIME;
end;
procedure TCocoaCustomControlWithBaseInputClient.unmarkText;
var
textView: NSTextView;
begin
_inIME:= false;
textView:= getWindowEditor();
if Assigned(textView) then
textView.removeFromSuperview;
end;
function TCocoaCustomControlWithBaseInputClient.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 TCocoaCustomControlWithBaseInputClient.selectedRange: NSRange;
var
textView: NSText;
begin
textView:= getWindowEditor();
if not Assigned(textView) then
Result:= NSMakeRange( NSNotFound, 0 )
else
Result:= textView.selectedRange;
end;
function TCocoaCustomControlWithBaseInputClient.markedRange: NSRange;
var
textView: NSTextView;
begin
textView:= getWindowEditor();
if not Assigned(textView) then
Result:= NSMakeRange( NSNotFound, 0 )
else
Result:= textView.markedRange;
end;
function TCocoaCustomControlWithBaseInputClient.attributedSubstringForProposedRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
begin
Result := nil;
end;
function TCocoaCustomControlWithBaseInputClient.validAttributesForMarkedText: NSArray;
begin
Result := nil;
end;
function TCocoaCustomControlWithBaseInputClient.characterIndexForPoint(aPoint: NSPoint
): NSUInteger;
begin
Result := 0;
end;
end.

View File

@ -46,7 +46,7 @@ type
// Key Class for Cocoa IME support
// 1. obtain IME capability from Cocoa by implementing NSTextInputClientProtocol
// 2. synchronize IME data with LCL via ICocoaIMEControl
TCocoaFullControlEdit = objcclass(TCocoaCustomControl)
TCocoaFullControlEdit = objcclass(TCocoaCustomControl, NSTextInputClientProtocol)
private
_currentParams: TCocoaIMEParameters;
_currentMarkedText: NSString;
@ -58,13 +58,17 @@ type
procedure mouseUp(event: NSEvent); override;
function resignFirstResponder: ObjCBOOL; override;
procedure setMarkedText_selectedRange_replacementRange (aString: id; newRange: NSRange; replacementRange: NSRange); override;
procedure insertText_replacementRange (aString: id; replacementRange: NSRange); override;
procedure unmarkText; override;
function markedRange: NSRange; override;
function selectedRange: NSRange; override;
function hasMarkedText: LCLObjCBoolean; override;
function firstRectForCharacterRange_actualRange ({%H-}aRange: NSRange; {%H-}actualRange: NSRangePointer): NSRect; override;
procedure insertText_replacementRange (aString: id; replacementRange: NSRange);
procedure setMarkedText_selectedRange_replacementRange (aString: id; newRange: NSRange; replacementRange: NSRange);
procedure unmarkText;
function selectedRange: NSRange;
function markedRange: NSRange;
function hasMarkedText: LCLObjCBoolean;
function firstRectForCharacterRange_actualRange ({%H-}aRange: NSRange; {%H-}actualRange: NSRangePointer): NSRect;
function attributedSubstringForProposedRange_actualRange (aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
function validAttributesForMarkedText: NSArray;
function characterIndexForPoint (aPoint: NSPoint): NSUInteger;
end;
implementation
@ -291,5 +295,22 @@ begin
Result:= ( _currentParams.textNSLength > 0 );
end;
function TCocoaFullControlEdit.attributedSubstringForProposedRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
begin
Result := nil;
end;
function TCocoaFullControlEdit.validAttributesForMarkedText: NSArray;
begin
Result := nil;
end;
function TCocoaFullControlEdit.characterIndexForPoint(aPoint: NSPoint
): NSUInteger;
begin
Result := 0;
end;
end.

View File

@ -184,7 +184,7 @@ type
{ TCocoaWindowContentDocument }
TCocoaWindowContentDocument = objcclass(TCocoaCustomControl)
TCocoaWindowContentDocument = objcclass(TCocoaCustomControlWithBaseInputClient)
protected
procedure didBecomeKeyNotification(sender: NSNotification); message 'didBecomeKeyNotification:';
procedure didResignKeyNotification(sender: NSNotification); message 'didResignKeyNotification:';

View File

@ -1978,13 +1978,13 @@ begin
ctrl := TCocoaFullControlEdit.alloc.lclInitWithCreateParams(AParams);
lcl := TLCLFullControlEditCallback.Create(ctrl, AWinControl);
TCocoaFullControlEdit(ctrl).imeHandler := imeHandler;
ctrl.unmarkText;
TCocoaFullControlEdit(ctrl).unmarkText;
end
else
begin
// AWinControl not implements ICocoaIMEControl
// AWinControl is a normal Custom Control
ctrl := TCocoaCustomControl.alloc.lclInitWithCreateParams(AParams);
ctrl := TCocoaCustomControlWithBaseInputClient.alloc.lclInitWithCreateParams(AParams);
lcl := TLCLCommonCallback.Create(ctrl, AWinControl);
end;
lcl.BlockCocoaUpDown := true;

View File

@ -2375,7 +2375,7 @@ begin
// set a content view in order to be able to customize drawing for labels/color
ns := GetNSRect(AParams.X, AParams.Y, AParams.Width, AParams.Height);
lGroupBoxContents := TCocoaCustomControl(TCocoaCustomControl.alloc.initWithFrame(ns));
lGroupBoxContents := TCocoaCustomControl.alloc.initWithFrame(ns);
lGroupBoxContents.callback := box.callback; //TLCLCustomControlCallback.Create(lGroupBoxContents, AWinControl);
//str := Format('%X=%X', [PtrUInt(box.callback), PtrUInt(lGroupBoxContents.callback)]);
lGroupBoxContents.autorelease;