diff options
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/color.c | 67 | ||||
-rw-r--r-- | src/ui/color.h | 3 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 10 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 2 | ||||
-rw-r--r-- | src/ui/root.c | 15 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 42 | ||||
-rw-r--r-- | src/ui/text.c | 258 | ||||
-rw-r--r-- | src/ui/text.h | 5 | ||||
-rw-r--r-- | src/ui/util.c | 69 | ||||
-rw-r--r-- | src/ui/util.h | 1 | ||||
-rw-r--r-- | src/ui/window.c | 8 |
11 files changed, 293 insertions, 187 deletions
diff --git a/src/ui/color.c b/src/ui/color.c index 0f7d4368..adbe444b 100644 --- a/src/ui/color.c +++ b/src/ui/color.c | |||
@@ -785,8 +785,14 @@ static const iColor ansi8BitColors_[256] = { | |||
785 | { 255, 255, 255, 255 } | 785 | { 255, 255, 255, 255 } |
786 | }; | 786 | }; |
787 | 787 | ||
788 | iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { | 788 | void ansiColors_Color(iRangecc escapeSequence, int fgDefault, int bgDefault, |
789 | iColor clr = get_Color(fallback); | 789 | iColor *fg_out, iColor *bg_out) { |
790 | if (!fg_out && !bg_out) { | ||
791 | return; | ||
792 | } | ||
793 | iColor fg, bg; | ||
794 | iZap(fg); | ||
795 | iZap(bg); | ||
790 | for (const char *ch = escapeSequence.start; ch < escapeSequence.end; ch++) { | 796 | for (const char *ch = escapeSequence.start; ch < escapeSequence.end; ch++) { |
791 | char *endPtr; | 797 | char *endPtr; |
792 | unsigned long arg = strtoul(ch, &endPtr, 10); | 798 | unsigned long arg = strtoul(ch, &endPtr, 10); |
@@ -802,19 +808,37 @@ iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { | |||
802 | case 35: | 808 | case 35: |
803 | case 36: | 809 | case 36: |
804 | case 37: | 810 | case 37: |
805 | clr = ansi8BitColors_[arg - 30]; | 811 | fg = ansi8BitColors_[arg - 30]; |
806 | break; | 812 | break; |
807 | case 38: { | 813 | case 38: |
814 | case 48: { | ||
815 | iColor *dst = (arg == 38 ? &fg : &bg); | ||
808 | /* Extended foreground color. */ | 816 | /* Extended foreground color. */ |
809 | arg = strtoul(ch + 1, &endPtr, 10); | 817 | arg = strtoul(ch + 1, &endPtr, 10); |
810 | ch = endPtr; | 818 | ch = endPtr; |
811 | if (arg == 5) /* 8-bit palette */ { | 819 | if (arg == 5) /* 8-bit palette */ { |
812 | arg = strtoul(ch + 1, &endPtr, 10); | 820 | arg = strtoul(ch + 1, &endPtr, 10); |
813 | ch = endPtr; | 821 | ch = endPtr; |
814 | clr = ansi8BitColors_[iClamp(arg, 0, 255)]; | 822 | *dst = ansi8BitColors_[iClamp(arg, 0, 255)]; |
815 | } | 823 | } |
816 | break; | 824 | break; |
817 | } | 825 | } |
826 | case 39: | ||
827 | fg = get_Color(fgDefault); | ||
828 | break; | ||
829 | case 40: | ||
830 | case 41: | ||
831 | case 42: | ||
832 | case 43: | ||
833 | case 44: | ||
834 | case 45: | ||
835 | case 46: | ||
836 | case 47: | ||
837 | bg = ansi8BitColors_[arg - 40]; | ||
838 | break; | ||
839 | case 49: | ||
840 | bg = get_Color(bgDefault); | ||
841 | break; | ||
818 | case 90: | 842 | case 90: |
819 | case 91: | 843 | case 91: |
820 | case 92: | 844 | case 92: |
@@ -823,17 +847,36 @@ iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { | |||
823 | case 95: | 847 | case 95: |
824 | case 96: | 848 | case 96: |
825 | case 97: | 849 | case 97: |
826 | clr = ansi8BitColors_[8 + arg - 90]; | 850 | fg = ansi8BitColors_[8 + arg - 90]; |
827 | break; | 851 | break; |
828 | } | 852 | } |
829 | } | 853 | } |
830 | /* On light backgrounds, darken the colors to make them more legible. */ | 854 | /* Ensure legibility if only one of the colors is set. */ |
831 | if (get_HSLColor(tmBackground_ColorId).lum > 0.5f) { | 855 | /* TODO: Force darkening of the background color, unless it is also specified. */ |
832 | clr.r /= 2; | 856 | #if 0 |
833 | clr.g /= 2; | 857 | if (bg.a == 0 && !equal_Color(fg, get_Color(fgDefault))) { |
834 | clr.b /= 2; | 858 | if (delta_Color(fg, get_Color(tmBackground_ColorId)) < 64) { |
859 | const iHSLColor fgHsl = hsl_Color(fg); | ||
860 | iHSLColor legibleBg = get_HSLColor(tmBackground_ColorId); | ||
861 | if () | ||
862 | bg = rgb_HSLColor(bgHsl); | ||
863 | } | ||
864 | } | ||
865 | } | ||
866 | #endif | ||
867 | /* | ||
868 | if (!bg_out || get_HSLColor(tmBackground_ColorId).lum > 0.5f) { | ||
869 | fg.r /= 2; | ||
870 | fg.g /= 2; | ||
871 | fg.b /= 2; | ||
872 | } | ||
873 | */ | ||
874 | if (fg.a && fg_out) { | ||
875 | *fg_out = fg; | ||
876 | } | ||
877 | if (bg.a && bg_out) { | ||
878 | *bg_out = bg; | ||
835 | } | 879 | } |
836 | return clr; | ||
837 | } | 880 | } |
838 | 881 | ||
839 | iBool loadPalette_Color(const char *path) { | 882 | iBool loadPalette_Color(const char *path) { |
diff --git a/src/ui/color.h b/src/ui/color.h index 03172fcb..6602b226 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -255,7 +255,8 @@ iLocalDef void setHsl_Color(int color, iHSLColor hsl) { | |||
255 | iBool loadPalette_Color (const char *path); | 255 | iBool loadPalette_Color (const char *path); |
256 | void setThemePalette_Color (enum iColorTheme theme); | 256 | void setThemePalette_Color (enum iColorTheme theme); |
257 | 257 | ||
258 | iColor ansiForeground_Color (iRangecc escapeSequence, int fallback); | 258 | void ansiColors_Color (iRangecc escapeSequence, int fgDefault, int bgDefault, |
259 | iColor *fg_out, iColor *bg_out); /* can be NULL */ | ||
259 | const char * escape_Color (int color); | 260 | const char * escape_Color (int color); |
260 | enum iColorId parseEscape_Color (const char *cstr, const char **endp); | 261 | enum iColorId parseEscape_Color (const char *cstr, const char **endp); |
261 | 262 | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index f2553632..0f0aeae8 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -3012,7 +3012,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3012 | } | 3012 | } |
3013 | else if (equal_Command(cmd, "document.input.submit") && document_Command(cmd) == d) { | 3013 | else if (equal_Command(cmd, "document.input.submit") && document_Command(cmd) == d) { |
3014 | postCommandf_Root(w->root, | 3014 | postCommandf_Root(w->root, |
3015 | "open url:%s", | 3015 | /* use the `redirect:1` argument to cause the input query URL to be |
3016 | replaced in History; we don't want to navigate onto it */ | ||
3017 | "open redirect:1 url:%s", | ||
3016 | cstrCollect_String(makeQueryUrl_DocumentWidget_ | 3018 | cstrCollect_String(makeQueryUrl_DocumentWidget_ |
3017 | (d, collect_String(suffix_Command(cmd, "value"))))); | 3019 | (d, collect_String(suffix_Command(cmd, "value"))))); |
3018 | return iTrue; | 3020 | return iTrue; |
@@ -3066,7 +3068,11 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3066 | iReleasePtr(&d->request); | 3068 | iReleasePtr(&d->request); |
3067 | updateVisible_DocumentWidget_(d); | 3069 | updateVisible_DocumentWidget_(d); |
3068 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 3070 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
3069 | postCommandf_Root(w->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 3071 | postCommandf_Root(w->root, |
3072 | "document.changed doc:%p status:%d url:%s", | ||
3073 | d, | ||
3074 | d->sourceStatus, | ||
3075 | cstr_String(d->mod.url)); | ||
3070 | /* Check for a pending goto. */ | 3076 | /* Check for a pending goto. */ |
3071 | if (!isEmpty_String(&d->pendingGotoHeading)) { | 3077 | if (!isEmpty_String(&d->pendingGotoHeading)) { |
3072 | scrollToHeading_DocumentWidget_(d, cstr_String(&d->pendingGotoHeading)); | 3078 | scrollToHeading_DocumentWidget_(d, cstr_String(&d->pendingGotoHeading)); |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index daca05d1..46bdc890 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -421,7 +421,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
421 | else { | 421 | else { |
422 | drawCenteredOutline_Text( | 422 | drawCenteredOutline_Text( |
423 | d->font, | 423 | d->font, |
424 | adjusted_Rect(bounds, add_I2(zero_I2(), init_I2(iconPad, 0)), neg_I2(zero_I2())), | 424 | adjusted_Rect(bounds, init_I2(iconPad * 1.5f, 0), init_I2(-iconPad, 0)), |
425 | d->flags.alignVisual, | 425 | d->flags.alignVisual, |
426 | d->flags.drawAsOutline ? fg : none_ColorId, | 426 | d->flags.drawAsOutline ? fg : none_ColorId, |
427 | d->flags.drawAsOutline ? d->widget.bgColor : fg, | 427 | d->flags.drawAsOutline ? d->widget.bgColor : fg, |
diff --git a/src/ui/root.c b/src/ui/root.c index 95126654..5bfae857 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
27 | #include "command.h" | 27 | #include "command.h" |
28 | #include "defs.h" | 28 | #include "defs.h" |
29 | #include "documentwidget.h" | 29 | #include "documentwidget.h" |
30 | #include "embedded.h" | 30 | #include "resources.h" |
31 | #include "inputwidget.h" | 31 | #include "inputwidget.h" |
32 | #include "keys.h" | 32 | #include "keys.h" |
33 | #include "labelwidget.h" | 33 | #include "labelwidget.h" |
@@ -78,6 +78,9 @@ static const iMenuItem navMenuItems_[] = { | |||
78 | { gear_Icon " ${menu.preferences}", SDLK_COMMA, KMOD_PRIMARY, "preferences" }, | 78 | { gear_Icon " ${menu.preferences}", SDLK_COMMA, KMOD_PRIMARY, "preferences" }, |
79 | { "${menu.help}", SDLK_F1, 0, "!open url:about:help" }, | 79 | { "${menu.help}", SDLK_F1, 0, "!open url:about:help" }, |
80 | { "${menu.releasenotes}", 0, 0, "!open url:about:version" }, | 80 | { "${menu.releasenotes}", 0, 0, "!open url:about:version" }, |
81 | #if defined (LAGRANGE_ENABLE_WINSPARKLE) | ||
82 | { "${menu.update}", 0, 0, "updater.check" }, | ||
83 | #endif | ||
81 | { "---" }, | 84 | { "---" }, |
82 | { "${menu.quit}", 'q', KMOD_PRIMARY, "quit" } | 85 | { "${menu.quit}", 'q', KMOD_PRIMARY, "quit" } |
83 | }; | 86 | }; |
@@ -757,9 +760,17 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
757 | if (equal_Command(cmd, "document.changed")) { | 760 | if (equal_Command(cmd, "document.changed")) { |
758 | iInputWidget *url = findWidget_Root("url"); | 761 | iInputWidget *url = findWidget_Root("url"); |
759 | const iString *urlStr = collect_String(suffix_Command(cmd, "url")); | 762 | const iString *urlStr = collect_String(suffix_Command(cmd, "url")); |
763 | const enum iGmStatusCode statusCode = argLabel_Command(cmd, "status"); | ||
760 | trimCache_App(); | 764 | trimCache_App(); |
761 | trimMemory_App(); | 765 | trimMemory_App(); |
762 | visitUrl_Visited(visited_App(), urlStr, 0); | 766 | visitUrl_Visited(visited_App(), |
767 | urlStr, | ||
768 | /* The transient flag modifies history navigation behavior on | ||
769 | special responses like input queries. */ | ||
770 | category_GmStatusCode(statusCode) == categoryInput_GmStatusCode || | ||
771 | category_GmStatusCode(statusCode) == categoryRedirect_GmStatusCode | ||
772 | ? transient_VisitedUrlFlag | ||
773 | : 0); | ||
763 | postCommand_App("visited.changed"); /* sidebar will update */ | 774 | postCommand_App("visited.changed"); /* sidebar will update */ |
764 | setText_InputWidget(url, urlStr); | 775 | setText_InputWidget(url, urlStr); |
765 | checkLoadAnimation_Root_(get_Root()); | 776 | checkLoadAnimation_Root_(get_Root()); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 4b6fde3c..581a7bb2 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -346,10 +346,24 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
346 | } | 346 | } |
347 | } | 347 | } |
348 | /* Actions. */ { | 348 | /* Actions. */ { |
349 | addActionButton_SidebarWidget_(d, "${sidebar.action.feeds.showall}", "feeds.mode arg:0", | 349 | addActionButton_SidebarWidget_( |
350 | d->feedsMode == all_FeedsMode ? selected_WidgetFlag : 0); | 350 | d, check_Icon " ${feeds.markallread}", "feeds.markallread", expand_WidgetFlag); |
351 | addActionButton_SidebarWidget_(d, "${sidebar.action.feeds.showunread}", "feeds.mode arg:1", | 351 | addChild_Widget(d->actions, iClob(new_LabelWidget("${sidebar.action.show}", NULL))); |
352 | d->feedsMode == unread_FeedsMode ? selected_WidgetFlag : 0); | 352 | const iMenuItem items[] = { |
353 | { "${sidebar.action.feeds.showall}", SDLK_u, KMOD_SHIFT, "feeds.mode arg:0" }, | ||
354 | { "${sidebar.action.feeds.showunread}", SDLK_u, 0, "feeds.mode arg:1" }, | ||
355 | }; | ||
356 | iWidget *dropButton = addChild_Widget( | ||
357 | d->actions, | ||
358 | iClob(makeMenuButton_LabelWidget(items[d->feedsMode].label, items, 2))); | ||
359 | setFixedSize_Widget( | ||
360 | dropButton, | ||
361 | init_I2(measure_Text( | ||
362 | default_FontId, | ||
363 | translateCStr_Lang(items[findWidestLabel_MenuItem(items, 2)].label)) | ||
364 | .advance.x + | ||
365 | 6 * gap_UI, | ||
366 | -1)); | ||
353 | } | 367 | } |
354 | d->menu = makeMenu_Widget( | 368 | d->menu = makeMenu_Widget( |
355 | as_Widget(d), | 369 | as_Widget(d), |
@@ -893,6 +907,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
893 | if (isEmpty_String(&item->url)) /* a folder */ { | 907 | if (isEmpty_String(&item->url)) /* a folder */ { |
894 | if (contains_IntSet(d->closedFolders, item->id)) { | 908 | if (contains_IntSet(d->closedFolders, item->id)) { |
895 | remove_IntSet(d->closedFolders, item->id); | 909 | remove_IntSet(d->closedFolders, item->id); |
910 | setRecentFolder_Bookmarks(bookmarks_App(), item->id); | ||
896 | } | 911 | } |
897 | else { | 912 | else { |
898 | insert_IntSet(d->closedFolders, item->id); | 913 | insert_IntSet(d->closedFolders, item->id); |
@@ -990,7 +1005,11 @@ void setWidth_SidebarWidget(iSidebarWidget *d, float widthAsGaps) { | |||
990 | } | 1005 | } |
991 | 1006 | ||
992 | iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *cmd) { | 1007 | iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *cmd) { |
993 | if (equal_Command(cmd, "bmed.accept") || equal_Command(cmd, "cancel")) { | 1008 | if (equal_Command(cmd, "dlg.bookmark.setfolder")) { |
1009 | setBookmarkEditorFolder_Widget(editor, arg_Command(cmd)); | ||
1010 | return iTrue; | ||
1011 | } | ||
1012 | if (equal_Command(cmd, "bmed.accept") || equal_Command(cmd, "bmed.cancel")) { | ||
994 | iAssert(startsWith_String(id_Widget(editor), "bmed.")); | 1013 | iAssert(startsWith_String(id_Widget(editor), "bmed.")); |
995 | iSidebarWidget *d = findWidget_App(cstr_String(id_Widget(editor)) + 5); /* bmed.sidebar */ | 1014 | iSidebarWidget *d = findWidget_App(cstr_String(id_Widget(editor)) + 5); /* bmed.sidebar */ |
996 | if (equal_Command(cmd, "bmed.accept")) { | 1015 | if (equal_Command(cmd, "bmed.accept")) { |
@@ -1020,7 +1039,11 @@ iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *c | |||
1020 | isSelected_Widget(findChild_Widget(editor, "bmed.tag.remote"))); | 1039 | isSelected_Widget(findChild_Widget(editor, "bmed.tag.remote"))); |
1021 | addOrRemoveTag_Bookmark(bm, linkSplit_BookmarkTag, | 1040 | addOrRemoveTag_Bookmark(bm, linkSplit_BookmarkTag, |
1022 | isSelected_Widget(findChild_Widget(editor, "bmed.tag.linksplit"))); | 1041 | isSelected_Widget(findChild_Widget(editor, "bmed.tag.linksplit"))); |
1023 | } | 1042 | } |
1043 | const iBookmark *folder = userData_Object(findChild_Widget(editor, "bmed.folder")); | ||
1044 | if (!folder || !hasParent_Bookmark(folder, id_Bookmark(bm))) { | ||
1045 | bm->parentId = folder ? id_Bookmark(folder) : 0; | ||
1046 | } | ||
1024 | postCommand_App("bookmarks.changed"); | 1047 | postCommand_App("bookmarks.changed"); |
1025 | } | 1048 | } |
1026 | setupSheetTransition_Mobile(editor, iFalse); | 1049 | setupSheetTransition_Mobile(editor, iFalse); |
@@ -1327,6 +1350,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1327 | setFlags_Widget(notNeeded[i], disabled_WidgetFlag, iTrue); | 1350 | setFlags_Widget(notNeeded[i], disabled_WidgetFlag, iTrue); |
1328 | } | 1351 | } |
1329 | } | 1352 | } |
1353 | setBookmarkEditorFolder_Widget(dlg, bm ? bm->parentId : 0); | ||
1330 | setCommandHandler_Widget(dlg, handleBookmarkEditorCommands_SidebarWidget_); | 1354 | setCommandHandler_Widget(dlg, handleBookmarkEditorCommands_SidebarWidget_); |
1331 | setFocus_Widget(findChild_Widget(dlg, "bmed.title")); | 1355 | setFocus_Widget(findChild_Widget(dlg, "bmed.title")); |
1332 | } | 1356 | } |
@@ -2019,7 +2043,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2019 | add_I2(topLeft_Rect(itemRect), | 2043 | add_I2(topLeft_Rect(itemRect), |
2020 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)), | 2044 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)), |
2021 | fg, | 2045 | fg, |
2022 | "%s%s%s%s%s%s", | 2046 | "%s%s%s%s%s%s%s%s", |
2023 | isGemini ? "" : cstr_Rangecc(parts.scheme), | 2047 | isGemini ? "" : cstr_Rangecc(parts.scheme), |
2024 | isGemini ? "" : isAbout ? ":" : "://", | 2048 | isGemini ? "" : isAbout ? ":" : "://", |
2025 | escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId | 2049 | escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId |
@@ -2027,7 +2051,9 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2027 | : uiTextStrong_ColorId), | 2051 | : uiTextStrong_ColorId), |
2028 | cstr_Rangecc(parts.host), | 2052 | cstr_Rangecc(parts.host), |
2029 | escape_Color(fg), | 2053 | escape_Color(fg), |
2030 | cstr_Rangecc(parts.path)); | 2054 | cstr_Rangecc(parts.path), |
2055 | !isEmpty_Range(&parts.query) ? escape_Color(uiAnnotation_ColorId) : "", | ||
2056 | !isEmpty_Range(&parts.query) ? cstr_Rangecc(parts.query) : ""); | ||
2031 | } | 2057 | } |
2032 | iEndCollect(); | 2058 | iEndCollect(); |
2033 | } | 2059 | } |
diff --git a/src/ui/text.c b/src/ui/text.c index abe8640c..91ff137a 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -23,7 +23,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
23 | #include "text.h" | 23 | #include "text.h" |
24 | #include "color.h" | 24 | #include "color.h" |
25 | #include "metrics.h" | 25 | #include "metrics.h" |
26 | #include "embedded.h" | 26 | #include "resources.h" |
27 | #include "window.h" | 27 | #include "window.h" |
28 | #include "paint.h" | 28 | #include "paint.h" |
29 | #include "app.h" | 29 | #include "app.h" |
@@ -180,21 +180,21 @@ static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *font | |||
180 | d->data = NULL; | 180 | d->data = NULL; |
181 | d->family = undefined_TextFont; | 181 | d->family = undefined_TextFont; |
182 | /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ | 182 | /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ |
183 | if (data == &fontNunitoRegular_Embedded || | 183 | if (data == &fontNunitoRegular_Resources || |
184 | data == &fontNunitoBold_Embedded || | 184 | data == &fontNunitoBold_Resources || |
185 | data == &fontNunitoExtraBold_Embedded || | 185 | data == &fontNunitoExtraBold_Resources || |
186 | //data == &fontNunitoLightItalic_Embedded || | 186 | //data == &fontNunitoLightItalic_Resources || |
187 | data == &fontNunitoExtraLight_Embedded) { | 187 | data == &fontNunitoExtraLight_Resources) { |
188 | d->family = nunito_TextFont; | 188 | d->family = nunito_TextFont; |
189 | } | 189 | } |
190 | else if (//data == &fontScheherazadeNewRegular_Embedded) { | 190 | else if (//data == &fontScheherazadeNewRegular_Resources) { |
191 | data == &fontNotoSansArabicUIRegular_Embedded) { | 191 | data == &fontNotoSansArabicUIRegular_Resources) { |
192 | d->family = arabic_TextFont; | 192 | d->family = arabic_TextFont; |
193 | } | 193 | } |
194 | else if (data == &fontNotoSansSymbolsRegular_Embedded || | 194 | else if (data == &fontNotoSansSymbolsRegular_Resources || |
195 | data == &fontNotoSansSymbols2Regular_Embedded || | 195 | data == &fontNotoSansSymbols2Regular_Resources || |
196 | data == &fontNotoEmojiRegular_Embedded || | 196 | data == &fontNotoEmojiRegular_Resources || |
197 | data == &fontSmolEmojiRegular_Embedded) { | 197 | data == &fontSmolEmojiRegular_Resources) { |
198 | d->family = emojiAndSymbols_TextFont; | 198 | d->family = emojiAndSymbols_TextFont; |
199 | } | 199 | } |
200 | #endif | 200 | #endif |
@@ -263,7 +263,7 @@ struct Impl_Text { | |||
263 | iRegExp * ansiEscape; | 263 | iRegExp * ansiEscape; |
264 | int ansiFlags; | 264 | int ansiFlags; |
265 | int baseFontId; /* base attributes (for restoring via escapes) */ | 265 | int baseFontId; /* base attributes (for restoring via escapes) */ |
266 | int baseColorId; | 266 | int baseFgColorId; |
267 | iBool missingGlyphs; /* true if a glyph couldn't be found */ | 267 | iBool missingGlyphs; /* true if a glyph couldn't be found */ |
268 | }; | 268 | }; |
269 | 269 | ||
@@ -390,35 +390,14 @@ static void deinitCache_Text_(iText *d) { | |||
390 | SDL_DestroyTexture(d->cache); | 390 | SDL_DestroyTexture(d->cache); |
391 | } | 391 | } |
392 | 392 | ||
393 | #if 0 | ||
394 | void loadUserFonts_Text(void) { | ||
395 | if (userFont_) { | ||
396 | delete_Block(userFont_); | ||
397 | userFont_ = NULL; | ||
398 | } | ||
399 | /* Load the system font. */ | ||
400 | const iPrefs *prefs = prefs_App(); | ||
401 | if (!isEmpty_String(&prefs->symbolFontPath)) { | ||
402 | iFile *f = new_File(&prefs->symbolFontPath); | ||
403 | if (open_File(f, readOnly_FileMode)) { | ||
404 | userFont_ = readAll_File(f); | ||
405 | } | ||
406 | else { | ||
407 | fprintf(stderr, "[Text] failed to open: %s\n", cstr_String(&prefs->symbolFontPath)); | ||
408 | } | ||
409 | iRelease(f); | ||
410 | } | ||
411 | } | ||
412 | #endif | ||
413 | |||
414 | void init_Text(iText *d, SDL_Renderer *render) { | 393 | void init_Text(iText *d, SDL_Renderer *render) { |
415 | iText *oldActive = activeText_; | 394 | iText *oldActive = activeText_; |
416 | activeText_ = d; | 395 | activeText_ = d; |
417 | init_Array(&d->fonts, sizeof(iFont)); | 396 | init_Array(&d->fonts, sizeof(iFont)); |
418 | d->contentFontSize = contentScale_Text_; | 397 | d->contentFontSize = contentScale_Text_; |
419 | d->ansiEscape = new_RegExp("[[()]([0-9;AB]*?)m", 0); | 398 | d->ansiEscape = new_RegExp("[[()][?]?([0-9;AB]*?)([ABCDEFGHJKSTfhilmn])", 0); |
420 | d->baseFontId = -1; | 399 | d->baseFontId = -1; |
421 | d->baseColorId = -1; | 400 | d->baseFgColorId = -1; |
422 | d->render = render; | 401 | d->render = render; |
423 | /* A grayscale palette for rasterized glyphs. */ { | 402 | /* A grayscale palette for rasterized glyphs. */ { |
424 | SDL_Color colors[256]; | 403 | SDL_Color colors[256]; |
@@ -459,9 +438,10 @@ void setOpacity_Text(float opacity) { | |||
459 | SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f); | 438 | SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f); |
460 | } | 439 | } |
461 | 440 | ||
462 | void setBaseAttributes_Text(int fontId, int colorId) { | 441 | void setBaseAttributes_Text(int fontId, int fgColorId) { |
463 | activeText_->baseFontId = fontId; | 442 | iText *d = activeText_; |
464 | activeText_->baseColorId = colorId; | 443 | d->baseFontId = fontId; |
444 | d->baseFgColorId = fgColorId; | ||
465 | } | 445 | } |
466 | 446 | ||
467 | void setAnsiFlags_Text(int ansiFlags) { | 447 | void setAnsiFlags_Text(int ansiFlags) { |
@@ -574,16 +554,6 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
574 | return overrideFont; | 554 | return overrideFont; |
575 | } | 555 | } |
576 | } | 556 | } |
577 | #if 0 | ||
578 | /* TODO: Put arrows in Smol Emoji. */ | ||
579 | /* Manual exceptions. */ { | ||
580 | if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { | ||
581 | d = font_Text_(iosevka_FontId + d->sizeId); | ||
582 | *glyphIndex = glyphIndex_Font_(d, ch); | ||
583 | return d; | ||
584 | } | ||
585 | } | ||
586 | #endif | ||
587 | /* The font's own version of the glyph. */ | 557 | /* The font's own version of the glyph. */ |
588 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { | 558 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { |
589 | return d; | 559 | return d; |
@@ -609,69 +579,6 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
609 | } | 579 | } |
610 | } | 580 | } |
611 | } | 581 | } |
612 | #if 0 | ||
613 | const int fallbacks[] = { | ||
614 | notoEmoji_FontId, | ||
615 | symbols2_FontId, | ||
616 | symbols_FontId | ||
617 | }; | ||
618 | /* First fallback is Smol Emoji. */ | ||
619 | if (ch != 0x20) { | ||
620 | iForIndices(i, fallbacks) { | ||
621 | iFont *fallback = font_Text_(fallbacks[i] + d->sizeId); | ||
622 | if (fallback != d && (*glyphIndex = glyphIndex_Font_(fallback, ch)) != 0) { | ||
623 | return fallback; | ||
624 | } | ||
625 | } | ||
626 | } | ||
627 | /* Try Simplified Chinese. */ | ||
628 | if (ch >= 0x2e80) { | ||
629 | iFont *sc = font_Text_(chineseSimplified_FontId + d->sizeId); | ||
630 | if (sc != d && (*glyphIndex = glyphIndex_Font_(sc, ch)) != 0) { | ||
631 | return sc; | ||
632 | } | ||
633 | } | ||
634 | /* Could be Korean. */ | ||
635 | if (ch >= 0x3000) { | ||
636 | iFont *korean = font_Text_(korean_FontId + d->sizeId); | ||
637 | if (korean != d && (*glyphIndex = glyphIndex_Font_(korean, ch)) != 0) { | ||
638 | return korean; | ||
639 | } | ||
640 | } | ||
641 | /* Japanese perhaps? */ | ||
642 | if (ch > 0x3040) { | ||
643 | iFont *japanese = font_Text_(japanese_FontId + d->sizeId); | ||
644 | if (japanese != d && (*glyphIndex = glyphIndex_Font_(japanese, ch)) != 0) { | ||
645 | return japanese; | ||
646 | } | ||
647 | } | ||
648 | /* Maybe Arabic. */ | ||
649 | if (ch >= 0x600) { | ||
650 | iFont *arabic = font_Text_(arabic_FontId + d->sizeId); | ||
651 | if (arabic != d && (*glyphIndex = glyphIndex_Font_(arabic, ch)) != 0) { | ||
652 | return arabic; | ||
653 | } | ||
654 | } | ||
655 | #if defined (iPlatformApple) | ||
656 | /* White up arrow is used for the Shift key on macOS. Symbola's glyph is not a great | ||
657 | match to the other text, so use the UI font instead. */ | ||
658 | if ((ch == 0x2318 || ch == 0x21e7) && d == font_Text_(regular_FontId)) { | ||
659 | *glyphIndex = glyphIndex_Font_(d = font_Text_(defaultContentRegular_FontId), ch); | ||
660 | return d; | ||
661 | } | ||
662 | #endif | ||
663 | /* User's symbols font. */ { | ||
664 | iFont *sys = font_Text_(userSymbols_FontId + d->sizeId); | ||
665 | if (sys != d && (*glyphIndex = glyphIndex_Font_(sys, ch)) != 0) { | ||
666 | return sys; | ||
667 | } | ||
668 | } | ||
669 | /* Final fallback. */ | ||
670 | iFont *font = font_Text_(iosevka_FontId + d->sizeId); | ||
671 | if (d != font) { | ||
672 | *glyphIndex = glyphIndex_Font_(font, ch); | ||
673 | } | ||
674 | #endif // 0 | ||
675 | if (!*glyphIndex) { | 582 | if (!*glyphIndex) { |
676 | activeText_->missingGlyphs = iTrue; | 583 | activeText_->missingGlyphs = iTrue; |
677 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 584 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); |
@@ -680,7 +587,9 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
680 | } | 587 | } |
681 | 588 | ||
682 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { | 589 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { |
683 | iAssert(d->table); | 590 | if (!d->table) { |
591 | d->table = new_GlyphTable(); | ||
592 | } | ||
684 | iGlyph* glyph = NULL; | 593 | iGlyph* glyph = NULL; |
685 | void * node = value_Hash(&d->table->glyphs, glyphIndex); | 594 | void * node = value_Hash(&d->table->glyphs, glyphIndex); |
686 | if (node) { | 595 | if (node) { |
@@ -739,6 +648,7 @@ struct Impl_AttributedRun { | |||
739 | iTextAttrib attrib; | 648 | iTextAttrib attrib; |
740 | iFont *font; | 649 | iFont *font; |
741 | iColor fgColor_; /* any RGB color; A > 0 */ | 650 | iColor fgColor_; /* any RGB color; A > 0 */ |
651 | iColor bgColor_; /* any RGB color; A > 0 */ | ||
742 | struct { | 652 | struct { |
743 | uint8_t isLineBreak : 1; | 653 | uint8_t isLineBreak : 1; |
744 | // uint8_t isRTL : 1; | 654 | // uint8_t isRTL : 1; |
@@ -750,29 +660,45 @@ static iColor fgColor_AttributedRun_(const iAttributedRun *d) { | |||
750 | if (d->fgColor_.a) { | 660 | if (d->fgColor_.a) { |
751 | return d->fgColor_; | 661 | return d->fgColor_; |
752 | } | 662 | } |
753 | if (d->attrib.colorId == none_ColorId) { | 663 | if (d->attrib.fgColorId == none_ColorId) { |
754 | return (iColor){ 255, 255, 255, 255 }; | 664 | return (iColor){ 255, 255, 255, 255 }; |
755 | } | 665 | } |
756 | return get_Color(d->attrib.colorId); | 666 | return get_Color(d->attrib.fgColorId); |
667 | } | ||
668 | |||
669 | static iColor bgColor_AttributedRun_(const iAttributedRun *d) { | ||
670 | if (d->bgColor_.a) { | ||
671 | return d->bgColor_; | ||
672 | } | ||
673 | return (iColor){ 255, 255, 255, 0 }; | ||
674 | if (d->attrib.bgColorId == none_ColorId) { | ||
675 | return (iColor){ 255, 255, 255, 0 }; | ||
676 | } | ||
677 | return get_Color(d->attrib.bgColorId); | ||
757 | } | 678 | } |
758 | 679 | ||
759 | static void setFgColor_AttributedRun_(iAttributedRun *d, int colorId) { | 680 | static void setFgColor_AttributedRun_(iAttributedRun *d, int colorId) { |
760 | d->attrib.colorId = colorId; | 681 | d->attrib.fgColorId = colorId; |
761 | d->fgColor_.a = 0; | 682 | d->fgColor_.a = 0; |
762 | } | 683 | } |
763 | 684 | ||
685 | static void setBgColor_AttributedRun_(iAttributedRun *d, int colorId) { | ||
686 | d->attrib.bgColorId = colorId; | ||
687 | d->bgColor_.a = 0; | ||
688 | } | ||
689 | |||
764 | iDeclareType(AttributedText) | 690 | iDeclareType(AttributedText) |
765 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, | 691 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, |
766 | int colorId, int baseDir, iFont *baseFont, int baseColorId, | 692 | int colorId, int baseDir, iFont *baseFont, int baseFgColorId, |
767 | iChar overrideChar) | 693 | iChar overrideChar) |
768 | 694 | ||
769 | struct Impl_AttributedText { | 695 | struct Impl_AttributedText { |
770 | iRangecc source; /* original source text */ | 696 | iRangecc source; /* original source text */ |
771 | size_t maxLen; | 697 | size_t maxLen; |
772 | iFont * font; | 698 | iFont * font; |
773 | int colorId; | 699 | int fgColorId; |
774 | iFont * baseFont; | 700 | iFont * baseFont; |
775 | int baseColorId; | 701 | int baseFgColorId; |
776 | iBool isBaseRTL; | 702 | iBool isBaseRTL; |
777 | iArray runs; | 703 | iArray runs; |
778 | iArray logical; /* UTF-32 text in logical order (mixed directions; matches source) */ | 704 | iArray logical; /* UTF-32 text in logical order (mixed directions; matches source) */ |
@@ -785,9 +711,9 @@ struct Impl_AttributedText { | |||
785 | 711 | ||
786 | iDefineTypeConstructionArgs(AttributedText, | 712 | iDefineTypeConstructionArgs(AttributedText, |
787 | (iRangecc text, size_t maxLen, iFont *font, int colorId, | 713 | (iRangecc text, size_t maxLen, iFont *font, int colorId, |
788 | int baseDir, iFont *baseFont, int baseColorId, | 714 | int baseDir, iFont *baseFont, int baseFgColorId, |
789 | iChar overrideChar), | 715 | iChar overrideChar), |
790 | text, maxLen, font, colorId, baseDir, baseFont, baseColorId, | 716 | text, maxLen, font, colorId, baseDir, baseFont, baseFgColorId, |
791 | overrideChar) | 717 | overrideChar) |
792 | 718 | ||
793 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { | 719 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { |
@@ -897,7 +823,8 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
897 | } | 823 | } |
898 | iAttributedRun run = { | 824 | iAttributedRun run = { |
899 | .logical = { 0, length }, | 825 | .logical = { 0, length }, |
900 | .attrib = { .colorId = d->colorId, .isBaseRTL = d->isBaseRTL }, | 826 | .attrib = { .fgColorId = d->fgColorId, .bgColorId = none_ColorId, |
827 | .isBaseRTL = d->isBaseRTL }, | ||
901 | .font = d->font, | 828 | .font = d->font, |
902 | }; | 829 | }; |
903 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); | 830 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); |
@@ -935,12 +862,13 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
935 | if (match_RegExp(activeText_->ansiEscape, srcPos, d->source.end - srcPos, &m)) { | 862 | if (match_RegExp(activeText_->ansiEscape, srcPos, d->source.end - srcPos, &m)) { |
936 | finishRun_AttributedText_(d, &run, pos - 1); | 863 | finishRun_AttributedText_(d, &run, pos - 1); |
937 | const int ansi = activeText_->ansiFlags; | 864 | const int ansi = activeText_->ansiFlags; |
938 | if (ansi) { | 865 | if (ansi && capturedRange_RegExpMatch(&m, 2).start[0] == |
866 | 'm' /* Select Graphic Rendition */) { | ||
939 | const iRangecc sequence = capturedRange_RegExpMatch(&m, 1); | 867 | const iRangecc sequence = capturedRange_RegExpMatch(&m, 1); |
940 | /* Note: This styling is hardcoded to match `typesetOneLine_RunTypesetter_()`. */ | 868 | /* Note: This styling is hardcoded to match `typesetOneLine_RunTypesetter_()`. */ |
941 | if (ansi & allowFontStyle_AnsiFlag && equal_Rangecc(sequence, "1")) { | 869 | if (ansi & allowFontStyle_AnsiFlag && equal_Rangecc(sequence, "1")) { |
942 | run.attrib.bold = iTrue; | 870 | run.attrib.bold = iTrue; |
943 | if (d->baseColorId == tmParagraph_ColorId) { | 871 | if (d->baseFgColorId == tmParagraph_ColorId) { |
944 | setFgColor_AttributedRun_(&run, tmFirstParagraph_ColorId); | 872 | setFgColor_AttributedRun_(&run, tmFirstParagraph_ColorId); |
945 | } | 873 | } |
946 | attribFont = font_Text_(fontWithStyle_Text(fontId_Text_(d->baseFont), | 874 | attribFont = font_Text_(fontWithStyle_Text(fontId_Text_(d->baseFont), |
@@ -962,10 +890,13 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
962 | run.attrib.italic = iFalse; | 890 | run.attrib.italic = iFalse; |
963 | run.attrib.monospace = iFalse; | 891 | run.attrib.monospace = iFalse; |
964 | attribFont = run.font = d->baseFont; | 892 | attribFont = run.font = d->baseFont; |
965 | setFgColor_AttributedRun_(&run, d->baseColorId); | 893 | setFgColor_AttributedRun_(&run, d->baseFgColorId); |
894 | setBgColor_AttributedRun_(&run, none_ColorId); | ||
966 | } | 895 | } |
967 | else if (ansi & allowFg_AnsiFlag) { | 896 | else { |
968 | run.fgColor_ = ansiForeground_Color(sequence, tmParagraph_ColorId); | 897 | ansiColors_Color(sequence, d->baseFgColorId, none_ColorId, |
898 | ansi & allowFg_AnsiFlag ? &run.fgColor_ : NULL, | ||
899 | ansi & allowBg_AnsiFlag ? &run.bgColor_ : NULL); | ||
969 | } | 900 | } |
970 | } | 901 | } |
971 | pos += length_Rangecc(capturedRange_RegExpMatch(&m, 0)); | 902 | pos += length_Rangecc(capturedRange_RegExpMatch(&m, 0)); |
@@ -988,7 +919,7 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
988 | colorNum = esc - asciiBase_ColorEscape; | 919 | colorNum = esc - asciiBase_ColorEscape; |
989 | } | 920 | } |
990 | run.logical.start = pos + 1; | 921 | run.logical.start = pos + 1; |
991 | setFgColor_AttributedRun_(&run, colorNum >= 0 ? colorNum : d->colorId); | 922 | setFgColor_AttributedRun_(&run, colorNum >= 0 ? colorNum : d->fgColorId); |
992 | continue; | 923 | continue; |
993 | } | 924 | } |
994 | if (ch == '\n') { | 925 | if (ch == '\n') { |
@@ -1051,14 +982,15 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
1051 | } | 982 | } |
1052 | 983 | ||
1053 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, int colorId, | 984 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, int colorId, |
1054 | int baseDir, iFont *baseFont, int baseColorId, iChar overrideChar) { | 985 | int baseDir, iFont *baseFont, int baseFgColorId, |
1055 | d->source = text; | 986 | iChar overrideChar) { |
1056 | d->maxLen = maxLen ? maxLen : iInvalidSize; | 987 | d->source = text; |
1057 | d->font = font; | 988 | d->maxLen = maxLen ? maxLen : iInvalidSize; |
1058 | d->colorId = colorId; | 989 | d->font = font; |
1059 | d->baseFont = baseFont; | 990 | d->fgColorId = colorId; |
1060 | d->baseColorId = baseColorId; | 991 | d->baseFont = baseFont; |
1061 | d->isBaseRTL = iFalse; | 992 | d->baseFgColorId = baseFgColorId; |
993 | d->isBaseRTL = iFalse; | ||
1062 | init_Array(&d->runs, sizeof(iAttributedRun)); | 994 | init_Array(&d->runs, sizeof(iAttributedRun)); |
1063 | init_Array(&d->logical, sizeof(iChar)); | 995 | init_Array(&d->logical, sizeof(iChar)); |
1064 | init_Array(&d->visual, sizeof(iChar)); | 996 | init_Array(&d->visual, sizeof(iChar)); |
@@ -1412,7 +1344,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1412 | init_AttributedText(&attrText, args->text, args->maxLen, d, args->color, | 1344 | init_AttributedText(&attrText, args->text, args->maxLen, d, args->color, |
1413 | args->baseDir, | 1345 | args->baseDir, |
1414 | activeText_->baseFontId >= 0 ? font_Text_(activeText_->baseFontId) : d, | 1346 | activeText_->baseFontId >= 0 ? font_Text_(activeText_->baseFontId) : d, |
1415 | activeText_->baseColorId, | 1347 | activeText_->baseFgColorId, |
1416 | wrap ? wrap->overrideChar : 0); | 1348 | wrap ? wrap->overrideChar : 0); |
1417 | if (wrap) { | 1349 | if (wrap) { |
1418 | wrap->baseDir = attrText.isBaseRTL ? -1 : +1; | 1350 | wrap->baseDir = attrText.isBaseRTL ? -1 : +1; |
@@ -1464,7 +1396,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1464 | iRangei wrapPosRange = { 0, textLen }; | 1396 | iRangei wrapPosRange = { 0, textLen }; |
1465 | int wrapResumePos = textLen; /* logical position where next line resumes */ | 1397 | int wrapResumePos = textLen; /* logical position where next line resumes */ |
1466 | size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */ | 1398 | size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */ |
1467 | iTextAttrib attrib = { .colorId = args->color, .isBaseRTL = attrText.isBaseRTL }; | 1399 | iTextAttrib attrib = { .fgColorId = args->color, |
1400 | .bgColorId = none_ColorId, | ||
1401 | .isBaseRTL = attrText.isBaseRTL }; | ||
1468 | iTextAttrib wrapAttrib = attrib; | 1402 | iTextAttrib wrapAttrib = attrib; |
1469 | iTextAttrib lastAttrib = attrib; | 1403 | iTextAttrib lastAttrib = attrib; |
1470 | const int layoutBound = (wrap ? wrap->maxWidth : 0); | 1404 | const int layoutBound = (wrap ? wrap->maxWidth : 0); |
@@ -1712,6 +1646,12 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1712 | yCursor += d->height; | 1646 | yCursor += d->height; |
1713 | continue; | 1647 | continue; |
1714 | } | 1648 | } |
1649 | const iColor fgClr = fgColor_AttributedRun_(run); | ||
1650 | const iColor bgClr = bgColor_AttributedRun_(run); | ||
1651 | iBool isBgFilled = iFalse; | ||
1652 | if (~mode & permanentColorFlag_RunMode) { | ||
1653 | isBgFilled = (bgClr.a != 0) || (mode & fillBackground_RunMode); | ||
1654 | } | ||
1715 | iGlyphBuffer *buf = at_Array(&buffers, runIndex); | 1655 | iGlyphBuffer *buf = at_Array(&buffers, runIndex); |
1716 | shape_GlyphBuffer_(buf); | 1656 | shape_GlyphBuffer_(buf); |
1717 | iAssert(run->font == buf->font); | 1657 | iAssert(run->font == buf->font); |
@@ -1772,34 +1712,46 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1772 | bounds.size.x = iMax(bounds.size.x, dst.x + dst.w - orig.x); | 1712 | bounds.size.x = iMax(bounds.size.x, dst.x + dst.w - orig.x); |
1773 | bounds.size.y = iMax(bounds.size.y, yCursor + glyph->font->height); | 1713 | bounds.size.y = iMax(bounds.size.y, yCursor + glyph->font->height); |
1774 | } | 1714 | } |
1775 | if (mode & draw_RunMode && logicalText[logPos] > 0x20) { | 1715 | const iBool isSpace = (logicalText[logPos] == 0x20); |
1716 | if (mode & draw_RunMode && (isBgFilled || !isSpace)) { | ||
1776 | /* Draw the glyph. */ | 1717 | /* Draw the glyph. */ |
1777 | if (!isRasterized_Glyph_(glyph, hoff)) { | 1718 | if (!isSpace && !isRasterized_Glyph_(glyph, hoff)) { |
1778 | cacheSingleGlyph_Font_(run->font, glyphId); /* may cause cache reset */ | 1719 | cacheSingleGlyph_Font_(run->font, glyphId); /* may cause cache reset */ |
1779 | glyph = glyphByIndex_Font_(run->font, glyphId); | 1720 | glyph = glyphByIndex_Font_(run->font, glyphId); |
1780 | iAssert(isRasterized_Glyph_(glyph, hoff)); | 1721 | iAssert(isRasterized_Glyph_(glyph, hoff)); |
1781 | } | 1722 | } |
1782 | if (~mode & permanentColorFlag_RunMode) { | 1723 | if (~mode & permanentColorFlag_RunMode) { |
1783 | const iColor clr = fgColor_AttributedRun_(run); | 1724 | SDL_SetTextureColorMod(activeText_->cache, fgClr.r, fgClr.g, fgClr.b); |
1784 | SDL_SetTextureColorMod(activeText_->cache, clr.r, clr.g, clr.b); | ||
1785 | if (args->mode & fillBackground_RunMode) { | ||
1786 | SDL_SetRenderDrawColor(activeText_->render, clr.r, clr.g, clr.b, 0); | ||
1787 | } | ||
1788 | } | 1725 | } |
1789 | SDL_Rect src; | ||
1790 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); | ||
1791 | dst.x += origin_Paint.x; | 1726 | dst.x += origin_Paint.x; |
1792 | dst.y += origin_Paint.y; | 1727 | dst.y += origin_Paint.y; |
1793 | if (args->mode & fillBackground_RunMode) { | 1728 | if (isBgFilled) { |
1794 | /* Alpha blending looks much better if the RGB components don't change in | ||
1795 | the partially transparent pixels. */ | ||
1796 | /* TODO: Backgrounds of all glyphs should be cleared before drawing anything else. */ | 1729 | /* TODO: Backgrounds of all glyphs should be cleared before drawing anything else. */ |
1797 | SDL_RenderFillRect(activeText_->render, &dst); | 1730 | if (bgClr.a) { |
1731 | SDL_SetRenderDrawColor(activeText_->render, bgClr.r, bgClr.g, bgClr.b, 255); | ||
1732 | const SDL_Rect bgRect = { | ||
1733 | origin_Paint.x + orig.x + xCursor, | ||
1734 | origin_Paint.y + orig.y + yCursor, | ||
1735 | xAdvance, | ||
1736 | d->height, | ||
1737 | }; | ||
1738 | SDL_RenderFillRect(activeText_->render, &bgRect); | ||
1739 | } | ||
1740 | else if (args->mode & fillBackground_RunMode) { | ||
1741 | /* Alpha blending looks much better if the RGB components don't change | ||
1742 | in the partially transparent pixels. */ | ||
1743 | SDL_SetRenderDrawColor(activeText_->render, fgClr.r, fgClr.g, fgClr.b, 0); | ||
1744 | SDL_RenderFillRect(activeText_->render, &dst); | ||
1745 | } | ||
1746 | } | ||
1747 | if (!isSpace) { | ||
1748 | SDL_Rect src; | ||
1749 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); | ||
1750 | SDL_RenderCopy(activeText_->render, activeText_->cache, &src, &dst); | ||
1798 | } | 1751 | } |
1799 | SDL_RenderCopy(activeText_->render, activeText_->cache, &src, &dst); | ||
1800 | #if 0 | 1752 | #if 0 |
1801 | /* Show spaces and direction. */ | 1753 | /* Show spaces and direction. */ |
1802 | if (logicalText[logPos] == 0x20) { | 1754 | if (isSpace) { |
1803 | const iColor debug = get_Color(run->flags.isRTL ? yellow_ColorId : red_ColorId); | 1755 | const iColor debug = get_Color(run->flags.isRTL ? yellow_ColorId : red_ColorId); |
1804 | SDL_SetRenderDrawColor(text_.render, debug.r, debug.g, debug.b, 255); | 1756 | SDL_SetRenderDrawColor(text_.render, debug.r, debug.g, debug.b, 255); |
1805 | dst.w = xAdvance; | 1757 | dst.w = xAdvance; |
diff --git a/src/ui/text.h b/src/ui/text.h index 13a636d4..de76ed09 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -157,7 +157,7 @@ enum iAnsiFlag { | |||
157 | }; | 157 | }; |
158 | 158 | ||
159 | void setOpacity_Text (float opacity); | 159 | void setOpacity_Text (float opacity); |
160 | void setBaseAttributes_Text (int fontId, int colorId); /* current "normal" text attributes */ | 160 | void setBaseAttributes_Text (int fontId, int fgColorId); /* current "normal" text attributes */ |
161 | void setAnsiFlags_Text (int ansiFlags); | 161 | void setAnsiFlags_Text (int ansiFlags); |
162 | 162 | ||
163 | void cache_Text (int fontId, iRangecc text); /* pre-render glyphs */ | 163 | void cache_Text (int fontId, iRangecc text); /* pre-render glyphs */ |
@@ -187,7 +187,8 @@ iDeclareType(TextAttrib) | |||
187 | /* Initial attributes at the start of a text string. These may be modified by control | 187 | /* Initial attributes at the start of a text string. These may be modified by control |
188 | sequences inside a text run. */ | 188 | sequences inside a text run. */ |
189 | struct Impl_TextAttrib { | 189 | struct Impl_TextAttrib { |
190 | int16_t colorId; | 190 | int16_t fgColorId; |
191 | int16_t bgColorId; | ||
191 | struct { | 192 | struct { |
192 | uint16_t bold : 1; | 193 | uint16_t bold : 1; |
193 | uint16_t italic : 1; | 194 | uint16_t italic : 1; |
diff --git a/src/ui/util.c b/src/ui/util.c index 0a9dde0c..e718631d 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -2181,6 +2181,14 @@ size_t findWidestLabel_MenuItem(const iMenuItem *items, size_t num) { | |||
2181 | return widestPos; | 2181 | return widestPos; |
2182 | } | 2182 | } |
2183 | 2183 | ||
2184 | const char *widestLabel_MenuItemArray(const iArray *items) { | ||
2185 | size_t index = findWidestLabel_MenuItem(constData_Array(items), size_Array(items)); | ||
2186 | if (index == iInvalidPos) { | ||
2187 | return ""; | ||
2188 | } | ||
2189 | return constValue_Array(items, index, iMenuItem).label; | ||
2190 | } | ||
2191 | |||
2184 | iChar removeIconPrefix_String(iString *d) { | 2192 | iChar removeIconPrefix_String(iString *d) { |
2185 | if (isEmpty_String(d)) { | 2193 | if (isEmpty_String(d)) { |
2186 | return 0; | 2194 | return 0; |
@@ -2772,9 +2780,38 @@ iWidget *makePreferences_Widget(void) { | |||
2772 | return dlg; | 2780 | return dlg; |
2773 | } | 2781 | } |
2774 | 2782 | ||
2783 | static iBool isBookmarkFolder_(void *context, const iBookmark *bm) { | ||
2784 | iUnused(context); | ||
2785 | return isFolder_Bookmark(bm); | ||
2786 | } | ||
2787 | |||
2788 | static const iArray *makeBookmarkFolderItems_(void) { | ||
2789 | iArray *folders = new_Array(sizeof(iMenuItem)); | ||
2790 | pushBack_Array(folders, &(iMenuItem){ "\u2014", 0, 0, "dlg.bookmark.setfolder arg:0" }); | ||
2791 | iConstForEach( | ||
2792 | PtrArray, | ||
2793 | i, | ||
2794 | list_Bookmarks(bookmarks_App(), cmpTree_Bookmark, isBookmarkFolder_, NULL)) { | ||
2795 | const iBookmark *bm = i.ptr; | ||
2796 | iString *title = collect_String(copy_String(&bm->title)); | ||
2797 | for (const iBookmark *j = bm; j && j->parentId; ) { | ||
2798 | j = get_Bookmarks(bookmarks_App(), j->parentId); | ||
2799 | prependCStr_String(title, " > "); | ||
2800 | prepend_String(title, &j->title); | ||
2801 | } | ||
2802 | pushBack_Array( | ||
2803 | folders, | ||
2804 | &(iMenuItem){ cstr_String(title), | ||
2805 | 0, | ||
2806 | 0, | ||
2807 | format_CStr("dlg.bookmark.setfolder arg:%u", id_Bookmark(bm)) }); | ||
2808 | } | ||
2809 | return collect_Array(folders); | ||
2810 | } | ||
2811 | |||
2775 | iWidget *makeBookmarkEditor_Widget(void) { | 2812 | iWidget *makeBookmarkEditor_Widget(void) { |
2776 | const iMenuItem actions[] = { | 2813 | const iMenuItem actions[] = { |
2777 | { "${cancel}" }, | 2814 | { "${cancel}", 0, 0, "bmed.cancel" }, |
2778 | { uiTextCaution_ColorEscape "${dlg.bookmark.save}", SDLK_RETURN, KMOD_PRIMARY, "bmed.accept" } | 2815 | { uiTextCaution_ColorEscape "${dlg.bookmark.save}", SDLK_RETURN, KMOD_PRIMARY, "bmed.accept" } |
2779 | }; | 2816 | }; |
2780 | if (isUsingPanelLayout_Mobile()) { | 2817 | if (isUsingPanelLayout_Mobile()) { |
@@ -2808,6 +2845,19 @@ iWidget *makeBookmarkEditor_Widget(void) { | |||
2808 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.title}", "bmed.title", iClob(inputs[0] = new_InputWidget(0))); | 2845 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.title}", "bmed.title", iClob(inputs[0] = new_InputWidget(0))); |
2809 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.url}", "bmed.url", iClob(inputs[1] = new_InputWidget(0))); | 2846 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.url}", "bmed.url", iClob(inputs[1] = new_InputWidget(0))); |
2810 | setUrlContent_InputWidget(inputs[1], iTrue); | 2847 | setUrlContent_InputWidget(inputs[1], iTrue); |
2848 | /* Folder to add to. */ { | ||
2849 | addChild_Widget(headings, iClob(makeHeading_Widget("${dlg.bookmark.folder}"))); | ||
2850 | const iArray *folderItems = makeBookmarkFolderItems_(); | ||
2851 | iLabelWidget *folderButton; | ||
2852 | setId_Widget(addChildFlags_Widget(values, | ||
2853 | iClob(folderButton = makeMenuButton_LabelWidget( | ||
2854 | widestLabel_MenuItemArray(folderItems), | ||
2855 | constData_Array(folderItems), | ||
2856 | size_Array(folderItems))), alignLeft_WidgetFlag), | ||
2857 | "bmed.folder"); | ||
2858 | updateDropdownSelection_LabelWidget( | ||
2859 | folderButton, format_CStr(" arg:%u", recentFolder_Bookmarks(bookmarks_App()))); | ||
2860 | } | ||
2811 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.tags}", "bmed.tags", iClob(inputs[2] = new_InputWidget(0))); | 2861 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.tags}", "bmed.tags", iClob(inputs[2] = new_InputWidget(0))); |
2812 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.icon}", "bmed.icon", iClob(inputs[3] = new_InputWidget(1))); | 2862 | addDialogInputWithHeading_(headings, values, "${dlg.bookmark.icon}", "bmed.icon", iClob(inputs[3] = new_InputWidget(1))); |
2813 | /* Buttons for special tags. */ | 2863 | /* Buttons for special tags. */ |
@@ -2830,12 +2880,23 @@ iWidget *makeBookmarkEditor_Widget(void) { | |||
2830 | return dlg; | 2880 | return dlg; |
2831 | } | 2881 | } |
2832 | 2882 | ||
2883 | void setBookmarkEditorFolder_Widget(iWidget *editor, uint32_t folderId) { | ||
2884 | iLabelWidget *button = findChild_Widget(editor, "bmed.folder"); | ||
2885 | updateDropdownSelection_LabelWidget(button, format_CStr(" arg:%u", folderId)); | ||
2886 | setUserData_Object(button, get_Bookmarks(bookmarks_App(), folderId)); | ||
2887 | } | ||
2888 | |||
2833 | static iBool handleBookmarkCreationCommands_SidebarWidget_(iWidget *editor, const char *cmd) { | 2889 | static iBool handleBookmarkCreationCommands_SidebarWidget_(iWidget *editor, const char *cmd) { |
2834 | if (equal_Command(cmd, "bmed.accept") || equal_Command(cmd, "cancel")) { | 2890 | if (equal_Command(cmd, "dlg.bookmark.setfolder")) { |
2891 | setBookmarkEditorFolder_Widget(editor, arg_Command(cmd)); | ||
2892 | return iTrue; | ||
2893 | } | ||
2894 | if (equal_Command(cmd, "bmed.accept") || equal_Command(cmd, "bmed.cancel")) { | ||
2835 | if (equal_Command(cmd, "bmed.accept")) { | 2895 | if (equal_Command(cmd, "bmed.accept")) { |
2836 | const iString *title = text_InputWidget(findChild_Widget(editor, "bmed.title")); | 2896 | const iString *title = text_InputWidget(findChild_Widget(editor, "bmed.title")); |
2837 | const iString *url = text_InputWidget(findChild_Widget(editor, "bmed.url")); | 2897 | const iString *url = text_InputWidget(findChild_Widget(editor, "bmed.url")); |
2838 | const iString *tags = text_InputWidget(findChild_Widget(editor, "bmed.tags")); | 2898 | const iString *tags = text_InputWidget(findChild_Widget(editor, "bmed.tags")); |
2899 | const iBookmark *folder = userData_Object(findChild_Widget(editor, "bmed.folder")); | ||
2839 | const iString *icon = collect_String(trimmed_String(text_InputWidget(findChild_Widget(editor, "bmed.icon")))); | 2900 | const iString *icon = collect_String(trimmed_String(text_InputWidget(findChild_Widget(editor, "bmed.icon")))); |
2840 | const uint32_t id = add_Bookmarks(bookmarks_App(), url, title, tags, first_String(icon)); | 2901 | const uint32_t id = add_Bookmarks(bookmarks_App(), url, title, tags, first_String(icon)); |
2841 | iBookmark * bm = get_Bookmarks(bookmarks_App(), id); | 2902 | iBookmark * bm = get_Bookmarks(bookmarks_App(), id); |
@@ -2851,6 +2912,10 @@ static iBool handleBookmarkCreationCommands_SidebarWidget_(iWidget *editor, cons | |||
2851 | if (isSelected_Widget(findChild_Widget(editor, "bmed.tag.linksplit"))) { | 2912 | if (isSelected_Widget(findChild_Widget(editor, "bmed.tag.linksplit"))) { |
2852 | addTag_Bookmark(bm, linkSplit_BookmarkTag); | 2913 | addTag_Bookmark(bm, linkSplit_BookmarkTag); |
2853 | } | 2914 | } |
2915 | bm->parentId = folder ? id_Bookmark(folder) : 0; | ||
2916 | if (bm->parentId) { | ||
2917 | setRecentFolder_Bookmarks(bookmarks_App(), bm->parentId); | ||
2918 | } | ||
2854 | postCommandf_App("bookmarks.changed added:%zu", id); | 2919 | postCommandf_App("bookmarks.changed added:%zu", id); |
2855 | } | 2920 | } |
2856 | setupSheetTransition_Mobile(editor, iFalse); | 2921 | setupSheetTransition_Mobile(editor, iFalse); |
diff --git a/src/ui/util.h b/src/ui/util.h index 52b3a692..81fb1cbd 100644 --- a/src/ui/util.h +++ b/src/ui/util.h | |||
@@ -311,6 +311,7 @@ iWidget * makePreferences_Widget (void); | |||
311 | void updatePreferencesLayout_Widget (iWidget *prefs); | 311 | void updatePreferencesLayout_Widget (iWidget *prefs); |
312 | 312 | ||
313 | iWidget * makeBookmarkEditor_Widget (void); | 313 | iWidget * makeBookmarkEditor_Widget (void); |
314 | void setBookmarkEditorFolder_Widget(iWidget *editor, uint32_t folderId); | ||
314 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); | 315 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); |
315 | iWidget * makeIdentityCreation_Widget (void); | 316 | iWidget * makeIdentityCreation_Widget (void); |
316 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); | 317 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); |
diff --git a/src/ui/window.c b/src/ui/window.c index fa364cff..2f6fe430 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
26 | #include "bookmarks.h" | 26 | #include "bookmarks.h" |
27 | #include "command.h" | 27 | #include "command.h" |
28 | #include "defs.h" | 28 | #include "defs.h" |
29 | #include "embedded.h" | 29 | #include "resources.h" |
30 | #include "keys.h" | 30 | #include "keys.h" |
31 | #include "labelwidget.h" | 31 | #include "labelwidget.h" |
32 | #include "documentwidget.h" | 32 | #include "documentwidget.h" |
@@ -583,7 +583,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
583 | #if defined (iPlatformLinux) | 583 | #if defined (iPlatformLinux) |
584 | SDL_SetWindowMinimumSize(d->base.win, minSize.x * d->base.pixelRatio, minSize.y * d->base.pixelRatio); | 584 | SDL_SetWindowMinimumSize(d->base.win, minSize.x * d->base.pixelRatio, minSize.y * d->base.pixelRatio); |
585 | /* Load the window icon. */ { | 585 | /* Load the window icon. */ { |
586 | SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, 0); | 586 | SDL_Surface *surf = loadImage_(&imageLagrange64_Resources, 0); |
587 | SDL_SetWindowIcon(d->base.win, surf); | 587 | SDL_SetWindowIcon(d->base.win, surf); |
588 | free(surf->pixels); | 588 | free(surf->pixels); |
589 | SDL_FreeSurface(surf); | 589 | SDL_FreeSurface(surf); |
@@ -597,7 +597,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
597 | setupUserInterface_MainWindow(d); | 597 | setupUserInterface_MainWindow(d); |
598 | postCommand_App("~bindings.changed"); /* update from bindings */ | 598 | postCommand_App("~bindings.changed"); /* update from bindings */ |
599 | /* Load the border shadow texture. */ { | 599 | /* Load the border shadow texture. */ { |
600 | SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0); | 600 | SDL_Surface *surf = loadImage_(&imageShadow_Resources, 0); |
601 | d->base.borderShadow = SDL_CreateTextureFromSurface(d->base.render, surf); | 601 | d->base.borderShadow = SDL_CreateTextureFromSurface(d->base.render, surf); |
602 | SDL_SetTextureBlendMode(d->base.borderShadow, SDL_BLENDMODE_BLEND); | 602 | SDL_SetTextureBlendMode(d->base.borderShadow, SDL_BLENDMODE_BLEND); |
603 | free(surf->pixels); | 603 | free(surf->pixels); |
@@ -607,7 +607,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
607 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 607 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
608 | /* Load the app icon for drawing in the title bar. */ | 608 | /* Load the app icon for drawing in the title bar. */ |
609 | if (prefs_App()->customFrame) { | 609 | if (prefs_App()->customFrame) { |
610 | SDL_Surface *surf = loadImage_(&imageLagrange64_Embedded, appIconSize_Root()); | 610 | SDL_Surface *surf = loadImage_(&imageLagrange64_Resources, appIconSize_Root()); |
611 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); | 611 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); |
612 | d->appIcon = SDL_CreateTextureFromSurface(d->base.render, surf); | 612 | d->appIcon = SDL_CreateTextureFromSurface(d->base.render, surf); |
613 | free(surf->pixels); | 613 | free(surf->pixels); |