X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=blobdiff_plain;f=Classes%2FEnjoyableApplicationDelegate.m;h=aaa9f00e5118ce373963795958b5445d20d7682b;hp=78b930b80f1f3a4350f8624e951e5e13bc637b8f;hb=22efc1e5000d462091f3156ee5a8959be32cfb11;hpb=810b7040cf03299b51a793c67c220907973e10a3 diff --git a/Classes/EnjoyableApplicationDelegate.m b/Classes/EnjoyableApplicationDelegate.m index 78b930b..aaa9f00 100644 --- a/Classes/EnjoyableApplicationDelegate.m +++ b/Classes/EnjoyableApplicationDelegate.m @@ -13,7 +13,9 @@ #import "NJOutputController.h" #import "NJEvents.h" -@implementation EnjoyableApplicationDelegate +@implementation EnjoyableApplicationDelegate { + NSStatusItem *statusItem; +} - (void)didSwitchApplication:(NSNotification *)note { NSRunningApplication *activeApp = note.userInfo[NSWorkspaceApplicationKey]; @@ -21,7 +23,7 @@ [self.mappingsController activateMappingForProcess:activeApp]; } -- (void)applicationDidFinishLaunching:(NSNotification *)notification { +- (void)applicationWillFinishLaunching:(NSNotification *)notification { [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(mappingDidChange:) @@ -43,60 +45,131 @@ name:NJEventTranslationDeactivated object:nil]; - [self.inputController setup]; [self.mappingsController load]; + + statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:36]; + statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; + statusItem.highlightMode = YES; + statusItem.menu = statusItemMenu; + statusItem.target = self; } -- (void)applicationDidBecomeActive:(NSNotification *)notification { +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + [self.inputController setup]; [window makeKeyAndOrderFront:nil]; } - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag { - [window makeKeyAndOrderFront:nil]; + [self restoreToForeground:theApplication]; + return NO; +} + +- (void)restoreToForeground:(id)sender { + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + [NSApplication.sharedApplication activateIgnoringOtherApps:YES]; + [window makeKeyAndOrderFront:sender]; + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(transformIntoElement:) + object:self]; +} + +- (void)transformIntoElement:(id)sender { + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToUIElementApplication); +} + +- (void)flashStatusItem { + if ([statusItem.image.name isEqualToString:@"Status Menu Icon"]) { + statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; + } else { + statusItem.image = [NSImage imageNamed:@"Status Menu Icon"]; + } + +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + [theApplication hide:theApplication]; + // If we turn into a UIElement right away, the application cancels + // the deactivation events. The dock icon disappears, but an + // unresponsive menu bar remains until the user clicks somewhere. + // So delay just long enough to be past the end handling that. + [self performSelector:@selector(transformIntoElement:) withObject:self afterDelay:0.001]; return NO; } - (void)eventTranslationActivated:(NSNotification *)note { - [NSProcessInfo.processInfo disableAutomaticTermination:@"Input translation is active."]; + [dockMenu itemAtIndex:0].state = NSOnState; + [statusItemMenu itemAtIndex:0].state = NSOnState; + statusItem.image = [NSImage imageNamed:@"Status Menu Icon"]; [NSWorkspace.sharedWorkspace.notificationCenter addObserver:self selector:@selector(didSwitchApplication:) name:NSWorkspaceDidActivateApplicationNotification object:nil]; - NSLog(@"Listening for application changes."); } - (void)eventTranslationDeactivated:(NSNotification *)note { - [NSProcessInfo.processInfo enableAutomaticTermination:@"Input translation is active."]; + [dockMenu itemAtIndex:0].state = NSOffState; + [statusItemMenu itemAtIndex:0].state = NSOffState; + statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; [NSWorkspace.sharedWorkspace.notificationCenter removeObserver:self name:NSWorkspaceDidActivateApplicationNotification object:nil]; - NSLog(@"Ignoring application changes."); } -- (void)mappingListDidChange:(NSNotification *)note { - NSArray *mappings = note.object; - while (dockMenuBase.lastItem.representedObject) - [dockMenuBase removeLastItem]; +- (void)restoreWindowAndShowMappings:(id)sender { + [self restoreToForeground:sender]; + [self.mappingsController mappingPressed:sender]; +} + +- (void)addMappingsToMenu:(NSMenu *)menu withKeys:(BOOL)withKeys atIndex:(NSInteger)index { + static const NSUInteger MAXIMUM_ITEMS = 5; int added = 0; - for (NJMapping *mapping in mappings) { - NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; + for (NJMapping *mapping in self.mappingsController) { + NSString *keyEquiv = (++added < 10 && withKeys) ? @(added).stringValue : @""; NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name action:@selector(chooseMapping:) keyEquivalent:keyEquiv]; item.representedObject = mapping; item.state = mapping == self.mappingsController.currentMapping; - [dockMenuBase addItem:item]; - } + [menu insertItem:item atIndex:index++]; + if (added == MAXIMUM_ITEMS && self.mappingsController.mappings.count > MAXIMUM_ITEMS + 1) { + NSMenuItem *end = [[NSMenuItem alloc] initWithTitle:@"…" + action:@selector(restoreWindowAndShowMappings:) + keyEquivalent:@""]; + end.target = self; + [menu insertItem:end atIndex:index++]; + break; + } + } +} + +- (void)mappingListDidChange:(NSNotification *)note { + while (mappingsMenu.lastItem.representedObject) + [mappingsMenu removeLastItem]; + [self addMappingsToMenu:mappingsMenu withKeys:YES atIndex:mappingsMenu.numberOfItems]; + while ([statusItemMenu itemAtIndex:2].representedObject) + [statusItemMenu removeItemAtIndex:2]; + [self addMappingsToMenu:statusItemMenu withKeys:NO atIndex:2]; } - (void)mappingDidChange:(NSNotification *)note { NJMapping *current = note.object; - for (NSMenuItem *item in dockMenuBase.itemArray) + for (NSMenuItem *item in mappingsMenu.itemArray) + if (item.representedObject) + item.state = item.representedObject == current; + for (NSMenuItem *item in statusItemMenu.itemArray) if (item.representedObject) item.state = item.representedObject == current; + + if (!window.isVisible) + for (int i = 0; i < 4; ++i) + [self performSelector:@selector(flashStatusItem) + withObject:self + afterDelay:0.2 * i]; } - (void)chooseMapping:(NSMenuItem *)sender { @@ -105,21 +178,14 @@ } - (NSMenu *)applicationDockMenu:(NSApplication *)sender { - NSMenu *menu = [[NSMenu alloc] init]; - int added = 0; - for (NJMapping *mapping in self.mappingsController) { - NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name - action:@selector(chooseMapping:) - keyEquivalent:keyEquiv]; - item.representedObject = mapping; - item.state = mapping == self.mappingsController.currentMapping; - [menu addItem:item]; - } - return menu; + while (dockMenu.lastItem.representedObject) + [dockMenu removeLastItem]; + [self addMappingsToMenu:dockMenu withKeys:NO atIndex:dockMenu.numberOfItems]; + return dockMenu; } - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { + [self restoreToForeground:sender]; NSURL *url = [NSURL fileURLWithPath:filename]; [self.mappingsController addMappingWithContentsOfURL:url]; return YES;