Cocoa: LookupWord implemented in FullControlEdit

This commit is contained in:
rich2014 2024-08-27 00:55:55 +08:00
parent 6153ab067b
commit cfbd95b451

View File

@ -74,6 +74,7 @@ type
_currentMarkedText: NSString;
public
imeHandler: ICocoaIMEControl;
lwHandler: ICocoaLookupWord;
public
procedure keyDown(theEvent: NSEvent); override;
procedure mouseDown(event: NSEvent); override;
@ -270,18 +271,91 @@ begin
end;
end;
{
the Cocoa API uses a continuous positive integer to locate the text position,
mainly in:
1. get the text index corresponding to the current mouse cursor through
the return value of characterIndexForPoint()
2. the text is obtained through the parameter aRange.location passed in by
attributedSubstringForProposedRange_actualRange().
note that Cocoa assumes that the index are continuous, with -1 corresponding
to the previous character and +1 corresponding to the next character,
which is where the trouble comes in.
however, in the controls such as SynEdit, there is no corresponding Index.
although the index can be calculated by traversing each line, it increases
the workload and complexity.
in Lookup Word, we only need to ensure that the index is continuous in a line,
so a simplified method is used:
1. controls such as SynEdit are indexed by row + column
2. the index required by Cooca is a 64-bit integer
3. so the rows and columns are encoded into 64-bit Index, with
the high 32 bits corresponding to the rows and
the lower 32 bits corresponding to the columns.
4. the operation on the Index cannot cross line. if it does,
a simple correction is required. see rangeToLWParams().
}
const
LW_LOCATION_BASE = $1000000000000000;
function rangeToLWParams( const aRange: NSRange ): TCocoaLWParameters;
var
location: NSUInteger;
begin
location:= aRange.location;
if location >= (LW_LOCATION_BASE/2) then
location:= location - LW_LOCATION_BASE;
Result.row:= location shr 32;
Result.col:= location and $FFFFFFFF;
Result.length:= aRange.length;
if Result.col < 0 then begin
Result.col:= 0;
Result.row:= Result.row + 1;
end;
end;
function LWParamsToRange( const params: TCocoaLWParameters ): NSRange;
var
location: NSUInteger;
begin
location:= (QWord(params.row) shl 32) + params.col;
Result.location:= location + LW_LOCATION_BASE;
Result.length:= params.length;
end;
// cursor tracking
function TCocoaFullControlEdit.firstRectForCharacterRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSRect;
function getImeTextBound: TRect;
var
params: TCocoaIMEParameters;
begin
params:= _currentParams;
setIMESelectedRange( params, _currentMarkedText, aRange );
params.isFirstCall:= not hasMarkedText();
Result:= imeHandler.IMEGetTextBound( params );
end;
function getLookupWordBound: TRect;
var
params: TCocoaLWParameters;
begin
params:= rangeToLWParams( aRange );
Result:= lwHandler.LWGetTextBound( params );
end;
var
params: TCocoaIMEParameters;
rect : TRect;
begin
params:= _currentParams;
setIMESelectedRange( params, _currentMarkedText, aRange );
params.isFirstCall:= not hasMarkedText();
rect:= imeHandler.IMEGetTextBound( params );
if aRange.location < LW_LOCATION_BASE then begin
rect:= getImeTextBound;
end else begin
rect:= getLookupWordBound;
end;
LCLToNSRect( rect, NSGlobalScreenBottom, Result );
end;
@ -317,10 +391,77 @@ begin
Result:= ( _currentParams.textNSLength > 0 );
end;
{
1. given the previous description of not crossing lines,
at most one line of text is returned.
2. LW_LOCATION_BASE has been added to location to distinguish
between IME and Lookup Word.
}
function TCocoaFullControlEdit.attributedSubstringForProposedRange_actualRange(
aRange: NSRange; actualRange: NSRangePointer): NSAttributedString;
var
params: TCocoaLWParameters;
textWord: NSString;
procedure initParams;
begin
params:= rangeToLWParams( aRange );
self.lwHandler.LWLineForRow( params );
end;
procedure initTextWord;
var
lineText: NSString;
subRange: NSRange;
begin
lineText:= StrToNSString( params.text );
subRange.location:= params.col;
subRange.length:= aRange.length;
if subRange.location + subRange.length > lineText.length then
subRange.length:= lineText.length - subRange.location;
textWord:= lineText.substringWithRange(subRange);
end;
function getAttributeWord: NSAttributedString;
var
attribs: NSDictionary;
lclFont: TFont;
cocoaFont: NSFont;
begin
lclFont:= self.lwHandler.LWGetFont( params );
cocoaFont:= TCocoaFont(lclFont.Reference.Handle).Font;
attribs:= NSMutableDictionary.alloc.initWithCapacity(1);
attribs.setValue_forKey( cocoaFont, NSFontAttributeName );
Result:= NSAttributedString.alloc.initWithString_attributes(
textWord, attribs );
Result.autorelease;
attribs.release;
end;
begin
Result := nil;
Result:= nil;
if NOT Assigned(self.lwHandler) then
Exit;
initParams;
initTextWord;
actualRange^:= LWParamsToRange( params );
Result:= getAttributeWord;
end;
function TCocoaFullControlEdit.characterIndexForPoint(aPoint: NSPoint
): NSUInteger;
var
params: TCocoaLWParameters;
lclPoint: TPoint;
begin
Result:= 0;
if NOT Assigned(self.lwHandler) then
Exit;
lclPoint:= ScreenPointFromNSToLCL( aPoint );
self.lwHandler.LWRowColForScreenPoint( params, lclPoint );
Result:= LWParamsToRange(params).location;
end;
function TCocoaFullControlEdit.validAttributesForMarkedText: NSArray;
@ -328,11 +469,5 @@ begin
Result := nil;
end;
function TCocoaFullControlEdit.characterIndexForPoint(aPoint: NSPoint
): NSUInteger;
begin
Result := 0;
end;
end.