From 4a490c57151dd4ba9cb27cc34a0e33fc68fc4d24 Mon Sep 17 00:00:00 2001 From: Joe Wreschnig Date: Sun, 3 Mar 2013 15:24:36 +0100 Subject: [PATCH] Rewrite KeyInputTextView into NJKeyInputField. New class communicates using a proper delegate, standard property names, fixes more bugs in UI edge cases, allow Alt+Escape to clear the field, support modified virtual keys usefully. --- English.lproj/MainMenu.xib | 54 +++++-------- Enjoyable.xcodeproj/project.pbxproj | 12 +-- KeyInputTextView.h | 24 ------ NJKeyInputField.h | 45 +++++++++++ KeyInputTextView.m => NJKeyInputField.m | 100 ++++++++++++------------ TargetController.h | 8 +- TargetController.m | 17 ++-- TargetKeyboard.m | 2 +- 8 files changed, 133 insertions(+), 129 deletions(-) delete mode 100644 KeyInputTextView.h create mode 100644 NJKeyInputField.h rename KeyInputTextView.m => NJKeyInputField.m (71%) diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index 6797fb3..6e87fb0 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -632,7 +632,6 @@ {{227, 55}, {180, 24}} - _NS:9 YES @@ -700,7 +699,7 @@ _NS:9 - KeyInputTextView + NJKeyInputField @@ -1114,7 +1113,6 @@ {{57, 4}, {39, 28}} - YES 603979776 @@ -1586,19 +1584,11 @@ - targetController + keyDelegate - 779 - - - - window - - - - 780 + 818 @@ -2270,7 +2260,7 @@ - 816 + 818 @@ -2365,16 +2355,11 @@ - NSButton NSButton NSTableView TargetController - - exportButton - NSButton - removeButton NSButton @@ -2421,25 +2406,22 @@ - KeyInputTextView + NJKeyInputField NSTextField - - TargetController - NSWindow - - - - targetController - TargetController - - - window - NSWindow + + keyDelegate + id + + + keyDelegate + + keyDelegate + id - + IBProjectSource - ./Classes/KeyInputTextView.h + ./Classes/NJKeyInputField.h @@ -2478,7 +2460,7 @@ NSPopUpButton ConfigsController JoystickController - KeyInputTextView + NJKeyInputField NSSegmentedControl NSSegmentedControl NSMatrix @@ -2500,7 +2482,7 @@ keyInput - KeyInputTextView + NJKeyInputField mouseBtnSelect diff --git a/Enjoyable.xcodeproj/project.pbxproj b/Enjoyable.xcodeproj/project.pbxproj index f07f6d7..e75f56c 100644 --- a/Enjoyable.xcodeproj/project.pbxproj +++ b/Enjoyable.xcodeproj/project.pbxproj @@ -24,7 +24,7 @@ D5617FD60FAFD06000928B3A /* Target.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617FD50FAFD06000928B3A /* Target.m */; }; D5617FD90FAFD1E600928B3A /* TargetKeyboard.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617FD80FAFD1E600928B3A /* TargetKeyboard.m */; }; D5617FE40FAFD7B000928B3A /* TargetController.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617FE30FAFD7B000928B3A /* TargetController.m */; }; - D5617FE70FAFDB5800928B3A /* KeyInputTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617FE60FAFDB5800928B3A /* KeyInputTextView.m */; }; + D5617FE70FAFDB5800928B3A /* NJKeyInputField.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617FE60FAFDB5800928B3A /* NJKeyInputField.m */; }; D594BE860FAE6219007A85F2 /* Joystick.m in Sources */ = {isa = PBXBuildFile; fileRef = D594BE850FAE6219007A85F2 /* Joystick.m */; }; D594BE8A0FAE64AD007A85F2 /* JSAction.m in Sources */ = {isa = PBXBuildFile; fileRef = D594BE890FAE64AD007A85F2 /* JSAction.m */; }; D594BEF90FAE6FF2007A85F2 /* JoystickController.m in Sources */ = {isa = PBXBuildFile; fileRef = D594BEF80FAE6FF2007A85F2 /* JoystickController.m */; }; @@ -72,8 +72,8 @@ D5617FD80FAFD1E600928B3A /* TargetKeyboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TargetKeyboard.m; sourceTree = ""; }; D5617FE20FAFD7B000928B3A /* TargetController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TargetController.h; sourceTree = ""; }; D5617FE30FAFD7B000928B3A /* TargetController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TargetController.m; sourceTree = ""; }; - D5617FE50FAFDB5800928B3A /* KeyInputTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyInputTextView.h; sourceTree = ""; }; - D5617FE60FAFDB5800928B3A /* KeyInputTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeyInputTextView.m; sourceTree = ""; }; + D5617FE50FAFDB5800928B3A /* NJKeyInputField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJKeyInputField.h; sourceTree = ""; }; + D5617FE60FAFDB5800928B3A /* NJKeyInputField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NJKeyInputField.m; sourceTree = ""; }; D594BE840FAE6219007A85F2 /* Joystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Joystick.h; sourceTree = ""; }; D594BE850FAE6219007A85F2 /* Joystick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Joystick.m; sourceTree = ""; }; D594BE880FAE64AD007A85F2 /* JSAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSAction.h; sourceTree = ""; }; @@ -135,8 +135,8 @@ D5617FD80FAFD1E600928B3A /* TargetKeyboard.m */, D5617FE20FAFD7B000928B3A /* TargetController.h */, D5617FE30FAFD7B000928B3A /* TargetController.m */, - D5617FE50FAFDB5800928B3A /* KeyInputTextView.h */, - D5617FE60FAFDB5800928B3A /* KeyInputTextView.m */, + D5617FE50FAFDB5800928B3A /* NJKeyInputField.h */, + D5617FE60FAFDB5800928B3A /* NJKeyInputField.m */, 8BD9B54115C230FE00929C5D /* TargetMouseMove.h */, 8BD9B54215C230FE00929C5D /* TargetMouseMove.m */, 8B7E476A15C314A200C588FA /* TargetMouseBtn.h */, @@ -303,7 +303,7 @@ D5617FD60FAFD06000928B3A /* Target.m in Sources */, D5617FD90FAFD1E600928B3A /* TargetKeyboard.m in Sources */, D5617FE40FAFD7B000928B3A /* TargetController.m in Sources */, - D5617FE70FAFDB5800928B3A /* KeyInputTextView.m in Sources */, + D5617FE70FAFDB5800928B3A /* NJKeyInputField.m in Sources */, D5F809710FB093400006A4DE /* TargetConfig.m in Sources */, 8BD9B54315C230FF00929C5D /* TargetMouseMove.m in Sources */, 8B7E476C15C314A200C588FA /* TargetMouseBtn.m in Sources */, diff --git a/KeyInputTextView.h b/KeyInputTextView.h deleted file mode 100644 index 485af1c..0000000 --- a/KeyInputTextView.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// KeyInputTextView.h -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -@class TargetController; - -@interface KeyInputTextView : NSTextField { - IBOutlet NSWindow *window; - IBOutlet TargetController *targetController; -} - -@property (assign) int vk; -@property (readonly) BOOL hasKey; -@property (assign) BOOL enabled; - -+ (NSString *)stringForKeyCode:(int)keycode; - -- (void)clear; - -@end diff --git a/NJKeyInputField.h b/NJKeyInputField.h new file mode 100644 index 0000000..ad026d3 --- /dev/null +++ b/NJKeyInputField.h @@ -0,0 +1,45 @@ +// +// NJKeyInputField.h +// Enjoyable +// +// Copyright 2013 Joe Wreschnig. +// + +#import + +extern CGKeyCode NJKeyInputFieldEmpty; + +@protocol NJKeyInputFieldDelegate; + +@interface NJKeyInputField : NSTextField + // An NJKeyInputField is a NSTextField-like widget that receives + // exactly one key press, and displays the name of that key, then + // resigns its first responder status. It can also inform a + // special delegate when its content changes. + ++ (NSString *)stringForKeyCode:(CGKeyCode)keyCode; + // Give the string name for a virtual key code. + +@property (nonatomic, weak) IBOutlet id keyDelegate; + +@property (nonatomic, assign) CGKeyCode keyCode; + // The currently displayed key code, or NJKeyInputFieldEmpty if no + // key is active. Changing this will update the display but not + // inform the delegate. + +@property (nonatomic, readonly) BOOL hasKeyCode; + // True if any key is active, false otherwise. + +- (void)clear; + // Clear the currently active key and call the delegate. + +@end + +@protocol NJKeyInputFieldDelegate + +- (void)keyInputField:(NJKeyInputField *)keyInput + didChangeKey:(CGKeyCode)keyCode; +- (void)keyInputFieldDidClear:(NJKeyInputField *)keyInput; + +@end + diff --git a/KeyInputTextView.m b/NJKeyInputField.m similarity index 71% rename from KeyInputTextView.m rename to NJKeyInputField.m index 6c8e094..13aa138 100644 --- a/KeyInputTextView.m +++ b/NJKeyInputField.m @@ -1,18 +1,15 @@ // -// KeyInputTextView.m -// Enjoy +// NJKeyInputField.h +// Enjoyable // -// Created by Sam McCall on 5/05/09. +// Copyright 2013 Joe Wreschnig. // -#import "KeyInputTextView.h" +#import "NJKeyInputField.h" -#import "TargetController.h" +CGKeyCode NJKeyInputFieldEmpty = 0xFFFF; -@implementation KeyInputTextView { - int vk; - BOOL enabled; -} +@implementation NJKeyInputField - (id)initWithFrame:(NSRect)frameRect { if ((self = [super initWithFrame:frameRect])) { @@ -24,18 +21,18 @@ } - (void)clear { - self.vk = -1; - [targetController keyChanged]; + self.keyCode = NJKeyInputFieldEmpty; + [self.keyDelegate keyInputFieldDidClear:self]; [self resignIfFirstResponder]; } -- (BOOL)hasKey { - return self.vk >= 0; +- (BOOL)hasKeyCode { + return self.keyCode >= 0; } -+ (NSString *)stringForKeyCode:(int)keycode { - switch(keycode) { - case -1: return @""; ++ (NSString *)stringForKeyCode:(CGKeyCode)keyCode { + switch (keyCode) { + case 0xffff: return @""; case 0x7a: return @"F1"; case 0x78: return @"F2"; case 0x63: return @"F3"; @@ -73,15 +70,15 @@ case 0x18: return @"="; case 0x3f: return @"Fn"; - case 0x39: return @"Caps Lock"; + case 0x36: return @"Right Command"; + case 0x37: return @"Left Command"; case 0x38: return @"Left Shift"; - case 0x3b: return @"Left Control"; + case 0x39: return @"Caps Lock"; case 0x3a: return @"Left Option"; - case 0x37: return @"Left Command"; - case 0x36: return @"Right Command"; + case 0x3b: return @"Left Control"; + case 0x3c: return @"Right Shift"; case 0x3d: return @"Right Option"; case 0x3e: return @"Right Control"; - case 0x3c: return @"Right Shift"; case 0x73: return @"Home"; case 0x74: return @"Page Up"; @@ -152,12 +149,13 @@ case 0x7d: return @"Down"; case 0x7b: return @"Left"; case 0x7c: return @"Right"; + default: + return [[NSString alloc] initWithFormat:@"Key 0x%x", keyCode]; } - return [[NSString alloc] initWithFormat: @"Key 0x%x",keycode]; } - (BOOL)acceptsFirstResponder { - return self.enabled; + return self.isEnabled; } - (BOOL)becomeFirstResponder { @@ -170,44 +168,42 @@ return [super resignFirstResponder]; } -- (void)setVk:(int)key { - vk = key; - [self setStringValue:[KeyInputTextView stringForKeyCode:key]]; +- (void)setKeyCode:(CGKeyCode)keyCode { + _keyCode = keyCode; + self.stringValue = [NJKeyInputField stringForKeyCode:keyCode]; } -- (int)vk { - return vk; -} - -- (void)keyDown:(NSEvent *)evt { - if (!evt.isARepeat) { - self.vk = evt.keyCode; - [targetController keyChanged]; +- (void)keyDown:(NSEvent *)theEvent { + if (!theEvent.isARepeat) { + if ((theEvent.modifierFlags & NSAlternateKeyMask) + && theEvent.keyCode == 0x35) { + // Allow Alt+Escape to clear the field. + self.keyCode = NJKeyInputFieldEmpty; + [self.keyDelegate keyInputFieldDidClear:self]; + } else { + self.keyCode = theEvent.keyCode; + [self.keyDelegate keyInputField:self didChangeKey:_keyCode]; + } [self resignIfFirstResponder]; } } - (void)mouseDown:(NSEvent *)theEvent { - [targetController keyChanged]; - [self.window makeFirstResponder:self]; -} - -- (void)flagsChanged:(NSEvent *)evt { - self.vk = evt.keyCode; - [targetController keyChanged]; - [self resignIfFirstResponder]; + if (self.acceptsFirstResponder) + [self.window makeFirstResponder:self]; } -- (void)setEnabled:(BOOL)newEnabled { - enabled = newEnabled; - - if (!enabled) - [self resignIfFirstResponder]; -} - -- (BOOL)enabled { - return enabled; +- (void)flagsChanged:(NSEvent *)theEvent { + // Many keys are only available on MacBook keyboards by using the + // Fn modifier key (e.g. Fn+Left for Home), so delay processing + // modifiers until the up event is received in order to let the + // user type these virtual keys. However, there is no actual event + // for modifier key up - so detect it by checking to see if any + // modifiers are still down. + if (!(theEvent.modifierFlags & NSDeviceIndependentModifierFlagsMask)) { + self.keyCode = theEvent.keyCode; + [self.keyDelegate keyInputField:self didChangeKey:_keyCode]; + } } - @end diff --git a/TargetController.h b/TargetController.h index 9c9ee2c..9c03c08 100644 --- a/TargetController.h +++ b/TargetController.h @@ -6,14 +6,15 @@ // Copyright 2009 University of Otago. All rights reserved. // -@class KeyInputTextView; +#import "NJKeyInputField.h" + @class ConfigsController; @class JoystickController; @class Target; @class TargetMouseMove; -@interface TargetController : NSObject { - IBOutlet KeyInputTextView *keyInput; +@interface TargetController : NSObject { + IBOutlet NJKeyInputField *keyInput; IBOutlet NSMatrix *radioButtons; IBOutlet NSSegmentedControl *mouseDirSelect; IBOutlet NSSegmentedControl *mouseBtnSelect; @@ -26,7 +27,6 @@ @property (assign) BOOL enabled; -- (void)keyChanged; - (void)loadCurrent; - (void)refreshConfigs; - (IBAction)configChosen:(id)sender; diff --git a/TargetController.m b/TargetController.m index 1f285ec..48119c2 100644 --- a/TargetController.m +++ b/TargetController.m @@ -11,7 +11,7 @@ #import "Config.h" #import "JSAction.h" #import "JoystickController.h" -#import "KeyInputTextView.h" +#import "NJKeyInputField.h" #import "TargetConfig.h" #import "TargetController.h" #import "TargetKeyboard.h" @@ -26,7 +26,7 @@ NSInteger row = radioButtons.selectedRow; if (row != 1) { - keyInput.vk = -1; + keyInput.keyCode = -1; [keyInput resignIfFirstResponder]; } @@ -62,12 +62,17 @@ [self commit]; } -- (void)keyChanged { +- (void)keyInputField:(NJKeyInputField *)keyInput didChangeKey:(CGKeyCode)keyCode { [radioButtons selectCellAtRow:1 column:0]; [radioButtons.window makeFirstResponder:radioButtons]; [self commit]; } +- (void)keyInputFieldDidClear:(NJKeyInputField *)keyInput { + [radioButtons selectCellAtRow:0 column:0]; + [self commit]; +} + - (void)configChosen:(id)sender { [radioButtons selectCellAtRow:2 column:0]; [configPopup.window makeFirstResponder:configPopup]; @@ -101,9 +106,9 @@ case 0: return nil; case 1: - if (keyInput.hasKey) { + if (keyInput.hasKeyCode) { TargetKeyboard *k = [[TargetKeyboard alloc] init]; - k.vk = keyInput.vk; + k.vk = keyInput.keyCode; return k; } else { return nil; @@ -172,7 +177,7 @@ if ([target isKindOfClass:TargetKeyboard.class]) { [radioButtons selectCellAtRow:1 column:0]; - keyInput.vk = [(TargetKeyboard*)target vk]; + keyInput.keyCode = [(TargetKeyboard*)target vk]; } else if ([target isKindOfClass:TargetConfig.class]) { [radioButtons selectCellAtRow:2 column:0]; NSUInteger idx = [configsController.configs diff --git a/TargetKeyboard.m b/TargetKeyboard.m index a3a0b51..eab2d4e 100644 --- a/TargetKeyboard.m +++ b/TargetKeyboard.m @@ -7,7 +7,7 @@ #import "TargetKeyboard.h" -#import "KeyInputTextView.h" +#import "NJKeyInputField.h" @implementation TargetKeyboard -- 2.30.2