summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-02-12 20:52:30 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-02-12 20:52:30 +0200
commit51a74dc9c66b76dff795a9da3874602f3f0f0285 (patch)
treef7e5aed92bca8017027cf42bd38fdd298254b2e0 /src
parent73c8166d5c8b397c166ee9ece0173f2b278b6ddb (diff)
Windows: Custom frame behavior
A borderless SDL window gets no standard window behavior, so this commit implements support for some of them. There is special handling for the window frame and various snapping modes. Not quite finished yet... It might make sense to research if a custom window class could work with this app (with SDL); could be less work.
Diffstat (limited to 'src')
-rw-r--r--src/app.c25
-rw-r--r--src/ui/window.c221
-rw-r--r--src/ui/window.h34
-rw-r--r--src/win32.c53
-rw-r--r--src/win32.h6
5 files changed, 283 insertions, 56 deletions
diff --git a/src/app.c b/src/app.c
index b50321b2..64ddd70e 100644
--- a/src/app.c
+++ b/src/app.c
@@ -160,12 +160,12 @@ static iString *serializePrefs_App_(const iApp *d) {
160 const iSidebarWidget *sidebar2 = findWidget_App("sidebar2"); 160 const iSidebarWidget *sidebar2 = findWidget_App("sidebar2");
161 appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize); 161 appendFormat_String(str, "window.retain arg:%d\n", d->prefs.retainWindowSize);
162 if (d->prefs.retainWindowSize) { 162 if (d->prefs.retainWindowSize) {
163 const iBool isMaximized = (SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_MAXIMIZED) != 0; 163 const iBool isMaximized = snap_Window(d->window) == maximized_WindowSnap;
164 int w, h, x, y; 164 int w, h, x, y;
165 x = d->window->lastRect.pos.x; 165 x = d->window->place.normalRect.pos.x;
166 y = d->window->lastRect.pos.y; 166 y = d->window->place.normalRect.pos.y;
167 w = d->window->lastRect.size.x; 167 w = d->window->place.normalRect.size.x;
168 h = d->window->lastRect.size.y; 168 h = d->window->place.normalRect.size.y;
169 appendFormat_String(str, "window.setrect width:%d height:%d coord:%d %d\n", w, h, x, y); 169 appendFormat_String(str, "window.setrect width:%d height:%d coord:%d %d\n", w, h, x, y);
170 appendFormat_String(str, "sidebar.width arg:%d\n", width_SidebarWidget(sidebar)); 170 appendFormat_String(str, "sidebar.width arg:%d\n", width_SidebarWidget(sidebar));
171 appendFormat_String(str, "sidebar2.width arg:%d\n", width_SidebarWidget(sidebar2)); 171 appendFormat_String(str, "sidebar2.width arg:%d\n", width_SidebarWidget(sidebar2));
@@ -1113,22 +1113,17 @@ iBool handleCommand_App(const char *cmd) {
1113 } 1113 }
1114 else if (equal_Command(cmd, "window.maximize")) { 1114 else if (equal_Command(cmd, "window.maximize")) {
1115 if (!argLabel_Command(cmd, "toggle")) { 1115 if (!argLabel_Command(cmd, "toggle")) {
1116 SDL_MaximizeWindow(d->window->win); 1116 setSnap_Window(d->window, maximized_WindowSnap);
1117 } 1117 }
1118 else { 1118 else {
1119 if (SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_MAXIMIZED) { 1119 setSnap_Window(d->window, snap_Window(d->window) == maximized_WindowSnap ? 0 :
1120 SDL_RestoreWindow(d->window->win); 1120 maximized_WindowSnap);
1121 }
1122 else {
1123 SDL_MaximizeWindow(d->window->win);
1124 }
1125 } 1121 }
1126 return iTrue; 1122 return iTrue;
1127 } 1123 }
1128 else if (equal_Command(cmd, "window.fullscreen")) { 1124 else if (equal_Command(cmd, "window.fullscreen")) {
1129 const iBool wasFull = 1125 const iBool wasFull = snap_Window(d->window) == fullscreen_WindowSnap;
1130 (SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; 1126 setSnap_Window(d->window, wasFull ? 0 : fullscreen_WindowSnap);
1131 SDL_SetWindowFullscreen(d->window->win, wasFull ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
1132 postCommandf_App("window.fullscreen.changed arg:%d", !wasFull); 1127 postCommandf_App("window.fullscreen.changed arg:%d", !wasFull);
1133 return iTrue; 1128 return iTrue;
1134 } 1129 }
diff --git a/src/ui/window.c b/src/ui/window.c
index 6356b292..37586aab 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -93,15 +93,6 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
93 setTextColor_LabelWidget(findWidget_App("winbar.title"), uiTextStrong_ColorId); 93 setTextColor_LabelWidget(findWidget_App("winbar.title"), uiTextStrong_ColorId);
94 return iFalse; 94 return iFalse;
95 } 95 }
96 else if (equal_Command(cmd, "window.fullscreen.changed")) {
97 iWidget *winBar = findWidget_App("winbar");
98 if (winBar) {
99 setFlags_Widget(winBar, hidden_WidgetFlag, arg_Command(cmd));
100 arrange_Widget(get_Window()->root);
101 postRefresh_App();
102 }
103 return iFalse;
104 }
105 else if (equal_Command(cmd, "window.minimize")) { 96 else if (equal_Command(cmd, "window.minimize")) {
106 SDL_MinimizeWindow(get_Window()->win); 97 SDL_MinimizeWindow(get_Window()->win);
107 return iTrue; 98 return iTrue;
@@ -562,7 +553,7 @@ static void setupUserInterface_Window(iWindow *d) {
562 setFont_LabelWidget(appButton, uiContentBold_FontId); 553 setFont_LabelWidget(appButton, uiContentBold_FontId);
563 setId_Widget(addChildFlags_Widget(winBar, 554 setId_Widget(addChildFlags_Widget(winBar,
564 iClob(appTitle = new_LabelWidget("", NULL)), 555 iClob(appTitle = new_LabelWidget("", NULL)),
565 expand_WidgetFlag | alignLeft_WidgetFlag | 556 expand_WidgetFlag | /*alignLeft_WidgetFlag |*/
566 fixedHeight_WidgetFlag | frameless_WidgetFlag | 557 fixedHeight_WidgetFlag | frameless_WidgetFlag |
567 commandOnClick_WidgetFlag), 558 commandOnClick_WidgetFlag),
568 "winbar.title"); 559 "winbar.title");
@@ -570,7 +561,7 @@ static void setupUserInterface_Window(iWindow *d) {
570 iLabelWidget *appMin, *appMax, *appClose; 561 iLabelWidget *appMin, *appMax, *appClose;
571 setId_Widget(addChildFlags_Widget( 562 setId_Widget(addChildFlags_Widget(
572 winBar, 563 winBar,
573 iClob(appMin = newLargeIcon_LabelWidget("\u2014", "window.minimize")), 564 iClob(appMin = newLargeIcon_LabelWidget("\u2013", "window.minimize")),
574 frameless_WidgetFlag), 565 frameless_WidgetFlag),
575 "winbar.min"); 566 "winbar.min");
576 setSize_Widget(as_Widget(appMin), 567 setSize_Widget(as_Widget(appMin),
@@ -582,6 +573,7 @@ static void setupUserInterface_Window(iWindow *d) {
582 addChildFlags_Widget(winBar, 573 addChildFlags_Widget(winBar,
583 iClob(appClose = newLargeIcon_LabelWidget("\u2a2f", "window.close")), 574 iClob(appClose = newLargeIcon_LabelWidget("\u2a2f", "window.close")),
584 frameless_WidgetFlag); 575 frameless_WidgetFlag);
576 setFont_LabelWidget(appClose, uiContent_FontId);
585 setSize_Widget(as_Widget(appMax), as_Widget(appMin)->rect.size); 577 setSize_Widget(as_Widget(appMax), as_Widget(appMin)->rect.size);
586 setSize_Widget(as_Widget(appClose), as_Widget(appMin)->rect.size); 578 setSize_Widget(as_Widget(appClose), as_Widget(appMin)->rect.size);
587 addChild_Widget(div, iClob(winBar)); 579 addChild_Widget(div, iClob(winBar));
@@ -744,8 +736,8 @@ static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) {
744 const iInt2 oldSize = *size; 736 const iInt2 oldSize = *size;
745 SDL_GetRendererOutputSize(d->render, &size->x, &size->y); 737 SDL_GetRendererOutputSize(d->render, &size->x, &size->y);
746 if (notifyAlways || !isEqual_I2(oldSize, *size)) { 738 if (notifyAlways || !isEqual_I2(oldSize, *size)) {
747 const iBool isHoriz = (d->lastNotifiedSize.x != size->x); 739 const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x);
748 const iBool isVert = (d->lastNotifiedSize.y != size->y); 740 const iBool isVert = (d->place.lastNotifiedSize.y != size->y);
749 arrange_Widget(d->root); 741 arrange_Widget(d->root);
750 postCommandf_App("window.resized width:%d height:%d horiz:%d vert:%d", 742 postCommandf_App("window.resized width:%d height:%d horiz:%d vert:%d",
751 size->x, 743 size->x,
@@ -753,7 +745,7 @@ static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) {
753 isHoriz, 745 isHoriz,
754 isVert); 746 isVert);
755 postRefresh_App(); 747 postRefresh_App();
756 d->lastNotifiedSize = *size; 748 d->place.lastNotifiedSize = *size;
757 } 749 }
758} 750}
759 751
@@ -793,6 +785,9 @@ static void drawBlank_Window_(iWindow *d) {
793static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) { 785static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) {
794 iWindow *d = data; 786 iWindow *d = data;
795 iAssert(d->win == win); 787 iAssert(d->win == win);
788 if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_MOUSE_CAPTURE) {
789 return SDL_HITTEST_NORMAL;
790 }
796 int w, h; 791 int w, h;
797 SDL_GetWindowSize(win, &w, &h); 792 SDL_GetWindowSize(win, &w, &h);
798 /* TODO: Check if inside the caption label widget. */ 793 /* TODO: Check if inside the caption label widget. */
@@ -802,15 +797,16 @@ static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos,
802 const iBool isBottom = pos->y >= h - gap_UI; 797 const iBool isBottom = pos->y >= h - gap_UI;
803 const int captionHeight = lineHeight_Text(uiContent_FontId) + gap_UI * 2; 798 const int captionHeight = lineHeight_Text(uiContent_FontId) + gap_UI * 2;
804 const int rightEdge = left_Rect(bounds_Widget(findChild_Widget(d->root, "winbar.min"))); 799 const int rightEdge = left_Rect(bounds_Widget(findChild_Widget(d->root, "winbar.min")));
800 d->place.lastHit = SDL_HITTEST_NORMAL;
805 if (isLeft) { 801 if (isLeft) {
806 return pos->y < captionHeight ? SDL_HITTEST_RESIZE_TOPLEFT 802 return pos->y < captionHeight ? SDL_HITTEST_RESIZE_TOPLEFT
807 : pos->y > h - captionHeight ? SDL_HITTEST_RESIZE_BOTTOMLEFT 803 : pos->y > h - captionHeight ? SDL_HITTEST_RESIZE_BOTTOMLEFT
808 : SDL_HITTEST_RESIZE_LEFT; 804 : (d->place.lastHit = SDL_HITTEST_RESIZE_LEFT);
809 } 805 }
810 if (isRight) { 806 if (isRight) {
811 return pos->y < captionHeight ? SDL_HITTEST_RESIZE_TOPRIGHT 807 return pos->y < captionHeight ? SDL_HITTEST_RESIZE_TOPRIGHT
812 : pos->y > h - captionHeight ? SDL_HITTEST_RESIZE_BOTTOMRIGHT 808 : pos->y > h - captionHeight ? SDL_HITTEST_RESIZE_BOTTOMRIGHT
813 : SDL_HITTEST_RESIZE_RIGHT; 809 : (d->place.lastHit = SDL_HITTEST_RESIZE_RIGHT);
814 } 810 }
815 if (isTop) { 811 if (isTop) {
816 return pos->x < captionHeight ? SDL_HITTEST_RESIZE_TOPLEFT 812 return pos->x < captionHeight ? SDL_HITTEST_RESIZE_TOPLEFT
@@ -827,6 +823,10 @@ static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos,
827 } 823 }
828 return SDL_HITTEST_NORMAL; 824 return SDL_HITTEST_NORMAL;
829} 825}
826
827SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos) {
828 return hitTest_Window_(d->win, &(SDL_Point){ pos.x, pos.y }, iConstCast(void *, d));
829}
830#endif 830#endif
831 831
832iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) { 832iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) {
@@ -841,7 +841,7 @@ iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) {
841 } 841 }
842#if defined (LAGRANGE_CUSTOM_FRAME) 842#if defined (LAGRANGE_CUSTOM_FRAME)
843 /* Register a handler for window hit testing (drag, resize). */ 843 /* Register a handler for window hit testing (drag, resize). */
844 SDL_SetWindowHitTest(d->win, hitTest_Window_, d); 844 SDL_SetWindowHitTest(d->win, hitTest_Window_, d);
845 SDL_SetWindowResizable(d->win, SDL_TRUE); 845 SDL_SetWindowResizable(d->win, SDL_TRUE);
846#endif 846#endif
847 return iTrue; 847 return iTrue;
@@ -850,12 +850,13 @@ iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) {
850void init_Window(iWindow *d, iRect rect) { 850void init_Window(iWindow *d, iRect rect) {
851 theWindow_ = d; 851 theWindow_ = d;
852 iZap(d->cursors); 852 iZap(d->cursors);
853 d->initialPos = rect.pos; 853 d->place.initialPos = rect.pos;
854 d->lastRect = rect; 854 d->place.normalRect = rect;
855 d->lastNotifiedSize = zero_I2(); 855 d->place.lastNotifiedSize = zero_I2();
856 d->pendingCursor = NULL; 856 d->pendingCursor = NULL;
857 d->isDrawFrozen = iTrue; 857 d->isDrawFrozen = iTrue;
858 d->isExposed = iFalse; 858 d->isExposed = iFalse;
859 d->isMinimized = iFalse;
859 d->isMouseInside = iTrue; 860 d->isMouseInside = iTrue;
860 d->focusGainedAt = 0; 861 d->focusGainedAt = 0;
861 uint32_t flags = 0; 862 uint32_t flags = 0;
@@ -936,6 +937,9 @@ void init_Window(iWindow *d, iRect rect) {
936 setupUserInterface_Window(d); 937 setupUserInterface_Window(d);
937 postCommand_App("bindings.changed"); /* update from bindings */ 938 postCommand_App("bindings.changed"); /* update from bindings */
938 updateRootSize_Window_(d, iFalse); 939 updateRootSize_Window_(d, iFalse);
940#if defined (LAGRANGE_CUSTOM_FRAME)
941 SDL_EventState(SDL_SYSWMEVENT, SDL_TRUE);
942#endif
939} 943}
940 944
941void deinit_Window(iWindow *d) { 945void deinit_Window(iWindow *d) {
@@ -958,17 +962,8 @@ SDL_Renderer *renderer_Window(const iWindow *d) {
958 return d->render; 962 return d->render;
959} 963}
960 964
961static iBool isMaximized_Window_(const iWindow *d) {
962#if !defined (iPlatformApple)
963 return (SDL_GetWindowFlags(d->win) & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED)) != 0;
964#else
965 iUnused(d);
966 return iFalse; /* There is fullscreen mode but that is not handled at the moment. */
967#endif
968}
969
970iBool isFullscreen_Window(const iWindow *d) { 965iBool isFullscreen_Window(const iWindow *d) {
971 return (SDL_GetWindowFlags(d->win) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; 966 return snap_Window(d) == fullscreen_WindowSnap;
972} 967}
973 968
974static void invalidate_Window_(iWindow *d) { 969static void invalidate_Window_(iWindow *d) {
@@ -976,6 +971,31 @@ static void invalidate_Window_(iWindow *d) {
976 postCommand_App("theme.changed"); /* forces UI invalidation */ 971 postCommand_App("theme.changed"); /* forces UI invalidation */
977} 972}
978 973
974static iBool isNormalPlacement_Window_(const iWindow *d) {
975 if (snap_Window(d) || d->isDrawFrozen) return iFalse;
976 return !(SDL_GetWindowFlags(d->win) & SDL_WINDOW_MINIMIZED);
977}
978
979static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) {
980#if defined (LAGRANGE_CUSTOM_FRAME)
981 const int snap = snap_Window(d);
982 if (snap == yMaximized_WindowSnap &&
983 (!newPos || (d->place.lastHit == SDL_HITTEST_RESIZE_LEFT ||
984 d->place.lastHit == SDL_HITTEST_RESIZE_RIGHT))) {
985 return iFalse;
986 }
987 if (snap && snap != fullscreen_WindowSnap) {
988 if (snap_Window(d) == yMaximized_WindowSnap && newPos) {
989 d->place.normalRect.pos = *newPos;
990 }
991 printf("unsnap\n"); fflush(stdout);
992 setSnap_Window(d, none_WindowSnap);
993 return iTrue;
994 }
995#endif
996 return iFalse;
997}
998
979static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { 999static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
980 switch (ev->event) { 1000 switch (ev->event) {
981 case SDL_WINDOWEVENT_EXPOSED: 1001 case SDL_WINDOWEVENT_EXPOSED:
@@ -989,33 +1009,62 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
989 is missing contents until other events trigger a refresh. */ 1009 is missing contents until other events trigger a refresh. */
990 postRefresh_App(); 1010 postRefresh_App();
991#if defined (LAGRANGE_ENABLE_WINDOWPOS_FIX) 1011#if defined (LAGRANGE_ENABLE_WINDOWPOS_FIX)
992 if (d->initialPos.x >= 0) { 1012 if (d->place.initialPos.x >= 0) {
993 int bx, by; 1013 int bx, by;
994 SDL_GetWindowBordersSize(d->win, &by, &bx, NULL, NULL); 1014 SDL_GetWindowBordersSize(d->win, &by, &bx, NULL, NULL);
995 SDL_SetWindowPosition(d->win, d->initialPos.x + bx, d->initialPos.y + by); 1015 SDL_SetWindowPosition(d->win, d->place.initialPos.x + bx, d->place.initialPos.y + by);
996 d->initialPos = init1_I2(-1); 1016 d->place.initialPos = init1_I2(-1);
997 } 1017 }
998#endif 1018#endif
999 return iFalse; 1019 return iFalse;
1000 case SDL_WINDOWEVENT_MOVED: { 1020 case SDL_WINDOWEVENT_MOVED: {
1001 if (!isMaximized_Window_(d) && !isFullscreen_Window(d) && !d->isDrawFrozen) { 1021 if (d->isMinimized) {
1002 d->lastRect.pos = init_I2(ev->data1, ev->data2); 1022 return iFalse;
1023 }
1024 const iInt2 newPos = init_I2(ev->data1, ev->data2);
1025 if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */
1026 /* Maybe minimized? Seems like a Windows constant of some kind. */
1027 d->isMinimized = iTrue;
1028 return iFalse;
1029 }
1030 //printf("MOVED: %d, %d\n", ev->data1, ev->data2); fflush(stdout);
1031 if (unsnap_Window_(d, &newPos)) {
1032 return iTrue;
1033 }
1034 if (isNormalPlacement_Window_(d)) {
1035 d->place.normalRect.pos = newPos;
1036 //printf("normal rect set (move)\n"); fflush(stdout);
1003 iInt2 border = zero_I2(); 1037 iInt2 border = zero_I2();
1004#if !defined (iPlatformApple) 1038#if !defined (iPlatformApple)
1005 SDL_GetWindowBordersSize(d->win, &border.y, &border.x, NULL, NULL); 1039 SDL_GetWindowBordersSize(d->win, &border.y, &border.x, NULL, NULL);
1006#endif 1040#endif
1007 d->lastRect.pos = max_I2(zero_I2(), sub_I2(d->lastRect.pos, border)); 1041 d->place.normalRect.pos = max_I2(zero_I2(), sub_I2(d->place.normalRect.pos, border));
1008 } 1042 }
1009 return iTrue; 1043 return iTrue;
1010 } 1044 }
1011 case SDL_WINDOWEVENT_RESIZED: 1045 case SDL_WINDOWEVENT_RESIZED:
1012 if (!isMaximized_Window_(d) && !isFullscreen_Window(d) && !d->isDrawFrozen) { 1046 if (d->isMinimized) {
1013 d->lastRect.size = init_I2(ev->data1, ev->data2); 1047 updateRootSize_Window_(d, iTrue);
1048 return iTrue;
1049 }
1050 if (unsnap_Window_(d, NULL)) {
1051 return iTrue;
1052 }
1053 if (isNormalPlacement_Window_(d)) {
1054 d->place.normalRect.size = init_I2(ev->data1, ev->data2);
1055 //printf("normal rect set (resize)\n"); fflush(stdout);
1014 } 1056 }
1015 updateRootSize_Window_(d, iTrue /* we were already redrawing during the resize */); 1057 updateRootSize_Window_(d, iTrue /* we were already redrawing during the resize */);
1016 return iTrue; 1058 return iTrue;
1017 case SDL_WINDOWEVENT_RESTORED: 1059 case SDL_WINDOWEVENT_RESTORED:
1060 //updateRootSize_Window_(d, iTrue);
1018 invalidate_Window_(d); 1061 invalidate_Window_(d);
1062 d->isMinimized = iFalse;
1063 //printf("restored %d\n", snap_Window(d)); fflush(stdout);
1064 return iTrue;
1065 case SDL_WINDOWEVENT_MINIMIZED:
1066 d->isMinimized = iTrue;
1067 //printf("minimized\n"); fflush(stdout);
1019 return iTrue; 1068 return iTrue;
1020 case SDL_WINDOWEVENT_LEAVE: 1069 case SDL_WINDOWEVENT_LEAVE:
1021 unhover_Widget(); 1070 unhover_Widget();
@@ -1051,6 +1100,16 @@ static void applyCursor_Window_(iWindow *d) {
1051 1100
1052iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { 1101iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
1053 switch (ev->type) { 1102 switch (ev->type) {
1103#if defined (LAGRANGE_CUSTOM_FRAME)
1104 case SDL_SYSWMEVENT: {
1105 /* We observe native Win32 messages for better user interaction with the
1106 window frame. Mouse clicks especially will not generate normal SDL
1107 events if they happen on the custom hit-tested regions. These events
1108 are processed only there; the UI widgets do not get involved. */
1109 processNativeEvent_Win32(ev->syswm.msg, d);
1110 break;
1111 }
1112#endif
1054 case SDL_WINDOWEVENT: { 1113 case SDL_WINDOWEVENT: {
1055 return handleWindowEvent_Window_(d, &ev->window); 1114 return handleWindowEvent_Window_(d, &ev->window);
1056 } 1115 }
@@ -1232,3 +1291,89 @@ uint32_t frameTime_Window(const iWindow *d) {
1232iWindow *get_Window(void) { 1291iWindow *get_Window(void) {
1233 return theWindow_; 1292 return theWindow_;
1234} 1293}
1294
1295#if !defined (LAGRANGE_CUSTOM_FRAME)
1296void setSnap_Window(iWindow *d, int snapMode) {
1297 if (snapMode == maximized_WindowSnap) {
1298 SDL_MaximizeWindow(d->win);
1299 }
1300 else if (snapMode == fullscreen_WindowSnap) {
1301 SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP);
1302 }
1303 else {
1304 SDL_RestoreWindow(d->win);
1305 }
1306}
1307
1308int snap_Window(const iWindow *d) {
1309 const int flags = SDL_GetWindowFlags(d->win);
1310 if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
1311 return fullscreen_WindowSnap;
1312 }
1313 else if (flags & SDL_WINDOW_MAXIMIZED) {
1314 return maximized_WindowSnap;
1315 }
1316 return none_WindowSnap;
1317}
1318#endif
1319
1320#if defined (LAGRANGE_CUSTOM_FRAME)
1321void setSnap_Window(iWindow *d, int snapMode) {
1322 if (d->place.snap == snapMode) {
1323 return;
1324 }
1325 iRect newRect = zero_Rect();
1326 SDL_Rect usable;
1327 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable);
1328 if (d->place.snap == fullscreen_WindowSnap) {
1329 SDL_SetWindowFullscreen(d->win, 0);
1330 }
1331 d->place.snap = snapMode;
1332 switch (snapMode & mask_WindowSnap) {
1333 case none_WindowSnap:
1334 newRect = d->place.normalRect;
1335 break;
1336 case left_WindowSnap:
1337 newRect = init_Rect(usable.x, usable.y, usable.w / 2, usable.h);
1338 break;
1339 case right_WindowSnap:
1340 newRect = init_Rect(usable.x, usable.y + usable.w / 2, usable.w / 2, usable.h);
1341 break;
1342 case maximized_WindowSnap:
1343 newRect = init_Rect(usable.x, usable.y, usable.w, usable.h);
1344 break;
1345 case yMaximized_WindowSnap:
1346 newRect.pos.y = 0;
1347 newRect.size.y = usable.h;
1348 SDL_GetWindowPosition(d->win, &newRect.pos.x, NULL);
1349 SDL_GetWindowSize(d->win, &newRect.size.x, NULL);
1350 break;
1351 case fullscreen_WindowSnap:
1352 SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP);
1353 break;
1354 }
1355 if (snapMode & (topBit_WindowSnap | bottomBit_WindowSnap)) {
1356 newRect.size.y /= 2;
1357 }
1358 if (snapMode & bottomBit_WindowSnap) {
1359 newRect.pos.y += newRect.size.y;
1360 }
1361 /* Show and hide the title bar. */
1362 iWidget *winBar = findWidget_App("winbar");
1363 const iBool wasVisible = isVisible_Widget(winBar);
1364 setFlags_Widget(winBar, hidden_WidgetFlag, snapMode == fullscreen_WindowSnap);
1365 if (newRect.size.x) {
1366 SDL_SetWindowPosition(d->win, newRect.pos.x, newRect.pos.y);
1367 SDL_SetWindowSize(d->win, newRect.size.x, newRect.size.y);
1368 postCommand_App("window.resized");
1369 }
1370 if (wasVisible != isVisible_Widget(winBar)) {
1371 arrange_Widget(d->root);
1372 postRefresh_App();
1373 }
1374}
1375
1376int snap_Window(const iWindow *d) {
1377 return d->place.snap;
1378}
1379#endif
diff --git a/src/ui/window.h b/src/ui/window.h
index 5bde9065..ddb03847 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -32,13 +32,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32iDeclareType(Window) 32iDeclareType(Window)
33iDeclareTypeConstructionArgs(Window, iRect rect) 33iDeclareTypeConstructionArgs(Window, iRect rect)
34 34
35enum iWindowSnap {
36 none_WindowSnap = 0,
37 left_WindowSnap = 1,
38 right_WindowSnap = 2,
39 maximized_WindowSnap = 3,
40 yMaximized_WindowSnap = 4,
41 fullscreen_WindowSnap = 5,
42 mask_WindowSnap = 0xff,
43 topBit_WindowSnap = iBit(9),
44 bottomBit_WindowSnap = iBit(10),
45};
46
47iDeclareType(WindowPlacement)
48
49/* Tracking of window placement. */
50struct Impl_WindowPlacement {
51 iInt2 initialPos;
52 iRect normalRect; /* updated when window is moved/resized */
53 iInt2 lastNotifiedSize; /* keep track of horizontal/vertical notifications */
54 int snap; /* LAGRANGE_CUSTOM_FRAME */
55 int lastHit;
56};
57
35struct Impl_Window { 58struct Impl_Window {
36 SDL_Window * win; 59 SDL_Window * win;
37 iInt2 initialPos; 60 iWindowPlacement place;
38 iRect lastRect; /* updated when window is moved/resized */
39 iInt2 lastNotifiedSize; /* keep track of horizontal/vertical notifications */
40 iBool isDrawFrozen; /* avoids premature draws while restoring window state */ 61 iBool isDrawFrozen; /* avoids premature draws while restoring window state */
41 iBool isExposed; 62 iBool isExposed;
63 iBool isMinimized;
42 iBool isMouseInside; 64 iBool isMouseInside;
43 uint32_t focusGainedAt; 65 uint32_t focusGainedAt;
44 SDL_Renderer *render; 66 SDL_Renderer *render;
@@ -60,6 +82,7 @@ void setTitle_Window (iWindow *, const iString *title);
60void setUiScale_Window (iWindow *, float uiScale); 82void setUiScale_Window (iWindow *, float uiScale);
61void setFreezeDraw_Window (iWindow *, iBool freezeDraw); 83void setFreezeDraw_Window (iWindow *, iBool freezeDraw);
62void setCursor_Window (iWindow *, int cursor); 84void setCursor_Window (iWindow *, int cursor);
85void setSnap_Window (iWindow *, int snapMode);
63 86
64uint32_t id_Window (const iWindow *); 87uint32_t id_Window (const iWindow *);
65iInt2 rootSize_Window (const iWindow *); 88iInt2 rootSize_Window (const iWindow *);
@@ -68,6 +91,11 @@ iInt2 coord_Window (const iWindow *, int x, int y);
68iInt2 mouseCoord_Window (const iWindow *); 91iInt2 mouseCoord_Window (const iWindow *);
69uint32_t frameTime_Window (const iWindow *); 92uint32_t frameTime_Window (const iWindow *);
70SDL_Renderer *renderer_Window (const iWindow *); 93SDL_Renderer *renderer_Window (const iWindow *);
94int snap_Window (const iWindow *);
71iBool isFullscreen_Window (const iWindow *); 95iBool isFullscreen_Window (const iWindow *);
72 96
73iWindow * get_Window (void); 97iWindow * get_Window (void);
98
99#if defined (LAGRANGE_CUSTOM_FRAME)
100SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos);
101#endif
diff --git a/src/win32.c b/src/win32.c
index 5eb2a0d5..585fdaaf 100644
--- a/src/win32.c
+++ b/src/win32.c
@@ -21,10 +21,13 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23#include "win32.h" 23#include "win32.h"
24#include "ui/window.h"
25#include "app.h"
24#include <SDL_syswm.h> 26#include <SDL_syswm.h>
25 27
26#define WIN32_LEAN_AND_MEAN 28#define WIN32_LEAN_AND_MEAN
27#include <Windows.h> 29#include <Windows.h>
30#include <windowsx.h>
28#include <d2d1.h> 31#include <d2d1.h>
29 32
30void setDPIAware_Win32(void) { 33void setDPIAware_Win32(void) {
@@ -59,3 +62,53 @@ void useExecutableIconResource_SDLWindow(SDL_Window *win) {
59 } 62 }
60 } 63 }
61} 64}
65
66#if 0
67void setup_SDLWindow(SDL_Window *win) {
68 SDL_SysWMinfo wmInfo;
69 SDL_VERSION(&wmInfo.version);
70 if (SDL_GetWindowWMInfo(win, &wmInfo)) {
71 HWND hwnd = wmInfo.info.win.window;
72// printf("configuring window\n");
73// SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
74 }
75}
76#endif
77
78#if defined (LAGRANGE_CUSTOM_FRAME)
79void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window) {
80 HWND hwnd = msg->msg.win.hwnd;
81// printf("[syswm] %x\n", msg->msg.win.msg); fflush(stdout);
82 switch (msg->msg.win.msg) {
83 case WM_NCLBUTTONDBLCLK: {
84 POINT point = { GET_X_LPARAM(msg->msg.win.lParam), GET_Y_LPARAM(msg->msg.win.lParam) };
85 ScreenToClient(hwnd, &point);
86 iInt2 pos = init_I2(point.x, point.y);
87 switch (hitTest_Window(window, pos)) {
88 case SDL_HITTEST_DRAGGABLE:
89 postCommand_App("window.maximize toggle:1");
90 break;
91 case SDL_HITTEST_RESIZE_TOP:
92 case SDL_HITTEST_RESIZE_BOTTOM: {
93 setSnap_Window(window, yMaximized_WindowSnap);
94 break;
95 }
96 }
97 //fflush(stdout);
98 break;
99 }
100#if 0
101 /* SDL does not use WS_SYSMENU on the window, so we can't display the system menu.
102 However, the only useful function in the menu would be moving-via-keyboard,
103 but that doesn't work with a custom frame. We could show a custom system menu? */
104 case WM_NCRBUTTONUP: {
105 POINT point = { GET_X_LPARAM(msg->msg.win.lParam), GET_Y_LPARAM(msg->msg.win.lParam) };
106 HMENU menu = GetSystemMenu(hwnd, FALSE);
107 printf("menu at %d,%d menu:%p\n", point.x, point.y, menu); fflush(stdout);
108 TrackPopupMenu(menu, TPM_RIGHTBUTTON, point.x, point.y, 0, hwnd, NULL);
109 break;
110 }
111#endif
112 }
113}
114#endif /* defined (LAGRANGE_CUSTOM_FRAME) */
diff --git a/src/win32.h b/src/win32.h
index b5bc0b45..b988c302 100644
--- a/src/win32.h
+++ b/src/win32.h
@@ -21,8 +21,14 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23#pragma once 23#pragma once
24
25#include <the_Foundation/defs.h>
24#include <SDL_video.h> 26#include <SDL_video.h>
27#include <SDL_syswm.h>
28
29iDeclareType(Window)
25 30
26void setDPIAware_Win32(void); 31void setDPIAware_Win32(void);
27float desktopDPI_Win32(void); 32float desktopDPI_Win32(void);
28void useExecutableIconResource_SDLWindow(SDL_Window *win); 33void useExecutableIconResource_SDLWindow(SDL_Window *win);
34void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window);