diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-18 19:51:38 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-18 19:51:38 +0200 |
commit | dd9add718c4a9a2b29cd38b05886f22877b89b35 (patch) | |
tree | 2bec152d5f813c30373c802a03076f8268e0aaac /src/app.c | |
parent | bef61b34d8a23e2e8594207c2876ce982fe0e15f (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.c | 202 |
1 files changed, 136 insertions, 66 deletions
@@ -476,7 +476,11 @@ static const char *magicSidebar_App_ = "side"; | |||
476 | 476 | ||
477 | enum iDocumentStateFlag { | 477 | enum iDocumentStateFlag { |
478 | current_DocumentStateFlag = iBit(1), | 478 | current_DocumentStateFlag = iBit(1), |
479 | rootIndex1_DocumentStateFlag = iBit(2) | 479 | rootIndex1_DocumentStateFlag = iBit(2), |
480 | }; | ||
481 | |||
482 | enum iWindowStateFlag { | ||
483 | current_WindowStateFlag = iBit(9), | ||
480 | }; | 484 | }; |
481 | 485 | ||
482 | static iBool loadState_App_(iApp *d) { | 486 | static 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) { | |||
593 | static void saveState_App_(const iApp *d) { | 643 | static 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 | ||
1935 | size_t windowIndex_App(const iMainWindow *win) { | ||
1936 | return indexOf_PtrArray(&app_.mainWindows, win); | ||
1937 | } | ||
1938 | |||
1873 | void setActiveWindow_App(iMainWindow *win) { | 1939 | void 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 | ||
3662 | void closePopups_App(void) { | 3728 | void 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 | } |