summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-28 17:48:38 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-28 17:48:38 +0300
commit87b5dbd9c0393e787d2499d796486d3293f17214 (patch)
tree556f0b6b6a0188fdf9bd02275f796656ef428e86 /src
parent408597bd4f71a13a511b6af33601dff0be2ed317 (diff)
Working on multiple UI roots
Root focus switching and opening links in the other root.
Diffstat (limited to 'src')
-rw-r--r--src/app.c27
-rw-r--r--src/app.h1
-rw-r--r--src/ui/documentwidget.c15
-rw-r--r--src/ui/keys.c9
-rw-r--r--src/ui/lookupwidget.c3
-rw-r--r--src/ui/root.c6
-rw-r--r--src/ui/util.c4
-rw-r--r--src/ui/util.h8
-rw-r--r--src/ui/widget.c13
-rw-r--r--src/ui/window.c64
-rw-r--r--src/ui/window.h3
11 files changed, 117 insertions, 36 deletions
diff --git a/src/app.c b/src/app.c
index 85284d19..f5351e89 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1306,7 +1306,7 @@ void postCommandf_App(const char *command, ...) {
1306 1306
1307void rootOrder_App(iRoot *roots[2]) { 1307void rootOrder_App(iRoot *roots[2]) {
1308 const iWindow *win = app_.window; 1308 const iWindow *win = app_.window;
1309 roots[0] = get_Root(); 1309 roots[0] = win->keyRoot;
1310 roots[1] = (roots[0] == win->roots[0] ? win->roots[1] : win->roots[0]); 1310 roots[1] = (roots[0] == win->roots[0] ? win->roots[1] : win->roots[0]);
1311} 1311}
1312 1312
@@ -1535,7 +1535,7 @@ iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNe
1535 } 1535 }
1536 arrange_Widget(tabs); 1536 arrange_Widget(tabs);
1537 refresh_Widget(tabs); 1537 refresh_Widget(tabs);
1538 postCommandf_App("tab.created id:%s", cstr_String(id_Widget(as_Widget(doc)))); 1538 postCommandf_Root(get_Root(), "tab.created id:%s", cstr_String(id_Widget(as_Widget(doc))));
1539 return doc; 1539 return doc;
1540} 1540}
1541 1541
@@ -1906,10 +1906,16 @@ iBool handleCommand_App(const char *cmd) {
1906 openInDefaultBrowser_App(url); 1906 openInDefaultBrowser_App(url);
1907 return iTrue; 1907 return iTrue;
1908 } 1908 }
1909 iDocumentWidget *doc = document_Command(cmd);
1910 const int newTab = argLabel_Command(cmd, "newtab"); 1909 const int newTab = argLabel_Command(cmd, "newtab");
1911 if (newTab) { 1910 iRoot *root = get_Root();
1912 doc = newTab_App(NULL, (newTab & 1) != 0); /* `newtab:2` to open in background */ 1911 iRoot *oldRoot = root;
1912 if (newTab & otherRoot_OpenTabFlag) {
1913 root = otherRoot_Window(d->window, root);
1914 setCurrent_Root(root); /* need to change for widget creation */
1915 }
1916 iDocumentWidget *doc = document_Command(cmd);
1917 if (newTab & (new_OpenTabFlag | newBackground_OpenTabFlag)) {
1918 doc = newTab_App(NULL, (newTab & new_OpenTabFlag) != 0); /* `newtab:2` to open in background */
1913 } 1919 }
1914 iHistory *history = history_DocumentWidget(doc); 1920 iHistory *history = history_DocumentWidget(doc);
1915 const iBool isHistory = argLabel_Command(cmd, "history") != 0; 1921 const iBool isHistory = argLabel_Command(cmd, "history") != 0;
@@ -1936,14 +1942,15 @@ iBool handleCommand_App(const char *cmd) {
1936 is already available, e.g., it's from "about:" or restored from cache. */ 1942 is already available, e.g., it's from "about:" or restored from cache. */
1937 const iRangecc gotoHeading = range_Command(cmd, "gotoheading"); 1943 const iRangecc gotoHeading = range_Command(cmd, "gotoheading");
1938 if (gotoHeading.start) { 1944 if (gotoHeading.start) {
1939 postCommandf_App("document.goto heading:%s", cstr_Rangecc(gotoHeading)); 1945 postCommandf_Root(root, "document.goto heading:%s", cstr_Rangecc(gotoHeading));
1940 } 1946 }
1941 const iRangecc gotoUrlHeading = range_Command(cmd, "gotourlheading"); 1947 const iRangecc gotoUrlHeading = range_Command(cmd, "gotourlheading");
1942 if (gotoUrlHeading.start) { 1948 if (gotoUrlHeading.start) {
1943 postCommandf_App("document.goto heading:%s", 1949 postCommandf_Root(root, "document.goto heading:%s",
1944 cstrCollect_String(urlDecode_String( 1950 cstrCollect_String(urlDecode_String(
1945 collect_String(newRange_String(gotoUrlHeading))))); 1951 collect_String(newRange_String(gotoUrlHeading)))));
1946 } 1952 }
1953 setCurrent_Root(oldRoot);
1947 } 1954 }
1948 else if (equal_Command(cmd, "document.request.cancelled")) { 1955 else if (equal_Command(cmd, "document.request.cancelled")) {
1949 /* TODO: How should cancelled requests be treated in the history? */ 1956 /* TODO: How should cancelled requests be treated in the history? */
@@ -2016,6 +2023,12 @@ iBool handleCommand_App(const char *cmd) {
2016 } 2023 }
2017 return iTrue; 2024 return iTrue;
2018 } 2025 }
2026 else if (equal_Command(cmd, "keyroot.next")) {
2027 if (setKeyRoot_Window(d->window, otherRoot_Window(d->window, d->window->keyRoot))) {
2028 setFocus_Widget(NULL);
2029 }
2030 return iTrue;
2031 }
2019 else if (equal_Command(cmd, "quit")) { 2032 else if (equal_Command(cmd, "quit")) {
2020 SDL_Event ev; 2033 SDL_Event ev;
2021 ev.type = SDL_QUIT; 2034 ev.type = SDL_QUIT;
diff --git a/src/app.h b/src/app.h
index 1b8360b3..a4ce2f6c 100644
--- a/src/app.h
+++ b/src/app.h
@@ -71,6 +71,7 @@ const iString *downloadDir_App (void);
71const iString *debugInfo_App (void); 71const iString *debugInfo_App (void);
72 72
73int run_App (int argc, char **argv); 73int run_App (int argc, char **argv);
74void rootOrder_App (iRoot *roots[2]); /* TODO: max roots? */
74void processEvents_App (enum iAppEventMode mode); 75void processEvents_App (enum iAppEventMode mode);
75iBool handleCommand_App (const char *cmd); 76iBool handleCommand_App (const char *cmd);
76void refresh_App (void); 77void refresh_App (void);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 2f20958f..1d6c340c 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -845,7 +845,8 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
845} 845}
846 846
847static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { 847static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) {
848 iLabelWidget *tabButton = tabPageButton_Widget(findWidget_App("doctabs"), d); 848 iLabelWidget *tabButton = tabPageButton_Widget(findChild_Widget(root_Widget(constAs_Widget(d)),
849 "doctabs"), d);
849 if (!tabButton) { 850 if (!tabButton) {
850 /* Not part of the UI at the moment. */ 851 /* Not part of the UI at the moment. */
851 return; 852 return;
@@ -2868,8 +2869,16 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2868 { openTabBg_Icon " ${link.newtab.background}", 2869 { openTabBg_Icon " ${link.newtab.background}",
2869 0, 2870 0,
2870 0, 2871 0,
2871 format_CStr("!open newtab:2 url:%s", cstr_String(linkUrl)) } }, 2872 format_CStr("!open newtab:2 url:%s", cstr_String(linkUrl)) },
2872 2); 2873 { "${link.side}",
2874 0,
2875 0,
2876 format_CStr("!open newtab:4 url:%s", cstr_String(linkUrl)) },
2877 { "${link.side.newtab}",
2878 0,
2879 0,
2880 format_CStr("!open newtab:5 url:%s", cstr_String(linkUrl)) } },
2881 4);
2873 } 2882 }
2874 else if (!willUseProxy_App(scheme)) { 2883 else if (!willUseProxy_App(scheme)) {
2875 pushBack_Array( 2884 pushBack_Array(
diff --git a/src/ui/keys.c b/src/ui/keys.c
index 6ad9d360..68319598 100644
--- a/src/ui/keys.c
+++ b/src/ui/keys.c
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23#include "keys.h" 23#include "keys.h"
24#include "util.h" 24#include "util.h"
25#include "window.h"
25#include "app.h" 26#include "app.h"
26 27
27#include <the_Foundation/file.h> 28#include <the_Foundation/file.h>
@@ -223,6 +224,7 @@ static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] =
223 { 77, { "${keys.tab.close}", closeTab_KeyShortcut, "tabs.close" }, 0 }, 224 { 77, { "${keys.tab.close}", closeTab_KeyShortcut, "tabs.close" }, 0 },
224 { 80, { "${keys.tab.prev}", prevTab_KeyShortcut, "tabs.prev" }, 0 }, 225 { 80, { "${keys.tab.prev}", prevTab_KeyShortcut, "tabs.prev" }, 0 },
225 { 81, { "${keys.tab.next}", nextTab_KeyShortcut, "tabs.next" }, 0 }, 226 { 81, { "${keys.tab.next}", nextTab_KeyShortcut, "tabs.next" }, 0 },
227 { 91, { "${keys.frame.next}", SDLK_TAB, KMOD_CTRL, "keyroot.next", }, 0 },
226 { 100,{ "${keys.hoverurl}", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 }, 228 { 100,{ "${keys.hoverurl}", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 },
227 /* The following cannot currently be changed (built-in duplicates). */ 229 /* The following cannot currently be changed (built-in duplicates). */
228#if defined (iPlatformApple) 230#if defined (iPlatformApple)
@@ -424,21 +426,22 @@ void setLabel_Keys(int id, const char *label) {
424 426
425iBool processEvent_Keys(const SDL_Event *ev) { 427iBool processEvent_Keys(const SDL_Event *ev) {
426 iKeys *d = &keys_; 428 iKeys *d = &keys_;
429 iRoot *root = get_Window()->keyRoot;
427 if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) { 430 if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) {
428 const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod)); 431 const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod));
429 if (bind) { 432 if (bind) {
430 if (ev->type == SDL_KEYUP) { 433 if (ev->type == SDL_KEYUP) {
431 if (bind->flags & argRelease_BindFlag) { 434 if (bind->flags & argRelease_BindFlag) {
432 postCommandf_App("%s release:1", cstr_String(&bind->command)); 435 postCommandf_Root(root, "%s release:1", cstr_String(&bind->command));
433 return iTrue; 436 return iTrue;
434 } 437 }
435 return iFalse; 438 return iFalse;
436 } 439 }
437 if (ev->key.repeat && (bind->flags & argRepeat_BindFlag)) { 440 if (ev->key.repeat && (bind->flags & argRepeat_BindFlag)) {
438 postCommandf_App("%s repeat:1", cstr_String(&bind->command)); 441 postCommandf_Root(root, "%s repeat:1", cstr_String(&bind->command));
439 } 442 }
440 else { 443 else {
441 postCommandString_Root(NULL, &bind->command); 444 postCommandString_Root(root, &bind->command);
442 } 445 }
443 return iTrue; 446 return iTrue;
444 } 447 }
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index e0de97bf..b3e88ce8 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -742,7 +742,8 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) {
742 return iTrue; 742 return iTrue;
743 } 743 }
744 } 744 }
745 if (key == SDLK_DOWN && !mods && focus_Widget() == findWidget_App("url") && 745 if (isVisible_Widget(w) &&
746 key == SDLK_DOWN && !mods && focus_Widget() == findWidget_App("url") &&
746 numItems_ListWidget(d->list)) { 747 numItems_ListWidget(d->list)) {
747 setCursor_LookupWidget_(d, 1); /* item 0 is always the first heading */ 748 setCursor_LookupWidget_(d, 1); /* item 0 is always the first heading */
748 setFocus_Widget(w); 749 setFocus_Widget(w);
diff --git a/src/ui/root.c b/src/ui/root.c
index 8faeb215..aec5f26f 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -716,14 +716,14 @@ static iBool handleSearchBarCommands_(iWidget *searchBar, const char *cmd) {
716 equal_Rangecc(range_Command(cmd, "id"), "find.input")) { 716 equal_Rangecc(range_Command(cmd, "id"), "find.input")) {
717 iInputWidget *input = findChild_Widget(searchBar, "find.input"); 717 iInputWidget *input = findChild_Widget(searchBar, "find.input");
718 if (arg_Command(cmd) && argLabel_Command(cmd, "enter") && isVisible_Widget(input)) { 718 if (arg_Command(cmd) && argLabel_Command(cmd, "enter") && isVisible_Widget(input)) {
719 postCommand_App("find.next"); 719 postCommand_Root(searchBar->root, "find.next");
720 /* Keep focus when pressing Enter. */ 720 /* Keep focus when pressing Enter. */
721 if (!isEmpty_String(text_InputWidget(input))) { 721 if (!isEmpty_String(text_InputWidget(input))) {
722 postCommand_App("focus.set id:find.input"); 722 postCommand_Root(searchBar->root, "focus.set id:find.input");
723 } 723 }
724 } 724 }
725 else { 725 else {
726 postCommand_App("find.clearmark"); 726 postCommand_Root(searchBar->root, "find.clearmark");
727 } 727 }
728 return iTrue; 728 return iTrue;
729 } 729 }
diff --git a/src/ui/util.c b/src/ui/util.c
index b487e13d..9a023ab9 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -171,7 +171,9 @@ int keyMods_Sym(int kmods) {
171 171
172int openTabMode_Sym(int kmods) { 172int openTabMode_Sym(int kmods) {
173 const int km = keyMods_Sym(kmods); 173 const int km = keyMods_Sym(kmods);
174 return ((km & KMOD_PRIMARY) && (km & KMOD_SHIFT)) ? 1 : (km & KMOD_PRIMARY) ? 2 : 0; 174 return (km & KMOD_ALT ? otherRoot_OpenTabFlag : 0) | /* open to the side */
175 (((km & KMOD_PRIMARY) && (km & KMOD_SHIFT)) ? new_OpenTabFlag :
176 (km & KMOD_PRIMARY) ? newBackground_OpenTabFlag : 0);
175} 177}
176 178
177iRangei intersect_Rangei(iRangei a, iRangei b) { 179iRangei intersect_Rangei(iRangei a, iRangei b) {
diff --git a/src/ui/util.h b/src/ui/util.h
index b8d10cf0..8dd16a7f 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -64,11 +64,17 @@ iLocalDef iBool isPerPixel_MouseWheelEvent(const SDL_MouseWheelEvent *ev) {
64# define KMOD_SECONDARY KMOD_GUI 64# define KMOD_SECONDARY KMOD_GUI
65#endif 65#endif
66 66
67enum iOpenTabFlag {
68 new_OpenTabFlag = iBit(1),
69 newBackground_OpenTabFlag = iBit(2),
70 otherRoot_OpenTabFlag = iBit(3),
71};
72
67iBool isMod_Sym (int key); 73iBool isMod_Sym (int key);
68int normalizedMod_Sym (int key); 74int normalizedMod_Sym (int key);
69int keyMods_Sym (int kmods); /* shift, alt, control, or gui */ 75int keyMods_Sym (int kmods); /* shift, alt, control, or gui */
70void toString_Sym (int key, int kmods, iString *str); 76void toString_Sym (int key, int kmods, iString *str);
71int openTabMode_Sym (int kmods); 77int openTabMode_Sym (int kmods); /* returns OpenTabFlags */
72 78
73iRangei intersect_Rangei (iRangei a, iRangei b); 79iRangei intersect_Rangei (iRangei a, iRangei b);
74iRangei union_Rangei (iRangei a, iRangei b); 80iRangei union_Rangei (iRangei a, iRangei b);
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 6d6bc202..bb71df7c 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -1346,6 +1346,7 @@ void setFocus_Widget(iWidget *d) {
1346 win->focus = d; 1346 win->focus = d;
1347 if (d) { 1347 if (d) {
1348 iAssert(flags_Widget(d) & focusable_WidgetFlag); 1348 iAssert(flags_Widget(d) & focusable_WidgetFlag);
1349 setKeyRoot_Window(get_Window(), d->root);
1349 postCommand_Widget(d, "focus.gained"); 1350 postCommand_Widget(d, "focus.gained");
1350 } 1351 }
1351 } 1352 }
@@ -1404,13 +1405,17 @@ static const iWidget *findFocusRoot_Widget_(const iWidget *d) {
1404} 1405}
1405 1406
1406iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir) { 1407iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir) {
1407 const iWidget *root = findFocusRoot_Widget_(get_Root()->widget); 1408 iRoot *uiRoot = (startFrom ? startFrom->root : get_Window()->keyRoot);
1408 iAssert(root != NULL); 1409 const iWidget *focusRoot = findFocusRoot_Widget_(uiRoot->widget);
1410 iAssert(focusRoot != NULL);
1409 iBool getNext = (startFrom ? iFalse : iTrue); 1411 iBool getNext = (startFrom ? iFalse : iTrue);
1410 const iWidget *found = findFocusable_Widget_(root, startFrom, &getNext, focusDir); 1412 const iWidget *found = findFocusable_Widget_(focusRoot, startFrom, &getNext, focusDir);
1411 if (!found && startFrom) { 1413 if (!found && startFrom) {
1412 getNext = iTrue; 1414 getNext = iTrue;
1413 found = findFocusable_Widget_(root, NULL, &getNext, focusDir); 1415 /* Switch to the next root, if available. */
1416 found = findFocusable_Widget_(findFocusRoot_Widget_(otherRoot_Window(get_Window(),
1417 uiRoot)->widget),
1418 NULL, &getNext, focusDir);
1414 } 1419 }
1415 return iConstCast(iWidget *, found); 1420 return iConstCast(iWidget *, found);
1416} 1421}
diff --git a/src/ui/window.c b/src/ui/window.c
index 78ea182c..54f05a20 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -179,21 +179,22 @@ static void setupUserInterface_Window(iWindow *d) {
179 createUserInterface_Root(d->roots[i]); 179 createUserInterface_Root(d->roots[i]);
180 setCurrent_Root(NULL); 180 setCurrent_Root(NULL);
181 } 181 }
182 /* One of the roots always has keyboard input focus. */
183 d->keyRoot = d->roots[0];
182} 184}
183 185
184static void windowSizeChanged_Window_(iWindow *d) { 186static void windowSizeChanged_Window_(iWindow *d) {
185 const int numRoots = numRoots_Window(d); 187 const int numRoots = numRoots_Window(d);
186 /* Horizontal split frame. */ 188 /* Horizontal split frame. */
187 const iInt2 rootSize = div_I2(d->size, init_I2(numRoots, 1)); 189 const iInt2 rootSize = d->size;
188 iForIndices(i, d->roots) { 190 iForIndices(i, d->roots) {
189 iRoot *root = d->roots[i]; 191 iRoot *root = d->roots[i];
190 if (root) { 192 if (root) {
191 root->widget->rect.pos = init_I2(rootSize.x * i, 0); 193 iRect *rect = &root->widget->rect;
192 root->widget->rect.size = addX_I2(rootSize, -gap_UI / 4); 194 /* Horizontal split frame. */
193 if (i == 1) { 195 rect->pos = init_I2(rootSize.x * i / numRoots, 0);
194 root->widget->rect.pos.x += gap_UI / 8; 196 rect->size = init_I2(rootSize.x * (i + 1) / numRoots - rect->pos.x, rootSize.y);
195 } 197 root->widget->minSize = rect->size;
196 root->widget->minSize = root->widget->rect.size;
197 updatePadding_Root(root); 198 updatePadding_Root(root);
198 arrange_Widget(root->widget); 199 arrange_Widget(root->widget);
199 } 200 }
@@ -530,6 +531,10 @@ iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) {
530 return NULL; 531 return NULL;
531} 532}
532 533
534iRoot *otherRoot_Window(const iWindow *d, iRoot *root) {
535 return root == d->roots[0] && d->roots[1] ? d->roots[1] : d->roots[0];
536}
537
533static void invalidate_Window_(iWindow *d) { 538static void invalidate_Window_(iWindow *d) {
534 iUnused(d); 539 iUnused(d);
535 resetFonts_Text(); 540 resetFonts_Text();
@@ -881,19 +886,35 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
881 return iFalse; 886 return iFalse;
882} 887}
883 888
889iBool setKeyRoot_Window(iWindow *d, iRoot *root) {
890 if (d->keyRoot != root) {
891 d->keyRoot = root;
892 postRefresh_App();
893 return iTrue;
894 }
895 return iFalse;
896}
897
884iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { 898iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) {
885 if (ev->type == SDL_MOUSEMOTION) { 899 if (ev->type == SDL_MOUSEMOTION) {
886 /* Hover widget may change. */ 900 /* Hover widget may change. */
887 setHover_Widget(NULL); 901 setHover_Widget(NULL);
888 } 902 }
889 iForIndices(i, d->roots) { 903 iRoot *order[2];
890 if (d->roots[i]) { 904 rootOrder_App(order);
891 if (isCommand_SDLEvent(ev) && ev->user.data2 && ev->user.data2 != d->roots[i]) { 905 iForIndices(i, order) {
906 iRoot *root = order[i];
907 if (root) {
908 if (isCommand_SDLEvent(ev) && ev->user.data2 && ev->user.data2 != root) {
892 continue; /* Not meant for this root. */ 909 continue; /* Not meant for this root. */
893 } 910 }
894 setCurrent_Root(d->roots[i]); 911 setCurrent_Root(root);
895 const iBool wasUsed = dispatchEvent_Widget(d->roots[i]->widget, ev); 912 const iBool wasUsed = dispatchEvent_Widget(root->widget, ev);
896 if (wasUsed) { 913 if (wasUsed) {
914 if (ev->type == SDL_MOUSEBUTTONDOWN ||
915 ev->type == SDL_MOUSEWHEEL) {
916 setKeyRoot_Window(d, root);
917 }
897 return iTrue; 918 return iTrue;
898 } 919 }
899 } 920 }
@@ -982,7 +1003,24 @@ void draw_Window(iWindow *d) {
982 NULL, 1003 NULL,
983 &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size }); 1004 &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size });
984 } 1005 }
985#endif 1006#endif
1007 /* Root separator and keyboard focus indicator. */ {
1008 iPaint p;
1009 init_Paint(&p);
1010 const iRect bounds = bounds_Widget(root->widget);
1011 if (i == 1) {
1012 fillRect_Paint(&p, (iRect){
1013 addX_I2(topLeft_Rect(bounds), -gap_UI / 8),
1014 init_I2(gap_UI / 4, height_Rect(bounds))
1015 }, uiSeparator_ColorId);
1016 }
1017 if (root == d->keyRoot) {
1018 fillRect_Paint(&p, (iRect){
1019 topLeft_Rect(bounds),
1020 init_I2(width_Rect(bounds), gap_UI / 2)
1021 }, uiBackgroundSelected_ColorId);
1022 }
1023 }
986 } 1024 }
987 } 1025 }
988 setCurrent_Root(NULL); 1026 setCurrent_Root(NULL);
diff --git a/src/ui/window.h b/src/ui/window.h
index de0133b1..b2b22e90 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -68,6 +68,7 @@ struct Impl_Window {
68 SDL_Renderer *render; 68 SDL_Renderer *render;
69 iInt2 size; 69 iInt2 size;
70 iRoot * roots[2]; /* root widget and UI state; second one is for split mode */ 70 iRoot * roots[2]; /* root widget and UI state; second one is for split mode */
71 iRoot * keyRoot; /* root that has the current keyboard input focus */
71 iWidget * hover; 72 iWidget * hover;
72 iWidget * mouseGrab; 73 iWidget * mouseGrab;
73 iWidget * focus; 74 iWidget * focus;
@@ -93,6 +94,7 @@ void resize_Window (iWindow *, int w, int h);
93void setTitle_Window (iWindow *, const iString *title); 94void setTitle_Window (iWindow *, const iString *title);
94void setUiScale_Window (iWindow *, float uiScale); 95void setUiScale_Window (iWindow *, float uiScale);
95void setFreezeDraw_Window (iWindow *, iBool freezeDraw); 96void setFreezeDraw_Window (iWindow *, iBool freezeDraw);
97iBool setKeyRoot_Window (iWindow *, iRoot *root);
96void setCursor_Window (iWindow *, int cursor); 98void setCursor_Window (iWindow *, int cursor);
97void setSnap_Window (iWindow *, int snapMode); 99void setSnap_Window (iWindow *, int snapMode);
98void setKeyboardHeight_Window(iWindow *, int height); 100void setKeyboardHeight_Window(iWindow *, int height);
@@ -111,6 +113,7 @@ SDL_Renderer *renderer_Window (const iWindow *);
111int snap_Window (const iWindow *); 113int snap_Window (const iWindow *);
112iBool isFullscreen_Window (const iWindow *); 114iBool isFullscreen_Window (const iWindow *);
113iRoot * findRoot_Window (const iWindow *, const iWidget *widget); 115iRoot * findRoot_Window (const iWindow *, const iWidget *widget);
116iRoot * otherRoot_Window (const iWindow *, iRoot *root);
114 117
115iWindow * get_Window (void); 118iWindow * get_Window (void);
116 119