summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Luby <plubius@neooffice.org>2022-12-20 10:30:00 -0500
committerCaolán McNamara <caolanm@redhat.com>2023-01-06 20:41:32 +0000
commitd8f358656e1c78c479a417adab12eaee190d3fe7 (patch)
tree870d87ed456acd906c94def0d62645c0ff91362e
parentRelated: tdf#152781 Stab the 0xE40A {es} vs Latin America quirk (diff)
downloadcore-d8f358656e1c78c479a417adab12eaee190d3fe7.tar.gz
core-d8f358656e1c78c479a417adab12eaee190d3fe7.zip
tdf#42437 Enable press-and-hold special character input method
The first step to enable this input method is to always return a valid selected range location. -[NSResponder interpretKeyEvents:] will not call [self firstRectForCharacterRange:actualRange:] if the selected range location is NSNotFound so that is what was disabling display of the press-and-hold input method popup. Once [self firstRectForCharacterRange:actualRange:] is called, emulate the press-and-hold behavior of the TextEdit application by setting the marked text to the last key down event's characters. The characters will already have been committed by the special character input method so set the mbTextInputWantsNonRepeatKeyDown flag to indicate that the characters need to be deleted if the input method replaces the committed characters. Also, emulate the press-and-hold behavior of the TextEdit application by committing any uncommitted text when either the escape or the return key is pressed while the press-and-hold input method popup is displayed. Lastly, handle repeat key events by explicitly inserting the text if there is no uncommitted text before -[NSResponder interpretKeyEvents:] is called. Change-Id: I104b3d64a8c66fef3a5a1cec0fba1daa553cd9a4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144630 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk> (cherry picked from commit 1e4ef6d69b22f6674aefb415edf2e8e7ca78ff2d) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145009 Reviewed-by: Patrick Luby <plubius@neooffice.org> Reviewed-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r--vcl/inc/osx/salframeview.h2
-rw-r--r--vcl/osx/salframeview.mm157
2 files changed, 151 insertions, 8 deletions
diff --git a/vcl/inc/osx/salframeview.h b/vcl/inc/osx/salframeview.h
index 4ec0b6c06651..2fcff0d1c1e5 100644
--- a/vcl/inc/osx/salframeview.h
+++ b/vcl/inc/osx/salframeview.h
@@ -86,6 +86,7 @@ enum class SalEvent;
BOOL mbInEndExtTextInput;
BOOL mbInCommitMarkedText;
NSAttributedString* mpLastMarkedText;
+ BOOL mbTextInputWantsNonRepeatKeyDown;
}
+(void)unsetMouseFrame: (AquaSalFrame*)pFrame;
-(id)initWithSalFrame: (AquaSalFrame*)pFrame;
@@ -217,6 +218,7 @@ enum class SalEvent;
-(void)endExtTextInput;
-(void)endExtTextInput:(EndExtTextInputFlags)nFlags;
+-(void)deleteTextInputWantsNonRepeatKeyDown;
@end
diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm
index 546c2a85d24e..355fc84a76b4 100644
--- a/vcl/osx/salframeview.mm
+++ b/vcl/osx/salframeview.mm
@@ -512,6 +512,7 @@ static AquaSalFrame* getMouseContainerFrame()
mbInEndExtTextInput = NO;
mbInCommitMarkedText = NO;
mpLastMarkedText = nil;
+ mbTextInputWantsNonRepeatKeyDown = NO;
}
return self;
@@ -998,8 +999,52 @@ static AquaSalFrame* getMouseContainerFrame()
if( ! [self handleKeyDownException: pEvent] )
{
+ sal_uInt16 nKeyCode = ImplMapKeyCode( [pEvent keyCode] );
+ if ( nKeyCode == KEY_DELETE && mbTextInputWantsNonRepeatKeyDown )
+ {
+ // tdf#42437 Enable press-and-hold special character input method
+ // Emulate the press-and-hold behavior of the TextEdit
+ // application by deleting the marked text when only the
+ // Delete key is pressed and keep the marked text when the
+ // Backspace key or Fn-Delete keys are pressed.
+ if ( [pEvent keyCode] == 51 )
+ {
+ [self deleteTextInputWantsNonRepeatKeyDown];
+ }
+ else
+ {
+ [self unmarkText];
+ mbKeyHandled = true;
+ mbInKeyInput = false;
+ }
+
+ [self endExtTextInput];
+ return;
+ }
+
NSArray* pArray = [NSArray arrayWithObject: pEvent];
[self interpretKeyEvents: pArray];
+
+ // Handle repeat key events by explicitly inserting the text if
+ // -[NSResponder interpretKeyEvents:] does not insert or mark any
+ // text. Note: do not do this step if there is uncommitted text.
+ if ( !mpLastMarkedText && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && [mpLastEvent isARepeat] )
+ {
+ NSString *pChars = [mpLastEvent characters];
+ [self insertText:pChars replacementRange:NSMakeRange( 0, [pChars length] )];
+ }
+ // tdf#42437 Enable press-and-hold special character input method
+ // Emulate the press-and-hold behavior of the TextEdit application
+ // by committing an empty string for key down events dispatched
+ // while the special character input method popup is displayed.
+ else if ( mpLastMarkedText && mbTextInputWantsNonRepeatKeyDown && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && ![mpLastEvent isARepeat] )
+ {
+ // If the escape or return key is pressed, unmark the text to
+ // skip deletion of marked text
+ if ( nKeyCode == KEY_ESCAPE || nKeyCode == KEY_RETURN )
+ [self unmarkText];
+ [self insertText:[NSString string] replacementRange:NSMakeRange( NSNotFound, 0 )];
+ }
}
mbInKeyInput = false;
@@ -1044,6 +1089,8 @@ static AquaSalFrame* getMouseContainerFrame()
SolarMutexGuard aGuard;
+ [self deleteTextInputWantsNonRepeatKeyDown];
+
// Ignore duplicate events that are sometimes posted during cancellation
// of the native input method session. This usually happens when
// [self endExtTextInput] is called from [self windowDidBecomeKey:] and,
@@ -1617,7 +1664,12 @@ static AquaSalFrame* getMouseContainerFrame()
- (NSRange)selectedRange
{
- return mSelectedRange;
+ // tdf#42437 Enable press-and-hold special character input method
+ // Always return a valid range location. If the range location is
+ // NSNotFound, -[NSResponder interpretKeyEvents:] will not call
+ // [self firstRectForCharacterRange:actualRange:] and will not display the
+ // special character input method popup.
+ return ( mSelectedRange.location == NSNotFound ? NSMakeRange( 0, 0 ) : mSelectedRange );
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange replacementRange:(NSRange)replacementRange
@@ -1626,6 +1678,8 @@ static AquaSalFrame* getMouseContainerFrame()
SolarMutexGuard aGuard;
+ [self deleteTextInputWantsNonRepeatKeyDown];
+
if( ![aString isKindOfClass:[NSAttributedString class]] )
aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
@@ -1695,6 +1749,7 @@ static AquaSalFrame* getMouseContainerFrame()
aInputEvent.maText = aInsertString;
aInputEvent.mnCursorPos = nSelectionStart;
+ aInputEvent.mnCursorFlags = 0;
aInputEvent.mpTextAttr = aInputFlags.data();
mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
} else {
@@ -1770,6 +1825,8 @@ static AquaSalFrame* getMouseContainerFrame()
[mpLastMarkedText release];
mpLastMarkedText = nil;
}
+
+ mbTextInputWantsNonRepeatKeyDown = NO;
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
@@ -1780,17 +1837,66 @@ static AquaSalFrame* getMouseContainerFrame()
SolarMutexGuard aGuard;
+ // tdf#42437 Enable press-and-hold special character input method
+ // Some text entry controls, such as Writer comments or the cell editor in
+ // Calc's Formula Bar, need to have an input method session open or else
+ // the returned position won't be anywhere near the text cursor. So,
+ // dispatch an empty SalEvent::ExtTextInput event, fetch the position,
+ // and then dispatch a SalEvent::EndExtTextInput event.
+ BOOL bNeedsExtTextInput = ( mbInKeyInput && !mpLastMarkedText && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && [mpLastEvent isARepeat] );
+ if ( bNeedsExtTextInput )
+ {
+ SalExtTextInputEvent aInputEvent;
+ aInputEvent.maText.clear();
+ aInputEvent.mnCursorPos = 0;
+ aInputEvent.mnCursorFlags = 0;
+ aInputEvent.mpTextAttr = nullptr;
+ if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
+ }
+
SalExtTextInputPosEvent aPosEvent;
- mpFrame->CallCallback( SalEvent::ExtTextInputPos, static_cast<void *>(&aPosEvent) );
+ if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->CallCallback( SalEvent::ExtTextInputPos, static_cast<void *>(&aPosEvent) );
+
+ if ( bNeedsExtTextInput )
+ {
+ if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
+
+ // tdf#42437 Enable press-and-hold special character input method
+ // Emulate the press-and-hold behavior of the TextEdit application by
+ // setting the marked text to the last key down event's characters. The
+ // characters will already have been committed by the special character
+ // input method so set the mbTextInputWantsNonRepeatKeyDown flag to
+ // indicate that the characters need to be deleted if the input method
+ // replaces the committed characters.
+ NSString *pChars = [mpLastEvent characters];
+ if ( pChars )
+ {
+ [self unmarkText];
+ mpLastMarkedText = [[NSAttributedString alloc] initWithString:pChars];
+ mSelectedRange = mMarkedRange = NSMakeRange( 0, [mpLastMarkedText length] );
+ mbTextInputWantsNonRepeatKeyDown = YES;
+ }
+ }
NSRect rect;
- rect.origin.x = aPosEvent.mnX + mpFrame->maGeometry.x();
- rect.origin.y = aPosEvent.mnY + mpFrame->maGeometry.y() + 4; // add some space for underlines
- rect.size.width = aPosEvent.mnWidth;
- rect.size.height = aPosEvent.mnHeight;
+ if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
+ {
+ rect.origin.x = aPosEvent.mnX + mpFrame->maGeometry.x();
+ rect.origin.y = aPosEvent.mnY + mpFrame->maGeometry.y() + 4; // add some space for underlines
+ rect.size.width = aPosEvent.mnWidth;
+ rect.size.height = aPosEvent.mnHeight;
+
+ mpFrame->VCLToCocoa( rect );
+ }
+ else
+ {
+ rect = NSMakeRect( aPosEvent.mnX, aPosEvent.mnY, aPosEvent.mnWidth, aPosEvent.mnHeight );
+ }
- mpFrame->VCLToCocoa( rect );
return rect;
}
@@ -1903,9 +2009,17 @@ static AquaSalFrame* getMouseContainerFrame()
// to be visible.
mbInCommitMarkedText = YES;
if (nFlags & EndExtTextInputFlags::Complete)
- [self insertText:mpLastMarkedText replacementRange:NSMakeRange(0, [mpLastMarkedText length])];
+ {
+ // Retain the last marked text as it will be releasd in
+ // [self insertText:replacementText:]
+ NSAttributedString *pText = [mpLastMarkedText retain];
+ [self insertText:pText replacementRange:NSMakeRange(0, [mpLastMarkedText length])];
+ [pText release];
+ }
else
+ {
[self insertText:[NSString string] replacementRange:NSMakeRange(0, 0)];
+ }
mbInCommitMarkedText = NO;
}
@@ -1927,6 +2041,33 @@ static AquaSalFrame* getMouseContainerFrame()
mbInEndExtTextInput = NO;
}
+-(void)deleteTextInputWantsNonRepeatKeyDown
+{
+ SolarMutexGuard aGuard;
+
+ // tdf#42437 Enable press-and-hold special character input method
+ // Emulate the press-and-hold behavior of the TextEdit application by
+ // dispatching backspace events to delete any marked characters. The
+ // special character input method commits the marked characters so we must
+ // delete the marked characters before the input method calls
+ // [self insertText:replacementRange:].
+ if (mbTextInputWantsNonRepeatKeyDown)
+ {
+ if ( mpLastMarkedText )
+ {
+ NSString *pChars = [mpLastMarkedText string];
+ if ( pChars )
+ {
+ NSUInteger nLength = [pChars length];
+ for ( NSUInteger i = 0; i < nLength; i++ )
+ [self deleteBackward:self];
+ }
+ }
+
+ [self unmarkText];
+ }
+}
+
@end
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */