diff options
Diffstat (limited to 'src/ui/window.c')
-rw-r--r-- | src/ui/window.c | 732 |
1 files changed, 458 insertions, 274 deletions
diff --git a/src/ui/window.c b/src/ui/window.c index f8391ed9..066ea102 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
30 | #include "keys.h" | 30 | #include "keys.h" |
31 | #include "labelwidget.h" | 31 | #include "labelwidget.h" |
32 | #include "documentwidget.h" | 32 | #include "documentwidget.h" |
33 | #include "sidebarwidget.h" | ||
33 | #include "paint.h" | 34 | #include "paint.h" |
34 | #include "root.h" | 35 | #include "root.h" |
35 | #include "touch.h" | 36 | #include "touch.h" |
@@ -57,7 +58,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
57 | #include "stb_image.h" | 58 | #include "stb_image.h" |
58 | #include "stb_image_resize.h" | 59 | #include "stb_image_resize.h" |
59 | 60 | ||
60 | static iWindow *theWindow_ = NULL; | 61 | static iWindow * theWindow_; |
62 | static iMainWindow *theMainWindow_; | ||
61 | 63 | ||
62 | #if defined (iPlatformApple) || defined (iPlatformLinux) || defined (iPlatformOther) | 64 | #if defined (iPlatformApple) || defined (iPlatformLinux) || defined (iPlatformOther) |
63 | static float initialUiScale_ = 1.0f; | 65 | static float initialUiScale_ = 1.0f; |
@@ -67,7 +69,10 @@ static float initialUiScale_ = 1.1f; | |||
67 | 69 | ||
68 | static iBool isOpenGLRenderer_; | 70 | static iBool isOpenGLRenderer_; |
69 | 71 | ||
70 | iDefineTypeConstructionArgs(Window, (iRect rect), rect) | 72 | iDefineTypeConstructionArgs(Window, |
73 | (enum iWindowType type, iRect rect, uint32_t flags), | ||
74 | type, rect, flags) | ||
75 | iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect) | ||
71 | 76 | ||
72 | /* TODO: Define menus per platform. */ | 77 | /* TODO: Define menus per platform. */ |
73 | 78 | ||
@@ -116,6 +121,7 @@ static const iMenuItem viewMenuItems_[] = { | |||
116 | static iMenuItem bookmarksMenuItems_[] = { | 121 | static iMenuItem bookmarksMenuItems_[] = { |
117 | { "${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, | 122 | { "${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, |
118 | { "${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" }, | 123 | { "${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" }, |
124 | { "${menu.newfolder}", 0, 0, "bookmarks.addfolder" }, | ||
119 | { "---", 0, 0, NULL }, | 125 | { "---", 0, 0, NULL }, |
120 | { "${menu.import.links}", 0, 0, "bookmark.links confirm:1" }, | 126 | { "${menu.import.links}", 0, 0, "bookmark.links confirm:1" }, |
121 | { "---", 0, 0, NULL }, | 127 | { "---", 0, 0, NULL }, |
@@ -124,6 +130,8 @@ static iMenuItem bookmarksMenuItems_[] = { | |||
124 | { "${macos.menu.bookmarks.bytime}", 0, 0, "open url:about:bookmarks?created" }, | 130 | { "${macos.menu.bookmarks.bytime}", 0, 0, "open url:about:bookmarks?created" }, |
125 | { "${menu.feeds.entrylist}", 0, 0, "open url:about:feeds" }, | 131 | { "${menu.feeds.entrylist}", 0, 0, "open url:about:feeds" }, |
126 | { "---", 0, 0, NULL }, | 132 | { "---", 0, 0, NULL }, |
133 | { "${menu.sort.alpha}", 0, 0, "bookmarks.sort" }, | ||
134 | { "---", 0, 0, NULL }, | ||
127 | { "${menu.bookmarks.refresh}", 0, 0, "bookmarks.reload.remote" }, | 135 | { "${menu.bookmarks.refresh}", 0, 0, "bookmarks.reload.remote" }, |
128 | { "${menu.feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" }, | 136 | { "${menu.feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" }, |
129 | }; | 137 | }; |
@@ -169,17 +177,17 @@ int numRoots_Window(const iWindow *d) { | |||
169 | return num; | 177 | return num; |
170 | } | 178 | } |
171 | 179 | ||
172 | static void windowSizeChanged_Window_(iWindow *d) { | 180 | static void windowSizeChanged_MainWindow_(iMainWindow *d) { |
173 | const int numRoots = numRoots_Window(d); | 181 | const int numRoots = numRoots_Window(as_Window(d)); |
174 | const iInt2 rootSize = d->size; | 182 | const iInt2 rootSize = d->base.size; |
175 | const int weights[2] = { | 183 | const int weights[2] = { |
176 | d->roots[0] ? (d->splitMode & twoToOne_WindowSplit ? 2 : 1) : 0, | 184 | d->base.roots[0] ? (d->splitMode & twoToOne_WindowSplit ? 2 : 1) : 0, |
177 | d->roots[1] ? (d->splitMode & oneToTwo_WindowSplit ? 2 : 1) : 0, | 185 | d->base.roots[1] ? (d->splitMode & oneToTwo_WindowSplit ? 2 : 1) : 0, |
178 | }; | 186 | }; |
179 | const int totalWeight = weights[0] + weights[1]; | 187 | const int totalWeight = weights[0] + weights[1]; |
180 | int w = 0; | 188 | int w = 0; |
181 | iForIndices(i, d->roots) { | 189 | iForIndices(i, d->base.roots) { |
182 | iRoot *root = d->roots[i]; | 190 | iRoot *root = d->base.roots[i]; |
183 | if (root) { | 191 | if (root) { |
184 | iRect *rect = &root->widget->rect; | 192 | iRect *rect = &root->widget->rect; |
185 | /* Horizontal split frame. */ | 193 | /* Horizontal split frame. */ |
@@ -199,26 +207,27 @@ static void windowSizeChanged_Window_(iWindow *d) { | |||
199 | } | 207 | } |
200 | } | 208 | } |
201 | 209 | ||
202 | static void setupUserInterface_Window(iWindow *d) { | 210 | static void setupUserInterface_MainWindow(iMainWindow *d) { |
203 | #if defined (iHaveNativeMenus) | 211 | #if defined (iHaveNativeMenus) |
204 | insertMacMenus_(); | 212 | insertMacMenus_(); |
205 | #endif | 213 | #endif |
206 | /* One root is created by default. */ | 214 | /* One root is created by default. */ |
207 | d->roots[0] = new_Root(); | 215 | d->base.roots[0] = new_Root(); |
208 | setCurrent_Root(d->roots[0]); | 216 | d->base.roots[0]->window = as_Window(d); |
209 | createUserInterface_Root(d->roots[0]); | 217 | setCurrent_Root(d->base.roots[0]); |
218 | createUserInterface_Root(d->base.roots[0]); | ||
210 | setCurrent_Root(NULL); | 219 | setCurrent_Root(NULL); |
211 | /* One of the roots always has keyboard input focus. */ | 220 | /* One of the roots always has keyboard input focus. */ |
212 | d->keyRoot = d->roots[0]; | 221 | d->base.keyRoot = d->base.roots[0]; |
213 | } | 222 | } |
214 | 223 | ||
215 | static void updateSize_Window_(iWindow *d, iBool notifyAlways) { | 224 | static void updateSize_MainWindow_(iMainWindow *d, iBool notifyAlways) { |
216 | iInt2 *size = &d->size; | 225 | iInt2 *size = &d->base.size; |
217 | const iInt2 oldSize = *size; | 226 | const iInt2 oldSize = *size; |
218 | SDL_GetRendererOutputSize(d->render, &size->x, &size->y); | 227 | SDL_GetRendererOutputSize(d->base.render, &size->x, &size->y); |
219 | size->y -= d->keyboardHeight; | 228 | size->y -= d->keyboardHeight; |
220 | if (notifyAlways || !isEqual_I2(oldSize, *size)) { | 229 | if (notifyAlways || !isEqual_I2(oldSize, *size)) { |
221 | windowSizeChanged_Window_(d); | 230 | windowSizeChanged_MainWindow_(d); |
222 | if (!isEqual_I2(*size, d->place.lastNotifiedSize)) { | 231 | if (!isEqual_I2(*size, d->place.lastNotifiedSize)) { |
223 | const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); | 232 | const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); |
224 | const iBool isVert = (d->place.lastNotifiedSize.y != size->y); | 233 | const iBool isVert = (d->place.lastNotifiedSize.y != size->y); |
@@ -234,8 +243,8 @@ static void updateSize_Window_(iWindow *d, iBool notifyAlways) { | |||
234 | } | 243 | } |
235 | } | 244 | } |
236 | 245 | ||
237 | void drawWhileResizing_Window(iWindow *d, int w, int h) { | 246 | void drawWhileResizing_MainWindow(iMainWindow *d, int w, int h) { |
238 | draw_Window(d); | 247 | draw_MainWindow(d); |
239 | } | 248 | } |
240 | 249 | ||
241 | static float pixelRatio_Window_(const iWindow *d) { | 250 | static float pixelRatio_Window_(const iWindow *d) { |
@@ -308,7 +317,7 @@ static iRoot *rootAt_Window_(const iWindow *d, iInt2 coord) { | |||
308 | } | 317 | } |
309 | 318 | ||
310 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 319 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
311 | static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) { | 320 | static SDL_HitTestResult hitTest_MainWindow_(SDL_Window *win, const SDL_Point *pos, void *data) { |
312 | iWindow *d = data; | 321 | iWindow *d = data; |
313 | iAssert(d->win == win); | 322 | iAssert(d->win == win); |
314 | if (SDL_GetWindowFlags(d->win) & (SDL_WINDOW_MOUSE_CAPTURE | SDL_WINDOW_FULLSCREEN_DESKTOP)) { | 323 | if (SDL_GetWindowFlags(d->win) & (SDL_WINDOW_MOUSE_CAPTURE | SDL_WINDOW_FULLSCREEN_DESKTOP)) { |
@@ -361,19 +370,22 @@ SDL_HitTestResult hitTest_Window(const iWindow *d, iInt2 pos) { | |||
361 | #endif | 370 | #endif |
362 | 371 | ||
363 | iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) { | 372 | iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) { |
364 | flags |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; | 373 | flags |= SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; |
374 | if (d->type == main_WindowType) { | ||
375 | flags |= SDL_WINDOW_RESIZABLE; | ||
365 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 376 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
366 | if (prefs_App()->customFrame) { | 377 | if (prefs_App()->customFrame) { |
367 | /* We are drawing a custom frame so hide the default one. */ | 378 | /* We are drawing a custom frame so hide the default one. */ |
368 | flags |= SDL_WINDOW_BORDERLESS; | 379 | flags |= SDL_WINDOW_BORDERLESS; |
369 | } | 380 | } |
370 | #endif | 381 | #endif |
382 | } | ||
371 | if (SDL_CreateWindowAndRenderer( | 383 | if (SDL_CreateWindowAndRenderer( |
372 | width_Rect(rect), height_Rect(rect), flags, &d->win, &d->render)) { | 384 | width_Rect(rect), height_Rect(rect), flags, &d->win, &d->render)) { |
373 | return iFalse; | 385 | return iFalse; |
374 | } | 386 | } |
375 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 387 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
376 | if (prefs_App()->customFrame) { | 388 | if (type_Window(d) == main_WindowType && prefs_App()->customFrame) { |
377 | /* Register a handler for window hit testing (drag, resize). */ | 389 | /* Register a handler for window hit testing (drag, resize). */ |
378 | SDL_SetWindowHitTest(d->win, hitTest_Window_, d); | 390 | SDL_SetWindowHitTest(d->win, hitTest_Window_, d); |
379 | SDL_SetWindowResizable(d->win, SDL_TRUE); | 391 | SDL_SetWindowResizable(d->win, SDL_TRUE); |
@@ -397,40 +409,27 @@ static SDL_Surface *loadImage_(const iBlock *data, int resized) { | |||
397 | pixels, w, h, 8 * num, w * num, SDL_PIXELFORMAT_RGBA32); | 409 | pixels, w, h, 8 * num, w * num, SDL_PIXELFORMAT_RGBA32); |
398 | } | 410 | } |
399 | 411 | ||
400 | void init_Window(iWindow *d, iRect rect) { | 412 | void init_Window(iWindow *d, enum iWindowType type, iRect rect, uint32_t flags) { |
401 | theWindow_ = d; | 413 | d->type = type; |
402 | d->win = NULL; | 414 | d->win = NULL; |
403 | d->size = zero_I2(); /* will be updated below */ | 415 | d->size = zero_I2(); /* will be updated below */ |
404 | iZap(d->roots); | 416 | d->hover = NULL; |
405 | d->splitMode = d->pendingSplitMode = 0; | 417 | d->lastHover = NULL; |
406 | d->pendingSplitUrl = new_String(); | 418 | d->mouseGrab = NULL; |
407 | d->hover = NULL; | 419 | d->focus = NULL; |
408 | d->mouseGrab = NULL; | ||
409 | d->focus = NULL; | ||
410 | iZap(d->cursors); | ||
411 | d->place.initialPos = rect.pos; | ||
412 | d->place.normalRect = rect; | ||
413 | d->place.lastNotifiedSize = zero_I2(); | ||
414 | d->place.snap = 0; | ||
415 | d->pendingCursor = NULL; | 420 | d->pendingCursor = NULL; |
416 | d->isDrawFrozen = iTrue; | 421 | d->isExposed = iFalse; |
417 | d->isExposed = iFalse; | 422 | d->isMinimized = iFalse; |
418 | d->isMinimized = iFalse; | ||
419 | d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */ | 423 | d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */ |
420 | d->isMouseInside = iTrue; | 424 | d->isMouseInside = iTrue; |
421 | d->ignoreClick = iFalse; | 425 | d->ignoreClick = iFalse; |
422 | d->focusGainedAt = 0; | 426 | d->focusGainedAt = 0; |
423 | d->keyboardHeight = 0; | 427 | d->presentTime = 0.0; |
424 | init_Anim(&d->rootOffset, 0.0f); | 428 | d->frameTime = SDL_GetTicks(); |
425 | uint32_t flags = 0; | 429 | d->keyRoot = NULL; |
426 | #if defined (iPlatformAppleDesktop) | 430 | d->borderShadow = NULL; |
427 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); | 431 | iZap(d->roots); |
428 | #elif defined (iPlatformAppleMobile) | 432 | iZap(d->cursors); |
429 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); | ||
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. */ | 433 | /* First try SDL's default renderer that should be the best option. */ |
435 | if (forceSoftwareRender_App() || !create_Window_(d, rect, flags)) { | 434 | 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. */ | 435 | /* No luck, maybe software only? This should always work as long as there is a display. */ |
@@ -443,36 +442,95 @@ void init_Window(iWindow *d, iRect rect) { | |||
443 | if (left_Rect(rect) >= 0 || top_Rect(rect) >= 0) { | 442 | if (left_Rect(rect) >= 0 || top_Rect(rect) >= 0) { |
444 | SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect)); | 443 | SDL_SetWindowPosition(d->win, left_Rect(rect), top_Rect(rect)); |
445 | } | 444 | } |
445 | SDL_GetRendererOutputSize(d->render, &d->size.x, &d->size.y); | ||
446 | drawBlank_Window_(d); | ||
447 | d->pixelRatio = pixelRatio_Window_(d); /* point/pixel conversion */ | ||
448 | d->displayScale = displayScale_Window_(d); | ||
449 | d->uiScale = initialUiScale_; | ||
450 | /* TODO: Ratios, scales, and metrics must be window-specific, not global. */ | ||
451 | setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale); | ||
452 | d->text = new_Text(d->render); | ||
453 | } | ||
454 | |||
455 | static void deinitRoots_Window_(iWindow *d) { | ||
456 | iRecycle(); | ||
457 | iForIndices(i, d->roots) { | ||
458 | if (d->roots[i]) { | ||
459 | setCurrent_Root(d->roots[i]); | ||
460 | delete_Root(d->roots[i]); | ||
461 | d->roots[i] = NULL; | ||
462 | } | ||
463 | } | ||
464 | setCurrent_Root(NULL); | ||
465 | } | ||
466 | |||
467 | void deinit_Window(iWindow *d) { | ||
468 | if (d->type == popup_WindowType) { | ||
469 | removePopup_App(d); | ||
470 | } | ||
471 | deinitRoots_Window_(d); | ||
472 | delete_Text(d->text); | ||
473 | SDL_DestroyRenderer(d->render); | ||
474 | SDL_DestroyWindow(d->win); | ||
475 | iForIndices(i, d->cursors) { | ||
476 | if (d->cursors[i]) { | ||
477 | SDL_FreeCursor(d->cursors[i]); | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | |||
482 | void init_MainWindow(iMainWindow *d, iRect rect) { | ||
483 | theWindow_ = &d->base; | ||
484 | theMainWindow_ = d; | ||
485 | uint32_t flags = 0; | ||
486 | #if defined (iPlatformAppleDesktop) | ||
487 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); | ||
488 | #elif defined (iPlatformAppleMobile) | ||
489 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); | ||
490 | #else | ||
491 | flags |= SDL_WINDOW_OPENGL; | ||
492 | #endif | ||
493 | SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); | ||
494 | init_Window(&d->base, main_WindowType, rect, flags); | ||
495 | d->isDrawFrozen = iTrue; | ||
496 | d->splitMode = 0; | ||
497 | d->pendingSplitMode = 0; | ||
498 | d->pendingSplitUrl = new_String(); | ||
499 | d->place.initialPos = rect.pos; | ||
500 | d->place.normalRect = rect; | ||
501 | d->place.lastNotifiedSize = zero_I2(); | ||
502 | d->place.snap = 0; | ||
503 | d->keyboardHeight = 0; | ||
504 | #if defined(iPlatformMobile) | ||
505 | const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */ | ||
506 | #else | ||
446 | const iInt2 minSize = init_I2(425, 325); | 507 | const iInt2 minSize = init_I2(425, 325); |
447 | SDL_SetWindowMinimumSize(d->win, minSize.x, minSize.y); | 508 | #endif |
448 | SDL_SetWindowTitle(d->win, "Lagrange"); | 509 | SDL_SetWindowMinimumSize(d->base.win, minSize.x, minSize.y); |
510 | SDL_SetWindowTitle(d->base.win, "Lagrange"); | ||
449 | /* Some info. */ { | 511 | /* Some info. */ { |
450 | SDL_RendererInfo info; | 512 | SDL_RendererInfo info; |
451 | SDL_GetRendererInfo(d->render, &info); | 513 | SDL_GetRendererInfo(d->base.render, &info); |
452 | isOpenGLRenderer_ = !iCmpStr(info.name, "opengl"); | 514 | isOpenGLRenderer_ = !iCmpStr(info.name, "opengl"); |
453 | printf("[window] renderer: %s%s\n", info.name, | 515 | printf("[window] renderer: %s%s\n", |
516 | info.name, | ||
454 | info.flags & SDL_RENDERER_ACCELERATED ? " (accelerated)" : ""); | 517 | info.flags & SDL_RENDERER_ACCELERATED ? " (accelerated)" : ""); |
455 | #if !defined (NDEBUG) | 518 | #if !defined(NDEBUG) |
456 | printf("[window] max texture size: %d x %d\n", | 519 | printf("[window] max texture size: %d x %d\n", |
457 | info.max_texture_width, | 520 | info.max_texture_width, |
458 | info.max_texture_height); | 521 | info.max_texture_height); |
459 | for (size_t i = 0; i < info.num_texture_formats; ++i) { | 522 | for (size_t i = 0; i < info.num_texture_formats; ++i) { |
460 | printf("[window] supported texture format: %s\n", SDL_GetPixelFormatName( | 523 | printf("[window] supported texture format: %s\n", |
461 | info.texture_formats[i])); | 524 | SDL_GetPixelFormatName(info.texture_formats[i])); |
462 | } | 525 | } |
463 | #endif | 526 | #endif |
464 | } | 527 | } |
465 | drawBlank_Window_(d); | 528 | #if defined(iPlatformMsys) |
466 | d->pixelRatio = pixelRatio_Window_(d); /* point/pixel conversion */ | 529 | SDL_SetWindowMinimumSize(d->base.win, minSize.x * d->base.displayScale, minSize.y * d->base.displayScale); |
467 | d->displayScale = displayScale_Window_(d); | 530 | useExecutableIconResource_SDLWindow(d->base.win); |
468 | d->uiScale = initialUiScale_; | ||
469 | setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale); | ||
470 | #if defined (iPlatformMsys) | ||
471 | SDL_SetWindowMinimumSize(d->win, minSize.x * d->displayScale, minSize.y * d->displayScale); | ||
472 | useExecutableIconResource_SDLWindow(d->win); | ||
473 | #endif | 531 | #endif |
474 | #if defined (iPlatformLinux) | 532 | #if defined (iPlatformLinux) |
475 | SDL_SetWindowMinimumSize(d->win, minSize.x * d->pixelRatio, minSize.y * d->pixelRatio); | 533 | SDL_SetWindowMinimumSize(d->win, minSize.x * d->base.pixelRatio, minSize.y * d->base.pixelRatio); |
476 | /* Load the window icon. */ { | 534 | /* Load the window icon. */ { |
477 | SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, 0); | 535 | SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, 0); |
478 | SDL_SetWindowIcon(d->win, surf); | 536 | SDL_SetWindowIcon(d->win, surf); |
@@ -481,20 +539,16 @@ void init_Window(iWindow *d, iRect rect) { | |||
481 | } | 539 | } |
482 | #endif | 540 | #endif |
483 | #if defined (iPlatformAppleMobile) | 541 | #if defined (iPlatformAppleMobile) |
484 | setupWindow_iOS(d); | 542 | setupWindow_iOS(as_Window(d)); |
485 | #endif | 543 | #endif |
486 | d->presentTime = 0.0; | 544 | setCurrent_Text(d->base.text); |
487 | d->frameTime = SDL_GetTicks(); | 545 | SDL_GetRendererOutputSize(d->base.render, &d->base.size.x, &d->base.size.y); |
488 | d->loadAnimTimer = 0; | 546 | setupUserInterface_MainWindow(d); |
489 | init_Text(d->render); | ||
490 | SDL_GetRendererOutputSize(d->render, &d->size.x, &d->size.y); | ||
491 | setupUserInterface_Window(d); | ||
492 | postCommand_App("~bindings.changed"); /* update from bindings */ | 547 | postCommand_App("~bindings.changed"); /* update from bindings */ |
493 | //updateSize_Window_(d, iFalse); | ||
494 | /* Load the border shadow texture. */ { | 548 | /* Load the border shadow texture. */ { |
495 | SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0); | 549 | SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0); |
496 | d->borderShadow = SDL_CreateTextureFromSurface(d->render, surf); | 550 | d->base.borderShadow = SDL_CreateTextureFromSurface(d->base.render, surf); |
497 | SDL_SetTextureBlendMode(d->borderShadow, SDL_BLENDMODE_BLEND); | 551 | SDL_SetTextureBlendMode(d->base.borderShadow, SDL_BLENDMODE_BLEND); |
498 | free(surf->pixels); | 552 | free(surf->pixels); |
499 | SDL_FreeSurface(surf); | 553 | SDL_FreeSurface(surf); |
500 | } | 554 | } |
@@ -504,36 +558,26 @@ void init_Window(iWindow *d, iRect rect) { | |||
504 | if (prefs_App()->customFrame) { | 558 | if (prefs_App()->customFrame) { |
505 | SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, appIconSize_Root()); | 559 | SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, appIconSize_Root()); |
506 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); | 560 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); |
507 | d->appIcon = SDL_CreateTextureFromSurface(d->render, surf); | 561 | d->appIcon = SDL_CreateTextureFromSurface(d->base.render, surf); |
508 | free(surf->pixels); | 562 | free(surf->pixels); |
509 | SDL_FreeSurface(surf); | 563 | SDL_FreeSurface(surf); |
510 | /* We need to observe non-client-area events. */ | 564 | /* We need to observe non-client-area events. */ |
511 | SDL_EventState(SDL_SYSWMEVENT, SDL_TRUE); | 565 | SDL_EventState(SDL_SYSWMEVENT, SDL_TRUE); |
512 | } | 566 | } |
513 | #endif | 567 | #endif |
568 | SDL_HideWindow(d->base.win); | ||
514 | } | 569 | } |
515 | 570 | ||
516 | void deinit_Window(iWindow *d) { | 571 | void deinit_MainWindow(iMainWindow *d) { |
517 | iRecycle(); | 572 | deinitRoots_Window_(as_Window(d)); |
518 | iForIndices(i, d->roots) { | 573 | if (theWindow_ == as_Window(d)) { |
519 | if (d->roots[i]) { | ||
520 | setCurrent_Root(d->roots[i]); | ||
521 | deinit_Root(d->roots[i]); | ||
522 | } | ||
523 | } | ||
524 | if (theWindow_ == d) { | ||
525 | theWindow_ = NULL; | 574 | theWindow_ = NULL; |
526 | } | 575 | } |
527 | setCurrent_Root(NULL); | 576 | if (theMainWindow_ == d) { |
528 | delete_String(d->pendingSplitUrl); | 577 | theMainWindow_ = NULL; |
529 | deinit_Text(); | ||
530 | SDL_DestroyRenderer(d->render); | ||
531 | SDL_DestroyWindow(d->win); | ||
532 | iForIndices(i, d->cursors) { | ||
533 | if (d->cursors[i]) { | ||
534 | SDL_FreeCursor(d->cursors[i]); | ||
535 | } | ||
536 | } | 578 | } |
579 | delete_String(d->pendingSplitUrl); | ||
580 | deinit_Window(&d->base); | ||
537 | } | 581 | } |
538 | 582 | ||
539 | SDL_Renderer *renderer_Window(const iWindow *d) { | 583 | SDL_Renderer *renderer_Window(const iWindow *d) { |
@@ -546,8 +590,8 @@ iInt2 maxTextureSize_Window(const iWindow *d) { | |||
546 | return init_I2(info.max_texture_width, info.max_texture_height); | 590 | return init_I2(info.max_texture_width, info.max_texture_height); |
547 | } | 591 | } |
548 | 592 | ||
549 | iBool isFullscreen_Window(const iWindow *d) { | 593 | iBool isFullscreen_MainWindow(const iMainWindow *d) { |
550 | return snap_Window(d) == fullscreen_WindowSnap; | 594 | return snap_MainWindow(d) == fullscreen_WindowSnap; |
551 | } | 595 | } |
552 | 596 | ||
553 | iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { | 597 | iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { |
@@ -566,36 +610,41 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) { | |||
566 | return root == d->roots[0] && d->roots[1] ? d->roots[1] : d->roots[0]; | 610 | return root == d->roots[0] && d->roots[1] ? d->roots[1] : d->roots[0]; |
567 | } | 611 | } |
568 | 612 | ||
569 | static void invalidate_Window_(iWindow *d, iBool forced) { | 613 | static void invalidate_MainWindow_(iMainWindow *d, iBool forced) { |
570 | if (d && (!d->isInvalidated || forced)) { | 614 | if (d && (!d->base.isInvalidated || forced)) { |
571 | d->isInvalidated = iTrue; | 615 | d->base.isInvalidated = iTrue; |
572 | resetFonts_Text(); | 616 | resetFonts_Text(text_Window(d)); |
573 | postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ | 617 | postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ |
574 | } | 618 | } |
575 | } | 619 | } |
576 | 620 | ||
577 | void invalidate_Window(iWindow *d) { | 621 | void invalidate_Window(iAnyWindow *d) { |
578 | invalidate_Window_(d, iFalse); | 622 | if (type_Window(d) == main_WindowType) { |
623 | invalidate_MainWindow_(as_MainWindow(d), iFalse); | ||
624 | } | ||
625 | else { | ||
626 | iAssert(type_Window(d) == main_WindowType); | ||
627 | } | ||
579 | } | 628 | } |
580 | 629 | ||
581 | static iBool isNormalPlacement_Window_(const iWindow *d) { | 630 | static iBool isNormalPlacement_MainWindow_(const iMainWindow *d) { |
582 | if (d->isDrawFrozen) return iFalse; | 631 | if (d->isDrawFrozen) return iFalse; |
583 | #if defined (iPlatformApple) | 632 | #if defined (iPlatformApple) |
584 | /* Maximized mode is not special on macOS. */ | 633 | /* Maximized mode is not special on macOS. */ |
585 | if (snap_Window(d) == maximized_WindowSnap) { | 634 | if (snap_MainWindow(d) == maximized_WindowSnap) { |
586 | return iTrue; | 635 | return iTrue; |
587 | } | 636 | } |
588 | #endif | 637 | #endif |
589 | if (snap_Window(d)) return iFalse; | 638 | if (snap_MainWindow(d)) return iFalse; |
590 | return !(SDL_GetWindowFlags(d->win) & SDL_WINDOW_MINIMIZED); | 639 | return !(SDL_GetWindowFlags(d->base.win) & SDL_WINDOW_MINIMIZED); |
591 | } | 640 | } |
592 | 641 | ||
593 | static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) { | 642 | static iBool unsnap_MainWindow_(iMainWindow *d, const iInt2 *newPos) { |
594 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 643 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
595 | if (!prefs_App()->customFrame) { | 644 | if (!prefs_App()->customFrame) { |
596 | return iFalse; | 645 | return iFalse; |
597 | } | 646 | } |
598 | const int snap = snap_Window(d); | 647 | const int snap = snap_MainWindow(d); |
599 | if (snap == yMaximized_WindowSnap || snap == left_WindowSnap || snap == right_WindowSnap) { | 648 | if (snap == yMaximized_WindowSnap || snap == left_WindowSnap || snap == right_WindowSnap) { |
600 | if (!newPos || (d->place.lastHit == SDL_HITTEST_RESIZE_LEFT || | 649 | if (!newPos || (d->place.lastHit == SDL_HITTEST_RESIZE_LEFT || |
601 | d->place.lastHit == SDL_HITTEST_RESIZE_RIGHT)) { | 650 | d->place.lastHit == SDL_HITTEST_RESIZE_RIGHT)) { |
@@ -603,21 +652,21 @@ static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) { | |||
603 | } | 652 | } |
604 | if (newPos) { | 653 | if (newPos) { |
605 | SDL_Rect usable; | 654 | SDL_Rect usable; |
606 | SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable); | 655 | SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->base.win), &usable); |
607 | /* Snap to top. */ | 656 | /* Snap to top. */ |
608 | if (snap == yMaximized_WindowSnap && | 657 | if (snap == yMaximized_WindowSnap && |
609 | iAbs(newPos->y - usable.y) < lineHeight_Text(uiContent_FontId) * 2) { | 658 | iAbs(newPos->y - usable.y) < lineHeight_Text(uiContent_FontId) * 2) { |
610 | setSnap_Window(d, redo_WindowSnap | yMaximized_WindowSnap); | 659 | setSnap_MainWindow(d, redo_WindowSnap | yMaximized_WindowSnap); |
611 | return iFalse; | 660 | return iFalse; |
612 | } | 661 | } |
613 | } | 662 | } |
614 | } | 663 | } |
615 | if (snap && snap != fullscreen_WindowSnap) { | 664 | if (snap && snap != fullscreen_WindowSnap) { |
616 | if (snap_Window(d) == yMaximized_WindowSnap && newPos) { | 665 | if (snap_MainWindow(d) == yMaximized_WindowSnap && newPos) { |
617 | d->place.normalRect.pos = *newPos; | 666 | d->place.normalRect.pos = *newPos; |
618 | } | 667 | } |
619 | //printf("unsnap\n"); fflush(stdout); | 668 | //printf("unsnap\n"); fflush(stdout); |
620 | setSnap_Window(d, none_WindowSnap); | 669 | setSnap_MainWindow(d, none_WindowSnap); |
621 | return iTrue; | 670 | return iTrue; |
622 | } | 671 | } |
623 | #endif | 672 | #endif |
@@ -627,7 +676,7 @@ static iBool unsnap_Window_(iWindow *d, const iInt2 *newPos) { | |||
627 | static void notifyMetricsChange_Window_(const iWindow *d) { | 676 | static void notifyMetricsChange_Window_(const iWindow *d) { |
628 | /* Dynamic UI metrics change. Widgets need to update themselves. */ | 677 | /* Dynamic UI metrics change. Widgets need to update themselves. */ |
629 | setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale); | 678 | setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale); |
630 | resetFonts_Text(); | 679 | resetFonts_Text(d->text); |
631 | postCommand_App("metrics.changed"); | 680 | postCommand_App("metrics.changed"); |
632 | } | 681 | } |
633 | 682 | ||
@@ -649,146 +698,178 @@ static void checkPixelRatioChange_Window_(iWindow *d) { | |||
649 | } | 698 | } |
650 | 699 | ||
651 | static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { | 700 | static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { |
701 | if (ev->windowID != SDL_GetWindowID(d->win)) { | ||
702 | return iFalse; | ||
703 | } | ||
652 | switch (ev->event) { | 704 | switch (ev->event) { |
653 | #if defined (iPlatformDesktop) | ||
654 | case SDL_WINDOWEVENT_EXPOSED: | 705 | case SDL_WINDOWEVENT_EXPOSED: |
655 | if (!d->isExposed) { | 706 | d->isExposed = iTrue; |
656 | drawBlank_Window_(d); /* avoid showing system-provided contents */ | 707 | postRefresh_App(); |
657 | d->isExposed = iTrue; | 708 | return iTrue; |
658 | } | 709 | case SDL_WINDOWEVENT_RESTORED: |
710 | case SDL_WINDOWEVENT_SHOWN: | ||
711 | postRefresh_App(); | ||
712 | return iTrue; | ||
713 | case SDL_WINDOWEVENT_FOCUS_LOST: | ||
714 | /* Popup windows are currently only used for menus. */ | ||
715 | closeMenu_Widget(d->roots[0]->widget); | ||
716 | return iTrue; | ||
717 | case SDL_WINDOWEVENT_LEAVE: | ||
718 | unhover_Widget(); | ||
719 | d->isMouseInside = iFalse; | ||
720 | //postCommand_App("window.mouse.exited"); | ||
721 | // SDL_SetWindowInputFocus(mainWindow_App()->base.win); | ||
722 | printf("mouse leaves popup\n"); fflush(stdout); | ||
723 | //SDL_RaiseWindow(mainWindow_App()->base.win); | ||
724 | postRefresh_App(); | ||
725 | return iTrue; | ||
726 | case SDL_WINDOWEVENT_ENTER: | ||
727 | d->isMouseInside = iTrue; | ||
728 | //postCommand_App("window.mouse.entered"); | ||
729 | printf("mouse enters popup\n"); fflush(stdout); | ||
730 | return iTrue; | ||
731 | } | ||
732 | return iFalse; | ||
733 | } | ||
734 | |||
735 | static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) { | ||
736 | switch (ev->event) { | ||
737 | #if defined(iPlatformDesktop) | ||
738 | case SDL_WINDOWEVENT_EXPOSED: | ||
739 | d->base.isExposed = iTrue; | ||
659 | /* Since we are manually controlling when to redraw the window, we are responsible | 740 | /* Since we are manually controlling when to redraw the window, we are responsible |
660 | for ensuring that window contents get redrawn after expose events. Under certain | 741 | for ensuring that window contents get redrawn after expose events. Under certain |
661 | circumstances (e.g., under openbox), not doing this would mean that the window | 742 | circumstances (e.g., under openbox), not doing this would mean that the window |
662 | is missing contents until other events trigger a refresh. */ | 743 | is missing contents until other events trigger a refresh. */ |
663 | postRefresh_App(); | 744 | postRefresh_App(); |
664 | #if defined (LAGRANGE_ENABLE_WINDOWPOS_FIX) | 745 | #if defined(LAGRANGE_ENABLE_WINDOWPOS_FIX) |
665 | if (d->place.initialPos.x >= 0) { | 746 | if (d->place.initialPos.x >= 0) { |
666 | int bx, by; | 747 | int bx, by; |
667 | SDL_GetWindowBordersSize(d->win, &by, &bx, NULL, NULL); | 748 | SDL_GetWindowBordersSize(d->win, &by, &bx, NULL, NULL); |
668 | SDL_SetWindowPosition(d->win, d->place.initialPos.x + bx, d->place.initialPos.y + by); | 749 | SDL_SetWindowPosition( |
750 | d->win, d->place.initialPos.x + bx, d->place.initialPos.y + by); | ||
669 | d->place.initialPos = init1_I2(-1); | 751 | d->place.initialPos = init1_I2(-1); |
670 | } | 752 | } |
671 | #endif | 753 | #endif |
672 | return iFalse; | 754 | return iFalse; |
673 | case SDL_WINDOWEVENT_MOVED: { | 755 | case SDL_WINDOWEVENT_MOVED: { |
674 | if (d->isMinimized) { | 756 | if (d->base.isMinimized) { |
675 | return iFalse; | 757 | return iFalse; |
676 | } | 758 | } |
677 | checkPixelRatioChange_Window_(d); | 759 | checkPixelRatioChange_Window_(as_Window(d)); |
678 | const iInt2 newPos = init_I2(ev->data1, ev->data2); | 760 | const iInt2 newPos = init_I2(ev->data1, ev->data2); |
679 | if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ | 761 | if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ |
680 | /* Maybe minimized? Seems like a Windows constant of some kind. */ | 762 | /* Maybe minimized? Seems like a Windows constant of some kind. */ |
681 | d->isMinimized = iTrue; | 763 | d->base.isMinimized = iTrue; |
682 | return iFalse; | 764 | return iFalse; |
683 | } | 765 | } |
684 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 766 | #if defined(LAGRANGE_ENABLE_CUSTOM_FRAME) |
685 | /* Set the snap position depending on where the mouse cursor is. */ | 767 | /* Set the snap position depending on where the mouse cursor is. */ |
686 | if (prefs_App()->customFrame) { | 768 | if (prefs_App()->customFrame) { |
687 | SDL_Rect usable; | 769 | SDL_Rect usable; |
688 | iInt2 mouse = cursor_Win32(); /* SDL is unaware of the current cursor pos */ | 770 | iInt2 mouse = cursor_Win32(); /* SDL is unaware of the current cursor pos */ |
689 | SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable); | 771 | SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->base.win), &usable); |
690 | const iBool isTop = iAbs(mouse.y - usable.y) < gap_UI * 20; | 772 | const iBool isTop = iAbs(mouse.y - usable.y) < gap_UI * 20; |
691 | const iBool isBottom = iAbs(usable.y + usable.h - mouse.y) < gap_UI * 20; | 773 | const iBool isBottom = iAbs(usable.y + usable.h - mouse.y) < gap_UI * 20; |
692 | if (iAbs(mouse.x - usable.x) < gap_UI) { | 774 | if (iAbs(mouse.x - usable.x) < gap_UI) { |
693 | setSnap_Window(d, | 775 | setSnap_MainWindow(d, |
694 | redo_WindowSnap | left_WindowSnap | | 776 | redo_WindowSnap | left_WindowSnap | |
695 | (isTop ? topBit_WindowSnap : 0) | | 777 | (isTop ? topBit_WindowSnap : 0) | |
696 | (isBottom ? bottomBit_WindowSnap : 0)); | 778 | (isBottom ? bottomBit_WindowSnap : 0)); |
697 | return iTrue; | 779 | return iTrue; |
698 | } | 780 | } |
699 | if (iAbs(mouse.x - usable.x - usable.w) < gap_UI) { | 781 | if (iAbs(mouse.x - usable.x - usable.w) < gap_UI) { |
700 | setSnap_Window(d, | 782 | setSnap_MainWindow(d, |
701 | redo_WindowSnap | right_WindowSnap | | 783 | redo_WindowSnap | right_WindowSnap | |
702 | (isTop ? topBit_WindowSnap : 0) | | 784 | (isTop ? topBit_WindowSnap : 0) | |
703 | (isBottom ? bottomBit_WindowSnap : 0)); | 785 | (isBottom ? bottomBit_WindowSnap : 0)); |
704 | return iTrue; | 786 | return iTrue; |
705 | } | 787 | } |
706 | if (iAbs(mouse.y - usable.y) < 2) { | 788 | if (iAbs(mouse.y - usable.y) < 2) { |
707 | setSnap_Window(d, | 789 | setSnap_MainWindow(d, |
708 | redo_WindowSnap | (d->place.lastHit == SDL_HITTEST_RESIZE_TOP | 790 | redo_WindowSnap | (d->place.lastHit == SDL_HITTEST_RESIZE_TOP |
709 | ? yMaximized_WindowSnap | 791 | ? yMaximized_WindowSnap |
710 | : maximized_WindowSnap)); | 792 | : maximized_WindowSnap)); |
711 | return iTrue; | 793 | return iTrue; |
712 | } | 794 | } |
713 | } | 795 | } |
714 | #endif /* defined LAGRANGE_ENABLE_CUSTOM_FRAME */ | 796 | #endif /* defined LAGRANGE_ENABLE_CUSTOM_FRAME */ |
715 | //printf("MOVED: %d, %d\n", ev->data1, ev->data2); fflush(stdout); | 797 | if (unsnap_MainWindow_(d, &newPos)) { |
716 | if (unsnap_Window_(d, &newPos)) { | ||
717 | return iTrue; | 798 | return iTrue; |
718 | } | 799 | } |
719 | if (isNormalPlacement_Window_(d)) { | 800 | if (isNormalPlacement_MainWindow_(d)) { |
720 | d->place.normalRect.pos = newPos; | 801 | d->place.normalRect.pos = newPos; |
721 | //printf("normal rect set (move)\n"); fflush(stdout); | 802 | // printf("normal rect set (move)\n"); fflush(stdout); |
722 | iInt2 border = zero_I2(); | 803 | iInt2 border = zero_I2(); |
723 | #if !defined (iPlatformApple) | 804 | #if !defined(iPlatformApple) |
724 | SDL_GetWindowBordersSize(d->win, &border.y, &border.x, NULL, NULL); | 805 | SDL_GetWindowBordersSize(d->base.win, &border.y, &border.x, NULL, NULL); |
725 | #endif | 806 | #endif |
726 | d->place.normalRect.pos = max_I2(zero_I2(), sub_I2(d->place.normalRect.pos, border)); | 807 | d->place.normalRect.pos = |
808 | max_I2(zero_I2(), sub_I2(d->place.normalRect.pos, border)); | ||
727 | } | 809 | } |
728 | return iTrue; | 810 | return iTrue; |
729 | } | 811 | } |
730 | case SDL_WINDOWEVENT_RESIZED: | 812 | case SDL_WINDOWEVENT_RESIZED: |
731 | if (d->isMinimized) { | 813 | if (d->base.isMinimized) { |
732 | //updateSize_Window_(d, iTrue); | 814 | // updateSize_Window_(d, iTrue); |
733 | return iTrue; | 815 | return iTrue; |
734 | } | 816 | } |
735 | if (unsnap_Window_(d, NULL)) { | 817 | if (unsnap_MainWindow_(d, NULL)) { |
736 | return iTrue; | 818 | return iTrue; |
737 | } | 819 | } |
738 | if (isNormalPlacement_Window_(d)) { | 820 | if (isNormalPlacement_MainWindow_(d)) { |
739 | d->place.normalRect.size = init_I2(ev->data1, ev->data2); | 821 | d->place.normalRect.size = init_I2(ev->data1, ev->data2); |
740 | //printf("normal rect set (resize)\n"); fflush(stdout); | 822 | // printf("normal rect set (resize)\n"); fflush(stdout); |
741 | } | 823 | } |
742 | checkPixelRatioChange_Window_(d); | 824 | checkPixelRatioChange_Window_(as_Window(d)); |
743 | //updateSize_Window_(d, iTrue /* we were already redrawing during the resize */); | ||
744 | postRefresh_App(); | 825 | postRefresh_App(); |
745 | return iTrue; | 826 | return iTrue; |
746 | case SDL_WINDOWEVENT_RESTORED: | 827 | case SDL_WINDOWEVENT_RESTORED: |
747 | case SDL_WINDOWEVENT_SHOWN: | 828 | case SDL_WINDOWEVENT_SHOWN: |
748 | updateSize_Window_(d, iTrue); | 829 | updateSize_MainWindow_(d, iTrue); |
749 | invalidate_Window_(d, iTrue); | 830 | invalidate_MainWindow_(d, iTrue); |
750 | d->isMinimized = iFalse; | 831 | d->base.isMinimized = iFalse; |
751 | postRefresh_App(); | 832 | postRefresh_App(); |
752 | return iTrue; | 833 | return iTrue; |
753 | case SDL_WINDOWEVENT_MINIMIZED: | 834 | case SDL_WINDOWEVENT_MINIMIZED: |
754 | d->isMinimized = iTrue; | 835 | d->base.isMinimized = iTrue; |
836 | return iTrue; | ||
837 | #else /* if defined (!iPlatformDesktop) */ | ||
838 | case SDL_WINDOWEVENT_RESIZED: | ||
839 | /* On mobile, this occurs when the display is rotated. */ | ||
840 | invalidate_Window(d); | ||
841 | postRefresh_App(); | ||
755 | return iTrue; | 842 | return iTrue; |
756 | #endif /* defined (iPlatformDesktop) */ | 843 | #endif |
757 | case SDL_WINDOWEVENT_LEAVE: | 844 | case SDL_WINDOWEVENT_LEAVE: |
758 | unhover_Widget(); | 845 | unhover_Widget(); |
759 | d->isMouseInside = iFalse; | 846 | d->base.isMouseInside = iFalse; |
760 | postCommand_App("window.mouse.exited"); | 847 | postCommand_App("window.mouse.exited"); |
761 | return iTrue; | 848 | return iTrue; |
762 | case SDL_WINDOWEVENT_ENTER: | 849 | case SDL_WINDOWEVENT_ENTER: |
763 | d->isMouseInside = iTrue; | 850 | d->base.isMouseInside = iTrue; |
851 | SDL_SetWindowInputFocus(d->base.win); | ||
764 | postCommand_App("window.mouse.entered"); | 852 | postCommand_App("window.mouse.entered"); |
765 | return iTrue; | 853 | return iTrue; |
766 | #if defined (iPlatformMobile) | ||
767 | case SDL_WINDOWEVENT_RESIZED: | ||
768 | /* On mobile, this occurs when the display is rotated. */ | ||
769 | invalidate_Window(d); | ||
770 | postRefresh_App(); | ||
771 | return iTrue; | ||
772 | #endif | ||
773 | case SDL_WINDOWEVENT_FOCUS_GAINED: | 854 | case SDL_WINDOWEVENT_FOCUS_GAINED: |
774 | d->focusGainedAt = SDL_GetTicks(); | 855 | d->base.focusGainedAt = SDL_GetTicks(); |
775 | setCapsLockDown_Keys(iFalse); | 856 | setCapsLockDown_Keys(iFalse); |
776 | postCommand_App("window.focus.gained"); | 857 | postCommand_App("window.focus.gained"); |
777 | d->isExposed = iTrue; | 858 | d->base.isExposed = iTrue; |
778 | #if defined (iPlatformMobile) | 859 | #if !defined (iPlatformDesktop) |
779 | /* Returned to foreground, may have lost buffered content. */ | 860 | /* Returned to foreground, may have lost buffered content. */ |
780 | invalidate_Window_(d, iTrue); | 861 | invalidate_MainWindow_(d, iTrue); |
781 | postCommand_App("window.unfreeze"); | 862 | postCommand_App("window.unfreeze"); |
782 | #endif | 863 | #endif |
783 | return iFalse; | 864 | return iFalse; |
784 | case SDL_WINDOWEVENT_FOCUS_LOST: | 865 | case SDL_WINDOWEVENT_FOCUS_LOST: |
785 | postCommand_App("window.focus.lost"); | 866 | postCommand_App("window.focus.lost"); |
786 | #if defined (iPlatformMobile) | 867 | #if !defined (iPlatformDesktop) |
787 | setFreezeDraw_Window(d, iTrue); | 868 | setFreezeDraw_MainWindow(d, iTrue); |
788 | #endif | 869 | #endif |
789 | return iFalse; | 870 | return iFalse; |
790 | case SDL_WINDOWEVENT_TAKE_FOCUS: | 871 | case SDL_WINDOWEVENT_TAKE_FOCUS: |
791 | SDL_SetWindowInputFocus(d->win); | 872 | SDL_SetWindowInputFocus(d->base.win); |
792 | postRefresh_App(); | 873 | postRefresh_App(); |
793 | return iTrue; | 874 | return iTrue; |
794 | default: | 875 | default: |
@@ -805,6 +886,7 @@ static void applyCursor_Window_(iWindow *d) { | |||
805 | } | 886 | } |
806 | 887 | ||
807 | iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | 888 | iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { |
889 | iMainWindow *mw = (type_Window(d) == main_WindowType ? as_MainWindow(d) : NULL); | ||
808 | switch (ev->type) { | 890 | switch (ev->type) { |
809 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 891 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
810 | case SDL_SYSWMEVENT: { | 892 | case SDL_SYSWMEVENT: { |
@@ -817,21 +899,28 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
817 | } | 899 | } |
818 | #endif | 900 | #endif |
819 | case SDL_WINDOWEVENT: { | 901 | case SDL_WINDOWEVENT: { |
820 | return handleWindowEvent_Window_(d, &ev->window); | 902 | if (mw) { |
903 | return handleWindowEvent_MainWindow_(mw, &ev->window); | ||
904 | } | ||
905 | else { | ||
906 | return handleWindowEvent_Window_(d, &ev->window); | ||
907 | } | ||
821 | } | 908 | } |
822 | case SDL_RENDER_TARGETS_RESET: | 909 | case SDL_RENDER_TARGETS_RESET: |
823 | case SDL_RENDER_DEVICE_RESET: { | 910 | case SDL_RENDER_DEVICE_RESET: { |
824 | invalidate_Window_(d, iTrue /* force full reset */); | 911 | if (mw) { |
912 | invalidate_MainWindow_(mw, iTrue /* force full reset */); | ||
913 | } | ||
825 | break; | 914 | break; |
826 | } | 915 | } |
827 | default: { | 916 | default: { |
828 | SDL_Event event = *ev; | 917 | SDL_Event event = *ev; |
829 | if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze")) { | 918 | if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze") && mw) { |
830 | d->isDrawFrozen = iFalse; | 919 | mw->isDrawFrozen = iFalse; |
831 | if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_HIDDEN) { | 920 | if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_HIDDEN) { |
832 | SDL_ShowWindow(d->win); | 921 | SDL_ShowWindow(d->win); |
833 | } | 922 | } |
834 | postRefresh_App(); | 923 | draw_MainWindow(mw); /* don't show a frame of placeholder content */ |
835 | postCommand_App("media.player.update"); /* in case a player needs updating */ | 924 | postCommand_App("media.player.update"); /* in case a player needs updating */ |
836 | return iTrue; | 925 | return iTrue; |
837 | } | 926 | } |
@@ -873,7 +962,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
873 | } | 962 | } |
874 | } | 963 | } |
875 | } | 964 | } |
876 | const iWidget *oldHover = d->hover; | 965 | // const iWidget *oldHover = d->hover; |
877 | iBool wasUsed = iFalse; | 966 | iBool wasUsed = iFalse; |
878 | /* Dispatch first to the mouse-grabbed widget. */ | 967 | /* Dispatch first to the mouse-grabbed widget. */ |
879 | // iWidget *widget = d->root.widget; | 968 | // iWidget *widget = d->root.widget; |
@@ -913,7 +1002,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
913 | updateMetrics_Root(d->roots[i]); | 1002 | updateMetrics_Root(d->roots[i]); |
914 | } | 1003 | } |
915 | } | 1004 | } |
916 | if (isCommand_UserEvent(&event, "lang.changed")) { | 1005 | if (isCommand_UserEvent(&event, "lang.changed") && mw) { |
917 | #if defined (iHaveNativeMenus) | 1006 | #if defined (iHaveNativeMenus) |
918 | /* Retranslate the menus. */ | 1007 | /* Retranslate the menus. */ |
919 | removeMacMenus_(); | 1008 | removeMacMenus_(); |
@@ -927,9 +1016,6 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
927 | } | 1016 | } |
928 | } | 1017 | } |
929 | } | 1018 | } |
930 | if (oldHover != d->hover) { | ||
931 | postRefresh_App(); | ||
932 | } | ||
933 | if (event.type == SDL_MOUSEMOTION) { | 1019 | if (event.type == SDL_MOUSEMOTION) { |
934 | applyCursor_Window_(d); | 1020 | applyCursor_Window_(d); |
935 | } | 1021 | } |
@@ -949,6 +1035,10 @@ iBool setKeyRoot_Window(iWindow *d, iRoot *root) { | |||
949 | return iFalse; | 1035 | return iFalse; |
950 | } | 1036 | } |
951 | 1037 | ||
1038 | iLocalDef iBool isEscapeKeypress_(const SDL_Event *ev) { | ||
1039 | return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE; | ||
1040 | } | ||
1041 | |||
952 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { | 1042 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { |
953 | if (ev->type == SDL_MOUSEMOTION) { | 1043 | if (ev->type == SDL_MOUSEMOTION) { |
954 | /* Hover widget may change. */ | 1044 | /* Hover widget may change. */ |
@@ -964,12 +1054,19 @@ iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
964 | } | 1054 | } |
965 | if ((ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP || ev->type == SDL_TEXTINPUT) | 1055 | if ((ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP || ev->type == SDL_TEXTINPUT) |
966 | && d->keyRoot != root) { | 1056 | && d->keyRoot != root) { |
967 | continue; /* Key events go only to the root with keyboard focus. */ | 1057 | if (!isEscapeKeypress_(ev)) { |
1058 | /* Key events go only to the root with keyboard focus, with the exception | ||
1059 | of Escape that will also affect the entire window. */ | ||
1060 | continue; | ||
1061 | } | ||
968 | } | 1062 | } |
969 | if (ev->type == SDL_MOUSEWHEEL && !contains_Rect(rect_Root(root), | 1063 | if (ev->type == SDL_MOUSEWHEEL && !contains_Rect(rect_Root(root), |
970 | coord_MouseWheelEvent(&ev->wheel))) { | 1064 | coord_MouseWheelEvent(&ev->wheel))) { |
971 | continue; /* Only process the event in the relevant split. */ | 1065 | continue; /* Only process the event in the relevant split. */ |
972 | } | 1066 | } |
1067 | if (!root->widget) { | ||
1068 | continue; | ||
1069 | } | ||
973 | setCurrent_Root(root); | 1070 | setCurrent_Root(root); |
974 | const iBool wasUsed = dispatchEvent_Widget(root->widget, ev); | 1071 | const iBool wasUsed = dispatchEvent_Widget(root->widget, ev); |
975 | if (wasUsed) { | 1072 | if (wasUsed) { |
@@ -1012,32 +1109,60 @@ iBool postContextClick_Window(iWindow *d, const SDL_MouseButtonEvent *ev) { | |||
1012 | } | 1109 | } |
1013 | 1110 | ||
1014 | void draw_Window(iWindow *d) { | 1111 | void draw_Window(iWindow *d) { |
1112 | if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_HIDDEN) { | ||
1113 | return; | ||
1114 | } | ||
1115 | iPaint p; | ||
1116 | init_Paint(&p); | ||
1117 | iRoot *root = d->roots[0]; | ||
1118 | setCurrent_Root(root); | ||
1119 | unsetClip_Paint(&p); /* update clip to full window */ | ||
1120 | const iColor back = get_Color(uiBackground_ColorId); | ||
1121 | SDL_SetRenderDrawColor(d->render, back.r, back.g, back.b, 255); | ||
1122 | SDL_RenderClear(d->render); | ||
1123 | d->frameTime = SDL_GetTicks(); | ||
1124 | if (isExposed_Window(d)) { | ||
1125 | d->isInvalidated = iFalse; | ||
1126 | extern int drawCount_; | ||
1127 | drawRoot_Widget(root->widget); | ||
1128 | #if !defined (NDEBUG) | ||
1129 | draw_Text(defaultBold_FontId, safeRect_Root(root).pos, red_ColorId, "%d", drawCount_); | ||
1130 | drawCount_ = 0; | ||
1131 | #endif | ||
1132 | } | ||
1133 | // drawRectThickness_Paint(&p, (iRect){ zero_I2(), sub_I2(d->size, one_I2()) }, gap_UI / 4, uiSeparator_ColorId); | ||
1134 | setCurrent_Root(NULL); | ||
1135 | SDL_RenderPresent(d->render); | ||
1136 | } | ||
1137 | |||
1138 | void draw_MainWindow(iMainWindow *d) { | ||
1139 | /* TODO: Try to make this a specialization of `draw_Window`? */ | ||
1140 | iWindow *w = as_Window(d); | ||
1015 | if (d->isDrawFrozen) { | 1141 | if (d->isDrawFrozen) { |
1016 | return; | 1142 | return; |
1017 | } | 1143 | } |
1018 | //#if defined (iPlatformMobile) | 1144 | setCurrent_Text(d->base.text); |
1019 | /* Check if root needs resizing. */ { | 1145 | /* Check if root needs resizing. */ { |
1020 | iInt2 renderSize; | 1146 | iInt2 renderSize; |
1021 | SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y); | 1147 | SDL_GetRendererOutputSize(w->render, &renderSize.x, &renderSize.y); |
1022 | if (!isEqual_I2(renderSize, d->size)) { | 1148 | if (!isEqual_I2(renderSize, w->size)) { |
1023 | updateSize_Window_(d, iTrue); | 1149 | updateSize_MainWindow_(d, iTrue); |
1024 | processEvents_App(postedEventsOnly_AppEventMode); | 1150 | processEvents_App(postedEventsOnly_AppEventMode); |
1025 | } | 1151 | } |
1026 | } | 1152 | } |
1027 | //#endif | 1153 | const int winFlags = SDL_GetWindowFlags(d->base.win); |
1028 | const int winFlags = SDL_GetWindowFlags(d->win); | ||
1029 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; | 1154 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; |
1030 | iPaint p; | 1155 | iPaint p; |
1031 | init_Paint(&p); | 1156 | init_Paint(&p); |
1032 | /* Clear the window. The clear color is visible as a border around the window | 1157 | /* Clear the window. The clear color is visible as a border around the window |
1033 | when the custom frame is being used. */ { | 1158 | when the custom frame is being used. */ { |
1034 | setCurrent_Root(d->roots[0]); | 1159 | setCurrent_Root(w->roots[0]); |
1035 | #if defined (iPlatformAppleMobile) | 1160 | #if defined (iPlatformMobile) |
1036 | iColor back = get_Color(uiBackground_ColorId); | 1161 | iColor back = get_Color(uiBackground_ColorId); |
1037 | if (deviceType_App() == phone_AppDeviceType) { | 1162 | if (deviceType_App() == phone_AppDeviceType) { |
1038 | /* Page background extends to safe area, so fill it completely. */ | 1163 | /* Page background extends to safe area, so fill it completely. */ |
1039 | back = get_Color(tmBackground_ColorId); | 1164 | back = get_Color(tmBackground_ColorId); |
1040 | } | 1165 | } |
1041 | #else | 1166 | #else |
1042 | const iColor back = get_Color(gotFocus && d->place.snap != maximized_WindowSnap && | 1167 | const iColor back = get_Color(gotFocus && d->place.snap != maximized_WindowSnap && |
1043 | ~winFlags & SDL_WINDOW_FULLSCREEN_DESKTOP | 1168 | ~winFlags & SDL_WINDOW_FULLSCREEN_DESKTOP |
@@ -1045,19 +1170,20 @@ void draw_Window(iWindow *d) { | |||
1045 | : uiSeparator_ColorId); | 1170 | : uiSeparator_ColorId); |
1046 | #endif | 1171 | #endif |
1047 | unsetClip_Paint(&p); /* update clip to full window */ | 1172 | unsetClip_Paint(&p); /* update clip to full window */ |
1048 | SDL_SetRenderDrawColor(d->render, back.r, back.g, back.b, 255); | 1173 | SDL_SetRenderDrawColor(w->render, back.r, back.g, back.b, 255); |
1049 | SDL_RenderClear(d->render); | 1174 | SDL_RenderClear(w->render); |
1050 | } | 1175 | } |
1051 | /* Draw widgets. */ | 1176 | /* Draw widgets. */ |
1052 | d->frameTime = SDL_GetTicks(); | 1177 | w->frameTime = SDL_GetTicks(); |
1053 | if (isExposed_Window(d)) { | 1178 | if (isExposed_Window(w)) { |
1054 | d->isInvalidated = iFalse; | 1179 | w->isInvalidated = iFalse; |
1055 | iForIndices(i, d->roots) { | 1180 | extern int drawCount_; |
1056 | iRoot *root = d->roots[i]; | 1181 | iForIndices(i, w->roots) { |
1182 | iRoot *root = w->roots[i]; | ||
1057 | if (root) { | 1183 | if (root) { |
1058 | setCurrent_Root(root); | 1184 | setCurrent_Root(root); |
1059 | unsetClip_Paint(&p); /* update clip to current root */ | 1185 | unsetClip_Paint(&p); /* update clip to current root */ |
1060 | draw_Widget(root->widget); | 1186 | drawRoot_Widget(root->widget); |
1061 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 1187 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
1062 | /* App icon. */ | 1188 | /* App icon. */ |
1063 | const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon"); | 1189 | const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon"); |
@@ -1070,14 +1196,14 @@ void draw_Window(iWindow *d) { | |||
1070 | SDL_SetTextureColorMod(d->appIcon, iconColor.r, iconColor.g, iconColor.b); | 1196 | SDL_SetTextureColorMod(d->appIcon, iconColor.r, iconColor.g, iconColor.b); |
1071 | SDL_SetTextureAlphaMod(d->appIcon, gotFocus || !isLight ? 255 : 92); | 1197 | SDL_SetTextureAlphaMod(d->appIcon, gotFocus || !isLight ? 255 : 92); |
1072 | SDL_RenderCopy( | 1198 | SDL_RenderCopy( |
1073 | d->render, | 1199 | w->render, |
1074 | d->appIcon, | 1200 | d->appIcon, |
1075 | NULL, | 1201 | NULL, |
1076 | &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size }); | 1202 | &(SDL_Rect){ left_Rect(rect) + gap_UI * 1.25f, mid.y - size / 2, size, size }); |
1077 | } | 1203 | } |
1078 | #endif | 1204 | #endif |
1079 | /* Root separator and keyboard focus indicator. */ | 1205 | /* Root separator and keyboard focus indicator. */ |
1080 | if (numRoots_Window(d) > 1){ | 1206 | if (numRoots_Window(w) > 1){ |
1081 | const iRect bounds = bounds_Widget(root->widget); | 1207 | const iRect bounds = bounds_Widget(root->widget); |
1082 | if (i == 1) { | 1208 | if (i == 1) { |
1083 | fillRect_Paint(&p, (iRect){ | 1209 | fillRect_Paint(&p, (iRect){ |
@@ -1085,7 +1211,7 @@ void draw_Window(iWindow *d) { | |||
1085 | init_I2(gap_UI / 4, height_Rect(bounds)) | 1211 | init_I2(gap_UI / 4, height_Rect(bounds)) |
1086 | }, uiSeparator_ColorId); | 1212 | }, uiSeparator_ColorId); |
1087 | } | 1213 | } |
1088 | if (root == d->keyRoot) { | 1214 | if (root == w->keyRoot) { |
1089 | const iBool isDark = isDark_ColorTheme(colorTheme_App()); | 1215 | const iBool isDark = isDark_ColorTheme(colorTheme_App()); |
1090 | fillRect_Paint(&p, (iRect){ | 1216 | fillRect_Paint(&p, (iRect){ |
1091 | topLeft_Rect(bounds), | 1217 | topLeft_Rect(bounds), |
@@ -1097,6 +1223,10 @@ void draw_Window(iWindow *d) { | |||
1097 | } | 1223 | } |
1098 | } | 1224 | } |
1099 | setCurrent_Root(NULL); | 1225 | setCurrent_Root(NULL); |
1226 | #if !defined (NDEBUG) | ||
1227 | draw_Text(defaultBold_FontId, safeRect_Root(w->roots[0]).pos, red_ColorId, "%d", drawCount_); | ||
1228 | drawCount_ = 0; | ||
1229 | #endif | ||
1100 | } | 1230 | } |
1101 | #if 0 | 1231 | #if 0 |
1102 | /* Text cache debugging. */ { | 1232 | /* Text cache debugging. */ { |
@@ -1106,21 +1236,21 @@ void draw_Window(iWindow *d) { | |||
1106 | SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect); | 1236 | SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect); |
1107 | } | 1237 | } |
1108 | #endif | 1238 | #endif |
1109 | SDL_RenderPresent(d->render); | 1239 | SDL_RenderPresent(w->render); |
1110 | } | 1240 | } |
1111 | 1241 | ||
1112 | void resize_Window(iWindow *d, int w, int h) { | 1242 | void resize_MainWindow(iMainWindow *d, int w, int h) { |
1113 | if (w > 0 && h > 0) { | 1243 | if (w > 0 && h > 0) { |
1114 | SDL_SetWindowSize(d->win, w, h); | 1244 | SDL_SetWindowSize(d->base.win, w, h); |
1115 | updateSize_Window_(d, iFalse); | 1245 | updateSize_MainWindow_(d, iFalse); |
1116 | } | 1246 | } |
1117 | else { | 1247 | else { |
1118 | updateSize_Window_(d, iTrue); /* notify always */ | 1248 | updateSize_MainWindow_(d, iTrue); /* notify always */ |
1119 | } | 1249 | } |
1120 | } | 1250 | } |
1121 | 1251 | ||
1122 | void setTitle_Window(iWindow *d, const iString *title) { | 1252 | void setTitle_MainWindow(iMainWindow *d, const iString *title) { |
1123 | SDL_SetWindowTitle(d->win, cstr_String(title)); | 1253 | SDL_SetWindowTitle(d->base.win, cstr_String(title)); |
1124 | iLabelWidget *bar = findChild_Widget(get_Root()->widget, "winbar.title"); | 1254 | iLabelWidget *bar = findChild_Widget(get_Root()->widget, "winbar.title"); |
1125 | if (bar) { | 1255 | if (bar) { |
1126 | updateText_LabelWidget(bar, title); | 1256 | updateText_LabelWidget(bar, title); |
@@ -1128,6 +1258,9 @@ void setTitle_Window(iWindow *d, const iString *title) { | |||
1128 | } | 1258 | } |
1129 | 1259 | ||
1130 | void setUiScale_Window(iWindow *d, float uiScale) { | 1260 | void setUiScale_Window(iWindow *d, float uiScale) { |
1261 | if (uiScale <= 0.0f) { | ||
1262 | uiScale = 1.0f; | ||
1263 | } | ||
1131 | uiScale = iClamp(uiScale, 0.5f, 4.0f); | 1264 | uiScale = iClamp(uiScale, 0.5f, 4.0f); |
1132 | if (d) { | 1265 | if (d) { |
1133 | if (iAbs(d->uiScale - uiScale) > 0.0001f) { | 1266 | if (iAbs(d->uiScale - uiScale) > 0.0001f) { |
@@ -1140,7 +1273,7 @@ void setUiScale_Window(iWindow *d, float uiScale) { | |||
1140 | } | 1273 | } |
1141 | } | 1274 | } |
1142 | 1275 | ||
1143 | void setFreezeDraw_Window(iWindow *d, iBool freezeDraw) { | 1276 | void setFreezeDraw_MainWindow(iMainWindow *d, iBool freezeDraw) { |
1144 | d->isDrawFrozen = freezeDraw; | 1277 | d->isDrawFrozen = freezeDraw; |
1145 | } | 1278 | } |
1146 | 1279 | ||
@@ -1191,63 +1324,80 @@ iWindow *get_Window(void) { | |||
1191 | return theWindow_; | 1324 | return theWindow_; |
1192 | } | 1325 | } |
1193 | 1326 | ||
1327 | void setCurrent_Window(iAnyWindow *d) { | ||
1328 | theWindow_ = d; | ||
1329 | if (type_Window(d) == main_WindowType) { | ||
1330 | theMainWindow_ = d; | ||
1331 | } | ||
1332 | if (d) { | ||
1333 | setCurrent_Text(theWindow_->text); | ||
1334 | setCurrent_Root(theWindow_->keyRoot); | ||
1335 | } | ||
1336 | else { | ||
1337 | setCurrent_Text(NULL); | ||
1338 | setCurrent_Root(NULL); | ||
1339 | } | ||
1340 | } | ||
1341 | |||
1342 | iMainWindow *get_MainWindow(void) { | ||
1343 | return theMainWindow_; | ||
1344 | } | ||
1345 | |||
1194 | iBool isOpenGLRenderer_Window(void) { | 1346 | iBool isOpenGLRenderer_Window(void) { |
1195 | return isOpenGLRenderer_; | 1347 | return isOpenGLRenderer_; |
1196 | } | 1348 | } |
1197 | 1349 | ||
1198 | void setKeyboardHeight_Window(iWindow *d, int height) { | 1350 | void setKeyboardHeight_MainWindow(iMainWindow *d, int height) { |
1199 | if (d->keyboardHeight != height) { | 1351 | if (d->keyboardHeight != height) { |
1200 | d->keyboardHeight = height; | 1352 | d->keyboardHeight = height; |
1201 | if (height == 0) { | ||
1202 | setFlags_Anim(&d->rootOffset, easeBoth_AnimFlag, iTrue); | ||
1203 | setValue_Anim(&d->rootOffset, 0, 250); | ||
1204 | } | ||
1205 | postCommandf_App("keyboard.changed arg:%d", height); | 1353 | postCommandf_App("keyboard.changed arg:%d", height); |
1206 | postRefresh_App(); | 1354 | postRefresh_App(); |
1207 | } | 1355 | } |
1208 | } | 1356 | } |
1209 | 1357 | ||
1210 | void checkPendingSplit_Window(iWindow *d) { | 1358 | void checkPendingSplit_MainWindow(iMainWindow *d) { |
1211 | if (d->splitMode != d->pendingSplitMode) { | 1359 | if (d->splitMode != d->pendingSplitMode) { |
1212 | setSplitMode_Window(d, d->pendingSplitMode); | 1360 | setSplitMode_MainWindow(d, d->pendingSplitMode); |
1213 | } | 1361 | } |
1214 | } | 1362 | } |
1215 | 1363 | ||
1216 | void swapRoots_Window(iWindow *d) { | 1364 | void swapRoots_MainWindow(iMainWindow *d) { |
1217 | if (numRoots_Window(d) == 2) { | 1365 | iWindow *w = as_Window(d); |
1218 | iSwap(iRoot *, d->roots[0], d->roots[1]); | 1366 | if (numRoots_Window(w) == 2) { |
1219 | updateSize_Window_(d, iTrue); | 1367 | iSwap(iRoot *, w->roots[0], w->roots[1]); |
1368 | updateSize_MainWindow_(d, iTrue); | ||
1220 | } | 1369 | } |
1221 | } | 1370 | } |
1222 | 1371 | ||
1223 | void setSplitMode_Window(iWindow *d, int splitFlags) { | 1372 | void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) { |
1224 | const int splitMode = splitFlags & mode_WindowSplit; | 1373 | const int splitMode = splitFlags & mode_WindowSplit; |
1225 | if (deviceType_App() == phone_AppDeviceType) { | 1374 | if (deviceType_App() == phone_AppDeviceType) { |
1226 | /* There isn't enough room on the phone. */ | 1375 | /* There isn't enough room on the phone. */ |
1227 | /* TODO: Maybe in landscape only? */ | 1376 | /* TODO: Maybe in landscape only? */ |
1228 | return; | 1377 | return; |
1229 | } | 1378 | } |
1379 | iWindow *w = as_Window(d); | ||
1230 | iAssert(current_Root() == NULL); | 1380 | iAssert(current_Root() == NULL); |
1231 | if (d->splitMode != splitMode) { | 1381 | if (d->splitMode != splitMode) { |
1232 | int oldCount = numRoots_Window(d); | 1382 | int oldCount = numRoots_Window(w); |
1233 | setFreezeDraw_Window(d, iTrue); | 1383 | setFreezeDraw_MainWindow(d, iTrue); |
1234 | if (oldCount == 2 && splitMode == 0) { | 1384 | if (oldCount == 2 && splitMode == 0) { |
1235 | /* Keep references to the tabs of the second root. */ | 1385 | /* Keep references to the tabs of the second root. */ |
1236 | const iDocumentWidget *curPage = document_Root(d->keyRoot); | 1386 | const iDocumentWidget *curPage = document_Root(w->keyRoot); |
1237 | if (!curPage) { | 1387 | if (!curPage) { |
1238 | /* All tabs closed on that side. */ | 1388 | /* All tabs closed on that side. */ |
1239 | curPage = document_Root(otherRoot_Window(d, d->keyRoot)); | 1389 | curPage = document_Root(otherRoot_Window(w, w->keyRoot)); |
1240 | } | 1390 | } |
1241 | iObjectList *tabs = listDocuments_App(d->roots[1]); | 1391 | iObjectList *tabs = listDocuments_App(w->roots[1]); |
1242 | iForEach(ObjectList, i, tabs) { | 1392 | iForEach(ObjectList, i, tabs) { |
1243 | setRoot_Widget(i.object, d->roots[0]); | 1393 | setRoot_Widget(i.object, w->roots[0]); |
1244 | } | 1394 | } |
1245 | setFocus_Widget(NULL); | 1395 | setFocus_Widget(NULL); |
1246 | delete_Root(d->roots[1]); | 1396 | delete_Root(w->roots[1]); |
1247 | d->roots[1] = NULL; | 1397 | w->roots[1] = NULL; |
1248 | d->keyRoot = d->roots[0]; | 1398 | w->keyRoot = w->roots[0]; |
1249 | /* Move the deleted root's tabs to the first root. */ | 1399 | /* Move the deleted root's tabs to the first root. */ |
1250 | setCurrent_Root(d->roots[0]); | 1400 | setCurrent_Root(w->roots[0]); |
1251 | iWidget *docTabs = findWidget_Root("doctabs"); | 1401 | iWidget *docTabs = findWidget_Root("doctabs"); |
1252 | iForEach(ObjectList, j, tabs) { | 1402 | iForEach(ObjectList, j, tabs) { |
1253 | appendTabPage_Widget(docTabs, j.object, "", 0, 0); | 1403 | appendTabPage_Widget(docTabs, j.object, "", 0, 0); |
@@ -1259,38 +1409,48 @@ void setSplitMode_Window(iWindow *d, int splitFlags) { | |||
1259 | } | 1409 | } |
1260 | else if (splitMode && oldCount == 1) { | 1410 | else if (splitMode && oldCount == 1) { |
1261 | /* Add a second root. */ | 1411 | /* Add a second root. */ |
1262 | iDocumentWidget *moved = document_Root(d->roots[0]); | 1412 | iDocumentWidget *moved = document_Root(w->roots[0]); |
1263 | iAssert(d->roots[1] == NULL); | 1413 | iAssert(w->roots[1] == NULL); |
1264 | const iBool addToLeft = (prefs_App()->pinSplit == 2); | 1414 | const iBool addToLeft = (prefs_App()->pinSplit == 2); |
1265 | size_t newRootIndex = 1; | 1415 | size_t newRootIndex = 1; |
1266 | if (addToLeft) { | 1416 | if (addToLeft) { |
1267 | iSwap(iRoot *, d->roots[0], d->roots[1]); | 1417 | iSwap(iRoot *, w->roots[0], w->roots[1]); |
1268 | newRootIndex = 0; | 1418 | newRootIndex = 0; |
1269 | } | 1419 | } |
1270 | d->roots[newRootIndex] = new_Root(); | 1420 | w->roots[newRootIndex] = new_Root(); |
1271 | d->keyRoot = d->roots[newRootIndex]; | 1421 | w->keyRoot = w->roots[newRootIndex]; |
1272 | setCurrent_Root(d->roots[newRootIndex]); | 1422 | w->keyRoot->window = w; |
1273 | createUserInterface_Root(d->roots[newRootIndex]); | 1423 | setCurrent_Root(w->roots[newRootIndex]); |
1424 | createUserInterface_Root(w->roots[newRootIndex]); | ||
1425 | /* Bookmark folder state will match the old root's state. */ { | ||
1426 | for (int sb = 0; sb < 2; sb++) { | ||
1427 | const char *sbId = (sb == 0 ? "sidebar" : "sidebar2"); | ||
1428 | setClosedFolders_SidebarWidget( | ||
1429 | findChild_Widget(w->roots[newRootIndex]->widget, sbId), | ||
1430 | closedFolders_SidebarWidget( | ||
1431 | findChild_Widget(w->roots[newRootIndex ^ 1]->widget, sbId))); | ||
1432 | } | ||
1433 | } | ||
1274 | if (!isEmpty_String(d->pendingSplitUrl)) { | 1434 | if (!isEmpty_String(d->pendingSplitUrl)) { |
1275 | postCommandf_Root(d->roots[newRootIndex], "open url:%s", | 1435 | postCommandf_Root(w->roots[newRootIndex], "open url:%s", |
1276 | cstr_String(d->pendingSplitUrl)); | 1436 | cstr_String(d->pendingSplitUrl)); |
1277 | clear_String(d->pendingSplitUrl); | 1437 | clear_String(d->pendingSplitUrl); |
1278 | } | 1438 | } |
1279 | else if (~splitFlags & noEvents_WindowSplit) { | 1439 | else if (~splitFlags & noEvents_WindowSplit) { |
1280 | iWidget *docTabs0 = findChild_Widget(d->roots[newRootIndex ^ 1]->widget, "doctabs"); | 1440 | iWidget *docTabs0 = findChild_Widget(w->roots[newRootIndex ^ 1]->widget, "doctabs"); |
1281 | iWidget *docTabs1 = findChild_Widget(d->roots[newRootIndex]->widget, "doctabs"); | 1441 | iWidget *docTabs1 = findChild_Widget(w->roots[newRootIndex]->widget, "doctabs"); |
1282 | /* If the old root has multiple tabs, move the current one to the new split. */ | 1442 | /* If the old root has multiple tabs, move the current one to the new split. */ |
1283 | if (tabCount_Widget(docTabs0) >= 2) { | 1443 | if (tabCount_Widget(docTabs0) >= 2) { |
1284 | int movedIndex = tabPageIndex_Widget(docTabs0, moved); | 1444 | int movedIndex = tabPageIndex_Widget(docTabs0, moved); |
1285 | removeTabPage_Widget(docTabs0, movedIndex); | 1445 | removeTabPage_Widget(docTabs0, movedIndex); |
1286 | showTabPage_Widget(docTabs0, tabPage_Widget(docTabs0, iMax(movedIndex - 1, 0))); | 1446 | showTabPage_Widget(docTabs0, tabPage_Widget(docTabs0, iMax(movedIndex - 1, 0))); |
1287 | iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */ | 1447 | iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */ |
1288 | setRoot_Widget(as_Widget(moved), d->roots[newRootIndex]); | 1448 | setRoot_Widget(as_Widget(moved), w->roots[newRootIndex]); |
1289 | prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0); | 1449 | prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0); |
1290 | postCommandf_App("tabs.switch page:%p", moved); | 1450 | postCommandf_App("tabs.switch page:%p", moved); |
1291 | } | 1451 | } |
1292 | else { | 1452 | else { |
1293 | postCommand_Root(d->roots[newRootIndex], "navigate.home"); | 1453 | postCommand_Root(w->roots[newRootIndex], "navigate.home"); |
1294 | } | 1454 | } |
1295 | } | 1455 | } |
1296 | setCurrent_Root(NULL); | 1456 | setCurrent_Root(NULL); |
@@ -1319,26 +1479,26 @@ void setSplitMode_Window(iWindow *d, int splitFlags) { | |||
1319 | } | 1479 | } |
1320 | #endif | 1480 | #endif |
1321 | if (~splitFlags & noEvents_WindowSplit) { | 1481 | if (~splitFlags & noEvents_WindowSplit) { |
1322 | updateSize_Window_(d, iTrue); | 1482 | updateSize_MainWindow_(d, iTrue); |
1323 | postCommand_App("window.unfreeze"); | 1483 | postCommand_App("window.unfreeze"); |
1324 | } | 1484 | } |
1325 | } | 1485 | } |
1326 | } | 1486 | } |
1327 | 1487 | ||
1328 | void setSnap_Window(iWindow *d, int snapMode) { | 1488 | void setSnap_MainWindow(iMainWindow *d, int snapMode) { |
1329 | if (!prefs_App()->customFrame) { | 1489 | if (!prefs_App()->customFrame) { |
1330 | if (snapMode == maximized_WindowSnap) { | 1490 | if (snapMode == maximized_WindowSnap) { |
1331 | SDL_MaximizeWindow(d->win); | 1491 | SDL_MaximizeWindow(d->base.win); |
1332 | } | 1492 | } |
1333 | else if (snapMode == fullscreen_WindowSnap) { | 1493 | else if (snapMode == fullscreen_WindowSnap) { |
1334 | SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP); | 1494 | SDL_SetWindowFullscreen(d->base.win, SDL_WINDOW_FULLSCREEN_DESKTOP); |
1335 | } | 1495 | } |
1336 | else { | 1496 | else { |
1337 | if (snap_Window(d) == fullscreen_WindowSnap) { | 1497 | if (snap_MainWindow(d) == fullscreen_WindowSnap) { |
1338 | SDL_SetWindowFullscreen(d->win, 0); | 1498 | SDL_SetWindowFullscreen(d->base.win, 0); |
1339 | } | 1499 | } |
1340 | else { | 1500 | else { |
1341 | SDL_RestoreWindow(d->win); | 1501 | SDL_RestoreWindow(d->base.win); |
1342 | } | 1502 | } |
1343 | } | 1503 | } |
1344 | return; | 1504 | return; |
@@ -1350,9 +1510,9 @@ void setSnap_Window(iWindow *d, int snapMode) { | |||
1350 | const int snapDist = gap_UI * 4; | 1510 | const int snapDist = gap_UI * 4; |
1351 | iRect newRect = zero_Rect(); | 1511 | iRect newRect = zero_Rect(); |
1352 | SDL_Rect usable; | 1512 | SDL_Rect usable; |
1353 | SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->win), &usable); | 1513 | SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(d->base.win), &usable); |
1354 | if (d->place.snap == fullscreen_WindowSnap) { | 1514 | if (d->place.snap == fullscreen_WindowSnap) { |
1355 | SDL_SetWindowFullscreen(d->win, 0); | 1515 | SDL_SetWindowFullscreen(d->base.win, 0); |
1356 | } | 1516 | } |
1357 | d->place.snap = snapMode & ~redo_WindowSnap; | 1517 | d->place.snap = snapMode & ~redo_WindowSnap; |
1358 | switch (snapMode & mask_WindowSnap) { | 1518 | switch (snapMode & mask_WindowSnap) { |
@@ -1373,8 +1533,8 @@ void setSnap_Window(iWindow *d, int snapMode) { | |||
1373 | case yMaximized_WindowSnap: | 1533 | case yMaximized_WindowSnap: |
1374 | newRect.pos.y = 0; | 1534 | newRect.pos.y = 0; |
1375 | newRect.size.y = usable.h; | 1535 | newRect.size.y = usable.h; |
1376 | SDL_GetWindowSize(d->win, &newRect.size.x, NULL); | 1536 | SDL_GetWindowSize(d->base.win, &newRect.size.x, NULL); |
1377 | SDL_GetWindowPosition(d->win, &newRect.pos.x, NULL); | 1537 | SDL_GetWindowPosition(d->base.win, &newRect.pos.x, NULL); |
1378 | /* Snap the window to left/right edges, if close by. */ | 1538 | /* Snap the window to left/right edges, if close by. */ |
1379 | if (iAbs(right_Rect(newRect) - (usable.x + usable.w)) < snapDist) { | 1539 | if (iAbs(right_Rect(newRect) - (usable.x + usable.w)) < snapDist) { |
1380 | newRect.pos.x = usable.x + usable.w - width_Rect(newRect); | 1540 | newRect.pos.x = usable.x + usable.w - width_Rect(newRect); |
@@ -1384,7 +1544,7 @@ void setSnap_Window(iWindow *d, int snapMode) { | |||
1384 | } | 1544 | } |
1385 | break; | 1545 | break; |
1386 | case fullscreen_WindowSnap: | 1546 | case fullscreen_WindowSnap: |
1387 | SDL_SetWindowFullscreen(d->win, SDL_WINDOW_FULLSCREEN_DESKTOP); | 1547 | SDL_SetWindowFullscreen(d->base.win, SDL_WINDOW_FULLSCREEN_DESKTOP); |
1388 | break; | 1548 | break; |
1389 | } | 1549 | } |
1390 | if (snapMode & (topBit_WindowSnap | bottomBit_WindowSnap)) { | 1550 | if (snapMode & (topBit_WindowSnap | bottomBit_WindowSnap)) { |
@@ -1416,9 +1576,9 @@ void setSnap_Window(iWindow *d, int snapMode) { | |||
1416 | #endif /* defined (LAGRANGE_ENABLE_CUSTOM_FRAME) */ | 1576 | #endif /* defined (LAGRANGE_ENABLE_CUSTOM_FRAME) */ |
1417 | } | 1577 | } |
1418 | 1578 | ||
1419 | int snap_Window(const iWindow *d) { | 1579 | int snap_MainWindow(const iMainWindow *d) { |
1420 | if (!prefs_App()->customFrame) { | 1580 | if (!prefs_App()->customFrame) { |
1421 | const int flags = SDL_GetWindowFlags(d->win); | 1581 | const int flags = SDL_GetWindowFlags(d->base.win); |
1422 | if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { | 1582 | if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { |
1423 | return fullscreen_WindowSnap; | 1583 | return fullscreen_WindowSnap; |
1424 | } | 1584 | } |
@@ -1429,3 +1589,27 @@ int snap_Window(const iWindow *d) { | |||
1429 | } | 1589 | } |
1430 | return d->place.snap; | 1590 | return d->place.snap; |
1431 | } | 1591 | } |
1592 | |||
1593 | /*----------------------------------------------------------------------------------------------*/ | ||
1594 | |||
1595 | iWindow *newPopup_Window(iInt2 screenPos, iWidget *rootWidget) { | ||
1596 | iWindow *win = | ||
1597 | new_Window(popup_WindowType, | ||
1598 | (iRect){ screenPos, divf_I2(rootWidget->rect.size, get_Window()->pixelRatio) }, | ||
1599 | SDL_WINDOW_ALWAYS_ON_TOP | | ||
1600 | #if !defined (iPlatformAppleDesktop) | ||
1601 | SDL_WINDOW_BORDERLESS | | ||
1602 | #endif | ||
1603 | SDL_WINDOW_POPUP_MENU | | ||
1604 | SDL_WINDOW_SKIP_TASKBAR); | ||
1605 | #if defined (iPlatformAppleDesktop) | ||
1606 | hideTitleBar_MacOS(win); /* make it a borderless window */ | ||
1607 | #endif | ||
1608 | iRoot *root = new_Root(); | ||
1609 | win->roots[0] = root; | ||
1610 | win->keyRoot = root; | ||
1611 | root->widget = rootWidget; | ||
1612 | root->window = win; | ||
1613 | setRoot_Widget(rootWidget, root); | ||
1614 | return win; | ||
1615 | } | ||