From 30a8c4bf4205bc87da6844539982d9e7cd0a8875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Ziad=C3=A9?= Date: Mon, 26 May 2025 20:28:08 +0300 Subject: [PATCH] Add Vim menu navigation --- SDL/gui.c | 13 ++++-- iOS/GBMenuViewController.h | 2 + iOS/GBMenuViewController.m | 91 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/SDL/gui.c b/SDL/gui.c index 9170ce9..cf03adc 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -87,6 +87,7 @@ void render_texture(void *pixels, void *previous) static const char *help[] = { "Keyboard Shortcuts:\n" " Open Menu: Escape\n" +" Menu Navigation: Arrow keys or hjkl\n" " Open ROM: " MODIFIER_NAME "+O\n" " Reset: " MODIFIER_NAME "+R\n" " Pause: " MODIFIER_NAME "+P\n" @@ -2316,6 +2317,10 @@ void run_gui(bool is_running) case SDL_SCANCODE_LEFT: case SDL_SCANCODE_UP: case SDL_SCANCODE_DOWN: + case SDL_SCANCODE_H: + case SDL_SCANCODE_J: + case SDL_SCANCODE_K: + case SDL_SCANCODE_L: break; default: @@ -2703,12 +2708,12 @@ void run_gui(bool is_running) } } else if (gui_state == SHOWING_MENU) { - if (event.key.keysym.scancode == SDL_SCANCODE_DOWN && current_menu[current_selection + 1].string) { + if ((event.key.keysym.scancode == SDL_SCANCODE_DOWN || event.key.keysym.scancode == SDL_SCANCODE_J) && current_menu[current_selection + 1].string) { current_selection++; mouse_scroling = false; should_render = true; } - else if (event.key.keysym.scancode == SDL_SCANCODE_UP && current_selection) { + else if ((event.key.keysym.scancode == SDL_SCANCODE_UP || event.key.keysym.scancode == SDL_SCANCODE_K) && current_selection) { current_selection--; mouse_scroling = false; should_render = true; @@ -2732,11 +2737,11 @@ void run_gui(bool is_running) return; } } - else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT && current_menu[current_selection].backwards_handler) { + else if ((event.key.keysym.scancode == SDL_SCANCODE_RIGHT || event.key.keysym.scancode == SDL_SCANCODE_L) && current_menu[current_selection].backwards_handler) { current_menu[current_selection].handler(current_selection); should_render = true; } - else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT && current_menu[current_selection].backwards_handler) { + else if ((event.key.keysym.scancode == SDL_SCANCODE_LEFT || event.key.keysym.scancode == SDL_SCANCODE_H) && current_menu[current_selection].backwards_handler) { current_menu[current_selection].backwards_handler(current_selection); should_render = true; } diff --git a/iOS/GBMenuViewController.h b/iOS/GBMenuViewController.h index 26b8253..d66a21c 100644 --- a/iOS/GBMenuViewController.h +++ b/iOS/GBMenuViewController.h @@ -2,4 +2,6 @@ @interface GBMenuViewController : UIAlertController + (instancetype)menu; +@property (nonatomic) NSInteger selectedButtonIndex; +@property (nonatomic, strong) NSArray *menuButtons; @end diff --git a/iOS/GBMenuViewController.m b/iOS/GBMenuViewController.m index ff28db2..d484fe6 100644 --- a/iOS/GBMenuViewController.m +++ b/iOS/GBMenuViewController.m @@ -29,6 +29,7 @@ static NSString *const tips[] = { { UILabel *_tipLabel; UIVisualEffectView *_effectView; + NSMutableArray *_buttons; } + (instancetype)menu @@ -41,6 +42,8 @@ static NSString *const tips[] = { [ret addAction:[UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleCancel handler:nil]]; + ret.selectedButtonIndex = 0; + ret->_buttons = [[NSMutableArray alloc] init]; return ret; } @@ -80,6 +83,7 @@ static NSString *const tips[] = { button.frame = CGRectMake(round(width * x), height * y, round(width), height); button.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; [self.view addSubview:button]; + [_buttons addObject:button]; if (!buttons[i].selector) { button.enabled = false; @@ -102,6 +106,9 @@ static NSString *const tips[] = { [button addTarget:block action:@selector(invoke) forControlEvents:UIControlEventTouchUpInside]; } + self.menuButtons = [_buttons copy]; + [self updateSelectedButton]; + _effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleProminent]]; _effectView.layer.cornerRadius = 8; _effectView.layer.masksToBounds = true; @@ -189,4 +196,88 @@ static NSString *const tips[] = { return [[UIViewController alloc] init]; } +#pragma mark - Vim Navigation + +- (BOOL)canBecomeFirstResponder +{ + return YES; +} + +- (NSArray *)keyCommands +{ + return @[ + [UIKeyCommand keyCommandWithInput:@"h" modifierFlags:0 action:@selector(moveLeft)], + [UIKeyCommand keyCommandWithInput:@"j" modifierFlags:0 action:@selector(moveDown)], + [UIKeyCommand keyCommandWithInput:@"k" modifierFlags:0 action:@selector(moveUp)], + [UIKeyCommand keyCommandWithInput:@"l" modifierFlags:0 action:@selector(moveRight)], + [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(activateSelected)], + [UIKeyCommand keyCommandWithInput:@" " modifierFlags:0 action:@selector(activateSelected)], + ]; +} + +- (void)moveLeft +{ + if (self.selectedButtonIndex % 4 > 0) { + self.selectedButtonIndex--; + [self updateSelectedButton]; + } +} + +- (void)moveRight +{ + if (self.selectedButtonIndex % 4 < 3 && self.selectedButtonIndex + 1 < self.menuButtons.count) { + self.selectedButtonIndex++; + [self updateSelectedButton]; + } +} + +- (void)moveUp +{ + if (self.selectedButtonIndex >= 4) { + self.selectedButtonIndex -= 4; + [self updateSelectedButton]; + } +} + +- (void)moveDown +{ + if (self.selectedButtonIndex + 4 < self.menuButtons.count) { + self.selectedButtonIndex += 4; + [self updateSelectedButton]; + } +} + +- (void)activateSelected +{ + if (self.selectedButtonIndex >= 0 && self.selectedButtonIndex < self.menuButtons.count) { + UIButton *button = self.menuButtons[self.selectedButtonIndex]; + if (button.enabled) { + [button sendActionsForControlEvents:UIControlEventTouchUpInside]; + } + } +} + +- (void)updateSelectedButton +{ + for (NSInteger i = 0; i < self.menuButtons.count; i++) { + UIButton *button = self.menuButtons[i]; + if (i == self.selectedButtonIndex) { + button.backgroundColor = [UIColor colorWithWhite:0.5 alpha:0.3]; + button.layer.borderWidth = 2.0; + button.layer.borderColor = [UIColor systemBlueColor].CGColor; + button.layer.cornerRadius = 8.0; + } else { + button.backgroundColor = [UIColor clearColor]; + button.layer.borderWidth = 0.0; + button.layer.borderColor = [UIColor clearColor].CGColor; + } + } +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + [self becomeFirstResponder]; +} + @end