summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c122
-rw-r--r--src/ui/documentwidget.c2
-rw-r--r--src/ui/paint.c3
-rw-r--r--src/ui/root.c10
-rw-r--r--src/ui/uploadwidget.c2
-rw-r--r--src/ui/util.c2
-rw-r--r--src/ui/widget.c4
-rw-r--r--src/ui/window.c557
-rw-r--r--src/ui/window.h135
9 files changed, 462 insertions, 375 deletions
diff --git a/src/app.c b/src/app.c
index e06a32ce..91b3a06d 100644
--- a/src/app.c
+++ b/src/app.c
@@ -117,7 +117,7 @@ struct Impl_App {
117 iGmCerts * certs; 117 iGmCerts * certs;
118 iVisited * visited; 118 iVisited * visited;
119 iBookmarks * bookmarks; 119 iBookmarks * bookmarks;
120 iWindow * window; 120 iMainWindow *window;
121 iSortedArray tickers; /* per-frame callbacks, used for animations */ 121 iSortedArray tickers; /* per-frame callbacks, used for animations */
122 uint32_t lastTickerTime; 122 uint32_t lastTickerTime;
123 uint32_t elapsedSinceLastTicker; 123 uint32_t elapsedSinceLastTicker;
@@ -188,7 +188,7 @@ static iString *serializePrefs_App_(const iApp *d) {
188 /* On macOS, maximization should be applied at creation time or the window will take 188 /* On macOS, maximization should be applied at creation time or the window will take
189 a moment to animate to its maximized size. */ 189 a moment to animate to its maximized size. */
190#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 190#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
191 if (snap_Window(d->window)) { 191 if (snap_MainWindow(d->window)) {
192 if (~SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_MINIMIZED) { 192 if (~SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_MINIMIZED) {
193 /* Save the actual visible window position, too, because snapped windows may 193 /* Save the actual visible window position, too, because snapped windows may
194 still be resized/moved without affecting normalRect. */ 194 still be resized/moved without affecting normalRect. */
@@ -196,17 +196,17 @@ static iString *serializePrefs_App_(const iApp *d) {
196 SDL_GetWindowSize(d->window->win, &w, &h); 196 SDL_GetWindowSize(d->window->win, &w, &h);
197 appendFormat_String( 197 appendFormat_String(
198 str, "~window.setrect snap:%d width:%d height:%d coord:%d %d\n", 198 str, "~window.setrect snap:%d width:%d height:%d coord:%d %d\n",
199 snap_Window(d->window), w, h, x, y); 199 snap_MainWindow(d->window), w, h, x, y);
200 } 200 }
201 } 201 }
202#elif !defined (iPlatformApple) 202#elif !defined (iPlatformApple)
203 if (snap_Window(d->window) == maximized_WindowSnap) { 203 if (snap_MainWindow(d->window) == maximized_WindowSnap) {
204 appendFormat_String(str, "~window.maximize\n"); 204 appendFormat_String(str, "~window.maximize\n");
205 } 205 }
206#endif 206#endif
207 } 207 }
208 appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.uiLanguage)); 208 appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.uiLanguage));
209 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window)); 209 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(as_Window(d->window)));
210 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab); 210 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab);
211 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font); 211 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font);
212 appendFormat_String(str, "font.user path:%s\n", cstr_String(&d->prefs.symbolFontPath)); 212 appendFormat_String(str, "font.user path:%s\n", cstr_String(&d->prefs.symbolFontPath));
@@ -414,8 +414,8 @@ static iBool loadState_App_(iApp *d) {
414 const int splitMode = read32_File(f); 414 const int splitMode = read32_File(f);
415 const int keyRoot = read32_File(f); 415 const int keyRoot = read32_File(f);
416 d->window->pendingSplitMode = splitMode; 416 d->window->pendingSplitMode = splitMode;
417 setSplitMode_Window(d->window, splitMode | noEvents_WindowSplit); 417 setSplitMode_MainWindow(d->window, splitMode | noEvents_WindowSplit);
418 d->window->keyRoot = d->window->roots[keyRoot]; 418 d->window->base.keyRoot = d->window->base.roots[keyRoot];
419 } 419 }
420 else if (!memcmp(magic, magicSidebar_App_, 4)) { 420 else if (!memcmp(magic, magicSidebar_App_, 4)) {
421 const uint16_t bits = readU16_File(f); 421 const uint16_t bits = readU16_File(f);
@@ -426,7 +426,7 @@ static iBool loadState_App_(iApp *d) {
426 }; 426 };
427 const uint8_t rootIndex = bits & 0xff; 427 const uint8_t rootIndex = bits & 0xff;
428 const uint8_t flags = bits >> 8; 428 const uint8_t flags = bits >> 8;
429 iRoot *root = d->window->roots[rootIndex]; 429 iRoot *root = d->window->base.roots[rootIndex];
430 if (root) { 430 if (root) {
431 iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); 431 iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar");
432 iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2"); 432 iSidebarWidget *sidebar2 = findChild_Widget(root->widget, "sidebar2");
@@ -443,10 +443,10 @@ static iBool loadState_App_(iApp *d) {
443 else if (!memcmp(magic, magicTabDocument_App_, 4)) { 443 else if (!memcmp(magic, magicTabDocument_App_, 4)) {
444 const int8_t flags = read8_File(f); 444 const int8_t flags = read8_File(f);
445 int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0; 445 int rootIndex = flags & rootIndex1_DocumentStateFlag ? 1 : 0;
446 if (rootIndex > numRoots_Window(d->window) - 1) { 446 if (rootIndex > numRoots_Window(as_Window(d->window)) - 1) {
447 rootIndex = 0; 447 rootIndex = 0;
448 } 448 }
449 setCurrent_Root(d->window->roots[rootIndex]); 449 setCurrent_Root(d->window->base.roots[rootIndex]);
450 if (isFirstTab[rootIndex]) { 450 if (isFirstTab[rootIndex]) {
451 isFirstTab[rootIndex] = iFalse; 451 isFirstTab[rootIndex] = iFalse;
452 /* There is one pre-created tab in each root. */ 452 /* There is one pre-created tab in each root. */
@@ -469,7 +469,7 @@ static iBool loadState_App_(iApp *d) {
469 } 469 }
470 if (d->window->splitMode) { 470 if (d->window->splitMode) {
471 /* Update root placement. */ 471 /* Update root placement. */
472 resize_Window(d->window, -1, -1); 472 resize_MainWindow(d->window, -1, -1);
473 } 473 }
474 iForIndices(i, current) { 474 iForIndices(i, current) {
475 postCommandf_Root(NULL, "tabs.switch page:%p", current[i]); 475 postCommandf_Root(NULL, "tabs.switch page:%p", current[i]);
@@ -483,7 +483,7 @@ static iBool loadState_App_(iApp *d) {
483static void saveState_App_(const iApp *d) { 483static void saveState_App_(const iApp *d) {
484 iUnused(d); 484 iUnused(d);
485 trimCache_App(); 485 trimCache_App();
486 iWindow *win = d->window; 486 iMainWindow *win = d->window;
487 /* UI state is saved in binary because it is quite complex (e.g., 487 /* UI state is saved in binary because it is quite complex (e.g.,
488 navigation history, cached content) and depends closely on the widget 488 navigation history, cached content) and depends closely on the widget
489 tree. The data is largely not reorderable and should not be modified 489 tree. The data is largely not reorderable and should not be modified
@@ -495,11 +495,11 @@ static void saveState_App_(const iApp *d) {
495 /* Begin with window state. */ { 495 /* Begin with window state. */ {
496 writeData_File(f, magicWindow_App_, 4); 496 writeData_File(f, magicWindow_App_, 4);
497 writeU32_File(f, win->splitMode); 497 writeU32_File(f, win->splitMode);
498 writeU32_File(f, win->keyRoot == win->roots[0] ? 0 : 1); 498 writeU32_File(f, win->base.keyRoot == win->base.roots[0] ? 0 : 1);
499 } 499 }
500 /* State of UI elements. */ { 500 /* State of UI elements. */ {
501 iForIndices(i, win->roots) { 501 iForIndices(i, win->base.roots) {
502 const iRoot *root = win->roots[i]; 502 const iRoot *root = win->base.roots[i];
503 if (root) { 503 if (root) {
504 writeData_File(f, magicSidebar_App_, 4); 504 writeData_File(f, magicSidebar_App_, 4);
505 const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar"); 505 const iSidebarWidget *sidebar = findChild_Widget(root->widget, "sidebar");
@@ -520,7 +520,7 @@ static void saveState_App_(const iApp *d) {
520 const iWidget *widget = constAs_Widget(i.object); 520 const iWidget *widget = constAs_Widget(i.object);
521 writeData_File(f, magicTabDocument_App_, 4); 521 writeData_File(f, magicTabDocument_App_, 4);
522 int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0); 522 int8_t flags = (document_Root(widget->root) == i.object ? current_DocumentStateFlag : 0);
523 if (widget->root == win->roots[1]) { 523 if (widget->root == win->base.roots[1]) {
524 flags |= rootIndex1_DocumentStateFlag; 524 flags |= rootIndex1_DocumentStateFlag;
525 } 525 }
526 write8_File(f, flags); 526 write8_File(f, flags);
@@ -801,7 +801,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
801 d->initialWindowRect.size.y = toInt_String(value_CommandLineArg(arg, 0)); 801 d->initialWindowRect.size.y = toInt_String(value_CommandLineArg(arg, 0));
802 } 802 }
803 } 803 }
804 d->window = new_Window(d->initialWindowRect); 804 d->window = new_MainWindow(d->initialWindowRect);
805 load_Visited(d->visited, dataDir_App_()); 805 load_Visited(d->visited, dataDir_App_());
806 load_Bookmarks(d->bookmarks, dataDir_App_()); 806 load_Bookmarks(d->bookmarks, dataDir_App_());
807 load_MimeHooks(d->mimehooks, dataDir_App_()); 807 load_MimeHooks(d->mimehooks, dataDir_App_());
@@ -848,7 +848,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
848 fetchRemote_Bookmarks(d->bookmarks); 848 fetchRemote_Bookmarks(d->bookmarks);
849 if (deviceType_App() != desktop_AppDeviceType) { 849 if (deviceType_App() != desktop_AppDeviceType) {
850 /* HACK: Force a resize so widgets update their state. */ 850 /* HACK: Force a resize so widgets update their state. */
851 resize_Window(d->window, -1, -1); 851 resize_MainWindow(d->window, -1, -1);
852 } 852 }
853} 853}
854 854
@@ -871,7 +871,7 @@ static void deinit_App(iApp *d) {
871 delete_GmCerts(d->certs); 871 delete_GmCerts(d->certs);
872 save_MimeHooks(d->mimehooks); 872 save_MimeHooks(d->mimehooks);
873 delete_MimeHooks(d->mimehooks); 873 delete_MimeHooks(d->mimehooks);
874 delete_Window(d->window); 874 delete_MainWindow(d->window);
875 d->window = NULL; 875 d->window = NULL;
876 deinit_CommandLine(&d->args); 876 deinit_CommandLine(&d->args);
877 iRelease(d->launchCommands); 877 iRelease(d->launchCommands);
@@ -1110,7 +1110,7 @@ void processEvents_App(enum iAppEventMode eventMode) {
1110 clearCache_App_(); 1110 clearCache_App_();
1111 break; 1111 break;
1112 case SDL_APP_WILLENTERFOREGROUND: 1112 case SDL_APP_WILLENTERFOREGROUND:
1113 invalidate_Window(d->window); 1113 invalidate_Window(as_Window(d->window));
1114 break; 1114 break;
1115 case SDL_APP_DIDENTERFOREGROUND: 1115 case SDL_APP_DIDENTERFOREGROUND:
1116 gotEvents = iTrue; 1116 gotEvents = iTrue;
@@ -1125,17 +1125,17 @@ void processEvents_App(enum iAppEventMode eventMode) {
1125#if defined (iPlatformAppleMobile) 1125#if defined (iPlatformAppleMobile)
1126 updateNowPlayingInfo_iOS(); 1126 updateNowPlayingInfo_iOS();
1127#endif 1127#endif
1128 setFreezeDraw_Window(d->window, iTrue); 1128 setFreezeDraw_Window(as_Window(d), iTrue);
1129 savePrefs_App_(d); 1129 savePrefs_App_(d);
1130 saveState_App_(d); 1130 saveState_App_(d);
1131 break; 1131 break;
1132 case SDL_APP_TERMINATING: 1132 case SDL_APP_TERMINATING:
1133 setFreezeDraw_Window(d->window, iTrue); 1133 setFreezeDraw_Window(as_Window(d), iTrue);
1134 savePrefs_App_(d); 1134 savePrefs_App_(d);
1135 saveState_App_(d); 1135 saveState_App_(d);
1136 break; 1136 break;
1137 case SDL_DROPFILE: { 1137 case SDL_DROPFILE: {
1138 iBool wasUsed = processEvent_Window(d->window, &ev); 1138 iBool wasUsed = processEvent_MainWindow(d->window, &ev);
1139 if (!wasUsed) { 1139 if (!wasUsed) {
1140 iBool newTab = iFalse; 1140 iBool newTab = iFalse;
1141 if (elapsedSeconds_Time(&d->lastDropTime) < 0.1) { 1141 if (elapsedSeconds_Time(&d->lastDropTime) < 0.1) {
@@ -1177,10 +1177,10 @@ void processEvents_App(enum iAppEventMode eventMode) {
1177#endif 1177#endif
1178 if (ev.type == SDL_USEREVENT && ev.user.code == arrange_UserEventCode) { 1178 if (ev.type == SDL_USEREVENT && ev.user.code == arrange_UserEventCode) {
1179 printf("[App] rearrange\n"); 1179 printf("[App] rearrange\n");
1180 resize_Window(d->window, -1, -1); 1180 resize_MainWindow(d->window, -1, -1);
1181 iForIndices(i, d->window->roots) { 1181 iForIndices(i, d->window->base.roots) {
1182 if (d->window->roots[i]) { 1182 if (d->window->base.roots[i]) {
1183 d->window->roots[i]->pendingArrange = iFalse; 1183 d->window->base.roots[i]->pendingArrange = iFalse;
1184 } 1184 }
1185 } 1185 }
1186// if (ev.user.data2 == d->window->roots[0]) { 1186// if (ev.user.data2 == d->window->roots[0]) {
@@ -1209,8 +1209,8 @@ void processEvents_App(enum iAppEventMode eventMode) {
1209 if (ev.wheel.which == 0) { 1209 if (ev.wheel.which == 0) {
1210 /* Trackpad with precise scrolling w/inertia (points). */ 1210 /* Trackpad with precise scrolling w/inertia (points). */
1211 setPerPixel_MouseWheelEvent(&ev.wheel, iTrue); 1211 setPerPixel_MouseWheelEvent(&ev.wheel, iTrue);
1212 ev.wheel.x *= -d->window->pixelRatio; 1212 ev.wheel.x *= -d->window->base.pixelRatio;
1213 ev.wheel.y *= d->window->pixelRatio; 1213 ev.wheel.y *= d->window->base.pixelRatio;
1214 /* Only scroll on one axis at a time. */ 1214 /* Only scroll on one axis at a time. */
1215 if (iAbs(ev.wheel.x) > iAbs(ev.wheel.y)) { 1215 if (iAbs(ev.wheel.x) > iAbs(ev.wheel.y)) {
1216 ev.wheel.y = 0; 1216 ev.wheel.y = 0;
@@ -1268,8 +1268,8 @@ void processEvents_App(enum iAppEventMode eventMode) {
1268 } 1268 }
1269 } 1269 }
1270#endif 1270#endif
1271 d->window->lastHover = d->window->hover; 1271 d->window->base.lastHover = d->window->base.hover;
1272 iBool wasUsed = processEvent_Window(d->window, &ev); 1272 iBool wasUsed = processEvent_MainWindow(d->window, &ev);
1273 if (!wasUsed) { 1273 if (!wasUsed) {
1274 /* There may be a key bindings for this. */ 1274 /* There may be a key bindings for this. */
1275 wasUsed = processEvent_Keys(&ev); 1275 wasUsed = processEvent_Keys(&ev);
@@ -1289,8 +1289,8 @@ void processEvents_App(enum iAppEventMode eventMode) {
1289 handleCommand_MacOS(command_UserEvent(&ev)); 1289 handleCommand_MacOS(command_UserEvent(&ev));
1290#endif 1290#endif
1291 if (isMetricsChange_UserEvent(&ev)) { 1291 if (isMetricsChange_UserEvent(&ev)) {
1292 iForIndices(i, d->window->roots) { 1292 iForIndices(i, d->window->base.roots) {
1293 iRoot *root = d->window->roots[i]; 1293 iRoot *root = d->window->base.roots[i];
1294 if (root) { 1294 if (root) {
1295 arrange_Widget(root->widget); 1295 arrange_Widget(root->widget);
1296 } 1296 }
@@ -1304,9 +1304,9 @@ void processEvents_App(enum iAppEventMode eventMode) {
1304 free(ev.user.data1); 1304 free(ev.user.data1);
1305 } 1305 }
1306 /* Update when hover has changed. */ 1306 /* Update when hover has changed. */
1307 if (d->window->lastHover != d->window->hover) { 1307 if (d->window->base.lastHover != d->window->base.hover) {
1308 refresh_Widget(d->window->lastHover); 1308 refresh_Widget(d->window->base.lastHover);
1309 refresh_Widget(d->window->hover); 1309 refresh_Widget(d->window->base.hover);
1310 } 1310 }
1311 break; 1311 break;
1312 } 1312 }
@@ -1362,15 +1362,16 @@ static int resizeWatcher_(void *user, SDL_Event *event) {
1362 dispatchEvent_Window(d->window, &u); 1362 dispatchEvent_Window(d->window, &u);
1363 } 1363 }
1364#endif 1364#endif
1365 drawWhileResizing_Window(d->window, winev->data1, winev->data2); 1365 drawWhileResizing_MainWindow(d->window, winev->data1, winev->data2);
1366 } 1366 }
1367 return 0; 1367 return 0;
1368} 1368}
1369 1369
1370static int run_App_(iApp *d) { 1370static int run_App_(iApp *d) {
1371 iForIndices(i, d->window->roots) { 1371 /* Initial arrangement. */
1372 if (d->window->roots[i]) { 1372 iForIndices(i, d->window->base.roots) {
1373 arrange_Widget(d->window->roots[i]->widget); 1373 if (d->window->base.roots[i]) {
1374 arrange_Widget(d->window->base.roots[i]->widget);
1374 } 1375 }
1375 } 1376 }
1376 d->isRunning = iTrue; 1377 d->isRunning = iTrue;
@@ -1384,7 +1385,7 @@ static int run_App_(iApp *d) {
1384 runTickers_App_(d); 1385 runTickers_App_(d);
1385 refresh_App(); 1386 refresh_App();
1386 /* Change the widget tree while we are not iterating through it. */ 1387 /* Change the widget tree while we are not iterating through it. */
1387 checkPendingSplit_Window(d->window); 1388 checkPendingSplit_MainWindow(d->window);
1388 recycle_Garbage(); 1389 recycle_Garbage();
1389 } 1390 }
1390 SDL_DelEventWatch(resizeWatcher_, d); 1391 SDL_DelEventWatch(resizeWatcher_, d);
@@ -1393,8 +1394,8 @@ static int run_App_(iApp *d) {
1393 1394
1394void refresh_App(void) { 1395void refresh_App(void) {
1395 iApp *d = &app_; 1396 iApp *d = &app_;
1396 iForIndices(i, d->window->roots) { 1397 iForIndices(i, d->window->base.roots) {
1397 iRoot *root = d->window->roots[i]; 1398 iRoot *root = d->window->base.roots[i];
1398 if (root) { 1399 if (root) {
1399 destroyPending_Root(root); 1400 destroyPending_Root(root);
1400 } 1401 }
@@ -1409,7 +1410,7 @@ void refresh_App(void) {
1409 } 1410 }
1410// iTime draw; 1411// iTime draw;
1411// initCurrent_Time(&draw); 1412// initCurrent_Time(&draw);
1412 draw_Window(d->window); 1413 draw_MainWindow(d->window);
1413// printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000)); 1414// printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000));
1414// fflush(stdout); 1415// fflush(stdout);
1415 if (d->warmupFrames > 0) { 1416 if (d->warmupFrames > 0) {
@@ -1545,7 +1546,7 @@ void postCommandf_App(const char *command, ...) {
1545} 1546}
1546 1547
1547void rootOrder_App(iRoot *roots[2]) { 1548void rootOrder_App(iRoot *roots[2]) {
1548 const iWindow *win = app_.window; 1549 const iWindow *win = as_Window(app_.window);
1549 roots[0] = win->keyRoot; 1550 roots[0] = win->keyRoot;
1550 roots[1] = (roots[0] == win->roots[0] ? win->roots[1] : win->roots[0]); 1551 roots[1] = (roots[0] == win->roots[0] ? win->roots[1] : win->roots[0]);
1551} 1552}
@@ -1979,7 +1980,7 @@ const iString *searchQueryUrl_App(const iString *queryStringUnescaped) {
1979 1980
1980iBool handleCommand_App(const char *cmd) { 1981iBool handleCommand_App(const char *cmd) {
1981 iApp *d = &app_; 1982 iApp *d = &app_;
1982 const iBool isFrozen = !d->window || d->window->isDrawFrozen; 1983 const iBool isFrozen = !d->window || d->window->base.isDrawFrozen;
1983 if (equal_Command(cmd, "config.error")) { 1984 if (equal_Command(cmd, "config.error")) {
1984 makeSimpleMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR", 1985 makeSimpleMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR",
1985 format_CStr("Error in config file: %s\n" 1986 format_CStr("Error in config file: %s\n"
@@ -2011,13 +2012,13 @@ iBool handleCommand_App(const char *cmd) {
2011 } 2012 }
2012 else if (equal_Command(cmd, "ui.split")) { 2013 else if (equal_Command(cmd, "ui.split")) {
2013 if (argLabel_Command(cmd, "swap")) { 2014 if (argLabel_Command(cmd, "swap")) {
2014 swapRoots_Window(d->window); 2015 swapRoots_MainWindow(d->window);
2015 return iTrue; 2016 return iTrue;
2016 } 2017 }
2017 d->window->pendingSplitMode = 2018 d->window->pendingSplitMode =
2018 (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1); 2019 (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1);
2019 const char *url = suffixPtr_Command(cmd, "url"); 2020 const char *url = suffixPtr_Command(cmd, "url");
2020 setCStr_String(get_Window()->pendingSplitUrl, url ? url : ""); 2021 setCStr_String(d->window->pendingSplitUrl, url ? url : "");
2021 postRefresh_App(); 2022 postRefresh_App();
2022 return iTrue; 2023 return iTrue;
2023 } 2024 }
@@ -2031,17 +2032,17 @@ iBool handleCommand_App(const char *cmd) {
2031 } 2032 }
2032 else if (equal_Command(cmd, "window.maximize")) { 2033 else if (equal_Command(cmd, "window.maximize")) {
2033 if (!argLabel_Command(cmd, "toggle")) { 2034 if (!argLabel_Command(cmd, "toggle")) {
2034 setSnap_Window(d->window, maximized_WindowSnap); 2035 setSnap_MainWindow(d->window, maximized_WindowSnap);
2035 } 2036 }
2036 else { 2037 else {
2037 setSnap_Window(d->window, snap_Window(d->window) == maximized_WindowSnap ? 0 : 2038 setSnap_MainWindow(d->window, snap_MainWindow(d->window) == maximized_WindowSnap ? 0 :
2038 maximized_WindowSnap); 2039 maximized_WindowSnap);
2039 } 2040 }
2040 return iTrue; 2041 return iTrue;
2041 } 2042 }
2042 else if (equal_Command(cmd, "window.fullscreen")) { 2043 else if (equal_Command(cmd, "window.fullscreen")) {
2043 const iBool wasFull = snap_Window(d->window) == fullscreen_WindowSnap; 2044 const iBool wasFull = snap_MainWindow(d->window) == fullscreen_WindowSnap;
2044 setSnap_Window(d->window, wasFull ? 0 : fullscreen_WindowSnap); 2045 setSnap_MainWindow(d->window, wasFull ? 0 : fullscreen_WindowSnap);
2045 postCommandf_App("window.fullscreen.changed arg:%d", !wasFull); 2046 postCommandf_App("window.fullscreen.changed arg:%d", !wasFull);
2046 return iTrue; 2047 return iTrue;
2047 } 2048 }
@@ -2210,7 +2211,7 @@ iBool handleCommand_App(const char *cmd) {
2210 equal_Command(cmd, "prefs.mono.gopher.changed")) { 2211 equal_Command(cmd, "prefs.mono.gopher.changed")) {
2211 const iBool isSet = (arg_Command(cmd) != 0); 2212 const iBool isSet = (arg_Command(cmd) != 0);
2212 if (!isFrozen) { 2213 if (!isFrozen) {
2213 setFreezeDraw_Window(d->window, iTrue); 2214 setFreezeDraw_Window(as_Window(d->window), iTrue);
2214 } 2215 }
2215 if (startsWith_CStr(cmd, "prefs.mono.gemini")) { 2216 if (startsWith_CStr(cmd, "prefs.mono.gemini")) {
2216 d->prefs.monospaceGemini = isSet; 2217 d->prefs.monospaceGemini = isSet;
@@ -2405,8 +2406,8 @@ iBool handleCommand_App(const char *cmd) {
2405 iRoot *root = get_Root(); 2406 iRoot *root = get_Root();
2406 iRoot *oldRoot = root; 2407 iRoot *oldRoot = root;
2407 if (newTab & otherRoot_OpenTabFlag) { 2408 if (newTab & otherRoot_OpenTabFlag) {
2408 root = otherRoot_Window(d->window, root); 2409 root = otherRoot_Window(as_Window(d->window), root);
2409 setKeyRoot_Window(d->window, root); 2410 setKeyRoot_Window(as_Window(d->window), root);
2410 setCurrent_Root(root); /* need to change for widget creation */ 2411 setCurrent_Root(root); /* need to change for widget creation */
2411 } 2412 }
2412 iDocumentWidget *doc = document_Command(cmd); 2413 iDocumentWidget *doc = document_Command(cmd);
@@ -2560,7 +2561,8 @@ iBool handleCommand_App(const char *cmd) {
2560 return iTrue; 2561 return iTrue;
2561 } 2562 }
2562 else if (equal_Command(cmd, "keyroot.next")) { 2563 else if (equal_Command(cmd, "keyroot.next")) {
2563 if (setKeyRoot_Window(d->window, otherRoot_Window(d->window, d->window->keyRoot))) { 2564 if (setKeyRoot_Window(as_Window(d->window),
2565 otherRoot_Window(as_Window(d->window), d->window->base.keyRoot))) {
2564 setFocus_Widget(NULL); 2566 setFocus_Widget(NULL);
2565 } 2567 }
2566 return iTrue; 2568 return iTrue;
@@ -2592,7 +2594,7 @@ iBool handleCommand_App(const char *cmd) {
2592 format_CStr("returnkey.set arg:%d", d->prefs.returnKey)); 2594 format_CStr("returnkey.set arg:%d", d->prefs.returnKey));
2593 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); 2595 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
2594 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), 2596 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"),
2595 collectNewFormat_String("%g", uiScale_Window(d->window))); 2597 collectNewFormat_String("%g", uiScale_Window(as_Window(d->window))));
2596 setFlags_Widget(findChild_Widget(dlg, format_CStr("prefs.font.%d", d->prefs.font)), 2598 setFlags_Widget(findChild_Widget(dlg, format_CStr("prefs.font.%d", d->prefs.font)),
2597 selected_WidgetFlag, 2599 selected_WidgetFlag,
2598 iTrue); 2600 iTrue);
@@ -2749,7 +2751,7 @@ iBool handleCommand_App(const char *cmd) {
2749 /* Set of open tabs has changed. */ 2751 /* Set of open tabs has changed. */
2750 postCommand_App("document.openurls.changed"); 2752 postCommand_App("document.openurls.changed");
2751 if (deviceType_App() == phone_AppDeviceType) { 2753 if (deviceType_App() == phone_AppDeviceType) {
2752 showToolbar_Root(d->window->roots[0], iTrue); 2754 showToolbar_Root(d->window->base.roots[0], iTrue);
2753 } 2755 }
2754 return iFalse; 2756 return iFalse;
2755 } 2757 }
@@ -2825,8 +2827,8 @@ iBool handleCommand_App(const char *cmd) {
2825 } 2827 }
2826 else if (equal_Command(cmd, "ipc.signal")) { 2828 else if (equal_Command(cmd, "ipc.signal")) {
2827 if (argLabel_Command(cmd, "raise")) { 2829 if (argLabel_Command(cmd, "raise")) {
2828 if (d->window && d->window->win) { 2830 if (d->window && d->window->base.win) {
2829 SDL_RaiseWindow(d->window->win); 2831 SDL_RaiseWindow(d->window->base.win);
2830 } 2832 }
2831 } 2833 }
2832 signal_Ipc(arg_Command(cmd)); 2834 signal_Ipc(arg_Command(cmd));
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 8ea695d5..ed9e41d6 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -934,7 +934,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) {
934 iString *text = collect_String(joinCStr_StringArray(title, " \u2014 ")); 934 iString *text = collect_String(joinCStr_StringArray(title, " \u2014 "));
935 if (setWindow) { 935 if (setWindow) {
936 /* Longest version for the window title, and omit the icon. */ 936 /* Longest version for the window title, and omit the icon. */
937 setTitle_Window(get_Window(), text); 937 setTitle_MainWindow(get_MainWindow(), text);
938 setWindow = iFalse; 938 setWindow = iFalse;
939 } 939 }
940 const iChar siteIcon = siteIcon_GmDocument(d->doc); 940 const iChar siteIcon = siteIcon_GmDocument(d->doc);
diff --git a/src/ui/paint.c b/src/ui/paint.c
index a3ee32c6..5506f845 100644
--- a/src/ui/paint.c
+++ b/src/ui/paint.c
@@ -152,6 +152,9 @@ void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color
152 addv_I2(&inner.pos, origin_Paint); 152 addv_I2(&inner.pos, origin_Paint);
153 SDL_Renderer *render = renderer_Paint_(d); 153 SDL_Renderer *render = renderer_Paint_(d);
154 SDL_Texture *shadow = get_Window()->borderShadow; 154 SDL_Texture *shadow = get_Window()->borderShadow;
155 if (!shadow) {
156 return;
157 }
155 const iInt2 size = size_SDLTexture(shadow); 158 const iInt2 size = size_SDLTexture(shadow);
156 const iRect outer = expanded_Rect(inner, init1_I2(thickness)); 159 const iRect outer = expanded_Rect(inner, init1_I2(thickness));
157 const iColor clr = get_Color(color); 160 const iColor clr = get_Color(color);
diff --git a/src/ui/root.c b/src/ui/root.c
index 6ea5f521..52a08eca 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -383,18 +383,18 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
383 else if (equal_Command(cmd, "window.setrect")) { 383 else if (equal_Command(cmd, "window.setrect")) {
384 const int snap = argLabel_Command(cmd, "snap"); 384 const int snap = argLabel_Command(cmd, "snap");
385 if (snap) { 385 if (snap) {
386 iWindow *window = get_Window(); 386 iMainWindow *window = get_MainWindow();
387 iInt2 coord = coord_Command(cmd); 387 iInt2 coord = coord_Command(cmd);
388 iInt2 size = init_I2(argLabel_Command(cmd, "width"), 388 iInt2 size = init_I2(argLabel_Command(cmd, "width"),
389 argLabel_Command(cmd, "height")); 389 argLabel_Command(cmd, "height"));
390 SDL_SetWindowPosition(window->win, coord.x, coord.y); 390 SDL_SetWindowPosition(window->base.win, coord.x, coord.y);
391 SDL_SetWindowSize(window->win, size.x, size.y); 391 SDL_SetWindowSize(window->base.win, size.x, size.y);
392 window->place.snap = snap; 392 window->place.snap = snap;
393 return iTrue; 393 return iTrue;
394 } 394 }
395 } 395 }
396 else if (equal_Command(cmd, "window.restore")) { 396 else if (equal_Command(cmd, "window.restore")) {
397 setSnap_Window(get_Window(), none_WindowSnap); 397 setSnap_MainWindow(get_MainWindow(), none_WindowSnap);
398 return iTrue; 398 return iTrue;
399 } 399 }
400 else if (equal_Command(cmd, "window.minimize")) { 400 else if (equal_Command(cmd, "window.minimize")) {
@@ -1525,5 +1525,5 @@ iRect safeRect_Root(const iRoot *d) {
1525} 1525}
1526 1526
1527iInt2 visibleSize_Root(const iRoot *d) { 1527iInt2 visibleSize_Root(const iRoot *d) {
1528 return addY_I2(size_Root(d), -get_Window()->keyboardHeight); 1528 return addY_I2(size_Root(d), -get_MainWindow()->keyboardHeight);
1529} 1529}
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index 72608851..ba7545fd 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -97,7 +97,7 @@ static void updateInputMaxHeight_UploadWidget_(iUploadWidget *d) {
97 height_Widget(findChild_Widget(w, "dialogbuttons")) + 97 height_Widget(findChild_Widget(w, "dialogbuttons")) +
98 12 * gap_UI); 98 12 * gap_UI);
99 const int avail = bottom_Rect(safeRect_Root(w->root)) - footerHeight - 99 const int avail = bottom_Rect(safeRect_Root(w->root)) - footerHeight -
100 get_Window()->keyboardHeight; 100 get_MainWindow()->keyboardHeight;
101 setLineLimits_InputWidget(d->input, 101 setLineLimits_InputWidget(d->input,
102 minLines_InputWidget(d->input), 102 minLines_InputWidget(d->input),
103 iMaxi(minLines_InputWidget(d->input), 103 iMaxi(minLines_InputWidget(d->input),
diff --git a/src/ui/util.c b/src/ui/util.c
index 01939e66..721aed2d 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1222,7 +1222,7 @@ static void updateValueInputWidth_(iWidget *dlg) {
1222 iMin(rootSize.x, iMaxi(iMaxi(100 * gap_UI, title->rect.size.x), prompt->rect.size.x)); 1222 iMin(rootSize.x, iMaxi(iMaxi(100 * gap_UI, title->rect.size.x), prompt->rect.size.x));
1223 } 1223 }
1224 /* Adjust the maximum number of visible lines. */ 1224 /* Adjust the maximum number of visible lines. */
1225 int footer = 6 * gap_UI + get_Window()->keyboardHeight; 1225 int footer = 6 * gap_UI + get_MainWindow()->keyboardHeight;
1226 iWidget *buttons = findChild_Widget(dlg, "dialogbuttons"); 1226 iWidget *buttons = findChild_Widget(dlg, "dialogbuttons");
1227 if (buttons) { 1227 if (buttons) {
1228 footer += height_Widget(buttons); 1228 footer += height_Widget(buttons);
diff --git a/src/ui/widget.c b/src/ui/widget.c
index c1fb9e95..23c19315 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -1088,7 +1088,7 @@ void scrollInfo_Widget(const iWidget *d, iWidgetScrollInfo *info) {
1088 iRect bounds = boundsWithoutVisualOffset_Widget(d); 1088 iRect bounds = boundsWithoutVisualOffset_Widget(d);
1089 const iRect winRect = adjusted_Rect(safeRect_Root(d->root), 1089 const iRect winRect = adjusted_Rect(safeRect_Root(d->root),
1090 zero_I2(), 1090 zero_I2(),
1091 init_I2(0, -get_Window()->keyboardHeight)); 1091 init_I2(0, -get_MainWindow()->keyboardHeight));
1092 info->height = bounds.size.y; 1092 info->height = bounds.size.y;
1093 info->avail = height_Rect(winRect); 1093 info->avail = height_Rect(winRect);
1094 if (info->avail >= info->height) { 1094 if (info->avail >= info->height) {
@@ -1109,7 +1109,7 @@ iBool scrollOverflow_Widget(iWidget *d, int delta) {
1109 iRect bounds = boundsWithoutVisualOffset_Widget(d); 1109 iRect bounds = boundsWithoutVisualOffset_Widget(d);
1110 const iRect winRect = adjusted_Rect(safeRect_Root(d->root), 1110 const iRect winRect = adjusted_Rect(safeRect_Root(d->root),
1111 zero_I2(), 1111 zero_I2(),
1112 init_I2(0, -get_Window()->keyboardHeight)); 1112 init_I2(0, -get_MainWindow()->keyboardHeight));
1113 const int yTop = top_Rect(winRect); 1113 const int yTop = top_Rect(winRect);
1114 const int yBottom = bottom_Rect(winRect); 1114 const int yBottom = bottom_Rect(winRect);
1115 if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) < yBottom) { 1115 if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) < yBottom) {
diff --git a/src/ui/window.c b/src/ui/window.c
index b8198636..92125d81 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -67,7 +67,7 @@ static float initialUiScale_ = 1.1f;
67 67
68static iBool isOpenGLRenderer_; 68static iBool isOpenGLRenderer_;
69 69
70iDefineTypeConstructionArgs(Window, (iRect rect), rect) 70iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect)
71 71
72/* TODO: Define menus per platform. */ 72/* TODO: Define menus per platform. */
73 73
@@ -169,17 +169,17 @@ int numRoots_Window(const iWindow *d) {
169 return num; 169 return num;
170} 170}
171 171
172static void windowSizeChanged_Window_(iWindow *d) { 172static void windowSizeChanged_MainWindow_(iMainWindow *d) {
173 const int numRoots = numRoots_Window(d); 173 const int numRoots = numRoots_Window(as_Window(d));
174 const iInt2 rootSize = d->size; 174 const iInt2 rootSize = d->base.size;
175 const int weights[2] = { 175 const int weights[2] = {
176 d->roots[0] ? (d->splitMode & twoToOne_WindowSplit ? 2 : 1) : 0, 176 d->base.roots[0] ? (d->splitMode & twoToOne_WindowSplit ? 2 : 1) : 0,
177 d->roots[1] ? (d->splitMode & oneToTwo_WindowSplit ? 2 : 1) : 0, 177 d->base.roots[1] ? (d->splitMode & oneToTwo_WindowSplit ? 2 : 1) : 0,
178 }; 178 };
179 const int totalWeight = weights[0] + weights[1]; 179 const int totalWeight = weights[0] + weights[1];
180 int w = 0; 180 int w = 0;
181 iForIndices(i, d->roots) { 181 iForIndices(i, d->base.roots) {
182 iRoot *root = d->roots[i]; 182 iRoot *root = d->base.roots[i];
183 if (root) { 183 if (root) {
184 iRect *rect = &root->widget->rect; 184 iRect *rect = &root->widget->rect;
185 /* Horizontal split frame. */ 185 /* Horizontal split frame. */
@@ -199,26 +199,26 @@ static void windowSizeChanged_Window_(iWindow *d) {
199 } 199 }
200} 200}
201 201
202static void setupUserInterface_Window(iWindow *d) { 202static void setupUserInterface_MainWindow(iMainWindow *d) {
203#if defined (iHaveNativeMenus) 203#if defined (iHaveNativeMenus)
204 insertMacMenus_(); 204 insertMacMenus_();
205#endif 205#endif
206 /* One root is created by default. */ 206 /* One root is created by default. */
207 d->roots[0] = new_Root(); 207 d->base.roots[0] = new_Root();
208 setCurrent_Root(d->roots[0]); 208 setCurrent_Root(d->base.roots[0]);
209 createUserInterface_Root(d->roots[0]); 209 createUserInterface_Root(d->base.roots[0]);
210 setCurrent_Root(NULL); 210 setCurrent_Root(NULL);
211 /* One of the roots always has keyboard input focus. */ 211 /* One of the roots always has keyboard input focus. */
212 d->keyRoot = d->roots[0]; 212 d->base.keyRoot = d->base.roots[0];
213} 213}
214 214
215static void updateSize_Window_(iWindow *d, iBool notifyAlways) { 215static void updateSize_MainWindow_(iMainWindow *d, iBool notifyAlways) {
216 iInt2 *size = &d->size; 216 iInt2 *size = &d->base.size;
217 const iInt2 oldSize = *size; 217 const iInt2 oldSize = *size;
218 SDL_GetRendererOutputSize(d->render, &size->x, &size->y); 218 SDL_GetRendererOutputSize(d->base.render, &size->x, &size->y);
219 size->y -= d->keyboardHeight; 219 size->y -= d->keyboardHeight;
220 if (notifyAlways || !isEqual_I2(oldSize, *size)) { 220 if (notifyAlways || !isEqual_I2(oldSize, *size)) {
221 windowSizeChanged_Window_(d); 221 windowSizeChanged_MainWindow_(d);
222 if (!isEqual_I2(*size, d->place.lastNotifiedSize)) { 222 if (!isEqual_I2(*size, d->place.lastNotifiedSize)) {
223 const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); 223 const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x);
224 const iBool isVert = (d->place.lastNotifiedSize.y != size->y); 224 const iBool isVert = (d->place.lastNotifiedSize.y != size->y);
@@ -234,8 +234,8 @@ static void updateSize_Window_(iWindow *d, iBool notifyAlways) {
234 } 234 }
235} 235}
236 236
237void drawWhileResizing_Window(iWindow *d, int w, int h) { 237void drawWhileResizing_MainWindow(iMainWindow *d, int w, int h) {
238 draw_Window(d); 238 draw_MainWindow(d);
239} 239}
240 240
241static float pixelRatio_Window_(const iWindow *d) { 241static float pixelRatio_Window_(const iWindow *d) {
@@ -308,7 +308,7 @@ static iRoot *rootAt_Window_(const iWindow *d, iInt2 coord) {
308} 308}
309 309
310#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 310#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
311static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) { 311static SDL_HitTestResult hitTest_MainWindow_(SDL_Window *win, const SDL_Point *pos, void *data) {
312 iWindow *d = data; 312 iWindow *d = data;
313 iAssert(d->win == win); 313 iAssert(d->win == win);
314 if (SDL_GetWindowFlags(d->win) & (SDL_WINDOW_MOUSE_CAPTURE | SDL_WINDOW_FULLSCREEN_DESKTOP)) { 314 if (SDL_GetWindowFlags(d->win) & (SDL_WINDOW_MOUSE_CAPTURE | SDL_WINDOW_FULLSCREEN_DESKTOP)) {
@@ -361,19 +361,22 @@ SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos) {
361#endif 361#endif
362 362
363iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) { 363iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) {
364 flags |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; 364 flags |= SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN;
365 if (d->type == main_WindowType) {
366 flags |= SDL_WINDOW_RESIZABLE;
365#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 367#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
366 if (prefs_App()->customFrame) { 368 if (prefs_App()->customFrame) {
367 /* We are drawing a custom frame so hide the default one. */ 369 /* We are drawing a custom frame so hide the default one. */
368 flags |= SDL_WINDOW_BORDERLESS; 370 flags |= SDL_WINDOW_BORDERLESS;
369 } 371 }
370#endif 372#endif
373 }
371 if (SDL_CreateWindowAndRenderer( 374 if (SDL_CreateWindowAndRenderer(
372 width_Rect(rect), height_Rect(rect), flags, &d->win, &d->render)) { 375 width_Rect(rect), height_Rect(rect), flags, &d->win, &d->render)) {
373 return iFalse; 376 return iFalse;
374 } 377 }
375#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 378#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
376 if (prefs_App()->customFrame) { 379 if (type_Window(d) == main_WindowType && prefs_App()->customFrame) {
377 /* Register a handler for window hit testing (drag, resize). */ 380 /* Register a handler for window hit testing (drag, resize). */
378 SDL_SetWindowHitTest(d->win, hitTest_Window_, d); 381 SDL_SetWindowHitTest(d->win, hitTest_Window_, d);
379 SDL_SetWindowResizable(d->win, SDL_TRUE); 382 SDL_SetWindowResizable(d->win, SDL_TRUE);
@@ -397,40 +400,28 @@ static SDL_Surface *loadImage_(const iBlock *data, int resized) {
397 pixels, w, h, 8 * num, w * num, SDL_PIXELFORMAT_RGBA32); 400 pixels, w, h, 8 * num, w * num, SDL_PIXELFORMAT_RGBA32);
398} 401}
399 402
400void init_Window(iWindow *d, iRect rect) { 403void init_Window(iWindow *d, enum iWindowType type, iRect rect, uint32_t flags) {
401 theWindow_ = d; 404 d->type = type;
402 d->win = NULL; 405 d->win = NULL;
403 d->size = zero_I2(); /* will be updated below */ 406 d->size = zero_I2(); /* will be updated below */
404 iZap(d->roots); 407 d->hover = NULL;
405 d->splitMode = d->pendingSplitMode = 0; 408 d->lastHover = NULL;
406 d->pendingSplitUrl = new_String(); 409 d->mouseGrab = NULL;
407 d->hover = NULL; 410 d->focus = NULL;
408 d->lastHover = NULL;
409 d->mouseGrab = NULL;
410 d->focus = NULL;
411 iZap(d->cursors);
412 d->place.initialPos = rect.pos;
413 d->place.normalRect = rect;
414 d->place.lastNotifiedSize = zero_I2();
415 d->place.snap = 0;
416 d->pendingCursor = NULL; 411 d->pendingCursor = NULL;
417 d->isDrawFrozen = iTrue; 412 d->isDrawFrozen = iTrue;
418 d->isExposed = iFalse; 413 d->isExposed = iFalse;
419 d->isMinimized = iFalse; 414 d->isMinimized = iFalse;
420 d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */ 415 d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */
421 d->isMouseInside = iTrue; 416 d->isMouseInside = iTrue;
422 d->ignoreClick = iFalse; 417 d->ignoreClick = iFalse;
423 d->focusGainedAt = 0; 418 d->focusGainedAt = 0;
424 d->keyboardHeight = 0; 419 d->presentTime = 0.0;
425 uint32_t flags = 0; 420 d->frameTime = SDL_GetTicks();
426#if defined (iPlatformAppleDesktop) 421 d->keyRoot = NULL;
427 SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); 422 d->borderShadow = NULL;
428#elif defined (iPlatformAppleMobile) 423 iZap(d->roots);
429 SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); 424 iZap(d->cursors);
430#else
431 flags |= SDL_WINDOW_OPENGL;
432#endif
433 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
434 /* First try SDL's default renderer that should be the best option. */ 425 /* First try SDL's default renderer that should be the best option. */
435 if (forceSoftwareRender_App() || !create_Window_(d, rect, flags)) { 426 if (forceSoftwareRender_App() || !create_Window_(d, rect, flags)) {
436 /* No luck, maybe software only? This should always work as long as there is a display. */ 427 /* No luck, maybe software only? This should always work as long as there is a display. */
@@ -443,40 +434,74 @@ void init_Window(iWindow *d, iRect rect) {
443 if (left_Rect(rect) >= 0 || top_Rect(rect) >= 0) { 434 if (left_Rect(rect) >= 0 || top_Rect(rect) >= 0) {
444 SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect)); 435 SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect));
445 } 436 }
446#if defined (iPlatformMobile) 437 SDL_GetRendererOutputSize(d->render, &d->size.x, &d->size.y);
438 drawBlank_Window_(d);
439 d->pixelRatio = pixelRatio_Window_(d); /* point/pixel conversion */
440 d->displayScale = displayScale_Window_(d);
441 d->uiScale = initialUiScale_;
442 /* TODO: Ratios, scales, and metrics must be window-specific, not global. */
443 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale);
444}
445
446void deinit_Window(iWindow *d) {
447 SDL_DestroyRenderer(d->render);
448 SDL_DestroyWindow(d->win);
449 iForIndices(i, d->cursors) {
450 if (d->cursors[i]) {
451 SDL_FreeCursor(d->cursors[i]);
452 }
453 }
454}
455
456void init_MainWindow(iMainWindow *d, iRect rect) {
457 theWindow_ = &d->base;
458 uint32_t flags = 0;
459#if defined (iPlatformAppleDesktop)
460 SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl");
461#elif defined (iPlatformAppleMobile)
462 SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
463#else
464 flags |= SDL_WINDOW_OPENGL;
465#endif
466 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
467 init_Window(&d->base, main_WindowType, rect, flags);
468 d->splitMode = d->pendingSplitMode = 0;
469 d->pendingSplitUrl = new_String();
470 d->place.initialPos = rect.pos;
471 d->place.normalRect = rect;
472 d->place.lastNotifiedSize = zero_I2();
473 d->place.snap = 0;
474 d->keyboardHeight = 0;
475#if defined(iPlatformMobile)
447 const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */ 476 const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */
448#else 477#else
449 const iInt2 minSize = init_I2(425, 325); 478 const iInt2 minSize = init_I2(425, 325);
450#endif 479#endif
451 SDL_SetWindowMinimumSize(d->win, minSize.x, minSize.y); 480 SDL_SetWindowMinimumSize(d->base.win, minSize.x, minSize.y);
452 SDL_SetWindowTitle(d->win, "Lagrange"); 481 SDL_SetWindowTitle(d->base.win, "Lagrange");
453 /* Some info. */ { 482 /* Some info. */ {
454 SDL_RendererInfo info; 483 SDL_RendererInfo info;
455 SDL_GetRendererInfo(d->render, &info); 484 SDL_GetRendererInfo(d->base.render, &info);
456 isOpenGLRenderer_ = !iCmpStr(info.name, "opengl"); 485 isOpenGLRenderer_ = !iCmpStr(info.name, "opengl");
457 printf("[window] renderer: %s%s\n", info.name, 486 printf("[window] renderer: %s%s\n",
487 info.name,
458 info.flags & SDL_RENDERER_ACCELERATED ? " (accelerated)" : ""); 488 info.flags & SDL_RENDERER_ACCELERATED ? " (accelerated)" : "");
459#if !defined (NDEBUG) 489#if !defined(NDEBUG)
460 printf("[window] max texture size: %d x %d\n", 490 printf("[window] max texture size: %d x %d\n",
461 info.max_texture_width, 491 info.max_texture_width,
462 info.max_texture_height); 492 info.max_texture_height);
463 for (size_t i = 0; i < info.num_texture_formats; ++i) { 493 for (size_t i = 0; i < info.num_texture_formats; ++i) {
464 printf("[window] supported texture format: %s\n", SDL_GetPixelFormatName( 494 printf("[window] supported texture format: %s\n",
465 info.texture_formats[i])); 495 SDL_GetPixelFormatName(info.texture_formats[i]));
466 } 496 }
467#endif 497#endif
468 } 498 }
469 drawBlank_Window_(d); 499#if defined(iPlatformMsys)
470 d->pixelRatio = pixelRatio_Window_(d); /* point/pixel conversion */ 500 SDL_SetWindowMinimumSize(d->win, minSize.x * d->base.displayScale, minSize.y * d->base.displayScale);
471 d->displayScale = displayScale_Window_(d); 501 useExecutableIconResource_SDLWindow(d->base.win);
472 d->uiScale = initialUiScale_;
473 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale);
474#if defined (iPlatformMsys)
475 SDL_SetWindowMinimumSize(d->win, minSize.x * d->displayScale, minSize.y * d->displayScale);
476 useExecutableIconResource_SDLWindow(d->win);
477#endif 502#endif
478#if defined (iPlatformLinux) 503#if defined (iPlatformLinux)
479 SDL_SetWindowMinimumSize(d->win, minSize.x * d->pixelRatio, minSize.y * d->pixelRatio); 504 SDL_SetWindowMinimumSize(d->win, minSize.x * d->base.pixelRatio, minSize.y * d->base.pixelRatio);
480 /* Load the window icon. */ { 505 /* Load the window icon. */ {
481 SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, 0); 506 SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, 0);
482 SDL_SetWindowIcon(d->win, surf); 507 SDL_SetWindowIcon(d->win, surf);
@@ -487,18 +512,14 @@ void init_Window(iWindow *d, iRect rect) {
487#if defined (iPlatformAppleMobile) 512#if defined (iPlatformAppleMobile)
488 setupWindow_iOS(d); 513 setupWindow_iOS(d);
489#endif 514#endif
490 d->presentTime = 0.0; 515 init_Text(d->base.render);
491 d->frameTime = SDL_GetTicks(); 516 SDL_GetRendererOutputSize(d->base.render, &d->base.size.x, &d->base.size.y);
492 d->loadAnimTimer = 0; 517 setupUserInterface_MainWindow(d);
493 init_Text(d->render);
494 SDL_GetRendererOutputSize(d->render, &d->size.x, &d->size.y);
495 setupUserInterface_Window(d);
496 postCommand_App("~bindings.changed"); /* update from bindings */ 518 postCommand_App("~bindings.changed"); /* update from bindings */
497 //updateSize_Window_(d, iFalse);
498 /* Load the border shadow texture. */ { 519 /* Load the border shadow texture. */ {
499 SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0); 520 SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0);
500 d->borderShadow = SDL_CreateTextureFromSurface(d->render, surf); 521 d->base.borderShadow = SDL_CreateTextureFromSurface(d->base.render, surf);
501 SDL_SetTextureBlendMode(d->borderShadow, SDL_BLENDMODE_BLEND); 522 SDL_SetTextureBlendMode(d->base.borderShadow, SDL_BLENDMODE_BLEND);
502 free(surf->pixels); 523 free(surf->pixels);
503 SDL_FreeSurface(surf); 524 SDL_FreeSurface(surf);
504 } 525 }
@@ -508,7 +529,7 @@ void init_Window(iWindow *d, iRect rect) {
508 if (prefs_App()->customFrame) { 529 if (prefs_App()->customFrame) {
509 SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, appIconSize_Root()); 530 SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, appIconSize_Root());
510 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); 531 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
511 d->appIcon = SDL_CreateTextureFromSurface(d->render, surf); 532 d->appIcon = SDL_CreateTextureFromSurface(d->base.render, surf);
512 free(surf->pixels); 533 free(surf->pixels);
513 SDL_FreeSurface(surf); 534 SDL_FreeSurface(surf);
514 /* We need to observe non-client-area events. */ 535 /* We need to observe non-client-area events. */
@@ -517,7 +538,7 @@ void init_Window(iWindow *d, iRect rect) {
517#endif 538#endif
518} 539}
519 540
520void deinit_Window(iWindow *d) { 541static void deinitRoots_Window_(iWindow *d) {
521 iRecycle(); 542 iRecycle();
522 iForIndices(i, d->roots) { 543 iForIndices(i, d->roots) {
523 if (d->roots[i]) { 544 if (d->roots[i]) {
@@ -525,19 +546,17 @@ void deinit_Window(iWindow *d) {
525 deinit_Root(d->roots[i]); 546 deinit_Root(d->roots[i]);
526 } 547 }
527 } 548 }
528 if (theWindow_ == d) { 549 setCurrent_Root(NULL);
550}
551
552void deinit_MainWindow(iMainWindow *d) {
553 deinitRoots_Window_(as_Window(d));
554 if (theWindow_ == as_Window(d)) {
529 theWindow_ = NULL; 555 theWindow_ = NULL;
530 } 556 }
531 setCurrent_Root(NULL);
532 delete_String(d->pendingSplitUrl); 557 delete_String(d->pendingSplitUrl);
533 deinit_Text(); 558 deinit_Text();
534 SDL_DestroyRenderer(d->render); 559 deinit_Window(&d->base);
535 SDL_DestroyWindow(d->win);
536 iForIndices(i, d->cursors) {
537 if (d->cursors[i]) {
538 SDL_FreeCursor(d->cursors[i]);
539 }
540 }
541} 560}
542 561
543SDL_Renderer *renderer_Window(const iWindow *d) { 562SDL_Renderer *renderer_Window(const iWindow *d) {
@@ -550,8 +569,8 @@ iInt2 maxTextureSize_Window(const iWindow *d) {
550 return init_I2(info.max_texture_width, info.max_texture_height); 569 return init_I2(info.max_texture_width, info.max_texture_height);
551} 570}
552 571
553iBool isFullscreen_Window(const iWindow *d) { 572iBool isFullscreen_MainWindow(const iMainWindow *d) {
554 return snap_Window(d) == fullscreen_WindowSnap; 573 return snap_MainWindow(d) == fullscreen_WindowSnap;
555} 574}
556 575
557iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { 576iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) {
@@ -570,36 +589,41 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) {
570 return root == d->roots[0] && d->roots[1] ? d->roots[1] : d->roots[0]; 589 return root == d->roots[0] && d->roots[1] ? d->roots[1] : d->roots[0];
571} 590}
572 591
573static void invalidate_Window_(iWindow *d, iBool forced) { 592static void invalidate_MainWindow_(iMainWindow *d, iBool forced) {
574 if (d && (!d->isInvalidated || forced)) { 593 if (d && (!d->base.isInvalidated || forced)) {
575 d->isInvalidated = iTrue; 594 d->base.isInvalidated = iTrue;
576 resetFonts_Text(); 595 resetFonts_Text();
577 postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ 596 postCommand_App("theme.changed auto:1"); /* forces UI invalidation */
578 } 597 }
579} 598}
580 599
581void invalidate_Window(iWindow *d) { 600void invalidate_Window(iAnyWindow *d) {
582 invalidate_Window_(d, iFalse); 601 if (type_Window(d) == main_WindowType) {
602 invalidate_MainWindow_(as_MainWindow(d), iFalse);
603 }
604 else {
605 iAssert(type_Window(d) == main_WindowType);
606 }
583} 607}
584 608
585static iBool isNormalPlacement_Window_(const iWindow *d) { 609static iBool isNormalPlacement_MainWindow_(const iMainWindow *d) {
586 if (d->isDrawFrozen) return iFalse; 610 if (d->base.isDrawFrozen) return iFalse;
587#if defined (iPlatformApple) 611#if defined (iPlatformApple)
588 /* Maximized mode is not special on macOS. */ 612 /* Maximized mode is not special on macOS. */
589 if (snap_Window(d) == maximized_WindowSnap) { 613 if (snap_MainWindow(d) == maximized_WindowSnap) {
590 return iTrue; 614 return iTrue;
591 } 615 }
592#endif 616#endif
593 if (snap_Window(d)) return iFalse; 617 if (snap_MainWindow(d)) return iFalse;
594 return !(SDL_GetWindowFlags(d->win) & SDL_WINDOW_MINIMIZED); 618 return !(SDL_GetWindowFlags(d->base.win) & SDL_WINDOW_MINIMIZED);
595} 619}
596 620
597static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) { 621static iBool unsnap_MainWindow_(iMainWindow *d, const iInt2 *newPos) {
598#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 622#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
599 if (!prefs_App()->customFrame) { 623 if (!prefs_App()->customFrame) {
600 return iFalse; 624 return iFalse;
601 } 625 }
602 const int snap = snap_Window(d); 626 const int snap = snap_MainWindow(d);
603 if (snap == yMaximized_WindowSnap || snap == left_WindowSnap || snap == right_WindowSnap) { 627 if (snap == yMaximized_WindowSnap || snap == left_WindowSnap || snap == right_WindowSnap) {
604 if (!newPos || (d->place.lastHit == SDL_HITTEST_RESIZE_LEFT || 628 if (!newPos || (d->place.lastHit == SDL_HITTEST_RESIZE_LEFT ||
605 d->place.lastHit == SDL_HITTEST_RESIZE_RIGHT)) { 629 d->place.lastHit == SDL_HITTEST_RESIZE_RIGHT)) {
@@ -607,21 +631,21 @@ static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) {
607 } 631 }
608 if (newPos) { 632 if (newPos) {
609 SDL_Rect usable; 633 SDL_Rect usable;
610 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable); 634 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->base.win), &usable);
611 /* Snap to top. */ 635 /* Snap to top. */
612 if (snap == yMaximized_WindowSnap && 636 if (snap == yMaximized_WindowSnap &&
613 iAbs(newPos->y - usable.y) < lineHeight_Text(uiContent_FontId) * 2) { 637 iAbs(newPos->y - usable.y) < lineHeight_Text(uiContent_FontId) * 2) {
614 setSnap_Window(d, redo_WindowSnap | yMaximized_WindowSnap); 638 setSnap_MainWindow(d, redo_WindowSnap | yMaximized_WindowSnap);
615 return iFalse; 639 return iFalse;
616 } 640 }
617 } 641 }
618 } 642 }
619 if (snap && snap != fullscreen_WindowSnap) { 643 if (snap && snap != fullscreen_WindowSnap) {
620 if (snap_Window(d) == yMaximized_WindowSnap && newPos) { 644 if (snap_MainWindow(d) == yMaximized_WindowSnap && newPos) {
621 d->place.normalRect.pos = *newPos; 645 d->place.normalRect.pos = *newPos;
622 } 646 }
623 //printf("unsnap\n"); fflush(stdout); 647 //printf("unsnap\n"); fflush(stdout);
624 setSnap_Window(d, none_WindowSnap); 648 setSnap_MainWindow(d, none_WindowSnap);
625 return iTrue; 649 return iTrue;
626 } 650 }
627#endif 651#endif
@@ -652,109 +676,110 @@ static void checkPixelRatioChange_Window_(iWindow *d) {
652 } 676 }
653} 677}
654 678
655static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { 679static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) {
656 switch (ev->event) { 680 switch (ev->event) {
657#if defined (iPlatformDesktop) 681#if defined(iPlatformDesktop)
658 case SDL_WINDOWEVENT_EXPOSED: 682 case SDL_WINDOWEVENT_EXPOSED:
659 if (!d->isExposed) { 683 if (!d->base.isExposed) {
660 drawBlank_Window_(d); /* avoid showing system-provided contents */ 684 drawBlank_Window_(as_Window(d)); /* avoid showing system-provided contents */
661 d->isExposed = iTrue; 685 d->base.isExposed = iTrue;
662 } 686 }
663 /* Since we are manually controlling when to redraw the window, we are responsible 687 /* Since we are manually controlling when to redraw the window, we are responsible
664 for ensuring that window contents get redrawn after expose events. Under certain 688 for ensuring that window contents get redrawn after expose events. Under certain
665 circumstances (e.g., under openbox), not doing this would mean that the window 689 circumstances (e.g., under openbox), not doing this would mean that the window
666 is missing contents until other events trigger a refresh. */ 690 is missing contents until other events trigger a refresh. */
667 postRefresh_App(); 691 postRefresh_App();
668#if defined (LAGRANGE_ENABLE_WINDOWPOS_FIX) 692#if defined(LAGRANGE_ENABLE_WINDOWPOS_FIX)
669 if (d->place.initialPos.x >= 0) { 693 if (d->place.initialPos.x >= 0) {
670 int bx, by; 694 int bx, by;
671 SDL_GetWindowBordersSize(d->win, &by, &bx, NULL, NULL); 695 SDL_GetWindowBordersSize(d->win, &by, &bx, NULL, NULL);
672 SDL_SetWindowPosition(d->win, d->place.initialPos.x + bx, d->place.initialPos.y + by); 696 SDL_SetWindowPosition(
697 d->win, d->place.initialPos.x + bx, d->place.initialPos.y + by);
673 d->place.initialPos = init1_I2(-1); 698 d->place.initialPos = init1_I2(-1);
674 } 699 }
675#endif 700#endif
676 return iFalse; 701 return iFalse;
677 case SDL_WINDOWEVENT_MOVED: { 702 case SDL_WINDOWEVENT_MOVED: {
678 if (d->isMinimized) { 703 if (d->base.isMinimized) {
679 return iFalse; 704 return iFalse;
680 } 705 }
681 checkPixelRatioChange_Window_(d); 706 checkPixelRatioChange_Window_(as_Window(d));
682 const iInt2 newPos = init_I2(ev->data1, ev->data2); 707 const iInt2 newPos = init_I2(ev->data1, ev->data2);
683 if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ 708 if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */
684 /* Maybe minimized? Seems like a Windows constant of some kind. */ 709 /* Maybe minimized? Seems like a Windows constant of some kind. */
685 d->isMinimized = iTrue; 710 d->base.isMinimized = iTrue;
686 return iFalse; 711 return iFalse;
687 } 712 }
688#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 713#if defined(LAGRANGE_ENABLE_CUSTOM_FRAME)
689 /* Set the snap position depending on where the mouse cursor is. */ 714 /* Set the snap position depending on where the mouse cursor is. */
690 if (prefs_App()->customFrame) { 715 if (prefs_App()->customFrame) {
691 SDL_Rect usable; 716 SDL_Rect usable;
692 iInt2 mouse = cursor_Win32(); /* SDL is unaware of the current cursor pos */ 717 iInt2 mouse = cursor_Win32(); /* SDL is unaware of the current cursor pos */
693 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable); 718 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->base.win), &usable);
694 const iBool isTop = iAbs(mouse.y - usable.y) < gap_UI * 20; 719 const iBool isTop = iAbs(mouse.y - usable.y) < gap_UI * 20;
695 const iBool isBottom = iAbs(usable.y + usable.h - mouse.y) < gap_UI * 20; 720 const iBool isBottom = iAbs(usable.y + usable.h - mouse.y) < gap_UI * 20;
696 if (iAbs(mouse.x - usable.x) < gap_UI) { 721 if (iAbs(mouse.x - usable.x) < gap_UI) {
697 setSnap_Window(d, 722 setSnap_MainWindow(d,
698 redo_WindowSnap | left_WindowSnap | 723 redo_WindowSnap | left_WindowSnap |
699 (isTop ? topBit_WindowSnap : 0) | 724 (isTop ? topBit_WindowSnap : 0) |
700 (isBottom ? bottomBit_WindowSnap : 0)); 725 (isBottom ? bottomBit_WindowSnap : 0));
701 return iTrue; 726 return iTrue;
702 } 727 }
703 if (iAbs(mouse.x - usable.x - usable.w) < gap_UI) { 728 if (iAbs(mouse.x - usable.x - usable.w) < gap_UI) {
704 setSnap_Window(d, 729 setSnap_MainWindow(d,
705 redo_WindowSnap | right_WindowSnap | 730 redo_WindowSnap | right_WindowSnap |
706 (isTop ? topBit_WindowSnap : 0) | 731 (isTop ? topBit_WindowSnap : 0) |
707 (isBottom ? bottomBit_WindowSnap : 0)); 732 (isBottom ? bottomBit_WindowSnap : 0));
708 return iTrue; 733 return iTrue;
709 } 734 }
710 if (iAbs(mouse.y - usable.y) < 2) { 735 if (iAbs(mouse.y - usable.y) < 2) {
711 setSnap_Window(d, 736 setSnap_MainWindow(d,
712 redo_WindowSnap | (d->place.lastHit == SDL_HITTEST_RESIZE_TOP 737 redo_WindowSnap | (d->place.lastHit == SDL_HITTEST_RESIZE_TOP
713 ? yMaximized_WindowSnap 738 ? yMaximized_WindowSnap
714 : maximized_WindowSnap)); 739 : maximized_WindowSnap));
715 return iTrue; 740 return iTrue;
716 } 741 }
717 } 742 }
718#endif /* defined LAGRANGE_ENABLE_CUSTOM_FRAME */ 743#endif /* defined LAGRANGE_ENABLE_CUSTOM_FRAME */
719 if (unsnap_Window_(d, &newPos)) { 744 if (unsnap_MainWindow_(d, &newPos)) {
720 return iTrue; 745 return iTrue;
721 } 746 }
722 if (isNormalPlacement_Window_(d)) { 747 if (isNormalPlacement_MainWindow_(d)) {
723 d->place.normalRect.pos = newPos; 748 d->place.normalRect.pos = newPos;
724 //printf("normal rect set (move)\n"); fflush(stdout); 749 // printf("normal rect set (move)\n"); fflush(stdout);
725 iInt2 border = zero_I2(); 750 iInt2 border = zero_I2();
726#if !defined (iPlatformApple) 751#if !defined(iPlatformApple)
727 SDL_GetWindowBordersSize(d->win, &border.y, &border.x, NULL, NULL); 752 SDL_GetWindowBordersSize(d->win, &border.y, &border.x, NULL, NULL);
728#endif 753#endif
729 d->place.normalRect.pos = max_I2(zero_I2(), sub_I2(d->place.normalRect.pos, border)); 754 d->place.normalRect.pos =
755 max_I2(zero_I2(), sub_I2(d->place.normalRect.pos, border));
730 } 756 }
731 return iTrue; 757 return iTrue;
732 } 758 }
733 case SDL_WINDOWEVENT_RESIZED: 759 case SDL_WINDOWEVENT_RESIZED:
734 if (d->isMinimized) { 760 if (d->base.isMinimized) {
735 //updateSize_Window_(d, iTrue); 761 // updateSize_Window_(d, iTrue);
736 return iTrue; 762 return iTrue;
737 } 763 }
738 if (unsnap_Window_(d, NULL)) { 764 if (unsnap_MainWindow_(d, NULL)) {
739 return iTrue; 765 return iTrue;
740 } 766 }
741 if (isNormalPlacement_Window_(d)) { 767 if (isNormalPlacement_MainWindow_(d)) {
742 d->place.normalRect.size = init_I2(ev->data1, ev->data2); 768 d->place.normalRect.size = init_I2(ev->data1, ev->data2);
743 //printf("normal rect set (resize)\n"); fflush(stdout); 769 // printf("normal rect set (resize)\n"); fflush(stdout);
744 } 770 }
745 checkPixelRatioChange_Window_(d); 771 checkPixelRatioChange_Window_(as_Window(d));
746 //updateSize_Window_(d, iTrue /* we were already redrawing during the resize */);
747 postRefresh_App(); 772 postRefresh_App();
748 return iTrue; 773 return iTrue;
749 case SDL_WINDOWEVENT_RESTORED: 774 case SDL_WINDOWEVENT_RESTORED:
750 case SDL_WINDOWEVENT_SHOWN: 775 case SDL_WINDOWEVENT_SHOWN:
751 updateSize_Window_(d, iTrue); 776 updateSize_MainWindow_(d, iTrue);
752 invalidate_Window_(d, iTrue); 777 invalidate_MainWindow_(d, iTrue);
753 d->isMinimized = iFalse; 778 d->base.isMinimized = iFalse;
754 postRefresh_App(); 779 postRefresh_App();
755 return iTrue; 780 return iTrue;
756 case SDL_WINDOWEVENT_MINIMIZED: 781 case SDL_WINDOWEVENT_MINIMIZED:
757 d->isMinimized = iTrue; 782 d->base.isMinimized = iTrue;
758 return iTrue; 783 return iTrue;
759#else /* if defined (!iPlatformDesktop) */ 784#else /* if defined (!iPlatformDesktop) */
760 case SDL_WINDOWEVENT_RESIZED: 785 case SDL_WINDOWEVENT_RESIZED:
@@ -765,19 +790,19 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
765#endif 790#endif
766 case SDL_WINDOWEVENT_LEAVE: 791 case SDL_WINDOWEVENT_LEAVE:
767 unhover_Widget(); 792 unhover_Widget();
768 d->isMouseInside = iFalse; 793 d->base.isMouseInside = iFalse;
769 postCommand_App("window.mouse.exited"); 794 postCommand_App("window.mouse.exited");
770 return iTrue; 795 return iTrue;
771 case SDL_WINDOWEVENT_ENTER: 796 case SDL_WINDOWEVENT_ENTER:
772 d->isMouseInside = iTrue; 797 d->base.isMouseInside = iTrue;
773 postCommand_App("window.mouse.entered"); 798 postCommand_App("window.mouse.entered");
774 return iTrue; 799 return iTrue;
775 case SDL_WINDOWEVENT_FOCUS_GAINED: 800 case SDL_WINDOWEVENT_FOCUS_GAINED:
776 d->focusGainedAt = SDL_GetTicks(); 801 d->base.focusGainedAt = SDL_GetTicks();
777 setCapsLockDown_Keys(iFalse); 802 setCapsLockDown_Keys(iFalse);
778 postCommand_App("window.focus.gained"); 803 postCommand_App("window.focus.gained");
779 d->isExposed = iTrue; 804 d->base.isExposed = iTrue;
780#if !defined (iPlatformDesktop) 805#if !defined(iPlatformDesktop)
781 /* Returned to foreground, may have lost buffered content. */ 806 /* Returned to foreground, may have lost buffered content. */
782 invalidate_Window_(d, iTrue); 807 invalidate_Window_(d, iTrue);
783 postCommand_App("window.unfreeze"); 808 postCommand_App("window.unfreeze");
@@ -785,12 +810,12 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
785 return iFalse; 810 return iFalse;
786 case SDL_WINDOWEVENT_FOCUS_LOST: 811 case SDL_WINDOWEVENT_FOCUS_LOST:
787 postCommand_App("window.focus.lost"); 812 postCommand_App("window.focus.lost");
788#if !defined (iPlatformDesktop) 813#if !defined(iPlatformDesktop)
789 setFreezeDraw_Window(d, iTrue); 814 setFreezeDraw_Window(d, iTrue);
790#endif 815#endif
791 return iFalse; 816 return iFalse;
792 case SDL_WINDOWEVENT_TAKE_FOCUS: 817 case SDL_WINDOWEVENT_TAKE_FOCUS:
793 SDL_SetWindowInputFocus(d->win); 818 SDL_SetWindowInputFocus(d->base.win);
794 postRefresh_App(); 819 postRefresh_App();
795 return iTrue; 820 return iTrue;
796 default: 821 default:
@@ -806,7 +831,8 @@ static void applyCursor_Window_(iWindow *d) {
806 } 831 }
807} 832}
808 833
809iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { 834iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {
835 iWindow *w = as_Window(d);
810 switch (ev->type) { 836 switch (ev->type) {
811#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 837#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
812 case SDL_SYSWMEVENT: { 838 case SDL_SYSWMEVENT: {
@@ -819,19 +845,19 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
819 } 845 }
820#endif 846#endif
821 case SDL_WINDOWEVENT: { 847 case SDL_WINDOWEVENT: {
822 return handleWindowEvent_Window_(d, &ev->window); 848 return handleWindowEvent_MainWindow_(d, &ev->window);
823 } 849 }
824 case SDL_RENDER_TARGETS_RESET: 850 case SDL_RENDER_TARGETS_RESET:
825 case SDL_RENDER_DEVICE_RESET: { 851 case SDL_RENDER_DEVICE_RESET: {
826 invalidate_Window_(d, iTrue /* force full reset */); 852 invalidate_MainWindow_(d, iTrue /* force full reset */);
827 break; 853 break;
828 } 854 }
829 default: { 855 default: {
830 SDL_Event event = *ev; 856 SDL_Event event = *ev;
831 if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze")) { 857 if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze")) {
832 d->isDrawFrozen = iFalse; 858 d->base.isDrawFrozen = iFalse;
833 if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_HIDDEN) { 859 if (SDL_GetWindowFlags(w->win) & SDL_WINDOW_HIDDEN) {
834 SDL_ShowWindow(d->win); 860 SDL_ShowWindow(w->win);
835 } 861 }
836 postRefresh_App(); 862 postRefresh_App();
837 postCommand_App("media.player.update"); /* in case a player needs updating */ 863 postCommand_App("media.player.update"); /* in case a player needs updating */
@@ -840,35 +866,35 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
840 if (processEvent_Touch(&event)) { 866 if (processEvent_Touch(&event)) {
841 return iTrue; 867 return iTrue;
842 } 868 }
843 if (event.type == SDL_KEYDOWN && SDL_GetTicks() - d->focusGainedAt < 10) { 869 if (event.type == SDL_KEYDOWN && SDL_GetTicks() - d->base.focusGainedAt < 10) {
844 /* Suspiciously close to when input focus was received. For example under openbox, 870 /* Suspiciously close to when input focus was received. For example under openbox,
845 closing xterm with Ctrl+D will cause the keydown event to "spill" over to us. 871 closing xterm with Ctrl+D will cause the keydown event to "spill" over to us.
846 As a workaround, ignore these events. */ 872 As a workaround, ignore these events. */
847 return iTrue; /* won't go to bindings, either */ 873 return iTrue; /* won't go to bindings, either */
848 } 874 }
849 if (event.type == SDL_MOUSEBUTTONDOWN && d->ignoreClick) { 875 if (event.type == SDL_MOUSEBUTTONDOWN && d->base.ignoreClick) {
850 d->ignoreClick = iFalse; 876 d->base.ignoreClick = iFalse;
851 return iTrue; 877 return iTrue;
852 } 878 }
853 /* Map mouse pointer coordinate to our coordinate system. */ 879 /* Map mouse pointer coordinate to our coordinate system. */
854 if (event.type == SDL_MOUSEMOTION) { 880 if (event.type == SDL_MOUSEMOTION) {
855 setCursor_Window(d, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */ 881 setCursor_Window(w, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */
856 const iInt2 pos = coord_Window(d, event.motion.x, event.motion.y); 882 const iInt2 pos = coord_Window(w, event.motion.x, event.motion.y);
857 event.motion.x = pos.x; 883 event.motion.x = pos.x;
858 event.motion.y = pos.y; 884 event.motion.y = pos.y;
859 } 885 }
860 else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { 886 else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
861 const iInt2 pos = coord_Window(d, event.button.x, event.button.y); 887 const iInt2 pos = coord_Window(w, event.button.x, event.button.y);
862 event.button.x = pos.x; 888 event.button.x = pos.x;
863 event.button.y = pos.y; 889 event.button.y = pos.y;
864 if (event.type == SDL_MOUSEBUTTONDOWN) { 890 if (event.type == SDL_MOUSEBUTTONDOWN) {
865 /* Button clicks will change keyroot. */ 891 /* Button clicks will change keyroot. */
866 if (numRoots_Window(d) > 1) { 892 if (numRoots_Window(w) > 1) {
867 const iInt2 click = init_I2(event.button.x, event.button.y); 893 const iInt2 click = init_I2(event.button.x, event.button.y);
868 iForIndices(i, d->roots) { 894 iForIndices(i, w->roots) {
869 iRoot *root = d->roots[i]; 895 iRoot *root = w->roots[i];
870 if (root != d->keyRoot && contains_Rect(rect_Root(root), click)) { 896 if (root != w->keyRoot && contains_Rect(rect_Root(root), click)) {
871 setKeyRoot_Window(d, root); 897 setKeyRoot_Window(w, root);
872 break; 898 break;
873 } 899 }
874 } 900 }
@@ -883,13 +909,13 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
883 event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { 909 event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
884 if (mouseGrab_Widget()) { 910 if (mouseGrab_Widget()) {
885 iWidget *grabbed = mouseGrab_Widget(); 911 iWidget *grabbed = mouseGrab_Widget();
886 setCurrent_Root(findRoot_Window(d, grabbed)); 912 setCurrent_Root(findRoot_Window(w, grabbed));
887 wasUsed = dispatchEvent_Widget(grabbed, &event); 913 wasUsed = dispatchEvent_Widget(grabbed, &event);
888 } 914 }
889 } 915 }
890 /* Dispatch the event to the tree of widgets. */ 916 /* Dispatch the event to the tree of widgets. */
891 if (!wasUsed) { 917 if (!wasUsed) {
892 wasUsed = dispatchEvent_Window(d, &event); 918 wasUsed = dispatchEvent_Window(w, &event);
893 } 919 }
894 if (!wasUsed) { 920 if (!wasUsed) {
895 /* As a special case, clicking the middle mouse button can be used for pasting 921 /* As a special case, clicking the middle mouse button can be used for pasting
@@ -902,17 +928,17 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
902 paste.key.keysym.mod = KMOD_PRIMARY; 928 paste.key.keysym.mod = KMOD_PRIMARY;
903 paste.key.state = SDL_PRESSED; 929 paste.key.state = SDL_PRESSED;
904 paste.key.timestamp = SDL_GetTicks(); 930 paste.key.timestamp = SDL_GetTicks();
905 wasUsed = dispatchEvent_Window(d, &paste); 931 wasUsed = dispatchEvent_Window(w, &paste);
906 } 932 }
907 if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) { 933 if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {
908 if (postContextClick_Window(d, &event.button)) { 934 if (postContextClick_Window(w, &event.button)) {
909 wasUsed = iTrue; 935 wasUsed = iTrue;
910 } 936 }
911 } 937 }
912 } 938 }
913 if (isMetricsChange_UserEvent(&event)) { 939 if (isMetricsChange_UserEvent(&event)) {
914 iForIndices(i, d->roots) { 940 iForIndices(i, w->roots) {
915 updateMetrics_Root(d->roots[i]); 941 updateMetrics_Root(w->roots[i]);
916 } 942 }
917 } 943 }
918 if (isCommand_UserEvent(&event, "lang.changed")) { 944 if (isCommand_UserEvent(&event, "lang.changed")) {
@@ -921,16 +947,16 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
921 removeMacMenus_(); 947 removeMacMenus_();
922 insertMacMenus_(); 948 insertMacMenus_();
923#endif 949#endif
924 invalidate_Window(d); 950 invalidate_Window(w);
925 iForIndices(i, d->roots) { 951 iForIndices(i, w->roots) {
926 if (d->roots[i]) { 952 if (w->roots[i]) {
927 updatePreferencesLayout_Widget(findChild_Widget(d->roots[i]->widget, "prefs")); 953 updatePreferencesLayout_Widget(findChild_Widget(w->roots[i]->widget, "prefs"));
928 arrange_Widget(d->roots[i]->widget); 954 arrange_Widget(w->roots[i]->widget);
929 } 955 }
930 } 956 }
931 } 957 }
932 if (event.type == SDL_MOUSEMOTION) { 958 if (event.type == SDL_MOUSEMOTION) {
933 applyCursor_Window_(d); 959 applyCursor_Window_(w);
934 } 960 }
935 return wasUsed; 961 return wasUsed;
936 } 962 }
@@ -1018,25 +1044,26 @@ iBool postContextClick_Window(iWindow *d, const SDL_MouseButtonEvent *ev) {
1018 return iFalse; 1044 return iFalse;
1019} 1045}
1020 1046
1021void draw_Window(iWindow *d) { 1047void draw_MainWindow(iMainWindow *d) {
1022 if (d->isDrawFrozen) { 1048 iWindow *w = as_Window(d);
1049 if (w->isDrawFrozen) {
1023 return; 1050 return;
1024 } 1051 }
1025 /* Check if root needs resizing. */ { 1052 /* Check if root needs resizing. */ {
1026 iInt2 renderSize; 1053 iInt2 renderSize;
1027 SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y); 1054 SDL_GetRendererOutputSize(w->render, &renderSize.x, &renderSize.y);
1028 if (!isEqual_I2(renderSize, d->size)) { 1055 if (!isEqual_I2(renderSize, w->size)) {
1029 updateSize_Window_(d, iTrue); 1056 updateSize_MainWindow_(d, iTrue);
1030 processEvents_App(postedEventsOnly_AppEventMode); 1057 processEvents_App(postedEventsOnly_AppEventMode);
1031 } 1058 }
1032 } 1059 }
1033 const int winFlags = SDL_GetWindowFlags(d->win); 1060 const int winFlags = SDL_GetWindowFlags(d->base.win);
1034 const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; 1061 const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0;
1035 iPaint p; 1062 iPaint p;
1036 init_Paint(&p); 1063 init_Paint(&p);
1037 /* Clear the window. The clear color is visible as a border around the window 1064 /* Clear the window. The clear color is visible as a border around the window
1038 when the custom frame is being used. */ { 1065 when the custom frame is being used. */ {
1039 setCurrent_Root(d->roots[0]); 1066 setCurrent_Root(w->roots[0]);
1040#if defined (iPlatformMobile) 1067#if defined (iPlatformMobile)
1041 iColor back = get_Color(uiBackground_ColorId); 1068 iColor back = get_Color(uiBackground_ColorId);
1042 if (deviceType_App() == phone_AppDeviceType) { 1069 if (deviceType_App() == phone_AppDeviceType) {
@@ -1050,16 +1077,16 @@ void draw_Window(iWindow *d) {
1050 : uiSeparator_ColorId); 1077 : uiSeparator_ColorId);
1051#endif 1078#endif
1052 unsetClip_Paint(&p); /* update clip to full window */ 1079 unsetClip_Paint(&p); /* update clip to full window */
1053 SDL_SetRenderDrawColor(d->render, back.r, back.g, back.b, 255); 1080 SDL_SetRenderDrawColor(w->render, back.r, back.g, back.b, 255);
1054 SDL_RenderClear(d->render); 1081 SDL_RenderClear(w->render);
1055 } 1082 }
1056 /* Draw widgets. */ 1083 /* Draw widgets. */
1057 d->frameTime = SDL_GetTicks(); 1084 w->frameTime = SDL_GetTicks();
1058 if (isExposed_Window(d)) { 1085 if (isExposed_Window(w)) {
1059 d->isInvalidated = iFalse; 1086 w->isInvalidated = iFalse;
1060 extern int drawCount_; 1087 extern int drawCount_;
1061 iForIndices(i, d->roots) { 1088 iForIndices(i, w->roots) {
1062 iRoot *root = d->roots[i]; 1089 iRoot *root = w->roots[i];
1063 if (root) { 1090 if (root) {
1064 setCurrent_Root(root); 1091 setCurrent_Root(root);
1065 unsetClip_Paint(&p); /* update clip to current root */ 1092 unsetClip_Paint(&p); /* update clip to current root */
@@ -1076,14 +1103,14 @@ void draw_Window(iWindow *d) {
1076 SDL_SetTextureColorMod(d->appIcon, iconColor.r, iconColor.g, iconColor.b); 1103 SDL_SetTextureColorMod(d->appIcon, iconColor.r, iconColor.g, iconColor.b);
1077 SDL_SetTextureAlphaMod(d->appIcon, gotFocus || !isLight ? 255 : 92); 1104 SDL_SetTextureAlphaMod(d->appIcon, gotFocus || !isLight ? 255 : 92);
1078 SDL_RenderCopy( 1105 SDL_RenderCopy(
1079 d->render, 1106 w->render,
1080 d->appIcon, 1107 d->appIcon,
1081 NULL, 1108 NULL,
1082 &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size }); 1109 &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size });
1083 } 1110 }
1084#endif 1111#endif
1085 /* Root separator and keyboard focus indicator. */ 1112 /* Root separator and keyboard focus indicator. */
1086 if (numRoots_Window(d) > 1){ 1113 if (numRoots_Window(w) > 1){
1087 const iRect bounds = bounds_Widget(root->widget); 1114 const iRect bounds = bounds_Widget(root->widget);
1088 if (i == 1) { 1115 if (i == 1) {
1089 fillRect_Paint(&p, (iRect){ 1116 fillRect_Paint(&p, (iRect){
@@ -1091,7 +1118,7 @@ void draw_Window(iWindow *d) {
1091 init_I2(gap_UI / 4, height_Rect(bounds)) 1118 init_I2(gap_UI / 4, height_Rect(bounds))
1092 }, uiSeparator_ColorId); 1119 }, uiSeparator_ColorId);
1093 } 1120 }
1094 if (root == d->keyRoot) { 1121 if (root == w->keyRoot) {
1095 const iBool isDark = isDark_ColorTheme(colorTheme_App()); 1122 const iBool isDark = isDark_ColorTheme(colorTheme_App());
1096 fillRect_Paint(&p, (iRect){ 1123 fillRect_Paint(&p, (iRect){
1097 topLeft_Rect(bounds), 1124 topLeft_Rect(bounds),
@@ -1104,7 +1131,7 @@ void draw_Window(iWindow *d) {
1104 } 1131 }
1105 setCurrent_Root(NULL); 1132 setCurrent_Root(NULL);
1106#if !defined (NDEBUG) 1133#if !defined (NDEBUG)
1107 draw_Text(defaultBold_FontId, safeRect_Root(d->roots[0]).pos, red_ColorId, "%d", drawCount_); 1134 draw_Text(defaultBold_FontId, safeRect_Root(w->roots[0]).pos, red_ColorId, "%d", drawCount_);
1108 drawCount_ = 0; 1135 drawCount_ = 0;
1109#endif 1136#endif
1110 } 1137 }
@@ -1116,21 +1143,21 @@ void draw_Window(iWindow *d) {
1116 SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect); 1143 SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect);
1117 } 1144 }
1118#endif 1145#endif
1119 SDL_RenderPresent(d->render); 1146 SDL_RenderPresent(w->render);
1120} 1147}
1121 1148
1122void resize_Window(iWindow *d, int w, int h) { 1149void resize_MainWindow(iMainWindow *d, int w, int h) {
1123 if (w > 0 && h > 0) { 1150 if (w > 0 && h > 0) {
1124 SDL_SetWindowSize(d->win, w, h); 1151 SDL_SetWindowSize(d->base.win, w, h);
1125 updateSize_Window_(d, iFalse); 1152 updateSize_MainWindow_(d, iFalse);
1126 } 1153 }
1127 else { 1154 else {
1128 updateSize_Window_(d, iTrue); /* notify always */ 1155 updateSize_MainWindow_(d, iTrue); /* notify always */
1129 } 1156 }
1130} 1157}
1131 1158
1132void setTitle_Window(iWindow *d, const iString *title) { 1159void setTitle_MainWindow(iMainWindow *d, const iString *title) {
1133 SDL_SetWindowTitle(d->win, cstr_String(title)); 1160 SDL_SetWindowTitle(d->base.win, cstr_String(title));
1134 iLabelWidget *bar = findChild_Widget(get_Root()->widget, "winbar.title"); 1161 iLabelWidget *bar = findChild_Widget(get_Root()->widget, "winbar.title");
1135 if (bar) { 1162 if (bar) {
1136 updateText_LabelWidget(bar, title); 1163 updateText_LabelWidget(bar, title);
@@ -1204,11 +1231,15 @@ iWindow *get_Window(void) {
1204 return theWindow_; 1231 return theWindow_;
1205} 1232}
1206 1233
1234iMainWindow *get_MainWindow(void) {
1235 return as_MainWindow(theWindow_);
1236}
1237
1207iBool isOpenGLRenderer_Window(void) { 1238iBool isOpenGLRenderer_Window(void) {
1208 return isOpenGLRenderer_; 1239 return isOpenGLRenderer_;
1209} 1240}
1210 1241
1211void setKeyboardHeight_Window(iWindow *d, int height) { 1242void setKeyboardHeight_MainWindow(iMainWindow *d, int height) {
1212 if (d->keyboardHeight != height) { 1243 if (d->keyboardHeight != height) {
1213 d->keyboardHeight = height; 1244 d->keyboardHeight = height;
1214 postCommandf_App("keyboard.changed arg:%d", height); 1245 postCommandf_App("keyboard.changed arg:%d", height);
@@ -1216,47 +1247,49 @@ void setKeyboardHeight_Window(iWindow *d, int height) {
1216 } 1247 }
1217} 1248}
1218 1249
1219void checkPendingSplit_Window(iWindow *d) { 1250void checkPendingSplit_MainWindow(iMainWindow *d) {
1220 if (d->splitMode != d->pendingSplitMode) { 1251 if (d->splitMode != d->pendingSplitMode) {
1221 setSplitMode_Window(d, d->pendingSplitMode); 1252 setSplitMode_MainWindow(d, d->pendingSplitMode);
1222 } 1253 }
1223} 1254}
1224 1255
1225void swapRoots_Window(iWindow *d) { 1256void swapRoots_MainWindow(iMainWindow *d) {
1226 if (numRoots_Window(d) == 2) { 1257 iWindow *w = as_Window(d);
1227 iSwap(iRoot *, d->roots[0], d->roots[1]); 1258 if (numRoots_Window(w) == 2) {
1228 updateSize_Window_(d, iTrue); 1259 iSwap(iRoot *, w->roots[0], w->roots[1]);
1260 updateSize_MainWindow_(d, iTrue);
1229 } 1261 }
1230} 1262}
1231 1263
1232void setSplitMode_Window(iWindow *d, int splitFlags) { 1264void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) {
1233 const int splitMode = splitFlags & mode_WindowSplit; 1265 const int splitMode = splitFlags & mode_WindowSplit;
1234 if (deviceType_App() == phone_AppDeviceType) { 1266 if (deviceType_App() == phone_AppDeviceType) {
1235 /* There isn't enough room on the phone. */ 1267 /* There isn't enough room on the phone. */
1236 /* TODO: Maybe in landscape only? */ 1268 /* TODO: Maybe in landscape only? */
1237 return; 1269 return;
1238 } 1270 }
1271 iWindow *w = as_Window(d);
1239 iAssert(current_Root() == NULL); 1272 iAssert(current_Root() == NULL);
1240 if (d->splitMode != splitMode) { 1273 if (d->splitMode != splitMode) {
1241 int oldCount = numRoots_Window(d); 1274 int oldCount = numRoots_Window(w);
1242 setFreezeDraw_Window(d, iTrue); 1275 setFreezeDraw_Window(w, iTrue);
1243 if (oldCount == 2 && splitMode == 0) { 1276 if (oldCount == 2 && splitMode == 0) {
1244 /* Keep references to the tabs of the second root. */ 1277 /* Keep references to the tabs of the second root. */
1245 const iDocumentWidget *curPage = document_Root(d->keyRoot); 1278 const iDocumentWidget *curPage = document_Root(w->keyRoot);
1246 if (!curPage) { 1279 if (!curPage) {
1247 /* All tabs closed on that side. */ 1280 /* All tabs closed on that side. */
1248 curPage = document_Root(otherRoot_Window(d, d->keyRoot)); 1281 curPage = document_Root(otherRoot_Window(w, w->keyRoot));
1249 } 1282 }
1250 iObjectList *tabs = listDocuments_App(d->roots[1]); 1283 iObjectList *tabs = listDocuments_App(w->roots[1]);
1251 iForEach(ObjectList, i, tabs) { 1284 iForEach(ObjectList, i, tabs) {
1252 setRoot_Widget(i.object, d->roots[0]); 1285 setRoot_Widget(i.object, w->roots[0]);
1253 } 1286 }
1254 setFocus_Widget(NULL); 1287 setFocus_Widget(NULL);
1255 delete_Root(d->roots[1]); 1288 delete_Root(w->roots[1]);
1256 d->roots[1] = NULL; 1289 w->roots[1] = NULL;
1257 d->keyRoot = d->roots[0]; 1290 w->keyRoot = w->roots[0];
1258 /* Move the deleted root's tabs to the first root. */ 1291 /* Move the deleted root's tabs to the first root. */
1259 setCurrent_Root(d->roots[0]); 1292 setCurrent_Root(w->roots[0]);
1260 iWidget *docTabs = findWidget_Root("doctabs"); 1293 iWidget *docTabs = findWidget_Root("doctabs");
1261 iForEach(ObjectList, j, tabs) { 1294 iForEach(ObjectList, j, tabs) {
1262 appendTabPage_Widget(docTabs, j.object, "", 0, 0); 1295 appendTabPage_Widget(docTabs, j.object, "", 0, 0);
@@ -1268,38 +1301,38 @@ void setSplitMode_Window(iWindow *d, int splitFlags) {
1268 } 1301 }
1269 else if (splitMode && oldCount == 1) { 1302 else if (splitMode && oldCount == 1) {
1270 /* Add a second root. */ 1303 /* Add a second root. */
1271 iDocumentWidget *moved = document_Root(d->roots[0]); 1304 iDocumentWidget *moved = document_Root(w->roots[0]);
1272 iAssert(d->roots[1] == NULL); 1305 iAssert(w->roots[1] == NULL);
1273 const iBool addToLeft = (prefs_App()->pinSplit == 2); 1306 const iBool addToLeft = (prefs_App()->pinSplit == 2);
1274 size_t newRootIndex = 1; 1307 size_t newRootIndex = 1;
1275 if (addToLeft) { 1308 if (addToLeft) {
1276 iSwap(iRoot *, d->roots[0], d->roots[1]); 1309 iSwap(iRoot *, w->roots[0], w->roots[1]);
1277 newRootIndex = 0; 1310 newRootIndex = 0;
1278 } 1311 }
1279 d->roots[newRootIndex] = new_Root(); 1312 w->roots[newRootIndex] = new_Root();
1280 d->keyRoot = d->roots[newRootIndex]; 1313 w->keyRoot = w->roots[newRootIndex];
1281 setCurrent_Root(d->roots[newRootIndex]); 1314 setCurrent_Root(w->roots[newRootIndex]);
1282 createUserInterface_Root(d->roots[newRootIndex]); 1315 createUserInterface_Root(w->roots[newRootIndex]);
1283 if (!isEmpty_String(d->pendingSplitUrl)) { 1316 if (!isEmpty_String(d->pendingSplitUrl)) {
1284 postCommandf_Root(d->roots[newRootIndex], "open url:%s", 1317 postCommandf_Root(w->roots[newRootIndex], "open url:%s",
1285 cstr_String(d->pendingSplitUrl)); 1318 cstr_String(d->pendingSplitUrl));
1286 clear_String(d->pendingSplitUrl); 1319 clear_String(d->pendingSplitUrl);
1287 } 1320 }
1288 else if (~splitFlags & noEvents_WindowSplit) { 1321 else if (~splitFlags & noEvents_WindowSplit) {
1289 iWidget *docTabs0 = findChild_Widget(d->roots[newRootIndex ^ 1]->widget, "doctabs"); 1322 iWidget *docTabs0 = findChild_Widget(w->roots[newRootIndex ^ 1]->widget, "doctabs");
1290 iWidget *docTabs1 = findChild_Widget(d->roots[newRootIndex]->widget, "doctabs"); 1323 iWidget *docTabs1 = findChild_Widget(w->roots[newRootIndex]->widget, "doctabs");
1291 /* If the old root has multiple tabs, move the current one to the new split. */ 1324 /* If the old root has multiple tabs, move the current one to the new split. */
1292 if (tabCount_Widget(docTabs0) >= 2) { 1325 if (tabCount_Widget(docTabs0) >= 2) {
1293 int movedIndex = tabPageIndex_Widget(docTabs0, moved); 1326 int movedIndex = tabPageIndex_Widget(docTabs0, moved);
1294 removeTabPage_Widget(docTabs0, movedIndex); 1327 removeTabPage_Widget(docTabs0, movedIndex);
1295 showTabPage_Widget(docTabs0, tabPage_Widget(docTabs0, iMax(movedIndex - 1, 0))); 1328 showTabPage_Widget(docTabs0, tabPage_Widget(docTabs0, iMax(movedIndex - 1, 0)));
1296 iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */ 1329 iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */
1297 setRoot_Widget(as_Widget(moved), d->roots[newRootIndex]); 1330 setRoot_Widget(as_Widget(moved), w->roots[newRootIndex]);
1298 prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0); 1331 prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0);
1299 postCommandf_App("tabs.switch page:%p", moved); 1332 postCommandf_App("tabs.switch page:%p", moved);
1300 } 1333 }
1301 else { 1334 else {
1302 postCommand_Root(d->roots[newRootIndex], "navigate.home"); 1335 postCommand_Root(w->roots[newRootIndex], "navigate.home");
1303 } 1336 }
1304 } 1337 }
1305 setCurrent_Root(NULL); 1338 setCurrent_Root(NULL);
@@ -1328,26 +1361,26 @@ void setSplitMode_Window(iWindow *d, int splitFlags) {
1328 } 1361 }
1329#endif 1362#endif
1330 if (~splitFlags & noEvents_WindowSplit) { 1363 if (~splitFlags & noEvents_WindowSplit) {
1331 updateSize_Window_(d, iTrue); 1364 updateSize_MainWindow_(d, iTrue);
1332 postCommand_App("window.unfreeze"); 1365 postCommand_App("window.unfreeze");
1333 } 1366 }
1334 } 1367 }
1335} 1368}
1336 1369
1337void setSnap_Window(iWindow *d, int snapMode) { 1370void setSnap_MainWindow(iMainWindow *d, int snapMode) {
1338 if (!prefs_App()->customFrame) { 1371 if (!prefs_App()->customFrame) {
1339 if (snapMode == maximized_WindowSnap) { 1372 if (snapMode == maximized_WindowSnap) {
1340 SDL_MaximizeWindow(d->win); 1373 SDL_MaximizeWindow(d->base.win);
1341 } 1374 }
1342 else if (snapMode == fullscreen_WindowSnap) { 1375 else if (snapMode == fullscreen_WindowSnap) {
1343 SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP); 1376 SDL_SetWindowFullscreen(d->base.win, SDL_WINDOW_FULLSCREEN_DESKTOP);
1344 } 1377 }
1345 else { 1378 else {
1346 if (snap_Window(d) == fullscreen_WindowSnap) { 1379 if (snap_MainWindow(d) == fullscreen_WindowSnap) {
1347 SDL_SetWindowFullscreen(d->win, 0); 1380 SDL_SetWindowFullscreen(d->base.win, 0);
1348 } 1381 }
1349 else { 1382 else {
1350 SDL_RestoreWindow(d->win); 1383 SDL_RestoreWindow(d->base.win);
1351 } 1384 }
1352 } 1385 }
1353 return; 1386 return;
@@ -1359,9 +1392,9 @@ void setSnap_Window(iWindow *d, int snapMode) {
1359 const int snapDist = gap_UI * 4; 1392 const int snapDist = gap_UI * 4;
1360 iRect newRect = zero_Rect(); 1393 iRect newRect = zero_Rect();
1361 SDL_Rect usable; 1394 SDL_Rect usable;
1362 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable); 1395 SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->base.win), &usable);
1363 if (d->place.snap == fullscreen_WindowSnap) { 1396 if (d->place.snap == fullscreen_WindowSnap) {
1364 SDL_SetWindowFullscreen(d->win, 0); 1397 SDL_SetWindowFullscreen(d->base.win, 0);
1365 } 1398 }
1366 d->place.snap = snapMode & ~redo_WindowSnap; 1399 d->place.snap = snapMode & ~redo_WindowSnap;
1367 switch (snapMode & mask_WindowSnap) { 1400 switch (snapMode & mask_WindowSnap) {
@@ -1382,8 +1415,8 @@ void setSnap_Window(iWindow *d, int snapMode) {
1382 case yMaximized_WindowSnap: 1415 case yMaximized_WindowSnap:
1383 newRect.pos.y = 0; 1416 newRect.pos.y = 0;
1384 newRect.size.y = usable.h; 1417 newRect.size.y = usable.h;
1385 SDL_GetWindowSize(d->win, &newRect.size.x, NULL); 1418 SDL_GetWindowSize(d->base.win, &newRect.size.x, NULL);
1386 SDL_GetWindowPosition(d->win, &newRect.pos.x, NULL); 1419 SDL_GetWindowPosition(d->base.win, &newRect.pos.x, NULL);
1387 /* Snap the window to left/right edges, if close by. */ 1420 /* Snap the window to left/right edges, if close by. */
1388 if (iAbs(right_Rect(newRect) - (usable.x + usable.w)) < snapDist) { 1421 if (iAbs(right_Rect(newRect) - (usable.x + usable.w)) < snapDist) {
1389 newRect.pos.x = usable.x + usable.w - width_Rect(newRect); 1422 newRect.pos.x = usable.x + usable.w - width_Rect(newRect);
@@ -1393,7 +1426,7 @@ void setSnap_Window(iWindow *d, int snapMode) {
1393 } 1426 }
1394 break; 1427 break;
1395 case fullscreen_WindowSnap: 1428 case fullscreen_WindowSnap:
1396 SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP); 1429 SDL_SetWindowFullscreen(d->base.win, SDL_WINDOW_FULLSCREEN_DESKTOP);
1397 break; 1430 break;
1398 } 1431 }
1399 if (snapMode & (topBit_WindowSnap | bottomBit_WindowSnap)) { 1432 if (snapMode & (topBit_WindowSnap | bottomBit_WindowSnap)) {
@@ -1425,9 +1458,9 @@ void setSnap_Window(iWindow *d, int snapMode) {
1425#endif /* defined (LAGRANGE_ENABLE_CUSTOM_FRAME) */ 1458#endif /* defined (LAGRANGE_ENABLE_CUSTOM_FRAME) */
1426} 1459}
1427 1460
1428int snap_Window(const iWindow *d) { 1461int snap_MainWindow(const iMainWindow *d) {
1429 if (!prefs_App()->customFrame) { 1462 if (!prefs_App()->customFrame) {
1430 const int flags = SDL_GetWindowFlags(d->win); 1463 const int flags = SDL_GetWindowFlags(d->base.win);
1431 if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { 1464 if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
1432 return fullscreen_WindowSnap; 1465 return fullscreen_WindowSnap;
1433 } 1466 }
diff --git a/src/ui/window.h b/src/ui/window.h
index 282e1682..73e92391 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -29,8 +29,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29#include <SDL_render.h> 29#include <SDL_render.h>
30#include <SDL_video.h> 30#include <SDL_video.h>
31 31
32iDeclareType(MainWindow)
32iDeclareType(Window) 33iDeclareType(Window)
33iDeclareTypeConstructionArgs(Window, iRect rect) 34iDeclareTypeConstructionArgs(MainWindow, iRect rect)
35
36typedef iAny iAnyWindow;
34 37
35enum iWindowSnap { 38enum iWindowSnap {
36 none_WindowSnap = 0, 39 none_WindowSnap = 0,
@@ -68,9 +71,14 @@ enum iWindowSplit {
68 noEvents_WindowSplit = iBit(11), 71 noEvents_WindowSplit = iBit(11),
69}; 72};
70 73
74enum iWindowType {
75 main_WindowType,
76 popup_WindowType,
77};
78
71struct Impl_Window { 79struct Impl_Window {
80 enum iWindowType type;
72 SDL_Window * win; 81 SDL_Window * win;
73 iWindowPlacement place;
74 iBool isDrawFrozen; /* avoids premature draws while restoring window state */ 82 iBool isDrawFrozen; /* avoids premature draws while restoring window state */
75 iBool isExposed; 83 iBool isExposed;
76 iBool isMinimized; 84 iBool isMinimized;
@@ -80,11 +88,6 @@ struct Impl_Window {
80 uint32_t focusGainedAt; 88 uint32_t focusGainedAt;
81 SDL_Renderer *render; 89 SDL_Renderer *render;
82 iInt2 size; 90 iInt2 size;
83 int splitMode;
84 int pendingSplitMode;
85 iString * pendingSplitUrl; /* URL to open in a newly opened split */
86 iRoot * roots[2]; /* root widget and UI state; second one is for split mode */
87 iRoot * keyRoot; /* root that has the current keyboard input focus */
88 iWidget * hover; 91 iWidget * hover;
89 iWidget * lastHover; /* cleared if deleted */ 92 iWidget * lastHover; /* cleared if deleted */
90 iWidget * mouseGrab; 93 iWidget * mouseGrab;
@@ -94,56 +97,102 @@ struct Impl_Window {
94 float uiScale; 97 float uiScale;
95 uint32_t frameTime; 98 uint32_t frameTime;
96 double presentTime; 99 double presentTime;
97 SDL_Texture * appIcon;
98 SDL_Texture * borderShadow;
99 SDL_Cursor * cursors[SDL_NUM_SYSTEM_CURSORS]; 100 SDL_Cursor * cursors[SDL_NUM_SYSTEM_CURSORS];
100 SDL_Cursor * pendingCursor; 101 SDL_Cursor * pendingCursor;
101 int loadAnimTimer; 102 iRoot * roots[2]; /* root widget and UI state; second one is for split mode */
102 int keyboardHeight; /* mobile software keyboards */ 103 iRoot * keyRoot; /* root that has the current keyboard input focus */
104 SDL_Texture * borderShadow;
105};
106
107struct Impl_MainWindow {
108 iWindow base;
109 iWindowPlacement place;
110 int splitMode;
111 int pendingSplitMode;
112 iString * pendingSplitUrl; /* URL to open in a newly opened split */
113 SDL_Texture * appIcon;
114 int keyboardHeight; /* mobile software keyboards */
103}; 115};
104 116
105iBool processEvent_Window (iWindow *, const SDL_Event *); 117iLocalDef enum iWindowType type_Window(const iAnyWindow *d) {
118 return ((const iWindow *) d)->type;
119}
120
121uint32_t id_Window (const iWindow *);
122iInt2 size_Window (const iWindow *);
123iInt2 maxTextureSize_Window (const iWindow *);
124float uiScale_Window (const iWindow *);
125iInt2 coord_Window (const iWindow *, int x, int y);
126iInt2 mouseCoord_Window (const iWindow *, int whichDevice);
127iAnyObject * hitChild_Window (const iWindow *, iInt2 coord);
128uint32_t frameTime_Window (const iWindow *);
129SDL_Renderer * renderer_Window (const iWindow *);
130int numRoots_Window (const iWindow *);
131iRoot * findRoot_Window (const iWindow *, const iWidget *widget);
132iRoot * otherRoot_Window (const iWindow *, iRoot *root);
133
106iBool dispatchEvent_Window (iWindow *, const SDL_Event *); 134iBool dispatchEvent_Window (iWindow *, const SDL_Event *);
107void invalidate_Window (iWindow *); /* discard all cached graphics */ 135void invalidate_Window (iAnyWindow *); /* discard all cached graphics */
108void draw_Window (iWindow *); 136void draw_Window (iWindow *);
109void drawWhileResizing_Window(iWindow *d, int w, int h); /* workaround for SDL bug */
110void resize_Window (iWindow *, int w, int h);
111void setTitle_Window (iWindow *, const iString *title);
112void setUiScale_Window (iWindow *, float uiScale); 137void setUiScale_Window (iWindow *, float uiScale);
113void setFreezeDraw_Window (iWindow *, iBool freezeDraw); 138void setFreezeDraw_Window (iWindow *, iBool freezeDraw);
114iBool setKeyRoot_Window (iWindow *, iRoot *root);
115void setCursor_Window (iWindow *, int cursor); 139void setCursor_Window (iWindow *, int cursor);
116void setSnap_Window (iWindow *, int snapMode); 140iBool setKeyRoot_Window (iWindow *, iRoot *root);
117void setKeyboardHeight_Window(iWindow *, int height);
118void setSplitMode_Window (iWindow *, int splitMode);
119void showToolbars_Window (iWindow *, iBool show);
120iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *); 141iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *);
121void checkPendingSplit_Window(iWindow *);
122void swapRoots_Window (iWindow *);
123
124uint32_t id_Window (const iWindow *);
125iInt2 size_Window (const iWindow *);
126iInt2 maxTextureSize_Window (const iWindow *);
127float uiScale_Window (const iWindow *);
128iInt2 coord_Window (const iWindow *, int x, int y);
129iInt2 mouseCoord_Window (const iWindow *, int whichDevice);
130iAnyObject *hitChild_Window (const iWindow *, iInt2 coord);
131uint32_t frameTime_Window (const iWindow *);
132SDL_Renderer *renderer_Window (const iWindow *);
133int snap_Window (const iWindow *);
134iBool isFullscreen_Window (const iWindow *);
135int numRoots_Window (const iWindow *);
136iRoot * findRoot_Window (const iWindow *, const iWidget *widget);
137iRoot * otherRoot_Window (const iWindow *, iRoot *root);
138iWindow * get_Window (void);
139 142
143iWindow * get_Window (void);
140iBool isOpenGLRenderer_Window (void); 144iBool isOpenGLRenderer_Window (void);
141 145
142#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
143SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos);
144#endif
145
146iLocalDef iBool isExposed_Window(const iWindow *d) { 146iLocalDef iBool isExposed_Window(const iWindow *d) {
147 iAssert(d); 147 iAssert(d);
148 return d->isExposed; 148 return d->isExposed;
149} 149}
150
151iLocalDef iWindow *as_Window(iAnyWindow *d) {
152 iAssert(type_Window(d) == main_WindowType || type_Window(d) == popup_WindowType);
153 return (iWindow *) d;
154}
155
156iLocalDef const iWindow *constAs_Window(const iAnyWindow *d) {
157 iAssert(type_Window(d) == main_WindowType || type_Window(d) == popup_WindowType);
158 return (const iWindow *) d;
159}
160
161/*----------------------------------------------------------------------------------------------*/
162
163iLocalDef iWindow *asWindow_MainWindow(iMainWindow *d) {
164 iAssert(type_Window(d) == main_WindowType);
165 return &d->base;
166}
167
168void setTitle_MainWindow (iMainWindow *, const iString *title);
169void setSnap_MainWindow (iMainWindow *, int snapMode);
170void setKeyboardHeight_MainWindow (iMainWindow *, int height);
171void setSplitMode_MainWindow (iMainWindow *, int splitMode);
172void checkPendingSplit_MainWindow (iMainWindow *);
173void swapRoots_MainWindow (iMainWindow *);
174void showToolbars_MainWindow (iMainWindow *, iBool show);
175void resize_MainWindow (iMainWindow *, int w, int h);
176
177iBool processEvent_MainWindow (iMainWindow *, const SDL_Event *);
178void draw_MainWindow (iMainWindow *);
179void drawWhileResizing_MainWindow (iMainWindow *, int w, int h); /* workaround for SDL bug */
180
181int snap_MainWindow (const iMainWindow *);
182iBool isFullscreen_Window (const iMainWindow *);
183
184#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
185SDL_HitTestResult hitTest_MainWindow(const iMainWindow *d, iInt2 pos);
186#endif
187
188iMainWindow * get_MainWindow (void);
189
190iLocalDef iMainWindow *as_MainWindow(iAnyWindow *d) {
191 iAssert(type_Window(d) == main_WindowType);
192 return (iMainWindow *) d;
193}
194
195iLocalDef const iMainWindow *constAs_MainWindow(const iAnyWindow *d) {
196 iAssert(type_Window(d) == main_WindowType);
197 return (const iMainWindow *) d;
198}