summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--src/app.c20
-rw-r--r--src/ui/documentwidget.c4
-rw-r--r--src/ui/labelwidget.c12
-rw-r--r--src/ui/labelwidget.h1
-rw-r--r--src/ui/widget.c2
-rw-r--r--src/ui/window.c142
7 files changed, 176 insertions, 11 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8b0dd9b..38d36377 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,7 @@ option (ENABLE_RELATIVE_EMBED "Resources should always be found via relative p
33option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF) 33option (ENABLE_WINDOWPOS_FIX "Set position after showing window (workaround for SDL bug)" OFF)
34option (ENABLE_IDLE_SLEEP "While idle, sleep in the main thread instead of waiting for events" ON) 34option (ENABLE_IDLE_SLEEP "While idle, sleep in the main thread instead of waiting for events" ON)
35option (ENABLE_DOWNLOAD_EDIT "Allow changing the Downloads directory" ON) 35option (ENABLE_DOWNLOAD_EDIT "Allow changing the Downloads directory" ON)
36option (ENABLE_CUSTOM_FRAME "Draw a custom window frame (Windows)" OFF)
36 37
37include (BuildType.cmake) 38include (BuildType.cmake)
38include (res/Embed.cmake) 39include (res/Embed.cmake)
@@ -220,6 +221,9 @@ endif ()
220if (ENABLE_DOWNLOAD_EDIT) 221if (ENABLE_DOWNLOAD_EDIT)
221 target_compile_definitions (app PUBLIC LAGRANGE_DOWNLOAD_EDIT=1) 222 target_compile_definitions (app PUBLIC LAGRANGE_DOWNLOAD_EDIT=1)
222endif () 223endif ()
224if (ENABLE_CUSTOM_FRAME AND MSYS)
225 target_compile_definitions (app PUBLIC LAGRANGE_CUSTOM_FRAME=1)
226endif ()
223target_link_libraries (app PUBLIC the_Foundation::the_Foundation) 227target_link_libraries (app PUBLIC the_Foundation::the_Foundation)
224target_link_libraries (app PUBLIC ${SDL2_LDFLAGS}) 228target_link_libraries (app PUBLIC ${SDL2_LDFLAGS})
225if (APPLE) 229if (APPLE)
@@ -233,7 +237,7 @@ if (APPLE)
233 target_link_options (app PUBLIC -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}) 237 target_link_options (app PUBLIC -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET})
234 endif () 238 endif ()
235 if (XCODE_DEVELOPMENT_TEAM) 239 if (XCODE_DEVELOPMENT_TEAM)
236 set_property (TARGET app PROPERTY 240 set_property (TARGET app PROPERTY
237 XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${XCODE_DEVELOPMENT_TEAM} 241 XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${XCODE_DEVELOPMENT_TEAM}
238 ) 242 )
239 endif () 243 endif ()
diff --git a/src/app.c b/src/app.c
index 90ad271d..b50321b2 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1112,14 +1112,24 @@ iBool handleCommand_App(const char *cmd) {
1112 return iTrue; 1112 return iTrue;
1113 } 1113 }
1114 else if (equal_Command(cmd, "window.maximize")) { 1114 else if (equal_Command(cmd, "window.maximize")) {
1115 SDL_MaximizeWindow(d->window->win); 1115 if (!argLabel_Command(cmd, "toggle")) {
1116 SDL_MaximizeWindow(d->window->win);
1117 }
1118 else {
1119 if (SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_MAXIMIZED) {
1120 SDL_RestoreWindow(d->window->win);
1121 }
1122 else {
1123 SDL_MaximizeWindow(d->window->win);
1124 }
1125 }
1116 return iTrue; 1126 return iTrue;
1117 } 1127 }
1118 else if (equal_Command(cmd, "window.fullscreen")) { 1128 else if (equal_Command(cmd, "window.fullscreen")) {
1119 SDL_SetWindowFullscreen(d->window->win, 1129 const iBool wasFull =
1120 SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_FULLSCREEN_DESKTOP 1130 (SDL_GetWindowFlags(d->window->win) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
1121 ? 0 1131 SDL_SetWindowFullscreen(d->window->win, wasFull ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP);
1122 : SDL_WINDOW_FULLSCREEN_DESKTOP); 1132 postCommandf_App("window.fullscreen.changed arg:%d", !wasFull);
1123 return iTrue; 1133 return iTrue;
1124 } 1134 }
1125 else if (equal_Command(cmd, "font.set")) { 1135 else if (equal_Command(cmd, "font.set")) {
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index e35892cf..6ec76c0a 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -633,7 +633,9 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) {
633 iUrl parts; 633 iUrl parts;
634 init_Url(&parts, d->mod.url); 634 init_Url(&parts, d->mod.url);
635 if (equalCase_Rangecc(parts.scheme, "about")) { 635 if (equalCase_Rangecc(parts.scheme, "about")) {
636 pushBackCStr_StringArray(title, "Lagrange"); 636 if (!findWidget_App("winbar")) {
637 pushBackCStr_StringArray(title, "Lagrange");
638 }
637 } 639 }
638 else if (!isEmpty_Range(&parts.host)) { 640 else if (!isEmpty_Range(&parts.host)) {
639 pushBackRange_StringArray(title, parts.host); 641 pushBackRange_StringArray(title, parts.host);
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index 57136509..d7e6c020 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -38,6 +38,7 @@ struct Impl_LabelWidget {
38 int font; 38 int font;
39 int key; 39 int key;
40 int kmods; 40 int kmods;
41 int forceFg;
41 iString command; 42 iString command;
42 iBool alignVisual; /* align according to visible bounds, not typography */ 43 iBool alignVisual; /* align according to visible bounds, not typography */
43 iClick click; 44 iClick click;
@@ -177,6 +178,9 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int
177 } 178 }
178 *fg = uiTextPressed_ColorId | permanent_ColorId; 179 *fg = uiTextPressed_ColorId | permanent_ColorId;
179 } 180 }
181 if (d->forceFg >= 0) {
182 *fg = d->forceFg;
183 }
180} 184}
181 185
182static void draw_LabelWidget_(const iLabelWidget *d) { 186static void draw_LabelWidget_(const iLabelWidget *d) {
@@ -277,6 +281,7 @@ void updateSize_LabelWidget(iLabelWidget *d) {
277void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { 281void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) {
278 init_Widget(&d->widget); 282 init_Widget(&d->widget);
279 d->font = uiLabel_FontId; 283 d->font = uiLabel_FontId;
284 d->forceFg = none_ColorId;
280 initCStr_String(&d->label, label); 285 initCStr_String(&d->label, label);
281 if (cmd) { 286 if (cmd) {
282 initCStr_String(&d->command, cmd); 287 initCStr_String(&d->command, cmd);
@@ -304,6 +309,13 @@ void setFont_LabelWidget(iLabelWidget *d, int fontId) {
304 updateSize_LabelWidget(d); 309 updateSize_LabelWidget(d);
305} 310}
306 311
312void setTextColor_LabelWidget(iLabelWidget *d, int color) {
313 if (d && d->forceFg != color) {
314 d->forceFg = color;
315 refresh_Widget(d);
316 }
317}
318
307void setText_LabelWidget(iLabelWidget *d, const iString *text) { 319void setText_LabelWidget(iLabelWidget *d, const iString *text) {
308 updateText_LabelWidget(d, text); 320 updateText_LabelWidget(d, text);
309 updateSize_LabelWidget(d); 321 updateSize_LabelWidget(d);
diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h
index c91793e5..266c3b02 100644
--- a/src/ui/labelwidget.h
+++ b/src/ui/labelwidget.h
@@ -31,6 +31,7 @@ iDeclareObjectConstructionArgs(LabelWidget, const char *label, const char *comma
31 31
32void setAlignVisually_LabelWidget(iLabelWidget *, iBool alignVisual); 32void setAlignVisually_LabelWidget(iLabelWidget *, iBool alignVisual);
33void setFont_LabelWidget (iLabelWidget *, int fontId); 33void setFont_LabelWidget (iLabelWidget *, int fontId);
34void setTextColor_LabelWidget (iLabelWidget *, int color);
34void setText_LabelWidget (iLabelWidget *, const iString *text); /* resizes widget */ 35void setText_LabelWidget (iLabelWidget *, const iString *text); /* resizes widget */
35void setTextCStr_LabelWidget (iLabelWidget *, const char *text); 36void setTextCStr_LabelWidget (iLabelWidget *, const char *text);
36void setCommand_LabelWidget (iLabelWidget *, const iString *command); 37void setCommand_LabelWidget (iLabelWidget *, const iString *command);
diff --git a/src/ui/widget.c b/src/ui/widget.c
index ddb3f092..b60c67e3 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -149,6 +149,8 @@ void setPos_Widget(iWidget *d, iInt2 pos) {
149} 149}
150 150
151void setSize_Widget(iWidget *d, iInt2 size) { 151void setSize_Widget(iWidget *d, iInt2 size) {
152 if (size.x < 0) size.x = d->rect.size.x;
153 if (size.y < 0) size.y = d->rect.size.y;
152 d->rect.size = size; 154 d->rect.size = size;
153 setFlags_Widget(d, fixedSize_WidgetFlag, iTrue); 155 setFlags_Widget(d, fixedSize_WidgetFlag, iTrue);
154} 156}
diff --git a/src/ui/window.c b/src/ui/window.c
index 2d65a655..6356b292 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -84,8 +84,32 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
84 } 84 }
85 else if (equal_Command(cmd, "window.focus.lost")) { 85 else if (equal_Command(cmd, "window.focus.lost")) {
86 setFocus_Widget(NULL); 86 setFocus_Widget(NULL);
87 setTextColor_LabelWidget(findWidget_App("winbar.app"), uiAnnotation_ColorId);
88 setTextColor_LabelWidget(findWidget_App("winbar.title"), uiAnnotation_ColorId);
87 return iFalse; 89 return iFalse;
88 } 90 }
91 else if (equal_Command(cmd, "window.focus.gained")) {
92 setTextColor_LabelWidget(findWidget_App("winbar.app"), uiTextCaution_ColorId);
93 setTextColor_LabelWidget(findWidget_App("winbar.title"), uiTextStrong_ColorId);
94 return iFalse;
95 }
96 else if (equal_Command(cmd, "window.fullscreen.changed")) {
97 iWidget *winBar = findWidget_App("winbar");
98 if (winBar) {
99 setFlags_Widget(winBar, hidden_WidgetFlag, arg_Command(cmd));
100 arrange_Widget(get_Window()->root);
101 postRefresh_App();
102 }
103 return iFalse;
104 }
105 else if (equal_Command(cmd, "window.minimize")) {
106 SDL_MinimizeWindow(get_Window()->win);
107 return iTrue;
108 }
109 else if (equal_Command(cmd, "window.close")) {
110 SDL_PushEvent(&(SDL_Event){ .type = SDL_QUIT });
111 return iTrue;
112 }
89 else if (handleCommand_App(cmd)) { 113 else if (handleCommand_App(cmd)) {
90 return iTrue; 114 return iTrue;
91 } 115 }
@@ -503,6 +527,12 @@ static iBool handleSearchBarCommands_(iWidget *searchBar, const char *cmd) {
503 return iFalse; 527 return iFalse;
504} 528}
505 529
530static iLabelWidget *newLargeIcon_LabelWidget(const char *text, const char *cmd) {
531 iLabelWidget *lab = newIcon_LabelWidget(text, 0, 0, cmd);
532 setFont_LabelWidget(lab, uiLabelLarge_FontId);
533 return lab;
534}
535
506static void setupUserInterface_Window(iWindow *d) { 536static void setupUserInterface_Window(iWindow *d) {
507 /* Children of root cover the entire window. */ 537 /* Children of root cover the entire window. */
508 setFlags_Widget(d->root, resizeChildren_WidgetFlag, iTrue); 538 setFlags_Widget(d->root, resizeChildren_WidgetFlag, iTrue);
@@ -512,11 +542,58 @@ static void setupUserInterface_Window(iWindow *d) {
512 setId_Widget(div, "navdiv"); 542 setId_Widget(div, "navdiv");
513 addChild_Widget(d->root, iClob(div)); 543 addChild_Widget(d->root, iClob(div));
514 544
545#if defined (LAGRANGE_CUSTOM_FRAME)
546 /* Window title bar. */ {
547 const int border = gap_UI / 4;
548 setPadding1_Widget(div, border); /* draggable edges */
549 iWidget *winBar = new_Widget();
550 setId_Widget(winBar, "winbar");
551 setFlags_Widget(winBar,
552 arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag |
553 arrangeHorizontal_WidgetFlag | collapse_WidgetFlag,
554 iTrue);
555 iLabelWidget *appButton =
556 addChildFlags_Widget(winBar,
557 iClob(new_LabelWidget("Lagrange", NULL)),
558 fixedHeight_WidgetFlag | frameless_WidgetFlag);
559 setTextColor_LabelWidget(appButton, uiTextCaution_ColorId);
560 setId_Widget(as_Widget(appButton), "winbar.app");
561 iLabelWidget *appTitle;
562 setFont_LabelWidget(appButton, uiContentBold_FontId);
563 setId_Widget(addChildFlags_Widget(winBar,
564 iClob(appTitle = new_LabelWidget("", NULL)),
565 expand_WidgetFlag | alignLeft_WidgetFlag |
566 fixedHeight_WidgetFlag | frameless_WidgetFlag |
567 commandOnClick_WidgetFlag),
568 "winbar.title");
569 setTextColor_LabelWidget(appTitle, uiTextStrong_ColorId);
570 iLabelWidget *appMin, *appMax, *appClose;
571 setId_Widget(addChildFlags_Widget(
572 winBar,
573 iClob(appMin = newLargeIcon_LabelWidget("\u2014", "window.minimize")),
574 frameless_WidgetFlag),
575 "winbar.min");
576 setSize_Widget(as_Widget(appMin),
577 init_I2(gap_UI * 11.5f, height_Widget(appTitle)));
578 addChildFlags_Widget(
579 winBar,
580 iClob(appMax = newLargeIcon_LabelWidget("\u25a1", "window.maximize toggle:1")),
581 frameless_WidgetFlag);
582 addChildFlags_Widget(winBar,
583 iClob(appClose = newLargeIcon_LabelWidget("\u2a2f", "window.close")),
584 frameless_WidgetFlag);
585 setSize_Widget(as_Widget(appMax), as_Widget(appMin)->rect.size);
586 setSize_Widget(as_Widget(appClose), as_Widget(appMin)->rect.size);
587 addChild_Widget(div, iClob(winBar));
588 setBackgroundColor_Widget(winBar, uiBackground_ColorId);
589 }
590#endif
591
515 /* Navigation bar. */ { 592 /* Navigation bar. */ {
516 iWidget *navBar = new_Widget(); 593 iWidget *navBar = new_Widget();
517 setId_Widget(navBar, "navbar"); 594 setId_Widget(navBar, "navbar");
518 /*setPadding_Widget(navBar, gap_UI / 2, 0, gap_UI / 2, 0);*/ 595 int topPad = !findChild_Widget(div, "winbar") ? gap_UI / 2 : 0;
519 setPadding_Widget(navBar, gap_UI, gap_UI / 2, gap_UI, gap_UI / 2); 596 setPadding_Widget(navBar, gap_UI, topPad, gap_UI, gap_UI / 2);
520 setFlags_Widget(navBar, 597 setFlags_Widget(navBar,
521 arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag | 598 arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag |
522 arrangeHorizontal_WidgetFlag, 599 arrangeHorizontal_WidgetFlag,
@@ -697,7 +774,7 @@ static float pixelRatio_Window_(const iWindow *d) {
697 SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(d->win), NULL, NULL, &vdpi); 774 SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(d->win), NULL, NULL, &vdpi);
698 const float factor = vdpi / 96.0f; 775 const float factor = vdpi / 96.0f;
699 return iMax(1.0f, factor); 776 return iMax(1.0f, factor);
700#else 777#else
701 int dx, x; 778 int dx, x;
702 SDL_GetRendererOutputSize(d->render, &dx, NULL); 779 SDL_GetRendererOutputSize(d->render, &dx, NULL);
703 SDL_GetWindowSize(d->win, &x, NULL); 780 SDL_GetWindowSize(d->win, &x, NULL);
@@ -712,12 +789,61 @@ static void drawBlank_Window_(iWindow *d) {
712 SDL_RenderPresent(d->render); 789 SDL_RenderPresent(d->render);
713} 790}
714 791
792#if defined (LAGRANGE_CUSTOM_FRAME)
793static SDL_HitTestResult hitTest_Window_(SDL_Window *win, const SDL_Point *pos, void *data) {
794 iWindow *d = data;
795 iAssert(d->win == win);
796 int w, h;
797 SDL_GetWindowSize(win, &w, &h);
798 /* TODO: Check if inside the caption label widget. */
799 const iBool isLeft = pos->x < gap_UI;
800 const iBool isRight = pos->x >= w - gap_UI;
801 const iBool isTop = pos->y < gap_UI;
802 const iBool isBottom = pos->y >= h - gap_UI;
803 const int captionHeight = lineHeight_Text(uiContent_FontId) + gap_UI * 2;
804 const int rightEdge = left_Rect(bounds_Widget(findChild_Widget(d->root, "winbar.min")));
805 if (isLeft) {
806 return pos->y < captionHeight ? SDL_HITTEST_RESIZE_TOPLEFT
807 : pos->y > h - captionHeight ? SDL_HITTEST_RESIZE_BOTTOMLEFT
808 : SDL_HITTEST_RESIZE_LEFT;
809 }
810 if (isRight) {
811 return pos->y < captionHeight ? SDL_HITTEST_RESIZE_TOPRIGHT
812 : pos->y > h - captionHeight ? SDL_HITTEST_RESIZE_BOTTOMRIGHT
813 : SDL_HITTEST_RESIZE_RIGHT;
814 }
815 if (isTop) {
816 return pos->x < captionHeight ? SDL_HITTEST_RESIZE_TOPLEFT
817 : pos->x > w - captionHeight ? SDL_HITTEST_RESIZE_TOPRIGHT
818 : SDL_HITTEST_RESIZE_TOP;
819 }
820 if (isBottom) {
821 return pos->x < captionHeight ? SDL_HITTEST_RESIZE_BOTTOMLEFT
822 : pos->x > w - captionHeight ? SDL_HITTEST_RESIZE_BOTTOMRIGHT
823 : SDL_HITTEST_RESIZE_BOTTOM;
824 }
825 if (pos->x < rightEdge && pos->y < captionHeight) {
826 return SDL_HITTEST_DRAGGABLE;
827 }
828 return SDL_HITTEST_NORMAL;
829}
830#endif
831
715iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) { 832iBool create_Window_(iWindow *d, iRect rect, uint32_t flags) {
716 flags |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN; 833 flags |= SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN;
834#if defined (LAGRANGE_CUSTOM_FRAME)
835 /* We are drawing a custom frame so hide the default one. */
836 flags |= SDL_WINDOW_BORDERLESS;
837#endif
717 if (SDL_CreateWindowAndRenderer( 838 if (SDL_CreateWindowAndRenderer(
718 width_Rect(rect), height_Rect(rect), flags, &d->win, &d->render)) { 839 width_Rect(rect), height_Rect(rect), flags, &d->win, &d->render)) {
719 return iFalse; 840 return iFalse;
720 } 841 }
842#if defined (LAGRANGE_CUSTOM_FRAME)
843 /* Register a handler for window hit testing (drag, resize). */
844 SDL_SetWindowHitTest(d->win, hitTest_Window_, d);
845 SDL_SetWindowResizable(d->win, SDL_TRUE);
846#endif
721 return iTrue; 847 return iTrue;
722} 848}
723 849
@@ -776,6 +902,9 @@ void init_Window(iWindow *d, iRect rect) {
776 d->pixelRatio = pixelRatio_Window_(d); 902 d->pixelRatio = pixelRatio_Window_(d);
777 setPixelRatio_Metrics(d->pixelRatio * d->uiScale); 903 setPixelRatio_Metrics(d->pixelRatio * d->uiScale);
778#if defined (iPlatformMsys) 904#if defined (iPlatformMsys)
905 SDL_Rect usable;
906 SDL_GetDisplayUsableBounds(0, &usable);
907 SDL_SetWindowMaximumSize(d->win, usable.w, usable.h);
779 SDL_SetWindowMinimumSize(d->win, minSize.x * d->pixelRatio, minSize.y * d->pixelRatio); 908 SDL_SetWindowMinimumSize(d->win, minSize.x * d->pixelRatio, minSize.y * d->pixelRatio);
780 useExecutableIconResource_SDLWindow(d->win); 909 useExecutableIconResource_SDLWindow(d->win);
781#endif 910#endif
@@ -1006,7 +1135,8 @@ void draw_Window(iWindow *d) {
1006// printf("draw %d\n", d->frameTime); fflush(stdout); 1135// printf("draw %d\n", d->frameTime); fflush(stdout);
1007//#endif 1136//#endif
1008 /* Clear the window. */ 1137 /* Clear the window. */
1009 SDL_SetRenderDrawColor(d->render, 0, 0, 0, 255); 1138 const iColor back = get_Color(uiSeparator_ColorId);
1139 SDL_SetRenderDrawColor(d->render, back.r, back.g, back.b, 255);
1010 SDL_RenderClear(d->render); 1140 SDL_RenderClear(d->render);
1011 /* Draw widgets. */ 1141 /* Draw widgets. */
1012 d->frameTime = SDL_GetTicks(); 1142 d->frameTime = SDL_GetTicks();
@@ -1030,6 +1160,10 @@ void resize_Window(iWindow *d, int w, int h) {
1030 1160
1031void setTitle_Window(iWindow *d, const iString *title) { 1161void setTitle_Window(iWindow *d, const iString *title) {
1032 SDL_SetWindowTitle(d->win, cstr_String(title)); 1162 SDL_SetWindowTitle(d->win, cstr_String(title));
1163 iLabelWidget *bar = findChild_Widget(d->root, "winbar.title");
1164 if (bar) {
1165 updateText_LabelWidget(bar, title);
1166 }
1033} 1167}
1034 1168
1035void setUiScale_Window(iWindow *d, float uiScale) { 1169void setUiScale_Window(iWindow *d, float uiScale) {