summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2022-02-18 19:51:38 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2022-02-18 19:51:38 +0200
commitdd9add718c4a9a2b29cd38b05886f22877b89b35 (patch)
tree2bec152d5f813c30373c802a03076f8268e0aaac
parentbef61b34d8a23e2e8594207c2876ce982fe0e15f (diff)
Serializing multiple windows
Not quite fully functional yet. The window positioning still needs to be written to prefs.cfg.
-rw-r--r--res/about/version.gmi3
-rw-r--r--src/app.c202
-rw-r--r--src/app.h3
-rw-r--r--src/defs.h5
-rw-r--r--src/ui/documentwidget.c5
-rw-r--r--src/ui/text.c20
-rw-r--r--src/ui/window.c36
7 files changed, 193 insertions, 81 deletions
diff --git a/res/about/version.gmi b/res/about/version.gmi
index aee62e82..73b88a52 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -6,6 +6,9 @@
6``` 6```
7# Release notes 7# Release notes
8 8
9## 1.11
10⚠️ Downgrading back to v1.10 causes all site-specific themes to be forgotten. Back up your sitespec.ini beforehand.
11
9## 1.10.6 12## 1.10.6
10* Added bindings for switching Feeds list to Unread/All mode. 13* Added bindings for switching Feeds list to Unread/All mode.
11* Fixed normalization of empty Gemini URL paths to `/` as per the November 2021 spec update. 14* Fixed normalization of empty Gemini URL paths to `/` as per the November 2021 spec update.
diff --git a/src/app.c b/src/app.c
index 686d8b82..850a9706 100644
--- a/src/app.c
+++ b/src/app.c
@@ -476,7 +476,11 @@ static const char *magicSidebar_App_ = "side";
476 476
477enum iDocumentStateFlag { 477enum iDocumentStateFlag {
478 current_DocumentStateFlag = iBit(1), 478 current_DocumentStateFlag = iBit(1),
479 rootIndex1_DocumentStateFlag = iBit(2) 479 rootIndex1_DocumentStateFlag = iBit(2),
480};
481
482enum iWindowStateFlag {
483 current_WindowStateFlag = iBit(9),
480}; 484};
481 485
482static iBool loadState_App_(iApp *d) { 486static iBool loadState_App_(iApp *d) {
@@ -499,19 +503,48 @@ static iBool loadState_App_(iApp *d) {
499 } 503 }
500 setVersion_Stream(stream_File(f), version); 504 setVersion_Stream(stream_File(f), version);
501 /* Window state. */ 505 /* Window state. */
502 iDocumentWidget *doc = NULL; 506 iDeclareType(CurrentTabs);
503 iDocumentWidget *current[2] = { NULL, NULL }; 507 struct Impl_CurrentTabs {
504 iBool isFirstTab[2] = { iTrue, iTrue }; 508 iDocumentWidget *currentTab[2]; /* for each root */
509 };
510 int numWins = 0;
511 iMainWindow * win = NULL;
512 iMainWindow * currentWin = d->window;
513 iArray * currentTabs; /* two per window (per root per window) */
514 iBool isFirstTab[2];
515 currentTabs = collectNew_Array(sizeof(iCurrentTabs));
505 while (!atEnd_File(f)) { 516 while (!atEnd_File(f)) {
506 readData_File(f, 4, magic); 517 readData_File(f, 4, magic);
507 if (!memcmp(magic, magicWindow_App_, 4)) { 518 if (!memcmp(magic, magicWindow_App_, 4)) {
508 const int splitMode = read32_File(f); 519 numWins++;
509 const int keyRoot = read32_File(f); 520 const int splitMode = read32_File(f);
510 d->window->pendingSplitMode = splitMode; 521 const int winState = read32_File(f);
511 setSplitMode_MainWindow(d->window, splitMode | noEvents_WindowSplit); 522 const int keyRoot = (winState & 1);
512 d->window->base.keyRoot = d->window->base.roots[keyRoot]; 523 const iBool isCurrent = (winState & current_WindowStateFlag) != 0;
524 if (numWins == 1) {
525 win = d->window;
526 }
527 else {
528 win = new_MainWindow(d->initialWindowRect);
529 addWindow_App(win);
530 }
531 pushBack_Array(currentTabs, &(iCurrentTabs){ { NULL, NULL } });
532 isFirstTab[0] = isFirstTab[1] = iTrue;
533 if (isCurrent) {
534 currentWin = win;
535 }
536 setCurrent_Window(win);
537 setCurrent_Root(NULL);
538 win->pendingSplitMode = splitMode;
539 setSplitMode_MainWindow(win, splitMode | noEvents_WindowSplit);
540 win->base.keyRoot = d->window->base.roots[keyRoot];
513 } 541 }
514 else if (!memcmp(magic, magicSidebar_App_, 4)) { 542 else if (!memcmp(magic, magicSidebar_App_, 4)) {
543 if (!win) {
544 printf("%s: missing window\n", cstr_String(path_File(f)));
545 setCurrent_Root(NULL);
546 return iFalse;
547 }
515 const uint16_t bits = readU16_File(f); 548 const uint16_t bits = readU16_File(f);
516 const uint8_t modes = readU8_File(f); 549 const uint8_t modes = readU8_File(f);
517 const float widths[2] = { 550 const float widths[2] = {
@@ -528,7 +561,7 @@ static iBool loadState_App_(iApp *d) {
528 } 561 }
529 const uint8_t rootIndex = bits & 0xff; 562 const uint8_t rootIndex = bits & 0xff;
530 const uint8_t flags = bits >> 8; 563 const uint8_t flags = bits >> 8;
531 iRoot *root = d->window->base.roots[rootIndex]; 564 iRoot *root = win->base.roots[rootIndex];
532 if (root) { 565 if (root) {
533 iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); 566 iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar");
534 iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); 567 iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2");
@@ -551,12 +584,18 @@ static iBool loadState_App_(iApp *d) {
551 } 584 }
552 } 585 }
553 else if (!memcmp(magic, magicTabDocument_App_, 4)) { 586 else if (!memcmp(magic, magicTabDocument_App_, 4)) {
587 if (!win) {
588 printf("%s: missing window\n", cstr_String(path_File(f)));
589 setCurrent_Root(NULL);
590 return iFalse;
591 }
554 const int8_t flags = read8_File(f); 592 const int8_t flags = read8_File(f);
555 int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; 593 int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0;
556 if (rootIndex > numRoots_Window(as_Window(d->window)) - 1) { 594 if (rootIndex > numRoots_Window(as_Window(win)) - 1) {
557 rootIndex = 0; 595 rootIndex = 0;
558 } 596 }
559 setCurrent_Root(d->window->base.roots[rootIndex]); 597 setCurrent_Root(win->base.roots[rootIndex]);
598 iDocumentWidget *doc;
560 if (isFirstTab[rootIndex]) { 599 if (isFirstTab[rootIndex]) {
561 isFirstTab[rootIndex] = iFalse; 600 isFirstTab[rootIndex] = iFalse;
562 /* There is one pre-created tab in each root. */ 601 /* There is one pre-created tab in each root. */
@@ -566,7 +605,7 @@ static iBool loadState_App_(iApp *d) {
566 doc = newTab_App(NULL, iFalse /* no switching */); 605 doc = newTab_App(NULL, iFalse /* no switching */);
567 } 606 }
568 if (flags & current_DocumentStateFlag) { 607 if (flags & current_DocumentStateFlag) {
569 current[rootIndex] = doc; 608 value_Array(currentTabs, numWins - 1, iCurrentTabs).currentTab[rootIndex] = doc;
570 } 609 }
571 deserializeState_DocumentWidget(doc, stream_File(f)); 610 deserializeState_DocumentWidget(doc, stream_File(f));
572 doc = NULL; 611 doc = NULL;
@@ -577,12 +616,23 @@ static iBool loadState_App_(iApp *d) {
577 return iFalse; 616 return iFalse;
578 } 617 }
579 } 618 }
580 if (d->window->splitMode) { 619 iForEach(Array, i, currentTabs) {
581 /* Update root placement. */ 620 const iCurrentTabs *cur = i.value;
582 resize_MainWindow(d->window, -1, -1); 621 win = at_PtrArray(&d->mainWindows, index_ArrayIterator(&i));
622 for (size_t j = 0; j < 2; ++j) {
623 postCommandf_Root(win->base.roots[j], "tabs.switch page:%p", cur->currentTab[j]);
624 }
625 if (win->splitMode) {
626 /* Update root placement. */
627 resize_MainWindow(win, -1, -1);
628 }
629// postCommand_Root(win->base.roots[0], "window.unfreeze");
630 win->isDrawFrozen = iFalse;
631 SDL_ShowWindow(win->base.win);
583 } 632 }
584 iForIndices(i, current) { 633 if (numWindows_App() > 1) {
585 postCommandf_Root(NULL, "tabs.switch page:%p", current[i]); 634 SDL_RaiseWindow(currentWin->base.win);
635 setActiveWindow_App(currentWin);
586 } 636 }
587 setCurrent_Root(NULL); 637 setCurrent_Root(NULL);
588 return iTrue; 638 return iTrue;
@@ -593,7 +643,6 @@ static iBool loadState_App_(iApp *d) {
593static void saveState_App_(const iApp *d) { 643static void saveState_App_(const iApp *d) {
594 iUnused(d); 644 iUnused(d);
595 trimCache_App(); 645 trimCache_App();
596 iMainWindow *win = d->window;
597 /* UI state is saved in binary because it is quite complex (e.g., 646 /* UI state is saved in binary because it is quite complex (e.g.,
598 navigation history, cached content) and depends closely on the widget 647 navigation history, cached content) and depends closely on the widget
599 tree. The data is largely not reorderable and should not be modified 648 tree. The data is largely not reorderable and should not be modified
@@ -602,43 +651,48 @@ static void saveState_App_(const iApp *d) {
602 if (open_File(f, writeOnly_FileMode)) { 651 if (open_File(f, writeOnly_FileMode)) {
603 writeData_File(f, magicState_App_, 4); 652 writeData_File(f, magicState_App_, 4);
604 writeU32_File(f, latest_FileVersion); /* version */ 653 writeU32_File(f, latest_FileVersion); /* version */
605 /* Begin with window state. */ { 654 iConstForEach(PtrArray, winIter, &d->mainWindows) {
606 writeData_File(f, magicWindow_App_, 4); 655 const iMainWindow *win = winIter.ptr;
607 writeU32_File(f, win->splitMode); 656 setCurrent_Window(winIter.ptr);
608 writeU32_File(f, win->base.keyRoot == win->base.roots[0] ? 0 : 1); 657 /* Window state. */ {
609 } 658 writeData_File(f, magicWindow_App_, 4);
610 /* State of UI elements. */ { 659 writeU32_File(f, win->splitMode);
611 iForIndices(i, win->base.roots) { 660 writeU32_File(f, (win->base.keyRoot == win->base.roots[0] ? 0 : 1) |
612 const iRoot *root = win->base.roots[i]; 661 (win == d->window ? current_WindowStateFlag : 0));
613 if (root) { 662 }
614 writeData_File(f, magicSidebar_App_, 4); 663 /* State of UI elements. */ {
615 const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); 664 iForIndices(i, win->base.roots) {
616 const iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); 665 const iRoot *root = win->base.roots[i];
617 writeU16_File(f, i | 666 if (root) {
618 (isVisible_Widget(sidebar) ? 0x100 : 0) | 667 writeData_File(f, magicSidebar_App_, 4);
619 (isVisible_Widget(sidebar2) ? 0x200 : 0) | 668 const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar");
620 (feedsMode_SidebarWidget(sidebar) == unread_FeedsMode ? 0x400 : 0) | 669 const iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2");
621 (feedsMode_SidebarWidget(sidebar2) == unread_FeedsMode ? 0x800 : 0)); 670 writeU16_File(f, i |
622 writeU8_File(f, 671 (isVisible_Widget(sidebar) ? 0x100 : 0) |
623 mode_SidebarWidget(sidebar) | 672 (isVisible_Widget(sidebar2) ? 0x200 : 0) |
624 (mode_SidebarWidget(sidebar2) << 4)); 673 (feedsMode_SidebarWidget(sidebar) == unread_FeedsMode ? 0x400 : 0) |
625 writef_Stream(stream_File(f), width_SidebarWidget(sidebar)); 674 (feedsMode_SidebarWidget(sidebar2) == unread_FeedsMode ? 0x800 : 0));
626 writef_Stream(stream_File(f), width_SidebarWidget(sidebar2)); 675 writeU8_File(f,
627 serialize_IntSet(closedFolders_SidebarWidget(sidebar), stream_File(f)); 676 mode_SidebarWidget(sidebar) |
628 serialize_IntSet(closedFolders_SidebarWidget(sidebar2), stream_File(f)); 677 (mode_SidebarWidget(sidebar2) << 4));
678 writef_Stream(stream_File(f), width_SidebarWidget(sidebar));
679 writef_Stream(stream_File(f), width_SidebarWidget(sidebar2));
680 serialize_IntSet(closedFolders_SidebarWidget(sidebar), stream_File(f));
681 serialize_IntSet(closedFolders_SidebarWidget(sidebar2), stream_File(f));
682 }
629 } 683 }
630 } 684 }
631 } 685 iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) {
632 iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { 686 iAssert(isInstance_Object(i.object, &Class_DocumentWidget));
633 iAssert(isInstance_Object(i.object, &Class_DocumentWidget)); 687 const iWidget *widget = constAs_Widget(i.object);
634 const iWidget *widget = constAs_Widget(i.object); 688 writeData_File(f, magicTabDocument_App_, 4);
635 writeData_File(f, magicTabDocument_App_, 4); 689 int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0);
636 int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0); 690 if (widget->root == win->base.roots[1]) {
637 if (widget->root == win->base.roots[1]) { 691 flags |= rootIndex1_DocumentStateFlag;
638 flags |= rootIndex1_DocumentStateFlag; 692 }
693 write8_File(f, flags);
694 serializeState_DocumentWidget(i.object, stream_File(f));
639 } 695 }
640 write8_File(f, flags);
641 serializeState_DocumentWidget(i.object, stream_File(f));
642 } 696 }
643 iRelease(f); 697 iRelease(f);
644 } 698 }
@@ -1491,6 +1545,7 @@ void processEvents_App(enum iAppEventMode eventMode) {
1491 window->lastHover = window->hover; 1545 window->lastHover = window->hover;
1492 wasUsed = processEvent_Window(window, &ev); 1546 wasUsed = processEvent_Window(window, &ev);
1493 if (ev.type == SDL_MOUSEMOTION || ev.type == SDL_MOUSEBUTTONDOWN) { 1547 if (ev.type == SDL_MOUSEMOTION || ev.type == SDL_MOUSEBUTTONDOWN) {
1548 /* Only offered to the frontmost window. */
1494 break; 1549 break;
1495 } 1550 }
1496 if (wasUsed) break; 1551 if (wasUsed) break;
@@ -1776,7 +1831,7 @@ void postCommand_Root(iRoot *d, const char *command) {
1776 } 1831 }
1777 SDL_Event ev = { .type = SDL_USEREVENT }; 1832 SDL_Event ev = { .type = SDL_USEREVENT };
1778 ev.user.code = command_UserEventCode; 1833 ev.user.code = command_UserEventCode;
1779 /*ev.user.windowID = id_Window(get_Window());*/ 1834// ev.user.windowID = id_Window(get_Window());
1780 ev.user.data1 = strdup(command); 1835 ev.user.data1 = strdup(command);
1781 ev.user.data2 = d; /* all events are root-specific */ 1836 ev.user.data2 = d; /* all events are root-specific */
1782 SDL_PushEvent(&ev); 1837 SDL_PushEvent(&ev);
@@ -1788,10 +1843,17 @@ void postCommand_Root(iRoot *d, const char *command) {
1788 command); 1843 command);
1789#else 1844#else
1790 if (app_.commandEcho) { 1845 if (app_.commandEcho) {
1791 printf("%s[command] {%d} %s\n", 1846 const int windowIndex =
1792 app_.isLoadingPrefs ? "[Prefs] " : "", 1847 win && type_Window(win) == main_WindowType ? windowIndex_App(as_MainWindow(win)) + 1 : 0;
1793 (d == NULL || win == NULL ? 0 : d == win->roots[0] ? 1 : 2), 1848 printf("%s%s[command] {%d:%d} %s\n",
1794 command); fflush(stdout); 1849 !app_.isFinishedLaunching ? "<Ln> " : "",
1850 app_.isLoadingPrefs ? "<Pr> " : "",
1851 windowIndex,
1852 (d == NULL || win == NULL ? 0
1853 : d == win->roots[0] ? 1
1854 : 2),
1855 command);
1856 fflush(stdout);
1795 } 1857 }
1796#endif 1858#endif
1797} 1859}
@@ -1870,6 +1932,10 @@ size_t numWindows_App(void) {
1870 return size_PtrArray(&app_.mainWindows); 1932 return size_PtrArray(&app_.mainWindows);
1871} 1933}
1872 1934
1935size_t windowIndex_App(const iMainWindow *win) {
1936 return indexOf_PtrArray(&app_.mainWindows, win);
1937}
1938
1873void setActiveWindow_App(iMainWindow *win) { 1939void setActiveWindow_App(iMainWindow *win) {
1874 iApp *d = &app_; 1940 iApp *d = &app_;
1875 d->window = win; 1941 d->window = win;
@@ -3066,12 +3132,12 @@ iBool handleCommand_App(const char *cmd) {
3066 return iFalse; 3132 return iFalse;
3067 } 3133 }
3068 else if (equal_Command(cmd, "window.new")) { 3134 else if (equal_Command(cmd, "window.new")) {
3069 iMainWindow *newWin = new_MainWindow(moved_Rect(d->initialWindowRect, init_I2(20, 20))); 3135 iMainWindow *newWin = new_MainWindow(moved_Rect(d->initialWindowRect, init_I2(30, 30)));
3070 addWindow_App(newWin); 3136 addWindow_App(newWin); /* takes ownership */
3071 SDL_ShowWindow(newWin->base.win); 3137 SDL_ShowWindow(newWin->base.win);
3072 setCurrent_Window(newWin); 3138 setCurrent_Window(newWin);
3073 postCommand_Root(newWin->base.roots[0], "navigate.home"); 3139 postCommand_Root(newWin->base.roots[0], "~navigate.home");
3074 postCommand_Root(newWin->base.roots[0], "window.unfreeze"); 3140 postCommand_Root(newWin->base.roots[0], "~window.unfreeze");
3075 return iTrue; 3141 return iTrue;
3076 } 3142 }
3077 else if (equal_Command(cmd, "tabs.new")) { 3143 else if (equal_Command(cmd, "tabs.new")) {
@@ -3659,12 +3725,16 @@ iMainWindow *mainWindow_App(void) {
3659 return app_.window; 3725 return app_.window;
3660} 3726}
3661 3727
3662void closePopups_App(void) { 3728void closePopups_App(iBool doForce) {
3663 iApp *d = &app_; 3729 iApp *d = &app_;
3664 const uint32_t now = SDL_GetTicks(); 3730 const uint32_t now = SDL_GetTicks();
3665 iConstForEach(PtrArray, i, &d->popupWindows) { 3731 iForEach(PtrArray, i, &d->popupWindows) {
3666 const iWindow *win = i.ptr; 3732 iWindow *win = i.ptr;
3667 if (now - win->focusGainedAt > 200) { 3733// if (doForce) {
3734// collect_Garbage(win, (iDeleteFunc) delete_Window);
3735// }
3736// else
3737 if (now - win->focusGainedAt > 200) {
3668 postCommand_Root(((const iWindow *) i.ptr)->roots[0], "cancel"); 3738 postCommand_Root(((const iWindow *) i.ptr)->roots[0], "cancel");
3669 } 3739 }
3670 } 3740 }
diff --git a/src/app.h b/src/app.h
index 74a5429b..dd24ec8e 100644
--- a/src/app.h
+++ b/src/app.h
@@ -127,6 +127,7 @@ void removeWindow_App (iMainWindow *win);
127void setActiveWindow_App (iMainWindow *win); 127void setActiveWindow_App (iMainWindow *win);
128void closeWindow_App (iMainWindow *win); 128void closeWindow_App (iMainWindow *win);
129size_t numWindows_App (void); 129size_t numWindows_App (void);
130size_t windowIndex_App (const iMainWindow *win);
130void addPopup_App (iWindow *popup); 131void addPopup_App (iWindow *popup);
131void removePopup_App (iWindow *popup); 132void removePopup_App (iWindow *popup);
132void postRefresh_App (void); 133void postRefresh_App (void);
@@ -151,4 +152,4 @@ void resetFonts_App (void);
151void availableFontsChanged_App(void); 152void availableFontsChanged_App(void);
152 153
153iMainWindow * mainWindow_App (void); 154iMainWindow * mainWindow_App (void);
154void closePopups_App (void); 155void closePopups_App (iBool doForce);
diff --git a/src/defs.h b/src/defs.h
index cd502f70..be5280fa 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -38,9 +38,10 @@ enum iFileVersion {
38 serializedSidebarState_FileVersion = 3, 38 serializedSidebarState_FileVersion = 3,
39 addedRecentUrlFlags_FileVersion = 4, 39 addedRecentUrlFlags_FileVersion = 4,
40 bookmarkFolderState_FileVersion = 5, 40 bookmarkFolderState_FileVersion = 5,
41 multipleWindows_FileVersion = 6,
41 /* meta */ 42 /* meta */
42 idents_FileVersion = 1, /* version used by GmCerts/idents.lgr */ 43 latest_FileVersion = 6, /* used by state.lgr */
43 latest_FileVersion = 5, 44 idents_FileVersion = 1, /* used by GmCerts/idents.lgr */
44}; 45};
45 46
46enum iImageStyle { 47enum iImageStyle {
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 7c071f47..f8cc10b9 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -5275,6 +5275,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
5275 "document.upload", 5275 "document.upload",
5276 !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") && 5276 !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") &&
5277 !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")); 5277 !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan"));
5278 setMenuItemDisabled_Widget(
5279 d->menu,
5280 "document.upload copy:1",
5281 !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") &&
5282 !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan"));
5278 } 5283 }
5279 processContextMenuEvent_Widget(d->menu, ev, {}); 5284 processContextMenuEvent_Widget(d->menu, ev, {});
5280 } 5285 }
diff --git a/src/ui/text.c b/src/ui/text.c
index 51531057..83e87d0c 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -295,6 +295,7 @@ static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId)
295 /* This is the highest priority override font. */ 295 /* This is the highest priority override font. */
296 d->overrideFontId = baseId; 296 d->overrideFontId = baseId;
297 } 297 }
298 iAssert(activeText_ == d);
298 pushBack_Array(&d->fontPriorityOrder, &(iPrioMapItem){ spec->priority, baseId }); 299 pushBack_Array(&d->fontPriorityOrder, &(iPrioMapItem){ spec->priority, baseId });
299 for (enum iFontStyle style = 0; style < max_FontStyle; style++) { 300 for (enum iFontStyle style = 0; style < max_FontStyle; style++) {
300 for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { 301 for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) {
@@ -499,10 +500,13 @@ static void resetCache_Text_(iText *d) {
499} 500}
500 501
501void resetFonts_Text(iText *d) { 502void resetFonts_Text(iText *d) {
503 iText *oldActive = activeText_;
504 setCurrent_Text(d); /* some routines rely on the global `activeText_` pointer */
502 deinitFonts_Text_(d); 505 deinitFonts_Text_(d);
503 deinitCache_Text_(d); 506 deinitCache_Text_(d);
504 initCache_Text_(d); 507 initCache_Text_(d);
505 initFonts_Text_(d); 508 initFonts_Text_(d);
509 setCurrent_Text(oldActive);
506} 510}
507 511
508static SDL_Palette *glyphPalette_(void) { 512static SDL_Palette *glyphPalette_(void) {
@@ -1476,14 +1480,14 @@ static void evenMonospaceAdvances_GlyphBuffer_(iGlyphBuffer *d, iFont *baseFont)
1476} 1480}
1477 1481
1478static iRect run_Font_(iFont *d, const iRunArgs *args) { 1482static iRect run_Font_(iFont *d, const iRunArgs *args) {
1479 const int mode = args->mode; 1483 const int mode = args->mode;
1480 const iInt2 orig = args->pos; 1484 const iInt2 orig = args->pos;
1481 iRect bounds = { orig, init_I2(0, d->height) }; 1485 iRect bounds = { orig, init_I2(0, d->height) };
1482 float xCursor = 0.0f; 1486 float xCursor = 0.0f;
1483 float yCursor = 0.0f; 1487 float yCursor = 0.0f;
1484 float xCursorMax = 0.0f; 1488 float xCursorMax = 0.0f;
1485 const iBool isMonospaced = isMonospaced_Font(d); 1489 const iBool isMonospaced = isMonospaced_Font(d);
1486 iWrapText *wrap = args->wrap; 1490 iWrapText *wrap = args->wrap;
1487 iAssert(args->text.end >= args->text.start); 1491 iAssert(args->text.end >= args->text.start);
1488 /* Split the text into a number of attributed runs that specify exactly which 1492 /* Split the text into a number of attributed runs that specify exactly which
1489 font is used and other attributes such as color. (HarfBuzz shaping is done 1493 font is used and other attributes such as color. (HarfBuzz shaping is done
diff --git a/src/ui/window.c b/src/ui/window.c
index bc32e479..0a97b97c 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -865,7 +865,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent
865 if (d->base.isMinimized) { 865 if (d->base.isMinimized) {
866 return iFalse; 866 return iFalse;
867 } 867 }
868 closePopups_App(); 868 closePopups_App(iFalse);
869 checkPixelRatioChange_Window_(as_Window(d)); 869 checkPixelRatioChange_Window_(as_Window(d));
870 const iInt2 newPos = init_I2(ev->data1, ev->data2); 870 const iInt2 newPos = init_I2(ev->data1, ev->data2);
871 if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ 871 if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */
@@ -915,7 +915,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent
915 // updateSize_Window_(d, iTrue); 915 // updateSize_Window_(d, iTrue);
916 return iTrue; 916 return iTrue;
917 } 917 }
918 closePopups_App(); 918 closePopups_App(iFalse);
919 if (unsnap_MainWindow_(d, NULL)) { 919 if (unsnap_MainWindow_(d, NULL)) {
920 return iTrue; 920 return iTrue;
921 } 921 }
@@ -937,7 +937,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent
937 return iTrue; 937 return iTrue;
938 case SDL_WINDOWEVENT_MINIMIZED: 938 case SDL_WINDOWEVENT_MINIMIZED:
939 d->base.isMinimized = iTrue; 939 d->base.isMinimized = iTrue;
940 closePopups_App(); 940 closePopups_App(iTrue);
941 return iTrue; 941 return iTrue;
942#else /* if defined (!iPlatformDesktop) */ 942#else /* if defined (!iPlatformDesktop) */
943 case SDL_WINDOWEVENT_RESIZED: 943 case SDL_WINDOWEVENT_RESIZED:
@@ -973,7 +973,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent
973#if !defined (iPlatformDesktop) 973#if !defined (iPlatformDesktop)
974 setFreezeDraw_MainWindow(d, iTrue); 974 setFreezeDraw_MainWindow(d, iTrue);
975#endif 975#endif
976 closePopups_App(); 976 closePopups_App(iTrue);
977 return iFalse; 977 return iFalse;
978 case SDL_WINDOWEVENT_TAKE_FOCUS: 978 case SDL_WINDOWEVENT_TAKE_FOCUS:
979 SDL_SetWindowInputFocus(d->base.win); 979 SDL_SetWindowInputFocus(d->base.win);
@@ -1178,7 +1178,35 @@ iLocalDef iBool isEscapeKeypress_(const SDL_Event *ev) {
1178 return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE; 1178 return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE;
1179} 1179}
1180 1180
1181static uint32_t windowId_SDLEvent_(const SDL_Event *ev) {
1182 switch (ev->type) {
1183 case SDL_MOUSEBUTTONDOWN:
1184 case SDL_MOUSEBUTTONUP:
1185 return ev->button.windowID;
1186 case SDL_MOUSEMOTION:
1187 return ev->motion.windowID;
1188 case SDL_MOUSEWHEEL:
1189 return ev->wheel.windowID;
1190 case SDL_KEYDOWN:
1191 case SDL_KEYUP:
1192 return ev->key.windowID;
1193 case SDL_TEXTINPUT:
1194 return ev->text.windowID;
1195 case SDL_USEREVENT:
1196 return ev->user.windowID;
1197 default:
1198 return 0;
1199 }
1200}
1201
1181iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { 1202iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) {
1203#if 0
1204 /* For the right window? */
1205 const uint32_t evWin = windowId_SDLEvent_(ev);
1206 if (evWin && evWin != id_Window(d)) {
1207 return iFalse; /* Meant for a different window. */
1208 }
1209#endif
1182 if (ev->type == SDL_MOUSEMOTION) { 1210 if (ev->type == SDL_MOUSEMOTION) {
1183 /* Hover widget may change. */ 1211 /* Hover widget may change. */
1184 setHover_Widget(NULL); 1212 setHover_Widget(NULL);