diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-09-25 18:32:54 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-09-25 18:32:54 +0300 |
commit | 84d3089735bd81a9d628acf416b481f2535765cf (patch) | |
tree | 762303b237cb4d34909a175a2b62bf2ec74d23c6 /src | |
parent | 8700c039dc04b4c9f22584d4e901ed372442b0f4 (diff) |
Document side elements; hover outline
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 12 | ||||
-rw-r--r-- | src/gmdocument.h | 2 | ||||
-rw-r--r-- | src/prefs.c | 1 | ||||
-rw-r--r-- | src/prefs.h | 6 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 226 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 5 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 1 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 2 | ||||
-rw-r--r-- | src/ui/text.c | 80 | ||||
-rw-r--r-- | src/ui/text.h | 10 | ||||
-rw-r--r-- | src/ui/util.c | 10 | ||||
-rw-r--r-- | src/ui/window.c | 16 |
12 files changed, 320 insertions, 51 deletions
@@ -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 *); | |||
118 | const iGmRun * siteBanner_GmDocument (const iGmDocument *); | 118 | const iGmRun * siteBanner_GmDocument (const iGmDocument *); |
119 | iBool hasSiteBanner_GmDocument (const iGmDocument *); | 119 | iBool hasSiteBanner_GmDocument (const iGmDocument *); |
120 | const iString * bannerText_GmDocument (const iGmDocument *); | 120 | const iString * bannerText_GmDocument (const iGmDocument *); |
121 | const iArray * headings_GmDocument (const iGmDocument *); | 121 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ |
122 | const iString * source_GmDocument (const iGmDocument *); | 122 | const iString * source_GmDocument (const iGmDocument *); |
123 | 123 | ||
124 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); | 124 | iRangecc 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 | ||
11 | struct Impl_Prefs { | 12 | struct 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 | ||
130 | iDeclareType(OutlineItem) | ||
131 | |||
132 | struct Impl_OutlineItem { | ||
133 | iRangecc text; | ||
134 | int font; | ||
135 | iRect rect; | ||
136 | int seenColor; | ||
137 | int sepColor; | ||
138 | }; | ||
139 | |||
140 | /*----------------------------------------------------------------------------------------------*/ | ||
141 | |||
130 | static const int smoothSpeed_DocumentWidget_ = 120; /* unit: gap_Text per second */ | 142 | static const int smoothSpeed_DocumentWidget_ = 120; /* unit: gap_Text per second */ |
131 | 143 | ||
132 | enum iRequestState { | 144 | enum 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) { | |||
227 | void deinit_DocumentWidget(iDocumentWidget *d) { | 244 | void 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 | ||
315 | static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { | 333 | static 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 | ||
380 | static void animate_DocumentWidget_(void *ticker) { | 399 | static 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 | |||
416 | static 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 | ||
541 | static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */ | ||
542 | static const int outlineMaxWidth_DocumentWidget_ = 65; /* times gap_UI */ | ||
543 | static const int outlinePadding_DocumentWidget_ = 3; /* times gap_UI */ | ||
544 | |||
545 | static 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 | |||
558 | static 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 | |||
563 | static 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 | |||
509 | static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) { | 599 | static 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) { | |||
1069 | void updateSize_DocumentWidget(iDocumentWidget *d) { | 1163 | void 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 | ||
1731 | static 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 | |||
1736 | static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color, | 1830 | static 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 | ||
2061 | static 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 | |||
1967 | static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | 2070 | static 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 | ||
2020 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 2200 | static 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 | ||
207 | void selectAll_InputWidget(iInputWidget *d) { | ||
208 | d->mark = (iRanges){ 0, size_Array(&d->text) }; | ||
209 | refresh_Widget(as_Widget(d)); | ||
210 | } | ||
211 | |||
207 | void begin_InputWidget(iInputWidget *d) { | 212 | void 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) | |||
43 | void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); | 43 | void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); |
44 | void begin_InputWidget (iInputWidget *); | 44 | void begin_InputWidget (iInputWidget *); |
45 | void end_InputWidget (iInputWidget *, iBool accept); | 45 | void end_InputWidget (iInputWidget *, iBool accept); |
46 | void selectAll_InputWidget (iInputWidget *); | ||
46 | 47 | ||
47 | const iString * text_InputWidget (const iInputWidget *); | 48 | const 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 | ||
144 | struct Impl_Text { | 144 | struct 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 | ||
157 | static iText text_; | 158 | static iText text_; |
@@ -159,6 +160,16 @@ static iText text_; | |||
159 | static void initFonts_Text_(iText *d) { | 160 | static 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 | ||
279 | void init_Text(SDL_Renderer *render) { | 290 | void 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 | ||
321 | void setContentFont_Text(enum iTextFont font) { | ||
322 | if (text_.contentFont != font) { | ||
323 | text_.contentFont = font; | ||
324 | resetFonts_Text(); | ||
325 | } | ||
326 | } | ||
327 | |||
309 | void setContentFontSize_Text(float fontSizeFactor) { | 328 | void 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 | ||
788 | iInt2 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 | |||
800 | int 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 | |||
769 | void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, const char *format, ...) { | 811 | void 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 | ||
108 | enum iTextFont { | ||
109 | nunito_TextFont, | ||
110 | firaSans_TextFont, | ||
111 | }; | ||
112 | |||
108 | extern int gap_Text; /* affected by content font size */ | 113 | extern int gap_Text; /* affected by content font size */ |
109 | 114 | ||
110 | void init_Text (SDL_Renderer *); | 115 | void init_Text (SDL_Renderer *); |
111 | void deinit_Text (void); | 116 | void deinit_Text (void); |
112 | 117 | ||
118 | void setContentFont_Text (enum iTextFont font); | ||
113 | void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ | 119 | void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ |
114 | void resetFonts_Text (void); | 120 | void resetFonts_Text (void); |
115 | 121 | ||
116 | int lineHeight_Text (int fontId); | 122 | int lineHeight_Text (int fontId); |
117 | iInt2 measure_Text (int fontId, const char *text); | 123 | iInt2 measure_Text (int fontId, const char *text); |
@@ -120,6 +126,7 @@ iRect visualBounds_Text (int fontId, iRangecc text); | |||
120 | iInt2 advance_Text (int fontId, const char *text); | 126 | iInt2 advance_Text (int fontId, const char *text); |
121 | iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */ | 127 | iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */ |
122 | iInt2 advanceRange_Text (int fontId, iRangecc text); | 128 | iInt2 advanceRange_Text (int fontId, iRangecc text); |
129 | iInt2 advanceWrapRange_Text (int fontId, int maxWidth, iRangecc text); | ||
123 | 130 | ||
124 | iInt2 tryAdvance_Text (int fontId, iRangecc text, int width, const char **endPos); | 131 | iInt2 tryAdvance_Text (int fontId, iRangecc text, int width, const char **endPos); |
125 | iInt2 tryAdvanceNoWrap_Text (int fontId, iRangecc text, int width, const char **endPos); | 132 | iInt2 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 | |||
137 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); | 144 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); |
138 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); | 145 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); |
139 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); | 146 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); |
147 | int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ | ||
140 | 148 | ||
141 | SDL_Texture * glyphCache_Text (void); | 149 | SDL_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 | ||
40 | iBool isCommand_SDLEvent(const SDL_Event *d) { | 41 | iBool 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 | ||
141 | void setValue_Anim(iAnim *d, float to, uint32_t span) { | 142 | void 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. */ |
95 | static const iMenuItem navMenuItems[] = { | 95 | static 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. */ |
119 | static const iMenuItem fileMenuItems[] = { | 119 | static 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"); |