summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-03-04 09:14:30 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-03-04 09:14:30 +0200
commit1d735299127aaf0474d8fc2a1c5ec4947c1dca9a (patch)
tree926f4d136a2799c5fcf62589259ede57c04c35a9
parent2f6fd59952ce3d76b15a4b7b8f526e27edd39775 (diff)
Changing UI scaling at runtime
UI scaling factor is applied when closing the Preferences dialog. IssueID #83
-rw-r--r--src/app.c2
-rw-r--r--src/ui/documentwidget.c5
-rw-r--r--src/ui/inputwidget.c46
-rw-r--r--src/ui/labelwidget.c17
-rw-r--r--src/ui/labelwidget.h6
-rw-r--r--src/ui/listwidget.c5
-rw-r--r--src/ui/lookupwidget.c13
-rw-r--r--src/ui/scrollwidget.c9
-rw-r--r--src/ui/sidebarwidget.c36
-rw-r--r--src/ui/util.h3
-rw-r--r--src/ui/widget.c9
-rw-r--r--src/ui/widget.h1
-rw-r--r--src/ui/window.c75
13 files changed, 166 insertions, 61 deletions
diff --git a/src/app.c b/src/app.c
index b1036ffe..35a70f0b 100644
--- a/src/app.c
+++ b/src/app.c
@@ -914,7 +914,7 @@ void processEvents_App(enum iAppEventMode eventMode) {
914#if defined (iPlatformAppleDesktop) 914#if defined (iPlatformAppleDesktop)
915 handleCommand_MacOS(command_UserEvent(&ev)); 915 handleCommand_MacOS(command_UserEvent(&ev));
916#endif 916#endif
917 if (isCommand_UserEvent(&ev, "metrics.changed")) { 917 if (isMetricsChange_UserEvent(&ev)) {
918 arrange_Widget(d->window->root); 918 arrange_Widget(d->window->root);
919 } 919 }
920 if (!wasUsed) { 920 if (!wasUsed) {
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 242afe30..6972f32d 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -2246,7 +2246,10 @@ static iChar linkOrdinalChar_DocumentWidget_(const iDocumentWidget *d, size_t or
2246 2246
2247static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { 2247static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) {
2248 iWidget *w = as_Widget(d); 2248 iWidget *w = as_Widget(d);
2249 if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { 2249 if (isMetricsChange_UserEvent(ev)) {
2250 updateSize_DocumentWidget(d);
2251 }
2252 else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) {
2250 if (!handleCommand_DocumentWidget_(d, command_UserEvent(ev))) { 2253 if (!handleCommand_DocumentWidget_(d, command_UserEvent(ev))) {
2251 /* Base class commands. */ 2254 /* Base class commands. */
2252 return processEvent_Widget(w, ev); 2255 return processEvent_Widget(w, ev);
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index dd8fa744..4b5cd623 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -107,6 +107,24 @@ static void showCursor_InputWidget_(iInputWidget *d) {
107 d->cursorVis = 2; 107 d->cursorVis = 2;
108} 108}
109 109
110static void invalidateBuffered_InputWidget_(iInputWidget *d) {
111 if (d->buffered) {
112 delete_TextBuf(d->buffered);
113 d->buffered = NULL;
114 }
115}
116
117static void updateMetrics_InputWidget_(iInputWidget *d) {
118 iWidget *w = as_Widget(d);
119 /* Caller must arrange the width, but the height is fixed. */
120 w->rect.size.y = lineHeight_Text(default_FontId) + 2 * gap_UI;
121#if defined (iPlatformAppleMobile)
122 w->rect.size.y += 2 * gap_UI;
123#endif
124 invalidateBuffered_InputWidget_(d);
125 arrange_Widget(w);
126}
127
110void init_InputWidget(iInputWidget *d, size_t maxLen) { 128void init_InputWidget(iInputWidget *d, size_t maxLen) {
111 iWidget *w = &d->widget; 129 iWidget *w = &d->widget;
112 init_Widget(w); 130 init_Widget(w);
@@ -115,24 +133,20 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
115 init_Array(&d->oldText, sizeof(iChar)); 133 init_Array(&d->oldText, sizeof(iChar));
116 init_String(&d->hint); 134 init_String(&d->hint);
117 init_Array(&d->undoStack, sizeof(iInputUndo)); 135 init_Array(&d->undoStack, sizeof(iInputUndo));
118 d->font = uiInput_FontId | alwaysVariableFlag_FontId; 136 d->font = uiInput_FontId | alwaysVariableFlag_FontId;
119 d->leftPadding = 0; 137 d->leftPadding = 0;
120 d->rightPadding = 0; 138 d->rightPadding = 0;
121 d->cursor = 0; 139 d->cursor = 0;
122 d->lastCursor = 0; 140 d->lastCursor = 0;
123 d->inFlags = eatEscape_InputWidgetFlag; 141 d->inFlags = eatEscape_InputWidgetFlag;
124 iZap(d->mark); 142 iZap(d->mark);
125 setMaxLen_InputWidget(d, maxLen); 143 setMaxLen_InputWidget(d, maxLen);
126 /* Caller must arrange the width, but the height is fixed. */
127 w->rect.size.y = lineHeight_Text(default_FontId) + 2 * gap_UI;
128#if defined (iPlatformAppleMobile)
129 w->rect.size.y += 2 * gap_UI;
130#endif
131 setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); 144 setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue);
132 init_Click(&d->click, d, SDL_BUTTON_LEFT); 145 init_Click(&d->click, d, SDL_BUTTON_LEFT);
133 d->timer = 0; 146 d->timer = 0;
134 d->cursorVis = 0; 147 d->cursorVis = 0;
135 d->buffered = NULL; 148 d->buffered = NULL;
149 updateMetrics_InputWidget_(d);
136} 150}
137 151
138void deinit_InputWidget(iInputWidget *d) { 152void deinit_InputWidget(iInputWidget *d) {
@@ -226,13 +240,6 @@ static iString *visText_InputWidget_(const iInputWidget *d) {
226 return text; 240 return text;
227} 241}
228 242
229static void invalidateBuffered_InputWidget_(iInputWidget *d) {
230 if (d->buffered) {
231 delete_TextBuf(d->buffered);
232 d->buffered = NULL;
233 }
234}
235
236static void updateBuffered_InputWidget_(iInputWidget *d) { 243static void updateBuffered_InputWidget_(iInputWidget *d) {
237 invalidateBuffered_InputWidget_(d); 244 invalidateBuffered_InputWidget_(d);
238 iString *visText = visText_InputWidget_(d); 245 iString *visText = visText_InputWidget_(d);
@@ -600,6 +607,9 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
600 } 607 }
601 return iFalse; 608 return iFalse;
602 } 609 }
610 else if (isMetricsChange_UserEvent(ev)) {
611 updateMetrics_InputWidget_(d);
612 }
603 else if (isFocused_Widget(d) && isCommand_UserEvent(ev, "copy")) { 613 else if (isFocused_Widget(d) && isCommand_UserEvent(ev, "copy")) {
604 copy_InputWidget_(d, iFalse); 614 copy_InputWidget_(d, iFalse);
605 return iTrue; 615 return iTrue;
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index 8cba2d00..87c33562 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -78,7 +78,7 @@ static void updateKey_LabelWidget_(iLabelWidget *d) {
78 78
79static iBool processEvent_LabelWidget_(iLabelWidget *d, const SDL_Event *ev) { 79static iBool processEvent_LabelWidget_(iLabelWidget *d, const SDL_Event *ev) {
80 iWidget *w = &d->widget; 80 iWidget *w = &d->widget;
81 if (isCommand_UserEvent(ev, "metrics.changed")) { 81 if (isMetricsChange_UserEvent(ev)) {
82 updateSize_LabelWidget(d); 82 updateSize_LabelWidget(d);
83 } 83 }
84 else if (isCommand_UserEvent(ev, "bindings.changed")) { 84 else if (isCommand_UserEvent(ev, "bindings.changed")) {
@@ -277,8 +277,8 @@ static void sizeChanged_LabelWidget_(iLabelWidget *d) {
277 } 277 }
278} 278}
279 279
280void updateSize_LabelWidget(iLabelWidget *d) { 280iInt2 defaultSize_LabelWidget(const iLabelWidget *d) {
281 iWidget *w = as_Widget(d); 281 const iWidget *w = constAs_Widget(d);
282 const int64_t flags = flags_Widget(w); 282 const int64_t flags = flags_Widget(w);
283 iInt2 size = add_I2(measure_Text(d->font, cstr_String(&d->label)), muli_I2(padding_(flags), 2)); 283 iInt2 size = add_I2(measure_Text(d->font, cstr_String(&d->label)), muli_I2(padding_(flags), 2));
284 if ((flags & drawKey_WidgetFlag) && d->key) { 284 if ((flags & drawKey_WidgetFlag) && d->key) {
@@ -288,6 +288,17 @@ void updateSize_LabelWidget(iLabelWidget *d) {
288 size.x += 2 * gap_UI + measure_Text(uiShortcuts_FontId, cstr_String(&str)).x; 288 size.x += 2 * gap_UI + measure_Text(uiShortcuts_FontId, cstr_String(&str)).x;
289 deinit_String(&str); 289 deinit_String(&str);
290 } 290 }
291 return size;
292}
293
294int font_LabelWidget(const iLabelWidget *d) {
295 return d->font;
296}
297
298void updateSize_LabelWidget(iLabelWidget *d) {
299 iWidget *w = as_Widget(d);
300 const int64_t flags = flags_Widget(w);
301 const iInt2 size = defaultSize_LabelWidget(d);
291 /* Wrapped text implies that width must be defined by arrangement. */ 302 /* Wrapped text implies that width must be defined by arrangement. */
292 if (!(flags & (fixedWidth_WidgetFlag | wrapText_WidgetFlag))) { 303 if (!(flags & (fixedWidth_WidgetFlag | wrapText_WidgetFlag))) {
293 w->rect.size.x = size.x; 304 w->rect.size.x = size.x;
diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h
index 266c3b02..83dd66e5 100644
--- a/src/ui/labelwidget.h
+++ b/src/ui/labelwidget.h
@@ -40,8 +40,10 @@ void updateSize_LabelWidget (iLabelWidget *);
40void updateText_LabelWidget (iLabelWidget *, const iString *text); /* not resized */ 40void updateText_LabelWidget (iLabelWidget *, const iString *text); /* not resized */
41void updateTextCStr_LabelWidget (iLabelWidget *, const char *text); /* not resized */ 41void updateTextCStr_LabelWidget (iLabelWidget *, const char *text); /* not resized */
42 42
43const iString *text_LabelWidget (const iLabelWidget *); 43iInt2 defaultSize_LabelWidget (const iLabelWidget *);
44const iString *command_LabelWidget (const iLabelWidget *); 44int font_LabelWidget (const iLabelWidget *);
45const iString * text_LabelWidget (const iLabelWidget *);
46const iString * command_LabelWidget (const iLabelWidget *);
45 47
46iLabelWidget *newKeyMods_LabelWidget(const char *label, int key, int kmods, const char *command); 48iLabelWidget *newKeyMods_LabelWidget(const char *label, int key, int kmods, const char *command);
47iLabelWidget *newColor_LabelWidget (const char *text, int color); 49iLabelWidget *newColor_LabelWidget (const char *text, int color);
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c
index d31ac2f5..2e5519a5 100644
--- a/src/ui/listwidget.c
+++ b/src/ui/listwidget.c
@@ -278,7 +278,10 @@ static void sizeChanged_ListWidget_(iListWidget *d) {
278 278
279static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { 279static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) {
280 iWidget *w = as_Widget(d); 280 iWidget *w = as_Widget(d);
281 if (isCommand_SDLEvent(ev)) { 281 if (isMetricsChange_UserEvent(ev)) {
282 invalidate_ListWidget(d);
283 }
284 else if (isCommand_SDLEvent(ev)) {
282 const char *cmd = command_UserEvent(ev); 285 const char *cmd = command_UserEvent(ev);
283 if (equal_Command(cmd, "theme.changed")) { 286 if (equal_Command(cmd, "theme.changed")) {
284 invalidate_ListWidget(d); 287 invalidate_ListWidget(d);
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index 7d6052e2..10039e10 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -372,6 +372,10 @@ static iThreadResult worker_LookupWidget_(iThread *thread) {
372 372
373iDefineObjectConstruction(LookupWidget) 373iDefineObjectConstruction(LookupWidget)
374 374
375static void updateMetrics_LookupWidget_(iLookupWidget *d) {
376 setItemHeight_ListWidget(d->list, lineHeight_Text(uiContent_FontId) * 1.25f);
377}
378
375void init_LookupWidget(iLookupWidget *d) { 379void init_LookupWidget(iLookupWidget *d) {
376 iWidget *w = as_Widget(d); 380 iWidget *w = as_Widget(d);
377 init_Widget(w); 381 init_Widget(w);
@@ -381,7 +385,6 @@ void init_LookupWidget(iLookupWidget *d) {
381 setFlags_Widget(w, unhittable_WidgetFlag, iTrue); 385 setFlags_Widget(w, unhittable_WidgetFlag, iTrue);
382#endif 386#endif
383 d->list = addChild_Widget(w, iClob(new_ListWidget())); 387 d->list = addChild_Widget(w, iClob(new_ListWidget()));
384 setItemHeight_ListWidget(d->list, lineHeight_Text(uiContent_FontId) * 1.25f);
385 d->cursor = iInvalidPos; 388 d->cursor = iInvalidPos;
386 d->work = new_Thread(worker_LookupWidget_); 389 d->work = new_Thread(worker_LookupWidget_);
387 setUserData_Thread(d->work, d); 390 setUserData_Thread(d->work, d);
@@ -390,6 +393,7 @@ void init_LookupWidget(iLookupWidget *d) {
390 init_String(&d->pendingTerm); 393 init_String(&d->pendingTerm);
391 d->pendingDocs = NULL; 394 d->pendingDocs = NULL;
392 d->finishedJob = NULL; 395 d->finishedJob = NULL;
396 updateMetrics_LookupWidget_(d);
393 start_Thread(d->work); 397 start_Thread(d->work);
394} 398}
395 399
@@ -634,8 +638,11 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) {
634 presentResults_LookupWidget_(d); 638 presentResults_LookupWidget_(d);
635 return iTrue; 639 return iTrue;
636 } 640 }
637 if (isResize_UserEvent(ev) || (equal_Command(cmd, "layout.changed") && 641 if (isMetricsChange_UserEvent(ev)) {
638 equal_Rangecc(range_Command(cmd, "id"), "navbar"))) { 642 updateMetrics_LookupWidget_(d);
643 }
644 else if (isResize_UserEvent(ev) || (equal_Command(cmd, "layout.changed") &&
645 equal_Rangecc(range_Command(cmd, "id"), "navbar"))) {
639 /* Position the lookup popup under the URL bar. */ { 646 /* Position the lookup popup under the URL bar. */ {
640 const iWindow *window = get_Window(); 647 const iWindow *window = get_Window();
641 const iInt2 rootSize = rootSize_Window(window); 648 const iInt2 rootSize = rootSize_Window(window);
diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c
index 448103b5..e1893fec 100644
--- a/src/ui/scrollwidget.c
+++ b/src/ui/scrollwidget.c
@@ -35,6 +35,10 @@ struct Impl_ScrollWidget {
35 int startThumb; 35 int startThumb;
36}; 36};
37 37
38static void updateMetrics_ScrollWidget_(iScrollWidget *d) {
39 as_Widget(d)->rect.size.x = gap_UI * 3;
40}
41
38void init_ScrollWidget(iScrollWidget *d) { 42void init_ScrollWidget(iScrollWidget *d) {
39 iWidget *w = as_Widget(d); 43 iWidget *w = as_Widget(d);
40 init_Widget(w); 44 init_Widget(w);
@@ -43,7 +47,7 @@ void init_ScrollWidget(iScrollWidget *d) {
43 fixedWidth_WidgetFlag | resizeToParentHeight_WidgetFlag | 47 fixedWidth_WidgetFlag | resizeToParentHeight_WidgetFlag |
44 moveToParentRightEdge_WidgetFlag | touchDrag_WidgetFlag, 48 moveToParentRightEdge_WidgetFlag | touchDrag_WidgetFlag,
45 iTrue); 49 iTrue);
46 w->rect.size.x = gap_UI * 3; 50 updateMetrics_ScrollWidget_(d);
47 init_Click(&d->click, d, SDL_BUTTON_LEFT); 51 init_Click(&d->click, d, SDL_BUTTON_LEFT);
48} 52}
49 53
@@ -88,6 +92,9 @@ void setThumb_ScrollWidget(iScrollWidget *d, int thumb, int thumbSize) {
88 92
89static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) { 93static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) {
90 iWidget *w = as_Widget(d); 94 iWidget *w = as_Widget(d);
95 if (isMetricsChange_UserEvent(ev)) {
96 updateMetrics_ScrollWidget_(d);
97 }
91 switch (processEvent_Click(&d->click, ev)) { 98 switch (processEvent_Click(&d->click, ev)) {
92 case started_ClickResult: 99 case started_ClickResult:
93 setFlags_Widget(w, pressed_WidgetFlag, iTrue); 100 setFlags_Widget(w, pressed_WidgetFlag, iTrue);
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 4d3818bd..dd7b977e 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -414,8 +414,10 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
414} 414}
415 415
416static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) { 416static void updateItemHeight_SidebarWidget_(iSidebarWidget *d) {
417 const float heights[max_SidebarMode] = { 1.333f, 2.333f, 1.333f, 3.5f, 1.2f }; 417 if (d->list) {
418 setItemHeight_ListWidget(d->list, heights[d->mode] * lineHeight_Text(d->itemFonts[0])); 418 const float heights[max_SidebarMode] = { 1.333f, 2.333f, 1.333f, 3.5f, 1.2f };
419 setItemHeight_ListWidget(d->list, heights[d->mode] * lineHeight_Text(d->itemFonts[0]));
420 }
419} 421}
420 422
421iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { 423iBool setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) {
@@ -466,6 +468,22 @@ const char *icon_SidebarMode(enum iSidebarMode mode) {
466 return tightModeLabels_[mode]; 468 return tightModeLabels_[mode];
467} 469}
468 470
471static void updateMetrics_SidebarWidget_(iSidebarWidget *d) {
472 if (d->resizer) {
473 d->resizer->rect.size.x = gap_UI;
474 }
475 d->maxButtonLabelWidth = 0;
476 for (int i = 0; i < max_SidebarMode; i++) {
477 if (d->modeButtons[i]) {
478 d->maxButtonLabelWidth = iMaxi(
479 d->maxButtonLabelWidth,
480 3 * gap_UI +
481 measure_Text(font_LabelWidget(d->modeButtons[i]), normalModeLabels_[i]).x);
482 }
483 }
484 updateItemHeight_SidebarWidget_(d);
485}
486
469void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { 487void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
470 iWidget *w = as_Widget(d); 488 iWidget *w = as_Widget(d);
471 init_Widget(w); 489 init_Widget(w);
@@ -493,6 +511,8 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
493 iWidget *vdiv = makeVDiv_Widget(); 511 iWidget *vdiv = makeVDiv_Widget();
494 addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag); 512 addChildFlags_Widget(w, vdiv, resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag);
495 iZap(d->modeButtons); 513 iZap(d->modeButtons);
514 d->resizer = NULL;
515 d->list = NULL;
496 /* On a phone, the right sidebar is used exclusively for Identities. */ 516 /* On a phone, the right sidebar is used exclusively for Identities. */
497 const iBool isPhone = deviceType_App() == phone_AppDeviceType; 517 const iBool isPhone = deviceType_App() == phone_AppDeviceType;
498 if (!isPhone || d->side == left_SideBarSide) { 518 if (!isPhone || d->side == left_SideBarSide) {
@@ -550,10 +570,10 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
550 setFlags_Widget(d->resizer, hidden_WidgetFlag | disabled_WidgetFlag, iTrue); 570 setFlags_Widget(d->resizer, hidden_WidgetFlag | disabled_WidgetFlag, iTrue);
551 } 571 }
552 setId_Widget(d->resizer, side == left_SideBarSide ? "sidebar.grab" : "sidebar2.grab"); 572 setId_Widget(d->resizer, side == left_SideBarSide ? "sidebar.grab" : "sidebar2.grab");
553 d->resizer->rect.size.x = gap_UI;
554 setBackgroundColor_Widget(d->resizer, none_ColorId); 573 setBackgroundColor_Widget(d->resizer, none_ColorId);
555 d->menu = NULL; 574 d->menu = NULL;
556 addAction_Widget(w, SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh"); 575 addAction_Widget(w, SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh");
576 updateMetrics_SidebarWidget_(d);
557} 577}
558 578
559void deinit_SidebarWidget(iSidebarWidget *d) { 579void deinit_SidebarWidget(iSidebarWidget *d) {
@@ -561,15 +581,12 @@ void deinit_SidebarWidget(iSidebarWidget *d) {
561} 581}
562 582
563void setButtonFont_SidebarWidget(iSidebarWidget *d, int font) { 583void setButtonFont_SidebarWidget(iSidebarWidget *d, int font) {
564 d->maxButtonLabelWidth = 0;
565 for (int i = 0; i < max_SidebarMode; i++) { 584 for (int i = 0; i < max_SidebarMode; i++) {
566 if (d->modeButtons[i]) { 585 if (d->modeButtons[i]) {
567 setFont_LabelWidget(d->modeButtons[i], font); 586 setFont_LabelWidget(d->modeButtons[i], font);
568 d->maxButtonLabelWidth =
569 iMaxi(d->maxButtonLabelWidth,
570 3 * gap_UI + measure_Text(font, normalModeLabels_[i]).x);
571 } 587 }
572 } 588 }
589 updateMetrics_SidebarWidget_(d);
573} 590}
574 591
575static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { 592static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) {
@@ -759,6 +776,11 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
759 if (isResize_UserEvent(ev)) { 776 if (isResize_UserEvent(ev)) {
760 checkModeButtonLayout_SidebarWidget_(d); 777 checkModeButtonLayout_SidebarWidget_(d);
761 } 778 }
779 else if (isMetricsChange_UserEvent(ev)) {
780 updateMetrics_SidebarWidget_(d);
781 arrange_Widget(w);
782 checkModeButtonLayout_SidebarWidget_(d);
783 }
762 else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { 784 else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) {
763 const char *cmd = command_UserEvent(ev); 785 const char *cmd = command_UserEvent(ev);
764 if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { 786 if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) {
diff --git a/src/ui/util.h b/src/ui/util.h
index 96e08e95..9e45f369 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -39,6 +39,9 @@ const char * command_UserEvent (const SDL_Event *);
39iLocalDef iBool isResize_UserEvent(const SDL_Event *d) { 39iLocalDef iBool isResize_UserEvent(const SDL_Event *d) {
40 return isCommand_UserEvent(d, "window.resized"); 40 return isCommand_UserEvent(d, "window.resized");
41} 41}
42iLocalDef iBool isMetricsChange_UserEvent(const SDL_Event *d) {
43 return isCommand_UserEvent(d, "metrics.changed");
44}
42 45
43#if defined (iPlatformApple) 46#if defined (iPlatformApple)
44# define KMOD_PRIMARY KMOD_GUI 47# define KMOD_PRIMARY KMOD_GUI
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 386ba6d6..f9c84c7a 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -397,17 +397,20 @@ void arrange_Widget(iWidget *d) {
397 else { 397 else {
398 /* Evenly size all children. */ 398 /* Evenly size all children. */
399 iInt2 childSize = innerRect_Widget_(d).size; 399 iInt2 childSize = innerRect_Widget_(d).size;
400 iInt2 unpaddedChildSize = d->rect.size;
400 if (d->flags & arrangeHorizontal_WidgetFlag) { 401 if (d->flags & arrangeHorizontal_WidgetFlag) {
401 childSize.x /= childCount; 402 childSize.x /= childCount;
403 unpaddedChildSize.x /= childCount;
402 } 404 }
403 else if (d->flags & arrangeVertical_WidgetFlag) { 405 else if (d->flags & arrangeVertical_WidgetFlag) {
404 childSize.y /= childCount; 406 childSize.y /= childCount;
407 unpaddedChildSize.y /= childCount;
405 } 408 }
406 iForEach(ObjectList, i, d->children) { 409 iForEach(ObjectList, i, d->children) {
407 iWidget *child = as_Widget(i.object); 410 iWidget *child = as_Widget(i.object);
408 if (!isCollapsed_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { 411 if (!isCollapsed_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) {
409 if (dirs.x) setWidth_Widget_(child, childSize.x); 412 if (dirs.x) setWidth_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.x : childSize.x);
410 if (dirs.y) setHeight_Widget_(child, childSize.y); 413 if (dirs.y) setHeight_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.y : childSize.y);
411 } 414 }
412 } 415 }
413 } 416 }
@@ -441,7 +444,7 @@ void arrange_Widget(iWidget *d) {
441 pos.y += child->rect.size.y; 444 pos.y += child->rect.size.y;
442 } 445 }
443 } 446 }
444 else if (d->flags & resizeChildren_WidgetFlag) { 447 else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag) {
445 child->rect.pos = pos; 448 child->rect.pos = pos;
446 } 449 }
447 } 450 }
diff --git a/src/ui/widget.h b/src/ui/widget.h
index c5a1a360..955abf5d 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -100,6 +100,7 @@ enum iWidgetFlag {
100#define visualOffset_WidgetFlag iBit64(45) 100#define visualOffset_WidgetFlag iBit64(45)
101#define parentCannotResize_WidgetFlag iBit64(46) 101#define parentCannotResize_WidgetFlag iBit64(46)
102#define noTopFrame_WidgetFlag iBit64(47) 102#define noTopFrame_WidgetFlag iBit64(47)
103#define unpadded_WidgetFlag iBit64(48) /* ignore parent's padding */
103 104
104enum iWidgetAddPos { 105enum iWidgetAddPos {
105 back_WidgetAddPos, 106 back_WidgetAddPos,
diff --git a/src/ui/window.c b/src/ui/window.c
index fb5daa80..4f9ac9c3 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -511,7 +511,7 @@ static int navBarAvailableSpace_(iWidget *navBar) {
511} 511}
512 512
513static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { 513static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
514 if (equal_Command(cmd, "window.resized")) { 514 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "metrics.changed")) {
515 const iBool isPhone = deviceType_App() == phone_AppDeviceType; 515 const iBool isPhone = deviceType_App() == phone_AppDeviceType;
516 const iBool isNarrow = !isPhone && width_Rect(bounds_Widget(navBar)) / gap_UI < 140; 516 const iBool isNarrow = !isPhone && width_Rect(bounds_Widget(navBar)) / gap_UI < 140;
517 /* Adjust navbar padding. */ { 517 /* Adjust navbar padding. */ {
@@ -810,8 +810,43 @@ static int appIconSize_(void) {
810 return lineHeight_Text(uiContent_FontId); 810 return lineHeight_Text(uiContent_FontId);
811} 811}
812 812
813static void updateMetrics_Window_(iWindow *d) {
814 /* Custom frame. */
815 iWidget *winBar = findChild_Widget(d->root, "winbar");
816 if (winBar) {
817 iWidget *appIcon = findChild_Widget(winBar, "winbar.icon");
818 iWidget *appTitle = findChild_Widget(winBar, "winbar.title");
819 iWidget *appMin = findChild_Widget(winBar, "winbar.min");
820 iWidget *appMax = findChild_Widget(winBar, "winbar.max");
821 iWidget *appClose = findChild_Widget(winBar, "winbar.close");
822 setPadding_Widget(winBar, 0, gap_UI / 3, 0, 0);
823 setSize_Widget(appMin, init_I2(gap_UI * 11.5f, height_Widget(appTitle)));
824 setSize_Widget(appMax, appMin->rect.size);
825 setSize_Widget(appClose, appMin->rect.size);
826 setSize_Widget(appIcon, init_I2(appIconSize_(), appMin->rect.size.y));
827 }
828 iWidget *navBar = findChild_Widget(d->root, "navbar");
829 iWidget *lock = findChild_Widget(navBar, "navbar.lock");
830 iWidget *url = findChild_Widget(d->root, "url");
831 iWidget *fprog = findChild_Widget(navBar, "feeds.progress");
832 iWidget *docProg = findChild_Widget(navBar, "document.progress");
833 iWidget *indSearch = findChild_Widget(navBar, "input.indicator.search");
834 setPadding_Widget(as_Widget(url), 0, gap_UI, gap_UI, gap_UI);
835 navBar->rect.size.y = 0; /* recalculate height based on children (FIXME: shouldn't be needed) */
836 updateSize_LabelWidget((iLabelWidget *) lock);
837 setContentPadding_InputWidget((iInputWidget *) url, width_Widget(lock) * 0.75, -1);
838 fprog->rect.pos.y = gap_UI;
839 docProg->rect.pos.y = gap_UI;
840 indSearch->rect.pos.y = gap_UI;
841 updatePadding_Window_(d);
842 arrange_Widget(d->root);
843 postRefresh_App();
844}
845
813static void setupUserInterface_Window(iWindow *d) { 846static void setupUserInterface_Window(iWindow *d) {
847#if defined (iPlatformMobile)
814 const iBool isPhone = (deviceType_App() == phone_AppDeviceType); 848 const iBool isPhone = (deviceType_App() == phone_AppDeviceType);
849#endif
815 /* Children of root cover the entire window. */ 850 /* Children of root cover the entire window. */
816 setFlags_Widget(d->root, resizeChildren_WidgetFlag, iTrue); 851 setFlags_Widget(d->root, resizeChildren_WidgetFlag, iTrue);
817 setCommandHandler_Widget(d->root, handleRootCommands_); 852 setCommandHandler_Widget(d->root, handleRootCommands_);
@@ -825,7 +860,6 @@ static void setupUserInterface_Window(iWindow *d) {
825 if (prefs_App()->customFrame) { 860 if (prefs_App()->customFrame) {
826 setPadding1_Widget(div, 1); 861 setPadding1_Widget(div, 1);
827 iWidget *winBar = new_Widget(); 862 iWidget *winBar = new_Widget();
828 setPadding_Widget(winBar, 0, gap_UI / 3, 0, 0);
829 setId_Widget(winBar, "winbar"); 863 setId_Widget(winBar, "winbar");
830 setFlags_Widget(winBar, 864 setFlags_Widget(winBar,
831 arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag | 865 arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag |
@@ -854,8 +888,6 @@ static void setupUserInterface_Window(iWindow *d) {
854 iClob(appMin = newLargeIcon_LabelWidget("\u2013", "window.minimize")), 888 iClob(appMin = newLargeIcon_LabelWidget("\u2013", "window.minimize")),
855 frameless_WidgetFlag), 889 frameless_WidgetFlag),
856 "winbar.min"); 890 "winbar.min");
857 setSize_Widget(as_Widget(appMin),
858 init_I2(gap_UI * 11.5f, height_Widget(appTitle)));
859 addChildFlags_Widget( 891 addChildFlags_Widget(
860 winBar, 892 winBar,
861 iClob(appMax = newLargeIcon_LabelWidget("\u25a1", "window.maximize toggle:1")), 893 iClob(appMax = newLargeIcon_LabelWidget("\u25a1", "window.maximize toggle:1")),
@@ -864,10 +896,8 @@ static void setupUserInterface_Window(iWindow *d) {
864 addChildFlags_Widget(winBar, 896 addChildFlags_Widget(winBar,
865 iClob(appClose = newLargeIcon_LabelWidget("\u2a2f", "window.close")), 897 iClob(appClose = newLargeIcon_LabelWidget("\u2a2f", "window.close")),
866 frameless_WidgetFlag); 898 frameless_WidgetFlag);
899 setId_Widget(appClose, "winbar.close");
867 setFont_LabelWidget(appClose, uiContent_FontId); 900 setFont_LabelWidget(appClose, uiContent_FontId);
868 setSize_Widget(as_Widget(appMax), as_Widget(appMin)->rect.size);
869 setSize_Widget(as_Widget(appClose), as_Widget(appMin)->rect.size);
870 setSize_Widget(appIcon, init_I2(appIconSize_(), as_Widget(appMin)->rect.size.y));
871 addChild_Widget(div, iClob(winBar)); 901 addChild_Widget(div, iClob(winBar));
872 setBackgroundColor_Widget(winBar, uiBackground_ColorId); 902 setBackgroundColor_Widget(winBar, uiBackground_ColorId);
873 } 903 }
@@ -895,23 +925,23 @@ static void setupUserInterface_Window(iWindow *d) {
895 setId_Widget(addChildFlags_Widget(navBar, iClob(idMenu), collapse_WidgetFlag), "navbar.ident"); 925 setId_Widget(addChildFlags_Widget(navBar, iClob(idMenu), collapse_WidgetFlag), "navbar.ident");
896 /* URL input field. */ { 926 /* URL input field. */ {
897 iInputWidget *url = new_InputWidget(0); 927 iInputWidget *url = new_InputWidget(0);
928 setFlags_Widget(as_Widget(url), resizeHeightOfChildren_WidgetFlag, iTrue);
898 setSelectAllOnFocus_InputWidget(url, iTrue); 929 setSelectAllOnFocus_InputWidget(url, iTrue);
899 setId_Widget(as_Widget(url), "url"); 930 setId_Widget(as_Widget(url), "url");
900 setUrlContent_InputWidget(url, iTrue); 931 setUrlContent_InputWidget(url, iTrue);
901 setNotifyEdits_InputWidget(url, iTrue); 932 setNotifyEdits_InputWidget(url, iTrue);
902 setTextCStr_InputWidget(url, "gemini://"); 933 setTextCStr_InputWidget(url, "gemini://");
903 addChildFlags_Widget(navBar, iClob(url), 0); 934 addChildFlags_Widget(navBar, iClob(url), 0);
904 setPadding_Widget(as_Widget(url), 0, 0, gap_UI, 0);
905 /* Page information/certificate warning. */ { 935 /* Page information/certificate warning. */ {
906 iLabelWidget *lock = 936 iLabelWidget *lock =
907 addChildFlags_Widget(as_Widget(url), 937 addChildFlags_Widget(as_Widget(url),
908 iClob(newIcon_LabelWidget("\U0001f513", SDLK_i, KMOD_PRIMARY, "document.info")), 938 iClob(newIcon_LabelWidget("\U0001f513", SDLK_i, KMOD_PRIMARY, "document.info")),
909 noBackground_WidgetFlag | frameless_WidgetFlag | moveToParentLeftEdge_WidgetFlag | 939 noBackground_WidgetFlag | frameless_WidgetFlag | moveToParentLeftEdge_WidgetFlag |
940 unpadded_WidgetFlag |
910 (deviceType_App() == desktop_AppDeviceType ? tight_WidgetFlag : 0)); 941 (deviceType_App() == desktop_AppDeviceType ? tight_WidgetFlag : 0));
911 setId_Widget(as_Widget(lock), "navbar.lock"); 942 setId_Widget(as_Widget(lock), "navbar.lock");
912 setFont_LabelWidget(lock, defaultSymbols_FontId); 943 setFont_LabelWidget(lock, defaultSymbols_FontId);
913 updateTextCStr_LabelWidget(lock, "\U0001f512"); 944 updateTextCStr_LabelWidget(lock, "\U0001f512");
914 setContentPadding_InputWidget(url, width_Widget(lock) * 0.75, -1);
915 } 945 }
916 /* Feeds refresh indicator is inside the input field. */ { 946 /* Feeds refresh indicator is inside the input field. */ {
917 iLabelWidget *fprog = new_LabelWidget(uiTextCaution_ColorEscape 947 iLabelWidget *fprog = new_LabelWidget(uiTextCaution_ColorEscape
@@ -919,7 +949,7 @@ static void setupUserInterface_Window(iWindow *d) {
919 setId_Widget(as_Widget(fprog), "feeds.progress"); 949 setId_Widget(as_Widget(fprog), "feeds.progress");
920 setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId); 950 setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId);
921 setAlignVisually_LabelWidget(fprog, iTrue); 951 setAlignVisually_LabelWidget(fprog, iTrue);
922 shrink_Rect(&as_Widget(fprog)->rect, init_I2(0, gap_UI)); 952// shrink_Rect(&as_Widget(fprog)->rect, init_I2(0, gap_UI));
923 addChildFlags_Widget(as_Widget(url), 953 addChildFlags_Widget(as_Widget(url),
924 iClob(fprog), 954 iClob(fprog),
925 moveToParentRightEdge_WidgetFlag | hidden_WidgetFlag); 955 moveToParentRightEdge_WidgetFlag | hidden_WidgetFlag);
@@ -930,7 +960,7 @@ static void setupUserInterface_Window(iWindow *d) {
930 setId_Widget(as_Widget(progress), "document.progress"); 960 setId_Widget(as_Widget(progress), "document.progress");
931 setBackgroundColor_Widget(as_Widget(progress), uiBackground_ColorId); 961 setBackgroundColor_Widget(as_Widget(progress), uiBackground_ColorId);
932 setAlignVisually_LabelWidget(progress, iTrue); 962 setAlignVisually_LabelWidget(progress, iTrue);
933 shrink_Rect(&as_Widget(progress)->rect, init_I2(0, gap_UI)); 963// shrink_Rect(&as_Widget(progress)->rect, init_I2(0, gap_UI));
934 addChildFlags_Widget(as_Widget(url), 964 addChildFlags_Widget(as_Widget(url),
935 iClob(progress), 965 iClob(progress),
936 moveToParentRightEdge_WidgetFlag); 966 moveToParentRightEdge_WidgetFlag);
@@ -942,7 +972,7 @@ static void setupUserInterface_Window(iWindow *d) {
942 setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId); 972 setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId);
943 setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId); 973 setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId);
944 setAlignVisually_LabelWidget(queryInd, iTrue); 974 setAlignVisually_LabelWidget(queryInd, iTrue);
945 shrink_Rect(&as_Widget(queryInd)->rect, init_I2(0, gap_UI)); 975// shrink_Rect(&as_Widget(queryInd)->rect, init_I2(0, gap_UI));
946 addChildFlags_Widget(as_Widget(url), 976 addChildFlags_Widget(as_Widget(url),
947 iClob(queryInd), 977 iClob(queryInd),
948 moveToParentRightEdge_WidgetFlag | hidden_WidgetFlag); 978 moveToParentRightEdge_WidgetFlag | hidden_WidgetFlag);
@@ -1098,6 +1128,7 @@ static void setupUserInterface_Window(iWindow *d) {
1098 addAction_Widget(d->root, '4', rightSidebar_KeyModifier, "sidebar2.mode arg:3 toggle:1"); 1128 addAction_Widget(d->root, '4', rightSidebar_KeyModifier, "sidebar2.mode arg:3 toggle:1");
1099 addAction_Widget(d->root, '5', rightSidebar_KeyModifier, "sidebar2.mode arg:4 toggle:1"); 1129 addAction_Widget(d->root, '5', rightSidebar_KeyModifier, "sidebar2.mode arg:4 toggle:1");
1100 } 1130 }
1131 updateMetrics_Window_(d);
1101} 1132}
1102 1133
1103static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) { 1134static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) {
@@ -1636,6 +1667,9 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
1636 wasUsed = dispatchEvent_Widget(widget, &paste); 1667 wasUsed = dispatchEvent_Widget(widget, &paste);
1637 } 1668 }
1638 } 1669 }
1670 if (isMetricsChange_UserEvent(&event)) {
1671 updateMetrics_Window_(d);
1672 }
1639 if (oldHover != hover_Widget()) { 1673 if (oldHover != hover_Widget()) {
1640 postRefresh_App(); 1674 postRefresh_App();
1641 } 1675 }
@@ -1716,14 +1750,13 @@ void setTitle_Window(iWindow *d, const iString *title) {
1716void setUiScale_Window(iWindow *d, float uiScale) { 1750void setUiScale_Window(iWindow *d, float uiScale) {
1717 uiScale = iClamp(uiScale, 0.5f, 4.0f); 1751 uiScale = iClamp(uiScale, 0.5f, 4.0f);
1718 if (d) { 1752 if (d) {
1719 d->uiScale = uiScale; 1753 if (iAbs(d->uiScale - uiScale) > 0.0001f) {
1720#if 0 1754 d->uiScale = uiScale;
1721 deinit_Text(); 1755 /* Dynamic UI metrics change. Widgets need to update themselves. */
1722 setPixelRatio_Metrics(d->pixelRatio * d->uiScale); 1756 setPixelRatio_Metrics(d->pixelRatio * d->uiScale);
1723 init_Text(d->render); 1757 resetFonts_Text();
1724 postCommand_App("metrics.changed"); 1758 postCommand_App("metrics.changed");
1725 /* TODO: Dynamic UI metrics change. Widgets need to update themselves. */ 1759 }
1726#endif
1727 } 1760 }
1728 else { 1761 else {
1729 initialUiScale_ = uiScale; 1762 initialUiScale_ = uiScale;