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.
This commit is contained in:
Marco Antognini 2013-06-27 22:29:43 +02:00
parent ef8391e507
commit 513cd7e57c
5 changed files with 151 additions and 47 deletions

View File

@ -83,7 +83,9 @@ else() # MACOSX
${SRCROOT}/OSX/SFContext.hpp ${SRCROOT}/OSX/SFContext.hpp
${SRCROOT}/OSX/SFContext.mm ${SRCROOT}/OSX/SFContext.mm
${SRCROOT}/OSX/SFOpenGLView.h ${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.h
${SRCROOT}/OSX/SFWindow.m ${SRCROOT}/OSX/SFWindow.m
${SRCROOT}/OSX/SFWindowController.h ${SRCROOT}/OSX/SFWindowController.h

View File

@ -34,8 +34,10 @@ namespace sf {
} }
} }
@class SFSilentResponder;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Spesialized NSOpenGLView /// \brief Specialized NSOpenGLView
/// ///
/// Handle event and send them back to the requester. /// Handle event and send them back to the requester.
/// ///
@ -64,6 +66,11 @@ namespace sf {
BOOL m_rightAlternateWasDown; BOOL m_rightAlternateWasDown;
BOOL m_leftAlternateWasDown; BOOL m_leftAlternateWasDown;
BOOL m_controlWasDown; 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;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -31,6 +31,7 @@
#include <SFML/System/Err.hpp> #include <SFML/System/Err.hpp>
#import <SFML/Window/OSX/SFOpenGLView.h> #import <SFML/Window/OSX/SFOpenGLView.h>
#import <SFML/Window/OSX/SFSilentResponder.h>
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// Here are define the mask value for the 'modifiers' keys (cmd, ctrl, alt, shift) /// 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); 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 /// SFOpenGLView class : Privates Methods Declaration
/// ///
@ -131,6 +141,11 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask);
selector:@selector(frameDidChange:) selector:@selector(frameDidChange:)
name:NSViewFrameDidChangeNotification name:NSViewFrameDidChangeNotification
object:self]; 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; return self;
@ -272,6 +287,10 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask);
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
-(void)dealloc -(void)dealloc
{ {
// Release attributes
[m_hiddenTextView release];
[m_silentResponder release];
// Unregister // Unregister
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
[self removeTrackingRect:m_trackingTag]; [self removeTrackingRect:m_trackingTag];
@ -538,61 +557,46 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask);
} }
// Handle text entred event // Handle text entred event:
// We create a new event without command/ctrl modifiers // Ignore event if we don't want repeated keystrokes
// to prevent the OS from sending an alert if (m_useKeyRepeat || ![theEvent isARepeat]) {
NSUInteger modifiers = [theEvent modifierFlags]; // Ignore escape key and other non text keycode (See NSEvent.h)
// because they produce a sound alert.
if (modifiers & NSCommandKeyMask) modifiers = modifiers & ~NSCommandKeyMask; if (isValidTextUnicode(theEvent)) {
if (modifiers & NSControlKeyMask) modifiers = modifiers & ~NSControlKeyMask; // Send the event to the hidden text view for processing
[m_hiddenTextView interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
NSEvent* ev = [NSEvent keyEventWithType:NSKeyDown }
location:[theEvent locationInWindow]
modifierFlags:modifiers // Carefully handle backspace and delete..
timestamp:[theEvent timestamp] // Note: the event is intentionally sent to the hidden view
windowNumber:[theEvent windowNumber] // even if we do something more specific below. This way
context:[theEvent context] // key combination are correctly interpreted.
characters:[theEvent characters]
charactersIgnoringModifiers:[theEvent charactersIgnoringModifiers] unsigned short keycode = [theEvent keyCode];
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 :
// Backspace // Backspace
if (keycode == 0x33) { if (keycode == 0x33) {
// Send the correct unicode value (i.e. 8) instead of 127 (which is 'delete') // Send the correct unicode value (i.e. 8) instead of 127 (which is 'delete')
m_requester->textEntered(8); m_requester->textEntered(8);
} }
// Delete // Delete
else if (keycode == 0x75 || keycode == NSDeleteFunctionKey) { else if (keycode == 0x75 || keycode == NSDeleteFunctionKey) {
// Instead of the value 63272 we send 127. // Instead of the value 63272 we send 127.
m_requester->textEntered(127); m_requester->textEntered(127);
} }
// All other unicode values // Otherwise, let's see what our hidden field has computed
else if (keycode != 0x35 && (code < 0xF700 || code > 0xF8FF)) { else {
NSString* string = [m_hiddenTextView string];
// Let's see if its a valid text.
NSText* text = [[self window] fieldEditor:YES forObject:self]; // Send each character to SFML event requester
[text interpretKeyEvents:[NSArray arrayWithObject:ev]]; for (NSUInteger index = 0; index < [string length]; ++index) {
m_requester->textEntered([string characterAtIndex:index]);
NSString* string = [text string];
if ([string length] > 0) {
// It's a valid TextEntered event.
m_requester->textEntered([string characterAtIndex:0]);
[text setString:@""];
} }
// Empty our hidden cache
[m_hiddenTextView setString:@""];
} }
} }
} }
@ -1097,4 +1101,15 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask)
return eraseMaskFromData(data, negative); 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;
}
}

View File

@ -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 <AppKit/AppKit.h>
////////////////////////////////////////////////////////////
/// \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

View File

@ -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 <SFML/Window/OSX/SFSilentResponder.h>
@implementation SFSilentResponder
-(void)doCommandBySelector:(SEL)sel
{
// Just do nothing, to prevent sound alerts
}
@end