diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-01-05 08:40:53 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-01-05 08:40:53 +0200 |
commit | 32cbf877ac137b28eb41a9084717707907c02d94 (patch) | |
tree | db971de78c1869a2c3a713080aa6222003e946c6 | |
parent | 31312b7a2e51a87a41bf777b6af135329927460d (diff) |
Handling keys while navigating via home row
When navigating via home row, ensure that other widgets will not trigger when keys are pressed. The document is given keyboard focus even though it is not flagged as focusable, to make sure it gets to handle all the keys first.
On macOS, disable all native menu items that have a home row keyboard shortcut.
Also on macOS, disable all native menus when changing bindings.
IssueID #419
-rw-r--r-- | src/macos.h | 2 | ||||
-rw-r--r-- | src/macos.m | 49 | ||||
-rw-r--r-- | src/ui/bindingswidget.c | 6 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 20 | ||||
-rw-r--r-- | src/ui/widget.c | 10 | ||||
-rw-r--r-- | src/ui/widget.h | 3 |
6 files changed, 82 insertions, 8 deletions
diff --git a/src/macos.h b/src/macos.h index 22a6dfff..10cbba81 100644 --- a/src/macos.h +++ b/src/macos.h | |||
@@ -39,8 +39,10 @@ void hideTitleBar_MacOS (iWindow *window); | |||
39 | void insertMenuItems_MacOS (const char *menuLabel, int atIndex, const iMenuItem *items, size_t count); | 39 | void insertMenuItems_MacOS (const char *menuLabel, int atIndex, const iMenuItem *items, size_t count); |
40 | void removeMenu_MacOS (int atIndex); | 40 | void removeMenu_MacOS (int atIndex); |
41 | void enableMenu_MacOS (const char *menuLabel, iBool enable); | 41 | void enableMenu_MacOS (const char *menuLabel, iBool enable); |
42 | void enableMenuIndex_MacOS (int index, iBool enable); | ||
42 | void enableMenuItem_MacOS (const char *menuItemCommand, iBool enable); | 43 | void enableMenuItem_MacOS (const char *menuItemCommand, iBool enable); |
43 | void enableMenuItemsByKey_MacOS (int key, int kmods, iBool enable); | 44 | void enableMenuItemsByKey_MacOS (int key, int kmods, iBool enable); |
45 | void enableMenuItemsOnHomeRow_MacOS(iBool enable); | ||
44 | void handleCommand_MacOS (const char *cmd); | 46 | void handleCommand_MacOS (const char *cmd); |
45 | 47 | ||
46 | void showPopupMenu_MacOS (iWidget *source, iInt2 windowCoord, const iMenuItem *items, size_t n); | 48 | void showPopupMenu_MacOS (iWidget *source, iInt2 windowCoord, const iMenuItem *items, size_t n); |
diff --git a/src/macos.m b/src/macos.m index cfbca488..28e349ea 100644 --- a/src/macos.m +++ b/src/macos.m | |||
@@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
31 | 31 | ||
32 | #include <SDL_timer.h> | 32 | #include <SDL_timer.h> |
33 | #include <SDL_syswm.h> | 33 | #include <SDL_syswm.h> |
34 | #include <the_Foundation/stringset.h> | ||
34 | 35 | ||
35 | #import <AppKit/AppKit.h> | 36 | #import <AppKit/AppKit.h> |
36 | 37 | ||
@@ -439,6 +440,13 @@ void enableMenu_MacOS(const char *menuLabel, iBool enable) { | |||
439 | [menuItem setEnabled:enable]; | 440 | [menuItem setEnabled:enable]; |
440 | } | 441 | } |
441 | 442 | ||
443 | void enableMenuIndex_MacOS(int index, iBool enable) { | ||
444 | NSApplication *app = [NSApplication sharedApplication]; | ||
445 | NSMenu *appMenu = [app mainMenu]; | ||
446 | NSMenuItem *menuItem = [appMenu itemAtIndex:index]; | ||
447 | [menuItem setEnabled:enable]; | ||
448 | } | ||
449 | |||
442 | void enableMenuItem_MacOS(const char *menuItemCommand, iBool enable) { | 450 | void enableMenuItem_MacOS(const char *menuItemCommand, iBool enable) { |
443 | NSApplication *app = [NSApplication sharedApplication]; | 451 | NSApplication *app = [NSApplication sharedApplication]; |
444 | NSMenu *appMenu = [app mainMenu]; | 452 | NSMenu *appMenu = [app mainMenu]; |
@@ -513,6 +521,47 @@ void enableMenuItemsByKey_MacOS(int key, int kmods, iBool enable) { | |||
513 | delete_String(keyEquiv); | 521 | delete_String(keyEquiv); |
514 | } | 522 | } |
515 | 523 | ||
524 | void enableMenuItemsOnHomeRow_MacOS(iBool enable) { | ||
525 | iStringSet *homeRowKeys = new_StringSet(); | ||
526 | const char *keys[] = { /* Note: another array in documentwidget.c */ | ||
527 | "f", "d", "s", "a", | ||
528 | "j", "k", "l", | ||
529 | "r", "e", "w", "q", | ||
530 | "u", "i", "o", "p", | ||
531 | "v", "c", "x", "z", | ||
532 | "m", "n", | ||
533 | "g", "h", | ||
534 | "b", | ||
535 | "t", "y" | ||
536 | }; | ||
537 | iForIndices(i, keys) { | ||
538 | iString str; | ||
539 | initCStr_String(&str, keys[i]); | ||
540 | insert_StringSet(homeRowKeys, &str); | ||
541 | deinit_String(&str); | ||
542 | } | ||
543 | NSApplication *app = [NSApplication sharedApplication]; | ||
544 | NSMenu *appMenu = [app mainMenu]; | ||
545 | for (NSMenuItem *mainMenuItem in appMenu.itemArray) { | ||
546 | NSMenu *menu = mainMenuItem.submenu; | ||
547 | if (menu) { | ||
548 | for (NSMenuItem *menuItem in menu.itemArray) { | ||
549 | if (menuItem.keyEquivalentModifierMask == 0) { | ||
550 | iString equiv; | ||
551 | initCStr_String(&equiv, [menuItem.keyEquivalent | ||
552 | cStringUsingEncoding:NSUTF8StringEncoding]); | ||
553 | if (contains_StringSet(homeRowKeys, &equiv)) { | ||
554 | [menuItem setEnabled:enable]; | ||
555 | [menu setAutoenablesItems:NO]; | ||
556 | } | ||
557 | deinit_String(&equiv); | ||
558 | } | ||
559 | } | ||
560 | } | ||
561 | } | ||
562 | iRelease(homeRowKeys); | ||
563 | } | ||
564 | |||
516 | static void setShortcut_NSMenuItem_(NSMenuItem *item, int key, int kmods) { | 565 | static void setShortcut_NSMenuItem_(NSMenuItem *item, int key, int kmods) { |
517 | NSEventModifierFlags modMask; | 566 | NSEventModifierFlags modMask; |
518 | iString *str = composeKeyEquivalent_(key, kmods, &modMask); | 567 | iString *str = composeKeyEquivalent_(key, kmods, &modMask); |
diff --git a/src/ui/bindingswidget.c b/src/ui/bindingswidget.c index 4cf8df8e..13f9434e 100644 --- a/src/ui/bindingswidget.c +++ b/src/ui/bindingswidget.c | |||
@@ -143,12 +143,16 @@ static void setActiveItem_BindingsWidget_(iBindingsWidget *d, size_t pos) { | |||
143 | item->isWaitingForEvent = iTrue; | 143 | item->isWaitingForEvent = iTrue; |
144 | invalidateItem_ListWidget(d->list, d->activePos); | 144 | invalidateItem_ListWidget(d->list, d->activePos); |
145 | } | 145 | } |
146 | #if defined (iPlatformAppleDesktop) | 146 | #if defined (iPlatformAppleDesktop) && defined (iHaveNativeContextMenus) |
147 | /* Native menus must be disabled while grabbing keys so the shortcuts don't trigger. */ | 147 | /* Native menus must be disabled while grabbing keys so the shortcuts don't trigger. */ |
148 | const iBool enableNativeMenus = (d->activePos == iInvalidPos); | 148 | const iBool enableNativeMenus = (d->activePos == iInvalidPos); |
149 | enableMenu_MacOS("${menu.title.file}", enableNativeMenus); | ||
149 | enableMenu_MacOS("${menu.title.edit}", enableNativeMenus); | 150 | enableMenu_MacOS("${menu.title.edit}", enableNativeMenus); |
150 | enableMenu_MacOS("${menu.title.view}", enableNativeMenus); | 151 | enableMenu_MacOS("${menu.title.view}", enableNativeMenus); |
152 | enableMenu_MacOS("${menu.title.bookmarks}", enableNativeMenus); | ||
151 | enableMenu_MacOS("${menu.title.identity}", enableNativeMenus); | 153 | enableMenu_MacOS("${menu.title.identity}", enableNativeMenus); |
154 | enableMenuIndex_MacOS(6, enableNativeMenus); | ||
155 | enableMenuIndex_MacOS(7, enableNativeMenus); | ||
152 | #endif | 156 | #endif |
153 | } | 157 | } |
154 | 158 | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 46af5fcd..f83539f7 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -55,6 +55,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
55 | #include "visbuf.h" | 55 | #include "visbuf.h" |
56 | #include "visited.h" | 56 | #include "visited.h" |
57 | 57 | ||
58 | #if defined (iPlatformAppleDesktop) | ||
59 | # include "macos.h" | ||
60 | #endif | ||
58 | #if defined (iPlatformAppleMobile) | 61 | #if defined (iPlatformAppleMobile) |
59 | # include "ios.h" | 62 | # include "ios.h" |
60 | #endif | 63 | #endif |
@@ -469,11 +472,18 @@ static void enableActions_DocumentWidget_(iDocumentWidget *d, iBool enable) { | |||
469 | } | 472 | } |
470 | 473 | ||
471 | static void setLinkNumberMode_DocumentWidget_(iDocumentWidget *d, iBool set) { | 474 | static void setLinkNumberMode_DocumentWidget_(iDocumentWidget *d, iBool set) { |
472 | iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, set); | 475 | if (((d->flags & showLinkNumbers_DocumentWidgetFlag) != 0) != set) { |
473 | /* Children have priority when handling events. */ | 476 | iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, set); |
474 | enableActions_DocumentWidget_(d, !set); | 477 | /* Children have priority when handling events. */ |
475 | if (d->menu) { | 478 | enableActions_DocumentWidget_(d, !set); |
476 | setFlags_Widget(d->menu, disabled_WidgetFlag, set); | 479 | #if defined (iPlatformAppleDesktop) |
480 | enableMenuItemsOnHomeRow_MacOS(!set); | ||
481 | #endif | ||
482 | /* Ensure all keyboard events come here first. */ | ||
483 | setKeyboardGrab_Widget(set ? as_Widget(d) : NULL); | ||
484 | if (d->menu) { | ||
485 | setFlags_Widget(d->menu, disabled_WidgetFlag, set); | ||
486 | } | ||
477 | } | 487 | } |
478 | } | 488 | } |
479 | 489 | ||
diff --git a/src/ui/widget.c b/src/ui/widget.c index cedda461..28c34ccf 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -1961,7 +1961,8 @@ iBool isAffectedByVisualOffset_Widget(const iWidget *d) { | |||
1961 | } | 1961 | } |
1962 | 1962 | ||
1963 | void setFocus_Widget(iWidget *d) { | 1963 | void setFocus_Widget(iWidget *d) { |
1964 | iWindow *win = get_Window(); | 1964 | iWindow *win = d ? window_Widget(d) : get_Window(); |
1965 | iAssert(win); | ||
1965 | if (win->focus != d) { | 1966 | if (win->focus != d) { |
1966 | if (win->focus) { | 1967 | if (win->focus) { |
1967 | iAssert(!contains_PtrSet(win->focus->root->pendingDestruction, win->focus)); | 1968 | iAssert(!contains_PtrSet(win->focus->root->pendingDestruction, win->focus)); |
@@ -1976,6 +1977,13 @@ void setFocus_Widget(iWidget *d) { | |||
1976 | } | 1977 | } |
1977 | } | 1978 | } |
1978 | 1979 | ||
1980 | void setKeyboardGrab_Widget(iWidget *d) { | ||
1981 | iWindow *win = d ? window_Widget(d) : get_Window(); | ||
1982 | iAssert(win); | ||
1983 | win->focus = d; | ||
1984 | /* no notifications sent */ | ||
1985 | } | ||
1986 | |||
1979 | iWidget *focus_Widget(void) { | 1987 | iWidget *focus_Widget(void) { |
1980 | return get_Window()->focus; | 1988 | return get_Window()->focus; |
1981 | } | 1989 | } |
diff --git a/src/ui/widget.h b/src/ui/widget.h index 4025f5c5..57088c07 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -302,7 +302,8 @@ void scrollInfo_Widget (const iWidget *, iWidgetScrollInfo *inf | |||
302 | 302 | ||
303 | int backgroundFadeColor_Widget (void); | 303 | int backgroundFadeColor_Widget (void); |
304 | 304 | ||
305 | void setFocus_Widget (iWidget *); | 305 | void setFocus_Widget (iWidget *); /* widget must be flagged `focusable` */ |
306 | void setKeyboardGrab_Widget (iWidget *); /* sets focus on any widget */ | ||
306 | iWidget * focus_Widget (void); | 307 | iWidget * focus_Widget (void); |
307 | void setHover_Widget (iWidget *); | 308 | void setHover_Widget (iWidget *); |
308 | iWidget * hover_Widget (void); | 309 | iWidget * hover_Widget (void); |