diff options
-rwxr-xr-x | .github/workflows/docker-debian/entrypoint.sh | 2 | ||||
-rw-r--r-- | CMakeLists.txt | 29 | ||||
m--------- | lib/the_Foundation | 0 | ||||
-rw-r--r-- | res/about/version.gmi | 8 | ||||
-rw-r--r-- | src/app.c | 12 | ||||
-rw-r--r-- | src/gmdocument.c | 36 | ||||
-rw-r--r-- | src/gmdocument.h | 22 | ||||
-rw-r--r-- | src/gopher.c | 4 | ||||
-rw-r--r-- | src/history.c | 11 | ||||
-rw-r--r-- | src/history.h | 1 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 6 | ||||
-rw-r--r-- | src/ui/text.c | 2 | ||||
-rw-r--r-- | src/ui/text_simple.c | 16 | ||||
-rw-r--r-- | src/ui/util.c | 23 |
14 files changed, 90 insertions, 82 deletions
diff --git a/.github/workflows/docker-debian/entrypoint.sh b/.github/workflows/docker-debian/entrypoint.sh index d3222c3f..33e59c44 100755 --- a/.github/workflows/docker-debian/entrypoint.sh +++ b/.github/workflows/docker-debian/entrypoint.sh | |||
@@ -2,7 +2,7 @@ | |||
2 | export LC_ALL=en_US.UTF-8 | 2 | export LC_ALL=en_US.UTF-8 |
3 | 3 | ||
4 | apt-get update -qq -y | 4 | apt-get update -qq -y |
5 | apt-get install -y -qq --no-install-recommends cmake libsdl2-dev libssl-dev libpcre3-dev zlib1g-dev libunistring-dev libmpg123-dev debhelper dh-make devscripts fakeroot git build-essential locales python3 python3-pip libharfbuzz-dev libfribidi-dev | 5 | apt-get install -y -qq --no-install-recommends cmake libsdl2-dev libssl-dev libpcre3-dev zlib1g-dev libunistring-dev libmpg123-dev debhelper dh-make devscripts fakeroot git build-essential locales python3 python3-pip zip libharfbuzz-dev libfribidi-dev |
6 | pip3 install git-archive-all | 6 | pip3 install git-archive-all |
7 | sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && locale-gen | 7 | sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && locale-gen |
8 | 8 | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6abfd094..378a1bba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -18,7 +18,7 @@ | |||
18 | cmake_minimum_required (VERSION 3.9) | 18 | cmake_minimum_required (VERSION 3.9) |
19 | 19 | ||
20 | project (Lagrange | 20 | project (Lagrange |
21 | VERSION 1.8.0 | 21 | VERSION 1.8.1 |
22 | DESCRIPTION "A Beautiful Gemini Client" | 22 | DESCRIPTION "A Beautiful Gemini Client" |
23 | LANGUAGES C | 23 | LANGUAGES C |
24 | ) | 24 | ) |
@@ -65,25 +65,6 @@ include (Depends.cmake) | |||
65 | # Package resources. | 65 | # Package resources. |
66 | message (STATUS "Preparing resources...") | 66 | message (STATUS "Preparing resources...") |
67 | make_fontpack (res/default.fontpack) | 67 | make_fontpack (res/default.fontpack) |
68 | # Fonts to install as separate files. | ||
69 | set (FONTPACKS | ||
70 | # arabic.fontpack | ||
71 | # cjk.fontpack | ||
72 | # firasans.fontpack | ||
73 | # literata.fontpack | ||
74 | # nunito.fontpack | ||
75 | # tinos.fontpack | ||
76 | ) | ||
77 | foreach (fp ${FONTPACKS}) | ||
78 | make_fontpack (res/${fp}) | ||
79 | set_source_files_properties (${CMAKE_BINARY_DIR}/${fp} | ||
80 | PROPERTIES MACOSX_PACKAGE_LOCATION Resources) | ||
81 | endforeach (fp) | ||
82 | macro (install_fonts dst) | ||
83 | foreach (fp ${FONTPACKS}) | ||
84 | install (FILES ${CMAKE_BINARY_DIR}/${fp} DESTINATION ${dst}) | ||
85 | endforeach (fp) | ||
86 | endmacro () | ||
87 | set (EMBED_RESOURCES | 68 | set (EMBED_RESOURCES |
88 | res/about/about.gmi | 69 | res/about/about.gmi |
89 | res/about/help.gmi | 70 | res/about/help.gmi |
@@ -240,11 +221,6 @@ set (SOURCES | |||
240 | ${CMAKE_CURRENT_BINARY_DIR}/embedded.h | 221 | ${CMAKE_CURRENT_BINARY_DIR}/embedded.h |
241 | ${CMAKE_CURRENT_BINARY_DIR}/resources.lgr | 222 | ${CMAKE_CURRENT_BINARY_DIR}/resources.lgr |
242 | ) | 223 | ) |
243 | if (APPLE) | ||
244 | foreach (fp ${FONTPACKS}) | ||
245 | list (APPEND SOURCES ${CMAKE_BINARY_DIR}/${fp}) | ||
246 | endforeach (fp) | ||
247 | endif () | ||
248 | if (ENABLE_IPC) | 224 | if (ENABLE_IPC) |
249 | list (APPEND SOURCES | 225 | list (APPEND SOURCES |
250 | src/ipc.c | 226 | src/ipc.c |
@@ -444,7 +420,6 @@ if (MSYS) | |||
444 | if (NOT ENABLE_RESOURCE_EMBED) | 420 | if (NOT ENABLE_RESOURCE_EMBED) |
445 | install (FILES ${EMB_BIN} DESTINATION .) | 421 | install (FILES ${EMB_BIN} DESTINATION .) |
446 | endif () | 422 | endif () |
447 | install_fonts (.) | ||
448 | install (PROGRAMS | 423 | install (PROGRAMS |
449 | ${SDL2_LIBDIR}/SDL2.dll | 424 | ${SDL2_LIBDIR}/SDL2.dll |
450 | res/urlopen.bat | 425 | res/urlopen.bat |
@@ -460,7 +435,6 @@ elseif (HAIKU) | |||
460 | LAGRANGE_EMB_BIN="${CMAKE_INSTALL_PREFIX}/resources.lgr") | 435 | LAGRANGE_EMB_BIN="${CMAKE_INSTALL_PREFIX}/resources.lgr") |
461 | install (FILES ${EMB_BIN} DESTINATION .) | 436 | install (FILES ${EMB_BIN} DESTINATION .) |
462 | endif () | 437 | endif () |
463 | install_fonts (.) | ||
464 | elseif (UNIX AND NOT APPLE) | 438 | elseif (UNIX AND NOT APPLE) |
465 | set_target_properties (app PROPERTIES | 439 | set_target_properties (app PROPERTIES |
466 | INSTALL_RPATH_USE_LINK_PATH YES | 440 | INSTALL_RPATH_USE_LINK_PATH YES |
@@ -493,5 +467,4 @@ MimeType=x-scheme-handler/gemini;x-scheme-handler/gopher; | |||
493 | endif () | 467 | endif () |
494 | install (FILES ${EMB_BIN} DESTINATION share/lagrange) | 468 | install (FILES ${EMB_BIN} DESTINATION share/lagrange) |
495 | endif () | 469 | endif () |
496 | install_fonts (share/lagrange) | ||
497 | endif () | 470 | endif () |
diff --git a/lib/the_Foundation b/lib/the_Foundation | |||
Subproject 6c7d2a3b050aae3b37adc626aa75f040b5acb93 | Subproject 9554ca7cc8b26c328d870760f3aaac4e701ba7e | ||
diff --git a/res/about/version.gmi b/res/about/version.gmi index f4fb83d1..372f6027 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -6,6 +6,14 @@ | |||
6 | ``` | 6 | ``` |
7 | # Release notes | 7 | # Release notes |
8 | 8 | ||
9 | ## 1.8.1 | ||
10 | * Added the `zip` utility as a build requirement. It is used for making fontpacks. | ||
11 | * Fixed build failure with the simple text renderer, i.e., when HarfBuzz is disabled. | ||
12 | * Fixed a line spacing artifact in long headings. With some fonts, the lines were clipping each other so the spacing was restored to normal. | ||
13 | * Fixed a socket I/O issue that caused received data to be ignored when the peer closed the connection prematurely. | ||
14 | * macOS: Tab close buttons are on the left side (platform UI convention). | ||
15 | * Gopher: Recognize both LF and CRLF line endings in page content. | ||
16 | |||
9 | ## 1.8 | 17 | ## 1.8 |
10 | ⚠️ Font settings will be reset to defaults. Only a minimal set of fonts is bundled with the app. If additional fonts are needed, one can use custom TrueType fonts or download some from the Font Library: | 18 | ⚠️ Font settings will be reset to defaults. Only a minimal set of fonts is bundled with the app. If additional fonts are needed, one can use custom TrueType fonts or download some from the Font Library: |
11 | => gemini://skyjake.fi/fonts/ | 19 | => gemini://skyjake.fi/fonts/ |
@@ -2125,6 +2125,12 @@ void resetFonts_App(void) { | |||
2125 | } | 2125 | } |
2126 | } | 2126 | } |
2127 | 2127 | ||
2128 | static void invalidateCachedDocuments_App_(void) { | ||
2129 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { | ||
2130 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); | ||
2131 | } | ||
2132 | } | ||
2133 | |||
2128 | iBool handleCommand_App(const char *cmd) { | 2134 | iBool handleCommand_App(const char *cmd) { |
2129 | iApp *d = &app_; | 2135 | iApp *d = &app_; |
2130 | const iBool isFrozen = !d->window || d->window->isDrawFrozen; | 2136 | const iBool isFrozen = !d->window || d->window->isDrawFrozen; |
@@ -2289,7 +2295,10 @@ iBool handleCommand_App(const char *cmd) { | |||
2289 | if (!isFrozen) { | 2295 | if (!isFrozen) { |
2290 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */ | 2296 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */ |
2291 | } | 2297 | } |
2292 | d->prefs.zoomPercent = arg_Command(cmd); | 2298 | if (arg_Command(cmd) != d->prefs.zoomPercent) { |
2299 | d->prefs.zoomPercent = arg_Command(cmd); | ||
2300 | invalidateCachedDocuments_App_(); | ||
2301 | } | ||
2293 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); | 2302 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); |
2294 | if (!isFrozen) { | 2303 | if (!isFrozen) { |
2295 | postCommand_App("font.changed"); | 2304 | postCommand_App("font.changed"); |
@@ -2306,6 +2315,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2306 | delta /= 2; | 2315 | delta /= 2; |
2307 | } | 2316 | } |
2308 | d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200); | 2317 | d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200); |
2318 | invalidateCachedDocuments_App_(); | ||
2309 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); | 2319 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); |
2310 | if (!isFrozen) { | 2320 | if (!isFrozen) { |
2311 | postCommand_App("font.changed"); | 2321 | postCommand_App("font.changed"); |
diff --git a/src/gmdocument.c b/src/gmdocument.c index 8b24ce29..0027bdb3 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -161,10 +161,9 @@ struct Impl_GmDocument { | |||
161 | iInt2 size; | 161 | iInt2 size; |
162 | int outsideMargin; | 162 | int outsideMargin; |
163 | iBool enableCommandLinks; /* `about:command?` only allowed on selected pages */ | 163 | iBool enableCommandLinks; /* `about:command?` only allowed on selected pages */ |
164 | iBool isLayoutInvalidated; | ||
164 | iArray layout; /* contents of source, laid out in document space */ | 165 | iArray layout; /* contents of source, laid out in document space */ |
165 | iPtrArray links; | 166 | iPtrArray links; |
166 | // enum iGmDocumentBanner bannerType; | ||
167 | // iString bannerText; | ||
168 | iString title; /* the first top-level title */ | 167 | iString title; /* the first top-level title */ |
169 | iArray headings; | 168 | iArray headings; |
170 | iArray preMeta; /* metadata about preformatted blocks */ | 169 | iArray preMeta; /* metadata about preformatted blocks */ |
@@ -607,6 +606,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
607 | // const iBool isDarkBg = isDark_GmDocumentTheme( | 606 | // const iBool isDarkBg = isDark_GmDocumentTheme( |
608 | // isDark_ColorTheme(colorTheme_App()) ? prefs->docThemeDark : prefs->docThemeLight); | 607 | // isDark_ColorTheme(colorTheme_App()) ? prefs->docThemeDark : prefs->docThemeLight); |
609 | initTheme_GmDocument_(d); | 608 | initTheme_GmDocument_(d); |
609 | d->isLayoutInvalidated = iFalse; | ||
610 | /* TODO: Collect these parameters into a GmTheme. */ | 610 | /* TODO: Collect these parameters into a GmTheme. */ |
611 | float indents[max_GmLineType] = { 5, 10, 5, isNarrow ? 5 : 10, 0, 0, 0, 5 }; | 611 | float indents[max_GmLineType] = { 5, 10, 5, isNarrow ? 5 : 10, 0, 0, 0, 5 }; |
612 | if (isExtremelyNarrow) { | 612 | if (isExtremelyNarrow) { |
@@ -968,6 +968,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
968 | ? 4 : 0) * gap_Text; | 968 | ? 4 : 0) * gap_Text; |
969 | } | 969 | } |
970 | if (!isMono) { | 970 | if (!isMono) { |
971 | #if 0 | ||
971 | /* Upper-level headings are typeset a bit tighter. */ | 972 | /* Upper-level headings are typeset a bit tighter. */ |
972 | if (type == heading1_GmLineType) { | 973 | if (type == heading1_GmLineType) { |
973 | rts.lineHeightReduction = 0.10f; | 974 | rts.lineHeightReduction = 0.10f; |
@@ -975,6 +976,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
975 | else if (type == heading2_GmLineType) { | 976 | else if (type == heading2_GmLineType) { |
976 | rts.lineHeightReduction = 0.06f; | 977 | rts.lineHeightReduction = 0.06f; |
977 | } | 978 | } |
979 | #endif | ||
978 | /* Visited links are never bold. */ | 980 | /* Visited links are never bold. */ |
979 | if (run.linkId && !prefs->boldLinkVisited && | 981 | if (run.linkId && !prefs->boldLinkVisited && |
980 | linkFlags_GmDocument(d, run.linkId) & visited_GmLinkFlag) { | 982 | linkFlags_GmDocument(d, run.linkId) & visited_GmLinkFlag) { |
@@ -1142,13 +1144,12 @@ void init_GmDocument(iGmDocument *d) { | |||
1142 | init_String(&d->source); | 1144 | init_String(&d->source); |
1143 | init_String(&d->url); | 1145 | init_String(&d->url); |
1144 | init_String(&d->localHost); | 1146 | init_String(&d->localHost); |
1145 | // d->bannerType = siteDomain_GmDocumentBanner; | ||
1146 | d->outsideMargin = 0; | 1147 | d->outsideMargin = 0; |
1147 | d->size = zero_I2(); | 1148 | d->size = zero_I2(); |
1148 | d->enableCommandLinks = iFalse; | 1149 | d->enableCommandLinks = iFalse; |
1150 | d->isLayoutInvalidated = iFalse; | ||
1149 | init_Array(&d->layout, sizeof(iGmRun)); | 1151 | init_Array(&d->layout, sizeof(iGmRun)); |
1150 | init_PtrArray(&d->links); | 1152 | init_PtrArray(&d->links); |
1151 | // init_String(&d->bannerText); | ||
1152 | init_String(&d->title); | 1153 | init_String(&d->title); |
1153 | init_Array(&d->headings, sizeof(iGmHeading)); | 1154 | init_Array(&d->headings, sizeof(iGmHeading)); |
1154 | init_Array(&d->preMeta, sizeof(iGmPreMeta)); | 1155 | init_Array(&d->preMeta, sizeof(iGmPreMeta)); |
@@ -1164,7 +1165,6 @@ void init_GmDocument(iGmDocument *d) { | |||
1164 | void deinit_GmDocument(iGmDocument *d) { | 1165 | void deinit_GmDocument(iGmDocument *d) { |
1165 | iReleasePtr(&d->openURLs); | 1166 | iReleasePtr(&d->openURLs); |
1166 | delete_Media(d->media); | 1167 | delete_Media(d->media); |
1167 | // deinit_String(&d->bannerText); | ||
1168 | deinit_String(&d->title); | 1168 | deinit_String(&d->title); |
1169 | clearLinks_GmDocument_(d); | 1169 | clearLinks_GmDocument_(d); |
1170 | deinit_PtrArray(&d->links); | 1170 | deinit_PtrArray(&d->links); |
@@ -1720,22 +1720,28 @@ void setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) { | |||
1720 | d->format = format; | 1720 | d->format = format; |
1721 | } | 1721 | } |
1722 | 1722 | ||
1723 | #if 0 | 1723 | void setWidth_GmDocument(iGmDocument *d, int width, int canvasWidth) { |
1724 | void setBanner_GmDocument(iGmDocument *d, enum iGmDocumentBanner type) { | 1724 | d->size.x = width; |
1725 | d->bannerType = type; | 1725 | d->outsideMargin = iMax(0, (canvasWidth - width) / 2); /* distance to edge of the canvas */ |
1726 | doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */ | ||
1726 | } | 1727 | } |
1727 | #endif | ||
1728 | 1728 | ||
1729 | void setWidth_GmDocument(iGmDocument *d, int width, int outsideMargin) { | 1729 | iBool updateWidth_GmDocument(iGmDocument *d, int width, int canvasWidth) { |
1730 | d->size.x = width; | 1730 | if (d->size.x != width || d->isLayoutInvalidated) { |
1731 | d->outsideMargin = outsideMargin; /* distance to edge of the viewport */ | 1731 | setWidth_GmDocument(d, width, canvasWidth); |
1732 | doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */ | 1732 | return iTrue; |
1733 | } | ||
1734 | return iFalse; | ||
1733 | } | 1735 | } |
1734 | 1736 | ||
1735 | void redoLayout_GmDocument(iGmDocument *d) { | 1737 | void redoLayout_GmDocument(iGmDocument *d) { |
1736 | doLayout_GmDocument_(d); | 1738 | doLayout_GmDocument_(d); |
1737 | } | 1739 | } |
1738 | 1740 | ||
1741 | void invalidateLayout_GmDocument(iGmDocument *d) { | ||
1742 | d->isLayoutInvalidated = iTrue; | ||
1743 | } | ||
1744 | |||
1739 | static void markLinkRunsVisited_GmDocument_(iGmDocument *d, const iIntSet *linkIds) { | 1745 | static void markLinkRunsVisited_GmDocument_(iGmDocument *d, const iIntSet *linkIds) { |
1740 | iForEach(Array, r, &d->layout) { | 1746 | iForEach(Array, r, &d->layout) { |
1741 | iGmRun *run = r.value; | 1747 | iGmRun *run = r.value; |
@@ -2080,7 +2086,7 @@ static void convertMarkdownToGemtext_GmDocument_(iGmDocument *d) { | |||
2080 | d->format = gemini_SourceFormat; | 2086 | d->format = gemini_SourceFormat; |
2081 | } | 2087 | } |
2082 | 2088 | ||
2083 | void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int outsideMargin, | 2089 | void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int canvasWidth, |
2084 | enum iGmDocumentUpdate updateType) { | 2090 | enum iGmDocumentUpdate updateType) { |
2085 | /* TODO: This API has been set up to allow partial/progressive updating of the content. | 2091 | /* TODO: This API has been set up to allow partial/progressive updating of the content. |
2086 | Currently the entire source is replaced every time, though. */ | 2092 | Currently the entire source is replaced every time, though. */ |
@@ -2122,7 +2128,7 @@ void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int | |||
2122 | if (isNormalized_GmDocument_(d)) { | 2128 | if (isNormalized_GmDocument_(d)) { |
2123 | normalize_GmDocument(d); | 2129 | normalize_GmDocument(d); |
2124 | } | 2130 | } |
2125 | setWidth_GmDocument(d, width, outsideMargin); /* re-do layout */ | 2131 | setWidth_GmDocument(d, width, canvasWidth); /* re-do layout */ |
2126 | } | 2132 | } |
2127 | 2133 | ||
2128 | void foldPre_GmDocument(iGmDocument *d, uint16_t preId) { | 2134 | void foldPre_GmDocument(iGmDocument *d, uint16_t preId) { |
diff --git a/src/gmdocument.h b/src/gmdocument.h index 444520c6..58fc3db3 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -120,10 +120,8 @@ enum iGmRunFlags { | |||
120 | decoration_GmRunFlag = iBit(1), /* not part of the source */ | 120 | decoration_GmRunFlag = iBit(1), /* not part of the source */ |
121 | startOfLine_GmRunFlag = iBit(2), | 121 | startOfLine_GmRunFlag = iBit(2), |
122 | endOfLine_GmRunFlag = iBit(3), | 122 | endOfLine_GmRunFlag = iBit(3), |
123 | // siteBanner_GmRunFlag = iBit(4), /* area reserved for the site banner */ | ||
124 | quoteBorder_GmRunFlag = iBit(5), | 123 | quoteBorder_GmRunFlag = iBit(5), |
125 | wide_GmRunFlag = iBit(6), /* horizontally scrollable */ | 124 | wide_GmRunFlag = iBit(6), /* horizontally scrollable */ |
126 | // footer_GmRunFlag = iBit(7), | ||
127 | altText_GmRunFlag = iBit(8), | 125 | altText_GmRunFlag = iBit(8), |
128 | }; | 126 | }; |
129 | 127 | ||
@@ -177,13 +175,6 @@ enum iGmDocumentWarning { | |||
177 | missingGlyphs_GmDocumentWarning = iBit(2), | 175 | missingGlyphs_GmDocumentWarning = iBit(2), |
178 | }; | 176 | }; |
179 | 177 | ||
180 | /* | ||
181 | enum iGmDocumentBanner { | ||
182 | none_GmDocumentBanner, | ||
183 | siteDomain_GmDocumentBanner, | ||
184 | certificateWarning_GmDocumentBanner, | ||
185 | };*/ | ||
186 | |||
187 | enum iGmDocumentUpdate { | 178 | enum iGmDocumentUpdate { |
188 | partial_GmDocumentUpdate, /* appending more content */ | 179 | partial_GmDocumentUpdate, /* appending more content */ |
189 | final_GmDocumentUpdate, /* process all lines, including the last one if not terminated */ | 180 | final_GmDocumentUpdate, /* process all lines, including the last one if not terminated */ |
@@ -191,12 +182,13 @@ enum iGmDocumentUpdate { | |||
191 | 182 | ||
192 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); | 183 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); |
193 | void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); | 184 | void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); |
194 | //void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); | 185 | void setWidth_GmDocument (iGmDocument *, int width, int canvasWidth); |
195 | void setWidth_GmDocument (iGmDocument *, int width, int outsideMargin); | 186 | iBool updateWidth_GmDocument (iGmDocument *, int width, int canvasWidth); |
196 | void redoLayout_GmDocument (iGmDocument *); | 187 | void redoLayout_GmDocument (iGmDocument *); |
188 | void invalidateLayout_GmDocument(iGmDocument *); /* will have to be redone later */ | ||
197 | iBool updateOpenURLs_GmDocument(iGmDocument *); | 189 | iBool updateOpenURLs_GmDocument(iGmDocument *); |
198 | void setUrl_GmDocument (iGmDocument *, const iString *url); | 190 | void setUrl_GmDocument (iGmDocument *, const iString *url); |
199 | void setSource_GmDocument (iGmDocument *, const iString *source, int width, int outsideMargin, | 191 | void setSource_GmDocument (iGmDocument *, const iString *source, int width, int canvasWidth, |
200 | enum iGmDocumentUpdate updateType); | 192 | enum iGmDocumentUpdate updateType); |
201 | void foldPre_GmDocument (iGmDocument *, uint16_t preId); | 193 | void foldPre_GmDocument (iGmDocument *, uint16_t preId); |
202 | 194 | ||
@@ -204,8 +196,6 @@ void updateVisitedLinks_GmDocument (iGmDocument *); /* check all links for | |||
204 | void invalidatePalette_GmDocument (iGmDocument *); | 196 | void invalidatePalette_GmDocument (iGmDocument *); |
205 | void makePaletteGlobal_GmDocument (const iGmDocument *); /* copies document colors to the global palette */ | 197 | void makePaletteGlobal_GmDocument (const iGmDocument *); /* copies document colors to the global palette */ |
206 | 198 | ||
207 | //void reset_GmDocument (iGmDocument *); /* free images */ | ||
208 | |||
209 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); | 199 | typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); |
210 | 200 | ||
211 | iMedia * media_GmDocument (iGmDocument *); | 201 | iMedia * media_GmDocument (iGmDocument *); |
@@ -219,10 +209,6 @@ const iGmRun * renderProgressive_GmDocument(const iGmDocument *d, const iGmRun | |||
219 | iRangei visRangeY, iGmDocumentRenderFunc render, | 209 | iRangei visRangeY, iGmDocumentRenderFunc render, |
220 | void *context); | 210 | void *context); |
221 | iInt2 size_GmDocument (const iGmDocument *); | 211 | iInt2 size_GmDocument (const iGmDocument *); |
222 | //const iGmRun * siteBanner_GmDocument (const iGmDocument *); | ||
223 | //iBool hasSiteBanner_GmDocument (const iGmDocument *); | ||
224 | //enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *); | ||
225 | //const iString * bannerText_GmDocument (const iGmDocument *); | ||
226 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ | 212 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ |
227 | const iString * source_GmDocument (const iGmDocument *); | 213 | const iString * source_GmDocument (const iGmDocument *); |
228 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ | 214 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ |
diff --git a/src/gopher.c b/src/gopher.c index ac5fe560..008a7743 100644 --- a/src/gopher.c +++ b/src/gopher.c | |||
@@ -103,10 +103,10 @@ static iBool convertSource_Gopher_(iGopher *d) { | |||
103 | for (;;) { | 103 | for (;;) { |
104 | /* Find the end of the line. */ | 104 | /* Find the end of the line. */ |
105 | iRangecc line = { body.start, body.start }; | 105 | iRangecc line = { body.start, body.start }; |
106 | while (line.end < body.end - 1 && !isCRLFLineTerminator_(line.end)) { | 106 | while (line.end < body.end - 1 && !isLineTerminator_(line.end)) { |
107 | line.end++; | 107 | line.end++; |
108 | } | 108 | } |
109 | if (line.end >= body.end - 1 || !isCRLFLineTerminator_(line.end)) { | 109 | if (line.end >= body.end - 1 || !isLineTerminator_(line.end)) { |
110 | /* Not a complete line. More may be coming later. */ | 110 | /* Not a complete line. More may be coming later. */ |
111 | break; | 111 | break; |
112 | } | 112 | } |
diff --git a/src/history.c b/src/history.c index 208c239d..7185912f 100644 --- a/src/history.c +++ b/src/history.c | |||
@@ -440,6 +440,17 @@ void clearCache_History(iHistory *d) { | |||
440 | unlock_Mutex(d->mtx); | 440 | unlock_Mutex(d->mtx); |
441 | } | 441 | } |
442 | 442 | ||
443 | void invalidateCachedLayout_History(iHistory *d) { | ||
444 | lock_Mutex(d->mtx); | ||
445 | iForEach(Array, i, &d->recent) { | ||
446 | iRecentUrl *url = i.value; | ||
447 | if (url->cachedDoc) { | ||
448 | invalidateLayout_GmDocument(url->cachedDoc); | ||
449 | } | ||
450 | } | ||
451 | unlock_Mutex(d->mtx); | ||
452 | } | ||
453 | |||
443 | size_t pruneLeastImportant_History(iHistory *d) { | 454 | size_t pruneLeastImportant_History(iHistory *d) { |
444 | size_t delta = 0; | 455 | size_t delta = 0; |
445 | size_t chosen = iInvalidPos; | 456 | size_t chosen = iInvalidPos; |
diff --git a/src/history.h b/src/history.h index 7dad72df..d3daae80 100644 --- a/src/history.h +++ b/src/history.h | |||
@@ -76,6 +76,7 @@ void clearCache_History (iHistory *); | |||
76 | size_t pruneLeastImportant_History (iHistory *); | 76 | size_t pruneLeastImportant_History (iHistory *); |
77 | size_t pruneLeastImportantMemory_History (iHistory *); | 77 | size_t pruneLeastImportantMemory_History (iHistory *); |
78 | void invalidateTheme_History (iHistory *); /* theme has changed, cached contents need updating */ | 78 | void invalidateTheme_History (iHistory *); /* theme has changed, cached contents need updating */ |
79 | void invalidateCachedLayout_History (iHistory *); | ||
79 | 80 | ||
80 | iBool atLatest_History (const iHistory *); | 81 | iBool atLatest_History (const iHistory *); |
81 | iBool atOldest_History (const iHistory *); | 82 | iBool atOldest_History (const iHistory *); |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 79bfea7b..8c24d4a9 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1702,6 +1702,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
1702 | } | 1702 | } |
1703 | if (cachedDoc) { | 1703 | if (cachedDoc) { |
1704 | replaceDocument_DocumentWidget_(d, cachedDoc); | 1704 | replaceDocument_DocumentWidget_(d, cachedDoc); |
1705 | updateWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d), width_Widget(d)); | ||
1705 | } | 1706 | } |
1706 | else if (setSource) { | 1707 | else if (setSource) { |
1707 | setSource_DocumentWidget(d, &str); | 1708 | setSource_DocumentWidget(d, &str); |
@@ -2498,7 +2499,7 @@ static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumen | |||
2498 | /* TODO: First *fully* visible run? */ | 2499 | /* TODO: First *fully* visible run? */ |
2499 | voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds); | 2500 | voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds); |
2500 | } | 2501 | } |
2501 | setWidth_GmDocument(d->doc, newWidth, (width_Widget(d) - newWidth) / 2); | 2502 | setWidth_GmDocument(d->doc, newWidth, width_Widget(d)); |
2502 | setWidth_Banner(d->banner, newWidth); | 2503 | setWidth_Banner(d->banner, newWidth); |
2503 | documentRunsInvalidated_DocumentWidget_(d); | 2504 | documentRunsInvalidated_DocumentWidget_(d); |
2504 | if (runLoc && !keepCenter) { | 2505 | if (runLoc && !keepCenter) { |
@@ -2734,6 +2735,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2734 | } | 2735 | } |
2735 | else if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed") || | 2736 | else if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed") || |
2736 | equal_Command(cmd, "keyroot.changed")) { | 2737 | equal_Command(cmd, "keyroot.changed")) { |
2738 | if (equal_Command(cmd, "font.changed")) { | ||
2739 | invalidateCachedLayout_History(d->mod.history); | ||
2740 | } | ||
2737 | /* Alt/Option key may be involved in window size changes. */ | 2741 | /* Alt/Option key may be involved in window size changes. */ |
2738 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 2742 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
2739 | d->phoneToolbar = findWidget_App("toolbar"); | 2743 | d->phoneToolbar = findWidget_App("toolbar"); |
diff --git a/src/ui/text.c b/src/ui/text.c index 09c01d46..46e355d7 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -2114,7 +2114,7 @@ iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { | |||
2114 | const int width = d->mode == word_WrapTextMode | 2114 | const int width = d->mode == word_WrapTextMode |
2115 | ? tryAdvance_Text(fontId, text, d->maxWidth, &endPos).x | 2115 | ? tryAdvance_Text(fontId, text, d->maxWidth, &endPos).x |
2116 | : tryAdvanceNoWrap_Text(fontId, text, d->maxWidth, &endPos).x; | 2116 | : tryAdvanceNoWrap_Text(fontId, text, d->maxWidth, &endPos).x; |
2117 | notify_WrapText_(d, endPos, 0, width, iFalse); | 2117 | notify_WrapText_(d, endPos, (iTextAttrib){ .colorId = color }, 0, width); |
2118 | drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endPos }); | 2118 | drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endPos }); |
2119 | text.start = endPos; | 2119 | text.start = endPos; |
2120 | pos.y += lineHeight_Text(fontId); | 2120 | pos.y += lineHeight_Text(fontId); |
diff --git a/src/ui/text_simple.c b/src/ui/text_simple.c index 8b1de64a..81fb94a5 100644 --- a/src/ui/text_simple.c +++ b/src/ui/text_simple.c | |||
@@ -61,6 +61,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
61 | and other non-complex LTR scripts. Composed glyphs are not supported (must rely on text | 61 | and other non-complex LTR scripts. Composed glyphs are not supported (must rely on text |
62 | being in a pre-composed form). This algorithm is used if HarfBuzz is not available. */ | 62 | being in a pre-composed form). This algorithm is used if HarfBuzz is not available. */ |
63 | const iInt2 orig = args->pos; | 63 | const iInt2 orig = args->pos; |
64 | iTextAttrib attrib = { .colorId = args->color }; | ||
64 | iRect bounds = { orig, init_I2(0, d->height) }; | 65 | iRect bounds = { orig, init_I2(0, d->height) }; |
65 | float xpos = orig.x; | 66 | float xpos = orig.x; |
66 | float xposMax = xpos; | 67 | float xposMax = xpos; |
@@ -86,7 +87,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
86 | // } | 87 | // } |
87 | const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2()); | 88 | const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2()); |
88 | const iBool checkHitChar = wrap && wrap->hitChar; | 89 | const iBool checkHitChar = wrap && wrap->hitChar; |
89 | const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); | 90 | const iBool isMonospaced = isMonospaced_Font(d) && !(mode & alwaysVariableWidthFlag_RunMode); |
90 | if (isMonospaced) { | 91 | if (isMonospaced) { |
91 | monoAdvance = glyph_Font_(d, 'M')->advance; | 92 | monoAdvance = glyph_Font_(d, 'M')->advance; |
92 | } | 93 | } |
@@ -166,7 +167,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
166 | /* TODO: Check out if `uc_wordbreak_property()` from libunistring can be used here. */ | 167 | /* TODO: Check out if `uc_wordbreak_property()` from libunistring can be used here. */ |
167 | if (ch == '\n') { | 168 | if (ch == '\n') { |
168 | /* Notify about the wrap. */ | 169 | /* Notify about the wrap. */ |
169 | if (!notify_WrapText_(wrap, chPos, 0, iMax(xpos, xposExtend) - orig.x, iFalse)) { | 170 | if (!notify_WrapText_(wrap, chPos, attrib, 0, iMax(xpos, xposExtend) - orig.x)) { |
170 | break; | 171 | break; |
171 | } | 172 | } |
172 | lastWordEnd = NULL; | 173 | lastWordEnd = NULL; |
@@ -246,7 +247,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
246 | wrapPos = iMin(wrapPos, args->text.end); | 247 | wrapPos = iMin(wrapPos, args->text.end); |
247 | advance = wrapAdvance; | 248 | advance = wrapAdvance; |
248 | } | 249 | } |
249 | if (!notify_WrapText_(wrap, wrapPos, 0, advance, iFalse)) { | 250 | if (!notify_WrapText_(wrap, wrapPos, attrib, 0, advance)) { |
250 | break; | 251 | break; |
251 | } | 252 | } |
252 | lastWordEnd = NULL; | 253 | lastWordEnd = NULL; |
@@ -282,8 +283,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
282 | } | 283 | } |
283 | /* Symbols and emojis are NOT monospaced, so must conform when the primary font | 284 | /* Symbols and emojis are NOT monospaced, so must conform when the primary font |
284 | is monospaced. Except with Japanese script, that's larger than the normal monospace. */ | 285 | is monospaced. Except with Japanese script, that's larger than the normal monospace. */ |
285 | const iBool useMonoAdvance = | 286 | const iBool useMonoAdvance = monoAdvance > 0; // && !isJapanese_FontId(fontId_Text_(glyph->font)); |
286 | monoAdvance > 0 && !isJapanese_FontId(fontId_Text_(glyph->font)); | ||
287 | const float advance = (useMonoAdvance && glyph->advance > 0 ? monoAdvance : glyph->advance); | 287 | const float advance = (useMonoAdvance && glyph->advance > 0 ? monoAdvance : glyph->advance); |
288 | if (!isMeasuring_(mode) && ch != 0x20 /* don't bother rendering spaces */) { | 288 | if (!isMeasuring_(mode) && ch != 0x20 /* don't bother rendering spaces */) { |
289 | if (useMonoAdvance && dst.w > advance && glyph->font != d && !isEmoji) { | 289 | if (useMonoAdvance && dst.w > advance && glyph->font != d && !isEmoji) { |
@@ -328,9 +328,9 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
328 | if (enableKerning_Text && next) { | 328 | if (enableKerning_Text && next) { |
329 | const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next); | 329 | const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next); |
330 | int kern = stbtt_GetGlyphKernAdvance( | 330 | int kern = stbtt_GetGlyphKernAdvance( |
331 | &glyph->font->font, index_Glyph_(glyph), nextGlyphIndex); | 331 | &glyph->font->fontFile->stbInfo, index_Glyph_(glyph), nextGlyphIndex); |
332 | /* Nunito needs some kerning fixes. */ | 332 | /* Nunito needs some kerning fixes. */ |
333 | if (glyph->font->family == nunito_TextFont) { | 333 | if (glyph->font->fontSpec->flags & fixNunitoKerning_FontSpecFlag) { |
334 | if (ch == 'W' && (next == 'i' || next == 'h')) { | 334 | if (ch == 'W' && (next == 'i' || next == 'h')) { |
335 | kern = -30; | 335 | kern = -30; |
336 | } | 336 | } |
@@ -362,7 +362,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
362 | break; | 362 | break; |
363 | } | 363 | } |
364 | } | 364 | } |
365 | notify_WrapText_(wrap, chPos, 0, xpos - orig.x, iFalse); | 365 | notify_WrapText_(wrap, chPos, attrib, 0, xpos - orig.x); |
366 | if (checkHitChar && wrap->hitChar == args->text.end) { | 366 | if (checkHitChar && wrap->hitChar == args->text.end) { |
367 | wrap->hitAdvance_out = sub_I2(init_I2(xpos, ypos), orig); | 367 | wrap->hitAdvance_out = sub_I2(init_I2(xpos, ypos), orig); |
368 | } | 368 | } |
diff --git a/src/ui/util.c b/src/ui/util.c index 6d0453ff..8affa57e 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -1023,11 +1023,13 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) { | |||
1023 | setFlags_Widget(d, commandOnMouseMiss_WidgetFlag, iTrue); | 1023 | setFlags_Widget(d, commandOnMouseMiss_WidgetFlag, iTrue); |
1024 | setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iFalse); | 1024 | setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iFalse); |
1025 | arrange_Widget(d); /* need to know the height */ | 1025 | arrange_Widget(d); /* need to know the height */ |
1026 | iBool allowOverflow = iFalse; | ||
1026 | /* A vertical offset determined by a possible selected label in the menu. */ { | 1027 | /* A vertical offset determined by a possible selected label in the menu. */ { |
1027 | iConstForEach(ObjectList, child, children_Widget(d)) { | 1028 | iConstForEach(ObjectList, child, children_Widget(d)) { |
1028 | const iWidget *item = constAs_Widget(child.object); | 1029 | const iWidget *item = constAs_Widget(child.object); |
1029 | if (flags_Widget(item) & selected_WidgetFlag) { | 1030 | if (flags_Widget(item) & selected_WidgetFlag) { |
1030 | windowCoord.y -= item->rect.pos.y; | 1031 | windowCoord.y -= item->rect.pos.y; |
1032 | allowOverflow = iTrue; | ||
1031 | } | 1033 | } |
1032 | } | 1034 | } |
1033 | } | 1035 | } |
@@ -1129,11 +1131,13 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) { | |||
1129 | rightExcess += r; | 1131 | rightExcess += r; |
1130 | } | 1132 | } |
1131 | #endif | 1133 | #endif |
1132 | if (bottomExcess > 0 && (!isPortraitPhone || !isSlidePanel)) { | 1134 | if (!allowOverflow) { |
1133 | d->rect.pos.y -= bottomExcess; | 1135 | if (bottomExcess > 0 && (!isPortraitPhone || !isSlidePanel)) { |
1134 | } | 1136 | d->rect.pos.y -= bottomExcess; |
1135 | if (topExcess > 0) { | 1137 | } |
1136 | d->rect.pos.y += topExcess; | 1138 | if (topExcess > 0) { |
1139 | d->rect.pos.y += topExcess; | ||
1140 | } | ||
1137 | } | 1141 | } |
1138 | if (rightExcess > 0) { | 1142 | if (rightExcess > 0) { |
1139 | d->rect.pos.x -= rightExcess; | 1143 | d->rect.pos.x -= rightExcess; |
@@ -1455,12 +1459,17 @@ void addTabCloseButton_Widget(iWidget *tabs, const iWidget *page, const char *co | |||
1455 | iLabelWidget *tabButton = tabButtonForPage_Widget_(tabs, page); | 1459 | iLabelWidget *tabButton = tabButtonForPage_Widget_(tabs, page); |
1456 | setPadding_Widget(as_Widget(tabButton), 0, 0, 0, gap_UI / 4); | 1460 | setPadding_Widget(as_Widget(tabButton), 0, 0, 0, gap_UI / 4); |
1457 | setFlags_Widget(as_Widget(tabButton), arrangeVertical_WidgetFlag | resizeHeightOfChildren_WidgetFlag, iTrue); | 1461 | setFlags_Widget(as_Widget(tabButton), arrangeVertical_WidgetFlag | resizeHeightOfChildren_WidgetFlag, iTrue); |
1462 | #if defined (iPlatformApple) | ||
1463 | const int64_t edge = moveToParentLeftEdge_WidgetFlag; | ||
1464 | #else | ||
1465 | const int64_t edge = moveToParentRightEdge_WidgetFlag; | ||
1466 | #endif | ||
1458 | iLabelWidget *close = addChildFlags_Widget( | 1467 | iLabelWidget *close = addChildFlags_Widget( |
1459 | as_Widget(tabButton), | 1468 | as_Widget(tabButton), |
1460 | iClob(new_LabelWidget(close_Icon, | 1469 | iClob(new_LabelWidget(close_Icon, |
1461 | format_CStr("%s id:%s", command, cstr_String(id_Widget(page))))), | 1470 | format_CStr("%s id:%s", command, cstr_String(id_Widget(page))))), |
1462 | moveToParentRightEdge_WidgetFlag | tight_WidgetFlag | frameless_WidgetFlag | | 1471 | edge | tight_WidgetFlag | frameless_WidgetFlag | noBackground_WidgetFlag | |
1463 | noBackground_WidgetFlag | hidden_WidgetFlag | visibleOnParentHover_WidgetFlag); | 1472 | hidden_WidgetFlag | visibleOnParentHover_WidgetFlag); |
1464 | if (deviceType_App() != desktop_AppDeviceType) { | 1473 | if (deviceType_App() != desktop_AppDeviceType) { |
1465 | setFlags_Widget(as_Widget(close), | 1474 | setFlags_Widget(as_Widget(close), |
1466 | hidden_WidgetFlag | visibleOnParentHover_WidgetFlag, iFalse); | 1475 | hidden_WidgetFlag | visibleOnParentHover_WidgetFlag, iFalse); |