diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-28 14:02:20 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-28 14:02:20 +0300 |
commit | 408597bd4f71a13a511b6af33601dff0be2ed317 (patch) | |
tree | 44f6b776ddab24bb5d1b18179872546125c3d679 /src | |
parent | 852689943bfbbbb933ba617cb0a971f82923a2f3 (diff) |
Working on multiple UI roots
Various refactorings and fixes to handle root-global and window-global state, root-specific palettes, and proper coordinate system changes (e.g., when opening menus).
UI events are posted and handled in the context of a specific root.
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 92 | ||||
-rw-r--r-- | src/app.h | 10 | ||||
-rw-r--r-- | src/history.c | 15 | ||||
-rw-r--r-- | src/periodic.c | 5 | ||||
-rw-r--r-- | src/ui/color.c | 21 | ||||
-rw-r--r-- | src/ui/color.h | 11 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 51 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 6 | ||||
-rw-r--r-- | src/ui/keys.c | 2 | ||||
-rw-r--r-- | src/ui/lookupwidget.c | 14 | ||||
-rw-r--r-- | src/ui/root.c | 17 | ||||
-rw-r--r-- | src/ui/root.h | 6 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 12 | ||||
-rw-r--r-- | src/ui/touch.c | 3 | ||||
-rw-r--r-- | src/ui/util.c | 27 | ||||
-rw-r--r-- | src/ui/util.h | 4 | ||||
-rw-r--r-- | src/ui/widget.c | 107 | ||||
-rw-r--r-- | src/ui/widget.h | 16 | ||||
-rw-r--r-- | src/ui/window.c | 31 | ||||
-rw-r--r-- | src/ui/window.h | 3 |
20 files changed, 269 insertions, 184 deletions
@@ -346,7 +346,7 @@ static void loadPrefs_App_(iApp *d) { | |||
346 | } | 346 | } |
347 | #endif | 347 | #endif |
348 | else { | 348 | else { |
349 | postCommandString_App(&cmdStr); | 349 | postCommandString_Root(NULL, &cmdStr); |
350 | } | 350 | } |
351 | deinit_String(&cmdStr); | 351 | deinit_String(&cmdStr); |
352 | } | 352 | } |
@@ -399,18 +399,25 @@ static iBool loadState_App_(iApp *d) { | |||
399 | return iFalse; | 399 | return iFalse; |
400 | } | 400 | } |
401 | setVersion_Stream(stream_File(f), version); | 401 | setVersion_Stream(stream_File(f), version); |
402 | iDocumentWidget *doc = document_App(); /* first one is always from root 0 */ | 402 | iDocumentWidget *doc = NULL; // document_App(); /* first one is always from root 0 */ |
403 | iDocumentWidget *current = NULL; | 403 | iBool isFirstTab[2] = { iTrue, iTrue }; |
404 | iDocumentWidget *current[2] = { NULL, NULL }; | ||
404 | while (!atEnd_File(f)) { | 405 | while (!atEnd_File(f)) { |
405 | readData_File(f, 4, magic); | 406 | readData_File(f, 4, magic); |
406 | if (!memcmp(magic, magicTabDocument_App_, 4)) { | 407 | if (!memcmp(magic, magicTabDocument_App_, 4)) { |
407 | const int8_t flags = read8_File(f); | 408 | const int8_t flags = read8_File(f); |
408 | if (!doc) { | 409 | const int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; |
409 | setCurrent_Root(d->window->roots[flags & rootIndex1_DocumentStateFlag ? 1 : 0]); | 410 | setCurrent_Root(d->window->roots[rootIndex]); |
411 | if (isFirstTab[rootIndex]) { | ||
412 | isFirstTab[rootIndex] = iFalse; | ||
413 | /* There is one pre-created tab in each root. */ | ||
414 | doc = document_Root(get_Root()); | ||
415 | } | ||
416 | else { | ||
410 | doc = newTab_App(NULL, iFalse /* no switching */); | 417 | doc = newTab_App(NULL, iFalse /* no switching */); |
411 | } | 418 | } |
412 | if (flags & current_DocumentStateFlag) { | 419 | if (flags & current_DocumentStateFlag) { |
413 | current = doc; | 420 | current[rootIndex] = doc; |
414 | } | 421 | } |
415 | deserializeState_DocumentWidget(doc, stream_File(f)); | 422 | deserializeState_DocumentWidget(doc, stream_File(f)); |
416 | doc = NULL; | 423 | doc = NULL; |
@@ -421,7 +428,9 @@ static iBool loadState_App_(iApp *d) { | |||
421 | return iFalse; | 428 | return iFalse; |
422 | } | 429 | } |
423 | } | 430 | } |
424 | postCommandf_App("tabs.switch page:%p", current); | 431 | iForIndices(i, current) { |
432 | postCommandf_Root(NULL, "tabs.switch page:%p", current[i]); | ||
433 | } | ||
425 | setCurrent_Root(NULL); | 434 | setCurrent_Root(NULL); |
426 | return iTrue; | 435 | return iTrue; |
427 | } | 436 | } |
@@ -466,7 +475,7 @@ static uint32_t checkAsleep_App_(uint32_t interval, void *param) { | |||
466 | 475 | ||
467 | static uint32_t postAutoReloadCommand_App_(uint32_t interval, void *param) { | 476 | static uint32_t postAutoReloadCommand_App_(uint32_t interval, void *param) { |
468 | iUnused(param); | 477 | iUnused(param); |
469 | postCommand_App("document.autoreload"); | 478 | postCommand_Root(NULL, "document.autoreload"); |
470 | return interval; | 479 | return interval; |
471 | } | 480 | } |
472 | 481 | ||
@@ -660,7 +669,6 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
660 | d->bookmarks = new_Bookmarks(); | 669 | d->bookmarks = new_Bookmarks(); |
661 | d->tabEnum = 0; /* generates unique IDs for tab pages */ | 670 | d->tabEnum = 0; /* generates unique IDs for tab pages */ |
662 | init_Periodic(&d->periodic); | 671 | init_Periodic(&d->periodic); |
663 | setThemePalette_Color(d->prefs.theme); | ||
664 | #if defined (iPlatformAppleDesktop) | 672 | #if defined (iPlatformAppleDesktop) |
665 | setupApplication_MacOS(); | 673 | setupApplication_MacOS(); |
666 | #endif | 674 | #endif |
@@ -691,11 +699,12 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
691 | /* Widget state init. */ | 699 | /* Widget state init. */ |
692 | processEvents_App(postedEventsOnly_AppEventMode); | 700 | processEvents_App(postedEventsOnly_AppEventMode); |
693 | if (!loadState_App_(d)) { | 701 | if (!loadState_App_(d)) { |
694 | postCommand_App("open url:about:help"); | 702 | postCommand_Root(NULL, "open url:about:help"); |
695 | } | 703 | } |
696 | postCommand_App("window.unfreeze"); | 704 | //setThemePalette_Color(d->prefs.theme); |
705 | postCommand_Root(NULL, "window.unfreeze"); | ||
697 | d->autoReloadTimer = SDL_AddTimer(60 * 1000, postAutoReloadCommand_App_, NULL); | 706 | d->autoReloadTimer = SDL_AddTimer(60 * 1000, postAutoReloadCommand_App_, NULL); |
698 | postCommand_App("document.autoreload"); | 707 | postCommand_Root(NULL, "document.autoreload"); |
699 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) | 708 | #if defined (LAGRANGE_ENABLE_IDLE_SLEEP) |
700 | d->isIdling = iFalse; | 709 | d->isIdling = iFalse; |
701 | d->lastEventTime = 0; | 710 | d->lastEventTime = 0; |
@@ -704,12 +713,12 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
704 | d->isFinishedLaunching = iTrue; | 713 | d->isFinishedLaunching = iTrue; |
705 | /* Run any commands that were pending completion of launch. */ { | 714 | /* Run any commands that were pending completion of launch. */ { |
706 | iForEach(StringList, i, d->launchCommands) { | 715 | iForEach(StringList, i, d->launchCommands) { |
707 | postCommandString_App(i.value); | 716 | postCommandString_Root(NULL, i.value); |
708 | } | 717 | } |
709 | } | 718 | } |
710 | /* URLs from the command line. */ { | 719 | /* URLs from the command line. */ { |
711 | iConstForEach(StringList, i, openCmds) { | 720 | iConstForEach(StringList, i, openCmds) { |
712 | postCommandString_App(i.value); | 721 | postCommandString_Root(NULL, i.value); |
713 | } | 722 | } |
714 | iRelease(openCmds); | 723 | iRelease(openCmds); |
715 | } | 724 | } |
@@ -824,7 +833,8 @@ const iString *debugInfo_App(void) { | |||
824 | appendFormat_String(msg, "## Documents\n"); | 833 | appendFormat_String(msg, "## Documents\n"); |
825 | iForEach(ObjectList, k, iClob(listDocuments_App(NULL))) { | 834 | iForEach(ObjectList, k, iClob(listDocuments_App(NULL))) { |
826 | iDocumentWidget *doc = k.object; | 835 | iDocumentWidget *doc = k.object; |
827 | appendFormat_String(msg, "### Tab %zu: %s\n", | 836 | appendFormat_String(msg, "### Tab %d.%zu: %s\n", |
837 | constAs_Widget(doc)->root == get_Window()->roots[0] ? 0 : 1, | ||
828 | childIndex_Widget(constAs_Widget(doc)->parent, k.object), | 838 | childIndex_Widget(constAs_Widget(doc)->parent, k.object), |
829 | cstr_String(bookmarkTitle_DocumentWidget(doc))); | 839 | cstr_String(bookmarkTitle_DocumentWidget(doc))); |
830 | append_String(msg, collect_String(debugInfo_History(history_DocumentWidget(doc)))); | 840 | append_String(msg, collect_String(debugInfo_History(history_DocumentWidget(doc)))); |
@@ -918,7 +928,6 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
918 | iApp *d = &app_; | 928 | iApp *d = &app_; |
919 | SDL_Event ev; | 929 | SDL_Event ev; |
920 | iBool gotEvents = iFalse; | 930 | iBool gotEvents = iFalse; |
921 | dispatchCommands_Periodic(&d->periodic); | ||
922 | while (nextEvent_App_(d, eventMode, &ev)) { | 931 | while (nextEvent_App_(d, eventMode, &ev)) { |
923 | #if defined (iPlatformAppleMobile) | 932 | #if defined (iPlatformAppleMobile) |
924 | if (processEvent_iOS(&ev)) { | 933 | if (processEvent_iOS(&ev)) { |
@@ -930,7 +939,7 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
930 | d->isRunning = iFalse; | 939 | d->isRunning = iFalse; |
931 | if (findWidget_App("prefs")) { | 940 | if (findWidget_App("prefs")) { |
932 | /* Make sure changed preferences get saved. */ | 941 | /* Make sure changed preferences get saved. */ |
933 | postCommand_App("prefs.dismiss"); | 942 | postCommand_Root(NULL, "prefs.dismiss"); |
934 | processEvents_App(postedEventsOnly_AppEventMode); | 943 | processEvents_App(postedEventsOnly_AppEventMode); |
935 | } | 944 | } |
936 | goto backToMainLoop; | 945 | goto backToMainLoop; |
@@ -964,10 +973,10 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
964 | if (startsWithCase_CStr(ev.drop.file, "gemini:") || | 973 | if (startsWithCase_CStr(ev.drop.file, "gemini:") || |
965 | startsWithCase_CStr(ev.drop.file, "gopher:") || | 974 | startsWithCase_CStr(ev.drop.file, "gopher:") || |
966 | startsWithCase_CStr(ev.drop.file, "file:")) { | 975 | startsWithCase_CStr(ev.drop.file, "file:")) { |
967 | postCommandf_App("~open newtab:%d url:%s", newTab, ev.drop.file); | 976 | postCommandf_Root(NULL, "~open newtab:%d url:%s", newTab, ev.drop.file); |
968 | } | 977 | } |
969 | else { | 978 | else { |
970 | postCommandf_App( | 979 | postCommandf_Root(NULL, |
971 | "~open newtab:%d url:%s", newTab, makeFileUrl_CStr(ev.drop.file)); | 980 | "~open newtab:%d url:%s", newTab, makeFileUrl_CStr(ev.drop.file)); |
972 | } | 981 | } |
973 | } | 982 | } |
@@ -1094,7 +1103,7 @@ static void runTickers_App_(iApp *d) { | |||
1094 | iConstForEach(Array, i, &pending->values) { | 1103 | iConstForEach(Array, i, &pending->values) { |
1095 | const iTicker *ticker = i.value; | 1104 | const iTicker *ticker = i.value; |
1096 | if (ticker->callback) { | 1105 | if (ticker->callback) { |
1097 | setCurrent_Root(findRoot_Window(d->window, ticker->context)); | 1106 | setCurrent_Root(findRoot_Window(d->window, ticker->context)); /* root might be NULL */ |
1098 | ticker->callback(ticker->context); | 1107 | ticker->callback(ticker->context); |
1099 | } | 1108 | } |
1100 | } | 1109 | } |
@@ -1135,6 +1144,7 @@ static int run_App_(iApp *d) { | |||
1135 | SDL_AddEventWatch(resizeWatcher_, d); /* redraw window during resizing */ | 1144 | SDL_AddEventWatch(resizeWatcher_, d); /* redraw window during resizing */ |
1136 | #endif | 1145 | #endif |
1137 | while (d->isRunning) { | 1146 | while (d->isRunning) { |
1147 | dispatchCommands_Periodic(&d->periodic); | ||
1138 | processEvents_App(waitForNewEvents_AppEventMode); | 1148 | processEvents_App(waitForNewEvents_AppEventMode); |
1139 | setCurrent_Root(NULL); | 1149 | setCurrent_Root(NULL); |
1140 | runTickers_App_(d); | 1150 | runTickers_App_(d); |
@@ -1242,8 +1252,7 @@ void postRefresh_App(void) { | |||
1242 | } | 1252 | } |
1243 | } | 1253 | } |
1244 | 1254 | ||
1245 | void postCommand_App(const char *command) { | 1255 | void postCommand_Root(iRoot *d, const char *command) { |
1246 | iApp *d = &app_; | ||
1247 | iAssert(command); | 1256 | iAssert(command); |
1248 | if (strlen(command) == 0) { | 1257 | if (strlen(command) == 0) { |
1249 | return; | 1258 | return; |
@@ -1255,8 +1264,8 @@ void postCommand_App(const char *command) { | |||
1255 | if (*command == '~') { | 1264 | if (*command == '~') { |
1256 | /* Requires launch to be finished; defer it if needed. */ | 1265 | /* Requires launch to be finished; defer it if needed. */ |
1257 | command++; | 1266 | command++; |
1258 | if (!d->isFinishedLaunching) { | 1267 | if (!app_.isFinishedLaunching) { |
1259 | pushBackCStr_StringList(d->launchCommands, command); | 1268 | pushBackCStr_StringList(app_.launchCommands, command); |
1260 | return; | 1269 | return; |
1261 | } | 1270 | } |
1262 | } | 1271 | } |
@@ -1264,12 +1273,26 @@ void postCommand_App(const char *command) { | |||
1264 | ev.user.code = command_UserEventCode; | 1273 | ev.user.code = command_UserEventCode; |
1265 | /*ev.user.windowID = id_Window(get_Window());*/ | 1274 | /*ev.user.windowID = id_Window(get_Window());*/ |
1266 | ev.user.data1 = strdup(command); | 1275 | ev.user.data1 = strdup(command); |
1276 | ev.user.data2 = d; /* all events are root-specific */ | ||
1267 | SDL_PushEvent(&ev); | 1277 | SDL_PushEvent(&ev); |
1268 | if (app_.commandEcho) { | 1278 | if (app_.commandEcho) { |
1269 | printf("[command] %s\n", command); fflush(stdout); | 1279 | printf("[command] {%d} %s\n", |
1280 | (d == NULL ? 0 : d == get_Window()->roots[0] ? 1 : 2), | ||
1281 | command); fflush(stdout); | ||
1270 | } | 1282 | } |
1271 | } | 1283 | } |
1272 | 1284 | ||
1285 | void postCommandf_Root(iRoot *d, const char *command, ...) { | ||
1286 | iBlock chars; | ||
1287 | init_Block(&chars, 0); | ||
1288 | va_list args; | ||
1289 | va_start(args, command); | ||
1290 | vprintf_Block(&chars, command, args); | ||
1291 | va_end(args); | ||
1292 | postCommand_Root(d, cstr_Block(&chars)); | ||
1293 | deinit_Block(&chars); | ||
1294 | } | ||
1295 | |||
1273 | void postCommandf_App(const char *command, ...) { | 1296 | void postCommandf_App(const char *command, ...) { |
1274 | iBlock chars; | 1297 | iBlock chars; |
1275 | init_Block(&chars, 0); | 1298 | init_Block(&chars, 0); |
@@ -1277,16 +1300,23 @@ void postCommandf_App(const char *command, ...) { | |||
1277 | va_start(args, command); | 1300 | va_start(args, command); |
1278 | vprintf_Block(&chars, command, args); | 1301 | vprintf_Block(&chars, command, args); |
1279 | va_end(args); | 1302 | va_end(args); |
1280 | postCommand_App(cstr_Block(&chars)); | 1303 | postCommand_Root(NULL, cstr_Block(&chars)); |
1281 | deinit_Block(&chars); | 1304 | deinit_Block(&chars); |
1282 | } | 1305 | } |
1283 | 1306 | ||
1307 | void rootOrder_App(iRoot *roots[2]) { | ||
1308 | const iWindow *win = app_.window; | ||
1309 | roots[0] = get_Root(); | ||
1310 | roots[1] = (roots[0] == win->roots[0] ? win->roots[1] : win->roots[0]); | ||
1311 | } | ||
1312 | |||
1284 | iAny *findWidget_App(const char *id) { | 1313 | iAny *findWidget_App(const char *id) { |
1285 | if (!*id) return NULL; | 1314 | if (!*id) return NULL; |
1286 | iForIndices(i, app_.window->roots) { | 1315 | iRoot *order[2]; |
1287 | iRoot *root = app_.window->roots[i]; | 1316 | rootOrder_App(order); |
1288 | if (root) { | 1317 | iForIndices(i, order) { |
1289 | iAny *found = findChild_Widget(root->widget, id); | 1318 | if (order[i]) { |
1319 | iAny *found = findChild_Widget(order[i]->widget, id); | ||
1290 | if (found) { | 1320 | if (found) { |
1291 | return found; | 1321 | return found; |
1292 | } | 1322 | } |
@@ -1485,7 +1515,7 @@ iDocumentWidget *document_Command(const char *cmd) { | |||
1485 | 1515 | ||
1486 | iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNew) { | 1516 | iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNew) { |
1487 | iApp *d = &app_; | 1517 | iApp *d = &app_; |
1488 | iWidget *tabs = findWidget_App("doctabs"); | 1518 | iWidget *tabs = findWidget_Root("doctabs"); |
1489 | setFlags_Widget(tabs, hidden_WidgetFlag, iFalse); | 1519 | setFlags_Widget(tabs, hidden_WidgetFlag, iFalse); |
1490 | iWidget *newTabButton = findChild_Widget(tabs, "newtab"); | 1520 | iWidget *newTabButton = findChild_Widget(tabs, "newtab"); |
1491 | removeChild_Widget(newTabButton->parent, newTabButton); | 1521 | removeChild_Widget(newTabButton->parent, newTabButton); |
@@ -107,14 +107,18 @@ iAny * findWidget_App (const char *id); | |||
107 | void addTicker_App (iTickerFunc ticker, iAny *context); | 107 | void addTicker_App (iTickerFunc ticker, iAny *context); |
108 | void removeTicker_App (iTickerFunc ticker, iAny *context); | 108 | void removeTicker_App (iTickerFunc ticker, iAny *context); |
109 | void postRefresh_App (void); | 109 | void postRefresh_App (void); |
110 | void postCommand_App (const char *command); | 110 | void postCommand_Root (iRoot *, const char *command); |
111 | void postCommandf_Root (iRoot *, const char *command, ...); | ||
111 | void postCommandf_App (const char *command, ...); | 112 | void postCommandf_App (const char *command, ...); |
112 | 113 | ||
113 | iLocalDef void postCommandString_App(const iString *command) { | 114 | iLocalDef void postCommandString_Root(iRoot *d, const iString *command) { |
114 | if (command) { | 115 | if (command) { |
115 | postCommand_App(cstr_String(command)); | 116 | postCommand_Root(d, cstr_String(command)); |
116 | } | 117 | } |
117 | } | 118 | } |
119 | iLocalDef void postCommand_App(const char *command) { | ||
120 | postCommandf_App(command); | ||
121 | } | ||
118 | 122 | ||
119 | iDocumentWidget * document_Command (const char *cmd); | 123 | iDocumentWidget * document_Command (const char *cmd); |
120 | 124 | ||
diff --git a/src/history.c b/src/history.c index 48820f6d..9f4e415b 100644 --- a/src/history.c +++ b/src/history.c | |||
@@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "history.h" | 23 | #include "history.h" |
24 | #include "ui/root.h" | ||
24 | #include "app.h" | 25 | #include "app.h" |
25 | 26 | ||
26 | #include <the_Foundation/file.h> | 27 | #include <the_Foundation/file.h> |
@@ -244,9 +245,10 @@ iBool goBack_History(iHistory *d) { | |||
244 | lock_Mutex(d->mtx); | 245 | lock_Mutex(d->mtx); |
245 | if (d->recentPos < size_Array(&d->recent) - 1) { | 246 | if (d->recentPos < size_Array(&d->recent) - 1) { |
246 | d->recentPos++; | 247 | d->recentPos++; |
247 | postCommandf_App("open history:1 scroll:%f url:%s", | 248 | postCommandf_Root(get_Root(), |
248 | mostRecentUrl_History(d)->normScrollY, | 249 | "open history:1 scroll:%f url:%s", |
249 | cstr_String(url_History(d, d->recentPos))); | 250 | mostRecentUrl_History(d)->normScrollY, |
251 | cstr_String(url_History(d, d->recentPos))); | ||
250 | unlock_Mutex(d->mtx); | 252 | unlock_Mutex(d->mtx); |
251 | return iTrue; | 253 | return iTrue; |
252 | } | 254 | } |
@@ -258,9 +260,10 @@ iBool goForward_History(iHistory *d) { | |||
258 | lock_Mutex(d->mtx); | 260 | lock_Mutex(d->mtx); |
259 | if (d->recentPos > 0) { | 261 | if (d->recentPos > 0) { |
260 | d->recentPos--; | 262 | d->recentPos--; |
261 | postCommandf_App("open history:1 scroll:%f url:%s", | 263 | postCommandf_Root(get_Root(), |
262 | mostRecentUrl_History(d)->normScrollY, | 264 | "open history:1 scroll:%f url:%s", |
263 | cstr_String(url_History(d, d->recentPos))); | 265 | mostRecentUrl_History(d)->normScrollY, |
266 | cstr_String(url_History(d, d->recentPos))); | ||
264 | unlock_Mutex(d->mtx); | 267 | unlock_Mutex(d->mtx); |
265 | return iTrue; | 268 | return iTrue; |
266 | } | 269 | } |
diff --git a/src/periodic.c b/src/periodic.c index eb4fb98d..0291ff98 100644 --- a/src/periodic.c +++ b/src/periodic.c | |||
@@ -70,10 +70,11 @@ iBool dispatchCommands_Periodic(iPeriodic *d) { | |||
70 | const SDL_UserEvent ev = { | 70 | const SDL_UserEvent ev = { |
71 | .type = SDL_USEREVENT, | 71 | .type = SDL_USEREVENT, |
72 | .code = command_UserEventCode, | 72 | .code = command_UserEventCode, |
73 | .data1 = (void *) cstr_String(&pc->command) | 73 | .data1 = (void *) cstr_String(&pc->command), |
74 | .data2 = findRoot_Window(get_Window(), pc->context) | ||
74 | }; | 75 | }; |
75 | iAssert(isInstance_Object(pc->context, &Class_Widget)); | 76 | iAssert(isInstance_Object(pc->context, &Class_Widget)); |
76 | setCurrent_Root(findRoot_Window(get_Window(), pc->context)); | 77 | setCurrent_Root(ev.data2); |
77 | dispatchEvent_Widget(pc->context, (const SDL_Event *) &ev); | 78 | dispatchEvent_Widget(pc->context, (const SDL_Event *) &ev); |
78 | wasPosted = iTrue; | 79 | wasPosted = iTrue; |
79 | } | 80 | } |
diff --git a/src/ui/color.c b/src/ui/color.c index bcecb529..6cbbdf28 100644 --- a/src/ui/color.c +++ b/src/ui/color.c | |||
@@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "color.h" | 23 | #include "color.h" |
24 | #include "root.h" | ||
24 | #include "app.h" | 25 | #include "app.h" |
25 | 26 | ||
26 | #include <the_Foundation/string.h> | 27 | #include <the_Foundation/string.h> |
@@ -65,7 +66,11 @@ static const iColor lightPalette_[] = { | |||
65 | { 0, 150, 0, 255 }, | 66 | { 0, 150, 0, 255 }, |
66 | }; | 67 | }; |
67 | 68 | ||
68 | static iColor palette_[max_ColorId]; | 69 | static iColor uiPalette_[tmFirst_ColorId]; /* not theme-specific */ |
70 | |||
71 | iColor *paletteColor_(enum iColorId id) { | ||
72 | return id < tmFirst_ColorId ? &uiPalette_[id] : &get_Root()->tmPalette[id - tmFirst_ColorId]; | ||
73 | } | ||
69 | 74 | ||
70 | iLocalDef void copy_(enum iColorId dst, enum iColorId src) { | 75 | iLocalDef void copy_(enum iColorId dst, enum iColorId src) { |
71 | set_Color(dst, get_Color(src)); | 76 | set_Color(dst, get_Color(src)); |
@@ -73,7 +78,7 @@ iLocalDef void copy_(enum iColorId dst, enum iColorId src) { | |||
73 | 78 | ||
74 | void setThemePalette_Color(enum iColorTheme theme) { | 79 | void setThemePalette_Color(enum iColorTheme theme) { |
75 | const iPrefs *prefs = prefs_App(); | 80 | const iPrefs *prefs = prefs_App(); |
76 | memcpy(palette_, isDark_ColorTheme(theme) ? darkPalette_ : lightPalette_, sizeof(darkPalette_)); | 81 | memcpy(uiPalette_, isDark_ColorTheme(theme) ? darkPalette_ : lightPalette_, sizeof(darkPalette_)); |
77 | const int accentHi = (prefs->accent == cyan_ColorAccent ? cyan_ColorId : orange_ColorId); | 82 | const int accentHi = (prefs->accent == cyan_ColorAccent ? cyan_ColorId : orange_ColorId); |
78 | const int accentLo = (prefs->accent == cyan_ColorAccent ? teal_ColorId : brown_ColorId); | 83 | const int accentLo = (prefs->accent == cyan_ColorAccent ? teal_ColorId : brown_ColorId); |
79 | const int altAccentHi = (prefs->accent == cyan_ColorAccent ? orange_ColorId : cyan_ColorId); | 84 | const int altAccentHi = (prefs->accent == cyan_ColorAccent ? orange_ColorId : cyan_ColorId); |
@@ -298,8 +303,8 @@ void setThemePalette_Color(enum iColorTheme theme) { | |||
298 | set_Color(uiTextShortcut_ColorId, mix_Color(get_Color(uiTextShortcut_ColorId), | 303 | set_Color(uiTextShortcut_ColorId, mix_Color(get_Color(uiTextShortcut_ColorId), |
299 | get_Color(uiBackground_ColorId), | 304 | get_Color(uiBackground_ColorId), |
300 | 0.4f)); | 305 | 0.4f)); |
301 | palette_[uiMarked_ColorId].a = 128; | 306 | uiPalette_[uiMarked_ColorId ].a = 128; |
302 | palette_[uiMatching_ColorId].a = 128; | 307 | uiPalette_[uiMatching_ColorId].a = 128; |
303 | if (deviceType_App() == phone_AppDeviceType) { | 308 | if (deviceType_App() == phone_AppDeviceType) { |
304 | copy_(uiInputBackground_ColorId, uiBackgroundSidebar_ColorId); | 309 | copy_(uiInputBackground_ColorId, uiBackgroundSidebar_ColorId); |
305 | copy_(uiInputFrame_ColorId, uiBackgroundSidebar_ColorId); | 310 | copy_(uiInputFrame_ColorId, uiBackgroundSidebar_ColorId); |
@@ -311,14 +316,14 @@ void setThemePalette_Color(enum iColorTheme theme) { | |||
311 | iColor get_Color(int color) { | 316 | iColor get_Color(int color) { |
312 | const iColor *rgba = &transparent_; | 317 | const iColor *rgba = &transparent_; |
313 | if (color >= 0 && color < max_ColorId) { | 318 | if (color >= 0 && color < max_ColorId) { |
314 | rgba = &palette_[color]; | 319 | rgba = paletteColor_(color); |
315 | } | 320 | } |
316 | return *rgba; | 321 | return *rgba; |
317 | } | 322 | } |
318 | 323 | ||
319 | void set_Color(int color, iColor rgba) { | 324 | void set_Color(int color, iColor rgba) { |
320 | if (color >= uiBackground_ColorId && color < max_ColorId) { | 325 | if (color >= uiBackground_ColorId && color < max_ColorId) { |
321 | palette_[color] = rgba; | 326 | *paletteColor_(color) = rgba; |
322 | } | 327 | } |
323 | } | 328 | } |
324 | 329 | ||
@@ -341,7 +346,7 @@ iLocalDef iBool equal_Color_(const iColor *x, const iColor *y) { | |||
341 | int darker_Color(int color) { | 346 | int darker_Color(int color) { |
342 | const iColor rgb = get_Color(color); | 347 | const iColor rgb = get_Color(color); |
343 | for (int i = 0; i < uiFirst_ColorId; i++) { | 348 | for (int i = 0; i < uiFirst_ColorId; i++) { |
344 | if (equal_Color_(&rgb, &palette_[i])) { | 349 | if (equal_Color_(&rgb, paletteColor_(i))) { |
345 | return i > 0 ? i - 1 : i; | 350 | return i > 0 ? i - 1 : i; |
346 | } | 351 | } |
347 | } | 352 | } |
@@ -351,7 +356,7 @@ int darker_Color(int color) { | |||
351 | int lighter_Color(int color) { | 356 | int lighter_Color(int color) { |
352 | const iColor rgb = get_Color(color); | 357 | const iColor rgb = get_Color(color); |
353 | for (int i = 0; i < uiFirst_ColorId; i++) { | 358 | for (int i = 0; i < uiFirst_ColorId; i++) { |
354 | if (equal_Color_(&rgb, &palette_[i])) { | 359 | if (equal_Color_(&rgb, paletteColor_(i))) { |
355 | return i < uiFirst_ColorId - 1 ? i + 1 : i; | 360 | return i < uiFirst_ColorId - 1 ? i + 1 : i; |
356 | } | 361 | } |
357 | } | 362 | } |
diff --git a/src/ui/color.h b/src/ui/color.h index cd2f95d3..c0db4382 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -115,9 +115,7 @@ enum iColorId { | |||
115 | uiTextAppTitle_ColorId, | 115 | uiTextAppTitle_ColorId, |
116 | uiBackgroundSidebar_ColorId, | 116 | uiBackgroundSidebar_ColorId, |
117 | uiBackgroundMenu_ColorId, | 117 | uiBackgroundMenu_ColorId, |
118 | tmLinkCustomIconVisited_ColorId, /* derived from other theme colors */ | 118 | |
119 | tmAltTextBackground_ColorId, /* derived from other theme colors */ | ||
120 | |||
121 | /* content theme colors */ | 119 | /* content theme colors */ |
122 | tmFirst_ColorId, | 120 | tmFirst_ColorId, |
123 | tmBackground_ColorId = tmFirst_ColorId, | 121 | tmBackground_ColorId = tmFirst_ColorId, |
@@ -133,9 +131,9 @@ enum iColorId { | |||
133 | tmBannerTitle_ColorId, | 131 | tmBannerTitle_ColorId, |
134 | tmBannerIcon_ColorId, | 132 | tmBannerIcon_ColorId, |
135 | tmBannerSideTitle_ColorId, | 133 | tmBannerSideTitle_ColorId, |
136 | tmOutlineHeadingAbove_ColorId, | ||
137 | tmOutlineHeadingBelow_ColorId, | ||
138 | tmInlineContentMetadata_ColorId, | 134 | tmInlineContentMetadata_ColorId, |
135 | tmAltTextBackground_ColorId, /* derived from other theme colors */ | ||
136 | tmLinkCustomIconVisited_ColorId, /* derived from other theme colors */ | ||
139 | tmBadLink_ColorId, | 137 | tmBadLink_ColorId, |
140 | 138 | ||
141 | tmLinkIcon_ColorId, | 139 | tmLinkIcon_ColorId, |
@@ -159,7 +157,8 @@ enum iColorId { | |||
159 | tmGopherLinkDomain_ColorId, | 157 | tmGopherLinkDomain_ColorId, |
160 | tmGopherLinkLastVisitDate_ColorId, | 158 | tmGopherLinkLastVisitDate_ColorId, |
161 | 159 | ||
162 | max_ColorId | 160 | max_ColorId, |
161 | tmMax_ColorId = max_ColorId - tmFirst_ColorId | ||
163 | }; | 162 | }; |
164 | 163 | ||
165 | iLocalDef iBool isLink_ColorId(enum iColorId d) { | 164 | iLocalDef iBool isLink_ColorId(enum iColorId d) { |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 79678f56..2f20958f 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1234,7 +1234,10 @@ static void fetch_DocumentWidget_(iDocumentWidget *d) { | |||
1234 | iRelease(d->request); | 1234 | iRelease(d->request); |
1235 | d->request = NULL; | 1235 | d->request = NULL; |
1236 | } | 1236 | } |
1237 | postCommandf_App("document.request.started doc:%p url:%s", d, cstr_String(d->mod.url)); | 1237 | postCommandf_Root(as_Widget(d)->root, |
1238 | "document.request.started doc:%p url:%s", | ||
1239 | d, | ||
1240 | cstr_String(d->mod.url)); | ||
1238 | clear_ObjectList(d->media); | 1241 | clear_ObjectList(d->media); |
1239 | d->certFlags = 0; | 1242 | d->certFlags = 0; |
1240 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 1243 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
@@ -1318,7 +1321,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | |||
1318 | updateVisible_DocumentWidget_(d); | 1321 | updateVisible_DocumentWidget_(d); |
1319 | moveSpan_SmoothScroll(&d->scrollY, 0, 0); /* clamp position to new max */ | 1322 | moveSpan_SmoothScroll(&d->scrollY, 0, 0); /* clamp position to new max */ |
1320 | cacheDocumentGlyphs_DocumentWidget_(d); | 1323 | cacheDocumentGlyphs_DocumentWidget_(d); |
1321 | postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 1324 | postCommandf_Root(as_Widget(d)->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); |
1322 | return iTrue; | 1325 | return iTrue; |
1323 | } | 1326 | } |
1324 | else if (!isEmpty_String(d->mod.url)) { | 1327 | else if (!isEmpty_String(d->mod.url)) { |
@@ -1391,7 +1394,7 @@ static void scrollToHeading_DocumentWidget_(iDocumentWidget *d, const char *head | |||
1391 | iConstForEach(Array, h, headings_GmDocument(d->doc)) { | 1394 | iConstForEach(Array, h, headings_GmDocument(d->doc)) { |
1392 | const iGmHeading *head = h.value; | 1395 | const iGmHeading *head = h.value; |
1393 | if (startsWithCase_Rangecc(head->text, heading)) { | 1396 | if (startsWithCase_Rangecc(head->text, heading)) { |
1394 | postCommandf_App("document.goto loc:%p", head->text.start); | 1397 | postCommandf_Root(as_Widget(d)->root, "document.goto loc:%p", head->text.start); |
1395 | break; | 1398 | break; |
1396 | } | 1399 | } |
1397 | } | 1400 | } |
@@ -1514,7 +1517,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1514 | cstr_Rangecc(urlScheme_String(d->mod.url)))) { | 1517 | cstr_Rangecc(urlScheme_String(d->mod.url)))) { |
1515 | /* Redirects with the same scheme are automatic. */ | 1518 | /* Redirects with the same scheme are automatic. */ |
1516 | visitUrl_Visited(visited_App(), d->mod.url, transient_VisitedUrlFlag); | 1519 | visitUrl_Visited(visited_App(), d->mod.url, transient_VisitedUrlFlag); |
1517 | postCommandf_App( | 1520 | postCommandf_Root(as_Widget(d)->root, |
1518 | "open doc:%p redirect:%d url:%s", d, d->redirectCount + 1, cstr_String(dstUrl)); | 1521 | "open doc:%p redirect:%d url:%s", d, d->redirectCount + 1, cstr_String(dstUrl)); |
1519 | } | 1522 | } |
1520 | else { | 1523 | else { |
@@ -2037,7 +2040,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2037 | if (!isEmpty_Block(d->certFingerprint) && !isEmpty_Range(&host)) { | 2040 | if (!isEmpty_Block(d->certFingerprint) && !isEmpty_Range(&host)) { |
2038 | setTrusted_GmCerts(certs_App(), host, d->certFingerprint, &d->certExpiry); | 2041 | setTrusted_GmCerts(certs_App(), host, d->certFingerprint, &d->certExpiry); |
2039 | d->certFlags |= trusted_GmCertFlag; | 2042 | d->certFlags |= trusted_GmCertFlag; |
2040 | postCommand_App("document.info"); | 2043 | postCommand_Widget(w, "document.info"); |
2041 | updateTrust_DocumentWidget_(d, NULL); | 2044 | updateTrust_DocumentWidget_(d, NULL); |
2042 | redoLayout_GmDocument(d->doc); | 2045 | redoLayout_GmDocument(d->doc); |
2043 | invalidate_DocumentWidget_(d); | 2046 | invalidate_DocumentWidget_(d); |
@@ -2065,7 +2068,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2065 | SDL_SetClipboardText(cstr_String(copied)); | 2068 | SDL_SetClipboardText(cstr_String(copied)); |
2066 | delete_String(copied); | 2069 | delete_String(copied); |
2067 | if (flags_Widget(w) & touchDrag_WidgetFlag) { | 2070 | if (flags_Widget(w) & touchDrag_WidgetFlag) { |
2068 | postCommand_App("document.select arg:0"); | 2071 | postCommand_Widget(w, "document.select arg:0"); |
2069 | } | 2072 | } |
2070 | return iTrue; | 2073 | return iTrue; |
2071 | } | 2074 | } |
@@ -2102,13 +2105,13 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2102 | } | 2105 | } |
2103 | appendCStr_String(url, "?"); | 2106 | appendCStr_String(url, "?"); |
2104 | append_String(url, value); | 2107 | append_String(url, value); |
2105 | postCommandf_App("open url:%s", cstr_String(url)); | 2108 | postCommandf_Root(w->root, "open url:%s", cstr_String(url)); |
2106 | delete_String(value); | 2109 | delete_String(value); |
2107 | return iTrue; | 2110 | return iTrue; |
2108 | } | 2111 | } |
2109 | else if (equal_Command(cmd, "valueinput.cancelled") && | 2112 | else if (equal_Command(cmd, "valueinput.cancelled") && |
2110 | equal_Rangecc(range_Command(cmd, "id"), "document.input.submit") && document_App() == d) { | 2113 | equal_Rangecc(range_Command(cmd, "id"), "document.input.submit") && document_App() == d) { |
2111 | postCommand_App("navigate.back"); | 2114 | postCommand_Root(get_Root(), "navigate.back"); |
2112 | return iTrue; | 2115 | return iTrue; |
2113 | } | 2116 | } |
2114 | else if (equalWidget_Command(cmd, w, "document.request.updated") && | 2117 | else if (equalWidget_Command(cmd, w, "document.request.updated") && |
@@ -2149,7 +2152,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2149 | iReleasePtr(&d->request); | 2152 | iReleasePtr(&d->request); |
2150 | updateVisible_DocumentWidget_(d); | 2153 | updateVisible_DocumentWidget_(d); |
2151 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 2154 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
2152 | postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 2155 | postCommandf_Root(w->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); |
2153 | /* Check for a pending goto. */ | 2156 | /* Check for a pending goto. */ |
2154 | if (!isEmpty_String(&d->pendingGotoHeading)) { | 2157 | if (!isEmpty_String(&d->pendingGotoHeading)) { |
2155 | scrollToHeading_DocumentWidget_(d, cstr_String(&d->pendingGotoHeading)); | 2158 | scrollToHeading_DocumentWidget_(d, cstr_String(&d->pendingGotoHeading)); |
@@ -2193,12 +2196,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2193 | } | 2196 | } |
2194 | else if (equal_Command(cmd, "document.stop") && document_App() == d) { | 2197 | else if (equal_Command(cmd, "document.stop") && document_App() == d) { |
2195 | if (d->request) { | 2198 | if (d->request) { |
2196 | postCommandf_App( | 2199 | postCommandf_Root(w->root, |
2197 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); | 2200 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); |
2198 | iReleasePtr(&d->request); | 2201 | iReleasePtr(&d->request); |
2199 | if (d->state != ready_RequestState) { | 2202 | if (d->state != ready_RequestState) { |
2200 | d->state = ready_RequestState; | 2203 | d->state = ready_RequestState; |
2201 | postCommand_App("navigate.back"); | 2204 | postCommand_Root(w->root, "navigate.back"); |
2202 | } | 2205 | } |
2203 | updateFetchProgress_DocumentWidget_(d); | 2206 | updateFetchProgress_DocumentWidget_(d); |
2204 | return iTrue; | 2207 | return iTrue; |
@@ -2267,7 +2270,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2267 | } | 2270 | } |
2268 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { | 2271 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { |
2269 | if (d->request) { | 2272 | if (d->request) { |
2270 | postCommandf_App( | 2273 | postCommandf_Root(w->root, |
2271 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); | 2274 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); |
2272 | iReleasePtr(&d->request); | 2275 | iReleasePtr(&d->request); |
2273 | updateFetchProgress_DocumentWidget_(d); | 2276 | updateFetchProgress_DocumentWidget_(d); |
@@ -2291,14 +2294,14 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2291 | if (parts.path.end[-1] == '/') break; | 2294 | if (parts.path.end[-1] == '/') break; |
2292 | parts.path.end--; | 2295 | parts.path.end--; |
2293 | } | 2296 | } |
2294 | postCommandf_App( | 2297 | postCommandf_Root(w->root, |
2295 | "open url:%s", | 2298 | "open url:%s", |
2296 | cstr_Rangecc((iRangecc){ constBegin_String(d->mod.url), parts.path.end })); | 2299 | cstr_Rangecc((iRangecc){ constBegin_String(d->mod.url), parts.path.end })); |
2297 | } | 2300 | } |
2298 | return iTrue; | 2301 | return iTrue; |
2299 | } | 2302 | } |
2300 | else if (equal_Command(cmd, "navigate.root") && document_App() == d) { | 2303 | else if (equal_Command(cmd, "navigate.root") && document_App() == d) { |
2301 | postCommandf_App("open url:%s/", cstr_Rangecc(urlRoot_String(d->mod.url))); | 2304 | postCommandf_Root(w->root, "open url:%s/", cstr_Rangecc(urlRoot_String(d->mod.url))); |
2302 | return iTrue; | 2305 | return iTrue; |
2303 | } | 2306 | } |
2304 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { | 2307 | else if (equalWidget_Command(cmd, w, "scroll.moved")) { |
@@ -2390,7 +2393,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2390 | } | 2393 | } |
2391 | } | 2394 | } |
2392 | if (flags_Widget(w) & touchDrag_WidgetFlag) { | 2395 | if (flags_Widget(w) & touchDrag_WidgetFlag) { |
2393 | postCommand_App("document.select arg:0"); /* we can't handle both at the same time */ | 2396 | postCommand_Root(w->root, "document.select arg:0"); /* we can't handle both at the same time */ |
2394 | } | 2397 | } |
2395 | invalidateWideRunsWithNonzeroOffset_DocumentWidget_(d); /* markers don't support offsets */ | 2398 | invalidateWideRunsWithNonzeroOffset_DocumentWidget_(d); /* markers don't support offsets */ |
2396 | resetWideRuns_DocumentWidget_(d); | 2399 | resetWideRuns_DocumentWidget_(d); |
@@ -2593,8 +2596,7 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev | |||
2593 | { cstrCollect_String(metadataLabel_Player(plr)), 0, 0, NULL }, | 2596 | { cstrCollect_String(metadataLabel_Player(plr)), 0, 0, NULL }, |
2594 | }, | 2597 | }, |
2595 | 1); | 2598 | 1); |
2596 | openMenu_Widget(d->playerMenu, | 2599 | openMenu_Widget(d->playerMenu, bottomLeft_Rect(ui.menuRect)); |
2597 | localCoord_Widget(constAs_Widget(d), bottomLeft_Rect(ui.menuRect))); | ||
2598 | return iTrue; | 2600 | return iTrue; |
2599 | } | 2601 | } |
2600 | } | 2602 | } |
@@ -2699,7 +2701,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
2699 | d->hoverLink = run; | 2701 | d->hoverLink = run; |
2700 | } | 2702 | } |
2701 | else { | 2703 | else { |
2702 | postCommandf_App("open newtab:%d url:%s", | 2704 | postCommandf_Root(w->root, |
2705 | "open newtab:%d url:%s", | ||
2703 | d->ordinalMode == | 2706 | d->ordinalMode == |
2704 | numbersAndAlphabet_DocumentLinkOrdinalMode | 2707 | numbersAndAlphabet_DocumentLinkOrdinalMode |
2705 | ? openTabMode_Sym(modState_Keys()) | 2708 | ? openTabMode_Sym(modState_Keys()) |
@@ -2813,15 +2816,15 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
2813 | } | 2816 | } |
2814 | if (ev->type == SDL_MOUSEBUTTONDOWN) { | 2817 | if (ev->type == SDL_MOUSEBUTTONDOWN) { |
2815 | if (ev->button.button == SDL_BUTTON_X1) { | 2818 | if (ev->button.button == SDL_BUTTON_X1) { |
2816 | postCommand_App("navigate.back"); | 2819 | postCommand_Root(w->root, "navigate.back"); |
2817 | return iTrue; | 2820 | return iTrue; |
2818 | } | 2821 | } |
2819 | if (ev->button.button == SDL_BUTTON_X2) { | 2822 | if (ev->button.button == SDL_BUTTON_X2) { |
2820 | postCommand_App("navigate.forward"); | 2823 | postCommand_Root(w->root, "navigate.forward"); |
2821 | return iTrue; | 2824 | return iTrue; |
2822 | } | 2825 | } |
2823 | if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) { | 2826 | if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) { |
2824 | postCommandf_App("open newtab:%d url:%s", | 2827 | postCommandf_Root(w->root, "open newtab:%d url:%s", |
2825 | modState_Keys() & KMOD_SHIFT ? 1 : 2, | 2828 | modState_Keys() & KMOD_SHIFT ? 1 : 2, |
2826 | cstr_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId))); | 2829 | cstr_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId))); |
2827 | return iTrue; | 2830 | return iTrue; |
@@ -2967,7 +2970,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
2967 | }, | 2970 | }, |
2968 | 3); | 2971 | 3); |
2969 | #endif | 2972 | #endif |
2970 | postCommand_App("document.select arg:1"); | 2973 | postCommand_Root(w->root, "document.select arg:1"); |
2971 | return iTrue; | 2974 | return iTrue; |
2972 | } | 2975 | } |
2973 | d->menu = makeMenu_Widget(w, data_Array(&items), size_Array(&items)); | 2976 | d->menu = makeMenu_Widget(w, data_Array(&items), size_Array(&items)); |
@@ -3188,7 +3191,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3188 | refresh_Widget(w); | 3191 | refresh_Widget(w); |
3189 | } | 3192 | } |
3190 | else if (linkFlags & supportedProtocol_GmLinkFlag) { | 3193 | else if (linkFlags & supportedProtocol_GmLinkFlag) { |
3191 | postCommandf_App("open newtab:%d url:%s", | 3194 | postCommandf_Root(w->root, "open newtab:%d url:%s", |
3192 | openTabMode_Sym(modState_Keys()), | 3195 | openTabMode_Sym(modState_Keys()), |
3193 | cstr_String(absoluteUrl_String( | 3196 | cstr_String(absoluteUrl_String( |
3194 | d->mod.url, linkUrl_GmDocument(d->doc, linkId)))); | 3197 | d->mod.url, linkUrl_GmDocument(d->doc, linkId)))); |
@@ -3221,7 +3224,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3221 | if (bannerType_DocumentWidget_(d) == certificateWarning_GmDocumentBanner && | 3224 | if (bannerType_DocumentWidget_(d) == certificateWarning_GmDocumentBanner && |
3222 | pos_Click(&d->click).y - top_Rect(banRect) > | 3225 | pos_Click(&d->click).y - top_Rect(banRect) > |
3223 | lineHeight_Text(banner_FontId) * 2) { | 3226 | lineHeight_Text(banner_FontId) * 2) { |
3224 | postCommand_App("document.info"); | 3227 | postCommand_Widget(d, "document.info"); |
3225 | } | 3228 | } |
3226 | else { | 3229 | else { |
3227 | postCommand_Widget(d, "navigate.root"); | 3230 | postCommand_Widget(d, "navigate.root"); |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 9d360f47..a40b77eb 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -754,10 +754,10 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
754 | return iTrue; | 754 | return iTrue; |
755 | } | 755 | } |
756 | if (ev->type == SDL_MOUSEMOTION && isHover_Widget(d)) { | 756 | if (ev->type == SDL_MOUSEMOTION && isHover_Widget(d)) { |
757 | const iInt2 local = localCoord_Widget(w, init_I2(ev->motion.x, ev->motion.y)); | 757 | const iInt2 inner = windowToInner_Widget(w, init_I2(ev->motion.x, ev->motion.y)); |
758 | setCursor_Window(get_Window(), | 758 | setCursor_Window(get_Window(), |
759 | local.x >= 2 * gap_UI + d->leftPadding && | 759 | inner.x >= 2 * gap_UI + d->leftPadding && |
760 | local.x < width_Widget(w) - d->rightPadding | 760 | inner.x < width_Widget(w) - d->rightPadding |
761 | ? SDL_SYSTEM_CURSOR_IBEAM | 761 | ? SDL_SYSTEM_CURSOR_IBEAM |
762 | : SDL_SYSTEM_CURSOR_ARROW); | 762 | : SDL_SYSTEM_CURSOR_ARROW); |
763 | } | 763 | } |
diff --git a/src/ui/keys.c b/src/ui/keys.c index 456ca928..6ad9d360 100644 --- a/src/ui/keys.c +++ b/src/ui/keys.c | |||
@@ -438,7 +438,7 @@ iBool processEvent_Keys(const SDL_Event *ev) { | |||
438 | postCommandf_App("%s repeat:1", cstr_String(&bind->command)); | 438 | postCommandf_App("%s repeat:1", cstr_String(&bind->command)); |
439 | } | 439 | } |
440 | else { | 440 | else { |
441 | postCommandString_App(&bind->command); | 441 | postCommandString_Root(NULL, &bind->command); |
442 | } | 442 | } |
443 | return iTrue; | 443 | return iTrue; |
444 | } | 444 | } |
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c index d8e594de..e0de97bf 100644 --- a/src/ui/lookupwidget.c +++ b/src/ui/lookupwidget.c | |||
@@ -654,11 +654,14 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) { | |||
654 | (equal_Command(cmd, "layout.changed") && | 654 | (equal_Command(cmd, "layout.changed") && |
655 | equal_Rangecc(range_Command(cmd, "id"), "navbar"))) { | 655 | equal_Rangecc(range_Command(cmd, "id"), "navbar"))) { |
656 | /* Position the lookup popup under the URL bar. */ { | 656 | /* Position the lookup popup under the URL bar. */ { |
657 | const iInt2 rootSize = size_Root(w->root); | 657 | iRoot *root = w->root; |
658 | const iRect navBarBounds = bounds_Widget(findWidget_App("navbar")); | 658 | const iInt2 rootSize = size_Root(root); |
659 | setFixedSize_Widget(w, init_I2(width_Widget(findWidget_App("url")), | 659 | const iRect navBarBounds = bounds_Widget(findChild_Widget(root->widget, "navbar")); |
660 | printf("navbar x:%d w:%d\n", navBarBounds.pos.x, navBarBounds.size.x); | ||
661 | iWidget *url = findChild_Widget(root->widget, "url"); | ||
662 | setFixedSize_Widget(w, init_I2(width_Widget(url), | ||
660 | (rootSize.y - bottom_Rect(navBarBounds)) / 2)); | 663 | (rootSize.y - bottom_Rect(navBarBounds)) / 2)); |
661 | setPos_Widget(w, bottomLeft_Rect(bounds_Widget(findWidget_App("url")))); | 664 | setPos_Widget(w, windowToLocal_Widget(w, bottomLeft_Rect(bounds_Widget(url)))); |
662 | #if defined (iPlatformAppleMobile) | 665 | #if defined (iPlatformAppleMobile) |
663 | /* Adjust height based on keyboard size. */ { | 666 | /* Adjust height based on keyboard size. */ { |
664 | w->rect.size.y = visibleRootSize_Window(window).y - top_Rect(bounds_Widget(w)); | 667 | w->rect.size.y = visibleRootSize_Window(window).y - top_Rect(bounds_Widget(w)); |
@@ -667,6 +670,7 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) { | |||
667 | safeAreaInsets_iOS(&l, NULL, &r, NULL); | 670 | safeAreaInsets_iOS(&l, NULL, &r, NULL); |
668 | w->rect.size.x = rootSize.x - l - r; | 671 | w->rect.size.x = rootSize.x - l - r; |
669 | w->rect.pos.x = l; | 672 | w->rect.pos.x = l; |
673 | /* TODO: Need to use windowToLocal_Widget? */ | ||
670 | } | 674 | } |
671 | } | 675 | } |
672 | #endif | 676 | #endif |
@@ -694,7 +698,7 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) { | |||
694 | setText_InputWidget(url, url_DocumentWidget(document_App())); | 698 | setText_InputWidget(url, url_DocumentWidget(document_App())); |
695 | showCollapsed_Widget(w, iFalse); | 699 | showCollapsed_Widget(w, iFalse); |
696 | setCursor_LookupWidget_(d, iInvalidPos); | 700 | setCursor_LookupWidget_(d, iInvalidPos); |
697 | postCommandString_App(&item->command); | 701 | postCommandString_Root(get_Root(), &item->command); |
698 | postCommand_App("focus.set id:"); /* unfocus */ | 702 | postCommand_App("focus.set id:"); /* unfocus */ |
699 | } | 703 | } |
700 | return iTrue; | 704 | return iTrue; |
diff --git a/src/ui/root.c b/src/ui/root.c index f91c3ba5..8faeb215 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -255,6 +255,13 @@ iRoot *get_Root(void) { | |||
255 | return activeRoot_; | 255 | return activeRoot_; |
256 | } | 256 | } |
257 | 257 | ||
258 | iAnyObject *findWidget_Root(const char *id) { | ||
259 | if (activeRoot_) { | ||
260 | return findChild_Widget(activeRoot_->widget, id); | ||
261 | } | ||
262 | return NULL; | ||
263 | } | ||
264 | |||
258 | void destroyPending_Root(iRoot *d) { | 265 | void destroyPending_Root(iRoot *d) { |
259 | setCurrent_Root(d); | 266 | setCurrent_Root(d); |
260 | iForEach(PtrSet, i, d->pendingDestruction) { | 267 | iForEach(PtrSet, i, d->pendingDestruction) { |
@@ -286,7 +293,7 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
286 | iWidget *menu = findChild_Widget(button, "menu"); | 293 | iWidget *menu = findChild_Widget(button, "menu"); |
287 | iAssert(menu); | 294 | iAssert(menu); |
288 | if (!isVisible_Widget(menu)) { | 295 | if (!isVisible_Widget(menu)) { |
289 | openMenu_Widget(menu, init_I2(0, button->rect.size.y)); | 296 | openMenu_Widget(menu, bottomLeft_Rect(bounds_Widget(button))); |
290 | } | 297 | } |
291 | else { | 298 | else { |
292 | closeMenu_Widget(menu); | 299 | closeMenu_Widget(menu); |
@@ -619,10 +626,10 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
619 | iString *newUrl = copy_String(text_InputWidget(url)); | 626 | iString *newUrl = copy_String(text_InputWidget(url)); |
620 | trim_String(newUrl); | 627 | trim_String(newUrl); |
621 | if (willPerformSearchQuery_(newUrl)) { | 628 | if (willPerformSearchQuery_(newUrl)) { |
622 | postCommandf_App("open url:%s", cstr_String(searchQueryUrl_App(newUrl))); | 629 | postCommandf_Root(navBar->root, "open url:%s", cstr_String(searchQueryUrl_App(newUrl))); |
623 | } | 630 | } |
624 | else { | 631 | else { |
625 | postCommandf_App( | 632 | postCommandf_Root(navBar->root, |
626 | "open url:%s", | 633 | "open url:%s", |
627 | cstr_String(absoluteUrl_String(&iStringLiteral(""), collect_String(newUrl)))); | 634 | cstr_String(absoluteUrl_String(&iStringLiteral(""), collect_String(newUrl)))); |
628 | } | 635 | } |
@@ -677,6 +684,7 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
677 | else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) { | 684 | else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) { |
678 | iWidget *widget = pointer_Command(cmd); | 685 | iWidget *widget = pointer_Command(cmd); |
679 | iWidget *menu = findWidget_App("doctabs.menu"); | 686 | iWidget *menu = findWidget_App("doctabs.menu"); |
687 | iAssert(menu->root == navBar->root); | ||
680 | if (isTabButton_Widget(widget)) { | 688 | if (isTabButton_Widget(widget)) { |
681 | if (!isVisible_Widget(menu)) { | 689 | if (!isVisible_Widget(menu)) { |
682 | iWidget *tabs = findWidget_App("doctabs"); | 690 | iWidget *tabs = findWidget_App("doctabs"); |
@@ -758,7 +766,7 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) { | |||
758 | argLabel_Command(cmd, "button") == SDL_BUTTON_RIGHT) { | 766 | argLabel_Command(cmd, "button") == SDL_BUTTON_RIGHT) { |
759 | iWidget *menu = findChild_Widget(toolBar, "toolbar.menu"); | 767 | iWidget *menu = findChild_Widget(toolBar, "toolbar.menu"); |
760 | arrange_Widget(menu); | 768 | arrange_Widget(menu); |
761 | openMenu_Widget(menu, init_I2(0, -height_Widget(menu))); | 769 | openMenu_Widget(menu, innerToWindow_Widget(menu, init_I2(0, -height_Widget(menu)))); |
762 | return iTrue; | 770 | return iTrue; |
763 | } | 771 | } |
764 | else if (equal_Command(cmd, "toolbar.showview")) { | 772 | else if (equal_Command(cmd, "toolbar.showview")) { |
@@ -868,6 +876,7 @@ void updateMetrics_Root(iRoot *d) { | |||
868 | 876 | ||
869 | void createUserInterface_Root(iRoot *d) { | 877 | void createUserInterface_Root(iRoot *d) { |
870 | iWidget *root = d->widget = new_Widget(); | 878 | iWidget *root = d->widget = new_Widget(); |
879 | iAssert(root->root == d); | ||
871 | setId_Widget(root, "root"); | 880 | setId_Widget(root, "root"); |
872 | /* Children of root cover the entire window. */ | 881 | /* Children of root cover the entire window. */ |
873 | setFlags_Widget( | 882 | setFlags_Widget( |
diff --git a/src/ui/root.h b/src/ui/root.h index 39e7bf3c..e5fdcbe4 100644 --- a/src/ui/root.h +++ b/src/ui/root.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include "widget.h" | 3 | #include "widget.h" |
4 | #include "color.h" | ||
4 | #include <the_Foundation/ptrset.h> | 5 | #include <the_Foundation/ptrset.h> |
5 | #include <the_Foundation/vec2.h> | 6 | #include <the_Foundation/vec2.h> |
6 | 7 | ||
@@ -8,12 +9,10 @@ iDeclareType(Root) | |||
8 | 9 | ||
9 | struct Impl_Root { | 10 | struct Impl_Root { |
10 | iWidget * widget; | 11 | iWidget * widget; |
11 | iWidget * hover; | ||
12 | iWidget * mouseGrab; | ||
13 | iWidget * focus; | ||
14 | iPtrArray *onTop; /* order is important; last one is topmost */ | 12 | iPtrArray *onTop; /* order is important; last one is topmost */ |
15 | iPtrSet * pendingDestruction; | 13 | iPtrSet * pendingDestruction; |
16 | int loadAnimTimer; | 14 | int loadAnimTimer; |
15 | iColor tmPalette[tmMax_ColorId]; /* theme-specific palette */ | ||
17 | }; | 16 | }; |
18 | 17 | ||
19 | iDeclareTypeConstruction(Root) | 18 | iDeclareTypeConstruction(Root) |
@@ -24,6 +23,7 @@ void createUserInterface_Root (iRoot *); | |||
24 | 23 | ||
25 | void setCurrent_Root (iRoot *); | 24 | void setCurrent_Root (iRoot *); |
26 | iRoot * get_Root (void); | 25 | iRoot * get_Root (void); |
26 | iAnyObject *findWidget_Root (const char *id); /* under current Root */ | ||
27 | 27 | ||
28 | iPtrArray * onTop_Root (iRoot *); | 28 | iPtrArray * onTop_Root (iRoot *); |
29 | void destroyPending_Root (iRoot *); | 29 | void destroyPending_Root (iRoot *); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 62d195fb..7d08b83a 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -713,14 +713,14 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, const iSidebarItem *it | |||
713 | break; | 713 | break; |
714 | } | 714 | } |
715 | case feeds_SidebarMode: { | 715 | case feeds_SidebarMode: { |
716 | postCommandString_App( | 716 | postCommandString_Root(get_Root(), |
717 | feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys()))); | 717 | feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys()))); |
718 | break; | 718 | break; |
719 | } | 719 | } |
720 | case bookmarks_SidebarMode: | 720 | case bookmarks_SidebarMode: |
721 | case history_SidebarMode: { | 721 | case history_SidebarMode: { |
722 | if (!isEmpty_String(&item->url)) { | 722 | if (!isEmpty_String(&item->url)) { |
723 | postCommandf_App("open newtab:%d url:%s", | 723 | postCommandf_Root(get_Root(), "open newtab:%d url:%s", |
724 | openTabMode_Sym(modState_Keys()), | 724 | openTabMode_Sym(modState_Keys()), |
725 | cstr_String(&item->url)); | 725 | cstr_String(&item->url)); |
726 | } | 726 | } |
@@ -961,13 +961,13 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
961 | } | 961 | } |
962 | else if (isCommand_Widget(w, ev, "mouse.moved")) { | 962 | else if (isCommand_Widget(w, ev, "mouse.moved")) { |
963 | if (isResizing_SidebarWidget_(d)) { | 963 | if (isResizing_SidebarWidget_(d)) { |
964 | const iInt2 local = localCoord_Widget(w, coord_Command(cmd)); | 964 | const iInt2 inner = windowToInner_Widget(w, coord_Command(cmd)); |
965 | const int resMid = d->resizer->rect.size.x / 2; | 965 | const int resMid = d->resizer->rect.size.x / 2; |
966 | setWidth_SidebarWidget( | 966 | setWidth_SidebarWidget( |
967 | d, | 967 | d, |
968 | ((d->side == left_SideBarSide | 968 | ((d->side == left_SideBarSide |
969 | ? local.x | 969 | ? inner.x |
970 | : (size_Root(w->root).x - coord_Command(cmd).x)) + | 970 | : (right_Rect(rect_Root(w->root)) - coord_Command(cmd).x)) + |
971 | resMid) / (float) gap_UI); | 971 | resMid) / (float) gap_UI); |
972 | } | 972 | } |
973 | return iTrue; | 973 | return iTrue; |
@@ -1086,7 +1086,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1086 | const iSidebarItem *item = d->contextItem; | 1086 | const iSidebarItem *item = d->contextItem; |
1087 | if (item) { | 1087 | if (item) { |
1088 | if (isCommand_Widget(w, ev, "feed.entry.opentab")) { | 1088 | if (isCommand_Widget(w, ev, "feed.entry.opentab")) { |
1089 | postCommandString_App(feedEntryOpenCommand_String(&item->url, 1)); | 1089 | postCommandString_Root(get_Root(), feedEntryOpenCommand_String(&item->url, 1)); |
1090 | return iTrue; | 1090 | return iTrue; |
1091 | } | 1091 | } |
1092 | if (isCommand_Widget(w, ev, "feed.entry.toggleread")) { | 1092 | if (isCommand_Widget(w, ev, "feed.entry.toggleread")) { |
diff --git a/src/ui/touch.c b/src/ui/touch.c index e49fce2a..b2c52526 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -233,7 +233,8 @@ static void dispatchNotification_Touch_(const iTouch *d, int code) { | |||
233 | .type = SDL_USEREVENT, | 233 | .type = SDL_USEREVENT, |
234 | .timestamp = SDL_GetTicks(), | 234 | .timestamp = SDL_GetTicks(), |
235 | .code = code, | 235 | .code = code, |
236 | .data1 = d->affinity | 236 | .data1 = d->affinity, |
237 | .data2 = d->affinity->root | ||
237 | }); | 238 | }); |
238 | setCurrent_Root(oldRoot); | 239 | setCurrent_Root(oldRoot); |
239 | } | 240 | } |
diff --git a/src/ui/util.c b/src/ui/util.c index edfd43cc..b487e13d 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -733,12 +733,13 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
733 | return menu; | 733 | return menu; |
734 | } | 734 | } |
735 | 735 | ||
736 | void openMenu_Widget(iWidget *d, iInt2 coord) { | 736 | void openMenu_Widget(iWidget *d, iInt2 windowCoord) { |
737 | openMenuFlags_Widget(d, coord, iTrue); | 737 | openMenuFlags_Widget(d, windowCoord, iTrue); |
738 | } | 738 | } |
739 | 739 | ||
740 | void openMenuFlags_Widget(iWidget *d, iInt2 coord, iBool postCommands) { | 740 | void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { |
741 | const iInt2 rootSize = size_Window(get_Window()); | 741 | const iRect rootRect = rect_Root(d->root); |
742 | const iInt2 rootSize = rootRect.size; | ||
742 | const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); | 743 | const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); |
743 | const iBool isSlidePanel = (flags_Widget(d) & horizontalOffset_WidgetFlag) != 0; | 744 | const iBool isSlidePanel = (flags_Widget(d) & horizontalOffset_WidgetFlag) != 0; |
744 | if (postCommands) { | 745 | if (postCommands) { |
@@ -756,7 +757,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 coord, iBool postCommands) { | |||
756 | if (!isSlidePanel) { | 757 | if (!isSlidePanel) { |
757 | setFlags_Widget(d, borderTop_WidgetFlag, iTrue); | 758 | setFlags_Widget(d, borderTop_WidgetFlag, iTrue); |
758 | } | 759 | } |
759 | d->rect.size.x = size_Window(get_Window()).x; | 760 | d->rect.size.x = rootSize.x; |
760 | } | 761 | } |
761 | /* Update item fonts. */ { | 762 | /* Update item fonts. */ { |
762 | iForEach(ObjectList, i, children_Widget(d)) { | 763 | iForEach(ObjectList, i, children_Widget(d)) { |
@@ -790,14 +791,14 @@ void openMenuFlags_Widget(iWidget *d, iInt2 coord, iBool postCommands) { | |||
790 | } | 791 | } |
791 | } | 792 | } |
792 | else { | 793 | else { |
793 | d->rect.pos = coord; | 794 | d->rect.pos = windowToLocal_Widget(d, windowCoord); |
794 | } | 795 | } |
795 | /* Ensure the full menu is visible. */ | 796 | /* Ensure the full menu is visible. */ |
796 | const iRect bounds = bounds_Widget(d); | 797 | const iRect bounds = bounds_Widget(d); |
797 | int leftExcess = -left_Rect(bounds); | 798 | int leftExcess = left_Rect(rootRect) - left_Rect(bounds); |
798 | int rightExcess = right_Rect(bounds) - rootSize.x; | 799 | int rightExcess = right_Rect(bounds) - right_Rect(rootRect); |
799 | int topExcess = -top_Rect(bounds); | 800 | int topExcess = top_Rect(rootRect) - top_Rect(bounds); |
800 | int bottomExcess = bottom_Rect(bounds) - rootSize.y; | 801 | int bottomExcess = bottom_Rect(bounds) - bottom_Rect(rootRect); |
801 | #if defined (iPlatformAppleMobile) | 802 | #if defined (iPlatformAppleMobile) |
802 | /* Reserve space for the system status bar. */ { | 803 | /* Reserve space for the system status bar. */ { |
803 | float l, t, r, b; | 804 | float l, t, r, b; |
@@ -868,7 +869,7 @@ int checkContextMenu_Widget(iWidget *menu, const SDL_Event *ev) { | |||
868 | } | 869 | } |
869 | const iInt2 mousePos = init_I2(ev->button.x, ev->button.y); | 870 | const iInt2 mousePos = init_I2(ev->button.x, ev->button.y); |
870 | if (contains_Widget(menu->parent, mousePos)) { | 871 | if (contains_Widget(menu->parent, mousePos)) { |
871 | openMenu_Widget(menu, localCoord_Widget(menu->parent, mousePos)); | 872 | openMenu_Widget(menu, mousePos); |
872 | return 0x2; | 873 | return 0x2; |
873 | } | 874 | } |
874 | } | 875 | } |
@@ -1044,7 +1045,7 @@ void showTabPage_Widget(iWidget *tabs, const iWidget *page) { | |||
1044 | } | 1045 | } |
1045 | /* Notify. */ | 1046 | /* Notify. */ |
1046 | if (!isEmpty_String(id_Widget(page))) { | 1047 | if (!isEmpty_String(id_Widget(page))) { |
1047 | postCommandf_App("tabs.changed id:%s", cstr_String(id_Widget(page))); | 1048 | postCommandf_Root(page->root, "tabs.changed id:%s", cstr_String(id_Widget(page))); |
1048 | } | 1049 | } |
1049 | } | 1050 | } |
1050 | 1051 | ||
@@ -1160,7 +1161,7 @@ static iBool slidePanelHandler_(iWidget *d, const char *cmd) { | |||
1160 | if (equal_Command(cmd, "panel.open")) { | 1161 | if (equal_Command(cmd, "panel.open")) { |
1161 | iWidget *button = pointer_Command(cmd); | 1162 | iWidget *button = pointer_Command(cmd); |
1162 | iWidget *panel = userData_Object(button); | 1163 | iWidget *panel = userData_Object(button); |
1163 | openMenu_Widget(panel, zero_I2()); | 1164 | openMenu_Widget(panel, innerToWindow_Widget(panel, zero_I2())); |
1164 | setFlags_Widget(panel, disabled_WidgetFlag, iFalse); | 1165 | setFlags_Widget(panel, disabled_WidgetFlag, iFalse); |
1165 | // updateTextCStr_LabelWidget(findWidget_App("panel.back"), ); | 1166 | // updateTextCStr_LabelWidget(findWidget_App("panel.back"), ); |
1166 | return iTrue; | 1167 | return iTrue; |
diff --git a/src/ui/util.h b/src/ui/util.h index aaffb1b8..b8d10cf0 100644 --- a/src/ui/util.h +++ b/src/ui/util.h | |||
@@ -212,8 +212,8 @@ struct Impl_MenuItem { | |||
212 | }; | 212 | }; |
213 | 213 | ||
214 | iWidget * makeMenu_Widget (iWidget *parent, const iMenuItem *items, size_t n); /* returns no ref */ | 214 | iWidget * makeMenu_Widget (iWidget *parent, const iMenuItem *items, size_t n); /* returns no ref */ |
215 | void openMenu_Widget (iWidget *, iInt2 coord); | 215 | void openMenu_Widget (iWidget *, iInt2 windowCoord); |
216 | void openMenuFlags_Widget(iWidget *d, iInt2 coord, iBool postCommands); | 216 | void openMenuFlags_Widget(iWidget *, iInt2 windowCoord, iBool postCommands); |
217 | void closeMenu_Widget (iWidget *); | 217 | void closeMenu_Widget (iWidget *); |
218 | 218 | ||
219 | iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command); | 219 | iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command); |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 5d924b57..6d6bc202 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -99,7 +99,7 @@ static void aboutToBeDestroyed_Widget_(iWidget *d) { | |||
99 | removeOne_PtrArray(onTop_Root(d->root), d); | 99 | removeOne_PtrArray(onTop_Root(d->root), d); |
100 | } | 100 | } |
101 | if (isHover_Widget(d)) { | 101 | if (isHover_Widget(d)) { |
102 | d->root->hover = NULL; | 102 | get_Window()->hover = NULL; |
103 | } | 103 | } |
104 | iForEach(ObjectList, i, d->children) { | 104 | iForEach(ObjectList, i, d->children) { |
105 | aboutToBeDestroyed_Widget_(as_Widget(i.object)); | 105 | aboutToBeDestroyed_Widget_(as_Widget(i.object)); |
@@ -675,16 +675,30 @@ static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { | |||
675 | 675 | ||
676 | iRect bounds_Widget(const iWidget *d) { | 676 | iRect bounds_Widget(const iWidget *d) { |
677 | iRect bounds = d->rect; | 677 | iRect bounds = d->rect; |
678 | applyVisualOffset_Widget_(d, &bounds.pos); | 678 | bounds.pos = localToWindow_Widget(d, bounds.pos); |
679 | return bounds; | ||
680 | } | ||
681 | |||
682 | iInt2 localToWindow_Widget(const iWidget *d, iInt2 localCoord) { | ||
683 | iInt2 window = localCoord; | ||
684 | applyVisualOffset_Widget_(d, &window); | ||
679 | for (const iWidget *w = d->parent; w; w = w->parent) { | 685 | for (const iWidget *w = d->parent; w; w = w->parent) { |
680 | iInt2 pos = w->rect.pos; | 686 | iInt2 pos = w->rect.pos; |
681 | applyVisualOffset_Widget_(w, &pos); | 687 | applyVisualOffset_Widget_(w, &pos); |
682 | addv_I2(&bounds.pos, pos); | 688 | addv_I2(&window, pos); |
683 | } | 689 | } |
684 | #if defined (iPlatformMobile) | 690 | #if defined (iPlatformMobile) |
685 | bounds.pos.y += value_Anim(&get_Window()->rootOffset); | 691 | window.y += value_Anim(&get_Window()->rootOffset); |
686 | #endif | 692 | #endif |
687 | return bounds; | 693 | return window; |
694 | } | ||
695 | |||
696 | iInt2 windowToLocal_Widget(const iWidget *d, iInt2 windowCoord) { | ||
697 | iInt2 local = windowCoord; | ||
698 | for (const iWidget *w = d->parent; w; w = w->parent) { | ||
699 | subv_I2(&local, w->rect.pos); | ||
700 | } | ||
701 | return local; | ||
688 | } | 702 | } |
689 | 703 | ||
690 | iRect boundsWithoutVisualOffset_Widget(const iWidget *d) { | 704 | iRect boundsWithoutVisualOffset_Widget(const iWidget *d) { |
@@ -695,25 +709,32 @@ iRect boundsWithoutVisualOffset_Widget(const iWidget *d) { | |||
695 | return bounds; | 709 | return bounds; |
696 | } | 710 | } |
697 | 711 | ||
698 | iInt2 localCoord_Widget(const iWidget *d, iInt2 coord) { | 712 | iInt2 innerToWindow_Widget(const iWidget *d, iInt2 innerCoord) { |
713 | for (const iWidget *w = d; w; w = w->parent) { | ||
714 | addv_I2(&innerCoord, w->rect.pos); | ||
715 | } | ||
716 | return innerCoord; | ||
717 | } | ||
718 | |||
719 | iInt2 windowToInner_Widget(const iWidget *d, iInt2 windowCoord) { | ||
699 | for (const iWidget *w = d; w; w = w->parent) { | 720 | for (const iWidget *w = d; w; w = w->parent) { |
700 | subv_I2(&coord, w->rect.pos); | 721 | subv_I2(&windowCoord, w->rect.pos); |
701 | } | 722 | } |
702 | return coord; | 723 | return windowCoord; |
703 | } | 724 | } |
704 | 725 | ||
705 | iBool contains_Widget(const iWidget *d, iInt2 coord) { | 726 | iBool contains_Widget(const iWidget *d, iInt2 windowCoord) { |
706 | return containsExpanded_Widget(d, coord, 0); | 727 | return containsExpanded_Widget(d, windowCoord, 0); |
707 | } | 728 | } |
708 | 729 | ||
709 | iBool containsExpanded_Widget(const iWidget *d, iInt2 coord, int expand) { | 730 | iBool containsExpanded_Widget(const iWidget *d, iInt2 windowCoord, int expand) { |
710 | const iRect bounds = { | 731 | const iRect bounds = { |
711 | zero_I2(), | 732 | zero_I2(), |
712 | addY_I2(d->rect.size, | 733 | addY_I2(d->rect.size, |
713 | d->flags & drawBackgroundToBottom_WidgetFlag ? size_Root(d->root).y : 0) | 734 | d->flags & drawBackgroundToBottom_WidgetFlag ? size_Root(d->root).y : 0) |
714 | }; | 735 | }; |
715 | return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, | 736 | return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, |
716 | localCoord_Widget(d, coord)); | 737 | windowToInner_Widget(d, windowCoord)); |
717 | } | 738 | } |
718 | 739 | ||
719 | iLocalDef iBool isKeyboardEvent_(const SDL_Event *ev) { | 740 | iLocalDef iBool isKeyboardEvent_(const SDL_Event *ev) { |
@@ -738,20 +759,15 @@ static iBool filterEvent_Widget_(const iWidget *d, const SDL_Event *ev) { | |||
738 | } | 759 | } |
739 | 760 | ||
740 | void unhover_Widget(void) { | 761 | void unhover_Widget(void) { |
741 | /* TODO: Which root? */ | 762 | get_Window()->hover = NULL; |
742 | get_Root()->hover = NULL; | ||
743 | } | 763 | } |
744 | 764 | ||
745 | iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | 765 | iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { |
746 | iAssert(d->root == get_Root()); | 766 | iAssert(d->root == get_Root()); |
747 | if (!d->parent) { | 767 | if (!d->parent) { |
748 | if (ev->type == SDL_MOUSEMOTION) { | 768 | if (get_Window()->focus && get_Window()->focus->root == d->root && isKeyboardEvent_(ev)) { |
749 | /* Hover widget may change. */ | ||
750 | setHover_Widget(NULL); | ||
751 | } | ||
752 | if (d->root->focus && isKeyboardEvent_(ev)) { | ||
753 | /* Root dispatches keyboard events directly to the focused widget. */ | 769 | /* Root dispatches keyboard events directly to the focused widget. */ |
754 | if (dispatchEvent_Widget(d->root->focus, ev)) { | 770 | if (dispatchEvent_Widget(get_Window()->focus, ev)) { |
755 | return iTrue; | 771 | return iTrue; |
756 | } | 772 | } |
757 | } | 773 | } |
@@ -780,7 +796,7 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
780 | } | 796 | } |
781 | } | 797 | } |
782 | else if (ev->type == SDL_MOUSEMOTION && | 798 | else if (ev->type == SDL_MOUSEMOTION && |
783 | (!d->root->hover || hasParent_Widget(d, d->root->hover)) && | 799 | (!get_Window()->hover || hasParent_Widget(d, get_Window()->hover)) && |
784 | flags_Widget(d) & hover_WidgetFlag && ~flags_Widget(d) & hidden_WidgetFlag && | 800 | flags_Widget(d) & hover_WidgetFlag && ~flags_Widget(d) & hidden_WidgetFlag && |
785 | ~flags_Widget(d) & disabled_WidgetFlag) { | 801 | ~flags_Widget(d) & disabled_WidgetFlag) { |
786 | if (contains_Widget(d, init_I2(ev->motion.x, ev->motion.y))) { | 802 | if (contains_Widget(d, init_I2(ev->motion.x, ev->motion.y))) { |
@@ -798,7 +814,8 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
798 | handle the events first. */ | 814 | handle the events first. */ |
799 | iReverseForEach(ObjectList, i, d->children) { | 815 | iReverseForEach(ObjectList, i, d->children) { |
800 | iWidget *child = as_Widget(i.object); | 816 | iWidget *child = as_Widget(i.object); |
801 | if (child == d->root->focus && isKeyboardEvent_(ev)) { | 817 | iAssert(child->root == d->root); |
818 | if (child == get_Window()->focus && isKeyboardEvent_(ev)) { | ||
802 | continue; /* Already dispatched. */ | 819 | continue; /* Already dispatched. */ |
803 | } | 820 | } |
804 | if (isVisible_Widget(child) && child->flags & keepOnTop_WidgetFlag) { | 821 | if (isVisible_Widget(child) && child->flags & keepOnTop_WidgetFlag) { |
@@ -870,7 +887,7 @@ static iBool scrollOverflow_Widget_(iWidget *d, int delta) { | |||
870 | // else { | 887 | // else { |
871 | // bounds.pos.y = iMax(bounds.pos.y, ); | 888 | // bounds.pos.y = iMax(bounds.pos.y, ); |
872 | // } | 889 | // } |
873 | const iInt2 newPos = localCoord_Widget(d->parent, bounds.pos); | 890 | const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos); |
874 | if (!isEqual_I2(newPos, d->rect.pos)) { | 891 | if (!isEqual_I2(newPos, d->rect.pos)) { |
875 | d->rect.pos = newPos; | 892 | d->rect.pos = newPos; |
876 | refresh_Widget(d); | 893 | refresh_Widget(d); |
@@ -1087,6 +1104,7 @@ iAny *addChildPosFlags_Widget(iWidget *d, iAnyObject *child, enum iWidgetAddPos | |||
1087 | iAssert(child); | 1104 | iAssert(child); |
1088 | iAssert(d != child); | 1105 | iAssert(d != child); |
1089 | iWidget *widget = as_Widget(child); | 1106 | iWidget *widget = as_Widget(child); |
1107 | iAssert(widget->root == d->root); | ||
1090 | iAssert(!widget->parent); | 1108 | iAssert(!widget->parent); |
1091 | if (!d->children) { | 1109 | if (!d->children) { |
1092 | d->children = new_ObjectList(); | 1110 | d->children = new_ObjectList(); |
@@ -1268,12 +1286,12 @@ iBool isDisabled_Widget(const iAnyObject *d) { | |||
1268 | 1286 | ||
1269 | iBool isFocused_Widget(const iAnyObject *d) { | 1287 | iBool isFocused_Widget(const iAnyObject *d) { |
1270 | iAssert(isInstance_Object(d, &Class_Widget)); | 1288 | iAssert(isInstance_Object(d, &Class_Widget)); |
1271 | return ((const iWidget *) d)->root->focus == d; | 1289 | return get_Window()->focus == d; |
1272 | } | 1290 | } |
1273 | 1291 | ||
1274 | iBool isHover_Widget(const iAnyObject *d) { | 1292 | iBool isHover_Widget(const iAnyObject *d) { |
1275 | iAssert(isInstance_Object(d, &Class_Widget)); | 1293 | iAssert(isInstance_Object(d, &Class_Widget)); |
1276 | return ((const iWidget *) d)->root->hover == d; | 1294 | return get_Window()->hover == d; |
1277 | } | 1295 | } |
1278 | 1296 | ||
1279 | iBool isSelected_Widget(const iAnyObject *d) { | 1297 | iBool isSelected_Widget(const iAnyObject *d) { |
@@ -1319,15 +1337,14 @@ iBool isAffectedByVisualOffset_Widget(const iWidget *d) { | |||
1319 | } | 1337 | } |
1320 | 1338 | ||
1321 | void setFocus_Widget(iWidget *d) { | 1339 | void setFocus_Widget(iWidget *d) { |
1322 | iRoot *root = d ? d->root : get_Root(); | 1340 | iWindow *win = get_Window(); |
1323 | if (root->focus != d) { | 1341 | if (win->focus != d) { |
1324 | if (root->focus) { | 1342 | if (win->focus) { |
1325 | iAssert(!contains_PtrSet(root->pendingDestruction, root->focus)); | 1343 | iAssert(!contains_PtrSet(win->focus->root->pendingDestruction, win->focus)); |
1326 | postCommand_Widget(root->focus, "focus.lost"); | 1344 | postCommand_Widget(win->focus, "focus.lost"); |
1327 | } | 1345 | } |
1328 | root->focus = d; | 1346 | win->focus = d; |
1329 | if (d) { | 1347 | if (d) { |
1330 | iAssert(root == d->root); | ||
1331 | iAssert(flags_Widget(d) & focusable_WidgetFlag); | 1348 | iAssert(flags_Widget(d) & focusable_WidgetFlag); |
1332 | postCommand_Widget(d, "focus.gained"); | 1349 | postCommand_Widget(d, "focus.gained"); |
1333 | } | 1350 | } |
@@ -1335,20 +1352,15 @@ void setFocus_Widget(iWidget *d) { | |||
1335 | } | 1352 | } |
1336 | 1353 | ||
1337 | iWidget *focus_Widget(void) { | 1354 | iWidget *focus_Widget(void) { |
1338 | return get_Root()->focus; | 1355 | return get_Window()->focus; |
1339 | } | 1356 | } |
1340 | 1357 | ||
1341 | void setHover_Widget(iWidget *d) { | 1358 | void setHover_Widget(iWidget *d) { |
1342 | if (d) { | 1359 | get_Window()->hover = d; |
1343 | d->root->hover = d; | ||
1344 | } | ||
1345 | else { | ||
1346 | get_Root()->hover = NULL; | ||
1347 | } | ||
1348 | } | 1360 | } |
1349 | 1361 | ||
1350 | iWidget *hover_Widget(void) { | 1362 | iWidget *hover_Widget(void) { |
1351 | return get_Root()->hover; | 1363 | return get_Window()->hover; |
1352 | } | 1364 | } |
1353 | 1365 | ||
1354 | static const iWidget *findFocusable_Widget_(const iWidget *d, const iWidget *startFrom, | 1366 | static const iWidget *findFocusable_Widget_(const iWidget *d, const iWidget *startFrom, |
@@ -1404,21 +1416,14 @@ iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusD | |||
1404 | } | 1416 | } |
1405 | 1417 | ||
1406 | void setMouseGrab_Widget(iWidget *d) { | 1418 | void setMouseGrab_Widget(iWidget *d) { |
1407 | iRoot *root = d ? d->root : get_Root(); | 1419 | if (get_Window()->mouseGrab != d) { |
1408 | if (root->mouseGrab != d) { | 1420 | get_Window()->mouseGrab = d; |
1409 | root->mouseGrab = d; | ||
1410 | SDL_CaptureMouse(d != NULL); | 1421 | SDL_CaptureMouse(d != NULL); |
1411 | } | 1422 | } |
1412 | } | 1423 | } |
1413 | 1424 | ||
1414 | iWidget *mouseGrab_Widget(void) { | 1425 | iWidget *mouseGrab_Widget(void) { |
1415 | iWindow *win = get_Window(); | 1426 | return get_Window()->mouseGrab; |
1416 | iForIndices(i, win->roots) { | ||
1417 | if (win->roots[i] && win->roots[i]->mouseGrab) { | ||
1418 | return win->roots[i]->mouseGrab; | ||
1419 | } | ||
1420 | } | ||
1421 | return NULL; | ||
1422 | } | 1427 | } |
1423 | 1428 | ||
1424 | void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) { | 1429 | void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) { |
@@ -1438,7 +1443,7 @@ void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) { | |||
1438 | iAssert(isInstance_Object(d, &Class_Widget)); | 1443 | iAssert(isInstance_Object(d, &Class_Widget)); |
1439 | appendFormat_String(&str, " ptr:%p", d); | 1444 | appendFormat_String(&str, " ptr:%p", d); |
1440 | } | 1445 | } |
1441 | postCommandString_App(&str); | 1446 | postCommandString_Root(((const iWidget *) d)->root, &str); |
1442 | deinit_String(&str); | 1447 | deinit_String(&str); |
1443 | } | 1448 | } |
1444 | 1449 | ||
diff --git a/src/ui/widget.h b/src/ui/widget.h index f126d40d..2ef035b6 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -169,16 +169,24 @@ void destroy_Widget (iWidget *); /* widget removed and deleted later | |||
169 | void destroyPending_Widget (void); | 169 | void destroyPending_Widget (void); |
170 | void releaseChildren_Widget (iWidget *); | 170 | void releaseChildren_Widget (iWidget *); |
171 | 171 | ||
172 | /* Coordinate spaces: | ||
173 | - window: 0,0 is at the top left corner of the window | ||
174 | - local: 0,0 is at the top left corner of the parent widget | ||
175 | - inner: 0,0 is at the top left corner of the widget */ | ||
176 | |||
172 | iWidget * root_Widget (const iWidget *); | 177 | iWidget * root_Widget (const iWidget *); |
173 | const iString * id_Widget (const iWidget *); | 178 | const iString * id_Widget (const iWidget *); |
174 | int64_t flags_Widget (const iWidget *); | 179 | int64_t flags_Widget (const iWidget *); |
175 | iRect bounds_Widget (const iWidget *); /* outer bounds */ | 180 | iRect bounds_Widget (const iWidget *); /* outer bounds */ |
176 | iRect innerBounds_Widget (const iWidget *); | 181 | iRect innerBounds_Widget (const iWidget *); |
177 | iRect boundsWithoutVisualOffset_Widget(const iWidget *); | 182 | iRect boundsWithoutVisualOffset_Widget(const iWidget *); |
178 | iInt2 localCoord_Widget (const iWidget *, iInt2 coord); | 183 | iInt2 localToWindow_Widget (const iWidget *, iInt2 localCoord); |
179 | iBool contains_Widget (const iWidget *, iInt2 coord); | 184 | iInt2 windowToLocal_Widget (const iWidget *, iInt2 windowCoord); |
180 | iBool containsExpanded_Widget (const iWidget *, iInt2 coord, int expand); | 185 | iInt2 innerToWindow_Widget (const iWidget *, iInt2 innerCoord); |
181 | iAny * hitChild_Widget (const iWidget *, iInt2 coord); | 186 | iInt2 windowToInner_Widget (const iWidget *, iInt2 windowCoord); |
187 | iBool contains_Widget (const iWidget *, iInt2 windowCoord); | ||
188 | iBool containsExpanded_Widget (const iWidget *, iInt2 windowCoord, int expand); | ||
189 | iAny * hitChild_Widget (const iWidget *, iInt2 windowCoord); | ||
182 | iAny * findChild_Widget (const iWidget *, const char *id); | 190 | iAny * findChild_Widget (const iWidget *, const char *id); |
183 | const iPtrArray *findChildren_Widget (const iWidget *, const char *id); | 191 | const iPtrArray *findChildren_Widget (const iWidget *, const char *id); |
184 | iAny * findParentClass_Widget (const iWidget *, const iAnyClass *class); | 192 | iAny * findParentClass_Widget (const iWidget *, const iAnyClass *class); |
diff --git a/src/ui/window.c b/src/ui/window.c index e47b579f..78ea182c 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -189,8 +189,11 @@ static void windowSizeChanged_Window_(iWindow *d) { | |||
189 | iRoot *root = d->roots[i]; | 189 | iRoot *root = d->roots[i]; |
190 | if (root) { | 190 | if (root) { |
191 | root->widget->rect.pos = init_I2(rootSize.x * i, 0); | 191 | root->widget->rect.pos = init_I2(rootSize.x * i, 0); |
192 | root->widget->rect.size = rootSize; /* Reposition roots. */ | 192 | root->widget->rect.size = addX_I2(rootSize, -gap_UI / 4); |
193 | root->widget->minSize = rootSize; | 193 | if (i == 1) { |
194 | root->widget->rect.pos.x += gap_UI / 8; | ||
195 | } | ||
196 | root->widget->minSize = root->widget->rect.size; | ||
194 | updatePadding_Root(root); | 197 | updatePadding_Root(root); |
195 | arrange_Widget(root->widget); | 198 | arrange_Widget(root->widget); |
196 | } | 199 | } |
@@ -220,7 +223,7 @@ static void updateSize_Window_(iWindow *d, iBool notifyAlways) { | |||
220 | void drawWhileResizing_Window(iWindow *d, int w, int h) { | 223 | void drawWhileResizing_Window(iWindow *d, int w, int h) { |
221 | /* This is called while a window resize is in progress, so we can be pretty confident | 224 | /* This is called while a window resize is in progress, so we can be pretty confident |
222 | the size has actually changed. */ | 225 | the size has actually changed. */ |
223 | d->size = init_I2(w, h); | 226 | d->size = coord_Window(d, w, h); |
224 | windowSizeChanged_Window_(d); | 227 | windowSizeChanged_Window_(d); |
225 | draw_Window(d); | 228 | draw_Window(d); |
226 | } | 229 | } |
@@ -274,7 +277,8 @@ static float displayScale_Window_(const iWindow *d) { | |||
274 | } | 277 | } |
275 | 278 | ||
276 | static void drawBlank_Window_(iWindow *d) { | 279 | static void drawBlank_Window_(iWindow *d) { |
277 | const iColor bg = get_Color(uiBackground_ColorId); | 280 | // const iColor bg = get_Color(uiBackground_ColorId); |
281 | const iColor bg = { 128, 128, 128, 255 }; /* TODO: Have no root yet. */ | ||
278 | SDL_SetRenderDrawColor(d->render, bg.r, bg.g, bg.b, 255); | 282 | SDL_SetRenderDrawColor(d->render, bg.r, bg.g, bg.b, 255); |
279 | SDL_RenderClear(d->render); | 283 | SDL_RenderClear(d->render); |
280 | SDL_RenderPresent(d->render); | 284 | SDL_RenderPresent(d->render); |
@@ -373,6 +377,9 @@ void init_Window(iWindow *d, iRect rect) { | |||
373 | d->win = NULL; | 377 | d->win = NULL; |
374 | d->size = zero_I2(); /* will be updated below */ | 378 | d->size = zero_I2(); /* will be updated below */ |
375 | iZap(d->roots); | 379 | iZap(d->roots); |
380 | d->hover = NULL; | ||
381 | d->mouseGrab = NULL; | ||
382 | d->focus = NULL; | ||
376 | iZap(d->cursors); | 383 | iZap(d->cursors); |
377 | d->place.initialPos = rect.pos; | 384 | d->place.initialPos = rect.pos; |
378 | d->place.normalRect = rect; | 385 | d->place.normalRect = rect; |
@@ -520,7 +527,6 @@ iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { | |||
520 | return d->roots[i]; | 527 | return d->roots[i]; |
521 | } | 528 | } |
522 | } | 529 | } |
523 | iAssert(iFalse); /* it must be under some Root */ | ||
524 | return NULL; | 530 | return NULL; |
525 | } | 531 | } |
526 | 532 | ||
@@ -811,10 +817,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
811 | event.button.x = pos.x; | 817 | event.button.x = pos.x; |
812 | event.button.y = pos.y; | 818 | event.button.y = pos.y; |
813 | } | 819 | } |
814 | const iWidget *oldHovers[2] = { | 820 | const iWidget *oldHover = d->hover; |
815 | d->roots[0]->hover, | ||
816 | d->roots[1]->hover, | ||
817 | }; | ||
818 | iBool wasUsed = iFalse; | 821 | iBool wasUsed = iFalse; |
819 | /* Dispatch first to the mouse-grabbed widget. */ | 822 | /* Dispatch first to the mouse-grabbed widget. */ |
820 | // iWidget *widget = d->root.widget; | 823 | // iWidget *widget = d->root.widget; |
@@ -866,7 +869,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
866 | } | 869 | } |
867 | } | 870 | } |
868 | } | 871 | } |
869 | if (oldHovers[0] != d->roots[0]->hover || oldHovers[1] != d->roots[1]->hover) { | 872 | if (oldHover != d->hover) { |
870 | postRefresh_App(); | 873 | postRefresh_App(); |
871 | } | 874 | } |
872 | if (event.type == SDL_MOUSEMOTION) { | 875 | if (event.type == SDL_MOUSEMOTION) { |
@@ -879,9 +882,15 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
879 | } | 882 | } |
880 | 883 | ||
881 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { | 884 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { |
882 | /* TODO: Dispatch to input-focused root first. */ | 885 | if (ev->type == SDL_MOUSEMOTION) { |
886 | /* Hover widget may change. */ | ||
887 | setHover_Widget(NULL); | ||
888 | } | ||
883 | iForIndices(i, d->roots) { | 889 | iForIndices(i, d->roots) { |
884 | if (d->roots[i]) { | 890 | if (d->roots[i]) { |
891 | if (isCommand_SDLEvent(ev) && ev->user.data2 && ev->user.data2 != d->roots[i]) { | ||
892 | continue; /* Not meant for this root. */ | ||
893 | } | ||
885 | setCurrent_Root(d->roots[i]); | 894 | setCurrent_Root(d->roots[i]); |
886 | const iBool wasUsed = dispatchEvent_Widget(d->roots[i]->widget, ev); | 895 | const iBool wasUsed = dispatchEvent_Widget(d->roots[i]->widget, ev); |
887 | if (wasUsed) { | 896 | if (wasUsed) { |
diff --git a/src/ui/window.h b/src/ui/window.h index 12c540d3..de0133b1 100644 --- a/src/ui/window.h +++ b/src/ui/window.h | |||
@@ -68,6 +68,9 @@ 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 | iWidget * hover; | ||
72 | iWidget * mouseGrab; | ||
73 | iWidget * focus; | ||
71 | float pixelRatio; /* conversion between points and pixels, e.g., coords, window size */ | 74 | float pixelRatio; /* conversion between points and pixels, e.g., coords, window size */ |
72 | float displayScale; /* DPI-based scaling factor of current display, affects uiScale only */ | 75 | float displayScale; /* DPI-based scaling factor of current display, affects uiScale only */ |
73 | float uiScale; | 76 | float uiScale; |