From 513cd7e57c39553a46ce4131e8bac3e17812cdfb Mon Sep 17 00:00:00 2001 From: Marco Antognini Date: Thu, 27 Jun 2013 22:29:43 +0200 Subject: [PATCH] Improve TextEntered for OS X (close #377) Code from pull request #401 was not used because some methods used don't exist on 10.5 which is still supported by SFML 2.1. --- src/SFML/Window/CMakeLists.txt | 4 +- src/SFML/Window/OSX/SFOpenGLView.h | 9 +- src/SFML/Window/OSX/SFOpenGLView.mm | 105 ++++++++++++++---------- src/SFML/Window/OSX/SFSilentResponder.h | 41 +++++++++ src/SFML/Window/OSX/SFSilentResponder.m | 39 +++++++++ 5 files changed, 151 insertions(+), 47 deletions(-) create mode 100644 src/SFML/Window/OSX/SFSilentResponder.h create mode 100644 src/SFML/Window/OSX/SFSilentResponder.m diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index 67e17b9f..57294a83 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -83,7 +83,9 @@ else() # MACOSX ${SRCROOT}/OSX/SFContext.hpp ${SRCROOT}/OSX/SFContext.mm ${SRCROOT}/OSX/SFOpenGLView.h - ${SRCROOT}/OSX/SFOpenGLView.mm + ${SRCROOT}/OSX/SFOpenGLView.mm + ${SRCROOT}/OSX/SFSilentResponder.h + ${SRCROOT}/OSX/SFSilentResponder.m ${SRCROOT}/OSX/SFWindow.h ${SRCROOT}/OSX/SFWindow.m ${SRCROOT}/OSX/SFWindowController.h diff --git a/src/SFML/Window/OSX/SFOpenGLView.h b/src/SFML/Window/OSX/SFOpenGLView.h index ac8f3f58..a8fffdc8 100644 --- a/src/SFML/Window/OSX/SFOpenGLView.h +++ b/src/SFML/Window/OSX/SFOpenGLView.h @@ -34,8 +34,10 @@ namespace sf { } } +@class SFSilentResponder; + //////////////////////////////////////////////////////////// -/// \brief Spesialized NSOpenGLView +/// \brief Specialized NSOpenGLView /// /// Handle event and send them back to the requester. /// @@ -64,6 +66,11 @@ namespace sf { BOOL m_rightAlternateWasDown; BOOL m_leftAlternateWasDown; BOOL m_controlWasDown; + + // Hidden text view use to convert key event to actual chars. + // To prevent sound alert we use a silent responder. + SFSilentResponder* m_silentResponder; + NSTextView* m_hiddenTextView; } //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/OSX/SFOpenGLView.mm b/src/SFML/Window/OSX/SFOpenGLView.mm index 9922b52d..8364bda5 100644 --- a/src/SFML/Window/OSX/SFOpenGLView.mm +++ b/src/SFML/Window/OSX/SFOpenGLView.mm @@ -31,6 +31,7 @@ #include #import +#import //////////////////////////////////////////////////////////// /// Here are define the mask value for the 'modifiers' keys (cmd, ctrl, alt, shift) @@ -61,6 +62,15 @@ NSUInteger eraseMaskFromData(NSUInteger data, NSUInteger mask); //////////////////////////////////////////////////////////// NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask); +//////////////////////////////////////////////////////////// +/// Returns true if `event` represents a representable character. +/// +/// The event is assumed to be a key down event. +/// False is returned if the event is either escape or a non text unicode. +/// +//////////////////////////////////////////////////////////// +BOOL isValidTextUnicode(NSEvent* event); + //////////////////////////////////////////////////////////// /// SFOpenGLView class : Privates Methods Declaration /// @@ -131,6 +141,11 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask); selector:@selector(frameDidChange:) name:NSViewFrameDidChangeNotification object:self]; + + // Create a hidden text view for parsing key down event properly + m_silentResponder = [[SFSilentResponder alloc] init]; + m_hiddenTextView = [[NSTextView alloc] initWithFrame:NSZeroRect]; + [m_hiddenTextView setNextResponder:m_silentResponder]; } return self; @@ -272,6 +287,10 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask); //////////////////////////////////////////////////////// -(void)dealloc { + // Release attributes + [m_hiddenTextView release]; + [m_silentResponder release]; + // Unregister [[NSNotificationCenter defaultCenter] removeObserver:self]; [self removeTrackingRect:m_trackingTag]; @@ -538,61 +557,46 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask); } - // Handle text entred event - // We create a new event without command/ctrl modifiers - // to prevent the OS from sending an alert - NSUInteger modifiers = [theEvent modifierFlags]; - - if (modifiers & NSCommandKeyMask) modifiers = modifiers & ~NSCommandKeyMask; - if (modifiers & NSControlKeyMask) modifiers = modifiers & ~NSControlKeyMask; - - NSEvent* ev = [NSEvent keyEventWithType:NSKeyDown - location:[theEvent locationInWindow] - modifierFlags:modifiers - timestamp:[theEvent timestamp] - windowNumber:[theEvent windowNumber] - context:[theEvent context] - characters:[theEvent characters] - charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers] - isARepeat:[theEvent isARepeat] - keyCode:[theEvent keyCode]]; - - if ((m_useKeyRepeat || ![ev isARepeat]) && [[ev characters] length] > 0) { - - // Ignore escape key and non text keycode. (See NSEvent.h) - // They produce a sound alert. - unichar code = [[ev characters] characterAtIndex:0]; - unsigned short keycode = [ev keyCode]; - - // Backspace and Delete unicode values are badly handled by Apple. - // We do a small workaround here : - + // Handle text entred event: + // Ignore event if we don't want repeated keystrokes + if (m_useKeyRepeat || ![theEvent isARepeat]) { + // Ignore escape key and other non text keycode (See NSEvent.h) + // because they produce a sound alert. + if (isValidTextUnicode(theEvent)) { + // Send the event to the hidden text view for processing + [m_hiddenTextView interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; + } + + // Carefully handle backspace and delete.. + // Note: the event is intentionally sent to the hidden view + // even if we do something more specific below. This way + // key combination are correctly interpreted. + + unsigned short keycode = [theEvent keyCode]; + // Backspace if (keycode == 0x33) { // Send the correct unicode value (i.e. 8) instead of 127 (which is 'delete') m_requester->textEntered(8); - } - + } + // Delete else if (keycode == 0x75 || keycode == NSDeleteFunctionKey) { // Instead of the value 63272 we send 127. m_requester->textEntered(127); } - - // All other unicode values - else if (keycode != 0x35 && (code < 0xF700 || code > 0xF8FF)) { - - // Let's see if its a valid text. - NSText* text = [[self window] fieldEditor:YES forObject:self]; - [text interpretKeyEvents:[NSArray arrayWithObject:ev]]; - - NSString* string = [text string]; - if ([string length] > 0) { - // It's a valid TextEntered event. - m_requester->textEntered([string characterAtIndex:0]); - - [text setString:@""]; + + // Otherwise, let's see what our hidden field has computed + else { + NSString* string = [m_hiddenTextView string]; + + // Send each character to SFML event requester + for (NSUInteger index = 0; index < [string length]; ++index) { + m_requester->textEntered([string characterAtIndex:index]); } + + // Empty our hidden cache + [m_hiddenTextView setString:@""]; } } } @@ -1097,4 +1101,15 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask) return eraseMaskFromData(data, negative); } +BOOL isValidTextUnicode(NSEvent* event) +{ + if ([event keyCode] == 0x35) { // Escape + return false; + } else if ([[event characters] length] > 0) { + unichar code = [[event characters] characterAtIndex:0]; + return code < 0xF700 || code > 0xF8FF; + } else { + return true; + } +} diff --git a/src/SFML/Window/OSX/SFSilentResponder.h b/src/SFML/Window/OSX/SFSilentResponder.h new file mode 100644 index 00000000..0890c920 --- /dev/null +++ b/src/SFML/Window/OSX/SFSilentResponder.h @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2012 Marco Antognini (antognini.marco@gmail.com), +// Laurent Gomila (laurent.gom@gmail.com), +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#import + +//////////////////////////////////////////////////////////// +/// \brief Silent Responder used to prevent sound alert with key event +/// +/// Mainly used by SFOpenGLView and its hidden text view. +//////////////////////////////////////////////////////////// +@interface SFSilentResponder : NSResponder + +-(void)doCommandBySelector:(SEL)sel; + +@end + diff --git a/src/SFML/Window/OSX/SFSilentResponder.m b/src/SFML/Window/OSX/SFSilentResponder.m new file mode 100644 index 00000000..0fb49f65 --- /dev/null +++ b/src/SFML/Window/OSX/SFSilentResponder.m @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2012 Marco Antognini (antognini.marco@gmail.com), +// Laurent Gomila (laurent.gom@gmail.com), +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#import + +@implementation SFSilentResponder + +-(void)doCommandBySelector:(SEL)sel +{ + // Just do nothing, to prevent sound alerts +} + +@end +