summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/about/help.gmi14
-rw-r--r--res/about/version.gmi6
-rw-r--r--src/app.c12
-rw-r--r--src/gmdocument.h2
-rw-r--r--src/prefs.c1
-rw-r--r--src/prefs.h6
-rw-r--r--src/ui/documentwidget.c226
-rw-r--r--src/ui/inputwidget.c5
-rw-r--r--src/ui/inputwidget.h1
-rw-r--r--src/ui/sidebarwidget.c2
-rw-r--r--src/ui/text.c80
-rw-r--r--src/ui/text.h10
-rw-r--r--src/ui/util.c10
-rw-r--r--src/ui/window.c16
14 files changed, 338 insertions, 53 deletions
diff --git a/res/about/help.gmi b/res/about/help.gmi
index ca5e164f..1059dd68 100644
--- a/res/about/help.gmi
+++ b/res/about/help.gmi
@@ -132,7 +132,19 @@ In addition to a title, bookmarks can have tags. Some tags have a special meanin
132 132
133Gemini uses TLS client certificates for manual user/session identification purposes. This is analogous to logging into a web site, except you are in full control of the information. The term "Identity" is used in Lagrange to refer to client certificates. 133Gemini uses TLS client certificates for manual user/session identification purposes. This is analogous to logging into a web site, except you are in full control of the information. The term "Identity" is used in Lagrange to refer to client certificates.
134 134
135Lagrange can easily create a new identity. The shortcut for this is ${SHIFT+}${CTRL+}N. Consider any information you enter in the certificate as public; only the Common Name is required and will appear as the issuer and subject of the certificate. 135The Identities sidebar tab shows all identities known to Lagrange. Press ${CTRL+}3 to show it. This includes identities created in Lagrange and any identities based on imported X.509 certificates.
136
137### Creating a new identity
138
139Click on the 👤 button in the navigation bar and select "New Identity...". The shortcut for this is ${SHIFT+}${CTRL+}N.
140
141Consider any information you enter in the certificate as public; only the Common Name is required and will appear as the issuer and subject of the certificate.
142
143### Using an identity
144
145You'll need to select an identity when you encounter this error message:
146
147...
136 148
137Clicking on an identity in the sidebar will toggle it on/off for the currently open URL. On subsequent page loads, the certificate will then be sent to the server when the URL or any URL under it is fetched. You can click on the 👤 button in the navigation bar to see which identity is being used for the current page. 149Clicking on an identity in the sidebar will toggle it on/off for the currently open URL. On subsequent page loads, the certificate will then be sent to the server when the URL or any URL under it is fetched. You can click on the 👤 button in the navigation bar to see which identity is being used for the current page.
138 150
diff --git a/res/about/version.gmi b/res/about/version.gmi
index 94ddae60..2df2f9b4 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -7,8 +7,12 @@
7# Release notes 7# Release notes
8 8
9## 0.3 9## 0.3
10* Added "Open Link in Background Tab" to link context menu. 10* Added preferences for font and page layout.
11* Added preference for color saturation.
11* Added tabs in Preferences for better grouping. 12* Added tabs in Preferences for better grouping.
13* Page outline appears when mouse is hovering over the scrollbar.
14* Site icon appears on the left when the window is wide enough.
15* Added "Open Link in Background Tab" to link context menu.
12* Slightly smaller first paragraph font size. 16* Slightly smaller first paragraph font size.
13* Improved text selection behavior when starting on empty space. 17* Improved text selection behavior when starting on empty space.
14* Fixed: Centered popups do not remain centered when window is resized. 18* Fixed: Centered popups do not remain centered when window is resized.
diff --git a/src/app.c b/src/app.c
index 27ddbb26..81fe67af 100644
--- a/src/app.c
+++ b/src/app.c
@@ -165,6 +165,7 @@ static iString *serializePrefs_App_(const iApp *d) {
165 } 165 }
166 appendFormat_String(str, "sidebar.mode arg:%d\n", mode_SidebarWidget(sidebar)); 166 appendFormat_String(str, "sidebar.mode arg:%d\n", mode_SidebarWidget(sidebar));
167 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window)); 167 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window));
168 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font);
168 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent); 169 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent);
169 appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); 170 appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth);
170 appendFormat_String(str, "prefs.biglede.changed arg:%d\n", d->prefs.bigFirstParagraph); 171 appendFormat_String(str, "prefs.biglede.changed arg:%d\n", d->prefs.bigFirstParagraph);
@@ -810,6 +811,14 @@ iBool handleCommand_App(const char *cmd) {
810 SDL_MaximizeWindow(d->window->win); 811 SDL_MaximizeWindow(d->window->win);
811 return iTrue; 812 return iTrue;
812 } 813 }
814 else if (equal_Command(cmd, "font.set")) {
815 setFreezeDraw_Window(get_Window(), iTrue);
816 d->prefs.font = arg_Command(cmd);
817 setContentFont_Text(d->prefs.font);
818 postCommand_App("font.changed");
819 postCommand_App("window.unfreeze");
820 return iTrue;
821 }
813 else if (equal_Command(cmd, "zoom.set")) { 822 else if (equal_Command(cmd, "zoom.set")) {
814 setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */ 823 setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */
815 d->prefs.zoomPercent = arg_Command(cmd); 824 d->prefs.zoomPercent = arg_Command(cmd);
@@ -977,6 +986,9 @@ iBool handleCommand_App(const char *cmd) {
977 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); 986 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
978 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), 987 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"),
979 collectNewFormat_String("%g", uiScale_Window(d->window))); 988 collectNewFormat_String("%g", uiScale_Window(d->window)));
989 setFlags_Widget(findChild_Widget(dlg, format_CStr("prefs.font.%d", d->prefs.font)),
990 selected_WidgetFlag,
991 iTrue);
980 setFlags_Widget( 992 setFlags_Widget(
981 findChild_Widget(dlg, format_CStr("prefs.linewidth.%d", d->prefs.lineWidth)), 993 findChild_Widget(dlg, format_CStr("prefs.linewidth.%d", d->prefs.lineWidth)),
982 selected_WidgetFlag, 994 selected_WidgetFlag,
diff --git a/src/gmdocument.h b/src/gmdocument.h
index ec47258a..891ac6f2 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -118,7 +118,7 @@ iInt2 size_GmDocument (const iGmDocument *);
118const iGmRun * siteBanner_GmDocument (const iGmDocument *); 118const iGmRun * siteBanner_GmDocument (const iGmDocument *);
119iBool hasSiteBanner_GmDocument (const iGmDocument *); 119iBool hasSiteBanner_GmDocument (const iGmDocument *);
120const iString * bannerText_GmDocument (const iGmDocument *); 120const iString * bannerText_GmDocument (const iGmDocument *);
121const iArray * headings_GmDocument (const iGmDocument *); 121const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */
122const iString * source_GmDocument (const iGmDocument *); 122const iString * source_GmDocument (const iGmDocument *);
123 123
124iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); 124iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start);
diff --git a/src/prefs.c b/src/prefs.c
index d773acbd..9b77b4f2 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -6,6 +6,7 @@ void init_Prefs(iPrefs *d) {
6 d->retainWindowSize = iTrue; 6 d->retainWindowSize = iTrue;
7 d->zoomPercent = 100; 7 d->zoomPercent = 100;
8 d->forceLineWrap = iFalse; 8 d->forceLineWrap = iFalse;
9 d->font = nunito_TextFont;
9 d->lineWidth = 40; 10 d->lineWidth = 40;
10 d->bigFirstParagraph = iTrue; 11 d->bigFirstParagraph = iTrue;
11 d->docThemeDark = colorfulDark_GmDocumentTheme; 12 d->docThemeDark = colorfulDark_GmDocumentTheme;
diff --git a/src/prefs.h b/src/prefs.h
index d4e300ec..42ccff85 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -1,8 +1,9 @@
1#pragma once 1#pragma once
2#include <the_Foundation/string.h> 2#include <the_Foundation/string.h>
3 3
4#include "ui/color.h"
5#include "gmdocument.h" 4#include "gmdocument.h"
5#include "ui/color.h"
6#include "ui/text.h"
6 7
7/* User preferences */ 8/* User preferences */
8 9
@@ -10,7 +11,7 @@ iDeclareType(Prefs)
10 11
11struct Impl_Prefs { 12struct Impl_Prefs {
12 iBool retainWindowSize; 13 iBool retainWindowSize;
13 float uiScale; 14 float uiScale;
14 int zoomPercent; 15 int zoomPercent;
15 iBool useSystemTheme; 16 iBool useSystemTheme;
16 enum iColorTheme theme; 17 enum iColorTheme theme;
@@ -18,6 +19,7 @@ struct Impl_Prefs {
18 iString httpProxy; 19 iString httpProxy;
19 iString downloadDir; 20 iString downloadDir;
20 /* Content */ 21 /* Content */
22 enum iTextFont font;
21 int lineWidth; 23 int lineWidth;
22 iBool bigFirstParagraph; 24 iBool bigFirstParagraph;
23 iBool forceLineWrap; 25 iBool forceLineWrap;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 351d1e62..0079465f 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -127,6 +127,18 @@ iDefineTypeConstruction(Model)
127 127
128/*----------------------------------------------------------------------------------------------*/ 128/*----------------------------------------------------------------------------------------------*/
129 129
130iDeclareType(OutlineItem)
131
132struct Impl_OutlineItem {
133 iRangecc text;
134 int font;
135 iRect rect;
136 int seenColor;
137 int sepColor;
138};
139
140/*----------------------------------------------------------------------------------------------*/
141
130static const int smoothSpeed_DocumentWidget_ = 120; /* unit: gap_Text per second */ 142static const int smoothSpeed_DocumentWidget_ = 120; /* unit: gap_Text per second */
131 143
132enum iRequestState { 144enum iRequestState {
@@ -161,6 +173,7 @@ struct Impl_DocumentWidget {
161 const iGmRun * contextLink; 173 const iGmRun * contextLink;
162 iBool noHoverWhileScrolling; 174 iBool noHoverWhileScrolling;
163 iBool showLinkNumbers; 175 iBool showLinkNumbers;
176 const iGmRun * lastVisibleRun;
164 iClick click; 177 iClick click;
165 float initNormScrollY; 178 float initNormScrollY;
166 int scrollY; 179 int scrollY;
@@ -170,6 +183,8 @@ struct Impl_DocumentWidget {
170 int smoothLastOffset; 183 int smoothLastOffset;
171 iBool smoothContinue; 184 iBool smoothContinue;
172 iAnim sideOpacity; 185 iAnim sideOpacity;
186 iAnim outlineOpacity;
187 iArray outline;
173 iWidget * menu; 188 iWidget * menu;
174 iVisBuf * visBuf; 189 iVisBuf * visBuf;
175 iPtrSet * invalidRuns; 190 iPtrSet * invalidRuns;
@@ -207,8 +222,10 @@ void init_DocumentWidget(iDocumentWidget *d) {
207 d->contextLink = NULL; 222 d->contextLink = NULL;
208 d->noHoverWhileScrolling = iFalse; 223 d->noHoverWhileScrolling = iFalse;
209 d->showLinkNumbers = iFalse; 224 d->showLinkNumbers = iFalse;
225 d->lastVisibleRun = NULL;
210 d->visBuf = new_VisBuf(); 226 d->visBuf = new_VisBuf();
211 d->invalidRuns = new_PtrSet(); 227 d->invalidRuns = new_PtrSet();
228 init_Array(&d->outline, sizeof(iOutlineItem));
212 init_Anim(&d->sideOpacity, 0); 229 init_Anim(&d->sideOpacity, 0);
213 init_String(&d->sourceMime); 230 init_String(&d->sourceMime);
214 init_Block(&d->sourceContent, 0); 231 init_Block(&d->sourceContent, 0);
@@ -227,6 +244,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
227void deinit_DocumentWidget(iDocumentWidget *d) { 244void deinit_DocumentWidget(iDocumentWidget *d) {
228 delete_VisBuf(d->visBuf); 245 delete_VisBuf(d->visBuf);
229 delete_PtrSet(d->invalidRuns); 246 delete_PtrSet(d->invalidRuns);
247 deinit_Array(&d->outline);
230 iRelease(d->media); 248 iRelease(d->media);
231 iRelease(d->request); 249 iRelease(d->request);
232 deinit_Block(&d->sourceContent); 250 deinit_Block(&d->sourceContent);
@@ -314,6 +332,7 @@ static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) {
314 332
315static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { 333static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) {
316 iDocumentWidget *d = context; 334 iDocumentWidget *d = context;
335 d->lastVisibleRun = run;
317 if (run->linkId && linkFlags_GmDocument(d->doc, run->linkId) & supportedProtocol_GmLinkFlag) { 336 if (run->linkId && linkFlags_GmDocument(d->doc, run->linkId) & supportedProtocol_GmLinkFlag) {
318 pushBack_PtrArray(&d->visibleLinks, run); 337 pushBack_PtrArray(&d->visibleLinks, run);
319 } 338 }
@@ -379,7 +398,7 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) {
379 398
380static void animate_DocumentWidget_(void *ticker) { 399static void animate_DocumentWidget_(void *ticker) {
381 iDocumentWidget *d = ticker; 400 iDocumentWidget *d = ticker;
382 if (!isFinished_Anim(&d->sideOpacity)) { 401 if (!isFinished_Anim(&d->sideOpacity) || !isFinished_Anim(&d->outlineOpacity)) {
383 addTicker_App(animate_DocumentWidget_, d); 402 addTicker_App(animate_DocumentWidget_, d);
384 } 403 }
385} 404}
@@ -390,7 +409,20 @@ static void updateSideOpacity_DocumentWidget_(iDocumentWidget *d) {
390 if (banner && bottom_Rect(banner->visBounds) < d->scrollY) { 409 if (banner && bottom_Rect(banner->visBounds) < d->scrollY) {
391 opacity = 1.0f; 410 opacity = 1.0f;
392 } 411 }
393 setValue_Anim(&d->sideOpacity, opacity, opacity < 0.5f ? 166 : 333); 412 setValue_Anim(&d->sideOpacity, opacity, opacity < 0.5f ? 100 : 200);
413 animate_DocumentWidget_(d);
414}
415
416static void updateOutlineOpacity_DocumentWidget_(iDocumentWidget *d) {
417 float opacity = 0.0f;
418 if (isEmpty_Array(&d->outline)) {
419 setValue_Anim(&d->outlineOpacity, 0.0f, 0);
420 return;
421 }
422 if (contains_Widget(constAs_Widget(d->scroll), mouseCoord_Window(get_Window()))) {
423 opacity = 1.0f;
424 }
425 setValue_Anim(&d->outlineOpacity, opacity, opacity > 0.5f? 166 : 333);
394 animate_DocumentWidget_(d); 426 animate_DocumentWidget_(d);
395} 427}
396 428
@@ -506,16 +538,77 @@ static void invalidate_DocumentWidget_(iDocumentWidget *d) {
506 clear_PtrSet(d->invalidRuns); 538 clear_PtrSet(d->invalidRuns);
507} 539}
508 540
541static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */
542static const int outlineMaxWidth_DocumentWidget_ = 65; /* times gap_UI */
543static const int outlinePadding_DocumentWidget_ = 3; /* times gap_UI */
544
545static int outlineWidth_DocumentWidget_(const iDocumentWidget *d) {
546 const iWidget *w = constAs_Widget(d);
547 const iRect bounds = bounds_Widget(w);
548 const int docWidth = documentWidth_DocumentWidget_(d);
549 int width =
550 (width_Rect(bounds) - docWidth) / 2 - gap_Text * d->pageMargin - gap_UI * d->pageMargin
551 - 2 * outlinePadding_DocumentWidget_ * gap_UI;
552 if (width < outlineMinWidth_DocumentWdiget_ * gap_UI) {
553 return outlineMinWidth_DocumentWdiget_ * gap_UI;
554 }
555 return iMin(width, outlineMaxWidth_DocumentWidget_ * gap_UI);
556}
557
558static iRangecc bannerText_DocumentWidget_(const iDocumentWidget *d) {
559 return isEmpty_String(d->titleUser) ? range_String(bannerText_GmDocument(d->doc))
560 : range_String(d->titleUser);
561}
562
563static void updateOutline_DocumentWidget_(iDocumentWidget *d) {
564 iWidget *w = as_Widget(d);
565 int outWidth = outlineWidth_DocumentWidget_(d);
566 clear_Array(&d->outline);
567 if (outWidth == 0 || d->state != ready_RequestState) {
568 return;
569 }
570 if (size_GmDocument(d->doc).y < height_Rect(bounds_Widget(w)) * 2) {
571 return; /* Too short */
572 }
573 iInt2 pos = zero_I2();
574// const iRangecc topText = urlHost_String(d->mod.url);
575// iInt2 size = advanceWrapRange_Text(uiContent_FontId, outWidth, topText);
576// pushBack_Array(&d->outline, &(iOutlineItem){ topText, uiContent_FontId, (iRect){ pos, size },
577// tmBannerTitle_ColorId, none_ColorId });
578// pos.y += size.y;
579 iInt2 size;
580 iConstForEach(Array, i, headings_GmDocument(d->doc)) {
581 const iGmHeading *head = i.value;
582 const int indent = head->level * 5 * gap_UI;
583 size = advanceWrapRange_Text(uiLabel_FontId, outWidth - indent, head->text);
584 if (head->level == 0) {
585 pos.y += gap_UI * 1.5f;
586 }
587 pushBack_Array(&d->outline,
588 &(iOutlineItem){ head->text,
589 uiLabel_FontId,
590 (iRect){ addX_I2(pos, indent), size },
591 head->level == 0 ? tmHeading1_ColorId
592 : head->level == 1 ? tmHeading2_ColorId
593 : tmHeading3_ColorId,
594 head->level == 0 ? tmQuoteIcon_ColorId : none_ColorId });
595 pos.y += size.y;
596 }
597}
598
509static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) { 599static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) {
510 setUrl_GmDocument(d->doc, d->mod.url); 600 setUrl_GmDocument(d->doc, d->mod.url);
511 setSource_GmDocument( 601 setSource_GmDocument(
512 d->doc, source, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d)); 602 d->doc, source, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d));
513 d->foundMark = iNullRange; 603 d->foundMark = iNullRange;
514 d->selectMark = iNullRange; 604 d->selectMark = iNullRange;
515 d->hoverLink = NULL; 605 d->hoverLink = NULL;
516 d->contextLink = NULL; 606 d->contextLink = NULL;
607 d->lastVisibleRun = NULL;
608 setValue_Anim(&d->outlineOpacity, 0.0f, 0);
517 updateWindowTitle_DocumentWidget_(d); 609 updateWindowTitle_DocumentWidget_(d);
518 updateVisible_DocumentWidget_(d); 610 updateVisible_DocumentWidget_(d);
611 updateOutline_DocumentWidget_(d);
519 invalidate_DocumentWidget_(d); 612 invalidate_DocumentWidget_(d);
520 refresh_Widget(as_Widget(d)); 613 refresh_Widget(as_Widget(d));
521} 614}
@@ -751,6 +844,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
751 updateDocument_DocumentWidget_(d, resp); 844 updateDocument_DocumentWidget_(d, resp);
752 d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y; 845 d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y;
753 d->state = ready_RequestState; 846 d->state = ready_RequestState;
847 updateOutline_DocumentWidget_(d);
754 updateVisible_DocumentWidget_(d); 848 updateVisible_DocumentWidget_(d);
755 postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); 849 postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url));
756 return iTrue; 850 return iTrue;
@@ -1069,6 +1163,7 @@ static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) {
1069void updateSize_DocumentWidget(iDocumentWidget *d) { 1163void updateSize_DocumentWidget(iDocumentWidget *d) {
1070 setWidth_GmDocument( 1164 setWidth_GmDocument(
1071 d->doc, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d)); 1165 d->doc, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d));
1166 updateOutline_DocumentWidget_(d);
1072 updateVisible_DocumentWidget_(d); 1167 updateVisible_DocumentWidget_(d);
1073 invalidate_DocumentWidget_(d); 1168 invalidate_DocumentWidget_(d);
1074} 1169}
@@ -1087,6 +1182,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1087 scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); 1182 scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue);
1088 } 1183 }
1089 } 1184 }
1185 updateOutline_DocumentWidget_(d);
1090 invalidate_DocumentWidget_(d); 1186 invalidate_DocumentWidget_(d);
1091 dealloc_VisBuf(d->visBuf); 1187 dealloc_VisBuf(d->visBuf);
1092 refresh_Widget(w); 1188 refresh_Widget(w);
@@ -1109,6 +1205,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1109 updateSize_DocumentWidget(d); 1205 updateSize_DocumentWidget(d);
1110 updateFetchProgress_DocumentWidget_(d); 1206 updateFetchProgress_DocumentWidget_(d);
1111 } 1207 }
1208 updateOutlineOpacity_DocumentWidget_(d);
1112 updateWindowTitle_DocumentWidget_(d); 1209 updateWindowTitle_DocumentWidget_(d);
1113 allocVisBuffer_DocumentWidget_(d); 1210 allocVisBuffer_DocumentWidget_(d);
1114 return iFalse; 1211 return iFalse;
@@ -1213,6 +1310,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1213 } 1310 }
1214 iReleasePtr(&d->request); 1311 iReleasePtr(&d->request);
1215 updateVisible_DocumentWidget_(d); 1312 updateVisible_DocumentWidget_(d);
1313 updateOutline_DocumentWidget_(d);
1216 postCommandf_App("document.changed url:%s", cstr_String(d->mod.url)); 1314 postCommandf_App("document.changed url:%s", cstr_String(d->mod.url));
1217 return iFalse; 1315 return iFalse;
1218 } 1316 }
@@ -1559,6 +1657,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
1559 else { 1657 else {
1560 updateHover_DocumentWidget_(d, init_I2(ev->motion.x, ev->motion.y)); 1658 updateHover_DocumentWidget_(d, init_I2(ev->motion.x, ev->motion.y));
1561 } 1659 }
1660 updateOutlineOpacity_DocumentWidget_(d);
1562 } 1661 }
1563 if (ev->type == SDL_MOUSEBUTTONDOWN) { 1662 if (ev->type == SDL_MOUSEBUTTONDOWN) {
1564 if (ev->button.button == SDL_BUTTON_X1) { 1663 if (ev->button.button == SDL_BUTTON_X1) {
@@ -1728,11 +1827,6 @@ struct Impl_DrawContext {
1728 iBool showLinkNumbers; 1827 iBool showLinkNumbers;
1729}; 1828};
1730 1829
1731static iRangecc bannerText_DocumentWidget_(const iDocumentWidget *d) {
1732 return isEmpty_String(d->titleUser) ? range_String(bannerText_GmDocument(d->doc))
1733 : range_String(d->titleUser);
1734}
1735
1736static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color, 1830static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color,
1737 iRangecc mark, iBool *isInside) { 1831 iRangecc mark, iBool *isInside) {
1738 if (mark.start > mark.end) { 1832 if (mark.start > mark.end) {
@@ -1964,6 +2058,15 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
1964// drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); 2058// drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId);
1965} 2059}
1966 2060
2061static void drawSideRect_(iPaint *p, iRect rect, int thickness) {
2062 if (equal_Color(get_Color(tmBannerBackground_ColorId), get_Color(tmBackground_ColorId))) {
2063 drawRectThickness_Paint(p, rect, thickness, tmBannerIcon_ColorId);
2064 }
2065 else {
2066 fillRect_Paint(p, rect, tmBannerBackground_ColorId);
2067 }
2068}
2069
1967static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { 2070static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
1968 const iWidget *w = constAs_Widget(d); 2071 const iWidget *w = constAs_Widget(d);
1969 const iRect bounds = bounds_Widget(w); 2072 const iRect bounds = bounds_Widget(w);
@@ -1973,17 +2076,19 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
1973 float opacity = value_Anim(&d->sideOpacity); 2076 float opacity = value_Anim(&d->sideOpacity);
1974 const int minBannerSize = lineHeight_Text(banner_FontId) * 2; 2077 const int minBannerSize = lineHeight_Text(banner_FontId) * 2;
1975 const int avail = left_Rect(docBounds) - left_Rect(bounds) - 2 * margin; 2078 const int avail = left_Rect(docBounds) - left_Rect(bounds) - 2 * margin;
2079 iPaint p;
2080 init_Paint(&p);
2081 setClip_Paint(&p, bounds);
1976 if (avail > minBannerSize) { 2082 if (avail > minBannerSize) {
1977 if (banner && opacity > 0) { 2083 if (banner && opacity > 0) {
1978 setOpacity_Text(opacity); 2084 setOpacity_Text(opacity);
1979 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); 2085 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
1980 const iChar icon = siteIcon_GmDocument(d->doc); 2086 const iChar icon = siteIcon_GmDocument(d->doc);
1981 iRect rect = { add_I2(topLeft_Rect(bounds), init1_I2(margin)), init1_I2(minBannerSize) }; 2087 iRect rect = { add_I2(topLeft_Rect(bounds), init1_I2(margin)), init1_I2(minBannerSize) };
1982 iPaint p;
1983 init_Paint(&p);
1984 p.alpha = opacity * 255; 2088 p.alpha = opacity * 255;
1985 int offset = iMax(0, bottom_Rect(banner->visBounds) - d->scrollY); 2089 //int offset = iMax(0, bottom_Rect(banner->visBounds) - d->scrollY);
1986 rect.pos.y += offset; 2090 rect.pos.y += height_Rect(bounds) / 2 - rect.size.y / 2 - (banner ? banner->visBounds.size.y / 2 : 0); // offset;
2091 drawSideRect_(&p, rect, gap_UI / 2);
1987 if (equal_Color(get_Color(tmBannerBackground_ColorId), get_Color(tmBackground_ColorId))) { 2092 if (equal_Color(get_Color(tmBannerBackground_ColorId), get_Color(tmBackground_ColorId))) {
1988 drawRectThickness_Paint(&p, rect, gap_UI / 2, tmBannerIcon_ColorId); 2093 drawRectThickness_Paint(&p, rect, gap_UI / 2, tmBannerIcon_ColorId);
1989 } 2094 }
@@ -1993,28 +2098,103 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
1993 iString str; 2098 iString str;
1994 initUnicodeN_String(&str, &icon, 1); 2099 initUnicodeN_String(&str, &icon, 1);
1995 drawCentered_Text(banner_FontId, rect, iTrue, tmBannerIcon_ColorId, "%s", cstr_String(&str)); 2100 drawCentered_Text(banner_FontId, rect, iTrue, tmBannerIcon_ColorId, "%s", cstr_String(&str));
2101#if 0
1996 if (avail >= minBannerSize * 2) { 2102 if (avail >= minBannerSize * 2) {
1997 const char *endp; 2103 const char *endp;
1998 iRangecc text = bannerText_DocumentWidget_(d); 2104 iRangecc text = bannerText_DocumentWidget_(d);
1999 iInt2 pos = addY_I2(bottomLeft_Rect(rect), gap_Text); 2105 iInt2 pos = addY_I2(bottomLeft_Rect(rect), gap_Text);
2000 const int font = banner_FontId; 2106 const int font = heading3_FontId;
2001 while (!isEmpty_Range(&text)) { 2107 while (!isEmpty_Range(&text)) {
2002 tryAdvance_Text(font, text, avail - 2 * margin, &endp); 2108 tryAdvance_Text(font, text, avail - 2 * margin, &endp);
2003 drawRange_Text(font, pos, tmBannerSideTitle_ColorId, (iRangecc){ text.start, endp }); 2109 drawRange_Text(
2110 font, pos, tmBannerTitle_ColorId, (iRangecc){ text.start, endp });
2111// drawRange_Text(font,
2112// add_I2(pos, init1_I2(-gap_UI / 4)),
2113// tmBackground_ColorId,
2114// (iRangecc){ text.start, endp });
2004 text.start = endp; 2115 text.start = endp;
2005 pos.y += lineHeight_Text(font); 2116 pos.y += lineHeight_Text(font);
2006 } 2117 }
2007 } 2118 }
2119#endif
2008 setOpacity_Text(1.0f); 2120 setOpacity_Text(1.0f);
2009 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); 2121 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
2010 } 2122 }
2011 /* Update date. */
2012 drawString_Text(default_FontId,
2013 add_I2(bottomLeft_Rect(bounds),
2014 init_I2(margin, -margin + -2 * lineHeight_Text(default_FontId))),
2015 tmQuoteIcon_ColorId,
2016 collect_String(format_Time(&d->sourceTime, "Received\n%H:%M %b %d, %Y")));
2017 } 2123 }
2124 /* Reception timestamp. */
2125 if (isValid_Time(&d->sourceTime)) {
2126 const int font = uiLabel_FontId;
2127 const iString *recv =
2128 collect_String(format_Time(&d->sourceTime, "Received at %I:%M %p\non %b %d, %Y"));
2129 const iInt2 size = advanceRange_Text(font, range_String(recv));
2130 if (size.x <= avail) {
2131 drawString_Text(font,
2132 add_I2(bottomLeft_Rect(bounds),
2133 init_I2(margin,
2134 -margin + -size.y +
2135 iMax(0, scrollMax_DocumentWidget_(d) - d->scrollY))),
2136 tmQuoteIcon_ColorId,
2137 recv);
2138 }
2139 }
2140 /* Outline on the right side. */
2141 const float outlineOpacity = value_Anim(&d->outlineOpacity);
2142 if (!isEmpty_Array(&d->outline) && outlineOpacity > 0.0f) {
2143// const int font = uiLabel_FontId;
2144 //iRect outlineRect = initCorners_Rect(topRight_Rect(docBounds), bottomRight_Rect(bounds));
2145 //const int excess = width_Rect(outlineRect) - 75 * gap_UI;
2146// if (excess > 0) {
2147// adjustEdges_Rect(&outlineRect, 0, 0, 0, excess);
2148// }
2149// const int margin = gap_UI * d->pageMargin;
2150 const int innerWidth = outlineWidth_DocumentWidget_(d);
2151 const int outWidth = innerWidth + 2 * outlinePadding_DocumentWidget_ * gap_UI;
2152 const int topMargin = 0;//d->pageMargin * gap_UI; // + (banner ? banner->visBounds.size.y : 0);
2153 const int bottomMargin = 3 * gap_UI; //d->pageMargin * gap_UI;
2154 iInt2 pos = add_I2(topRight_Rect(bounds), init_I2(-outWidth - width_Widget(d->scroll), topMargin));
2155// const int lineWidth = avail - margin;
2156// pos.y = drawRangeWrap_Text(uiContent_FontId, pos, lineWidth, tmBannerIcon_ColorId,
2157// bannerText_DocumentWidget_(d));
2158// pos.y += gap_UI;
2159 const int scrollMax = scrollMax_DocumentWidget_(d);
2160 const int outHeight = bottom_Rect(((const iOutlineItem *) constBack_Array(&d->outline))->rect);
2161 const int oversize = outHeight - height_Rect(bounds) + topMargin + bottomMargin;
2162 const int scroll =
2163 (oversize > 0 && scrollMax > 0 ? oversize * d->scrollY / scrollMax_DocumentWidget_(d)
2164 : 0);
2165 /* Center short outlines vertically. */
2166 if (oversize < 0) {
2167 pos.y -= oversize / 2;// + (banner ? banner->visBounds.size.y / 2 : 0);
2168 }
2169 pos.y -= scroll;
2170 setOpacity_Text(outlineOpacity);
2171 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
2172 p.alpha = outlineOpacity * 255;
2173 drawSideRect_(
2174 &p,
2175 (iRect){ addY_I2(pos, -outlinePadding_DocumentWidget_ * gap_UI / 2),
2176 init_I2(outWidth, outHeight + outlinePadding_DocumentWidget_ * gap_UI * 1.5f) },
2177 1);
2178 iConstForEach(Array, i, &d->outline) {
2179 const iOutlineItem *item = i.value;
2180 iInt2 visPos = addX_I2(add_I2(pos, item->rect.pos), outlinePadding_DocumentWidget_ * gap_UI);
2181// visPos.y -= scroll;
2182// if (item->sepColor != none_ColorId) {
2183// drawHLine_Paint(&p, addY_I2(visPos, -gap_UI), outWidth, item->sepColor);
2184// }
2185// drawRect_Paint(&p, (iRect){ visPos, item->rect.size }, red_ColorId);
2186 const iBool isVisible = d->lastVisibleRun && d->lastVisibleRun->text.start >= item->text.start;
2187 drawWrapRange_Text(item->font,
2188 visPos,
2189 innerWidth - left_Rect(item->rect),
2190 index_ArrayConstIterator(&i) == 0 || isVisible ? item->seenColor
2191 : tmQuoteIcon_ColorId,
2192 item->text);
2193 }
2194 setOpacity_Text(1.0f);
2195 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
2196 }
2197 unsetClip_Paint(&p);
2018} 2198}
2019 2199
2020static void draw_DocumentWidget_(const iDocumentWidget *d) { 2200static void draw_DocumentWidget_(const iDocumentWidget *d) {
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 2d6d84dd..d29548f1 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -204,6 +204,11 @@ static uint32_t cursorTimer_(uint32_t interval, void *w) {
204 return interval; 204 return interval;
205} 205}
206 206
207void selectAll_InputWidget(iInputWidget *d) {
208 d->mark = (iRanges){ 0, size_Array(&d->text) };
209 refresh_Widget(as_Widget(d));
210}
211
207void begin_InputWidget(iInputWidget *d) { 212void begin_InputWidget(iInputWidget *d) {
208 iWidget *w = as_Widget(d); 213 iWidget *w = as_Widget(d);
209 if (flags_Widget(w) & selected_WidgetFlag) { 214 if (flags_Widget(w) & selected_WidgetFlag) {
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
index ced7f968..f146ea00 100644
--- a/src/ui/inputwidget.h
+++ b/src/ui/inputwidget.h
@@ -43,6 +43,7 @@ void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus)
43void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); 43void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits);
44void begin_InputWidget (iInputWidget *); 44void begin_InputWidget (iInputWidget *);
45void end_InputWidget (iInputWidget *, iBool accept); 45void end_InputWidget (iInputWidget *, iBool accept);
46void selectAll_InputWidget (iInputWidget *);
46 47
47const iString * text_InputWidget (const iInputWidget *); 48const iString * text_InputWidget (const iInputWidget *);
48 49
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 1cd8cae9..8bf64b5c 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -116,7 +116,7 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
116 iSidebarItem *item = new_SidebarItem(); 116 iSidebarItem *item = new_SidebarItem();
117 item->id = index_ArrayConstIterator(&i); 117 item->id = index_ArrayConstIterator(&i);
118 setRange_String(&item->label, head->text); 118 setRange_String(&item->label, head->text);
119 item->indent = head->level * 4 * gap_UI; 119 item->indent = head->level * 5 * gap_UI;
120 addItem_ListWidget(d->list, item); 120 addItem_ListWidget(d->list, item);
121 iRelease(item); 121 iRelease(item);
122 } 122 }
diff --git a/src/ui/text.c b/src/ui/text.c
index b2110bb6..23524808 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -142,16 +142,17 @@ struct Impl_CacheRow {
142}; 142};
143 143
144struct Impl_Text { 144struct Impl_Text {
145 float contentFontSize; 145 enum iTextFont contentFont;
146 iFont fonts[max_FontId]; 146 float contentFontSize;
147 SDL_Renderer *render; 147 iFont fonts[max_FontId];
148 SDL_Texture * cache; 148 SDL_Renderer * render;
149 iInt2 cacheSize; 149 SDL_Texture * cache;
150 int cacheRowAllocStep; 150 iInt2 cacheSize;
151 int cacheBottom; 151 int cacheRowAllocStep;
152 iArray cacheRows; 152 int cacheBottom;
153 SDL_Palette * grayscale; 153 iArray cacheRows;
154 iRegExp * ansiEscape; 154 SDL_Palette * grayscale;
155 iRegExp * ansiEscape;
155}; 156};
156 157
157static iText text_; 158static iText text_;
@@ -159,6 +160,16 @@ static iText text_;
159static void initFonts_Text_(iText *d) { 160static void initFonts_Text_(iText *d) {
160 const float textSize = fontSize_UI * d->contentFontSize; 161 const float textSize = fontSize_UI * d->contentFontSize;
161 const float monoSize = fontSize_UI * d->contentFontSize / contentScale_Text_ * 0.866f; 162 const float monoSize = fontSize_UI * d->contentFontSize / contentScale_Text_ * 0.866f;
163 const iBlock *regularFont = &fontNunitoRegular_Embedded;
164 const iBlock *italicFont = &fontNunitoLightItalic_Embedded;
165 const iBlock *boldFont = &fontNunitoExtraBold_Embedded;
166 const iBlock *lightFont = &fontNunitoExtraLight_Embedded;
167 if (d->contentFont == firaSans_TextFont) {
168 regularFont = &fontFiraSansRegular_Embedded;
169 italicFont = &fontFiraSansItalic_Embedded;
170 boldFont = &fontFiraSansBold_Embedded;
171 lightFont = &fontFiraSansLight_Embedded;
172 }
162 const struct { 173 const struct {
163 const iBlock *ttf; 174 const iBlock *ttf;
164 int size; 175 int size;
@@ -168,17 +179,17 @@ static void initFonts_Text_(iText *d) {
168 { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.125f, defaultMediumSymbols_FontId }, 179 { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.125f, defaultMediumSymbols_FontId },
169 { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, defaultSymbols_FontId }, 180 { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, defaultSymbols_FontId },
170 /* content fonts */ 181 /* content fonts */
171 { &fontNunitoRegular_Embedded, textSize, symbols_FontId }, 182 { regularFont, textSize, symbols_FontId },
172 { &fontFiraMonoRegular_Embedded, monoSize, monospaceSymbols_FontId }, 183 { &fontFiraMonoRegular_Embedded, monoSize, monospaceSymbols_FontId },
173 { &fontFiraMonoRegular_Embedded, monoSize * 0.750f, monospaceSmallSymbols_FontId }, 184 { &fontFiraMonoRegular_Embedded, monoSize * 0.750f, monospaceSmallSymbols_FontId },
174 { &fontNunitoRegular_Embedded, textSize * 1.200f, mediumSymbols_FontId }, 185 { regularFont, textSize * 1.200f, mediumSymbols_FontId },
175 { &fontNunitoRegular_Embedded, textSize * 1.333f, bigSymbols_FontId }, 186 { regularFont, textSize * 1.333f, bigSymbols_FontId },
176 { &fontNunitoLightItalic_Embedded, textSize, symbols_FontId }, 187 { italicFont, textSize, symbols_FontId },
177 { &fontNunitoExtraBold_Embedded, textSize, symbols_FontId }, 188 { boldFont, textSize, symbols_FontId },
178 { &fontNunitoExtraBold_Embedded, textSize * 1.333f, mediumSymbols_FontId }, 189 { boldFont, textSize * 1.333f, mediumSymbols_FontId },
179 { &fontNunitoExtraBold_Embedded, textSize * 1.666f, largeSymbols_FontId }, 190 { boldFont, textSize * 1.666f, largeSymbols_FontId },
180 { &fontNunitoExtraBold_Embedded, textSize * 2.000f, hugeSymbols_FontId }, 191 { boldFont, textSize * 2.000f, hugeSymbols_FontId },
181 { &fontNunitoExtraLight_Embedded, textSize * 1.666f, largeSymbols_FontId }, 192 { lightFont, textSize * 1.666f, largeSymbols_FontId },
182 /* symbol fonts */ 193 /* symbol fonts */
183 { &fontSymbola_Embedded, fontSize_UI, defaultSymbols_FontId }, 194 { &fontSymbola_Embedded, fontSize_UI, defaultSymbols_FontId },
184 { &fontSymbola_Embedded, fontSize_UI * 1.125f, defaultMediumSymbols_FontId }, 195 { &fontSymbola_Embedded, fontSize_UI * 1.125f, defaultMediumSymbols_FontId },
@@ -278,6 +289,7 @@ static void deinitCache_Text_(iText *d) {
278 289
279void init_Text(SDL_Renderer *render) { 290void init_Text(SDL_Renderer *render) {
280 iText *d = &text_; 291 iText *d = &text_;
292 d->contentFont = nunito_TextFont;
281 d->contentFontSize = contentScale_Text_; 293 d->contentFontSize = contentScale_Text_;
282 d->ansiEscape = new_RegExp("\\[([0-9;]+)m", 0); 294 d->ansiEscape = new_RegExp("\\[([0-9;]+)m", 0);
283 d->render = render; 295 d->render = render;
@@ -306,6 +318,13 @@ void setOpacity_Text(float opacity) {
306 SDL_SetTextureAlphaMod(text_.cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f); 318 SDL_SetTextureAlphaMod(text_.cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f);
307} 319}
308 320
321void setContentFont_Text(enum iTextFont font) {
322 if (text_.contentFont != font) {
323 text_.contentFont = font;
324 resetFonts_Text();
325 }
326}
327
309void setContentFontSize_Text(float fontSizeFactor) { 328void setContentFontSize_Text(float fontSizeFactor) {
310 fontSizeFactor *= contentScale_Text_; 329 fontSizeFactor *= contentScale_Text_;
311 iAssert(fontSizeFactor > 0); 330 iAssert(fontSizeFactor > 0);
@@ -766,6 +785,29 @@ void drawRange_Text(int fontId, iInt2 pos, int color, iRangecc text) {
766 draw_Text_(fontId, pos, color, text); 785 draw_Text_(fontId, pos, color, text);
767} 786}
768 787
788iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) {
789 iInt2 size = zero_I2();
790 const char *endp;
791 while (!isEmpty_Range(&text)) {
792 iInt2 line = tryAdvance_Text(fontId, text, maxWidth, &endp);
793 text.start = endp;
794 size.x = iMax(size.x, line.x);
795 size.y += line.y;
796 }
797 return size;
798}
799
800int drawWrapRange_Text(int fontId, iInt2 pos, int maxWidth, int color, iRangecc text) {
801 const char *endp;
802 while (!isEmpty_Range(&text)) {
803 tryAdvance_Text(fontId, text, maxWidth, &endp);
804 drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endp });
805 text.start = endp;
806 pos.y += lineHeight_Text(fontId);
807 }
808 return pos.y;
809}
810
769void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, const char *format, ...) { 811void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, const char *format, ...) {
770 iBlock chars; 812 iBlock chars;
771 init_Block(&chars, 0); { 813 init_Block(&chars, 0); {
diff --git a/src/ui/text.h b/src/ui/text.h
index 71c2ba43..fa7b9402 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -105,13 +105,19 @@ iLocalDef iBool isVariationSelector_Char(iChar ch) {
105 105
106#define variationSelectorEmoji_Char ((iChar) 0xfe0f) 106#define variationSelectorEmoji_Char ((iChar) 0xfe0f)
107 107
108enum iTextFont {
109 nunito_TextFont,
110 firaSans_TextFont,
111};
112
108extern int gap_Text; /* affected by content font size */ 113extern int gap_Text; /* affected by content font size */
109 114
110void init_Text (SDL_Renderer *); 115void init_Text (SDL_Renderer *);
111void deinit_Text (void); 116void deinit_Text (void);
112 117
118void setContentFont_Text (enum iTextFont font);
113void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ 119void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */
114void resetFonts_Text (void); 120void resetFonts_Text (void);
115 121
116int lineHeight_Text (int fontId); 122int lineHeight_Text (int fontId);
117iInt2 measure_Text (int fontId, const char *text); 123iInt2 measure_Text (int fontId, const char *text);
@@ -120,6 +126,7 @@ iRect visualBounds_Text (int fontId, iRangecc text);
120iInt2 advance_Text (int fontId, const char *text); 126iInt2 advance_Text (int fontId, const char *text);
121iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */ 127iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */
122iInt2 advanceRange_Text (int fontId, iRangecc text); 128iInt2 advanceRange_Text (int fontId, iRangecc text);
129iInt2 advanceWrapRange_Text (int fontId, int maxWidth, iRangecc text);
123 130
124iInt2 tryAdvance_Text (int fontId, iRangecc text, int width, const char **endPos); 131iInt2 tryAdvance_Text (int fontId, iRangecc text, int width, const char **endPos);
125iInt2 tryAdvanceNoWrap_Text (int fontId, iRangecc text, int width, const char **endPos); 132iInt2 tryAdvanceNoWrap_Text (int fontId, iRangecc text, int width, const char **endPos);
@@ -137,6 +144,7 @@ void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment a
137void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); 144void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...);
138void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); 145void drawString_Text (int fontId, iInt2 pos, int color, const iString *text);
139void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); 146void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text);
147int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */
140 148
141SDL_Texture * glyphCache_Text (void); 149SDL_Texture * glyphCache_Text (void);
142 150
diff --git a/src/ui/util.c b/src/ui/util.c
index 5d283742..27bac834 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 36
37#include <the_Foundation/math.h> 37#include <the_Foundation/math.h>
38#include <the_Foundation/path.h> 38#include <the_Foundation/path.h>
39#include <SDL_timer.h>
39 40
40iBool isCommand_SDLEvent(const SDL_Event *d) { 41iBool isCommand_SDLEvent(const SDL_Event *d) {
41 return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode; 42 return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode;
@@ -140,7 +141,7 @@ void init_Anim(iAnim *d, float value) {
140 141
141void setValue_Anim(iAnim *d, float to, uint32_t span) { 142void setValue_Anim(iAnim *d, float to, uint32_t span) {
142 if (fabsf(to - d->to) > 0.00001f) { 143 if (fabsf(to - d->to) > 0.00001f) {
143 const uint32_t now = frameTime_Window(get_Window()); 144 const uint32_t now = SDL_GetTicks();
144 d->from = value_Anim(d); 145 d->from = value_Anim(d);
145 d->to = to; 146 d->to = to;
146 d->when = now; 147 d->when = now;
@@ -918,6 +919,13 @@ iWidget *makePreferences_Widget(void) {
918 } 919 }
919 /* Layout. */ { 920 /* Layout. */ {
920 appendTwoColumnPage_(tabs, "Layout", '2', &headings, &values); 921 appendTwoColumnPage_(tabs, "Layout", '2', &headings, &values);
922 addChild_Widget(headings, iClob(makeHeading_Widget("Font:")));
923 iWidget *fonts = new_Widget();
924 /* Fonts. */ {
925 addRadioButton_(fonts, "prefs.font.0", "Nunito", "font.set arg:0");
926 addRadioButton_(fonts, "prefs.font.1", "Fira Sans", "font.set arg:1");
927 }
928 addChildFlags_Widget(values, iClob(fonts), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
921 addChild_Widget(headings, iClob(makeHeading_Widget("Line width:"))); 929 addChild_Widget(headings, iClob(makeHeading_Widget("Line width:")));
922 iWidget *widths = new_Widget(); 930 iWidget *widths = new_Widget();
923 /* Line widths. */ { 931 /* Line widths. */ {
diff --git a/src/ui/window.c b/src/ui/window.c
index 19432691..b1fd3a07 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -94,7 +94,7 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
94/* TODO: Submenus wouldn't hurt here. */ 94/* TODO: Submenus wouldn't hurt here. */
95static const iMenuItem navMenuItems[] = { 95static const iMenuItem navMenuItems[] = {
96 { "New Tab", 't', KMOD_PRIMARY, "tabs.new" }, 96 { "New Tab", 't', KMOD_PRIMARY, "tabs.new" },
97 { "Open Location...", SDLK_l, KMOD_PRIMARY, "focus.set id:url" }, 97 { "Open Location...", SDLK_l, KMOD_PRIMARY, "navigate.focus" },
98 { "---", 0, 0, NULL }, 98 { "---", 0, 0, NULL },
99 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" }, 99 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" },
100 { "---", 0, 0, NULL }, 100 { "---", 0, 0, NULL },
@@ -118,7 +118,7 @@ static const iMenuItem navMenuItems[] = {
118/* Using native menus. */ 118/* Using native menus. */
119static const iMenuItem fileMenuItems[] = { 119static const iMenuItem fileMenuItems[] = {
120 { "New Tab", SDLK_t, KMOD_PRIMARY, "tabs.new" }, 120 { "New Tab", SDLK_t, KMOD_PRIMARY, "tabs.new" },
121 { "Open Location...", SDLK_l, KMOD_PRIMARY, "focus.set id:url" }, 121 { "Open Location...", SDLK_l, KMOD_PRIMARY, "navigate.focus" },
122 { "---", 0, 0, NULL }, 122 { "---", 0, 0, NULL },
123 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" }, 123 { "Save to Downloads", SDLK_s, KMOD_PRIMARY, "document.save" },
124}; 124};
@@ -212,6 +212,16 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
212 postCommand_Widget(navBar, "layout.changed id:navbar"); 212 postCommand_Widget(navBar, "layout.changed id:navbar");
213 return iFalse; 213 return iFalse;
214 } 214 }
215 else if (equal_Command(cmd, "navigate.focus")) {
216 iWidget *url = findChild_Widget(navBar, "url");
217 if (focus_Widget() != url) {
218 setFocus_Widget(findChild_Widget(navBar, "url"));
219 }
220 else {
221 selectAll_InputWidget((iInputWidget *) url);
222 }
223 return iTrue;
224 }
215 else if (equal_Command(cmd, "input.edited")) { 225 else if (equal_Command(cmd, "input.edited")) {
216 iAnyObject *url = findChild_Widget(navBar, "url"); 226 iAnyObject *url = findChild_Widget(navBar, "url");
217 if (pointer_Command(cmd) == url) { 227 if (pointer_Command(cmd) == url) {
@@ -459,7 +469,7 @@ static void setupUserInterface_Window(iWindow *d) {
459 addAction_Widget(d->root, prevTab_KeyShortcut, "tabs.prev"); 469 addAction_Widget(d->root, prevTab_KeyShortcut, "tabs.prev");
460 addAction_Widget(d->root, nextTab_KeyShortcut, "tabs.next"); 470 addAction_Widget(d->root, nextTab_KeyShortcut, "tabs.next");
461#if !defined (iHaveNativeMenus) 471#if !defined (iHaveNativeMenus)
462 addAction_Widget(d->root, 'l', KMOD_PRIMARY, "focus.set id:url"); 472 addAction_Widget(d->root, 'l', KMOD_PRIMARY, "navigate.focus");
463 addAction_Widget(d->root, 'f', KMOD_PRIMARY, "focus.set id:find.input"); 473 addAction_Widget(d->root, 'f', KMOD_PRIMARY, "focus.set id:find.input");
464 addAction_Widget(d->root, '1', KMOD_PRIMARY, "sidebar.mode arg:0 toggle:1"); 474 addAction_Widget(d->root, '1', KMOD_PRIMARY, "sidebar.mode arg:0 toggle:1");
465 addAction_Widget(d->root, '2', KMOD_PRIMARY, "sidebar.mode arg:1 toggle:1"); 475 addAction_Widget(d->root, '2', KMOD_PRIMARY, "sidebar.mode arg:1 toggle:1");