diff options
Diffstat (limited to 'src')
-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/lang.c | 2 | ||||
-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 | 24 | ||||
-rw-r--r-- | src/ui/widget.c | 96 |
12 files changed, 160 insertions, 72 deletions
@@ -2123,6 +2123,12 @@ void resetFonts_App(void) { | |||
2123 | } | 2123 | } |
2124 | } | 2124 | } |
2125 | 2125 | ||
2126 | static void invalidateCachedDocuments_App_(void) { | ||
2127 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { | ||
2128 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); | ||
2129 | } | ||
2130 | } | ||
2131 | |||
2126 | iBool handleCommand_App(const char *cmd) { | 2132 | iBool handleCommand_App(const char *cmd) { |
2127 | iApp *d = &app_; | 2133 | iApp *d = &app_; |
2128 | const iBool isFrozen = !d->window || d->window->isDrawFrozen; | 2134 | const iBool isFrozen = !d->window || d->window->isDrawFrozen; |
@@ -2287,7 +2293,10 @@ iBool handleCommand_App(const char *cmd) { | |||
2287 | if (!isFrozen) { | 2293 | if (!isFrozen) { |
2288 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */ | 2294 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */ |
2289 | } | 2295 | } |
2290 | d->prefs.zoomPercent = arg_Command(cmd); | 2296 | if (arg_Command(cmd) != d->prefs.zoomPercent) { |
2297 | d->prefs.zoomPercent = arg_Command(cmd); | ||
2298 | invalidateCachedDocuments_App_(); | ||
2299 | } | ||
2291 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); | 2300 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); |
2292 | if (!isFrozen) { | 2301 | if (!isFrozen) { |
2293 | postCommand_App("font.changed"); | 2302 | postCommand_App("font.changed"); |
@@ -2304,6 +2313,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2304 | delta /= 2; | 2313 | delta /= 2; |
2305 | } | 2314 | } |
2306 | d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200); | 2315 | d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200); |
2316 | invalidateCachedDocuments_App_(); | ||
2307 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); | 2317 | setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); |
2308 | if (!isFrozen) { | 2318 | if (!isFrozen) { |
2309 | postCommand_App("font.changed"); | 2319 | 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 *); |
@@ -84,6 +84,7 @@ static void clear_Lang_(iLang *d) { | |||
84 | 84 | ||
85 | static void load_Lang_(iLang *d, const char *id) { | 85 | static void load_Lang_(iLang *d, const char *id) { |
86 | /* Load compiled language strings from a resource blob. */ | 86 | /* Load compiled language strings from a resource blob. */ |
87 | /* TODO: How about an array for these? (id, blob, pluralType) */ | ||
87 | iUnused(id); | 88 | iUnused(id); |
88 | const iBlock *data = equal_CStr(id, "fi") ? &blobFi_Resources | 89 | const iBlock *data = equal_CStr(id, "fi") ? &blobFi_Resources |
89 | : equal_CStr(id, "fr") ? &blobFr_Resources | 90 | : equal_CStr(id, "fr") ? &blobFr_Resources |
@@ -93,6 +94,7 @@ static void load_Lang_(iLang *d, const char *id) { | |||
93 | : equal_CStr(id, "es_MX") ? &blobEs_MX_Resources | 94 | : equal_CStr(id, "es_MX") ? &blobEs_MX_Resources |
94 | : equal_CStr(id, "de") ? &blobDe_Resources | 95 | : equal_CStr(id, "de") ? &blobDe_Resources |
95 | : equal_CStr(id, "gl") ? &blobGl_Resources | 96 | : equal_CStr(id, "gl") ? &blobGl_Resources |
97 | : equal_CStr(id, "hu") ? &blobHu_Embedded | ||
96 | : equal_CStr(id, "ia") ? &blobIa_Resources | 98 | : equal_CStr(id, "ia") ? &blobIa_Resources |
97 | : equal_CStr(id, "ie") ? &blobIe_Resources | 99 | : equal_CStr(id, "ie") ? &blobIe_Resources |
98 | : equal_CStr(id, "isv") ? &blobIsv_Resources | 100 | : equal_CStr(id, "isv") ? &blobIsv_Resources |
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 e762b891..3805c666 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..5a58551b 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); |
@@ -2198,6 +2207,7 @@ iWidget *makePreferences_Widget(void) { | |||
2198 | { "${lang.fi} - fi", 0, 0, "uilang id:fi" }, | 2207 | { "${lang.fi} - fi", 0, 0, "uilang id:fi" }, |
2199 | { "${lang.fr} - fr", 0, 0, "uilang id:fr" }, | 2208 | { "${lang.fr} - fr", 0, 0, "uilang id:fr" }, |
2200 | { "${lang.gl} - gl", 0, 0, "uilang id:gl" }, | 2209 | { "${lang.gl} - gl", 0, 0, "uilang id:gl" }, |
2210 | { "${lang.hu} - hu", 0, 0, "uilang id:hu" }, | ||
2201 | { "${lang.ia} - ia", 0, 0, "uilang id:ia" }, | 2211 | { "${lang.ia} - ia", 0, 0, "uilang id:ia" }, |
2202 | { "${lang.ie} - ie", 0, 0, "uilang id:ie" }, | 2212 | { "${lang.ie} - ie", 0, 0, "uilang id:ie" }, |
2203 | { "${lang.isv} - isv", 0, 0, "uilang id:isv" }, | 2213 | { "${lang.isv} - isv", 0, 0, "uilang id:isv" }, |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 8a7127a2..b509cbe2 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
34 | #include <the_Foundation/ptrarray.h> | 34 | #include <the_Foundation/ptrarray.h> |
35 | #include <the_Foundation/ptrset.h> | 35 | #include <the_Foundation/ptrset.h> |
36 | #include <SDL_mouse.h> | 36 | #include <SDL_mouse.h> |
37 | #include <SDL_timer.h> | ||
37 | #include <stdarg.h> | 38 | #include <stdarg.h> |
38 | 39 | ||
39 | #if defined (iPlatformAppleMobile) | 40 | #if defined (iPlatformAppleMobile) |
@@ -1136,18 +1137,36 @@ void scrollInfo_Widget(const iWidget *d, iWidgetScrollInfo *info) { | |||
1136 | } | 1137 | } |
1137 | } | 1138 | } |
1138 | 1139 | ||
1139 | iBool scrollOverflow_Widget(iWidget *d, int delta) { | 1140 | static iBool isOverflowScrollPossible_Widget_(const iWidget *d, int delta) { |
1141 | if (~d->flags & overflowScrollable_WidgetFlag) { | ||
1142 | return iFalse; | ||
1143 | } | ||
1140 | iRect bounds = boundsWithoutVisualOffset_Widget(d); | 1144 | iRect bounds = boundsWithoutVisualOffset_Widget(d); |
1141 | const iRect winRect = adjusted_Rect(safeRect_Root(d->root), | 1145 | const iRect winRect = adjusted_Rect(safeRect_Root(d->root), |
1142 | zero_I2(), | 1146 | zero_I2(), |
1143 | init_I2(0, -get_MainWindow()->keyboardHeight)); | 1147 | init_I2(0, -get_MainWindow()->keyboardHeight)); |
1144 | const int yTop = top_Rect(winRect); | 1148 | const int yTop = top_Rect(winRect); |
1145 | const int yBottom = bottom_Rect(winRect); | 1149 | const int yBottom = bottom_Rect(winRect); |
1146 | if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) < yBottom) { | 1150 | if (delta == 0) { |
1147 | return iFalse; /* fits inside just fine */ | 1151 | if (top_Rect(bounds) >= yTop && bottom_Rect(bounds) <= yBottom) { |
1152 | return iFalse; /* fits inside just fine */ | ||
1153 | } | ||
1154 | } | ||
1155 | else if (delta > 0) { | ||
1156 | return top_Rect(bounds) < yTop; | ||
1157 | } | ||
1158 | return bottom_Rect(bounds) > yBottom; | ||
1159 | } | ||
1160 | |||
1161 | iBool scrollOverflow_Widget(iWidget *d, int delta) { | ||
1162 | if (!isOverflowScrollPossible_Widget_(d, delta)) { | ||
1163 | return iFalse; | ||
1148 | } | 1164 | } |
1149 | //const int safeBottom = rootSize.y - yBottom; | 1165 | iRect bounds = boundsWithoutVisualOffset_Widget(d); |
1150 | iRangei validPosRange = { bottom_Rect(winRect) - height_Rect(bounds), yTop }; | 1166 | const iRect winRect = adjusted_Rect(safeRect_Root(d->root), |
1167 | zero_I2(), | ||
1168 | init_I2(0, -get_MainWindow()->keyboardHeight)); | ||
1169 | iRangei validPosRange = { bottom_Rect(winRect) - height_Rect(bounds), top_Rect(winRect) }; | ||
1151 | if (validPosRange.start > validPosRange.end) { | 1170 | if (validPosRange.start > validPosRange.end) { |
1152 | validPosRange.start = validPosRange.end; /* no room to scroll */ | 1171 | validPosRange.start = validPosRange.end; /* no room to scroll */ |
1153 | } | 1172 | } |
@@ -1170,21 +1189,29 @@ iBool scrollOverflow_Widget(iWidget *d, int delta) { | |||
1170 | else { | 1189 | else { |
1171 | bounds.pos.y = iClamp(bounds.pos.y, validPosRange.start, validPosRange.end); | 1190 | bounds.pos.y = iClamp(bounds.pos.y, validPosRange.start, validPosRange.end); |
1172 | } | 1191 | } |
1173 | // if (delta >= 0) { | ||
1174 | // bounds.pos.y = iMin(bounds.pos.y, yTop); | ||
1175 | // } | ||
1176 | // else { | ||
1177 | // bounds.pos.y = iMax(bounds.pos.y, ); | ||
1178 | // } | ||
1179 | const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos); | 1192 | const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos); |
1180 | if (!isEqual_I2(newPos, d->rect.pos)) { | 1193 | if (!isEqual_I2(newPos, d->rect.pos)) { |
1181 | d->rect.pos = newPos; | 1194 | d->rect.pos = newPos; |
1182 | // refresh_Widget(d); | ||
1183 | postRefresh_App(); | 1195 | postRefresh_App(); |
1184 | } | 1196 | } |
1185 | return height_Rect(bounds) > height_Rect(winRect); | 1197 | return height_Rect(bounds) > height_Rect(winRect); |
1186 | } | 1198 | } |
1187 | 1199 | ||
1200 | static uint32_t lastHoverOverflowMotionTime_; | ||
1201 | |||
1202 | static void overflowHoverAnimation_(iAny *widget) { | ||
1203 | iWindow *win = window_Widget(widget); | ||
1204 | iInt2 coord = mouseCoord_Window(win, 0); | ||
1205 | /* A motion event will cause an overflow window to scroll. */ | ||
1206 | SDL_MouseMotionEvent ev = { | ||
1207 | .type = SDL_MOUSEMOTION, | ||
1208 | .windowID = SDL_GetWindowID(win->win), | ||
1209 | .x = coord.x / win->pixelRatio, | ||
1210 | .y = coord.y / win->pixelRatio, | ||
1211 | }; | ||
1212 | SDL_PushEvent((SDL_Event *) &ev); | ||
1213 | } | ||
1214 | |||
1188 | iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | 1215 | iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { |
1189 | if (d->flags & commandOnClick_WidgetFlag && | 1216 | if (d->flags & commandOnClick_WidgetFlag && |
1190 | (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) && | 1217 | (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) && |
@@ -1202,14 +1229,45 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
1202 | postCommand_Widget(d, "mouse.moved coord:%d %d", ev->motion.x, ev->motion.y); | 1229 | postCommand_Widget(d, "mouse.moved coord:%d %d", ev->motion.x, ev->motion.y); |
1203 | return iTrue; | 1230 | return iTrue; |
1204 | } | 1231 | } |
1205 | else if (d->flags & overflowScrollable_WidgetFlag && ev->type == SDL_MOUSEWHEEL && | 1232 | else if (d->flags & overflowScrollable_WidgetFlag && ~d->flags & visualOffset_WidgetFlag) { |
1206 | ~d->flags & visualOffset_WidgetFlag) { | 1233 | if (ev->type == SDL_MOUSEWHEEL) { |
1207 | int step = ev->wheel.y; | 1234 | int step = ev->wheel.y; |
1208 | if (!isPerPixel_MouseWheelEvent(&ev->wheel)) { | 1235 | if (!isPerPixel_MouseWheelEvent(&ev->wheel)) { |
1209 | step *= lineHeight_Text(uiLabel_FontId); | 1236 | step *= lineHeight_Text(uiLabel_FontId); |
1237 | } | ||
1238 | if (scrollOverflow_Widget(d, step)) { | ||
1239 | return iTrue; | ||
1240 | } | ||
1210 | } | 1241 | } |
1211 | if (scrollOverflow_Widget(d, step)) { | 1242 | else if (ev->type == SDL_MOUSEMOTION && ev->motion.which != SDL_TOUCH_MOUSEID && |
1212 | return iTrue; | 1243 | ev->motion.y >= 0) { |
1244 | /* TODO: Motion events occur frequently. Maybe it would help if these were handled | ||
1245 | via audiences that specifically register to listen for motion, to minimize the | ||
1246 | number of widgets that need to process them. */ | ||
1247 | const int hoverScrollLimit = 2 * lineHeight_Text(default_FontId); | ||
1248 | float speed = 0.0f; | ||
1249 | if (ev->motion.y < hoverScrollLimit) { | ||
1250 | speed = (hoverScrollLimit - ev->motion.y) / (float) hoverScrollLimit; | ||
1251 | } | ||
1252 | else { | ||
1253 | const int bottomLimit = bottom_Rect(rect_Root(d->root)) - hoverScrollLimit; | ||
1254 | if (ev->motion.y > bottomLimit ) { | ||
1255 | speed = -(ev->motion.y - bottomLimit) / (float) hoverScrollLimit; | ||
1256 | } | ||
1257 | } | ||
1258 | if (speed != 0.0f && isOverflowScrollPossible_Widget_(d, speed > 0 ? 1 : -1)) { | ||
1259 | const uint32_t nowTime = SDL_GetTicks(); | ||
1260 | uint32_t elapsed = nowTime - lastHoverOverflowMotionTime_; | ||
1261 | if (elapsed > 100) { | ||
1262 | elapsed = 16; | ||
1263 | } | ||
1264 | int step = elapsed * gap_UI / 16 * iClamp(speed, -1.0f, 1.0f); | ||
1265 | if (step != 0) { | ||
1266 | lastHoverOverflowMotionTime_ = nowTime; | ||
1267 | scrollOverflow_Widget(d, step); | ||
1268 | } | ||
1269 | addTicker_App(overflowHoverAnimation_, d); | ||
1270 | } | ||
1213 | } | 1271 | } |
1214 | } | 1272 | } |
1215 | switch (ev->type) { | 1273 | switch (ev->type) { |