summaryrefslogtreecommitdiff
path: root/src/ui/window.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-20 11:37:23 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-20 11:37:23 +0300
commit2d81addf78d6a8b0fb2f2959b04a385c4adffdf2 (patch)
tree5e0f45b9c945499bc6a6669563de13c5203981a6 /src/ui/window.c
parent201021092d204680b353c82ce9e9beb76f3044e8 (diff)
Experimenting with independent popup windows
Toe dipping into multiple window support by allowing popup menu widgets to be displayed in independent windows. This is not a 100% replacement for native menus, but it gets pretty close.
Diffstat (limited to 'src/ui/window.c')
-rw-r--r--src/ui/window.c257
1 files changed, 193 insertions, 64 deletions
diff --git a/src/ui/window.c b/src/ui/window.c
index 92125d81..e9a34ace 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -57,7 +57,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
57#include "stb_image.h" 57#include "stb_image.h"
58#include "stb_image_resize.h" 58#include "stb_image_resize.h"
59 59
60static iWindow *theWindow_ = NULL; 60static iWindow * theWindow_;
61static iMainWindow *theMainWindow_;
61 62
62#if defined (iPlatformApple) || defined (iPlatformLinux) || defined (iPlatformOther) 63#if defined (iPlatformApple) || defined (iPlatformLinux) || defined (iPlatformOther)
63static float initialUiScale_ = 1.0f; 64static float initialUiScale_ = 1.0f;
@@ -67,6 +68,9 @@ static float initialUiScale_ = 1.1f;
67 68
68static iBool isOpenGLRenderer_; 69static iBool isOpenGLRenderer_;
69 70
71iDefineTypeConstructionArgs(Window,
72 (enum iWindowType type, iRect rect, uint32_t flags),
73 type, rect, flags)
70iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect) 74iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect)
71 75
72/* TODO: Define menus per platform. */ 76/* TODO: Define menus per platform. */
@@ -205,6 +209,7 @@ static void setupUserInterface_MainWindow(iMainWindow *d) {
205#endif 209#endif
206 /* One root is created by default. */ 210 /* One root is created by default. */
207 d->base.roots[0] = new_Root(); 211 d->base.roots[0] = new_Root();
212 d->base.roots[0]->window = as_Window(d);
208 setCurrent_Root(d->base.roots[0]); 213 setCurrent_Root(d->base.roots[0]);
209 createUserInterface_Root(d->base.roots[0]); 214 createUserInterface_Root(d->base.roots[0]);
210 setCurrent_Root(NULL); 215 setCurrent_Root(NULL);
@@ -409,7 +414,6 @@ void init_Window(iWindow *d, enum iWindowType type, iRect rect, uint32_t flags)
409 d->mouseGrab = NULL; 414 d->mouseGrab = NULL;
410 d->focus = NULL; 415 d->focus = NULL;
411 d->pendingCursor = NULL; 416 d->pendingCursor = NULL;
412 d->isDrawFrozen = iTrue;
413 d->isExposed = iFalse; 417 d->isExposed = iFalse;
414 d->isMinimized = iFalse; 418 d->isMinimized = iFalse;
415 d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */ 419 d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */
@@ -441,9 +445,27 @@ void init_Window(iWindow *d, enum iWindowType type, iRect rect, uint32_t flags)
441 d->uiScale = initialUiScale_; 445 d->uiScale = initialUiScale_;
442 /* TODO: Ratios, scales, and metrics must be window-specific, not global. */ 446 /* TODO: Ratios, scales, and metrics must be window-specific, not global. */
443 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale); 447 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale);
448 d->text = new_Text(d->render);
449}
450
451static void deinitRoots_Window_(iWindow *d) {
452 iRecycle();
453 iForIndices(i, d->roots) {
454 if (d->roots[i]) {
455 setCurrent_Root(d->roots[i]);
456 delete_Root(d->roots[i]);
457 d->roots[i] = NULL;
458 }
459 }
460 setCurrent_Root(NULL);
444} 461}
445 462
446void deinit_Window(iWindow *d) { 463void deinit_Window(iWindow *d) {
464 if (d->type == popup_WindowType) {
465 removePopup_App(d);
466 }
467 deinitRoots_Window_(d);
468 delete_Text(d->text);
447 SDL_DestroyRenderer(d->render); 469 SDL_DestroyRenderer(d->render);
448 SDL_DestroyWindow(d->win); 470 SDL_DestroyWindow(d->win);
449 iForIndices(i, d->cursors) { 471 iForIndices(i, d->cursors) {
@@ -455,6 +477,7 @@ void deinit_Window(iWindow *d) {
455 477
456void init_MainWindow(iMainWindow *d, iRect rect) { 478void init_MainWindow(iMainWindow *d, iRect rect) {
457 theWindow_ = &d->base; 479 theWindow_ = &d->base;
480 theMainWindow_ = d;
458 uint32_t flags = 0; 481 uint32_t flags = 0;
459#if defined (iPlatformAppleDesktop) 482#if defined (iPlatformAppleDesktop)
460 SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); 483 SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl");
@@ -465,13 +488,15 @@ void init_MainWindow(iMainWindow *d, iRect rect) {
465#endif 488#endif
466 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); 489 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
467 init_Window(&d->base, main_WindowType, rect, flags); 490 init_Window(&d->base, main_WindowType, rect, flags);
468 d->splitMode = d->pendingSplitMode = 0; 491 d->isDrawFrozen = iTrue;
469 d->pendingSplitUrl = new_String(); 492 d->splitMode = 0;
470 d->place.initialPos = rect.pos; 493 d->pendingSplitMode = 0;
471 d->place.normalRect = rect; 494 d->pendingSplitUrl = new_String();
495 d->place.initialPos = rect.pos;
496 d->place.normalRect = rect;
472 d->place.lastNotifiedSize = zero_I2(); 497 d->place.lastNotifiedSize = zero_I2();
473 d->place.snap = 0; 498 d->place.snap = 0;
474 d->keyboardHeight = 0; 499 d->keyboardHeight = 0;
475#if defined(iPlatformMobile) 500#if defined(iPlatformMobile)
476 const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */ 501 const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */
477#else 502#else
@@ -510,9 +535,9 @@ void init_MainWindow(iMainWindow *d, iRect rect) {
510 } 535 }
511#endif 536#endif
512#if defined (iPlatformAppleMobile) 537#if defined (iPlatformAppleMobile)
513 setupWindow_iOS(d); 538 setupWindow_iOS(as_Window(d));
514#endif 539#endif
515 init_Text(d->base.render); 540 setCurrent_Text(d->base.text);
516 SDL_GetRendererOutputSize(d->base.render, &d->base.size.x, &d->base.size.y); 541 SDL_GetRendererOutputSize(d->base.render, &d->base.size.x, &d->base.size.y);
517 setupUserInterface_MainWindow(d); 542 setupUserInterface_MainWindow(d);
518 postCommand_App("~bindings.changed"); /* update from bindings */ 543 postCommand_App("~bindings.changed"); /* update from bindings */
@@ -538,24 +563,15 @@ void init_MainWindow(iMainWindow *d, iRect rect) {
538#endif 563#endif
539} 564}
540 565
541static void deinitRoots_Window_(iWindow *d) {
542 iRecycle();
543 iForIndices(i, d->roots) {
544 if (d->roots[i]) {
545 setCurrent_Root(d->roots[i]);
546 deinit_Root(d->roots[i]);
547 }
548 }
549 setCurrent_Root(NULL);
550}
551
552void deinit_MainWindow(iMainWindow *d) { 566void deinit_MainWindow(iMainWindow *d) {
553 deinitRoots_Window_(as_Window(d)); 567 deinitRoots_Window_(as_Window(d));
554 if (theWindow_ == as_Window(d)) { 568 if (theWindow_ == as_Window(d)) {
555 theWindow_ = NULL; 569 theWindow_ = NULL;
556 } 570 }
571 if (theMainWindow_ == d) {
572 theMainWindow_ = NULL;
573 }
557 delete_String(d->pendingSplitUrl); 574 delete_String(d->pendingSplitUrl);
558 deinit_Text();
559 deinit_Window(&d->base); 575 deinit_Window(&d->base);
560} 576}
561 577
@@ -592,7 +608,7 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) {
592static void invalidate_MainWindow_(iMainWindow *d, iBool forced) { 608static void invalidate_MainWindow_(iMainWindow *d, iBool forced) {
593 if (d && (!d->base.isInvalidated || forced)) { 609 if (d && (!d->base.isInvalidated || forced)) {
594 d->base.isInvalidated = iTrue; 610 d->base.isInvalidated = iTrue;
595 resetFonts_Text(); 611 resetFonts_Text(text_Window(d));
596 postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ 612 postCommand_App("theme.changed auto:1"); /* forces UI invalidation */
597 } 613 }
598} 614}
@@ -607,7 +623,7 @@ void invalidate_Window(iAnyWindow *d) {
607} 623}
608 624
609static iBool isNormalPlacement_MainWindow_(const iMainWindow *d) { 625static iBool isNormalPlacement_MainWindow_(const iMainWindow *d) {
610 if (d->base.isDrawFrozen) return iFalse; 626 if (d->isDrawFrozen) return iFalse;
611#if defined (iPlatformApple) 627#if defined (iPlatformApple)
612 /* Maximized mode is not special on macOS. */ 628 /* Maximized mode is not special on macOS. */
613 if (snap_MainWindow(d) == maximized_WindowSnap) { 629 if (snap_MainWindow(d) == maximized_WindowSnap) {
@@ -655,7 +671,7 @@ static iBool unsnap_MainWindow_(iMainWindow *d, const iInt2 *newPos) {
655static void notifyMetricsChange_Window_(const iWindow *d) { 671static void notifyMetricsChange_Window_(const iWindow *d) {
656 /* Dynamic UI metrics change. Widgets need to update themselves. */ 672 /* Dynamic UI metrics change. Widgets need to update themselves. */
657 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale); 673 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale);
658 resetFonts_Text(); 674 resetFonts_Text(d->text);
659 postCommand_App("metrics.changed"); 675 postCommand_App("metrics.changed");
660} 676}
661 677
@@ -676,6 +692,41 @@ static void checkPixelRatioChange_Window_(iWindow *d) {
676 } 692 }
677} 693}
678 694
695static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
696 if (ev->windowID != SDL_GetWindowID(d->win)) {
697 return iFalse;
698 }
699 switch (ev->event) {
700 case SDL_WINDOWEVENT_EXPOSED:
701 d->isExposed = iTrue;
702 postRefresh_App();
703 return iTrue;
704 case SDL_WINDOWEVENT_RESTORED:
705 case SDL_WINDOWEVENT_SHOWN:
706 postRefresh_App();
707 return iTrue;
708 case SDL_WINDOWEVENT_FOCUS_LOST:
709 /* Popup windows are currently only used for menus. */
710 closeMenu_Widget(d->roots[0]->widget);
711 return iTrue;
712 case SDL_WINDOWEVENT_LEAVE:
713 unhover_Widget();
714 d->isMouseInside = iFalse;
715 //postCommand_App("window.mouse.exited");
716// SDL_SetWindowInputFocus(mainWindow_App()->base.win);
717 printf("mouse leaves popup\n"); fflush(stdout);
718 //SDL_RaiseWindow(mainWindow_App()->base.win);
719 postRefresh_App();
720 return iTrue;
721 case SDL_WINDOWEVENT_ENTER:
722 d->isMouseInside = iTrue;
723 //postCommand_App("window.mouse.entered");
724 printf("mouse enters popup\n"); fflush(stdout);
725 return iTrue;
726 }
727 return iFalse;
728}
729
679static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) { 730static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) {
680 switch (ev->event) { 731 switch (ev->event) {
681#if defined(iPlatformDesktop) 732#if defined(iPlatformDesktop)
@@ -795,6 +846,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent
795 return iTrue; 846 return iTrue;
796 case SDL_WINDOWEVENT_ENTER: 847 case SDL_WINDOWEVENT_ENTER:
797 d->base.isMouseInside = iTrue; 848 d->base.isMouseInside = iTrue;
849 SDL_SetWindowInputFocus(d->base.win);
798 postCommand_App("window.mouse.entered"); 850 postCommand_App("window.mouse.entered");
799 return iTrue; 851 return iTrue;
800 case SDL_WINDOWEVENT_FOCUS_GAINED: 852 case SDL_WINDOWEVENT_FOCUS_GAINED:
@@ -802,16 +854,16 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent
802 setCapsLockDown_Keys(iFalse); 854 setCapsLockDown_Keys(iFalse);
803 postCommand_App("window.focus.gained"); 855 postCommand_App("window.focus.gained");
804 d->base.isExposed = iTrue; 856 d->base.isExposed = iTrue;
805#if !defined(iPlatformDesktop) 857#if !defined (iPlatformDesktop)
806 /* Returned to foreground, may have lost buffered content. */ 858 /* Returned to foreground, may have lost buffered content. */
807 invalidate_Window_(d, iTrue); 859 invalidate_MainWindow_(d, iTrue);
808 postCommand_App("window.unfreeze"); 860 postCommand_App("window.unfreeze");
809#endif 861#endif
810 return iFalse; 862 return iFalse;
811 case SDL_WINDOWEVENT_FOCUS_LOST: 863 case SDL_WINDOWEVENT_FOCUS_LOST:
812 postCommand_App("window.focus.lost"); 864 postCommand_App("window.focus.lost");
813#if !defined(iPlatformDesktop) 865#if !defined (iPlatformDesktop)
814 setFreezeDraw_Window(d, iTrue); 866 setFreezeDraw_MainWindow(d, iTrue);
815#endif 867#endif
816 return iFalse; 868 return iFalse;
817 case SDL_WINDOWEVENT_TAKE_FOCUS: 869 case SDL_WINDOWEVENT_TAKE_FOCUS:
@@ -831,8 +883,8 @@ static void applyCursor_Window_(iWindow *d) {
831 } 883 }
832} 884}
833 885
834iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) { 886iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
835 iWindow *w = as_Window(d); 887 iMainWindow *mw = (type_Window(d) == main_WindowType ? as_MainWindow(d) : NULL);
836 switch (ev->type) { 888 switch (ev->type) {
837#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 889#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
838 case SDL_SYSWMEVENT: { 890 case SDL_SYSWMEVENT: {
@@ -845,19 +897,26 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {
845 } 897 }
846#endif 898#endif
847 case SDL_WINDOWEVENT: { 899 case SDL_WINDOWEVENT: {
848 return handleWindowEvent_MainWindow_(d, &ev->window); 900 if (mw) {
901 return handleWindowEvent_MainWindow_(mw, &ev->window);
902 }
903 else {
904 return handleWindowEvent_Window_(d, &ev->window);
905 }
849 } 906 }
850 case SDL_RENDER_TARGETS_RESET: 907 case SDL_RENDER_TARGETS_RESET:
851 case SDL_RENDER_DEVICE_RESET: { 908 case SDL_RENDER_DEVICE_RESET: {
852 invalidate_MainWindow_(d, iTrue /* force full reset */); 909 if (mw) {
910 invalidate_MainWindow_(mw, iTrue /* force full reset */);
911 }
853 break; 912 break;
854 } 913 }
855 default: { 914 default: {
856 SDL_Event event = *ev; 915 SDL_Event event = *ev;
857 if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze")) { 916 if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze")) {
858 d->base.isDrawFrozen = iFalse; 917 mw->isDrawFrozen = iFalse;
859 if (SDL_GetWindowFlags(w->win) & SDL_WINDOW_HIDDEN) { 918 if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_HIDDEN) {
860 SDL_ShowWindow(w->win); 919 SDL_ShowWindow(d->win);
861 } 920 }
862 postRefresh_App(); 921 postRefresh_App();
863 postCommand_App("media.player.update"); /* in case a player needs updating */ 922 postCommand_App("media.player.update"); /* in case a player needs updating */
@@ -866,35 +925,35 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {
866 if (processEvent_Touch(&event)) { 925 if (processEvent_Touch(&event)) {
867 return iTrue; 926 return iTrue;
868 } 927 }
869 if (event.type == SDL_KEYDOWN && SDL_GetTicks() - d->base.focusGainedAt < 10) { 928 if (event.type == SDL_KEYDOWN && SDL_GetTicks() - d->focusGainedAt < 10) {
870 /* Suspiciously close to when input focus was received. For example under openbox, 929 /* Suspiciously close to when input focus was received. For example under openbox,
871 closing xterm with Ctrl+D will cause the keydown event to "spill" over to us. 930 closing xterm with Ctrl+D will cause the keydown event to "spill" over to us.
872 As a workaround, ignore these events. */ 931 As a workaround, ignore these events. */
873 return iTrue; /* won't go to bindings, either */ 932 return iTrue; /* won't go to bindings, either */
874 } 933 }
875 if (event.type == SDL_MOUSEBUTTONDOWN && d->base.ignoreClick) { 934 if (event.type == SDL_MOUSEBUTTONDOWN && d->ignoreClick) {
876 d->base.ignoreClick = iFalse; 935 d->ignoreClick = iFalse;
877 return iTrue; 936 return iTrue;
878 } 937 }
879 /* Map mouse pointer coordinate to our coordinate system. */ 938 /* Map mouse pointer coordinate to our coordinate system. */
880 if (event.type == SDL_MOUSEMOTION) { 939 if (event.type == SDL_MOUSEMOTION) {
881 setCursor_Window(w, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */ 940 setCursor_Window(d, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */
882 const iInt2 pos = coord_Window(w, event.motion.x, event.motion.y); 941 const iInt2 pos = coord_Window(d, event.motion.x, event.motion.y);
883 event.motion.x = pos.x; 942 event.motion.x = pos.x;
884 event.motion.y = pos.y; 943 event.motion.y = pos.y;
885 } 944 }
886 else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { 945 else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
887 const iInt2 pos = coord_Window(w, event.button.x, event.button.y); 946 const iInt2 pos = coord_Window(d, event.button.x, event.button.y);
888 event.button.x = pos.x; 947 event.button.x = pos.x;
889 event.button.y = pos.y; 948 event.button.y = pos.y;
890 if (event.type == SDL_MOUSEBUTTONDOWN) { 949 if (event.type == SDL_MOUSEBUTTONDOWN) {
891 /* Button clicks will change keyroot. */ 950 /* Button clicks will change keyroot. */
892 if (numRoots_Window(w) > 1) { 951 if (numRoots_Window(d) > 1) {
893 const iInt2 click = init_I2(event.button.x, event.button.y); 952 const iInt2 click = init_I2(event.button.x, event.button.y);
894 iForIndices(i, w->roots) { 953 iForIndices(i, d->roots) {
895 iRoot *root = w->roots[i]; 954 iRoot *root = d->roots[i];
896 if (root != w->keyRoot && contains_Rect(rect_Root(root), click)) { 955 if (root != d->keyRoot && contains_Rect(rect_Root(root), click)) {
897 setKeyRoot_Window(w, root); 956 setKeyRoot_Window(d, root);
898 break; 957 break;
899 } 958 }
900 } 959 }
@@ -909,13 +968,13 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {
909 event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { 968 event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
910 if (mouseGrab_Widget()) { 969 if (mouseGrab_Widget()) {
911 iWidget *grabbed = mouseGrab_Widget(); 970 iWidget *grabbed = mouseGrab_Widget();
912 setCurrent_Root(findRoot_Window(w, grabbed)); 971 setCurrent_Root(findRoot_Window(d, grabbed));
913 wasUsed = dispatchEvent_Widget(grabbed, &event); 972 wasUsed = dispatchEvent_Widget(grabbed, &event);
914 } 973 }
915 } 974 }
916 /* Dispatch the event to the tree of widgets. */ 975 /* Dispatch the event to the tree of widgets. */
917 if (!wasUsed) { 976 if (!wasUsed) {
918 wasUsed = dispatchEvent_Window(w, &event); 977 wasUsed = dispatchEvent_Window(d, &event);
919 } 978 }
920 if (!wasUsed) { 979 if (!wasUsed) {
921 /* As a special case, clicking the middle mouse button can be used for pasting 980 /* As a special case, clicking the middle mouse button can be used for pasting
@@ -928,35 +987,35 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {
928 paste.key.keysym.mod = KMOD_PRIMARY; 987 paste.key.keysym.mod = KMOD_PRIMARY;
929 paste.key.state = SDL_PRESSED; 988 paste.key.state = SDL_PRESSED;
930 paste.key.timestamp = SDL_GetTicks(); 989 paste.key.timestamp = SDL_GetTicks();
931 wasUsed = dispatchEvent_Window(w, &paste); 990 wasUsed = dispatchEvent_Window(d, &paste);
932 } 991 }
933 if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) { 992 if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {
934 if (postContextClick_Window(w, &event.button)) { 993 if (postContextClick_Window(d, &event.button)) {
935 wasUsed = iTrue; 994 wasUsed = iTrue;
936 } 995 }
937 } 996 }
938 } 997 }
939 if (isMetricsChange_UserEvent(&event)) { 998 if (isMetricsChange_UserEvent(&event)) {
940 iForIndices(i, w->roots) { 999 iForIndices(i, d->roots) {
941 updateMetrics_Root(w->roots[i]); 1000 updateMetrics_Root(d->roots[i]);
942 } 1001 }
943 } 1002 }
944 if (isCommand_UserEvent(&event, "lang.changed")) { 1003 if (isCommand_UserEvent(&event, "lang.changed") && mw) {
945#if defined (iHaveNativeMenus) 1004#if defined (iHaveNativeMenus)
946 /* Retranslate the menus. */ 1005 /* Retranslate the menus. */
947 removeMacMenus_(); 1006 removeMacMenus_();
948 insertMacMenus_(); 1007 insertMacMenus_();
949#endif 1008#endif
950 invalidate_Window(w); 1009 invalidate_Window(d);
951 iForIndices(i, w->roots) { 1010 iForIndices(i, d->roots) {
952 if (w->roots[i]) { 1011 if (d->roots[i]) {
953 updatePreferencesLayout_Widget(findChild_Widget(w->roots[i]->widget, "prefs")); 1012 updatePreferencesLayout_Widget(findChild_Widget(d->roots[i]->widget, "prefs"));
954 arrange_Widget(w->roots[i]->widget); 1013 arrange_Widget(d->roots[i]->widget);
955 } 1014 }
956 } 1015 }
957 } 1016 }
958 if (event.type == SDL_MOUSEMOTION) { 1017 if (event.type == SDL_MOUSEMOTION) {
959 applyCursor_Window_(w); 1018 applyCursor_Window_(d);
960 } 1019 }
961 return wasUsed; 1020 return wasUsed;
962 } 1021 }
@@ -1003,6 +1062,9 @@ iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) {
1003 coord_MouseWheelEvent(&ev->wheel))) { 1062 coord_MouseWheelEvent(&ev->wheel))) {
1004 continue; /* Only process the event in the relevant split. */ 1063 continue; /* Only process the event in the relevant split. */
1005 } 1064 }
1065 if (!root->widget) {
1066 continue;
1067 }
1006 setCurrent_Root(root); 1068 setCurrent_Root(root);
1007 const iBool wasUsed = dispatchEvent_Widget(root->widget, ev); 1069 const iBool wasUsed = dispatchEvent_Widget(root->widget, ev);
1008 if (wasUsed) { 1070 if (wasUsed) {
@@ -1044,11 +1106,40 @@ iBool postContextClick_Window(iWindow *d, const SDL_MouseButtonEvent *ev) {
1044 return iFalse; 1106 return iFalse;
1045} 1107}
1046 1108
1109void draw_Window(iWindow *d) {
1110 if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_HIDDEN) {
1111 return;
1112 }
1113 iPaint p;
1114 init_Paint(&p);
1115 iRoot *root = d->roots[0];
1116 setCurrent_Root(root);
1117 unsetClip_Paint(&p); /* update clip to full window */
1118 const iColor back = get_Color(uiBackground_ColorId);
1119 SDL_SetRenderDrawColor(d->render, back.r, back.g, back.b, 255);
1120 SDL_RenderClear(d->render);
1121 d->frameTime = SDL_GetTicks();
1122 if (isExposed_Window(d)) {
1123 d->isInvalidated = iFalse;
1124 extern int drawCount_;
1125 drawRoot_Widget(root->widget);
1126#if !defined (NDEBUG)
1127 draw_Text(defaultBold_FontId, safeRect_Root(root).pos, red_ColorId, "%d", drawCount_);
1128 drawCount_ = 0;
1129#endif
1130 }
1131// drawRectThickness_Paint(&p, (iRect){ zero_I2(), sub_I2(d->size, one_I2()) }, gap_UI / 4, uiSeparator_ColorId);
1132 setCurrent_Root(NULL);
1133 SDL_RenderPresent(d->render);
1134}
1135
1047void draw_MainWindow(iMainWindow *d) { 1136void draw_MainWindow(iMainWindow *d) {
1137 /* TODO: Try to make this a specialization of `draw_Window`? */
1048 iWindow *w = as_Window(d); 1138 iWindow *w = as_Window(d);
1049 if (w->isDrawFrozen) { 1139 if (d->isDrawFrozen) {
1050 return; 1140 return;
1051 } 1141 }
1142 setCurrent_Text(d->base.text);
1052 /* Check if root needs resizing. */ { 1143 /* Check if root needs resizing. */ {
1053 iInt2 renderSize; 1144 iInt2 renderSize;
1054 SDL_GetRendererOutputSize(w->render, &renderSize.x, &renderSize.y); 1145 SDL_GetRendererOutputSize(w->render, &renderSize.x, &renderSize.y);
@@ -1180,7 +1271,7 @@ void setUiScale_Window(iWindow *d, float uiScale) {
1180 } 1271 }
1181} 1272}
1182 1273
1183void setFreezeDraw_Window(iWindow *d, iBool freezeDraw) { 1274void setFreezeDraw_MainWindow(iMainWindow *d, iBool freezeDraw) {
1184 d->isDrawFrozen = freezeDraw; 1275 d->isDrawFrozen = freezeDraw;
1185} 1276}
1186 1277
@@ -1231,8 +1322,23 @@ iWindow *get_Window(void) {
1231 return theWindow_; 1322 return theWindow_;
1232} 1323}
1233 1324
1325void setCurrent_Window(iAnyWindow *d) {
1326 theWindow_ = d;
1327 if (type_Window(d) == main_WindowType) {
1328 theMainWindow_ = d;
1329 }
1330 if (d) {
1331 setCurrent_Text(theWindow_->text);
1332 setCurrent_Root(theWindow_->keyRoot);
1333 }
1334 else {
1335 setCurrent_Text(NULL);
1336 setCurrent_Root(NULL);
1337 }
1338}
1339
1234iMainWindow *get_MainWindow(void) { 1340iMainWindow *get_MainWindow(void) {
1235 return as_MainWindow(theWindow_); 1341 return theMainWindow_;
1236} 1342}
1237 1343
1238iBool isOpenGLRenderer_Window(void) { 1344iBool isOpenGLRenderer_Window(void) {
@@ -1272,7 +1378,7 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) {
1272 iAssert(current_Root() == NULL); 1378 iAssert(current_Root() == NULL);
1273 if (d->splitMode != splitMode) { 1379 if (d->splitMode != splitMode) {
1274 int oldCount = numRoots_Window(w); 1380 int oldCount = numRoots_Window(w);
1275 setFreezeDraw_Window(w, iTrue); 1381 setFreezeDraw_MainWindow(d, iTrue);
1276 if (oldCount == 2 && splitMode == 0) { 1382 if (oldCount == 2 && splitMode == 0) {
1277 /* Keep references to the tabs of the second root. */ 1383 /* Keep references to the tabs of the second root. */
1278 const iDocumentWidget *curPage = document_Root(w->keyRoot); 1384 const iDocumentWidget *curPage = document_Root(w->keyRoot);
@@ -1311,6 +1417,7 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) {
1311 } 1417 }
1312 w->roots[newRootIndex] = new_Root(); 1418 w->roots[newRootIndex] = new_Root();
1313 w->keyRoot = w->roots[newRootIndex]; 1419 w->keyRoot = w->roots[newRootIndex];
1420 w->keyRoot->window = w;
1314 setCurrent_Root(w->roots[newRootIndex]); 1421 setCurrent_Root(w->roots[newRootIndex]);
1315 createUserInterface_Root(w->roots[newRootIndex]); 1422 createUserInterface_Root(w->roots[newRootIndex]);
1316 if (!isEmpty_String(d->pendingSplitUrl)) { 1423 if (!isEmpty_String(d->pendingSplitUrl)) {
@@ -1471,3 +1578,25 @@ int snap_MainWindow(const iMainWindow *d) {
1471 } 1578 }
1472 return d->place.snap; 1579 return d->place.snap;
1473} 1580}
1581
1582/*----------------------------------------------------------------------------------------------*/
1583
1584iWindow *newPopup_Window(iInt2 screenPos, iWidget *rootWidget) {
1585 arrange_Widget(rootWidget);
1586 iWindow *win =
1587 new_Window(popup_WindowType,
1588 (iRect){ screenPos, divf_I2(rootWidget->rect.size, get_Window()->pixelRatio) },
1589 SDL_WINDOW_ALWAYS_ON_TOP |
1590 SDL_WINDOW_POPUP_MENU |
1591 SDL_WINDOW_SKIP_TASKBAR);
1592#if defined (iPlatformAppleDesktop)
1593 hideTitleBar_MacOS(win); /* make it a borderless window */
1594#endif
1595 iRoot *root = new_Root();
1596 win->roots[0] = root;
1597 win->keyRoot = root;
1598 root->widget = rootWidget;
1599 root->window = win;
1600 setRoot_Widget(rootWidget, root);
1601 return win;
1602}