diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-20 11:37:23 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-20 11:37:23 +0300 |
commit | 2d81addf78d6a8b0fb2f2959b04a385c4adffdf2 (patch) | |
tree | 5e0f45b9c945499bc6a6669563de13c5203981a6 /src/ui/window.c | |
parent | 201021092d204680b353c82ce9e9beb76f3044e8 (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.c | 257 |
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 | ||
60 | static iWindow *theWindow_ = NULL; | 60 | static iWindow * theWindow_; |
61 | static iMainWindow *theMainWindow_; | ||
61 | 62 | ||
62 | #if defined (iPlatformApple) || defined (iPlatformLinux) || defined (iPlatformOther) | 63 | #if defined (iPlatformApple) || defined (iPlatformLinux) || defined (iPlatformOther) |
63 | static float initialUiScale_ = 1.0f; | 64 | static float initialUiScale_ = 1.0f; |
@@ -67,6 +68,9 @@ static float initialUiScale_ = 1.1f; | |||
67 | 68 | ||
68 | static iBool isOpenGLRenderer_; | 69 | static iBool isOpenGLRenderer_; |
69 | 70 | ||
71 | iDefineTypeConstructionArgs(Window, | ||
72 | (enum iWindowType type, iRect rect, uint32_t flags), | ||
73 | type, rect, flags) | ||
70 | iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect) | 74 | iDefineTypeConstructionArgs(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 | |||
451 | static 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 | ||
446 | void deinit_Window(iWindow *d) { | 463 | void 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 | ||
456 | void init_MainWindow(iMainWindow *d, iRect rect) { | 478 | void 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 | ||
541 | static 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 | |||
552 | void deinit_MainWindow(iMainWindow *d) { | 566 | void 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) { | |||
592 | static void invalidate_MainWindow_(iMainWindow *d, iBool forced) { | 608 | static 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 | ||
609 | static iBool isNormalPlacement_MainWindow_(const iMainWindow *d) { | 625 | static 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) { | |||
655 | static void notifyMetricsChange_Window_(const iWindow *d) { | 671 | static 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 | ||
695 | static 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 | |||
679 | static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) { | 730 | static 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 | ||
834 | iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) { | 886 | iBool 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 | ||
1109 | void 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 | |||
1047 | void draw_MainWindow(iMainWindow *d) { | 1136 | void 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 | ||
1183 | void setFreezeDraw_Window(iWindow *d, iBool freezeDraw) { | 1274 | void 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 | ||
1325 | void 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 | |||
1234 | iMainWindow *get_MainWindow(void) { | 1340 | iMainWindow *get_MainWindow(void) { |
1235 | return as_MainWindow(theWindow_); | 1341 | return theMainWindow_; |
1236 | } | 1342 | } |
1237 | 1343 | ||
1238 | iBool isOpenGLRenderer_Window(void) { | 1344 | iBool 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 | |||
1584 | iWindow *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 | } | ||