summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-03-03 11:03:47 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-03-03 11:58:51 +0200
commite0f586866ebafa296bda849b392d09fc2e14d487 (patch)
tree5536cf6f56f9c71db6b24cc185618387c35bdf4d /src/ui
parent76f9ab985c135c6f691276b4edd0c340a6bc2de1 (diff)
CapsLock as a modifier key; adding a modifier mapping table
IssueID #87
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/documentwidget.c8
-rw-r--r--src/ui/inputwidget.c2
-rw-r--r--src/ui/keys.c118
-rw-r--r--src/ui/keys.h4
-rw-r--r--src/ui/sidebarwidget.c5
-rw-r--r--src/ui/util.c8
-rw-r--r--src/ui/window.c8
7 files changed, 117 insertions, 36 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 0af022c5..242afe30 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -2270,7 +2270,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2270 postCommandf_App("open newtab:%d url:%s", 2270 postCommandf_App("open newtab:%d url:%s",
2271 d->ordinalMode == 2271 d->ordinalMode ==
2272 numbersAndAlphabet_DocumentLinkOrdinalMode 2272 numbersAndAlphabet_DocumentLinkOrdinalMode
2273 ? openTabMode_Sym(SDL_GetModState()) 2273 ? openTabMode_Sym(modState_Keys())
2274 : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0), 2274 : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0),
2275 cstr_String(absoluteUrl_String( 2275 cstr_String(absoluteUrl_String(
2276 d->mod.url, linkUrl_GmDocument(d->doc, run->linkId)))); 2276 d->mod.url, linkUrl_GmDocument(d->doc, run->linkId))));
@@ -2352,7 +2352,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2352#else 2352#else
2353 const int amount = ev->wheel.y; 2353 const int amount = ev->wheel.y;
2354#endif 2354#endif
2355 if (keyMods_Sym(SDL_GetModState()) == KMOD_PRIMARY) { 2355 if (keyMods_Sym(modState_Keys()) == KMOD_PRIMARY) {
2356 postCommandf_App("zoom.delta arg:%d", amount > 0 ? 10 : -10); 2356 postCommandf_App("zoom.delta arg:%d", amount > 0 ? 10 : -10);
2357 return iTrue; 2357 return iTrue;
2358 } 2358 }
@@ -2397,7 +2397,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2397 } 2397 }
2398 if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) { 2398 if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) {
2399 postCommandf_App("open newtab:%d url:%s", 2399 postCommandf_App("open newtab:%d url:%s",
2400 SDL_GetModState() & KMOD_SHIFT ? 1 : 2, 2400 modState_Keys() & KMOD_SHIFT ? 1 : 2,
2401 cstr_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId))); 2401 cstr_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId)));
2402 return iTrue; 2402 return iTrue;
2403 } 2403 }
@@ -2634,7 +2634,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2634 } 2634 }
2635 else if (linkFlags & supportedProtocol_GmLinkFlag) { 2635 else if (linkFlags & supportedProtocol_GmLinkFlag) {
2636 postCommandf_App("open newtab:%d url:%s", 2636 postCommandf_App("open newtab:%d url:%s",
2637 openTabMode_Sym(SDL_GetModState()), 2637 openTabMode_Sym(modState_Keys()),
2638 cstr_String(absoluteUrl_String( 2638 cstr_String(absoluteUrl_String(
2639 d->mod.url, linkUrl_GmDocument(d->doc, linkId)))); 2639 d->mod.url, linkUrl_GmDocument(d->doc, linkId))));
2640 } 2640 }
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 600a4462..dd8fa744 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -377,7 +377,7 @@ iLocalDef size_t cursorMax_InputWidget_(const iInputWidget *d) {
377} 377}
378 378
379iLocalDef iBool isMarking_(void) { 379iLocalDef iBool isMarking_(void) {
380 return (SDL_GetModState() & KMOD_SHIFT) != 0; 380 return (modState_Keys() & KMOD_SHIFT) != 0;
381} 381}
382 382
383void setCursor_InputWidget(iInputWidget *d, size_t pos) { 383void setCursor_InputWidget(iInputWidget *d, size_t pos) {
diff --git a/src/ui/keys.c b/src/ui/keys.c
index 4aa42d36..ce80d9f0 100644
--- a/src/ui/keys.c
+++ b/src/ui/keys.c
@@ -27,6 +27,67 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
27#include <the_Foundation/file.h> 27#include <the_Foundation/file.h>
28#include <the_Foundation/path.h> 28#include <the_Foundation/path.h>
29#include <the_Foundation/ptrset.h> 29#include <the_Foundation/ptrset.h>
30#include <SDL_keyboard.h>
31
32enum iModMap {
33 none_ModMap,
34 leftShift_ModMap,
35 leftControl_ModMap,
36 leftAlt_ModMap,
37 leftGui_ModMap,
38 rightShift_ModMap,
39 rightControl_ModMap,
40 rightAlt_ModMap,
41 rightGui_ModMap,
42 capsLock_ModMap,
43 max_ModMap
44};
45
46static int modMap_[max_ModMap];
47static iBool capsLockDown_;
48
49static void init_ModMap_(void) {
50 for (int i = 0; i < max_ModMap; i++) {
51 modMap_[i] = i;
52 }
53}
54
55int mapMods_Keys(int modFlags) {
56 static const int bits[max_ModMap] = {
57 0,
58 KMOD_LSHIFT,
59 KMOD_LCTRL,
60 KMOD_LALT,
61 KMOD_LGUI,
62 KMOD_RSHIFT,
63 KMOD_RCTRL,
64 KMOD_RALT,
65 KMOD_RGUI,
66 KMOD_CAPS,
67 };
68 int mapped = 0;
69 /* Treat capslock as a modifier key. */
70 modFlags |= (capsLockDown_ ? KMOD_CAPS : 0);
71 for (int i = 0; i < max_ModMap; ++i) {
72 if (modFlags & bits[i]) {
73 mapped |= bits[modMap_[i]];
74 }
75 }
76 return mapped;
77}
78
79int modState_Keys(void) {
80 int state = SDL_GetModState() & ~(KMOD_NUM | KMOD_MODE | KMOD_CAPS);
81 /* Treat capslock as a modifier key. */
82 if (capsLockDown_) state |= KMOD_CAPS;
83 return mapMods_Keys(state);
84}
85
86void setCapsLockDown_Keys(iBool isDown) {
87 capsLockDown_ = isDown;
88}
89
90/*----------------------------------------------------------------------------------------------*/
30 91
31iDeclareType(Keys) 92iDeclareType(Keys)
32 93
@@ -64,35 +125,35 @@ enum iBindFlag {
64/* TODO: This indirection could be used for localization, although all UI strings 125/* TODO: This indirection could be used for localization, although all UI strings
65 would need to be similarly handled. */ 126 would need to be similarly handled. */
66static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] = { 127static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] = {
67 { 1, { "Jump to top", SDLK_HOME, 0, "scroll.top" }, 0 }, 128 { 1, { "Jump to top", SDLK_HOME, 0, "scroll.top" }, 0 },
68 { 2, { "Jump to bottom", SDLK_END, 0, "scroll.bottom" }, 0 }, 129 { 2, { "Jump to bottom", SDLK_END, 0, "scroll.bottom" }, 0 },
69 { 10, { "Scroll up", SDLK_UP, 0, "scroll.step arg:-1" }, argRepeat_BindFlag }, 130 { 10, { "Scroll up", SDLK_UP, 0, "scroll.step arg:-1" }, argRepeat_BindFlag },
70 { 11, { "Scroll down", SDLK_DOWN, 0, "scroll.step arg:1" }, argRepeat_BindFlag }, 131 { 11, { "Scroll down", SDLK_DOWN, 0, "scroll.step arg:1" }, argRepeat_BindFlag },
71 { 20, { "Scroll up half a page", SDLK_PAGEUP, 0, "scroll.page arg:-1" }, argRepeat_BindFlag }, 132 { 20, { "Scroll up half a page", SDLK_PAGEUP, 0, "scroll.page arg:-1" }, argRepeat_BindFlag },
72 { 21, { "Scroll down half a page", SDLK_PAGEDOWN, 0, "scroll.page arg:1" }, argRepeat_BindFlag }, 133 { 21, { "Scroll down half a page", SDLK_PAGEDOWN, 0, "scroll.page arg:1" }, argRepeat_BindFlag },
73 { 30, { "Go back", navigateBack_KeyShortcut, "navigate.back" }, 0 }, 134 { 30, { "Go back", navigateBack_KeyShortcut, "navigate.back" }, 0 },
74 { 31, { "Go forward", navigateForward_KeyShortcut, "navigate.forward" }, 0 }, 135 { 31, { "Go forward", navigateForward_KeyShortcut, "navigate.forward" }, 0 },
75 { 32, { "Go to parent directory", navigateParent_KeyShortcut, "navigate.parent" }, 0 }, 136 { 32, { "Go to parent directory", navigateParent_KeyShortcut, "navigate.parent" }, 0 },
76 { 33, { "Go to site root", navigateRoot_KeyShortcut, "navigate.root" }, 0 }, 137 { 33, { "Go to site root", navigateRoot_KeyShortcut, "navigate.root" }, 0 },
77 { 35, { "Reload page", reload_KeyShortcut, "document.reload" }, 0 }, 138 { 35, { "Reload page", reload_KeyShortcut, "document.reload" }, 0 },
78 { 41, { "Open link via modifier key", SDLK_LALT, 0, "document.linkkeys arg:0" }, argRelease_BindFlag }, 139 { 41, { "Open link via modifier key", SDLK_LALT, 0, "document.linkkeys arg:0" }, argRelease_BindFlag },
79 { 42, { "Open link via home row keys", 'f', 0, "document.linkkeys arg:1" }, 0 }, 140 { 42, { "Open link via home row keys", 'f', 0, "document.linkkeys arg:1" }, 0 },
80 { 45, { "Open link in new tab via home row keys", 'f', KMOD_SHIFT, "document.linkkeys arg:1 newtab:1" }, 0 }, 141 { 45, { "Open link in new tab via home row keys", 'f', KMOD_SHIFT, "document.linkkeys arg:1 newtab:1" }, 0 },
81 { 46, { "Hover on link via home row keys", 'h', 0, "document.linkkeys arg:1 hover:1" }, 0 }, 142 { 46, { "Hover on link via home row keys", 'h', 0, "document.linkkeys arg:1 hover:1" }, 0 },
82 { 47, { "Next set of home row key links", '.', 0, "document.linkkeys more:1" }, 0 }, 143 { 47, { "Next set of home row key links", '.', 0, "document.linkkeys more:1" }, 0 },
83 { 50, { "Add bookmark", 'd', KMOD_PRIMARY, "bookmark.add" }, 0 }, 144 { 50, { "Add bookmark", 'd', KMOD_PRIMARY, "bookmark.add" }, 0 },
84 { 60, { "Find text on page", 'f', KMOD_PRIMARY, "focus.set id:find.input" }, 0 }, 145 { 60, { "Find text on page", 'f', KMOD_PRIMARY, "focus.set id:find.input" }, 0 },
85 { 70, { "Zoom in", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 0 }, 146 { 70, { "Zoom in", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 0 },
86 { 71, { "Zoom out", SDLK_MINUS, KMOD_PRIMARY, "zoom.delta arg:-10" }, 0 }, 147 { 71, { "Zoom out", SDLK_MINUS, KMOD_PRIMARY, "zoom.delta arg:-10" }, 0 },
87 { 72, { "Reset zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, 0 }, 148 { 72, { "Reset zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, 0 },
88#if !defined (iPlatformApple) /* Ctrl-Cmd-F on macOS */ 149#if !defined (iPlatformApple) /* Ctrl-Cmd-F on macOS */
89 { 73, { "Toggle fullscreen mode", SDLK_F11, 0, "window.fullscreen" }, 0 }, 150 { 73, { "Toggle fullscreen mode", SDLK_F11, 0, "window.fullscreen" }, 0 },
90#endif 151#endif
91 { 76, { "New tab", newTab_KeyShortcut, "tabs.new" }, 0 }, 152 { 76, { "New tab", newTab_KeyShortcut, "tabs.new" }, 0 },
92 { 77, { "Close tab", closeTab_KeyShortcut, "tabs.close" }, 0 }, 153 { 77, { "Close tab", closeTab_KeyShortcut, "tabs.close" }, 0 },
93 { 80, { "Previous tab", prevTab_KeyShortcut, "tabs.prev" }, 0 }, 154 { 80, { "Previous tab", prevTab_KeyShortcut, "tabs.prev" }, 0 },
94 { 81, { "Next tab", nextTab_KeyShortcut, "tabs.next" }, 0 }, 155 { 81, { "Next tab", nextTab_KeyShortcut, "tabs.next" }, 0 },
95 { 100,{ "Toggle show URL on hover", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 }, 156 { 100,{ "Toggle show URL on hover", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 },
96 /* The following cannot currently be changed (built-in duplicates). */ 157 /* The following cannot currently be changed (built-in duplicates). */
97 { 1000, { NULL, SDLK_SPACE, KMOD_SHIFT, "scroll.page arg:-1" }, argRepeat_BindFlag }, 158 { 1000, { NULL, SDLK_SPACE, KMOD_SHIFT, "scroll.page arg:-1" }, argRepeat_BindFlag },
98 { 1001, { NULL, SDLK_SPACE, 0, "scroll.page arg:1" }, argRepeat_BindFlag }, 159 { 1001, { NULL, SDLK_SPACE, 0, "scroll.page arg:1" }, argRepeat_BindFlag },
@@ -193,6 +254,7 @@ static const char *filename_Keys_ = "bindings.txt";
193 254
194void init_Keys(void) { 255void init_Keys(void) {
195 iKeys *d = &keys_; 256 iKeys *d = &keys_;
257 init_ModMap_();
196 init_Array(&d->bindings, sizeof(iBinding)); 258 init_Array(&d->bindings, sizeof(iBinding));
197 initCmp_PtrSet(&d->lookup, cmpPtr_Binding_); 259 initCmp_PtrSet(&d->lookup, cmpPtr_Binding_);
198 bindDefaults_(); 260 bindDefaults_();
@@ -227,6 +289,7 @@ void load_Keys(const char *saveDir) {
227 if (*m == 'a') bind->mods |= KMOD_ALT; 289 if (*m == 'a') bind->mods |= KMOD_ALT;
228 if (*m == 'c') bind->mods |= KMOD_CTRL; 290 if (*m == 'c') bind->mods |= KMOD_CTRL;
229 if (*m == 'g') bind->mods |= KMOD_GUI; 291 if (*m == 'g') bind->mods |= KMOD_GUI;
292 if (*m == 'k') bind->mods |= KMOD_CAPS;
230 } 293 }
231 } 294 }
232 } 295 }
@@ -250,6 +313,7 @@ void save_Keys(const char *saveDir) {
250 if (bind->mods & KMOD_ALT) appendChar_String(line, 'a'); 313 if (bind->mods & KMOD_ALT) appendChar_String(line, 'a');
251 if (bind->mods & KMOD_CTRL) appendChar_String(line, 'c'); 314 if (bind->mods & KMOD_CTRL) appendChar_String(line, 'c');
252 if (bind->mods & KMOD_GUI) appendChar_String(line, 'g'); 315 if (bind->mods & KMOD_GUI) appendChar_String(line, 'g');
316 if (bind->mods & KMOD_CAPS) appendChar_String(line, 'k');
253 } 317 }
254 appendChar_String(line, '\n'); 318 appendChar_String(line, '\n');
255 write_File(f, &line->chars); 319 write_File(f, &line->chars);
diff --git a/src/ui/keys.h b/src/ui/keys.h
index f6f0b465..90e33b7c 100644
--- a/src/ui/keys.h
+++ b/src/ui/keys.h
@@ -90,3 +90,7 @@ const iBinding *findCommand_Keys (const char *command);
90 90
91iBool processEvent_Keys (const SDL_Event *); 91iBool processEvent_Keys (const SDL_Event *);
92const iPtrArray *list_Keys (void); 92const iPtrArray *list_Keys (void);
93
94int mapMods_Keys (int modFlags);
95int modState_Keys (void); /* current modifier key state */
96void setCapsLockDown_Keys(iBool isDown);
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 4e908f1e..4d3818bd 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
33#include "inputwidget.h" 33#include "inputwidget.h"
34#include "labelwidget.h" 34#include "labelwidget.h"
35#include "listwidget.h" 35#include "listwidget.h"
36#include "keys.h"
36#include "paint.h" 37#include "paint.h"
37#include "scrollwidget.h" 38#include "scrollwidget.h"
38#include "util.h" 39#include "util.h"
@@ -605,14 +606,14 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, const iSidebarItem *it
605 } 606 }
606 case feeds_SidebarMode: { 607 case feeds_SidebarMode: {
607 postCommandString_App( 608 postCommandString_App(
608 feedEntryOpenCommand_String(&item->url, openTabMode_Sym(SDL_GetModState()))); 609 feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys())));
609 break; 610 break;
610 } 611 }
611 case bookmarks_SidebarMode: 612 case bookmarks_SidebarMode:
612 case history_SidebarMode: { 613 case history_SidebarMode: {
613 if (!isEmpty_String(&item->url)) { 614 if (!isEmpty_String(&item->url)) {
614 postCommandf_App("open newtab:%d url:%s", 615 postCommandf_App("open newtab:%d url:%s",
615 openTabMode_Sym(SDL_GetModState()), 616 openTabMode_Sym(modState_Keys()),
616 cstr_String(&item->url)); 617 cstr_String(&item->url));
617 } 618 }
618 break; 619 break;
diff --git a/src/ui/util.c b/src/ui/util.c
index 5fb3e9f3..934a6d9b 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -89,6 +89,9 @@ void toString_Sym(int key, int kmods, iString *str) {
89 appendCStr_String(str, "Meta+"); 89 appendCStr_String(str, "Meta+");
90 } 90 }
91#endif 91#endif
92 if (kmods & KMOD_CAPS) {
93 appendCStr_String(str, "Caps+");
94 }
92 if (key == 0x20) { 95 if (key == 0x20) {
93 appendCStr_String(str, "Space"); 96 appendCStr_String(str, "Space");
94 } 97 }
@@ -120,7 +123,8 @@ void toString_Sym(int key, int kmods, iString *str) {
120 123
121iBool isMod_Sym(int key) { 124iBool isMod_Sym(int key) {
122 return key == SDLK_LALT || key == SDLK_RALT || key == SDLK_LCTRL || key == SDLK_RCTRL || 125 return key == SDLK_LALT || key == SDLK_RALT || key == SDLK_LCTRL || key == SDLK_RCTRL ||
123 key == SDLK_LGUI || key == SDLK_RGUI || key == SDLK_LSHIFT || key == SDLK_RSHIFT; 126 key == SDLK_LGUI || key == SDLK_RGUI || key == SDLK_LSHIFT || key == SDLK_RSHIFT ||
127 key == SDLK_CAPSLOCK;
124} 128}
125 129
126int normalizedMod_Sym(int key) { 130int normalizedMod_Sym(int key) {
@@ -132,7 +136,7 @@ int normalizedMod_Sym(int key) {
132} 136}
133 137
134int keyMods_Sym(int kmods) { 138int keyMods_Sym(int kmods) {
135 kmods &= (KMOD_SHIFT | KMOD_ALT | KMOD_CTRL | KMOD_GUI); 139 kmods &= (KMOD_SHIFT | KMOD_ALT | KMOD_CTRL | KMOD_GUI | KMOD_CAPS);
136 /* Don't treat left/right modifiers differently. */ 140 /* Don't treat left/right modifiers differently. */
137 if (kmods & KMOD_SHIFT) kmods |= KMOD_SHIFT; 141 if (kmods & KMOD_SHIFT) kmods |= KMOD_SHIFT;
138 if (kmods & KMOD_ALT) kmods |= KMOD_ALT; 142 if (kmods & KMOD_ALT) kmods |= KMOD_ALT;
diff --git a/src/ui/window.c b/src/ui/window.c
index 3518d97e..cbcca902 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1532,6 +1532,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
1532 return iTrue; 1532 return iTrue;
1533 case SDL_WINDOWEVENT_FOCUS_GAINED: 1533 case SDL_WINDOWEVENT_FOCUS_GAINED:
1534 d->focusGainedAt = SDL_GetTicks(); 1534 d->focusGainedAt = SDL_GetTicks();
1535 setCapsLockDown_Keys(iFalse);
1535 postCommand_App("window.focus.gained"); 1536 postCommand_App("window.focus.gained");
1536 return iFalse; 1537 return iFalse;
1537 case SDL_WINDOWEVENT_FOCUS_LOST: 1538 case SDL_WINDOWEVENT_FOCUS_LOST:
@@ -1593,6 +1594,13 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
1593 As a workaround, ignore these events. */ 1594 As a workaround, ignore these events. */
1594 return iTrue; /* won't go to bindings, either */ 1595 return iTrue; /* won't go to bindings, either */
1595 } 1596 }
1597 /* Apply keyboard modifier mapping. */
1598 if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
1599 if (event.key.keysym.sym == SDLK_CAPSLOCK) {
1600 setCapsLockDown_Keys(event.key.state == SDL_PRESSED);
1601 }
1602 event.key.keysym.mod = mapMods_Keys(event.key.keysym.mod & ~KMOD_CAPS);
1603 }
1596 if (event.type == SDL_MOUSEBUTTONDOWN && d->ignoreClick) { 1604 if (event.type == SDL_MOUSEBUTTONDOWN && d->ignoreClick) {
1597 d->ignoreClick = iFalse; 1605 d->ignoreClick = iFalse;
1598 return iTrue; 1606 return iTrue;