From 3107c9548d7911d9424ab589fd2ab8ca8043a84a Mon Sep 17 00:00:00 2001 From: Mario Dorn Date: Fri, 13 Nov 2015 17:19:20 +0100 Subject: [PATCH] Implement NSTextInputClient protocol on OS X This provides support for IME character composition. Fixes #456. Closes #643. --- README.md | 2 + src/cocoa_window.m | 107 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 95 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cf15b75b..2f047947 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ does not find Doxygen, the documentation will not be generated. - [Cocoa] Bugfix: Full screen windows on secondary monitors were mispositioned - [Cocoa] Bugfix: Connecting a joystick that reports no name would segfault - [Cocoa] Bugfix: Modifier flags cache was not updated when window became key + - [Cocoa] Bugfix: Dead key character composition did not work - [X11] Bugfix: Monitor connection and disconnection events were not reported - [X11] Bugfix: Decoding of UTF-8 text from XIM could continue past the end - [X11] Bugfix: An XKB structure was leaked during `glfwInit` @@ -165,6 +166,7 @@ skills. - Paul R. Deppe - Michael Dickens - Роман Донченко + - Mario Dorn - Jonathan Dummer - Ralph Eastwood - Siavash Eliasi diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 1a7662b1..f9c9c3da 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -146,6 +146,10 @@ static NSUInteger translateKeyToModifierFlag(int key) return 0; } +// Defines a constant for empty ranges in NSTextInputClient +// +static const NSRange kEmptyRange = {NSNotFound, 0}; + //------------------------------------------------------------------------ // Delegate for window related notifications @@ -296,10 +300,11 @@ static NSUInteger translateKeyToModifierFlag(int key) // Content view class for the GLFW window //------------------------------------------------------------------------ -@interface GLFWContentView : NSView +@interface GLFWContentView : NSView { _GLFWwindow* window; NSTrackingArea* trackingArea; + NSMutableAttributedString* markedText; } - (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; @@ -329,6 +334,7 @@ static NSUInteger translateKeyToModifierFlag(int key) { window = initWindow; trackingArea = nil; + markedText = [[NSMutableAttributedString alloc] init]; [self updateTrackingAreas]; [self registerForDraggedTypes:[NSArray arrayWithObjects: @@ -338,9 +344,10 @@ static NSUInteger translateKeyToModifierFlag(int key) return self; } --(void)dealloc +- (void)dealloc { [trackingArea release]; + [markedText release]; [super dealloc]; } @@ -501,18 +508,7 @@ static NSUInteger translateKeyToModifierFlag(int key) _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); - NSString* characters = [event characters]; - NSUInteger i, length = [characters length]; - const int plain = !(mods & GLFW_MOD_SUPER); - - for (i = 0; i < length; i++) - { - const unichar codepoint = [characters characterAtIndex:i]; - if ((codepoint & 0xff00) == 0xf700) - continue; - - _glfwInputChar(window, codepoint, mods, plain); - } + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } - (void)flagsChanged:(NSEvent *)event @@ -614,6 +610,89 @@ static NSUInteger translateKeyToModifierFlag(int key) [self setNeedsDisplay:YES]; } +- (BOOL)hasMarkedText +{ + return (markedText.length > 0); +} + +- (NSRange)markedRange +{ + return (markedText.length > 0) ? + NSMakeRange(0, markedText.length-1) : + kEmptyRange; +} + +- (NSRange)selectedRange +{ + return kEmptyRange; +} + +- (void)setMarkedText:(id)aString + selectedRange:(NSRange)selectedRange + replacementRange:(NSRange)replacementRange +{ + if( [aString isKindOfClass: [NSAttributedString class]] ) + { + [markedText initWithAttributedString: aString]; + } + else + { + [markedText initWithString: aString]; + } +} + +- (void)unmarkText +{ + [[markedText mutableString] setString:@""]; +} + +- (NSArray*)validAttributesForMarkedText +{ + return [NSArray array]; +} + +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange + actualRange:(NSRangePointer)actualRange +{ + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)aRange + actualRange:(NSRangePointer)actualRange +{ + return NSMakeRect(0, 0, 0, 0); +} + +- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange +{ + NSEvent* event = [NSApp currentEvent]; + const int mods = translateFlags([event modifierFlags]); + + NSString* characters; + if ([aString isKindOfClass: [NSAttributedString class]]) { + characters = [aString string]; + } else { + characters = (NSString*)aString; + } + + NSUInteger i, length = [characters length]; + const int plain = !(mods & GLFW_MOD_SUPER); + + for (i = 0; i < length; i++) + { + const unichar codepoint = [characters characterAtIndex:i]; + if ((codepoint & 0xff00) == 0xf700) + continue; + + _glfwInputChar(window, codepoint, mods, plain); + } +} + @end