summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-11-28 16:22:12 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-11-28 16:22:12 +0200
commitde1cbd837abc6a7dcfef6ed3ee357721cbc2907f (patch)
tree6795f9a08a2f89a0a79101ed5bb1d5260671f5da
parentedf1c0bb8b112879433f2e31fd9750c30e2d5144 (diff)
DocumentWidget: Horizontal scrolling improvements
Interaction with selection/found markers (will reset scrolling), and smooth horizontal scrolling with a mouse. IssueID #44
-rw-r--r--src/ui/documentwidget.c95
1 files changed, 75 insertions, 20 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index a1b26e7f..78036ef1 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -159,6 +159,9 @@ struct Impl_DocumentWidget {
159 iPtrArray visibleLinks; 159 iPtrArray visibleLinks;
160 iPtrArray visibleWideRuns; /* scrollable blocks */ 160 iPtrArray visibleWideRuns; /* scrollable blocks */
161 iArray wideRunOffsets; 161 iArray wideRunOffsets;
162 iAnim animWideRunOffset;
163 uint16_t animWideRunId;
164 iGmRunRange animWideRunRange;
162 iPtrArray visiblePlayers; /* currently playing audio */ 165 iPtrArray visiblePlayers; /* currently playing audio */
163 const iGmRun * grabbedPlayer; /* currently adjusting volume in a player */ 166 const iGmRun * grabbedPlayer; /* currently adjusting volume in a player */
164 float grabbedStartVolume; 167 float grabbedStartVolume;
@@ -204,6 +207,8 @@ void init_DocumentWidget(iDocumentWidget *d) {
204 d->redirectCount = 0; 207 d->redirectCount = 0;
205 d->initNormScrollY = 0; 208 d->initNormScrollY = 0;
206 init_Anim(&d->scrollY, 0); 209 init_Anim(&d->scrollY, 0);
210 d->animWideRunId = 0;
211 init_Anim(&d->animWideRunOffset, 0);
207 d->selectMark = iNullRange; 212 d->selectMark = iNullRange;
208 d->foundMark = iNullRange; 213 d->foundMark = iNullRange;
209 d->pageMargin = 5; 214 d->pageMargin = 5;
@@ -270,6 +275,13 @@ void deinit_DocumentWidget(iDocumentWidget *d) {
270 deinit_PersistentDocumentState(&d->mod); 275 deinit_PersistentDocumentState(&d->mod);
271} 276}
272 277
278static void resetWideRuns_DocumentWidget_(iDocumentWidget *d) {
279 clear_Array(&d->wideRunOffsets);
280 d->animWideRunId = 0;
281 init_Anim(&d->animWideRunOffset, 0);
282 iZap(d->animWideRunRange);
283}
284
273static void requestUpdated_DocumentWidget_(iAnyObject *obj) { 285static void requestUpdated_DocumentWidget_(iAnyObject *obj) {
274 iDocumentWidget *d = obj; 286 iDocumentWidget *d = obj;
275 const int wasUpdated = exchange_Atomic(&d->isRequestUpdated, iTrue); 287 const int wasUpdated = exchange_Atomic(&d->isRequestUpdated, iTrue);
@@ -386,6 +398,29 @@ static void invalidateVisibleLinks_DocumentWidget_(iDocumentWidget *d) {
386 } 398 }
387} 399}
388 400
401static int runOffset_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) {
402 if (run->preId && run->flags & wide_GmRunFlag) {
403 if (d->animWideRunId == run->preId) {
404 return -value_Anim(&d->animWideRunOffset);
405 }
406 const size_t numOffsets = size_Array(&d->wideRunOffsets);
407 const int *offsets = constData_Array(&d->wideRunOffsets);
408 if (run->preId <= numOffsets) {
409 return -offsets[run->preId - 1];
410 }
411 }
412 return 0;
413}
414
415static void invalidateWideRunsWithNonzeroOffset_DocumentWidget_(iDocumentWidget *d) {
416 iConstForEach(PtrArray, i, &d->visibleWideRuns) {
417 const iGmRun *run = i.ptr;
418 if (runOffset_DocumentWidget_(d, run)) {
419 insert_PtrSet(d->invalidRuns, run);
420 }
421 }
422}
423
389static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { 424static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) {
390 const iWidget *w = constAs_Widget(d); 425 const iWidget *w = constAs_Widget(d);
391 const iRect docBounds = documentBounds_DocumentWidget_(d); 426 const iRect docBounds = documentBounds_DocumentWidget_(d);
@@ -762,7 +797,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode
762 updateTheme_DocumentWidget_(d); 797 updateTheme_DocumentWidget_(d);
763 init_Anim(&d->scrollY, 0); 798 init_Anim(&d->scrollY, 0);
764 init_Anim(&d->sideOpacity, 0); 799 init_Anim(&d->sideOpacity, 0);
765 clear_Array(&d->wideRunOffsets); 800 resetWideRuns_DocumentWidget_(d);
766 d->state = ready_RequestState; 801 d->state = ready_RequestState;
767} 802}
768 803
@@ -951,7 +986,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
951 reset_GmDocument(d->doc); 986 reset_GmDocument(d->doc);
952 d->state = fetching_RequestState; 987 d->state = fetching_RequestState;
953 d->initNormScrollY = recent->normScrollY; 988 d->initNormScrollY = recent->normScrollY;
954 clear_Array(&d->wideRunOffsets); 989 resetWideRuns_DocumentWidget_(d);
955 /* Use the cached response data. */ 990 /* Use the cached response data. */
956 updateTrust_DocumentWidget_(d, resp); 991 updateTrust_DocumentWidget_(d, resp);
957 d->sourceTime = resp->when; 992 d->sourceTime = resp->when;
@@ -977,15 +1012,20 @@ static void refreshWhileScrolling_DocumentWidget_(iAny *ptr) {
977 iDocumentWidget *d = ptr; 1012 iDocumentWidget *d = ptr;
978 updateVisible_DocumentWidget_(d); 1013 updateVisible_DocumentWidget_(d);
979 refresh_Widget(d); 1014 refresh_Widget(d);
980 if (!isFinished_Anim(&d->scrollY)) { 1015 if (d->animWideRunId) {
1016 for (const iGmRun *r = d->animWideRunRange.start; r != d->animWideRunRange.end; r++) {
1017 insert_PtrSet(d->invalidRuns, r);
1018 }
1019 }
1020 if (isFinished_Anim(&d->animWideRunOffset)) {
1021 d->animWideRunId = 0;
1022 }
1023 if (!isFinished_Anim(&d->scrollY) || !isFinished_Anim(&d->animWideRunOffset)) {
981 addTicker_App(refreshWhileScrolling_DocumentWidget_, d); 1024 addTicker_App(refreshWhileScrolling_DocumentWidget_, d);
982 } 1025 }
983} 1026}
984 1027
985static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int duration) { 1028static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int duration) {
986 if (offset == 0) {
987 return;
988 }
989 /* Get rid of link numbers when scrolling. */ 1029 /* Get rid of link numbers when scrolling. */
990 if (offset && d->flags & showLinkNumbers_DocumentWidgetFlag) { 1030 if (offset && d->flags & showLinkNumbers_DocumentWidgetFlag) {
991 d->flags &= ~showLinkNumbers_DocumentWidgetFlag; 1031 d->flags &= ~showLinkNumbers_DocumentWidgetFlag;
@@ -1030,7 +1070,8 @@ static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool ce
1030 scroll_DocumentWidget_(d, 0); /* clamp it */ 1070 scroll_DocumentWidget_(d, 0); /* clamp it */
1031} 1071}
1032 1072
1033static void scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, int delta) { 1073static void scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, int delta,
1074 int duration) {
1034 if (delta == 0) { 1075 if (delta == 0) {
1035 return; 1076 return;
1036 } 1077 }
@@ -1057,6 +1098,21 @@ static void scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos,
1057 insert_PtrSet(d->invalidRuns, r); 1098 insert_PtrSet(d->invalidRuns, r);
1058 } 1099 }
1059 refresh_Widget(d); 1100 refresh_Widget(d);
1101 d->selectMark = iNullRange;
1102 d->foundMark = iNullRange;
1103 }
1104 if (duration) {
1105 if (d->animWideRunId != run->preId || isFinished_Anim(&d->animWideRunOffset)) {
1106 d->animWideRunId = run->preId;
1107 init_Anim(&d->animWideRunOffset, oldOffset);
1108 }
1109 setValueEased_Anim(&d->animWideRunOffset, *offset, duration);
1110 d->animWideRunRange = range;
1111 addTicker_App(refreshWhileScrolling_DocumentWidget_, d);
1112 }
1113 else {
1114 d->animWideRunId = 0;
1115 init_Anim(&d->animWideRunOffset, 0);
1060 } 1116 }
1061 break; 1117 break;
1062 } 1118 }
@@ -1097,7 +1153,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
1097 case categorySuccess_GmStatusCode: 1153 case categorySuccess_GmStatusCode:
1098 init_Anim(&d->scrollY, 0); 1154 init_Anim(&d->scrollY, 0);
1099 reset_GmDocument(d->doc); /* new content incoming */ 1155 reset_GmDocument(d->doc); /* new content incoming */
1100 clear_Array(&d->wideRunOffsets); 1156 resetWideRuns_DocumentWidget_(d);
1101 updateDocument_DocumentWidget_(d, resp, iTrue); 1157 updateDocument_DocumentWidget_(d, resp, iTrue);
1102 break; 1158 break;
1103 case categoryRedirect_GmStatusCode: 1159 case categoryRedirect_GmStatusCode:
@@ -1764,6 +1820,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1764 } 1820 }
1765 } 1821 }
1766 } 1822 }
1823 invalidateWideRunsWithNonzeroOffset_DocumentWidget_(d); /* markers don't support offsets */
1824 resetWideRuns_DocumentWidget_(d);
1767 refresh_Widget(w); 1825 refresh_Widget(w);
1768 return iTrue; 1826 return iTrue;
1769 } 1827 }
@@ -2063,6 +2121,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2063 if (ev->wheel.which == 0) { /* Trackpad with precise scrolling w/inertia. */ 2121 if (ev->wheel.which == 0) { /* Trackpad with precise scrolling w/inertia. */
2064 stop_Anim(&d->scrollY); 2122 stop_Anim(&d->scrollY);
2065 iInt2 wheel = init_I2(ev->wheel.x, ev->wheel.y); 2123 iInt2 wheel = init_I2(ev->wheel.x, ev->wheel.y);
2124 /* Only scroll on one axis at a time. */
2066 if (iAbs(wheel.x) > iAbs(wheel.y)) { 2125 if (iAbs(wheel.x) > iAbs(wheel.y)) {
2067 wheel.y = 0; 2126 wheel.y = 0;
2068 } 2127 }
@@ -2070,7 +2129,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2070 wheel.x = 0; 2129 wheel.x = 0;
2071 } 2130 }
2072 scroll_DocumentWidget_(d, -wheel.y * get_Window()->pixelRatio * acceleration); 2131 scroll_DocumentWidget_(d, -wheel.y * get_Window()->pixelRatio * acceleration);
2073 scrollWideBlock_DocumentWidget_(d, mouseCoord, wheel.x * get_Window()->pixelRatio); 2132 scrollWideBlock_DocumentWidget_(d, mouseCoord, wheel.x * get_Window()->pixelRatio, 0);
2074 } 2133 }
2075 else 2134 else
2076#endif 2135#endif
@@ -2092,7 +2151,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2092 /* accelerated speed for repeated wheelings */ 2151 /* accelerated speed for repeated wheelings */
2093 (!isFinished_Anim(&d->scrollY) && pos_Anim(&d->scrollY) < 0.25f ? 0.5f : 1.0f)); 2152 (!isFinished_Anim(&d->scrollY) && pos_Anim(&d->scrollY) < 0.25f ? 0.5f : 1.0f));
2094 scrollWideBlock_DocumentWidget_( 2153 scrollWideBlock_DocumentWidget_(
2095 d, mouseCoord, ev->wheel.x * lineHeight_Text(paragraph_FontId)); 2154 d, mouseCoord, ev->wheel.x * lineHeight_Text(paragraph_FontId) * 3, 167);
2096 } 2155 }
2097 iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iTrue); 2156 iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iTrue);
2098 return iTrue; 2157 return iTrue;
@@ -2244,6 +2303,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2244 /* Begin selecting a range of text. */ 2303 /* Begin selecting a range of text. */
2245 if (~d->flags & selecting_DocumentWidgetFlag) { 2304 if (~d->flags & selecting_DocumentWidgetFlag) {
2246 setFocus_Widget(NULL); /* TODO: Focus this document? */ 2305 setFocus_Widget(NULL); /* TODO: Focus this document? */
2306 invalidateWideRunsWithNonzeroOffset_DocumentWidget_(d);
2307 resetWideRuns_DocumentWidget_(d); /* Selections don't support horizontal scrolling. */
2247 iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iTrue); 2308 iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iTrue);
2248 d->selectMark.start = d->selectMark.end = 2309 d->selectMark.start = d->selectMark.end =
2249 sourceLoc_DocumentWidget_(d, d->click.startPos); 2310 sourceLoc_DocumentWidget_(d, d->click.startPos);
@@ -2444,15 +2505,9 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
2444 const iBool isHover = 2505 const iBool isHover =
2445 (run->linkId && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && 2506 (run->linkId && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId &&
2446 ~run->flags & decoration_GmRunFlag); 2507 ~run->flags & decoration_GmRunFlag);
2447 iInt2 visPos = add_I2(run->visBounds.pos, origin); 2508 const iInt2 visPos = addX_I2(add_I2(run->visBounds.pos, origin),
2448 /* Preformatted runs can be scrolled. */ 2509 /* Preformatted runs can be scrolled. */
2449 if (run->preId && run->flags & wide_GmRunFlag) { 2510 runOffset_DocumentWidget_(d->widget, run));
2450 const size_t numOffsets = size_Array(&d->widget->wideRunOffsets);
2451 const int *offsets = constData_Array(&d->widget->wideRunOffsets);
2452 if (run->preId <= numOffsets) {
2453 visPos.x -= offsets[run->preId - 1];
2454 }
2455 }
2456 fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackground_ColorId); 2511 fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackground_ColorId);
2457 if (run->linkId && ~run->flags & decoration_GmRunFlag) { 2512 if (run->linkId && ~run->flags & decoration_GmRunFlag) {
2458 fg = linkColor_GmDocument(doc, run->linkId, isHover ? textHover_GmLinkPart : text_GmLinkPart); 2513 fg = linkColor_GmDocument(doc, run->linkId, isHover ? textHover_GmLinkPart : text_GmLinkPart);
@@ -3015,7 +3070,7 @@ iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) {
3015 3070
3016void updateSize_DocumentWidget(iDocumentWidget *d) { 3071void updateSize_DocumentWidget(iDocumentWidget *d) {
3017 setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); 3072 setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d));
3018 clear_Array(&d->wideRunOffsets); 3073 resetWideRuns_DocumentWidget_(d);
3019 updateSideIconBuf_DocumentWidget_(d); 3074 updateSideIconBuf_DocumentWidget_(d);
3020 updateOutline_DocumentWidget_(d); 3075 updateOutline_DocumentWidget_(d);
3021 updateVisible_DocumentWidget_(d); 3076 updateVisible_DocumentWidget_(d);