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

@ -84,6 +84,8 @@ else() # MACOSX
${SRCROOT}/OSX/SFContext.mm
${SRCROOT}/OSX/SFOpenGLView.h
${SRCROOT}/OSX/SFOpenGLView.mm
${SRCROOT}/OSX/SFSilentResponder.h
${SRCROOT}/OSX/SFSilentResponder.m
${SRCROOT}/OSX/SFWindow.h
${SRCROOT}/OSX/SFWindow.m
${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.
///
@ -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;
}
////////////////////////////////////////////////////////////

View File

@ -31,6 +31,7 @@
#include <SFML/System/Err.hpp>
#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)
@ -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,34 +557,22 @@ 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];
// 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]];
}
if (modifiers & NSCommandKeyMask) modifiers = modifiers & ~NSCommandKeyMask;
if (modifiers & NSControlKeyMask) modifiers = modifiers & ~NSControlKeyMask;
// 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.
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 :
unsigned short keycode = [theEvent keyCode];
// Backspace
if (keycode == 0x33) {
@ -579,20 +586,17 @@ NSUInteger keepOnlyMaskFromData(NSUInteger data, NSUInteger mask);
m_requester->textEntered(127);
}
// All other unicode values
else if (keycode != 0x35 && (code < 0xF700 || code > 0xF8FF)) {
// Otherwise, let's see what our hidden field has computed
else {
NSString* string = [m_hiddenTextView string];
// 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:@""];
// 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;
}
}

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