summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-02-13 09:47:41 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-02-13 09:47:41 +0200
commit5d912fc85519e3d0f387a7183711bb5baf521e8a (patch)
tree1e460a5d735c9b28716ef16a87d174049ede2572
parent51a74dc9c66b76dff795a9da3874602f3f0f0285 (diff)
Windows: Improved custom frame behavior
Windows-key shortcuts and cursor edge snapping. Still missing: window shadow and saving the snap mode.
-rw-r--r--src/ui/window.c95
-rw-r--r--src/ui/window.h1
-rw-r--r--src/win32.c91
-rw-r--r--src/win32.h4
4 files changed, 173 insertions, 18 deletions
diff --git a/src/ui/window.c b/src/ui/window.c
index 37586aab..65386e14 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -93,6 +93,10 @@ 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.restore")) {
97 setSnap_Window(get_Window(), none_WindowSnap);
98 return iTrue;
99 }
96 else if (equal_Command(cmd, "window.minimize")) { 100 else if (equal_Command(cmd, "window.minimize")) {
97 SDL_MinimizeWindow(get_Window()->win); 101 SDL_MinimizeWindow(get_Window()->win);
98 return iTrue; 102 return iTrue;
@@ -553,9 +557,8 @@ static void setupUserInterface_Window(iWindow *d) {
553 setFont_LabelWidget(appButton, uiContentBold_FontId); 557 setFont_LabelWidget(appButton, uiContentBold_FontId);
554 setId_Widget(addChildFlags_Widget(winBar, 558 setId_Widget(addChildFlags_Widget(winBar,
555 iClob(appTitle = new_LabelWidget("", NULL)), 559 iClob(appTitle = new_LabelWidget("", NULL)),
556 expand_WidgetFlag | /*alignLeft_WidgetFlag |*/ 560 expand_WidgetFlag | fixedHeight_WidgetFlag |
557 fixedHeight_WidgetFlag | frameless_WidgetFlag | 561 frameless_WidgetFlag | commandOnClick_WidgetFlag),
558 commandOnClick_WidgetFlag),
559 "winbar.title"); 562 "winbar.title");
560 setTextColor_LabelWidget(appTitle, uiTextStrong_ColorId); 563 setTextColor_LabelWidget(appTitle, uiTextStrong_ColorId);
561 iLabelWidget *appMin, *appMax, *appClose; 564 iLabelWidget *appMin, *appMax, *appClose;
@@ -570,6 +573,7 @@ static void setupUserInterface_Window(iWindow *d) {
570 winBar, 573 winBar,
571 iClob(appMax = newLargeIcon_LabelWidget("\u25a1", "window.maximize toggle:1")), 574 iClob(appMax = newLargeIcon_LabelWidget("\u25a1", "window.maximize toggle:1")),
572 frameless_WidgetFlag); 575 frameless_WidgetFlag);
576 setId_Widget(as_Widget(appMax), "winbar.max");
573 addChildFlags_Widget(winBar, 577 addChildFlags_Widget(winBar,
574 iClob(appClose = newLargeIcon_LabelWidget("\u2a2f", "window.close")), 578 iClob(appClose = newLargeIcon_LabelWidget("\u2a2f", "window.close")),
575 frameless_WidgetFlag); 579 frameless_WidgetFlag);
@@ -785,7 +789,7 @@ static void drawBlank_Window_(iWindow *d) {
785static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) { 789static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) {
786 iWindow *d = data; 790 iWindow *d = data;
787 iAssert(d->win == win); 791 iAssert(d->win == win);
788 if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_MOUSE_CAPTURE) { 792 if (SDL_GetWindowFlags(d->win) & (SDL_WINDOW_MOUSE_CAPTURE | SDL_WINDOW_FULLSCREEN_DESKTOP)) {
789 return SDL_HITTEST_NORMAL; 793 return SDL_HITTEST_NORMAL;
790 } 794 }
791 int w, h; 795 int w, h;
@@ -979,10 +983,21 @@ static iBool isNormalPlacement_Window_(const iWindow *d) {
979static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) { 983static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) {
980#if defined (LAGRANGE_CUSTOM_FRAME) 984#if defined (LAGRANGE_CUSTOM_FRAME)
981 const int snap = snap_Window(d); 985 const int snap = snap_Window(d);
982 if (snap == yMaximized_WindowSnap && 986 if (snap == yMaximized_WindowSnap || snap == left_WindowSnap || snap == right_WindowSnap) {
983 (!newPos || (d->place.lastHit == SDL_HITTEST_RESIZE_LEFT || 987 if (!newPos || (d->place.lastHit == SDL_HITTEST_RESIZE_LEFT ||
984 d->place.lastHit == SDL_HITTEST_RESIZE_RIGHT))) { 988 d->place.lastHit == SDL_HITTEST_RESIZE_RIGHT)) {
985 return iFalse; 989 return iFalse;
990 }
991 if (newPos) {
992 SDL_Rect usable;
993 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable);
994 /* Snap to top. */
995 if (snap == yMaximized_WindowSnap &&
996 iAbs(newPos->y - usable.y) < lineHeight_Text(uiContent_FontId) * 2) {
997 setSnap_Window(d, redo_WindowSnap | yMaximized_WindowSnap);
998 return iFalse;
999 }
1000 }
986 } 1001 }
987 if (snap && snap != fullscreen_WindowSnap) { 1002 if (snap && snap != fullscreen_WindowSnap) {
988 if (snap_Window(d) == yMaximized_WindowSnap && newPos) { 1003 if (snap_Window(d) == yMaximized_WindowSnap && newPos) {
@@ -1027,6 +1042,33 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
1027 d->isMinimized = iTrue; 1042 d->isMinimized = iTrue;
1028 return iFalse; 1043 return iFalse;
1029 } 1044 }
1045#if defined (LAGRANGE_CUSTOM_FRAME)
1046 /* Set the snap position depending on where the mouse cursor is. */ {
1047 SDL_Rect usable;
1048 iInt2 mouse = cursor_Win32(); /* SDL is unaware of the current cursor pos */
1049 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable);
1050 const iBool isTop = iAbs(mouse.y - usable.y) < gap_UI * 20;
1051 const iBool isBottom = iAbs(usable.y + usable.h - mouse.y) < gap_UI * 20;
1052 if (iAbs(mouse.x - usable.x) < gap_UI) {
1053 setSnap_Window(d,
1054 redo_WindowSnap | left_WindowSnap |
1055 (isTop ? topBit_WindowSnap : 0) |
1056 (isBottom ? bottomBit_WindowSnap : 0));
1057 return iTrue;
1058 }
1059 if (iAbs(mouse.x - usable.x - usable.w) < gap_UI) {
1060 setSnap_Window(d,
1061 redo_WindowSnap | right_WindowSnap |
1062 (isTop ? topBit_WindowSnap : 0) |
1063 (isBottom ? bottomBit_WindowSnap : 0));
1064 return iTrue;
1065 }
1066 if (iAbs(mouse.y - usable.y) < 2) {
1067 setSnap_Window(d, redo_WindowSnap | maximized_WindowSnap);
1068 return iTrue;
1069 }
1070 }
1071#endif
1030 //printf("MOVED: %d, %d\n", ev->data1, ev->data2); fflush(stdout); 1072 //printf("MOVED: %d, %d\n", ev->data1, ev->data2); fflush(stdout);
1031 if (unsnap_Window_(d, &newPos)) { 1073 if (unsnap_Window_(d, &newPos)) {
1032 return iTrue; 1074 return iTrue;
@@ -1138,6 +1180,21 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
1138 As a workaround, ignore these events. */ 1180 As a workaround, ignore these events. */
1139 return iTrue; /* won't go to bindings, either */ 1181 return iTrue; /* won't go to bindings, either */
1140 } 1182 }
1183#if 0 //defined (LAGRANGE_CUSTOM_FRAME)
1184 if (event.type == SDL_KEYDOWN) {
1185 if (keyMods_Sym(event.key.keysym.mod) == KMOD_PRIMARY) {
1186 printf("got! %x\n", event.key.keysym.sym); fflush(stdout);
1187 /* Emulate window snapping keys. */
1188 switch (event.key.keysym.sym) {
1189 default:
1190 break;
1191 case SDLK_LEFT:
1192 setSnap_Window(d, left_WindowSnap);
1193 return iTrue;
1194 }
1195 }
1196 }
1197#endif
1141 /* Map mouse pointer coordinate to our coordinate system. */ 1198 /* Map mouse pointer coordinate to our coordinate system. */
1142 if (event.type == SDL_MOUSEMOTION) { 1199 if (event.type == SDL_MOUSEMOTION) {
1143 setCursor_Window(d, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */ 1200 setCursor_Window(d, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */
@@ -1322,13 +1379,14 @@ void setSnap_Window(iWindow *d, int snapMode) {
1322 if (d->place.snap == snapMode) { 1379 if (d->place.snap == snapMode) {
1323 return; 1380 return;
1324 } 1381 }
1382 const int snapDist = gap_UI * 4;
1325 iRect newRect = zero_Rect(); 1383 iRect newRect = zero_Rect();
1326 SDL_Rect usable; 1384 SDL_Rect usable;
1327 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable); 1385 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable);
1328 if (d->place.snap == fullscreen_WindowSnap) { 1386 if (d->place.snap == fullscreen_WindowSnap) {
1329 SDL_SetWindowFullscreen(d->win, 0); 1387 SDL_SetWindowFullscreen(d->win, 0);
1330 } 1388 }
1331 d->place.snap = snapMode; 1389 d->place.snap = snapMode & ~redo_WindowSnap;
1332 switch (snapMode & mask_WindowSnap) { 1390 switch (snapMode & mask_WindowSnap) {
1333 case none_WindowSnap: 1391 case none_WindowSnap:
1334 newRect = d->place.normalRect; 1392 newRect = d->place.normalRect;
@@ -1337,7 +1395,8 @@ void setSnap_Window(iWindow *d, int snapMode) {
1337 newRect = init_Rect(usable.x, usable.y, usable.w / 2, usable.h); 1395 newRect = init_Rect(usable.x, usable.y, usable.w / 2, usable.h);
1338 break; 1396 break;
1339 case right_WindowSnap: 1397 case right_WindowSnap:
1340 newRect = init_Rect(usable.x, usable.y + usable.w / 2, usable.w / 2, usable.h); 1398 newRect =
1399 init_Rect(usable.x + usable.w / 2, usable.y, usable.w - usable.w / 2, usable.h);
1341 break; 1400 break;
1342 case maximized_WindowSnap: 1401 case maximized_WindowSnap:
1343 newRect = init_Rect(usable.x, usable.y, usable.w, usable.h); 1402 newRect = init_Rect(usable.x, usable.y, usable.w, usable.h);
@@ -1345,8 +1404,15 @@ void setSnap_Window(iWindow *d, int snapMode) {
1345 case yMaximized_WindowSnap: 1404 case yMaximized_WindowSnap:
1346 newRect.pos.y = 0; 1405 newRect.pos.y = 0;
1347 newRect.size.y = usable.h; 1406 newRect.size.y = usable.h;
1348 SDL_GetWindowPosition(d->win, &newRect.pos.x, NULL);
1349 SDL_GetWindowSize(d->win, &newRect.size.x, NULL); 1407 SDL_GetWindowSize(d->win, &newRect.size.x, NULL);
1408 SDL_GetWindowPosition(d->win, &newRect.pos.x, NULL);
1409 /* Snap the window to left/right edges, if close by. */
1410 if (iAbs(right_Rect(newRect) - (usable.x + usable.w)) < snapDist) {
1411 newRect.pos.x = usable.x + usable.w - width_Rect(newRect);
1412 }
1413 if (iAbs(newRect.pos.x - usable.x) < snapDist) {
1414 newRect.pos.x = usable.x;
1415 }
1350 break; 1416 break;
1351 case fullscreen_WindowSnap: 1417 case fullscreen_WindowSnap:
1352 SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP); 1418 SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP);
@@ -1358,10 +1424,13 @@ void setSnap_Window(iWindow *d, int snapMode) {
1358 if (snapMode & bottomBit_WindowSnap) { 1424 if (snapMode & bottomBit_WindowSnap) {
1359 newRect.pos.y += newRect.size.y; 1425 newRect.pos.y += newRect.size.y;
1360 } 1426 }
1361 /* Show and hide the title bar. */ 1427 /* Update window controls. */
1362 iWidget *winBar = findWidget_App("winbar"); 1428 iWidget *winBar = findWidget_App("winbar");
1429 updateTextCStr_LabelWidget(findChild_Widget(winBar, "winbar.max"),
1430 d->place.snap == maximized_WindowSnap ? "\u25a2" : "\u25a1");
1431 /* Show and hide the title bar. */
1363 const iBool wasVisible = isVisible_Widget(winBar); 1432 const iBool wasVisible = isVisible_Widget(winBar);
1364 setFlags_Widget(winBar, hidden_WidgetFlag, snapMode == fullscreen_WindowSnap); 1433 setFlags_Widget(winBar, hidden_WidgetFlag, d->place.snap == fullscreen_WindowSnap);
1365 if (newRect.size.x) { 1434 if (newRect.size.x) {
1366 SDL_SetWindowPosition(d->win, newRect.pos.x, newRect.pos.y); 1435 SDL_SetWindowPosition(d->win, newRect.pos.x, newRect.pos.y);
1367 SDL_SetWindowSize(d->win, newRect.size.x, newRect.size.y); 1436 SDL_SetWindowSize(d->win, newRect.size.x, newRect.size.y);
diff --git a/src/ui/window.h b/src/ui/window.h
index ddb03847..6063df88 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -42,6 +42,7 @@ enum iWindowSnap {
42 mask_WindowSnap = 0xff, 42 mask_WindowSnap = 0xff,
43 topBit_WindowSnap = iBit(9), 43 topBit_WindowSnap = iBit(9),
44 bottomBit_WindowSnap = iBit(10), 44 bottomBit_WindowSnap = iBit(10),
45 redo_WindowSnap = iBit(11),
45}; 46};
46 47
47iDeclareType(WindowPlacement) 48iDeclareType(WindowPlacement)
diff --git a/src/win32.c b/src/win32.c
index 585fdaaf..504cfe90 100644
--- a/src/win32.c
+++ b/src/win32.c
@@ -76,17 +76,86 @@ void setup_SDLWindow(SDL_Window *win) {
76#endif 76#endif
77 77
78#if defined (LAGRANGE_CUSTOM_FRAME) 78#if defined (LAGRANGE_CUSTOM_FRAME)
79iInt2 cursor_Win32(void) {
80 POINT p;
81 GetPhysicalCursorPos(&p);
82 return init_I2(p.x, p.y);
83}
84
79void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window) { 85void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window) {
86 static int winDown_[2] = { 0, 0 };
80 HWND hwnd = msg->msg.win.hwnd; 87 HWND hwnd = msg->msg.win.hwnd;
81// printf("[syswm] %x\n", msg->msg.win.msg); fflush(stdout); 88 printf("[syswm] %x\n", msg->msg.win.msg); fflush(stdout);
89 const WPARAM wp = msg->msg.win.wParam;
82 switch (msg->msg.win.msg) { 90 switch (msg->msg.win.msg) {
91 case WM_KEYDOWN: {
92 if (wp == VK_LWIN) {
93 printf("lwin down\n"); fflush(stdout);
94 winDown_[0] = iTrue;
95 }
96 else if (wp == VK_RWIN) {
97 //printf("rwin down\n"); fflush(stdout);
98 winDown_[1] = iTrue;
99 }
100 break;
101 }
102 case WM_KEYUP: {
103 if (winDown_[0] || winDown_[1]) {
104 /* Emulate the default window snapping behavior. */
105 int snap = snap_Window(window);
106 if (wp == VK_LEFT) {
107 snap &= ~(topBit_WindowSnap | bottomBit_WindowSnap);
108 setSnap_Window(window,
109 snap == right_WindowSnap ? 0 : left_WindowSnap);
110 }
111 else if (wp == VK_RIGHT) {
112 snap &= ~(topBit_WindowSnap | bottomBit_WindowSnap);
113 setSnap_Window(window,
114 snap == left_WindowSnap ? 0 : right_WindowSnap);
115 }
116 else if (wp == VK_UP) {
117 if (~snap & topBit_WindowSnap) {
118 setSnap_Window(window,
119 snap & bottomBit_WindowSnap ? snap & ~bottomBit_WindowSnap
120 : snap == left_WindowSnap || snap == right_WindowSnap
121 ? snap | topBit_WindowSnap
122 : maximized_WindowSnap);
123 }
124 else {
125 postCommand_App("window.maximize");
126 }
127 }
128 else if (wp == VK_DOWN) {
129 if (snap == 0 || snap & bottomBit_WindowSnap) {
130 postCommand_App("window.minimize");
131 }
132 else {
133 setSnap_Window(window,
134 snap == maximized_WindowSnap ? 0
135 : snap & topBit_WindowSnap ? snap & ~topBit_WindowSnap
136 : snap == left_WindowSnap || snap == right_WindowSnap
137 ? snap | bottomBit_WindowSnap
138 : 0);
139 }
140 }
141 }
142 if (wp == VK_LWIN) {
143 winDown_[0] = iFalse;
144 }
145 if (wp == VK_RWIN) {
146 winDown_[1] = iFalse;
147 }
148 break;
149 }
83 case WM_NCLBUTTONDBLCLK: { 150 case WM_NCLBUTTONDBLCLK: {
84 POINT point = { GET_X_LPARAM(msg->msg.win.lParam), GET_Y_LPARAM(msg->msg.win.lParam) }; 151 POINT point = { GET_X_LPARAM(msg->msg.win.lParam),
152 GET_Y_LPARAM(msg->msg.win.lParam) };
85 ScreenToClient(hwnd, &point); 153 ScreenToClient(hwnd, &point);
86 iInt2 pos = init_I2(point.x, point.y); 154 iInt2 pos = init_I2(point.x, point.y);
87 switch (hitTest_Window(window, pos)) { 155 switch (hitTest_Window(window, pos)) {
88 case SDL_HITTEST_DRAGGABLE: 156 case SDL_HITTEST_DRAGGABLE:
89 postCommand_App("window.maximize toggle:1"); 157 postCommandf_App("window.%s",
158 snap_Window(window) ? "restore" : "maximize toggle:1");
90 break; 159 break;
91 case SDL_HITTEST_RESIZE_TOP: 160 case SDL_HITTEST_RESIZE_TOP:
92 case SDL_HITTEST_RESIZE_BOTTOM: { 161 case SDL_HITTEST_RESIZE_BOTTOM: {
@@ -98,11 +167,25 @@ void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window) {
98 break; 167 break;
99 } 168 }
100#if 0 169#if 0
170 case WM_NCLBUTTONUP: {
171 POINT point = { GET_X_LPARAM(msg->msg.win.lParam),
172 GET_Y_LPARAM(msg->msg.win.lParam) };
173 printf("%d,%d\n", point.x, point.y); fflush(stdout);
174 ScreenToClient(hwnd, &point);
175 iInt2 pos = init_I2(point.x, point.y);
176 if (hitTest_Window(window, pos) == SDL_HITTEST_DRAGGABLE) {
177 printf("released draggable\n"); fflush(stdout);
178 }
179 break;
180 }
181#endif
182#if 0
101 /* SDL does not use WS_SYSMENU on the window, so we can't display the system menu. 183 /* 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, 184 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? */ 185 but that doesn't work with a custom frame. We could show a custom system menu? */
104 case WM_NCRBUTTONUP: { 186 case WM_NCRBUTTONUP: {
105 POINT point = { GET_X_LPARAM(msg->msg.win.lParam), GET_Y_LPARAM(msg->msg.win.lParam) }; 187 POINT point = { GET_X_LPARAM(msg->msg.win.lParam),
188 GET_Y_LPARAM(msg->msg.win.lParam) };
106 HMENU menu = GetSystemMenu(hwnd, FALSE); 189 HMENU menu = GetSystemMenu(hwnd, FALSE);
107 printf("menu at %d,%d menu:%p\n", point.x, point.y, menu); fflush(stdout); 190 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); 191 TrackPopupMenu(menu, TPM_RIGHTBUTTON, point.x, point.y, 0, hwnd, NULL);
diff --git a/src/win32.h b/src/win32.h
index b988c302..4b8f00da 100644
--- a/src/win32.h
+++ b/src/win32.h
@@ -22,7 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23#pragma once 23#pragma once
24 24
25#include <the_Foundation/defs.h> 25#include <the_Foundation/vec2.h>
26#include <SDL_video.h> 26#include <SDL_video.h>
27#include <SDL_syswm.h> 27#include <SDL_syswm.h>
28 28
@@ -32,3 +32,5 @@ void setDPIAware_Win32(void);
32float desktopDPI_Win32(void); 32float desktopDPI_Win32(void);
33void useExecutableIconResource_SDLWindow(SDL_Window *win); 33void useExecutableIconResource_SDLWindow(SDL_Window *win);
34void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window); 34void processNativeEvent_Win32(const struct SDL_SysWMmsg *msg, iWindow *window);
35
36iInt2 cursor_Win32(void);