summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);