summaryrefslogtreecommitdiff
path: root/src/app.c
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 /src/app.c
parentbef61b34d8a23e2e8594207c2876ce982fe0e15f (diff)
Serializing multiple windows
Not quite fully functional yet. The window positioning still needs to be written to prefs.cfg.
Diffstat (limited to 'src/app.c')
-rw-r--r--src/app.c202
1 files changed, 136 insertions, 66 deletions
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 }