diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-06 12:16:43 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-06 12:16:43 +0300 |
commit | 61a3dc017067be43472dadb7909094aa04d1fe9d (patch) | |
tree | 18b87895489844b4e516f79cd1588038f1d49494 | |
parent | f6a54d5375aab9c41af3f7c8a5e8fcbd1e0c9287 (diff) |
Revised runtime font management
The built-in fonts are loaded via FontPack, and the font table is now constructed dynamically based on available fonts.
A full set of variants (style, size) are prepared for each font, but some of the data gets allocated lazily when needed.
GmRun needed a larger allocation for fonts, so now all the fields are combined into a single bit field.
TODO: Glyph scaling, vertical offsets, and symbol lookup are still not fully working.
-rw-r--r-- | res/fonts/fontpack.ini | 3 | ||||
-rw-r--r-- | src/app.c | 8 | ||||
-rw-r--r-- | src/fontpack.c | 143 | ||||
-rw-r--r-- | src/fontpack.h | 45 | ||||
-rw-r--r-- | src/gmdocument.c | 103 | ||||
-rw-r--r-- | src/gmdocument.h | 21 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/ui/color.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 49 | ||||
-rw-r--r-- | src/ui/mediaui.c | 4 | ||||
-rw-r--r-- | src/ui/mobile.c | 8 | ||||
-rw-r--r-- | src/ui/root.c | 13 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 18 | ||||
-rw-r--r-- | src/ui/text.c | 337 | ||||
-rw-r--r-- | src/ui/text.h | 131 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 2 | ||||
-rw-r--r-- | src/ui/util.c | 10 | ||||
-rw-r--r-- | src/ui/window.c | 4 |
18 files changed, 568 insertions, 336 deletions
diff --git a/res/fonts/fontpack.ini b/res/fonts/fontpack.ini index 53bd7402..153b7f86 100644 --- a/res/fonts/fontpack.ini +++ b/res/fonts/fontpack.ini | |||
@@ -16,6 +16,7 @@ regular = "IosevkaTerm-Extended.ttf" | |||
16 | 16 | ||
17 | [smolemoji] | 17 | [smolemoji] |
18 | name = "Smol Emoji" | 18 | name = "Smol Emoji" |
19 | override = true # These Emoji are always preferred. | ||
19 | auxiliary = true | 20 | auxiliary = true |
20 | priority = 100 | 21 | priority = 100 |
21 | regular = "SmolEmoji-Regular.ttf" | 22 | regular = "SmolEmoji-Regular.ttf" |
@@ -32,6 +33,7 @@ name = "Noto Sans Symbols 2" | |||
32 | auxiliary = true | 33 | auxiliary = true |
33 | priority = 20 | 34 | priority = 20 |
34 | scaling = 1.45 | 35 | scaling = 1.45 |
36 | voffset = 1.2 | ||
35 | regular = "NotoSansSymbols2-Regular.ttf" | 37 | regular = "NotoSansSymbols2-Regular.ttf" |
36 | 38 | ||
37 | [notosymbols] | 39 | [notosymbols] |
@@ -39,4 +41,5 @@ name = "Noto Sans Symbols" | |||
39 | auxiliary = true | 41 | auxiliary = true |
40 | priority = 10 | 42 | priority = 10 |
41 | scaling = 2.0 | 43 | scaling = 2.0 |
44 | voffset = 0.5 | ||
42 | regular = "NotoSansSymbols-Regular.ttf" | 45 | regular = "NotoSansSymbols-Regular.ttf" |
@@ -116,7 +116,7 @@ struct Impl_App { | |||
116 | iMimeHooks * mimehooks; | 116 | iMimeHooks * mimehooks; |
117 | iGmCerts * certs; | 117 | iGmCerts * certs; |
118 | iVisited * visited; | 118 | iVisited * visited; |
119 | iBookmarks * bookmarks; | 119 | iBookmarks * bookmarks; |
120 | iMainWindow *window; | 120 | iMainWindow *window; |
121 | iPtrArray popupWindows; | 121 | iPtrArray popupWindows; |
122 | iSortedArray tickers; /* per-frame callbacks, used for animations */ | 122 | iSortedArray tickers; /* per-frame callbacks, used for animations */ |
@@ -758,7 +758,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
758 | listen_Ipc(); /* We'll respond to commands from other instances. */ | 758 | listen_Ipc(); /* We'll respond to commands from other instances. */ |
759 | } | 759 | } |
760 | #endif | 760 | #endif |
761 | printf("Lagrange: A Beautiful Gemini Client\n"); | 761 | puts("Lagrange: A Beautiful Gemini Client"); |
762 | const iBool isFirstRun = | 762 | const iBool isFirstRun = |
763 | !fileExistsCStr_FileInfo(cleanedPath_CStr(concatPath_CStr(dataDir_App_(), "prefs.cfg"))); | 763 | !fileExistsCStr_FileInfo(cleanedPath_CStr(concatPath_CStr(dataDir_App_(), "prefs.cfg"))); |
764 | d->isFinishedLaunching = iFalse; | 764 | d->isFinishedLaunching = iFalse; |
@@ -803,6 +803,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
803 | setupApplication_iOS(); | 803 | setupApplication_iOS(); |
804 | #endif | 804 | #endif |
805 | init_Keys(); | 805 | init_Keys(); |
806 | init_Fonts(dataDir_App_()); | ||
806 | loadPalette_Color(dataDir_App_()); | 807 | loadPalette_Color(dataDir_App_()); |
807 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ | 808 | setThemePalette_Color(d->prefs.theme); /* default UI colors */ |
808 | loadPrefs_App_(d); | 809 | loadPrefs_App_(d); |
@@ -883,6 +884,7 @@ static void deinit_App(iApp *d) { | |||
883 | deinit_Feeds(); | 884 | deinit_Feeds(); |
884 | save_Keys(dataDir_App_()); | 885 | save_Keys(dataDir_App_()); |
885 | deinit_Keys(); | 886 | deinit_Keys(); |
887 | deinit_Fonts(); | ||
886 | deinit_SiteSpec(); | 888 | deinit_SiteSpec(); |
887 | savePrefs_App_(d); | 889 | savePrefs_App_(d); |
888 | deinit_Prefs(&d->prefs); | 890 | deinit_Prefs(&d->prefs); |
@@ -2124,6 +2126,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2124 | resetFonts_App_(d); | 2126 | resetFonts_App_(d); |
2125 | return iTrue; | 2127 | return iTrue; |
2126 | } | 2128 | } |
2129 | #if 0 | ||
2127 | else if (equal_Command(cmd, "font.user")) { | 2130 | else if (equal_Command(cmd, "font.user")) { |
2128 | const char *path = suffixPtr_Command(cmd, "path"); | 2131 | const char *path = suffixPtr_Command(cmd, "path"); |
2129 | if (cmp_String(&d->prefs.symbolFontPath, path)) { | 2132 | if (cmp_String(&d->prefs.symbolFontPath, path)) { |
@@ -2140,6 +2143,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2140 | } | 2143 | } |
2141 | return iTrue; | 2144 | return iTrue; |
2142 | } | 2145 | } |
2146 | #endif | ||
2143 | else if (equal_Command(cmd, "font.set")) { | 2147 | else if (equal_Command(cmd, "font.set")) { |
2144 | if (!isFrozen) { | 2148 | if (!isFrozen) { |
2145 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); | 2149 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); |
diff --git a/src/fontpack.c b/src/fontpack.c index af341363..0ca0d486 100644 --- a/src/fontpack.c +++ b/src/fontpack.c | |||
@@ -30,11 +30,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
30 | #include <the_Foundation/string.h> | 30 | #include <the_Foundation/string.h> |
31 | #include <the_Foundation/toml.h> | 31 | #include <the_Foundation/toml.h> |
32 | 32 | ||
33 | float scale_FontSize(enum iFontSize size) { | ||
34 | static const float sizes[max_FontSize] = { | ||
35 | 1.000, /* UI sizes */ | ||
36 | 1.125, | ||
37 | 1.333, | ||
38 | 1.666, | ||
39 | 0.800, | ||
40 | 0.900, | ||
41 | 1.000, /* document sizes */ | ||
42 | 1.200, | ||
43 | 1.333, | ||
44 | 1.666, | ||
45 | 2.000, | ||
46 | 0.568, | ||
47 | 0.710, | ||
48 | 0.800, | ||
49 | }; | ||
50 | if (size < 0 || size >= max_FontSize) { | ||
51 | return 1.0f; | ||
52 | } | ||
53 | return sizes[size]; | ||
54 | } | ||
55 | |||
33 | iDeclareType(Fonts) | 56 | iDeclareType(Fonts) |
34 | 57 | ||
35 | struct Impl_Fonts { | 58 | struct Impl_Fonts { |
36 | iString userDir; | 59 | iString userDir; |
60 | iPtrArray packs; | ||
37 | iPtrArray files; | 61 | iPtrArray files; |
62 | iPtrArray specOrder; /* specs sorted by priority */ | ||
38 | }; | 63 | }; |
39 | 64 | ||
40 | static iFonts fonts_; | 65 | static iFonts fonts_; |
@@ -55,6 +80,9 @@ void init_FontFile(iFontFile *d) { | |||
55 | static void load_FontFile_(iFontFile *d, const iBlock *data) { | 80 | static void load_FontFile_(iFontFile *d, const iBlock *data) { |
56 | set_Block(&d->sourceData, data); | 81 | set_Block(&d->sourceData, data); |
57 | stbtt_InitFont(&d->stbInfo, constData_Block(&d->sourceData), 0); | 82 | stbtt_InitFont(&d->stbInfo, constData_Block(&d->sourceData), 0); |
83 | /* Basic metrics. */ | ||
84 | stbtt_GetFontVMetrics(&d->stbInfo, &d->ascent, &d->descent, NULL); | ||
85 | stbtt_GetCodepointHMetrics(&d->stbInfo, 'M', &d->emAdvance, NULL); | ||
58 | #if defined(LAGRANGE_ENABLE_HARFBUZZ) | 86 | #if defined(LAGRANGE_ENABLE_HARFBUZZ) |
59 | /* HarfBuzz will read the font data. */ | 87 | /* HarfBuzz will read the font data. */ |
60 | d->hbBlob = hb_blob_create(constData_Block(&d->sourceData), size_Block(&d->sourceData), | 88 | d->hbBlob = hb_blob_create(constData_Block(&d->sourceData), size_Block(&d->sourceData), |
@@ -83,6 +111,23 @@ void deinit_FontFile(iFontFile *d) { | |||
83 | deinit_Block(&d->sourceData); | 111 | deinit_Block(&d->sourceData); |
84 | } | 112 | } |
85 | 113 | ||
114 | float scaleForPixelHeight_FontFile(const iFontFile *d, int pixelHeight) { | ||
115 | return stbtt_ScaleForPixelHeight(&d->stbInfo, pixelHeight); | ||
116 | } | ||
117 | |||
118 | uint8_t *rasterizeGlyph_FontFile(const iFontFile *d, float xScale, float yScale, float xShift, | ||
119 | uint32_t glyphIndex, int *w, int *h) { | ||
120 | return stbtt_GetGlyphBitmapSubpixel( | ||
121 | &d->stbInfo, xScale, yScale, xShift, 0.0f, glyphIndex, w, h, 0, 0); | ||
122 | } | ||
123 | |||
124 | void measureGlyph_FontFile(const iFontFile *d, uint32_t glyphIndex, | ||
125 | float xScale, float yScale, float xShift, | ||
126 | int *x0, int *y0, int *x1, int *y1) { | ||
127 | stbtt_GetGlyphBitmapBoxSubpixel( | ||
128 | &d->stbInfo, glyphIndex, xScale, yScale, xShift, 0.0f, x0, y0, x1, y1); | ||
129 | } | ||
130 | |||
86 | /*----------------------------------------------------------------------------------------------*/ | 131 | /*----------------------------------------------------------------------------------------------*/ |
87 | 132 | ||
88 | 133 | ||
@@ -90,7 +135,12 @@ iDefineTypeConstruction(FontSpec) | |||
90 | 135 | ||
91 | void init_FontSpec(iFontSpec *d) { | 136 | void init_FontSpec(iFontSpec *d) { |
92 | init_String(&d->id); | 137 | init_String(&d->id); |
93 | init_String(&d->name); | 138 | init_String(&d->name); |
139 | d->flags = 0; | ||
140 | d->priority = 0; | ||
141 | d->scaling = 1.0f; | ||
142 | d->vertOffset = 1.0f; | ||
143 | iZap(d->styles); | ||
94 | } | 144 | } |
95 | 145 | ||
96 | void deinit_FontSpec(iFontSpec *d) { | 146 | void deinit_FontSpec(iFontSpec *d) { |
@@ -124,6 +174,8 @@ void deinit_FontPack(iFontPack *d) { | |||
124 | deinit_Array(&d->fonts); | 174 | deinit_Array(&d->fonts); |
125 | } | 175 | } |
126 | 176 | ||
177 | iDefineTypeConstruction(FontPack) | ||
178 | |||
127 | void handleIniTable_FontPack_(void *context, const iString *table, iBool isStart) { | 179 | void handleIniTable_FontPack_(void *context, const iString *table, iBool isStart) { |
128 | iFontPack *d = context; | 180 | iFontPack *d = context; |
129 | if (isStart) { | 181 | if (isStart) { |
@@ -132,6 +184,24 @@ void handleIniTable_FontPack_(void *context, const iString *table, iBool isStart | |||
132 | set_String(&d->loadSpec->id, table); | 184 | set_String(&d->loadSpec->id, table); |
133 | } | 185 | } |
134 | else { | 186 | else { |
187 | /* Set fallback font files. */ { | ||
188 | const iFontFile **styles = d->loadSpec->styles; | ||
189 | if (!styles[regular_FontStyle]) { | ||
190 | fprintf(stderr, "[FontPack] \"%s\" missing a regular style font file\n", | ||
191 | cstr_String(table)); | ||
192 | delete_FontSpec(d->loadSpec); | ||
193 | d->loadSpec = NULL; | ||
194 | return; | ||
195 | } | ||
196 | if (!styles[semiBold_FontStyle]) { | ||
197 | styles[semiBold_FontStyle] = styles[bold_FontStyle]; | ||
198 | } | ||
199 | for (size_t s = 0; s < max_FontStyle; s++) { | ||
200 | if (!styles[s]) { | ||
201 | styles[s] = styles[regular_FontStyle]; | ||
202 | } | ||
203 | } | ||
204 | } | ||
135 | pushBack_Array(&d->fonts, d->loadSpec); | 205 | pushBack_Array(&d->fonts, d->loadSpec); |
136 | d->loadSpec = NULL; | 206 | d->loadSpec = NULL; |
137 | } | 207 | } |
@@ -151,6 +221,12 @@ void handleIniKeyValue_FontPack_(void *context, const iString *table, const iStr | |||
151 | else if (!cmp_String(key, "scaling")) { | 221 | else if (!cmp_String(key, "scaling")) { |
152 | d->loadSpec->scaling = (float) number_TomlValue(value); | 222 | d->loadSpec->scaling = (float) number_TomlValue(value); |
153 | } | 223 | } |
224 | else if (!cmp_String(key, "voffset")) { | ||
225 | d->loadSpec->vertOffset = (float) number_TomlValue(value); | ||
226 | } | ||
227 | else if (!cmp_String(key, "override") && value->type == boolean_TomlType) { | ||
228 | iChangeFlags(d->loadSpec->flags, override_FontSpecFlag, value->value.boolean); | ||
229 | } | ||
154 | else if (!cmp_String(key, "monospace") && value->type == boolean_TomlType) { | 230 | else if (!cmp_String(key, "monospace") && value->type == boolean_TomlType) { |
155 | iChangeFlags(d->loadSpec->flags, monospace_FontSpecFlag, value->value.boolean); | 231 | iChangeFlags(d->loadSpec->flags, monospace_FontSpecFlag, value->value.boolean); |
156 | } | 232 | } |
@@ -160,6 +236,10 @@ void handleIniKeyValue_FontPack_(void *context, const iString *table, const iStr | |||
160 | else if (!cmp_String(key, "arabic") && value->type == boolean_TomlType) { | 236 | else if (!cmp_String(key, "arabic") && value->type == boolean_TomlType) { |
161 | iChangeFlags(d->loadSpec->flags, arabic_FontSpecFlag, value->value.boolean); | 237 | iChangeFlags(d->loadSpec->flags, arabic_FontSpecFlag, value->value.boolean); |
162 | } | 238 | } |
239 | else if (!cmp_String(key, "tweaks")) { | ||
240 | iChangeFlags(d->loadSpec->flags, fixNunitoKerning_FontSpecFlag, | ||
241 | ((int) number_TomlValue(value)) & 1); | ||
242 | } | ||
163 | else if (value->type == string_TomlType) { | 243 | else if (value->type == string_TomlType) { |
164 | const char *styles[max_FontStyle] = { "regular", "italic", "light", "semibold", "bold" }; | 244 | const char *styles[max_FontStyle] = { "regular", "italic", "light", "semibold", "bold" }; |
165 | iForIndices(i, styles) { | 245 | iForIndices(i, styles) { |
@@ -191,8 +271,10 @@ iBool loadIniFile_FontPack(iFontPack *d, const iString *iniPath) { | |||
191 | d->loadPath = collect_String(newRange_String(dirName_Path(iniPath))); | 271 | d->loadPath = collect_String(newRange_String(dirName_Path(iniPath))); |
192 | iString *src = collect_String(readString_File(f)); | 272 | iString *src = collect_String(readString_File(f)); |
193 | iTomlParser *ini = collect_TomlParser(new_TomlParser()); | 273 | iTomlParser *ini = collect_TomlParser(new_TomlParser()); |
194 | setHandlers_TomlParser(ini, 0, 0, d); | 274 | setHandlers_TomlParser(ini, handleIniTable_FontPack_, handleIniKeyValue_FontPack_, d); |
195 | parse_TomlParser(ini, src); | 275 | if (!parse_TomlParser(ini, src)) { |
276 | fprintf(stderr, "[FontPack] error parsing %s\n", cstr_String(iniPath)); | ||
277 | } | ||
196 | iAssert(d->loadSpec == NULL); | 278 | iAssert(d->loadSpec == NULL); |
197 | d->loadPath = NULL; | 279 | d->loadPath = NULL; |
198 | ok = iTrue; | 280 | ok = iTrue; |
@@ -204,23 +286,74 @@ iBool loadIniFile_FontPack(iFontPack *d, const iString *iniPath) { | |||
204 | /*----------------------------------------------------------------------------------------------*/ | 286 | /*----------------------------------------------------------------------------------------------*/ |
205 | 287 | ||
206 | static void unloadFiles_Fonts_(iFonts *d) { | 288 | static void unloadFiles_Fonts_(iFonts *d) { |
289 | /* TODO: Mark all files in font packs as not resident. */ | ||
207 | iForEach(PtrArray, i, &d->files) { | 290 | iForEach(PtrArray, i, &d->files) { |
208 | delete_FontFile(i.ptr); | 291 | delete_FontFile(i.ptr); |
209 | } | 292 | } |
210 | clear_PtrArray(&d->files); | 293 | clear_PtrArray(&d->files); |
211 | } | 294 | } |
212 | 295 | ||
296 | static void unloadFonts_Fonts_(iFonts *d) { | ||
297 | iForEach(PtrArray, i, &d->packs) { | ||
298 | iFontPack *pack = i.ptr; | ||
299 | delete_FontPack(pack); | ||
300 | } | ||
301 | clear_PtrArray(&d->packs); | ||
302 | } | ||
303 | |||
304 | static int cmpPriority_FontSpecPtr_(const void *a, const void *b) { | ||
305 | const iFontSpec **p1 = (const iFontSpec **) a, **p2 = (const iFontSpec **) b; | ||
306 | return -iCmp((*p1)->priority, (*p2)->priority); /* highest priority first */ | ||
307 | } | ||
308 | |||
309 | static void sortSpecs_Fonts_(iFonts *d) { | ||
310 | clear_PtrArray(&d->specOrder); | ||
311 | iConstForEach(PtrArray, p, &d->packs) { | ||
312 | const iFontPack *pack = p.ptr; | ||
313 | iConstForEach(Array, i, &pack->fonts) { | ||
314 | pushBack_PtrArray(&d->specOrder, i.value); | ||
315 | } | ||
316 | } | ||
317 | sort_Array(&d->specOrder, cmpPriority_FontSpecPtr_); | ||
318 | } | ||
319 | |||
213 | void init_Fonts(const char *userDir) { | 320 | void init_Fonts(const char *userDir) { |
214 | iFonts *d = &fonts_; | 321 | iFonts *d = &fonts_; |
215 | initCStr_String(&d->userDir, userDir); | 322 | initCStr_String(&d->userDir, userDir); |
323 | init_PtrArray(&d->packs); | ||
216 | init_PtrArray(&d->files); | 324 | init_PtrArray(&d->files); |
325 | init_PtrArray(&d->specOrder); | ||
217 | /* Load the required fonts. */ | 326 | /* Load the required fonts. */ |
218 | 327 | iFontPack *pack = new_FontPack(); | |
328 | /* TODO: put default.fontpack in resources.lgr as a binary blob (uncompressed) */ | ||
329 | /* TODO: find and load .fontpack files in known locations */ | ||
330 | loadIniFile_FontPack(pack, collectNewCStr_String("/Users/jaakko/src/lagrange/" | ||
331 | "res/fonts/fontpack.ini")); | ||
332 | pushBack_PtrArray(&d->packs, pack); | ||
333 | sortSpecs_Fonts_(d); | ||
219 | } | 334 | } |
220 | 335 | ||
221 | void deinit_Fonts(void) { | 336 | void deinit_Fonts(void) { |
222 | iFonts *d = &fonts_; | 337 | iFonts *d = &fonts_; |
338 | unloadFonts_Fonts_(d); | ||
223 | unloadFiles_Fonts_(d); | 339 | unloadFiles_Fonts_(d); |
340 | deinit_PtrArray(&d->specOrder); | ||
341 | deinit_PtrArray(&d->packs); | ||
224 | deinit_PtrArray(&d->files); | 342 | deinit_PtrArray(&d->files); |
225 | deinit_String(&d->userDir); | 343 | deinit_String(&d->userDir); |
226 | } | 344 | } |
345 | |||
346 | const iFontSpec *findSpec_Fonts(const char *fontId) { | ||
347 | iFonts *d = &fonts_; | ||
348 | iConstForEach(PtrArray, i, &d->specOrder) { | ||
349 | const iFontSpec *spec = i.ptr; | ||
350 | if (!cmp_String(&spec->id, fontId)) { | ||
351 | return spec; | ||
352 | } | ||
353 | } | ||
354 | return NULL; | ||
355 | } | ||
356 | |||
357 | const iPtrArray *listSpecsByPriority_Fonts(void) { | ||
358 | return &fonts_.specOrder; | ||
359 | } | ||
diff --git a/src/fontpack.h b/src/fontpack.h index 7e78071f..c5aa993b 100644 --- a/src/fontpack.h +++ b/src/fontpack.h | |||
@@ -23,6 +23,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
23 | #pragma once | 23 | #pragma once |
24 | 24 | ||
25 | #include <the_Foundation/archive.h> | 25 | #include <the_Foundation/archive.h> |
26 | #include <the_Foundation/ptrarray.h> | ||
26 | #include "stb_truetype.h" | 27 | #include "stb_truetype.h" |
27 | 28 | ||
28 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) | 29 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) |
@@ -38,12 +39,12 @@ The user may install new fontpacks via the GUI. The user's fontpacks are stored | |||
38 | the config directory. There may also be fontpacks available from system-wide locations. */ | 39 | the config directory. There may also be fontpacks available from system-wide locations. */ |
39 | 40 | ||
40 | enum iFontSize { | 41 | enum iFontSize { |
41 | uiTiny_FontSize, /* 0.800 */ | 42 | uiNormal_FontSize, /* 1.000 -- keep at index 0 for convenience */ |
42 | uiSmall_FontSize, /* 0.900 */ | ||
43 | uiNormal_FontSize, /* 1.000 */ | ||
44 | uiMedium_FontSize, /* 1.125 */ | 43 | uiMedium_FontSize, /* 1.125 */ |
45 | uiBig_FontSize, /* 1.333 */ | 44 | uiBig_FontSize, /* 1.333 */ |
46 | uiLarge_FontSize, /* 1.666 */ | 45 | uiLarge_FontSize, /* 1.666 */ |
46 | uiTiny_FontSize, /* 0.800 */ | ||
47 | uiSmall_FontSize, /* 0.900 */ | ||
47 | contentRegular_FontSize, | 48 | contentRegular_FontSize, |
48 | contentMedium_FontSize, | 49 | contentMedium_FontSize, |
49 | contentBig_FontSize, | 50 | contentBig_FontSize, |
@@ -51,6 +52,7 @@ enum iFontSize { | |||
51 | contentHuge_FontSize, | 52 | contentHuge_FontSize, |
52 | contentMonoSmall_FontSize, | 53 | contentMonoSmall_FontSize, |
53 | contentMono_FontSize, | 54 | contentMono_FontSize, |
55 | contentSmall_FontSize, | ||
54 | max_FontSize | 56 | max_FontSize |
55 | }; | 57 | }; |
56 | 58 | ||
@@ -60,23 +62,22 @@ enum iFontStyle { | |||
60 | light_FontStyle, | 62 | light_FontStyle, |
61 | semiBold_FontStyle, | 63 | semiBold_FontStyle, |
62 | bold_FontStyle, | 64 | bold_FontStyle, |
63 | max_FontStyle | 65 | max_FontStyle, |
66 | /* all permutations: */ | ||
67 | maxVariants_Fonts = max_FontStyle * max_FontSize | ||
64 | }; | 68 | }; |
65 | 69 | ||
66 | iLocalDef enum iFontSize larger_FontSize(enum iFontSize size) { | 70 | float scale_FontSize (enum iFontSize size); |
67 | if (size == uiLarge_FontSize || size == contentHuge_FontSize || size == contentMono_FontSize) { | ||
68 | return size; /* largest available */ | ||
69 | } | ||
70 | return size + 1; | ||
71 | } | ||
72 | 71 | ||
73 | iDeclareType(FontSpec) | 72 | iDeclareType(FontSpec) |
74 | iDeclareTypeConstruction(FontSpec) | 73 | iDeclareTypeConstruction(FontSpec) |
75 | 74 | ||
76 | enum iFontSpecFlags { | 75 | enum iFontSpecFlags { |
77 | monospace_FontSpecFlag = iBit(1), /* can be used in preformatted content */ | 76 | override_FontSpecFlag = iBit(1), |
78 | auxiliary_FontSpecFlag = iBit(2), /* only used for looking up glyphs missing from other fonts */ | 77 | monospace_FontSpecFlag = iBit(2), /* can be used in preformatted content */ |
79 | arabic_FontSpecFlag = iBit(3), | 78 | auxiliary_FontSpecFlag = iBit(3), /* only used for looking up glyphs missing from other fonts */ |
79 | arabic_FontSpecFlag = iBit(4), | ||
80 | fixNunitoKerning_FontSpecFlag = iBit(31), /* manual hardcoded kerning tweaks for Nunito */ | ||
80 | }; | 81 | }; |
81 | 82 | ||
82 | iDeclareType(FontFile) | 83 | iDeclareType(FontFile) |
@@ -91,17 +92,33 @@ struct Impl_FontFile { | |||
91 | hb_face_t *hbFace; | 92 | hb_face_t *hbFace; |
92 | hb_font_t *hbFont; | 93 | hb_font_t *hbFont; |
93 | #endif | 94 | #endif |
95 | /* Metrics: */ | ||
96 | int ascent, descent, emAdvance; | ||
94 | }; | 97 | }; |
95 | 98 | ||
99 | float scaleForPixelHeight_FontFile (const iFontFile *, int pixelHeight); | ||
100 | |||
101 | iLocalDef uint32_t findGlyphIndex_FontFile(const iFontFile *d, iChar ch) { | ||
102 | return stbtt_FindGlyphIndex(&d->stbInfo, ch); | ||
103 | } | ||
104 | |||
105 | uint8_t * rasterizeGlyph_FontFile(const iFontFile *, float xScale, float yScale, float xShift, | ||
106 | uint32_t glyphIndex, int *w, int *h); /* caller must free() the returned bitmap */ | ||
107 | void measureGlyph_FontFile (const iFontFile *, uint32_t glyphIndex, | ||
108 | float xScale, float yScale, float xShift, | ||
109 | int *x0, int *y0, int *x1, int *y1); | ||
96 | struct Impl_FontSpec { | 110 | struct Impl_FontSpec { |
97 | iString id; /* unique ID */ | 111 | iString id; /* unique ID */ |
98 | iString name; /* human-readable label */ | 112 | iString name; /* human-readable label */ |
99 | int flags; | 113 | int flags; |
100 | int priority; | 114 | int priority; |
101 | float scaling; | 115 | float scaling; |
102 | const iFontFile *styles[max_FontSize]; | 116 | float vertOffset; |
117 | const iFontFile *styles[max_FontStyle]; | ||
103 | }; | 118 | }; |
104 | 119 | ||
105 | void init_Fonts (const char *userDir); | 120 | void init_Fonts (const char *userDir); |
106 | void deinit_Fonts (void); | 121 | void deinit_Fonts (void); |
107 | 122 | ||
123 | const iFontSpec * findSpec_Fonts (const char *fontId); | ||
124 | const iPtrArray * listSpecsByPriority_Fonts (void); | ||
diff --git a/src/gmdocument.c b/src/gmdocument.c index 2f4c7972..ce9fdec8 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -355,7 +355,7 @@ static enum iGmDocumentTheme currentTheme_(void) { | |||
355 | } | 355 | } |
356 | 356 | ||
357 | static void alignDecoration_GmRun_(iGmRun *run, iBool isCentered) { | 357 | static void alignDecoration_GmRun_(iGmRun *run, iBool isCentered) { |
358 | const iRect visBounds = visualBounds_Text(run->textParams.font, run->text); | 358 | const iRect visBounds = visualBounds_Text(run->font, run->text); |
359 | const int visWidth = width_Rect(visBounds); | 359 | const int visWidth = width_Rect(visBounds); |
360 | int xAdjust = 0; | 360 | int xAdjust = 0; |
361 | if (!isCentered) { | 361 | if (!isCentered) { |
@@ -438,7 +438,7 @@ static iBool typesetOneLine_RunTypesetter_(iWrapText *wrap, iRangecc wrapRange, | |||
438 | trimEnd_Rangecc(&wrapRange); | 438 | trimEnd_Rangecc(&wrapRange); |
439 | // printf("typeset: {%s}\n", cstr_Rangecc(wrapRange)); | 439 | // printf("typeset: {%s}\n", cstr_Rangecc(wrapRange)); |
440 | iRunTypesetter *d = wrap->context; | 440 | iRunTypesetter *d = wrap->context; |
441 | const int fontId = d->run.textParams.font; | 441 | const int fontId = d->run.font; |
442 | d->run.text = wrapRange; | 442 | d->run.text = wrapRange; |
443 | if (~d->run.flags & startOfLine_GmRunFlag && d->lineHeightReduction > 0.0f) { | 443 | if (~d->run.flags & startOfLine_GmRunFlag && d->lineHeightReduction > 0.0f) { |
444 | d->pos.y -= d->lineHeightReduction * lineHeight_Text(fontId); | 444 | d->pos.y -= d->lineHeightReduction * lineHeight_Text(fontId); |
@@ -450,7 +450,7 @@ static iBool typesetOneLine_RunTypesetter_(iWrapText *wrap, iRangecc wrapRange, | |||
450 | d->run.bounds.size.y = dims.y; | 450 | d->run.bounds.size.y = dims.y; |
451 | d->run.visBounds = d->run.bounds; | 451 | d->run.visBounds = d->run.bounds; |
452 | d->run.visBounds.size.x = dims.x; | 452 | d->run.visBounds.size.x = dims.x; |
453 | d->run.textParams.isRTL = isBaseRTL; | 453 | d->run.isRTL = isBaseRTL; |
454 | pushBack_Array(&d->layout, &d->run); | 454 | pushBack_Array(&d->layout, &d->run); |
455 | d->run.flags &= ~startOfLine_GmRunFlag; | 455 | d->run.flags &= ~startOfLine_GmRunFlag; |
456 | d->pos.y += lineHeight_Text(fontId) * prefs_App()->lineSpacing; | 456 | d->pos.y += lineHeight_Text(fontId) * prefs_App()->lineSpacing; |
@@ -543,7 +543,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
543 | if (*line.end == '\r') { | 543 | if (*line.end == '\r') { |
544 | line.end--; /* trim CR always */ | 544 | line.end--; /* trim CR always */ |
545 | } | 545 | } |
546 | iGmRun run = { .textParams = { .color = white_ColorId } }; | 546 | iGmRun run = { .color = white_ColorId }; |
547 | enum iGmLineType type; | 547 | enum iGmLineType type; |
548 | float indent = 0.0f; | 548 | float indent = 0.0f; |
549 | /* Detect the type of the line. */ | 549 | /* Detect the type of the line. */ |
@@ -581,14 +581,16 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
581 | continue; | 581 | continue; |
582 | } | 582 | } |
583 | else if (type == link_GmLineType) { | 583 | else if (type == link_GmLineType) { |
584 | line = addLink_GmDocument_(d, line, &run.linkId); | 584 | iGmLinkId linkId; |
585 | line = addLink_GmDocument_(d, line, &linkId); | ||
586 | run.linkId = linkId; | ||
585 | if (!run.linkId) { | 587 | if (!run.linkId) { |
586 | /* Invalid formatting. */ | 588 | /* Invalid formatting. */ |
587 | type = text_GmLineType; | 589 | type = text_GmLineType; |
588 | } | 590 | } |
589 | } | 591 | } |
590 | trimLine_Rangecc(&line, type, isNormalized); | 592 | trimLine_Rangecc(&line, type, isNormalized); |
591 | run.textParams.font = fonts[type]; | 593 | run.font = fonts[type]; |
592 | /* Remember headings for the document outline. */ | 594 | /* Remember headings for the document outline. */ |
593 | if (type == heading1_GmLineType || type == heading2_GmLineType || type == heading3_GmLineType) { | 595 | if (type == heading1_GmLineType || type == heading2_GmLineType || type == heading3_GmLineType) { |
594 | pushBack_Array( | 596 | pushBack_Array( |
@@ -609,7 +611,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
609 | continue; | 611 | continue; |
610 | } | 612 | } |
611 | run.preId = preId; | 613 | run.preId = preId; |
612 | run.textParams.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); | 614 | run.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); |
613 | indent = indents[type]; | 615 | indent = indents[type]; |
614 | } | 616 | } |
615 | if (addSiteBanner) { | 617 | if (addSiteBanner) { |
@@ -624,9 +626,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
624 | banner.visBounds.size.y += iMaxi(6000 * lineHeight_Text(uiLabel_FontId) / | 626 | banner.visBounds.size.y += iMaxi(6000 * lineHeight_Text(uiLabel_FontId) / |
625 | d->size.x, lineHeight_Text(uiLabel_FontId) * 5); | 627 | d->size.x, lineHeight_Text(uiLabel_FontId) * 5); |
626 | } | 628 | } |
627 | banner.text = bannerText; | 629 | banner.text = bannerText; |
628 | banner.textParams.font = banner_FontId; | 630 | banner.font = banner_FontId; |
629 | banner.textParams.color = tmBannerTitle_ColorId; | 631 | banner.color = tmBannerTitle_ColorId; |
630 | pushBack_Array(&d->layout, &banner); | 632 | pushBack_Array(&d->layout, &banner); |
631 | pos.y += height_Rect(banner.visBounds) + | 633 | pos.y += height_Rect(banner.visBounds) + |
632 | 1.5f * lineHeight_Text(paragraph_FontId) * prefs->lineSpacing; | 634 | 1.5f * lineHeight_Text(paragraph_FontId) * prefs->lineSpacing; |
@@ -637,13 +639,13 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
637 | if (type == quote_GmLineType && !prefs->quoteIcon) { | 639 | if (type == quote_GmLineType && !prefs->quoteIcon) { |
638 | /* For quote indicators we still need to produce a run. */ | 640 | /* For quote indicators we still need to produce a run. */ |
639 | run.visBounds.pos = addX_I2(pos, indents[type] * gap_Text); | 641 | run.visBounds.pos = addX_I2(pos, indents[type] * gap_Text); |
640 | run.visBounds.size = init_I2(gap_Text, lineHeight_Text(run.textParams.font)); | 642 | run.visBounds.size = init_I2(gap_Text, lineHeight_Text(run.font)); |
641 | run.bounds = zero_Rect(); /* just visual */ | 643 | run.bounds = zero_Rect(); /* just visual */ |
642 | run.flags = quoteBorder_GmRunFlag | decoration_GmRunFlag; | ||
643 | run.text = iNullRange; | 644 | run.text = iNullRange; |
645 | run.flags = quoteBorder_GmRunFlag | decoration_GmRunFlag; | ||
644 | pushBack_Array(&d->layout, &run); | 646 | pushBack_Array(&d->layout, &run); |
645 | } | 647 | } |
646 | pos.y += lineHeight_Text(run.textParams.font) * prefs->lineSpacing; | 648 | pos.y += lineHeight_Text(run.font) * prefs->lineSpacing; |
647 | prevType = type; | 649 | prevType = type; |
648 | if (type != quote_GmLineType) { | 650 | if (type != quote_GmLineType) { |
649 | addQuoteIcon = prefs->quoteIcon; | 651 | addQuoteIcon = prefs->quoteIcon; |
@@ -687,14 +689,14 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
687 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); | 689 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); |
688 | if (meta->flags & folded_GmPreMetaFlag) { | 690 | if (meta->flags & folded_GmPreMetaFlag) { |
689 | const iBool isBlank = isEmpty_Range(&meta->altText); | 691 | const iBool isBlank = isEmpty_Range(&meta->altText); |
690 | iGmRun altText = { | 692 | iGmRun altText = { .font = paragraph_FontId, |
691 | .textParams = { .font = paragraph_FontId, .color = tmQuote_ColorId }, | 693 | .color = tmQuote_ColorId, |
692 | .flags = (isBlank ? decoration_GmRunFlag : 0) | altText_GmRunFlag | 694 | .flags = (isBlank ? decoration_GmRunFlag : 0) | altText_GmRunFlag |
693 | }; | 695 | }; |
694 | const iInt2 margin = preRunMargin_GmDocument(d, 0); | 696 | const iInt2 margin = preRunMargin_GmDocument(d, 0); |
695 | altText.text = isBlank ? range_Lang(range_CStr("doc.pre.nocaption")) | 697 | altText.text = isBlank ? range_Lang(range_CStr("doc.pre.nocaption")) |
696 | : meta->altText; | 698 | : meta->altText; |
697 | iInt2 size = measureWrapRange_Text(altText.textParams.font, d->size.x - 2 * margin.x, | 699 | iInt2 size = measureWrapRange_Text(altText.font, d->size.x - 2 * margin.x, |
698 | altText.text).bounds.size; | 700 | altText.text).bounds.size; |
699 | altText.bounds = altText.visBounds = init_Rect(pos.x, pos.y, d->size.x, | 701 | altText.bounds = altText.visBounds = init_Rect(pos.x, pos.y, d->size.x, |
700 | size.y + 2 * margin.y); | 702 | size.y + 2 * margin.y); |
@@ -713,19 +715,20 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
713 | setRange_String(&d->title, line); | 715 | setRange_String(&d->title, line); |
714 | } | 716 | } |
715 | /* List bullet. */ | 717 | /* List bullet. */ |
716 | run.textParams.color = colors[type]; | 718 | run.color = colors[type]; |
717 | if (type == bullet_GmLineType) { | 719 | if (type == bullet_GmLineType) { |
718 | /* TODO: Literata bullet is broken? */ | 720 | /* TODO: Literata bullet is broken? */ |
719 | iGmRun bulRun = run; | 721 | iGmRun bulRun = run; |
720 | if (prefs->font == literata_TextFont) { | 722 | if (prefs->font == literata_TextFont) { |
721 | /* Something wrong this the glyph in Literata, looks cropped. */ | 723 | /* Something wrong this the glyph in Literata, looks cropped. */ |
722 | bulRun.textParams.font = defaultContentRegular_FontId; | 724 | bulRun.font = FONT_ID(default_FontId, regular_FontStyle, |
725 | contentRegular_FontSize); | ||
723 | } | 726 | } |
724 | bulRun.textParams.color = tmQuote_ColorId; | 727 | bulRun.color = tmQuote_ColorId; |
725 | bulRun.visBounds.pos = addX_I2(pos, (indents[text_GmLineType] - 0.55f) * gap_Text); | 728 | bulRun.visBounds.pos = addX_I2(pos, (indents[text_GmLineType] - 0.55f) * gap_Text); |
726 | bulRun.visBounds.size = | 729 | bulRun.visBounds.size = |
727 | init_I2((indents[bullet_GmLineType] - indents[text_GmLineType]) * gap_Text, | 730 | init_I2((indents[bullet_GmLineType] - indents[text_GmLineType]) * gap_Text, |
728 | lineHeight_Text(bulRun.textParams.font)); | 731 | lineHeight_Text(bulRun.font)); |
729 | // bulRun.visBounds.pos.x -= 4 * gap_Text - width_Rect(bulRun.visBounds) / 2; | 732 | // bulRun.visBounds.pos.x -= 4 * gap_Text - width_Rect(bulRun.visBounds) / 2; |
730 | bulRun.bounds = zero_Rect(); /* just visual */ | 733 | bulRun.bounds = zero_Rect(); /* just visual */ |
731 | bulRun.text = range_CStr(bullet); | 734 | bulRun.text = range_CStr(bullet); |
@@ -737,11 +740,11 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
737 | if (type == quote_GmLineType && addQuoteIcon) { | 740 | if (type == quote_GmLineType && addQuoteIcon) { |
738 | addQuoteIcon = iFalse; | 741 | addQuoteIcon = iFalse; |
739 | iGmRun quoteRun = run; | 742 | iGmRun quoteRun = run; |
740 | quoteRun.textParams.font = heading1_FontId; | 743 | quoteRun.font = heading1_FontId; |
741 | quoteRun.text = range_CStr(quote); | 744 | quoteRun.text = range_CStr(quote); |
742 | quoteRun.textParams.color = tmQuoteIcon_ColorId; | 745 | quoteRun.color = tmQuoteIcon_ColorId; |
743 | iRect vis = visualBounds_Text(quoteRun.textParams.font, quoteRun.text); | 746 | iRect vis = visualBounds_Text(quoteRun.font, quoteRun.text); |
744 | quoteRun.visBounds.size = measure_Text(quoteRun.textParams.font, quote).bounds.size; | 747 | quoteRun.visBounds.size = measure_Text(quoteRun.font, quote).bounds.size; |
745 | quoteRun.visBounds.pos = | 748 | quoteRun.visBounds.pos = |
746 | add_I2(pos, | 749 | add_I2(pos, |
747 | init_I2((indents[quote_GmLineType] - 5) * gap_Text, | 750 | init_I2((indents[quote_GmLineType] - 5) * gap_Text, |
@@ -757,7 +760,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
757 | if (type == link_GmLineType) { | 760 | if (type == link_GmLineType) { |
758 | iGmRun icon = run; | 761 | iGmRun icon = run; |
759 | icon.visBounds.pos = pos; | 762 | icon.visBounds.pos = pos; |
760 | icon.visBounds.size = init_I2(indent * gap_Text, lineHeight_Text(run.textParams.font)); | 763 | icon.visBounds.size = init_I2(indent * gap_Text, lineHeight_Text(run.font)); |
761 | icon.bounds = zero_Rect(); /* just visual */ | 764 | icon.bounds = zero_Rect(); /* just visual */ |
762 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); | 765 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); |
763 | const enum iGmLinkScheme scheme = scheme_GmLinkFlag(link->flags); | 766 | const enum iGmLinkScheme scheme = scheme_GmLinkFlag(link->flags); |
@@ -775,23 +778,23 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
775 | } | 778 | } |
776 | /* TODO: List bullets needs the same centering logic. */ | 779 | /* TODO: List bullets needs the same centering logic. */ |
777 | /* Special exception for the tiny bullet operator. */ | 780 | /* Special exception for the tiny bullet operator. */ |
778 | icon.textParams.font = equal_Rangecc(link->labelIcon, "\u2219") ? regularMonospace_FontId | 781 | icon.font = equal_Rangecc(link->labelIcon, "\u2219") ? regularMonospace_FontId |
779 | : regular_FontId; | 782 | : paragraph_FontId; |
780 | alignDecoration_GmRun_(&icon, iFalse); | 783 | alignDecoration_GmRun_(&icon, iFalse); |
781 | icon.textParams.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart); | 784 | icon.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart); |
782 | icon.flags |= decoration_GmRunFlag; | 785 | icon.flags |= decoration_GmRunFlag; |
783 | pushBack_Array(&d->layout, &icon); | 786 | pushBack_Array(&d->layout, &icon); |
784 | } | 787 | } |
785 | run.textParams.color = colors[type]; | 788 | run.color = colors[type]; |
786 | if (d->format == plainText_SourceFormat) { | 789 | if (d->format == plainText_SourceFormat) { |
787 | run.textParams.color = colors[text_GmLineType]; | 790 | run.color = colors[text_GmLineType]; |
788 | } | 791 | } |
789 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ | 792 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ |
790 | // int bigCount = 0; | 793 | // int bigCount = 0; |
791 | iBool isLedeParagraph = iFalse; | 794 | iBool isLedeParagraph = iFalse; |
792 | if (type == text_GmLineType && isFirstText) { | 795 | if (type == text_GmLineType && isFirstText) { |
793 | if (!isMono) run.textParams.font = firstParagraph_FontId; | 796 | if (!isMono) run.font = firstParagraph_FontId; |
794 | run.textParams.color = tmFirstParagraph_ColorId; | 797 | run.color = tmFirstParagraph_ColorId; |
795 | // bigCount = 15; /* max lines -- what if the whole document is one paragraph? */ | 798 | // bigCount = 15; /* max lines -- what if the whole document is one paragraph? */ |
796 | isLedeParagraph = iTrue; | 799 | isLedeParagraph = iTrue; |
797 | isFirstText = iFalse; | 800 | isFirstText = iFalse; |
@@ -838,7 +841,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
838 | } | 841 | } |
839 | /* Visited links are never bold. */ | 842 | /* Visited links are never bold. */ |
840 | if (run.linkId && linkFlags_GmDocument(d, run.linkId) & visited_GmLinkFlag) { | 843 | if (run.linkId && linkFlags_GmDocument(d, run.linkId) & visited_GmLinkFlag) { |
841 | rts.run.textParams.font = paragraph_FontId; | 844 | rts.run.font = paragraph_FontId; |
842 | } | 845 | } |
843 | } | 846 | } |
844 | if (!prefs->quoteIcon && type == quote_GmLineType) { | 847 | if (!prefs->quoteIcon && type == quote_GmLineType) { |
@@ -854,7 +857,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
854 | .mode = word_WrapTextMode, | 857 | .mode = word_WrapTextMode, |
855 | .wrapFunc = typesetOneLine_RunTypesetter_, | 858 | .wrapFunc = typesetOneLine_RunTypesetter_, |
856 | .context = &rts }; | 859 | .context = &rts }; |
857 | measure_WrapText(&wrapText, rts.run.textParams.font); | 860 | measure_WrapText(&wrapText, rts.run.font); |
858 | if (!isLedeParagraph || size_Array(&rts.layout) <= maxLedeLines_) { | 861 | if (!isLedeParagraph || size_Array(&rts.layout) <= maxLedeLines_) { |
859 | if (wrapText.baseDir < 0) { | 862 | if (wrapText.baseDir < 0) { |
860 | /* Right-aligned paragraphs need margins and decorations to be flipped. */ | 863 | /* Right-aligned paragraphs need margins and decorations to be flipped. */ |
@@ -880,8 +883,8 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
880 | } | 883 | } |
881 | clear_RunTypesetter_(&rts); | 884 | clear_RunTypesetter_(&rts); |
882 | rts.pos = pos; | 885 | rts.pos = pos; |
883 | rts.run.textParams.font = rts.fonts[text_GmLineType]; | 886 | rts.run.font = rts.fonts[text_GmLineType]; |
884 | rts.run.textParams.color = colors[text_GmLineType]; | 887 | rts.run.color = colors[text_GmLineType]; |
885 | isLedeParagraph = iFalse; | 888 | isLedeParagraph = iFalse; |
886 | } | 889 | } |
887 | pos = rts.pos; | 890 | pos = rts.pos; |
@@ -922,11 +925,11 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
922 | run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2; | 925 | run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2; |
923 | run.bounds.size.y = run.visBounds.size.y; | 926 | run.bounds.size.y = run.visBounds.size.y; |
924 | } | 927 | } |
925 | run.text = iNullRange; | 928 | run.text = iNullRange; |
926 | run.textParams.font = 0; | 929 | run.font = 0; |
927 | run.textParams.color = 0; | 930 | run.color = 0; |
928 | run.mediaType = image_GmRunMediaType; | 931 | run.mediaType = image_GmRunMediaType; |
929 | run.mediaId = imageId; | 932 | run.mediaId = imageId; |
930 | pushBack_Array(&d->layout, &run); | 933 | pushBack_Array(&d->layout, &run); |
931 | pos.y += run.bounds.size.y + margin; | 934 | pos.y += run.bounds.size.y + margin; |
932 | } | 935 | } |
@@ -941,9 +944,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
941 | run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI; | 944 | run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI; |
942 | run.visBounds = run.bounds; | 945 | run.visBounds = run.bounds; |
943 | run.text = iNullRange; | 946 | run.text = iNullRange; |
944 | run.textParams.color = 0; | 947 | run.color = 0; |
945 | run.mediaType = audio_GmRunMediaType; | 948 | run.mediaType = audio_GmRunMediaType; |
946 | run.mediaId = audioId; | 949 | run.mediaId = audioId; |
947 | pushBack_Array(&d->layout, &run); | 950 | pushBack_Array(&d->layout, &run); |
948 | pos.y += run.bounds.size.y + margin; | 951 | pos.y += run.bounds.size.y + margin; |
949 | } | 952 | } |
@@ -958,7 +961,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
958 | run.bounds.size.y = 2 * lineHeight_Text(uiContent_FontId) + 4 * gap_UI; | 961 | run.bounds.size.y = 2 * lineHeight_Text(uiContent_FontId) + 4 * gap_UI; |
959 | run.visBounds = run.bounds; | 962 | run.visBounds = run.bounds; |
960 | run.text = iNullRange; | 963 | run.text = iNullRange; |
961 | run.textParams.color = 0; | 964 | run.color = 0; |
962 | run.mediaType = download_GmRunMediaType; | 965 | run.mediaType = download_GmRunMediaType; |
963 | run.mediaId = downloadId; | 966 | run.mediaId = downloadId; |
964 | pushBack_Array(&d->layout, &run); | 967 | pushBack_Array(&d->layout, &run); |
@@ -1572,11 +1575,11 @@ static void markLinkRunsVisited_GmDocument_(iGmDocument *d, const iIntSet *linkI | |||
1572 | iForEach(Array, r, &d->layout) { | 1575 | iForEach(Array, r, &d->layout) { |
1573 | iGmRun *run = r.value; | 1576 | iGmRun *run = r.value; |
1574 | if (run->linkId && !run->mediaId && contains_IntSet(linkIds, run->linkId)) { | 1577 | if (run->linkId && !run->mediaId && contains_IntSet(linkIds, run->linkId)) { |
1575 | if (run->textParams.font == bold_FontId) { | 1578 | if (run->font == bold_FontId) { |
1576 | run->textParams.font = paragraph_FontId; | 1579 | run->font = paragraph_FontId; |
1577 | } | 1580 | } |
1578 | else if (run->flags & decoration_GmRunFlag) { | 1581 | else if (run->flags & decoration_GmRunFlag) { |
1579 | run->textParams.color = linkColor_GmDocument(d, run->linkId, icon_GmLinkPart); | 1582 | run->color = linkColor_GmDocument(d, run->linkId, icon_GmLinkPart); |
1580 | } | 1583 | } |
1581 | } | 1584 | } |
1582 | } | 1585 | } |
@@ -2072,7 +2075,7 @@ iRangecc findLoc_GmRun(const iGmRun *d, iInt2 pos) { | |||
2072 | return (iRangecc){ d->text.start, d->text.start }; | 2075 | return (iRangecc){ d->text.start, d->text.start }; |
2073 | } | 2076 | } |
2074 | iRangecc loc; | 2077 | iRangecc loc; |
2075 | tryAdvanceNoWrap_Text(d->textParams.font, d->text, x, &loc.start); | 2078 | tryAdvanceNoWrap_Text(d->font, d->text, x, &loc.start); |
2076 | loc.end = loc.start; | 2079 | loc.end = loc.start; |
2077 | if (!contains_Range(&d->text, loc.start)) { | 2080 | if (!contains_Range(&d->text, loc.start)) { |
2078 | return iNullRange; /* it's some other text */ | 2081 | return iNullRange; /* it's some other text */ |
diff --git a/src/gmdocument.h b/src/gmdocument.h index 332c3e00..b2c6d9b7 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -133,20 +133,23 @@ enum iGmRunMediaType { | |||
133 | download_GmRunMediaType, | 133 | download_GmRunMediaType, |
134 | }; | 134 | }; |
135 | 135 | ||
136 | /* This structure is tightly packed because GmDocuments are mostly composed of | ||
137 | a large number of GmRuns. */ | ||
136 | struct Impl_GmRun { | 138 | struct Impl_GmRun { |
137 | iRangecc text; | 139 | iRangecc text; |
138 | iRect bounds; /* used for hit testing, may extend to edges */ | 140 | iRect bounds; /* used for hit testing, may extend to edges */ |
139 | iRect visBounds; /* actual visual bounds */ | 141 | iRect visBounds; /* actual visual bounds */ |
140 | struct { | 142 | struct { |
141 | uint16_t color : 8; | 143 | uint32_t linkId : 16; /* GmLinkId; zero for non-links */ |
142 | uint16_t font : 7; | 144 | uint32_t flags : 8; /* GmRunFlags */ |
143 | uint16_t isRTL : 1; | 145 | uint32_t isRTL : 1; |
144 | } textParams; | 146 | uint32_t color : 7; /* see max_ColorId */ |
145 | uint8_t flags; | 147 | |
146 | uint8_t mediaType; | 148 | uint32_t font : 10; |
147 | uint16_t preId; /* preformatted block ID (sequential) */ | 149 | uint32_t mediaType : 2; |
148 | iGmLinkId linkId; /* zero for non-links */ | 150 | uint32_t mediaId : 10; /* zero if not an image */ |
149 | uint16_t mediaId; /* zero if not an image */ | 151 | uint32_t preId : 10; /* preformatted block ID (sequential); merge with mediaId? */ |
152 | }; | ||
150 | }; | 153 | }; |
151 | 154 | ||
152 | iDeclareType(GmRunRange) | 155 | iDeclareType(GmRunRange) |
@@ -68,6 +68,9 @@ int main(int argc, char **argv) { | |||
68 | SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); | 68 | SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); |
69 | SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); | 69 | SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); |
70 | SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); | 70 | SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); |
71 | #if 0 | ||
72 | SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); /* debugging! */ | ||
73 | #endif | ||
71 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) { | 74 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) { |
72 | fprintf(stderr, "[SDL] init failed: %s\n", SDL_GetError()); | 75 | fprintf(stderr, "[SDL] init failed: %s\n", SDL_GetError()); |
73 | return -1; | 76 | return -1; |
diff --git a/src/ui/color.h b/src/ui/color.h index 179db3e9..c9dff598 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -160,7 +160,7 @@ enum iColorId { | |||
160 | tmGopherLinkDomain_ColorId, | 160 | tmGopherLinkDomain_ColorId, |
161 | tmGopherLinkLastVisitDate_ColorId, | 161 | tmGopherLinkLastVisitDate_ColorId, |
162 | 162 | ||
163 | max_ColorId, | 163 | max_ColorId, /* note: GmRun packs color into limited number of bits */ |
164 | tmMax_ColorId = max_ColorId - tmFirst_ColorId | 164 | tmMax_ColorId = max_ColorId - tmFirst_ColorId |
165 | }; | 165 | }; |
166 | 166 | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 589b9e56..b83490f9 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -928,8 +928,9 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
928 | pushBackCStr_StringArray(title, "Lagrange"); | 928 | pushBackCStr_StringArray(title, "Lagrange"); |
929 | } | 929 | } |
930 | /* Take away parts if it doesn't fit. */ | 930 | /* Take away parts if it doesn't fit. */ |
931 | const int avail = bounds_Widget(as_Widget(tabButton)).size.x - 3 * gap_UI; | 931 | const int avail = bounds_Widget(as_Widget(tabButton)).size.x - 3 * gap_UI; |
932 | iBool setWindow = (document_App() == d && isUnderKeyRoot_Widget(d)); | 932 | iBool setWindow = (document_App() == d && isUnderKeyRoot_Widget(d)); |
933 | const int font = uiLabel_FontId; | ||
933 | for (;;) { | 934 | for (;;) { |
934 | iString *text = collect_String(joinCStr_StringArray(title, " \u2014 ")); | 935 | iString *text = collect_String(joinCStr_StringArray(title, " \u2014 ")); |
935 | if (setWindow) { | 936 | if (setWindow) { |
@@ -945,7 +946,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
945 | prependChar_String(text, siteIcon); | 946 | prependChar_String(text, siteIcon); |
946 | prependCStr_String(text, escape_Color(uiIcon_ColorId)); | 947 | prependCStr_String(text, escape_Color(uiIcon_ColorId)); |
947 | } | 948 | } |
948 | const int width = measureRange_Text(default_FontId, range_String(text)).advance.x; | 949 | const int width = measureRange_Text(font, range_String(text)).advance.x; |
949 | if (width <= avail || | 950 | if (width <= avail || |
950 | isEmpty_StringArray(title)) { | 951 | isEmpty_StringArray(title)) { |
951 | updateText_LabelWidget(tabButton, text); | 952 | updateText_LabelWidget(tabButton, text); |
@@ -954,9 +955,9 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
954 | if (size_StringArray(title) == 1) { | 955 | if (size_StringArray(title) == 1) { |
955 | /* Just truncate to fit. */ | 956 | /* Just truncate to fit. */ |
956 | const char *endPos; | 957 | const char *endPos; |
957 | tryAdvanceNoWrap_Text(default_FontId, | 958 | tryAdvanceNoWrap_Text(font, |
958 | range_String(text), | 959 | range_String(text), |
959 | avail - measure_Text(default_FontId, "...").advance.x, | 960 | avail - measure_Text(font, "...").advance.x, |
960 | &endPos); | 961 | &endPos); |
961 | updateText_LabelWidget( | 962 | updateText_LabelWidget( |
962 | tabButton, | 963 | tabButton, |
@@ -1619,7 +1620,7 @@ static void parseUser_DocumentWidget_(iDocumentWidget *d) { | |||
1619 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { | 1620 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { |
1620 | iUnused(data); | 1621 | iUnused(data); |
1621 | if (!isEmpty_Range(&run->text)) { | 1622 | if (!isEmpty_Range(&run->text)) { |
1622 | cache_Text(run->textParams.font, run->text); | 1623 | cache_Text(run->font, run->text); |
1623 | } | 1624 | } |
1624 | } | 1625 | } |
1625 | 1626 | ||
@@ -4020,14 +4021,14 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4020 | contains_Range(&mark, run->text.start))) { | 4021 | contains_Range(&mark, run->text.start))) { |
4021 | int x = 0; | 4022 | int x = 0; |
4022 | if (!*isInside) { | 4023 | if (!*isInside) { |
4023 | x = measureRange_Text(run->textParams.font, | 4024 | x = measureRange_Text(run->font, |
4024 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) | 4025 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) |
4025 | .advance.x; | 4026 | .advance.x; |
4026 | } | 4027 | } |
4027 | int w = width_Rect(run->visBounds) - x; | 4028 | int w = width_Rect(run->visBounds) - x; |
4028 | if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) { | 4029 | if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) { |
4029 | w = measureRange_Text( | 4030 | w = measureRange_Text( |
4030 | run->textParams.font, | 4031 | run->font, |
4031 | !*isInside ? mark | 4032 | !*isInside ? mark |
4032 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }) | 4033 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }) |
4033 | .advance.x; | 4034 | .advance.x; |
@@ -4083,15 +4084,15 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 | |||
4083 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | 4084 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); |
4084 | if (icon) { | 4085 | if (icon) { |
4085 | appendChar_String(&str, icon); | 4086 | appendChar_String(&str, icon); |
4086 | const iRect iconRect = visualBounds_Text(run->textParams.font, range_String(&str)); | 4087 | const iRect iconRect = visualBounds_Text(run->font, range_String(&str)); |
4087 | drawRange_Text( | 4088 | drawRange_Text( |
4088 | run->textParams.font, | 4089 | run->font, |
4089 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->textParams.font) / 2), | 4090 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), |
4090 | tmBannerIcon_ColorId, | 4091 | tmBannerIcon_ColorId, |
4091 | range_String(&str)); | 4092 | range_String(&str)); |
4092 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | 4093 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; |
4093 | } | 4094 | } |
4094 | drawRange_Text(run->textParams.font, | 4095 | drawRange_Text(run->font, |
4095 | bpos, | 4096 | bpos, |
4096 | tmBannerTitle_ColorId, | 4097 | tmBannerTitle_ColorId, |
4097 | bannerText_DocumentWidget_(d->widget)); | 4098 | bannerText_DocumentWidget_(d->widget)); |
@@ -4198,7 +4199,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4198 | /* Media UIs are drawn afterwards as a dynamic overlay. */ | 4199 | /* Media UIs are drawn afterwards as a dynamic overlay. */ |
4199 | return; | 4200 | return; |
4200 | } | 4201 | } |
4201 | enum iColorId fg = run->textParams.color; | 4202 | enum iColorId fg = run->color; |
4202 | const iGmDocument *doc = d->widget->doc; | 4203 | const iGmDocument *doc = d->widget->doc; |
4203 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); | 4204 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); |
4204 | /* Hover state of a link. */ | 4205 | /* Hover state of a link. */ |
@@ -4265,10 +4266,10 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4265 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); | 4266 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); |
4266 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); | 4267 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); |
4267 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); | 4268 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); |
4268 | drawWrapRange_Text(run->textParams.font, | 4269 | drawWrapRange_Text(run->font, |
4269 | add_I2(visPos, margin), | 4270 | add_I2(visPos, margin), |
4270 | run->visBounds.size.x - 2 * margin.x, | 4271 | run->visBounds.size.x - 2 * margin.x, |
4271 | run->textParams.color, | 4272 | run->color, |
4272 | run->text); | 4273 | run->text); |
4273 | } | 4274 | } |
4274 | else if (run->flags & siteBanner_GmRunFlag) { | 4275 | else if (run->flags & siteBanner_GmRunFlag) { |
@@ -4287,17 +4288,17 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4287 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); | 4288 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); |
4288 | if (ordChar) { | 4289 | if (ordChar) { |
4289 | const char *circle = "\u25ef"; /* Large Circle */ | 4290 | const char *circle = "\u25ef"; /* Large Circle */ |
4290 | const int circleFont = defaultContentRegular_FontId; | 4291 | const int circleFont = FONT_ID(default_FontId, regular_FontStyle, contentRegular_FontSize); |
4291 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), | 4292 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), |
4292 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; | 4293 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; |
4293 | drawRange_Text( | 4294 | drawRange_Text( |
4294 | circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); | 4295 | circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); |
4295 | iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); | 4296 | iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); |
4296 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); | 4297 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); |
4297 | drawCentered_Text(defaultContentSmall_FontId, | 4298 | drawCentered_Text(FONT_ID(default_FontId, regular_FontStyle, contentSmall_FontSize), |
4298 | circleArea, | 4299 | circleArea, |
4299 | iTrue, | 4300 | iTrue, |
4300 | tmQuote_ColorId, | 4301 | tmQuote_ColorId, |
4301 | "%lc", | 4302 | "%lc", |
4302 | (int) ordChar); | 4303 | (int) ordChar); |
4303 | goto runDrawn; | 4304 | goto runDrawn; |
@@ -4307,15 +4308,15 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4307 | if (run->flags & quoteBorder_GmRunFlag) { | 4308 | if (run->flags & quoteBorder_GmRunFlag) { |
4308 | drawVLine_Paint(&d->paint, | 4309 | drawVLine_Paint(&d->paint, |
4309 | addX_I2(visPos, | 4310 | addX_I2(visPos, |
4310 | !run->textParams.isRTL | 4311 | !run->isRTL |
4311 | ? -gap_Text * 5 / 2 | 4312 | ? -gap_Text * 5 / 2 |
4312 | : (width_Rect(run->visBounds) + gap_Text * 5 / 2)), | 4313 | : (width_Rect(run->visBounds) + gap_Text * 5 / 2)), |
4313 | height_Rect(run->visBounds), | 4314 | height_Rect(run->visBounds), |
4314 | tmQuoteIcon_ColorId); | 4315 | tmQuoteIcon_ColorId); |
4315 | } | 4316 | } |
4316 | drawBoundRange_Text(run->textParams.font, | 4317 | drawBoundRange_Text(run->font, |
4317 | visPos, | 4318 | visPos, |
4318 | (run->textParams.isRTL ? -1 : 1) * width_Rect(run->visBounds), | 4319 | (run->isRTL ? -1 : 1) * width_Rect(run->visBounds), |
4319 | fg, | 4320 | fg, |
4320 | run->text); | 4321 | run->text); |
4321 | runDrawn:; | 4322 | runDrawn:; |
@@ -4431,7 +4432,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4431 | append_String(&str, collect_String(format_Date(&date, "%b %d"))); | 4432 | append_String(&str, collect_String(format_Date(&date, "%b %d"))); |
4432 | } | 4433 | } |
4433 | if (!isEmpty_String(&str)) { | 4434 | if (!isEmpty_String(&str)) { |
4434 | if (run->textParams.isRTL) { | 4435 | if (run->isRTL) { |
4435 | appendCStr_String(&str, " \u2014 "); | 4436 | appendCStr_String(&str, " \u2014 "); |
4436 | } | 4437 | } |
4437 | else { | 4438 | else { |
@@ -4440,7 +4441,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4440 | const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)).bounds.size; | 4441 | const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)).bounds.size; |
4441 | int tx = topRight_Rect(linkRect).x; | 4442 | int tx = topRight_Rect(linkRect).x; |
4442 | const char *msg = cstr_String(&str); | 4443 | const char *msg = cstr_String(&str); |
4443 | if (run->textParams.isRTL) { | 4444 | if (run->isRTL) { |
4444 | tx = topLeft_Rect(linkRect).x - textSize.x; | 4445 | tx = topLeft_Rect(linkRect).x - textSize.x; |
4445 | } | 4446 | } |
4446 | if (tx + textSize.x > right_Rect(d->widgetBounds)) { | 4447 | if (tx + textSize.x > right_Rect(d->widgetBounds)) { |
@@ -4909,7 +4910,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4909 | } | 4910 | } |
4910 | /* Pinch zoom indicator. */ | 4911 | /* Pinch zoom indicator. */ |
4911 | if (d->flags & pinchZoom_DocumentWidgetFlag) { | 4912 | if (d->flags & pinchZoom_DocumentWidgetFlag) { |
4912 | const int font = defaultLargeBold_FontId; | 4913 | const int font = uiLabelLargeBold_FontId; |
4913 | const int height = lineHeight_Text(font) * 2; | 4914 | const int height = lineHeight_Text(font) * 2; |
4914 | const iInt2 size = init_I2(height * 2, height); | 4915 | const iInt2 size = init_I2(height * 2, height); |
4915 | const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size }; | 4916 | const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size }; |
diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c index b622a554..22552027 100644 --- a/src/ui/mediaui.c +++ b/src/ui/mediaui.c | |||
@@ -86,7 +86,7 @@ static int drawSevenSegmentTime_(iInt2 pos, int color, int align, int seconds) { | |||
86 | const int hours = seconds / 3600; | 86 | const int hours = seconds / 3600; |
87 | const int mins = (seconds / 60) % 60; | 87 | const int mins = (seconds / 60) % 60; |
88 | const int secs = seconds % 60; | 88 | const int secs = seconds % 60; |
89 | const int font = defaultBig_FontId; | 89 | const int font = uiLabelBig_FontId; |
90 | iString num; | 90 | iString num; |
91 | init_String(&num); | 91 | init_String(&num); |
92 | if (hours) { | 92 | if (hours) { |
@@ -123,7 +123,7 @@ void draw_PlayerUI(iPlayerUI *d, iPaint *p) { | |||
123 | drawPlayerButton_( | 123 | drawPlayerButton_( |
124 | p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId); | 124 | p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId); |
125 | } | 125 | } |
126 | const int hgt = lineHeight_Text(defaultBig_FontId); | 126 | const int hgt = lineHeight_Text(uiLabelBig_FontId); |
127 | const int yMid = mid_Rect(d->scrubberRect).y; | 127 | const int yMid = mid_Rect(d->scrubberRect).y; |
128 | const float playTime = time_Player(d->player); | 128 | const float playTime = time_Player(d->player); |
129 | const float totalTime = duration_Player(d->player); | 129 | const float totalTime = duration_Player(d->player); |
diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 3cb6e631..f11769f5 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c | |||
@@ -48,11 +48,11 @@ static iBool isSideBySideLayout_(void) { | |||
48 | } | 48 | } |
49 | 49 | ||
50 | static enum iFontId labelFont_(void) { | 50 | static enum iFontId labelFont_(void) { |
51 | return deviceType_App() == phone_AppDeviceType ? defaultBig_FontId : defaultMedium_FontId; | 51 | return deviceType_App() == phone_AppDeviceType ? uiLabelBig_FontId : uiLabelMedium_FontId; |
52 | } | 52 | } |
53 | 53 | ||
54 | static enum iFontId labelBoldFont_(void) { | 54 | static enum iFontId labelBoldFont_(void) { |
55 | return deviceType_App() == phone_AppDeviceType ? defaultBigBold_FontId : defaultMediumBold_FontId; | 55 | return deviceType_App() == phone_AppDeviceType ? uiLabelBigBold_FontId : uiLabelMediumBold_FontId; |
56 | } | 56 | } |
57 | 57 | ||
58 | static void updatePanelSheetMetrics_(iWidget *sheet) { | 58 | static void updatePanelSheetMetrics_(iWidget *sheet) { |
@@ -546,7 +546,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) { | |||
546 | updateSize_LabelWidget(button); | 546 | updateSize_LabelWidget(button); |
547 | } | 547 | } |
548 | setId_Widget(as_Widget(button), radId); | 548 | setId_Widget(as_Widget(button), radId); |
549 | setFont_LabelWidget(button, defaultMedium_FontId); | 549 | setFont_LabelWidget(button, uiLabelMedium_FontId); |
550 | addChildFlags_Widget(widget, iClob(button), flags); | 550 | addChildFlags_Widget(widget, iClob(button), flags); |
551 | } | 551 | } |
552 | } | 552 | } |
@@ -1056,7 +1056,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget, | |||
1056 | iForEach(ObjectList, sub, children_Widget(value)) { | 1056 | iForEach(ObjectList, sub, children_Widget(value)) { |
1057 | if (isInstance_Object(sub.object, &Class_LabelWidget)) { | 1057 | if (isInstance_Object(sub.object, &Class_LabelWidget)) { |
1058 | iLabelWidget *opt = sub.object; | 1058 | iLabelWidget *opt = sub.object; |
1059 | setFont_LabelWidget(opt, defaultMedium_FontId); | 1059 | setFont_LabelWidget(opt, uiLabelMedium_FontId); |
1060 | setFlags_Widget(as_Widget(opt), noBackground_WidgetFlag, iTrue); | 1060 | setFlags_Widget(as_Widget(opt), noBackground_WidgetFlag, iTrue); |
1061 | } | 1061 | } |
1062 | } | 1062 | } |
diff --git a/src/ui/root.c b/src/ui/root.c index 63cce62d..a2e6062f 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -447,7 +447,7 @@ static void updateNavBarIdentity_(iWidget *navBar) { | |||
447 | if (toolName) { | 447 | if (toolName) { |
448 | setOutline_LabelWidget(toolButton, ident == NULL); | 448 | setOutline_LabelWidget(toolButton, ident == NULL); |
449 | updateTextCStr_LabelWidget(toolName, subjectName ? cstr_String(subjectName) : ""); | 449 | updateTextCStr_LabelWidget(toolName, subjectName ? cstr_String(subjectName) : ""); |
450 | setFont_LabelWidget(toolButton, subjectName ? defaultMedium_FontId : uiLabelLarge_FontId); | 450 | setFont_LabelWidget(toolButton, subjectName ? uiLabelMedium_FontId : uiLabelLarge_FontId); |
451 | arrange_Widget(parent_Widget(toolButton)); | 451 | arrange_Widget(parent_Widget(toolButton)); |
452 | } | 452 | } |
453 | } | 453 | } |
@@ -996,7 +996,7 @@ void updateMetrics_Root(iRoot *d) { | |||
996 | const iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); | 996 | const iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); |
997 | const iWidget *viewButton = findChild_Widget(d->widget, "toolbar.view"); | 997 | const iWidget *viewButton = findChild_Widget(d->widget, "toolbar.view"); |
998 | const iWidget *idButton = findChild_Widget(toolBar, "toolbar.ident"); | 998 | const iWidget *idButton = findChild_Widget(toolBar, "toolbar.ident"); |
999 | const int font = defaultTiny_FontId; | 999 | const int font = uiLabelTiny_FontId; |
1000 | setFont_LabelWidget(idName, font); | 1000 | setFont_LabelWidget(idName, font); |
1001 | setPos_Widget(as_Widget(idName), | 1001 | setPos_Widget(as_Widget(idName), |
1002 | windowToLocal_Widget(as_Widget(idName), | 1002 | windowToLocal_Widget(as_Widget(idName), |
@@ -1125,7 +1125,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1125 | iClob(newIcon_LabelWidget("\U0001f513", SDLK_i, KMOD_PRIMARY, "document.info")), | 1125 | iClob(newIcon_LabelWidget("\U0001f513", SDLK_i, KMOD_PRIMARY, "document.info")), |
1126 | embedFlags | moveToParentLeftEdge_WidgetFlag); | 1126 | embedFlags | moveToParentLeftEdge_WidgetFlag); |
1127 | setId_Widget(as_Widget(lock), "navbar.lock"); | 1127 | setId_Widget(as_Widget(lock), "navbar.lock"); |
1128 | setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize); | 1128 | // setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize); |
1129 | updateTextCStr_LabelWidget(lock, "\U0001f512"); | 1129 | updateTextCStr_LabelWidget(lock, "\U0001f512"); |
1130 | } | 1130 | } |
1131 | /* Button for clearing the URL bar contents. */ { | 1131 | /* Button for clearing the URL bar contents. */ { |
@@ -1134,7 +1134,8 @@ void createUserInterface_Root(iRoot *d) { | |||
1134 | iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")), | 1134 | iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")), |
1135 | hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag | tight_WidgetFlag); | 1135 | hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag | tight_WidgetFlag); |
1136 | setId_Widget(as_Widget(clear), "navbar.clear"); | 1136 | setId_Widget(as_Widget(clear), "navbar.clear"); |
1137 | setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize); | 1137 | // setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize); |
1138 | setFont_LabelWidget(clear, uiLabelSymbols_FontId); | ||
1138 | // setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse); | 1139 | // setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse); |
1139 | // setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId); | 1140 | // setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId); |
1140 | } | 1141 | } |
@@ -1149,7 +1150,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1149 | iLabelWidget *queryInd = new_LabelWidget("${status.query} " return_Icon, NULL); | 1150 | iLabelWidget *queryInd = new_LabelWidget("${status.query} " return_Icon, NULL); |
1150 | setId_Widget(as_Widget(queryInd), "input.indicator.search"); | 1151 | setId_Widget(as_Widget(queryInd), "input.indicator.search"); |
1151 | setTextColor_LabelWidget(queryInd, uiTextAction_ColorId); | 1152 | setTextColor_LabelWidget(queryInd, uiTextAction_ColorId); |
1152 | setFont_LabelWidget(queryInd, defaultSmall_FontId); | 1153 | setFont_LabelWidget(queryInd, uiLabelSmall_FontId); |
1153 | setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId); | 1154 | setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId); |
1154 | setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId); | 1155 | setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId); |
1155 | // setAlignVisually_LabelWidget(queryInd, iTrue); | 1156 | // setAlignVisually_LabelWidget(queryInd, iTrue); |
@@ -1162,7 +1163,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1162 | iLabelWidget *fprog = new_LabelWidget("", NULL); | 1163 | iLabelWidget *fprog = new_LabelWidget("", NULL); |
1163 | setId_Widget(as_Widget(fprog), "feeds.progress"); | 1164 | setId_Widget(as_Widget(fprog), "feeds.progress"); |
1164 | setTextColor_LabelWidget(fprog, uiTextCaution_ColorId); | 1165 | setTextColor_LabelWidget(fprog, uiTextCaution_ColorId); |
1165 | setFont_LabelWidget(fprog, defaultSmall_FontId); | 1166 | setFont_LabelWidget(fprog, uiLabelSmall_FontId); |
1166 | setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId); | 1167 | setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId); |
1167 | // setAlignVisually_LabelWidget(fprog, iTrue); | 1168 | // setAlignVisually_LabelWidget(fprog, iTrue); |
1168 | setNoAutoMinHeight_LabelWidget(fprog, iTrue); | 1169 | setNoAutoMinHeight_LabelWidget(fprog, iTrue); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 057ff614..42661f6b 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -188,7 +188,7 @@ static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const cha | |||
188 | // extraPadding_WidgetFlag : 0) | | 188 | // extraPadding_WidgetFlag : 0) | |
189 | flags); | 189 | flags); |
190 | setFont_LabelWidget(btn, deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide | 190 | setFont_LabelWidget(btn, deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide |
191 | ? defaultBig_FontId | 191 | ? uiLabelBig_FontId |
192 | : d->buttonFont); | 192 | : d->buttonFont); |
193 | checkIcon_LabelWidget(btn); | 193 | checkIcon_LabelWidget(btn); |
194 | return btn; | 194 | return btn; |
@@ -742,7 +742,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
742 | d->itemFonts[1] = uiContentBold_FontId; | 742 | d->itemFonts[1] = uiContentBold_FontId; |
743 | #if defined (iPlatformMobile) | 743 | #if defined (iPlatformMobile) |
744 | if (deviceType_App() == phone_AppDeviceType) { | 744 | if (deviceType_App() == phone_AppDeviceType) { |
745 | d->itemFonts[0] = defaultBig_FontId; | 745 | d->itemFonts[0] = uiLabelBig_FontId; |
746 | d->itemFonts[1] = defaultBigBold_FontId; | 746 | d->itemFonts[1] = defaultBigBold_FontId; |
747 | } | 747 | } |
748 | d->widthAsGaps = 73.0f; | 748 | d->widthAsGaps = 73.0f; |
@@ -774,7 +774,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
774 | format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))), | 774 | format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))), |
775 | frameless_WidgetFlag | noBackground_WidgetFlag); | 775 | frameless_WidgetFlag | noBackground_WidgetFlag); |
776 | } | 776 | } |
777 | setButtonFont_SidebarWidget(d, isPhone ? defaultBig_FontId : uiLabel_FontId); | 777 | setButtonFont_SidebarWidget(d, isPhone ? uiLabelBig_FontId : uiLabel_FontId); |
778 | addChildFlags_Widget(vdiv, | 778 | addChildFlags_Widget(vdiv, |
779 | iClob(buttons), | 779 | iClob(buttons), |
780 | arrangeHorizontal_WidgetFlag | | 780 | arrangeHorizontal_WidgetFlag | |
@@ -936,8 +936,8 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) { | |||
936 | if (deviceType_App() == phone_AppDeviceType) { | 936 | if (deviceType_App() == phone_AppDeviceType) { |
937 | /* Change font size depending on orientation. */ | 937 | /* Change font size depending on orientation. */ |
938 | const int fonts[2] = { | 938 | const int fonts[2] = { |
939 | isPortrait_App() ? defaultBig_FontId : uiContent_FontId, | 939 | isPortrait_App() ? uiLabelBig_FontId : uiContent_FontId, |
940 | isPortrait_App() ? defaultBigBold_FontId : uiContentBold_FontId | 940 | isPortrait_App() ? uiLabelBigBold_FontId : uiContentBold_FontId |
941 | }; | 941 | }; |
942 | if (d->itemFonts[0] != fonts[0]) { | 942 | if (d->itemFonts[0] != fonts[0]) { |
943 | d->itemFonts[0] = fonts[0]; | 943 | d->itemFonts[0] = fonts[0]; |
@@ -945,7 +945,7 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) { | |||
945 | // updateMetrics_SidebarWidget_(d); | 945 | // updateMetrics_SidebarWidget_(d); |
946 | updateItemHeight_SidebarWidget_(d); | 946 | updateItemHeight_SidebarWidget_(d); |
947 | } | 947 | } |
948 | setButtonFont_SidebarWidget(d, isPortrait_App() ? defaultBig_FontId : uiLabel_FontId); | 948 | setButtonFont_SidebarWidget(d, isPortrait_App() ? uiLabelBig_FontId : uiLabel_FontId); |
949 | } | 949 | } |
950 | const iBool isTight = | 950 | const iBool isTight = |
951 | (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth); | 951 | (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth); |
@@ -1962,7 +1962,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
1962 | deinit_String(&str); | 1962 | deinit_String(&str); |
1963 | const iInt2 textPos = addY_I2(topRight_Rect(iconArea), (itemHeight - lineHeight_Text(font)) / 2); | 1963 | const iInt2 textPos = addY_I2(topRight_Rect(iconArea), (itemHeight - lineHeight_Text(font)) / 2); |
1964 | drawRange_Text(font, textPos, fg, range_String(&d->label)); | 1964 | drawRange_Text(font, textPos, fg, range_String(&d->label)); |
1965 | const int metaFont = default_FontId; | 1965 | const int metaFont = uiLabel_FontId; |
1966 | const int metaIconWidth = 4.5f * gap_UI; | 1966 | const int metaIconWidth = 4.5f * gap_UI; |
1967 | const iInt2 metaPos = | 1967 | const iInt2 metaPos = |
1968 | init_I2(right_Rect(itemRect) - | 1968 | init_I2(right_Rect(itemRect) - |
@@ -2045,7 +2045,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2045 | const int indent = 1.4f * lineHeight_Text(font); | 2045 | const int indent = 1.4f * lineHeight_Text(font); |
2046 | addv_I2(&cPos, | 2046 | addv_I2(&cPos, |
2047 | init_I2(3 * gap_UI, | 2047 | init_I2(3 * gap_UI, |
2048 | (itemHeight - lineHeight_Text(default_FontId) * 2 - lineHeight_Text(font)) / | 2048 | (itemHeight - lineHeight_Text(uiLabel_FontId) * 2 - lineHeight_Text(font)) / |
2049 | 2)); | 2049 | 2)); |
2050 | const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId | 2050 | const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId |
2051 | : uiTextFramelessHover_ColorId) | 2051 | : uiTextFramelessHover_ColorId) |
@@ -2064,7 +2064,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2064 | add_I2(cPos, init_I2(indent, 0)), | 2064 | add_I2(cPos, init_I2(indent, 0)), |
2065 | fg, | 2065 | fg, |
2066 | range_String(&d->label)); | 2066 | range_String(&d->label)); |
2067 | drawRange_Text(default_FontId, | 2067 | drawRange_Text(uiLabel_FontId, |
2068 | add_I2(cPos, init_I2(indent, lineHeight_Text(font))), | 2068 | add_I2(cPos, init_I2(indent, lineHeight_Text(font))), |
2069 | metaFg, | 2069 | metaFg, |
2070 | range_String(&d->meta)); | 2070 | range_String(&d->meta)); |
diff --git a/src/ui/text.c b/src/ui/text.c index e3f7f7c4..86fdc084 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -123,40 +123,57 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
123 | 123 | ||
124 | static iGlyph *glyph_Font_(iFont *d, iChar ch); | 124 | static iGlyph *glyph_Font_(iFont *d, iChar ch); |
125 | 125 | ||
126 | struct Impl_Font { | 126 | iDeclareType(GlyphTable) |
127 | iBlock * data; | 127 | |
128 | enum iTextFont family; | 128 | struct Impl_GlyphTable { |
129 | stbtt_fontinfo font; | 129 | iHash glyphs; /* key is glyph index in the font */ |
130 | float xScale, yScale; | 130 | /* TODO: `glyphs` does not need to be a Hash. |
131 | int vertOffset; /* offset due to scaling */ | 131 | We could lazily allocate an array with glyphCount elements instead. */ |
132 | int height; | ||
133 | int baseline; | ||
134 | iHash glyphs; /* key is glyph index in the font */ /* TODO: does not need to be a Hash */ | ||
135 | iBool isMonospaced; | ||
136 | float emAdvance; | ||
137 | enum iFontSize sizeId; /* used to look up different fonts of matching size */ | ||
138 | uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ | 132 | uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ |
139 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) | ||
140 | hb_blob_t * hbBlob; /* raw TrueType data */ | ||
141 | hb_face_t * hbFace; | ||
142 | hb_font_t * hbMainFont; | ||
143 | hb_font_t * hbFont; /* may be a sub-font with customized font metrics */ | ||
144 | #endif | ||
145 | }; | 133 | }; |
146 | 134 | ||
147 | static iFont *font_Text_(enum iFontId id); | 135 | static void clearGlyphs_Font_(iGlyphTable *d) { |
148 | 136 | iForEach(Hash, i, &d->glyphs) { | |
149 | #if 0 | 137 | delete_Glyph((iGlyph *) i.value); |
150 | static hb_position_t hbGlyphHKernForNunito_(hb_font_t *font, void *fontData, | 138 | } |
151 | hb_codepoint_t firstGlyph, hb_codepoint_t secondGlyph, | 139 | clear_Hash(&d->glyphs); |
152 | void *userData) { | ||
153 | return 100; | ||
154 | } | 140 | } |
155 | #endif | ||
156 | 141 | ||
157 | static void init_Font(iFont *d, const iBlock *data, int height, float scale, | 142 | static void init_GlyphTable(iGlyphTable *d) { |
158 | enum iFontSize sizeId, iBool isMonospaced) { | ||
159 | init_Hash(&d->glyphs); | 143 | init_Hash(&d->glyphs); |
144 | memset(d->indexTable, 0xff, sizeof(d->indexTable)); | ||
145 | } | ||
146 | |||
147 | static void deinit_GlyphTable(iGlyphTable *d) { | ||
148 | clearGlyphs_Font_(d); | ||
149 | deinit_Hash(&d->glyphs); | ||
150 | } | ||
151 | |||
152 | iDefineTypeConstruction(GlyphTable) | ||
153 | |||
154 | struct Impl_Font { | ||
155 | const iFontSpec *fontSpec; | ||
156 | const iFontFile *fontFile; | ||
157 | int height; | ||
158 | int baseline; | ||
159 | int vertOffset; /* offset due to glyph scaling */ | ||
160 | float xScale, yScale; | ||
161 | float emAdvance; | ||
162 | iGlyphTable * table; | ||
163 | }; | ||
164 | |||
165 | iLocalDef iBool isMonospaced_Font(const iFont *d) { | ||
166 | return (d->fontSpec->flags & monospace_FontSpecFlag) != 0; | ||
167 | } | ||
168 | |||
169 | static iFont *font_Text_(enum iFontId id); | ||
170 | |||
171 | static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *fontFile, | ||
172 | enum iFontSize sizeId, int height) { | ||
173 | d->fontSpec = fontSpec; | ||
174 | d->fontFile = fontFile; | ||
175 | /* TODO: Nunito kerning fixes need to be a font parameter of its own. */ | ||
176 | #if 0 | ||
160 | d->data = NULL; | 177 | d->data = NULL; |
161 | d->family = undefined_TextFont; | 178 | d->family = undefined_TextFont; |
162 | /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ | 179 | /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ |
@@ -177,95 +194,49 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, | |||
177 | data == &fontSmolEmojiRegular_Embedded) { | 194 | data == &fontSmolEmojiRegular_Embedded) { |
178 | d->family = emojiAndSymbols_TextFont; | 195 | d->family = emojiAndSymbols_TextFont; |
179 | } | 196 | } |
180 | d->isMonospaced = isMonospaced; | 197 | #endif |
198 | // d->isMonospaced = (fontSpec->flags & monospace_FontSpecFlag) != 0; | ||
181 | d->height = height; | 199 | d->height = height; |
182 | iZap(d->font); | 200 | //iZap(d->font); |
183 | stbtt_InitFont(&d->font, constData_Block(data), 0); | 201 | // stbtt_InitFont(&d->font, constData_Block(data), 0); |
184 | int ascent, descent, emAdv; | 202 | // int ascent, descent, emAdv; |
185 | stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); | 203 | // stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); |
186 | stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL); | 204 | // stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL); |
187 | d->xScale = d->yScale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; | 205 | const float scale = fontSpec->scaling; |
188 | if (d->isMonospaced) { | 206 | d->xScale = d->yScale = scaleForPixelHeight_FontFile(fontFile, height) * scale; |
207 | if (isMonospaced_Font(d)) { | ||
189 | /* It is important that monospaced fonts align 1:1 with the pixel grid so that | 208 | /* It is important that monospaced fonts align 1:1 with the pixel grid so that |
190 | box-drawing characters don't have partially occupied edge pixels, leading to seams | 209 | box-drawing characters don't have partially occupied edge pixels, leading to seams |
191 | between adjacent glyphs. */ | 210 | between adjacent glyphs. */ |
192 | const float advance = (float) emAdv * d->xScale; | 211 | const float advance = (float) fontFile->emAdvance * d->xScale; |
193 | if (advance > 4) { /* not too tiny */ | 212 | if (advance > 4) { /* not too tiny */ |
194 | d->xScale *= floorf(advance) / advance; | 213 | d->xScale *= floorf(advance) / advance; |
195 | } | 214 | } |
196 | } | 215 | } |
197 | d->emAdvance = emAdv * d->xScale; | 216 | d->emAdvance = fontFile->emAdvance * d->xScale; |
198 | d->baseline = ascent * d->yScale; | 217 | d->baseline = fontFile->ascent * d->yScale; |
199 | d->vertOffset = height * (1.0f - scale) / 2; | 218 | d->vertOffset = height * (1.0f - scale) / 2 * fontSpec->vertOffset; |
200 | /* Custom tweaks. */ | 219 | d->table = NULL; |
201 | if (data == &fontNotoSansSymbolsRegular_Embedded) { | ||
202 | d->vertOffset *= 1.2f; | ||
203 | } | ||
204 | else if (data == &fontNotoSansSymbols2Regular_Embedded) { | ||
205 | d->vertOffset /= 2; | ||
206 | } | ||
207 | else if (data == &fontNotoEmojiRegular_Embedded) { | ||
208 | //d->vertOffset -= height / 30; | ||
209 | } | ||
210 | d->sizeId = sizeId; | ||
211 | memset(d->indexTable, 0xff, sizeof(d->indexTable)); | ||
212 | #if defined(LAGRANGE_ENABLE_HARFBUZZ) | ||
213 | /* HarfBuzz will read the font data. */ { | ||
214 | d->hbBlob = hb_blob_create(constData_Block(data), size_Block(data), | ||
215 | HB_MEMORY_MODE_READONLY, NULL, NULL); | ||
216 | d->hbFace = hb_face_create(d->hbBlob, 0); | ||
217 | d->hbMainFont = hb_font_create(d->hbFace); | ||
218 | #if 0 | ||
219 | /* TODO: The custom kerning function doesn't get called? | ||
220 | Maybe HarfBuzz needs FreeType to do kerning? */ | ||
221 | if (d->family == nunito_TextFont) { | ||
222 | /* Customize the kerning of Nunito. */ | ||
223 | d->hbFont = hb_font_create_sub_font(d->hbMainFont); | ||
224 | hb_font_funcs_t *ffs = hb_font_funcs_create(); | ||
225 | hb_font_funcs_set_glyph_h_kerning_func(ffs, hbGlyphHKernForNunito_, d, NULL); | ||
226 | hb_font_set_funcs(d->hbFont, ffs, NULL, NULL); | ||
227 | hb_font_funcs_destroy(ffs); | ||
228 | } | ||
229 | else | ||
230 | #endif | ||
231 | { | ||
232 | d->hbFont = hb_font_reference(d->hbMainFont); | ||
233 | } | ||
234 | } | ||
235 | #endif | ||
236 | } | ||
237 | |||
238 | static void clearGlyphs_Font_(iFont *d) { | ||
239 | iForEach(Hash, i, &d->glyphs) { | ||
240 | delete_Glyph((iGlyph *) i.value); | ||
241 | } | ||
242 | clear_Hash(&d->glyphs); | ||
243 | } | 220 | } |
244 | 221 | ||
245 | static void deinit_Font(iFont *d) { | 222 | static void deinit_Font(iFont *d) { |
246 | #if defined(LAGRANGE_ENABLE_HARFBUZZ) | 223 | delete_GlyphTable(d->table); |
247 | /* HarfBuzz objects. */ { | ||
248 | hb_font_destroy(d->hbFont); | ||
249 | hb_font_destroy(d->hbMainFont); | ||
250 | hb_face_destroy(d->hbFace); | ||
251 | hb_blob_destroy(d->hbBlob); | ||
252 | } | ||
253 | #endif | ||
254 | clearGlyphs_Font_(d); | ||
255 | deinit_Hash(&d->glyphs); | ||
256 | delete_Block(d->data); | ||
257 | } | 224 | } |
258 | 225 | ||
259 | static uint32_t glyphIndex_Font_(iFont *d, iChar ch) { | 226 | static uint32_t glyphIndex_Font_(iFont *d, iChar ch) { |
260 | /* TODO: Add a small cache of ~5 most recently found indices. */ | 227 | /* TODO: Add a small cache of ~5 most recently found indices. */ |
261 | const size_t entry = ch - 32; | 228 | const size_t entry = ch - 32; |
262 | if (entry < iElemCount(d->indexTable)) { | 229 | if (!d->table) { |
263 | if (d->indexTable[entry] == ~0u) { | 230 | d->table = new_GlyphTable(); |
264 | d->indexTable[entry] = stbtt_FindGlyphIndex(&d->font, ch); | 231 | } |
232 | iGlyphTable *table = d->table; | ||
233 | if (entry < iElemCount(table->indexTable)) { | ||
234 | if (table->indexTable[entry] == ~0u) { | ||
235 | table->indexTable[entry] = findGlyphIndex_FontFile(d->fontFile, ch); | ||
265 | } | 236 | } |
266 | return d->indexTable[entry]; | 237 | return table->indexTable[entry]; |
267 | } | 238 | } |
268 | return stbtt_FindGlyphIndex(&d->font, ch); | 239 | return findGlyphIndex_FontFile(d->fontFile, ch); |
269 | } | 240 | } |
270 | 241 | ||
271 | /*----------------------------------------------------------------------------------------------*/ | 242 | /*----------------------------------------------------------------------------------------------*/ |
@@ -282,7 +253,8 @@ struct Impl_Text { | |||
282 | enum iTextFont contentFont; | 253 | enum iTextFont contentFont; |
283 | enum iTextFont headingFont; | 254 | enum iTextFont headingFont; |
284 | float contentFontSize; | 255 | float contentFontSize; |
285 | iFont fonts[max_FontId]; | 256 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ |
257 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ | ||
286 | SDL_Renderer * render; | 258 | SDL_Renderer * render; |
287 | SDL_Texture * cache; | 259 | SDL_Texture * cache; |
288 | iInt2 cacheSize; | 260 | iInt2 cacheSize; |
@@ -296,12 +268,49 @@ struct Impl_Text { | |||
296 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 268 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) |
297 | 269 | ||
298 | static iText *activeText_; | 270 | static iText *activeText_; |
299 | static iBlock *userFont_; | 271 | |
272 | static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId) { | ||
273 | #if defined (iPlatformMobile) | ||
274 | const float uiSize = fontSize_UI * 1.1f; | ||
275 | #else | ||
276 | const float uiSize = fontSize_UI; | ||
277 | #endif | ||
278 | const float textSize = fontSize_UI * d->contentFontSize; | ||
279 | // const float monoSize = textSize * 0.71f; | ||
280 | // const float smallMonoSize = monoSize * 0.8f; | ||
281 | if (spec->flags & override_FontSpecFlag && d->overrideFontId < 0) { | ||
282 | /* This is the highest priority override font. */ | ||
283 | d->overrideFontId = baseId; | ||
284 | } | ||
285 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { | ||
286 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { | ||
287 | init_Font(font_Text_(FONT_ID(baseId, style, sizeId)), | ||
288 | spec, | ||
289 | spec->styles[style], | ||
290 | sizeId, | ||
291 | (sizeId < contentRegular_FontSize ? uiSize : textSize) * scale_FontSize(sizeId)); | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | iLocalDef iFont *font_Text_(enum iFontId id) { | ||
297 | return at_Array(&activeText_->fonts, id & mask_FontId); | ||
298 | } | ||
299 | |||
300 | static enum iFontId fontId_Text_(const iFont *font) { | ||
301 | return (enum iFontId) (font - (const iFont *) constData_Array(&activeText_->fonts)); | ||
302 | } | ||
303 | |||
304 | iLocalDef enum iFontSize sizeId_Text_(const iFont *d) { | ||
305 | return fontId_Text_(d) % max_FontSize; | ||
306 | } | ||
307 | |||
308 | iLocalDef enum iFontStyle styleId_Text_(const iFont *d) { | ||
309 | return (fontId_Text_(d) / max_FontSize) % max_FontStyle; | ||
310 | } | ||
300 | 311 | ||
301 | static void initFonts_Text_(iText *d) { | 312 | static void initFonts_Text_(iText *d) { |
302 | const float textSize = fontSize_UI * d->contentFontSize; | 313 | #if 0 |
303 | const float monoSize = textSize * 0.71f; | ||
304 | const float smallMonoSize = monoSize * 0.8f; | ||
305 | const iBlock *regularFont = &fontNunitoRegular_Embedded; | 314 | const iBlock *regularFont = &fontNunitoRegular_Embedded; |
306 | const iBlock *boldFont = &fontNunitoBold_Embedded; | 315 | const iBlock *boldFont = &fontNunitoBold_Embedded; |
307 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; | 316 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; |
@@ -367,15 +376,10 @@ static void initFonts_Text_(iText *d) { | |||
367 | h12Font = &fontIosevkaTermExtended_Embedded; | 376 | h12Font = &fontIosevkaTermExtended_Embedded; |
368 | h3Font = &fontIosevkaTermExtended_Embedded; | 377 | h3Font = &fontIosevkaTermExtended_Embedded; |
369 | } | 378 | } |
370 | #if defined (iPlatformMobile) | ||
371 | const float uiSize = fontSize_UI * 1.1f; | ||
372 | #else | ||
373 | const float uiSize = fontSize_UI; | ||
374 | #endif | ||
375 | const struct { | 379 | const struct { |
376 | const iBlock *ttf; | 380 | const iFontFile *fontFile; |
377 | int size; | 381 | int size; /* pixels */ |
378 | float scaling; | 382 | // float scaling; |
379 | enum iFontSize sizeId; | 383 | enum iFontSize sizeId; |
380 | /* UI sizes: 1.0, 1.125, 1.333, 1.666 */ | 384 | /* UI sizes: 1.0, 1.125, 1.333, 1.666 */ |
381 | /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */ | 385 | /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */ |
@@ -433,8 +437,34 @@ static void initFonts_Text_(iText *d) { | |||
433 | DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f), | 437 | DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f), |
434 | // DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f), | 438 | // DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f), |
435 | }; | 439 | }; |
440 | #endif | ||
441 | /* The `fonts` array has precomputed scaling factors and other parameters in all sizes | ||
442 | and styles for each available font. Indices to `fonts` act as font runtime IDs. */ | ||
443 | /* First the mandatory fonts. */ | ||
444 | d->overrideFontId = -1; | ||
445 | resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */ | ||
446 | iAssert(auxiliary_FontId == documentHeading_FontId + maxVariants_Fonts); | ||
447 | setupFontVariants_Text_(d, findSpec_Fonts("default"), default_FontId); | ||
448 | setupFontVariants_Text_(d, findSpec_Fonts("iosevka"), monospace_FontId); | ||
449 | setupFontVariants_Text_(d, findSpec_Fonts("default"), documentBody_FontId); | ||
450 | setupFontVariants_Text_(d, findSpec_Fonts("default"), documentHeading_FontId); | ||
451 | /* Check if there are auxiliary fonts available and set those up, too. */ | ||
452 | iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) { | ||
453 | const iFontSpec *spec = s.ptr; | ||
454 | if (spec->flags & auxiliary_FontSpecFlag) { | ||
455 | const int fontId = size_Array(&d->fonts); | ||
456 | resize_Array(&d->fonts, fontId + maxVariants_Fonts); | ||
457 | setupFontVariants_Text_(d, spec, fontId); | ||
458 | } | ||
459 | } | ||
460 | /* test */ { | ||
461 | const iFont *h = font_Text_(preformatted_FontId); // FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize)); | ||
462 | printf("{%s} %d sz:%d st:%d\n", cstr_String(&h->fontSpec->name), h->height, sizeId_Text_(h), | ||
463 | styleId_Text_(h)); | ||
464 | } | ||
465 | #if 0 | ||
436 | iForIndices(i, fontData) { | 466 | iForIndices(i, fontData) { |
437 | iFont *font = &d->fonts[i]; | 467 | iFont *font = font_Text_(i); |
438 | init_Font(font, | 468 | init_Font(font, |
439 | fontData[i].ttf, | 469 | fontData[i].ttf, |
440 | fontData[i].size, | 470 | fontData[i].size, |
@@ -442,13 +472,15 @@ static void initFonts_Text_(iText *d) { | |||
442 | fontData[i].sizeId, | 472 | fontData[i].sizeId, |
443 | fontData[i].ttf == &fontIosevkaTermExtended_Embedded); | 473 | fontData[i].ttf == &fontIosevkaTermExtended_Embedded); |
444 | } | 474 | } |
475 | #endif | ||
445 | gap_Text = iRound(gap_UI * d->contentFontSize); | 476 | gap_Text = iRound(gap_UI * d->contentFontSize); |
446 | } | 477 | } |
447 | 478 | ||
448 | static void deinitFonts_Text_(iText *d) { | 479 | static void deinitFonts_Text_(iText *d) { |
449 | iForIndices(i, d->fonts) { | 480 | iForEach(Array, i, &d->fonts) { |
450 | deinit_Font(&d->fonts[i]); | 481 | deinit_Font(i.value); |
451 | } | 482 | } |
483 | clear_Array(&d->fonts); | ||
452 | } | 484 | } |
453 | 485 | ||
454 | static int maxGlyphHeight_Text_(const iText *d) { | 486 | static int maxGlyphHeight_Text_(const iText *d) { |
@@ -490,6 +522,7 @@ static void deinitCache_Text_(iText *d) { | |||
490 | SDL_DestroyTexture(d->cache); | 522 | SDL_DestroyTexture(d->cache); |
491 | } | 523 | } |
492 | 524 | ||
525 | #if 0 | ||
493 | void loadUserFonts_Text(void) { | 526 | void loadUserFonts_Text(void) { |
494 | if (userFont_) { | 527 | if (userFont_) { |
495 | delete_Block(userFont_); | 528 | delete_Block(userFont_); |
@@ -508,9 +541,12 @@ void loadUserFonts_Text(void) { | |||
508 | iRelease(f); | 541 | iRelease(f); |
509 | } | 542 | } |
510 | } | 543 | } |
544 | #endif | ||
511 | 545 | ||
512 | void init_Text(iText *d, SDL_Renderer *render) { | 546 | void init_Text(iText *d, SDL_Renderer *render) { |
513 | loadUserFonts_Text(); | 547 | iText *oldActive = activeText_; |
548 | activeText_ = d; | ||
549 | init_Array(&d->fonts, sizeof(iFont)); | ||
514 | d->contentFont = nunito_TextFont; | 550 | d->contentFont = nunito_TextFont; |
515 | d->headingFont = nunito_TextFont; | 551 | d->headingFont = nunito_TextFont; |
516 | d->contentFontSize = contentScale_Text_; | 552 | d->contentFontSize = contentScale_Text_; |
@@ -526,6 +562,7 @@ void init_Text(iText *d, SDL_Renderer *render) { | |||
526 | } | 562 | } |
527 | initCache_Text_(d); | 563 | initCache_Text_(d); |
528 | initFonts_Text_(d); | 564 | initFonts_Text_(d); |
565 | activeText_ = oldActive; | ||
529 | } | 566 | } |
530 | 567 | ||
531 | void deinit_Text(iText *d) { | 568 | void deinit_Text(iText *d) { |
@@ -534,6 +571,7 @@ void deinit_Text(iText *d) { | |||
534 | deinitCache_Text_(d); | 571 | deinitCache_Text_(d); |
535 | d->render = NULL; | 572 | d->render = NULL; |
536 | iRelease(d->ansiEscape); | 573 | iRelease(d->ansiEscape); |
574 | deinit_Array(&d->fonts); | ||
537 | } | 575 | } |
538 | 576 | ||
539 | void setCurrent_Text(iText *d) { | 577 | void setCurrent_Text(iText *d) { |
@@ -569,8 +607,8 @@ void setContentFontSize_Text(iText *d, float fontSizeFactor) { | |||
569 | 607 | ||
570 | static void resetCache_Text_(iText *d) { | 608 | static void resetCache_Text_(iText *d) { |
571 | deinitCache_Text_(d); | 609 | deinitCache_Text_(d); |
572 | for (int i = 0; i < max_FontId; i++) { | 610 | iForEach(Array, i, &d->fonts) { |
573 | clearGlyphs_Font_(&d->fonts[i]); | 611 | clearGlyphs_Font_(i.value); |
574 | } | 612 | } |
575 | initCache_Text_(d); | 613 | initCache_Text_(d); |
576 | } | 614 | } |
@@ -582,14 +620,10 @@ void resetFonts_Text(iText *d) { | |||
582 | initFonts_Text_(d); | 620 | initFonts_Text_(d); |
583 | } | 621 | } |
584 | 622 | ||
585 | iLocalDef iFont *font_Text_(enum iFontId id) { | ||
586 | return &activeText_->fonts[id & mask_FontId]; | ||
587 | } | ||
588 | |||
589 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { | 623 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { |
590 | int w, h; | 624 | int w, h; |
591 | uint8_t *bmp = stbtt_GetGlyphBitmapSubpixel( | 625 | uint8_t *bmp = rasterizeGlyph_FontFile(d->fontFile, d->xScale, d->yScale, xShift, glyphIndex, |
592 | &d->font, d->xScale, d->yScale, xShift, 0.0f, glyphIndex, &w, &h, 0, 0); | 626 | &w, &h); |
593 | SDL_Surface *surface8 = | 627 | SDL_Surface *surface8 = |
594 | SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); | 628 | SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); |
595 | SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE); | 629 | SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE); |
@@ -636,8 +670,8 @@ static iInt2 assignCachePos_Text_(iText *d, iInt2 size) { | |||
636 | static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { | 670 | static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { |
637 | iRect *glRect = &glyph->rect[hoff]; | 671 | iRect *glRect = &glyph->rect[hoff]; |
638 | int x0, y0, x1, y1; | 672 | int x0, y0, x1, y1; |
639 | stbtt_GetGlyphBitmapBoxSubpixel( | 673 | measureGlyph_FontFile(d->fontFile, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f, |
640 | &d->font, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f, 0.0f, &x0, &y0, &x1, &y1); | 674 | &x0, &y0, &x1, &y1); |
641 | glRect->size = init_I2(x1 - x0, y1 - y0); | 675 | glRect->size = init_I2(x1 - x0, y1 - y0); |
642 | /* Determine placement in the glyph cache texture, advancing in rows. */ | 676 | /* Determine placement in the glyph cache texture, advancing in rows. */ |
643 | glRect->pos = assignCachePos_Text_(activeText_, glRect->size); | 677 | glRect->pos = assignCachePos_Text_(activeText_, glRect->size); |
@@ -645,7 +679,7 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
645 | glyph->d[hoff].y += d->vertOffset; | 679 | glyph->d[hoff].y += d->vertOffset; |
646 | if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */ | 680 | if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */ |
647 | int adv; | 681 | int adv; |
648 | stbtt_GetGlyphHMetrics(&d->font, index_Glyph_(glyph), &adv, NULL); | 682 | stbtt_GetGlyphHMetrics(&d->fontFile->stbInfo, index_Glyph_(glyph), &adv, NULL); |
649 | glyph->advance = d->xScale * adv; | 683 | glyph->advance = d->xScale * adv; |
650 | } | 684 | } |
651 | } | 685 | } |
@@ -654,13 +688,18 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
654 | if (isVariationSelector_Char(ch)) { | 688 | if (isVariationSelector_Char(ch)) { |
655 | return d; | 689 | return d; |
656 | } | 690 | } |
657 | /* Smol Emoji overrides all other fonts. */ | 691 | const enum iFontStyle styleId = styleId_Text_(d); |
658 | if (ch != 0x20) { | 692 | const enum iFontSize sizeId = sizeId_Text_(d); |
659 | iFont *smol = font_Text_(smolEmoji_FontId + d->sizeId); | 693 | iFont *overrideFont = NULL; |
660 | if (smol != d && (*glyphIndex = glyphIndex_Font_(smol, ch)) != 0) { | 694 | if (ch != 0x20 && activeText_->overrideFontId >= 0) { |
661 | return smol; | 695 | /* Override font is checked first. */ |
696 | overrideFont = font_Text_(FONT_ID(activeText_->overrideFontId, styleId, sizeId)); | ||
697 | if (overrideFont != d && (*glyphIndex = glyphIndex_Font_(overrideFont, ch)) != 0) { | ||
698 | return overrideFont; | ||
662 | } | 699 | } |
663 | } | 700 | } |
701 | #if 0 | ||
702 | /* TODO: Put arrows in Smol Emoji. */ | ||
664 | /* Manual exceptions. */ { | 703 | /* Manual exceptions. */ { |
665 | if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { | 704 | if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { |
666 | d = font_Text_(iosevka_FontId + d->sizeId); | 705 | d = font_Text_(iosevka_FontId + d->sizeId); |
@@ -668,9 +707,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
668 | return d; | 707 | return d; |
669 | } | 708 | } |
670 | } | 709 | } |
710 | #endif | ||
711 | /* The font's own version of the glyph. */ | ||
671 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { | 712 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { |
672 | return d; | 713 | return d; |
673 | } | 714 | } |
715 | /* As a fallback, check all other available fonts of this size. */ | ||
716 | for (iFont *font = font_Text_(FONT_ID(0, styleId, sizeId)); | ||
717 | font < (iFont *) end_Array(&activeText_->fonts); | ||
718 | font += maxVariants_Fonts) { | ||
719 | if (font == d || font == overrideFont) { | ||
720 | continue; /* already checked this one */ | ||
721 | } | ||
722 | if ((*glyphIndex = glyphIndex_Font_(font, ch)) != 0) { | ||
723 | return font; | ||
724 | } | ||
725 | } | ||
726 | #if 0 | ||
674 | const int fallbacks[] = { | 727 | const int fallbacks[] = { |
675 | notoEmoji_FontId, | 728 | notoEmoji_FontId, |
676 | symbols2_FontId, | 729 | symbols2_FontId, |
@@ -732,6 +785,7 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
732 | if (d != font) { | 785 | if (d != font) { |
733 | *glyphIndex = glyphIndex_Font_(font, ch); | 786 | *glyphIndex = glyphIndex_Font_(font, ch); |
734 | } | 787 | } |
788 | #endif // 0 | ||
735 | if (!*glyphIndex) { | 789 | if (!*glyphIndex) { |
736 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 790 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); |
737 | } | 791 | } |
@@ -739,8 +793,9 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
739 | } | 793 | } |
740 | 794 | ||
741 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { | 795 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { |
796 | iAssert(d->table); | ||
742 | iGlyph* glyph = NULL; | 797 | iGlyph* glyph = NULL; |
743 | void * node = value_Hash(&d->glyphs, glyphIndex); | 798 | void * node = value_Hash(&d->table->glyphs, glyphIndex); |
744 | if (node) { | 799 | if (node) { |
745 | glyph = node; | 800 | glyph = node; |
746 | } | 801 | } |
@@ -758,7 +813,7 @@ static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { | |||
758 | and updates the glyph metrics. */ | 813 | and updates the glyph metrics. */ |
759 | allocate_Font_(d, glyph, 0); | 814 | allocate_Font_(d, glyph, 0); |
760 | allocate_Font_(d, glyph, 1); | 815 | allocate_Font_(d, glyph, 1); |
761 | insert_Hash(&d->glyphs, &glyph->node); | 816 | insert_Hash(&d->table->glyphs, &glyph->node); |
762 | } | 817 | } |
763 | return glyph; | 818 | return glyph; |
764 | } | 819 | } |
@@ -866,10 +921,6 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, i | |||
866 | run->logical.start = endAt; | 921 | run->logical.start = endAt; |
867 | } | 922 | } |
868 | 923 | ||
869 | static enum iFontId fontId_Text_(const iFont *font) { | ||
870 | return (enum iFontId) (font - activeText_->fonts); | ||
871 | } | ||
872 | |||
873 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { | 924 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { |
874 | iAssert(isEmpty_Array(&d->runs)); | 925 | iAssert(isEmpty_Array(&d->runs)); |
875 | size_t length = 0; | 926 | size_t length = 0; |
@@ -997,14 +1048,14 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh | |||
997 | continue; | 1048 | continue; |
998 | } | 1049 | } |
999 | if (ch == 0x20) { | 1050 | if (ch == 0x20) { |
1000 | if (run.font->family == emojiAndSymbols_TextFont) { | 1051 | if (run.font->fontSpec->flags & auxiliary_FontSpecFlag) { |
1001 | finishRun_AttributedText_(d, &run, pos); | 1052 | finishRun_AttributedText_(d, &run, pos); |
1002 | run.font = d->font; /* never use space from the symbols font, it's too wide */ | 1053 | run.font = d->font; /* never use space from the symbols font, it's too wide */ |
1003 | } | 1054 | } |
1004 | continue; | 1055 | continue; |
1005 | } | 1056 | } |
1006 | iFont *currentFont = d->font; | 1057 | iFont *currentFont = d->font; |
1007 | if (run.font->family == arabic_TextFont && isPunct_Char(ch)) { | 1058 | if (run.font->fontSpec->flags & arabic_FontSpecFlag && isPunct_Char(ch)) { |
1008 | currentFont = run.font; /* remain as Arabic for whitespace */ | 1059 | currentFont = run.font; /* remain as Arabic for whitespace */ |
1009 | } | 1060 | } |
1010 | const iGlyph *glyph = glyph_Font_(currentFont, ch); | 1061 | const iGlyph *glyph = glyph_Font_(currentFont, ch); |
@@ -1283,7 +1334,7 @@ static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int | |||
1283 | 1334 | ||
1284 | float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) { | 1335 | float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) { |
1285 | #if defined (LAGRANGE_ENABLE_KERNING) | 1336 | #if defined (LAGRANGE_ENABLE_KERNING) |
1286 | if (!enableKerning_Text || d->family != nunito_TextFont) { | 1337 | if (!enableKerning_Text || ~d->fontSpec->flags & fixNunitoKerning_FontSpecFlag) { |
1287 | return 0.0f; | 1338 | return 0.0f; |
1288 | } | 1339 | } |
1289 | if (glyph1 && glyph2) { | 1340 | if (glyph1 && glyph2) { |
@@ -1335,7 +1386,7 @@ static void deinit_GlyphBuffer_(iGlyphBuffer *d) { | |||
1335 | 1386 | ||
1336 | static void shape_GlyphBuffer_(iGlyphBuffer *d) { | 1387 | static void shape_GlyphBuffer_(iGlyphBuffer *d) { |
1337 | if (!d->glyphInfo) { | 1388 | if (!d->glyphInfo) { |
1338 | hb_shape(d->font->hbFont, d->hb, NULL, 0); | 1389 | hb_shape(d->font->fontFile->hbFont, d->hb, NULL, 0); |
1339 | d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount); | 1390 | d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount); |
1340 | d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount); | 1391 | d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount); |
1341 | } | 1392 | } |
@@ -1389,7 +1440,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1389 | float xCursor = 0.0f; | 1440 | float xCursor = 0.0f; |
1390 | float yCursor = 0.0f; | 1441 | float yCursor = 0.0f; |
1391 | float xCursorMax = 0.0f; | 1442 | float xCursorMax = 0.0f; |
1392 | const iBool isMonospaced = d->isMonospaced; | 1443 | const iBool isMonospaced = isMonospaced_Font(d); |
1393 | iWrapText *wrap = args->wrap; | 1444 | iWrapText *wrap = args->wrap; |
1394 | iAssert(args->text.end >= args->text.start); | 1445 | iAssert(args->text.end >= args->text.start); |
1395 | /* Split the text into a number of attributed runs that specify exactly which | 1446 | /* Split the text into a number of attributed runs that specify exactly which |
diff --git a/src/ui/text.h b/src/ui/text.h index 1b3200d4..f76c8125 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -31,79 +31,92 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
31 | 31 | ||
32 | /* Content sizes: regular (1x) -> medium (1.2x) -> big (1.33x) -> large (1.67x) -> huge (2x) */ | 32 | /* Content sizes: regular (1x) -> medium (1.2x) -> big (1.33x) -> large (1.67x) -> huge (2x) */ |
33 | 33 | ||
34 | #define FONT_ID(name, style, size) ((name) + ((style) * max_FontSize) + (size)) | ||
35 | |||
34 | enum iFontId { | 36 | enum iFontId { |
35 | /* UI fonts: normal weight (1x, 1.125x, 1.33x, 1.67x) */ | 37 | default_FontId = 0, /* default is always the first font */ |
36 | default_FontId = 0, | 38 | monospace_FontId = maxVariants_Fonts, /* 2nd font is always the monospace font */ |
37 | defaultMedium_FontId, | 39 | documentBody_FontId = maxVariants_Fonts * 2, /* 3rd font is the body font */ |
38 | defaultBig_FontId, | 40 | documentHeading_FontId = maxVariants_Fonts * 3, /* heading font */ |
39 | defaultLarge_FontId, | 41 | auxiliary_FontId = maxVariants_Fonts * 4, /* the first auxiliary font (e.g., symbols) */ |
40 | defaultTiny_FontId, | 42 | |
41 | defaultSmall_FontId, | 43 | // defaultMedium_FontId, |
44 | // defaultBig_FontId, | ||
45 | // defaultLarge_FontId, | ||
46 | // defaultTiny_FontId, | ||
47 | // defaultSmall_FontId, | ||
42 | /* UI fonts: bold weight */ | 48 | /* UI fonts: bold weight */ |
43 | defaultBold_FontId, | 49 | // defaultBold_FontId, |
44 | defaultMediumBold_FontId, | 50 | // defaultMediumBold_FontId, |
45 | defaultBigBold_FontId, | 51 | // defaultBigBold_FontId, |
46 | defaultLargeBold_FontId, | 52 | // defaultLargeBold_FontId, |
47 | /* content fonts */ | 53 | /* content fonts */ |
48 | regular_FontId, | 54 | // bold_FontId, |
49 | bold_FontId, | 55 | // italic_FontId, |
50 | italic_FontId, | 56 | // medium_FontId, |
51 | medium_FontId, | 57 | // big_FontId, |
52 | big_FontId, | 58 | // largeBold_FontId, |
53 | largeBold_FontId, | 59 | // largeLight_FontId, |
54 | largeLight_FontId, | 60 | // hugeBold_FontId, |
55 | hugeBold_FontId, | 61 | // monospaceSmall_FontId, |
56 | monospaceSmall_FontId, | ||
57 | monospace_FontId, | ||
58 | /* extra content fonts */ | 62 | /* extra content fonts */ |
59 | defaultContentRegular_FontId, /* UI font but sized to regular_FontId */ | 63 | // defaultContentRegular_FontId, /* UI font but sized to regular_FontId */ |
60 | defaultContentSmall_FontId, /* UI font but sized smaller */ | 64 | // defaultContentSmall_FontId, /* UI font but sized smaller */ |
61 | /* symbols and scripts */ | 65 | /* symbols and scripts */ |
62 | userSymbols_FontId, | 66 | // userSymbols_FontId, |
63 | iosevka_FontId = userSymbols_FontId + max_FontSize, | 67 | // iosevka_FontId = userSymbols_FontId + max_FontSize, |
64 | symbols_FontId = iosevka_FontId + max_FontSize, | 68 | // symbols_FontId = iosevka_FontId + max_FontSize, |
65 | symbols2_FontId = symbols_FontId + max_FontSize, | 69 | // symbols2_FontId = symbols_FontId + max_FontSize, |
66 | smolEmoji_FontId = symbols2_FontId + max_FontSize, | 70 | // smolEmoji_FontId = symbols2_FontId + max_FontSize, |
67 | notoEmoji_FontId = smolEmoji_FontId + max_FontSize, | 71 | // notoEmoji_FontId = smolEmoji_FontId + max_FontSize, |
68 | japanese_FontId = notoEmoji_FontId + max_FontSize, | 72 | // japanese_FontId = notoEmoji_FontId + max_FontSize, |
69 | chineseSimplified_FontId = japanese_FontId + max_FontSize, | 73 | // chineseSimplified_FontId = japanese_FontId + max_FontSize, |
70 | korean_FontId = chineseSimplified_FontId + max_FontSize, | 74 | // korean_FontId = chineseSimplified_FontId + max_FontSize, |
71 | arabic_FontId = korean_FontId + max_FontSize, | 75 | // arabic_FontId = korean_FontId + max_FontSize, |
72 | max_FontId = arabic_FontId + max_FontSize, | 76 | // max_FontId = arabic_FontId + max_FontSize, |
73 | 77 | ||
74 | /* Meta: */ | 78 | /* Meta: */ |
75 | mask_FontId = 0xffff, | 79 | mask_FontId = 0x0000ffff, /* font IDs are 16-bit; see GmRun's packing */ |
76 | alwaysVariableFlag_FontId = 0x10000, | 80 | alwaysVariableFlag_FontId = 0x00010000, |
77 | 81 | ||
78 | /* UI fonts: */ | 82 | /* UI fonts: */ |
79 | uiLabel_FontId = default_FontId, | 83 | uiLabelTiny_FontId = FONT_ID(default_FontId, semiBold_FontStyle, uiTiny_FontSize), |
80 | uiLabelBold_FontId = defaultBold_FontId, | 84 | uiLabelSmall_FontId = FONT_ID(default_FontId, regular_FontStyle, uiSmall_FontSize), |
81 | uiLabelLarge_FontId = defaultLarge_FontId, | 85 | uiLabel_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize), |
82 | uiLabelLargeBold_FontId = defaultLargeBold_FontId, | 86 | uiLabelMedium_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize), |
83 | uiShortcuts_FontId = default_FontId, | 87 | uiLabelMediumBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize), |
84 | uiInput_FontId = defaultMedium_FontId, | 88 | uiLabelBig_FontId = FONT_ID(default_FontId, regular_FontStyle, uiBig_FontSize), |
85 | uiContent_FontId = defaultMedium_FontId, | 89 | uiLabelBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiNormal_FontSize), |
86 | uiContentBold_FontId = defaultMediumBold_FontId, | 90 | uiLabelBigBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiBig_FontSize), |
87 | uiContentSymbols_FontId = symbols_FontId + uiMedium_FontSize, | 91 | uiLabelLarge_FontId = FONT_ID(default_FontId, regular_FontStyle, uiLarge_FontSize), |
92 | uiLabelLargeBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiLarge_FontSize), | ||
93 | uiLabelSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiNormal_FontSize), | ||
94 | uiShortcuts_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize), | ||
95 | uiInput_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize), | ||
96 | uiContent_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize), | ||
97 | uiContentBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize), | ||
98 | uiContentSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiMedium_FontSize), | ||
88 | /* Document fonts: */ | 99 | /* Document fonts: */ |
89 | paragraph_FontId = regular_FontId, | 100 | paragraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize), |
90 | firstParagraph_FontId = medium_FontId, | 101 | bold_FontId = FONT_ID(documentBody_FontId, semiBold_FontStyle, contentRegular_FontSize), |
91 | preformatted_FontId = monospace_FontId, | 102 | firstParagraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentMedium_FontSize), |
92 | preformattedSmall_FontId = monospaceSmall_FontId, | 103 | preformatted_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentMono_FontSize), |
93 | quote_FontId = italic_FontId, | 104 | preformattedSmall_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentMonoSmall_FontSize), |
94 | heading1_FontId = hugeBold_FontId, | 105 | quote_FontId = FONT_ID(documentBody_FontId, italic_FontStyle, contentRegular_FontSize), |
95 | heading2_FontId = largeBold_FontId, | 106 | heading1_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentHuge_FontSize), |
96 | heading3_FontId = big_FontId, | 107 | heading2_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentLarge_FontSize), |
97 | banner_FontId = largeLight_FontId, | 108 | heading3_FontId = FONT_ID(documentHeading_FontId, regular_FontStyle, contentBig_FontSize), |
98 | regularMonospace_FontId = iosevka_FontId + contentRegular_FontSize | 109 | banner_FontId = FONT_ID(documentHeading_FontId, light_FontStyle, contentLarge_FontSize), |
110 | regularMonospace_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentRegular_FontSize), | ||
99 | }; | 111 | }; |
100 | 112 | ||
101 | iLocalDef iBool isJapanese_FontId(enum iFontId id) { | 113 | //iLocalDef iBool isJapanese_FontId(enum iFontId id) { |
102 | return id >= japanese_FontId && id < japanese_FontId + max_FontSize; | 114 | // return id >= japanese_FontId && id < japanese_FontId + max_FontSize; |
103 | } | 115 | //} |
104 | 116 | ||
105 | #define emojiVariationSelector_Char ((iChar) 0xfe0f) | 117 | #define emojiVariationSelector_Char ((iChar) 0xfe0f) |
106 | 118 | ||
119 | /* TODO: get rid of this; configure using font ID strings, check RTL from FontFile flags */ | ||
107 | enum iTextFont { | 120 | enum iTextFont { |
108 | undefined_TextFont = -1, | 121 | undefined_TextFont = -1, |
109 | nunito_TextFont = 0, | 122 | nunito_TextFont = 0, |
@@ -127,7 +140,7 @@ void deinit_Text (iText *); | |||
127 | 140 | ||
128 | void setCurrent_Text (iText *); | 141 | void setCurrent_Text (iText *); |
129 | 142 | ||
130 | void loadUserFonts_Text (void); /* based on Prefs */ | 143 | //void loadUserFonts_Text (void); /* based on Prefs */ |
131 | 144 | ||
132 | void setContentFont_Text (iText *, enum iTextFont font); | 145 | void setContentFont_Text (iText *, enum iTextFont font); |
133 | void setHeadingFont_Text (iText *, enum iTextFont font); | 146 | void setHeadingFont_Text (iText *, enum iTextFont font); |
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index ba7545fd..ded8d7f8 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c | |||
@@ -255,7 +255,7 @@ void init_UploadWidget(iUploadWidget *d) { | |||
255 | setFlags_Widget(as_Widget(d->token), expand_WidgetFlag, iTrue); | 255 | setFlags_Widget(as_Widget(d->token), expand_WidgetFlag, iTrue); |
256 | setFocus_Widget(as_Widget(d->input)); | 256 | setFocus_Widget(as_Widget(d->input)); |
257 | } | 257 | } |
258 | setFont_InputWidget(d->input, iosevka_FontId); | 258 | setFont_InputWidget(d->input, FONT_ID(monospace_FontId, regular_FontStyle, uiNormal_FontSize)); |
259 | setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */ | 259 | setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */ |
260 | setLineLimits_InputWidget(d->input, 7, 20); | 260 | setLineLimits_InputWidget(d->input, 7, 20); |
261 | setHint_InputWidget(d->input, "${hint.upload.text}"); | 261 | setHint_InputWidget(d->input, "${hint.upload.text}"); |
diff --git a/src/ui/util.c b/src/ui/util.c index adca6269..a4411ea5 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -873,7 +873,7 @@ static void updateMenuItemFonts_Widget_(iWidget *d) { | |||
873 | } | 873 | } |
874 | else if (isPortraitPhone) { | 874 | else if (isPortraitPhone) { |
875 | if (!isSlidePanel) { | 875 | if (!isSlidePanel) { |
876 | setFont_LabelWidget(label, isCaution ? defaultBigBold_FontId : defaultBig_FontId); | 876 | setFont_LabelWidget(label, isCaution ? uiLabelBigBold_FontId : uiLabelBig_FontId); |
877 | } | 877 | } |
878 | } | 878 | } |
879 | else { | 879 | else { |
@@ -1626,8 +1626,8 @@ iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) { | |||
1626 | } | 1626 | } |
1627 | int fonts[2] = { uiLabel_FontId, uiLabelBold_FontId }; | 1627 | int fonts[2] = { uiLabel_FontId, uiLabelBold_FontId }; |
1628 | if (deviceType_App() == phone_AppDeviceType) { | 1628 | if (deviceType_App() == phone_AppDeviceType) { |
1629 | fonts[0] = defaultMedium_FontId; | 1629 | fonts[0] = uiLabelMedium_FontId; |
1630 | fonts[1] = defaultMediumBold_FontId; | 1630 | fonts[1] = uiLabelMediumBold_FontId; |
1631 | } | 1631 | } |
1632 | for (size_t i = 0; i < numActions; i++) { | 1632 | for (size_t i = 0; i < numActions; i++) { |
1633 | const char *label = actions[i].label; | 1633 | const char *label = actions[i].label; |
@@ -1694,7 +1694,7 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con | |||
1694 | resizeToParentWidth_WidgetFlag); | 1694 | resizeToParentWidth_WidgetFlag); |
1695 | setContentPadding_InputWidget(input, 0.5f * gap_UI, 0.5f * gap_UI); | 1695 | setContentPadding_InputWidget(input, 0.5f * gap_UI, 0.5f * gap_UI); |
1696 | if (deviceType_App() == phone_AppDeviceType) { | 1696 | if (deviceType_App() == phone_AppDeviceType) { |
1697 | setFont_InputWidget(input, defaultBig_FontId); | 1697 | setFont_InputWidget(input, uiLabelBig_FontId); |
1698 | setBackgroundColor_Widget(dlg, uiBackgroundSidebar_ColorId); | 1698 | setBackgroundColor_Widget(dlg, uiBackgroundSidebar_ColorId); |
1699 | setContentPadding_InputWidget(input, gap_UI, gap_UI); | 1699 | setContentPadding_InputWidget(input, gap_UI, gap_UI); |
1700 | } | 1700 | } |
@@ -1811,7 +1811,7 @@ iWidget *makeQuestion_Widget(const char *title, const char *msg, | |||
1811 | resizeToParentWidth_WidgetFlag | | 1811 | resizeToParentWidth_WidgetFlag | |
1812 | (first == '&' ? selected_WidgetFlag : 0)); | 1812 | (first == '&' ? selected_WidgetFlag : 0)); |
1813 | if (deviceType_App() != desktop_AppDeviceType) { | 1813 | if (deviceType_App() != desktop_AppDeviceType) { |
1814 | setFont_LabelWidget(option, defaultBig_FontId); | 1814 | setFont_LabelWidget(option, uiLabelBig_FontId); |
1815 | } | 1815 | } |
1816 | } | 1816 | } |
1817 | } | 1817 | } |
diff --git a/src/ui/window.c b/src/ui/window.c index 686a6dd6..ea783331 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -1188,7 +1188,7 @@ void draw_Window(iWindow *d) { | |||
1188 | extern int drawCount_; | 1188 | extern int drawCount_; |
1189 | drawRoot_Widget(root->widget); | 1189 | drawRoot_Widget(root->widget); |
1190 | #if !defined (NDEBUG) | 1190 | #if !defined (NDEBUG) |
1191 | draw_Text(defaultBold_FontId, safeRect_Root(root).pos, red_ColorId, "%d", drawCount_); | 1191 | draw_Text(uiLabelBold_FontId, safeRect_Root(root).pos, red_ColorId, "%d", drawCount_); |
1192 | drawCount_ = 0; | 1192 | drawCount_ = 0; |
1193 | #endif | 1193 | #endif |
1194 | } | 1194 | } |
@@ -1287,7 +1287,7 @@ void draw_MainWindow(iMainWindow *d) { | |||
1287 | } | 1287 | } |
1288 | setCurrent_Root(NULL); | 1288 | setCurrent_Root(NULL); |
1289 | #if !defined (NDEBUG) | 1289 | #if !defined (NDEBUG) |
1290 | draw_Text(defaultBold_FontId, safeRect_Root(w->roots[0]).pos, red_ColorId, "%d", drawCount_); | 1290 | draw_Text(uiLabelBold_FontId, safeRect_Root(w->roots[0]).pos, red_ColorId, "%d", drawCount_); |
1291 | drawCount_ = 0; | 1291 | drawCount_ = 0; |
1292 | #endif | 1292 | #endif |
1293 | } | 1293 | } |