mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-23 00:59:31 +02:00
Cocoa/FullEdit: decouple FullEditControl/IME from CocoaPrivate unit to CocoaFullEditControl unit
This commit is contained in:
parent
ee4ab33846
commit
fea0f147a1
295
lcl/interfaces/cocoa/cocoafullcontroledit.pas
Normal file
295
lcl/interfaces/cocoa/cocoafullcontroledit.pas
Normal file
@ -0,0 +1,295 @@
|
||||
unit CocoaFullControlEdit;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
{$modeswitch objectivec2}
|
||||
{$interfaces corba}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils,
|
||||
LazUTF8,
|
||||
CocoaAll, CocoaPrivate, CocoaUtils;
|
||||
|
||||
type
|
||||
{ ICocoaIMEControl }
|
||||
|
||||
// IME Parameters for Cocoa Interface internal and LCL Full Control Edit
|
||||
// intentionally keep the Record type, emphasizing that it is only a simple type,
|
||||
// only used as parameters, don‘t put into logical functions
|
||||
TCocoaIMEParameters = record
|
||||
text: ShortString; // Marked Text
|
||||
textCharLength: Integer; // length in code point
|
||||
textByteLength: Integer; // length in bytes
|
||||
textNSLength: Integer; // length in code unit (NSString)
|
||||
selectedStart: Integer; // selected range start in code point
|
||||
selectedLength: Integer; // selected range length in code point
|
||||
eatAmount: Integer; // delete char out of Marked Text
|
||||
isFirstCall: Boolean; // if first in the IME session
|
||||
end;
|
||||
|
||||
// the LCL Component that need Cocoa IME support need to
|
||||
// implement this simple interface
|
||||
// class LazSynCocoaIMM in SynEdit Component for reference
|
||||
// class ATSynEdit_Adapter_CocoaIME in ATSynEdit Component for reference
|
||||
ICocoaIMEControl = interface
|
||||
procedure IMESessionBegin;
|
||||
procedure IMESessionEnd;
|
||||
procedure IMEUpdateIntermediateText( var params: TCocoaIMEParameters );
|
||||
procedure IMEInsertFinalText( var params: TCocoaIMEParameters );
|
||||
function IMEGetTextBound( var params: TCocoaIMEParameters ) : TRect;
|
||||
end;
|
||||
|
||||
{ TCocoaFullControlEdit }
|
||||
|
||||
// backend of LCL Full Control Edit Component (such as SynEdit/ATSynEdit)
|
||||
// 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)
|
||||
private
|
||||
_currentParams: TCocoaIMEParameters;
|
||||
_currentMarkedText: NSString;
|
||||
public
|
||||
imeHandler: ICocoaIMEControl;
|
||||
public
|
||||
procedure keyDown(theEvent: NSEvent); override;
|
||||
procedure mouseDown(event: NSEvent); override;
|
||||
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;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TCocoaIMEParameters }
|
||||
|
||||
// set text and length in params
|
||||
procedure setIMEParamsText( var params: TCocoaIMEParameters; const nsText: NSString );
|
||||
begin
|
||||
params.text := NSStringToString( nsText );
|
||||
params.textCharLength := UTF8Length( params.text );
|
||||
params.textByteLength := Length( params.text );
|
||||
params.textNSLength := nsText.length;
|
||||
end;
|
||||
|
||||
// set selected range in code point
|
||||
procedure setIMESelectedRange( var params: TCocoaIMEParameters; const nsText: NSString; range: NSRange );
|
||||
begin
|
||||
if range.location<>NSNotFound then
|
||||
begin
|
||||
if range.location>nsText.length then
|
||||
range.location:= 0;
|
||||
if range.location+range.length>nsText.length then
|
||||
range.length:= nsText.length-range.location;
|
||||
end;
|
||||
|
||||
if range.location=NSNotFound then
|
||||
params.selectedStart:= 0
|
||||
else
|
||||
params.selectedStart:= UTF8Length( nsText.substringToIndex(range.location).UTF8String );
|
||||
|
||||
if range.length=0 then
|
||||
params.selectedLength:= 0
|
||||
else
|
||||
params.selectedLength:= UTF8Length( nsText.substringWithRange(range).UTF8String );
|
||||
end;
|
||||
|
||||
{ TCocoaFullControlEdit }
|
||||
|
||||
{
|
||||
for IME Key Down:
|
||||
Key step for IME (such as Chinese/Japanese/Korean and DeadKeys)
|
||||
1. forward key event to NSInputContext
|
||||
2. NSInputContext will call TCocoaFullControlEdit(NSTextControlClient)
|
||||
and then call LCL via imeHandler
|
||||
}
|
||||
procedure TCocoaFullControlEdit.keyDown(theEvent: NSEvent);
|
||||
begin
|
||||
inputContext.handleEvent(theEvent);
|
||||
end;
|
||||
|
||||
{
|
||||
for IME Close:
|
||||
1. mouseDown() will not be called when click in the IME Popup Window,
|
||||
so it must be clicking outside the IME Popup Windows,
|
||||
which should end the IME input
|
||||
2. Cocoa had called setMarkedText_selectedRange_replacementRange()
|
||||
or insertText_replacementRange() first, then mouseDown() here
|
||||
3. NSInputContext.handleEvent() just close IME window here
|
||||
4. LCL actually handle mouse event
|
||||
}
|
||||
procedure TCocoaFullControlEdit.mouseDown(event: NSEvent);
|
||||
begin
|
||||
inputContext.handleEvent(event);
|
||||
Inherited;
|
||||
end;
|
||||
|
||||
procedure TCocoaFullControlEdit.mouseUp(event: NSEvent);
|
||||
begin
|
||||
inputContext.handleEvent(event);
|
||||
Inherited;
|
||||
end;
|
||||
|
||||
// prevent switch to other control when in IME input state
|
||||
function TCocoaFullControlEdit.resignFirstResponder: ObjCBOOL;
|
||||
begin
|
||||
Result := not hasMarkedText();
|
||||
end;
|
||||
|
||||
function isIMEDuplicateCall( const newParams, currentParams: TCocoaIMEParameters ) : Boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
if newParams.isFirstCall then exit;
|
||||
if newParams.text <> currentParams.text then exit;
|
||||
if newParams.selectedStart<>currentParams.selectedStart then exit;
|
||||
if newParams.selectedLength<>currentParams.selectedLength then exit;
|
||||
Result:= true;
|
||||
end;
|
||||
|
||||
// send Marked/Intermediate Text to LCL Edit Control which has IME Handler
|
||||
// Key step for IME (such as Chinese/Japanese/Korean and DeadKeys)
|
||||
procedure TCocoaFullControlEdit.setMarkedText_selectedRange_replacementRange(
|
||||
aString: id; newRange: NSRange; replacementRange: NSRange);
|
||||
var
|
||||
params : TCocoaIMEParameters;
|
||||
nsText : NSString;
|
||||
begin
|
||||
params.isFirstCall:= not hasMarkedText();
|
||||
|
||||
// no markedText before, the first call
|
||||
if params.isFirstCall then imeHandler.IMESessionBegin;
|
||||
|
||||
// get IME Intermediate Text
|
||||
nsText:= getNSStringObject( aString );
|
||||
setIMEParamsText( params, nsText );
|
||||
|
||||
// some IME want to select subRange of Intermediate Text
|
||||
// such as Japanese
|
||||
setIMESelectedRange( params, nsText, newRange );
|
||||
|
||||
// some IME incorrectly call setMarkedText() twice with the same parameters
|
||||
if isIMEDuplicateCall( params, _currentParams ) then
|
||||
exit;
|
||||
|
||||
// some IME want to eat some chars
|
||||
// such as inputting DeadKeys
|
||||
if replacementRange.location<>NSNotFound then
|
||||
params.eatAmount:= 1 - replacementRange.location
|
||||
else
|
||||
params.eatAmount:= 0;
|
||||
|
||||
// Key Step to update(display) Marked/Intermediate Text
|
||||
imeHandler.IMEUpdateIntermediateText( params );
|
||||
|
||||
if params.textNSLength=0 then
|
||||
begin
|
||||
// cancel Marked/Intermediate Text
|
||||
imeHandler.IMESessionEnd;
|
||||
unmarkText;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// update Marked/Intermediate Text internal status
|
||||
_currentParams:= params;
|
||||
_currentMarkedText.release;
|
||||
_currentMarkedText:= nsText;
|
||||
_currentMarkedText.retain;
|
||||
end;
|
||||
end;
|
||||
|
||||
{
|
||||
send final Text to LCL Edit Control which has IME Handler
|
||||
Key step for IME (such as Chinese/Japanese/Korean and DeadKeys)
|
||||
1. if in IME input state, handle text via imeHandler.IMEInsertFinalText()
|
||||
2. otherwise via lclGetCallback.InputClientInsertText,
|
||||
mainly for maximum forward compatibility with TCocoaCustomControl
|
||||
}
|
||||
procedure TCocoaFullControlEdit.insertText_replacementRange(aString: id;
|
||||
replacementRange: NSRange);
|
||||
var
|
||||
params: TCocoaIMEParameters;
|
||||
nsText : NSString;
|
||||
begin
|
||||
params.isFirstCall:= not hasMarkedText();
|
||||
|
||||
// IME final text
|
||||
nsText:= getNSStringObject( aString );
|
||||
setIMEParamsText( params, nsText );
|
||||
|
||||
// some IME want to eat some chars, such as inputting DeadKeys
|
||||
if replacementRange.location<>NSNotFound then
|
||||
params.eatAmount:= 1 - replacementRange.location
|
||||
else
|
||||
params.eatAmount:= 0;
|
||||
|
||||
if (not params.isFirstCall) or (params.eatAmount<>0) then
|
||||
// insert IME final text
|
||||
imeHandler.IMEInsertFinalText( params )
|
||||
else
|
||||
// insert normal text (without IME) by LCLControl.IntfUTF8KeyPress()
|
||||
lclGetCallback.InputClientInsertText( params.text );
|
||||
|
||||
if not params.isFirstCall then
|
||||
begin
|
||||
imeHandler.IMESessionEnd;
|
||||
unmarkText;
|
||||
end;
|
||||
end;
|
||||
|
||||
// cursor tracking
|
||||
function TCocoaFullControlEdit.firstRectForCharacterRange_actualRange(
|
||||
aRange: NSRange; actualRange: NSRangePointer): NSRect;
|
||||
var
|
||||
params: TCocoaIMEParameters;
|
||||
rect : TRect;
|
||||
begin
|
||||
params:= _currentParams;
|
||||
setIMESelectedRange( params, _currentMarkedText, aRange );
|
||||
params.isFirstCall:= not hasMarkedText();
|
||||
|
||||
rect:= imeHandler.IMEGetTextBound( params );
|
||||
LCLToNSRect( rect, NSGlobalScreenBottom, Result );
|
||||
end;
|
||||
|
||||
procedure TCocoaFullControlEdit.unmarkText;
|
||||
begin
|
||||
setIMEParamsText( _currentParams, nil );
|
||||
_currentParams.selectedStart:= 0;
|
||||
_currentParams.selectedLength:= 0;
|
||||
_currentParams.eatAmount:= 0;
|
||||
_currentParams.isFirstCall:= true;
|
||||
_currentMarkedText.release;
|
||||
_currentMarkedText:= nil;
|
||||
end;
|
||||
|
||||
function TCocoaFullControlEdit.markedRange: NSRange;
|
||||
begin
|
||||
if _currentParams.textNSLength=0 then
|
||||
Result:= NSMakeRange( NSNotFound, 0 )
|
||||
else
|
||||
Result:= NSMakeRange( 0, _currentParams.textNSLength );
|
||||
end;
|
||||
|
||||
function TCocoaFullControlEdit.selectedRange: NSRange;
|
||||
begin
|
||||
if _currentParams.textNSLength=0 then
|
||||
Result:= NSMakeRange( 0, 0 )
|
||||
else
|
||||
Result:= NSMakeRange( _currentParams.selectedStart, _currentParams.selectedLength );
|
||||
end;
|
||||
|
||||
function TCocoaFullControlEdit.hasMarkedText: LCLObjCBoolean;
|
||||
begin
|
||||
Result:= ( _currentParams.textNSLength > 0 );
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -181,61 +181,6 @@ type
|
||||
procedure doCommandBySelector (aSelector: SEL); override;
|
||||
end;
|
||||
|
||||
{ ICocoaIMEControl }
|
||||
|
||||
// IME Parameters for Cocoa Interface internal and LCL Full Control Edit
|
||||
// intentionally keep the Record type, emphasizing that it is only a simple type,
|
||||
// only used as parameters, don‘t put into logical functions
|
||||
TCocoaIMEParameters = record
|
||||
text: ShortString; // Marked Text
|
||||
textCharLength: Integer; // length in code point
|
||||
textByteLength: Integer; // length in bytes
|
||||
textNSLength: Integer; // length in code unit (NSString)
|
||||
selectedStart: Integer; // selected range start in code point
|
||||
selectedLength: Integer; // selected range length in code point
|
||||
eatAmount: Integer; // delete char out of Marked Text
|
||||
isFirstCall: Boolean; // if first in the IME session
|
||||
end;
|
||||
|
||||
// the LCL Component that need Cocoa IME support need to
|
||||
// implement this simple interface
|
||||
// class LazSynCocoaIMM in SynEdit Component for reference
|
||||
// class ATSynEdit_Adapter_CocoaIME in ATSynEdit Component for reference
|
||||
ICocoaIMEControl = interface
|
||||
procedure IMESessionBegin;
|
||||
procedure IMESessionEnd;
|
||||
procedure IMEUpdateIntermediateText( var params: TCocoaIMEParameters );
|
||||
procedure IMEInsertFinalText( var params: TCocoaIMEParameters );
|
||||
function IMEGetTextBound( var params: TCocoaIMEParameters ) : TRect;
|
||||
end;
|
||||
|
||||
{ TCocoaFullControlEdit }
|
||||
|
||||
// backend of LCL Full Control Edit Component (such as SynEdit/ATSynEdit)
|
||||
// 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)
|
||||
private
|
||||
_currentParams: TCocoaIMEParameters;
|
||||
_currentMarkedText: NSString;
|
||||
public
|
||||
imeHandler: ICocoaIMEControl;
|
||||
public
|
||||
procedure keyDown(theEvent: NSEvent); override;
|
||||
procedure mouseDown(event: NSEvent); override;
|
||||
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;
|
||||
end;
|
||||
|
||||
TStatusItemData = record
|
||||
Text : NSString;
|
||||
Width : Integer;
|
||||
@ -512,14 +457,6 @@ end;
|
||||
|
||||
{ TCocoaCustomControl }
|
||||
|
||||
function getNSStringObject( const aString: id ) : NSString;
|
||||
begin
|
||||
if aString.isKindOfClass( NSAttributedString ) then
|
||||
Result:= NSAttributedString( aString ).string_
|
||||
else
|
||||
Result:= NSString( aString );
|
||||
end;
|
||||
|
||||
function TCocoaCustomControl.getWindowEditor(): NSTextView;
|
||||
begin
|
||||
Result:= NSTextView( self.window.fieldEditor_forObject(true,nil) );
|
||||
@ -874,228 +811,6 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
{ TCocoaIMEParameters }
|
||||
|
||||
// set text and length in params
|
||||
procedure setIMEParamsText( var params: TCocoaIMEParameters; const nsText: NSString );
|
||||
begin
|
||||
params.text := NSStringToString( nsText );
|
||||
params.textCharLength := UTF8Length( params.text );
|
||||
params.textByteLength := Length( params.text );
|
||||
params.textNSLength := nsText.length;
|
||||
end;
|
||||
|
||||
// set selected range in code point
|
||||
procedure setIMESelectedRange( var params: TCocoaIMEParameters; const nsText: NSString; range: NSRange );
|
||||
begin
|
||||
if range.location<>NSNotFound then
|
||||
begin
|
||||
if range.location>nsText.length then
|
||||
range.location:= 0;
|
||||
if range.location+range.length>nsText.length then
|
||||
range.length:= nsText.length-range.location;
|
||||
end;
|
||||
|
||||
if range.location=NSNotFound then
|
||||
params.selectedStart:= 0
|
||||
else
|
||||
params.selectedStart:= UTF8Length( nsText.substringToIndex(range.location).UTF8String );
|
||||
|
||||
if range.length=0 then
|
||||
params.selectedLength:= 0
|
||||
else
|
||||
params.selectedLength:= UTF8Length( nsText.substringWithRange(range).UTF8String );
|
||||
end;
|
||||
|
||||
{ TCocoaFullControlEdit }
|
||||
|
||||
{
|
||||
for IME Key Down:
|
||||
Key step for IME (such as Chinese/Japanese/Korean and DeadKeys)
|
||||
1. forward key event to NSInputContext
|
||||
2. NSInputContext will call TCocoaFullControlEdit(NSTextControlClient)
|
||||
and then call LCL via imeHandler
|
||||
}
|
||||
procedure TCocoaFullControlEdit.keyDown(theEvent: NSEvent);
|
||||
begin
|
||||
inputContext.handleEvent(theEvent);
|
||||
end;
|
||||
|
||||
{
|
||||
for IME Close:
|
||||
1. mouseDown() will not be called when click in the IME Popup Window,
|
||||
so it must be clicking outside the IME Popup Windows,
|
||||
which should end the IME input
|
||||
2. Cocoa had called setMarkedText_selectedRange_replacementRange()
|
||||
or insertText_replacementRange() first, then mouseDown() here
|
||||
3. NSInputContext.handleEvent() just close IME window here
|
||||
4. LCL actually handle mouse event
|
||||
}
|
||||
procedure TCocoaFullControlEdit.mouseDown(event: NSEvent);
|
||||
begin
|
||||
inputContext.handleEvent(event);
|
||||
Inherited;
|
||||
end;
|
||||
|
||||
procedure TCocoaFullControlEdit.mouseUp(event: NSEvent);
|
||||
begin
|
||||
inputContext.handleEvent(event);
|
||||
Inherited;
|
||||
end;
|
||||
|
||||
// prevent switch to other control when in IME input state
|
||||
function TCocoaFullControlEdit.resignFirstResponder: ObjCBOOL;
|
||||
begin
|
||||
Result := not hasMarkedText();
|
||||
end;
|
||||
|
||||
function isIMEDuplicateCall( const newParams, currentParams: TCocoaIMEParameters ) : Boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
if newParams.isFirstCall then exit;
|
||||
if newParams.text <> currentParams.text then exit;
|
||||
if newParams.selectedStart<>currentParams.selectedStart then exit;
|
||||
if newParams.selectedLength<>currentParams.selectedLength then exit;
|
||||
Result:= true;
|
||||
end;
|
||||
|
||||
// send Marked/Intermediate Text to LCL Edit Control which has IME Handler
|
||||
// Key step for IME (such as Chinese/Japanese/Korean and DeadKeys)
|
||||
procedure TCocoaFullControlEdit.setMarkedText_selectedRange_replacementRange(
|
||||
aString: id; newRange: NSRange; replacementRange: NSRange);
|
||||
var
|
||||
params : TCocoaIMEParameters;
|
||||
nsText : NSString;
|
||||
begin
|
||||
params.isFirstCall:= not hasMarkedText();
|
||||
|
||||
// no markedText before, the first call
|
||||
if params.isFirstCall then imeHandler.IMESessionBegin;
|
||||
|
||||
// get IME Intermediate Text
|
||||
nsText:= getNSStringObject( aString );
|
||||
setIMEParamsText( params, nsText );
|
||||
|
||||
// some IME want to select subRange of Intermediate Text
|
||||
// such as Japanese
|
||||
setIMESelectedRange( params, nsText, newRange );
|
||||
|
||||
// some IME incorrectly call setMarkedText() twice with the same parameters
|
||||
if isIMEDuplicateCall( params, _currentParams ) then
|
||||
exit;
|
||||
|
||||
// some IME want to eat some chars
|
||||
// such as inputting DeadKeys
|
||||
if replacementRange.location<>NSNotFound then
|
||||
params.eatAmount:= 1 - replacementRange.location
|
||||
else
|
||||
params.eatAmount:= 0;
|
||||
|
||||
// Key Step to update(display) Marked/Intermediate Text
|
||||
imeHandler.IMEUpdateIntermediateText( params );
|
||||
|
||||
if params.textNSLength=0 then
|
||||
begin
|
||||
// cancel Marked/Intermediate Text
|
||||
imeHandler.IMESessionEnd;
|
||||
unmarkText;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// update Marked/Intermediate Text internal status
|
||||
_currentParams:= params;
|
||||
_currentMarkedText.release;
|
||||
_currentMarkedText:= nsText;
|
||||
_currentMarkedText.retain;
|
||||
end;
|
||||
end;
|
||||
|
||||
{
|
||||
send final Text to LCL Edit Control which has IME Handler
|
||||
Key step for IME (such as Chinese/Japanese/Korean and DeadKeys)
|
||||
1. if in IME input state, handle text via imeHandler.IMEInsertFinalText()
|
||||
2. otherwise via lclGetCallback.InputClientInsertText,
|
||||
mainly for maximum forward compatibility with TCocoaCustomControl
|
||||
}
|
||||
procedure TCocoaFullControlEdit.insertText_replacementRange(aString: id;
|
||||
replacementRange: NSRange);
|
||||
var
|
||||
params: TCocoaIMEParameters;
|
||||
nsText : NSString;
|
||||
begin
|
||||
params.isFirstCall:= not hasMarkedText();
|
||||
|
||||
// IME final text
|
||||
nsText:= getNSStringObject( aString );
|
||||
setIMEParamsText( params, nsText );
|
||||
|
||||
// some IME want to eat some chars, such as inputting DeadKeys
|
||||
if replacementRange.location<>NSNotFound then
|
||||
params.eatAmount:= 1 - replacementRange.location
|
||||
else
|
||||
params.eatAmount:= 0;
|
||||
|
||||
if (not params.isFirstCall) or (params.eatAmount<>0) then
|
||||
// insert IME final text
|
||||
imeHandler.IMEInsertFinalText( params )
|
||||
else
|
||||
// insert normal text (without IME) by LCLControl.IntfUTF8KeyPress()
|
||||
lclGetCallback.InputClientInsertText( params.text );
|
||||
|
||||
if not params.isFirstCall then
|
||||
begin
|
||||
imeHandler.IMESessionEnd;
|
||||
unmarkText;
|
||||
end;
|
||||
end;
|
||||
|
||||
// cursor tracking
|
||||
function TCocoaFullControlEdit.firstRectForCharacterRange_actualRange(
|
||||
aRange: NSRange; actualRange: NSRangePointer): NSRect;
|
||||
var
|
||||
params: TCocoaIMEParameters;
|
||||
rect : TRect;
|
||||
begin
|
||||
params:= _currentParams;
|
||||
setIMESelectedRange( params, _currentMarkedText, aRange );
|
||||
params.isFirstCall:= not hasMarkedText();
|
||||
|
||||
rect:= imeHandler.IMEGetTextBound( params );
|
||||
LCLToNSRect( rect, NSGlobalScreenBottom, Result );
|
||||
end;
|
||||
|
||||
procedure TCocoaFullControlEdit.unmarkText;
|
||||
begin
|
||||
setIMEParamsText( _currentParams, nil );
|
||||
_currentParams.selectedStart:= 0;
|
||||
_currentParams.selectedLength:= 0;
|
||||
_currentParams.eatAmount:= 0;
|
||||
_currentParams.isFirstCall:= true;
|
||||
_currentMarkedText.release;
|
||||
_currentMarkedText:= nil;
|
||||
end;
|
||||
|
||||
function TCocoaFullControlEdit.markedRange: NSRange;
|
||||
begin
|
||||
if _currentParams.textNSLength=0 then
|
||||
Result:= NSMakeRange( NSNotFound, 0 )
|
||||
else
|
||||
Result:= NSMakeRange( 0, _currentParams.textNSLength );
|
||||
end;
|
||||
|
||||
function TCocoaFullControlEdit.selectedRange: NSRange;
|
||||
begin
|
||||
if _currentParams.textNSLength=0 then
|
||||
Result:= NSMakeRange( 0, 0 )
|
||||
else
|
||||
Result:= NSMakeRange( _currentParams.selectedStart, _currentParams.selectedLength );
|
||||
end;
|
||||
|
||||
function TCocoaFullControlEdit.hasMarkedText: LCLObjCBoolean;
|
||||
begin
|
||||
Result:= ( _currentParams.textNSLength > 0 );
|
||||
end;
|
||||
|
||||
{ LCLObjectExtension }
|
||||
|
||||
function LCLObjectExtension.lclIsEnabled: Boolean;
|
||||
|
@ -72,6 +72,8 @@ function StringRemoveAcceleration(const str: String): String;
|
||||
|
||||
function GetNSObjectWindow(obj: NSObject): NSWindow;
|
||||
|
||||
function getNSStringObject( const aString: id ) : NSString;
|
||||
|
||||
procedure SetNSText(text: NSText; const s: String); inline;
|
||||
function GetNSText(text: NSText): string; inline;
|
||||
|
||||
@ -959,6 +961,14 @@ begin
|
||||
Result:= str.Substring(0,posLeft).Trim;
|
||||
end;
|
||||
|
||||
function getNSStringObject( const aString: id ) : NSString;
|
||||
begin
|
||||
if aString.isKindOfClass( NSAttributedString ) then
|
||||
Result:= NSAttributedString( aString ).string_
|
||||
else
|
||||
Result:= NSString( aString );
|
||||
end;
|
||||
|
||||
procedure SetNSText(text: NSText; const s: String); inline;
|
||||
var
|
||||
ns: NSString;
|
||||
|
@ -12,7 +12,7 @@ uses
|
||||
WSControls, LCLType, LCLMessageGlue, LMessages, LCLProc, LCLIntf, Graphics, Forms,
|
||||
StdCtrls,
|
||||
CocoaAll, CocoaInt, CocoaConfig, CocoaPrivate, CocoaCallback, CocoaUtils,
|
||||
CocoaScrollers, CocoaWSScrollers,
|
||||
CocoaScrollers, CocoaWSScrollers, CocoaFullControlEdit,
|
||||
CocoaGDIObjects, CocoaCursor, CocoaCaret, cocoa_extra;
|
||||
|
||||
type
|
||||
|
@ -131,7 +131,7 @@ end;"/>
|
||||
<License Value="modified LGPL-2
|
||||
"/>
|
||||
<Version Major="3" Minor="99"/>
|
||||
<Files Count="541">
|
||||
<Files Count="542">
|
||||
<Item1>
|
||||
<Filename Value="carbon/agl.pp"/>
|
||||
<AddToUsesPkgSection Value="False"/>
|
||||
@ -2673,6 +2673,11 @@ end;"/>
|
||||
<AddToUsesPkgSection Value="False"/>
|
||||
<UnitName Value="cocoalistcontrol"/>
|
||||
</Item541>
|
||||
<Item542>
|
||||
<Filename Value="cocoa/cocoafullcontroledit.pas"/>
|
||||
<AddToUsesPkgSection Value="False"/>
|
||||
<UnitName Value="cocoafullcontroledit"/>
|
||||
</Item542>
|
||||
</Files>
|
||||
<CompatibilityMode Value="True"/>
|
||||
<LazDoc Paths="../../docs/xml/lcl"/>
|
||||
|
Loading…
Reference in New Issue
Block a user