diff options
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/color.h | 3 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 119 |
2 files changed, 105 insertions, 17 deletions
diff --git a/src/ui/color.h b/src/ui/color.h index 057ca42d..cd2f95d3 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -115,7 +115,8 @@ enum iColorId { | |||
115 | uiTextAppTitle_ColorId, | 115 | uiTextAppTitle_ColorId, |
116 | uiBackgroundSidebar_ColorId, | 116 | uiBackgroundSidebar_ColorId, |
117 | uiBackgroundMenu_ColorId, | 117 | uiBackgroundMenu_ColorId, |
118 | tmLinkCustomIconVisited_ColorId, | 118 | tmLinkCustomIconVisited_ColorId, /* derived from other theme colors */ |
119 | tmAltTextBackground_ColorId, /* derived from other theme colors */ | ||
119 | 120 | ||
120 | /* content theme colors */ | 121 | /* content theme colors */ |
121 | tmFirst_ColorId, | 122 | tmFirst_ColorId, |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 6bb16a93..93297c70 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -202,7 +202,8 @@ struct Impl_DocumentWidget { | |||
202 | iRangecc foundMark; | 202 | iRangecc foundMark; |
203 | int pageMargin; | 203 | int pageMargin; |
204 | iPtrArray visibleLinks; | 204 | iPtrArray visibleLinks; |
205 | iPtrArray visibleWideRuns; /* scrollable blocks */ | 205 | iPtrArray visiblePre; |
206 | iPtrArray visibleWideRuns; /* scrollable blocks; TODO: merge into `visiblePre` */ | ||
206 | iArray wideRunOffsets; | 207 | iArray wideRunOffsets; |
207 | iAnim animWideRunOffset; | 208 | iAnim animWideRunOffset; |
208 | uint16_t animWideRunId; | 209 | uint16_t animWideRunId; |
@@ -211,6 +212,8 @@ struct Impl_DocumentWidget { | |||
211 | const iGmRun * grabbedPlayer; /* currently adjusting volume in a player */ | 212 | const iGmRun * grabbedPlayer; /* currently adjusting volume in a player */ |
212 | float grabbedStartVolume; | 213 | float grabbedStartVolume; |
213 | int mediaTimer; | 214 | int mediaTimer; |
215 | const iGmRun * hoverPre; /* for clicking */ | ||
216 | const iGmRun * hoverAltPre; /* for drawing alt text */ | ||
214 | const iGmRun * hoverLink; | 217 | const iGmRun * hoverLink; |
215 | const iGmRun * contextLink; | 218 | const iGmRun * contextLink; |
216 | const iGmRun * firstVisibleRun; | 219 | const iGmRun * firstVisibleRun; |
@@ -220,6 +223,7 @@ struct Impl_DocumentWidget { | |||
220 | float initNormScrollY; | 223 | float initNormScrollY; |
221 | iAnim scrollY; | 224 | iAnim scrollY; |
222 | iAnim sideOpacity; | 225 | iAnim sideOpacity; |
226 | iAnim altTextOpacity; | ||
223 | iScrollWidget *scroll; | 227 | iScrollWidget *scroll; |
224 | iWidget * menu; | 228 | iWidget * menu; |
225 | iWidget * playerMenu; | 229 | iWidget * playerMenu; |
@@ -260,6 +264,8 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
260 | d->selectMark = iNullRange; | 264 | d->selectMark = iNullRange; |
261 | d->foundMark = iNullRange; | 265 | d->foundMark = iNullRange; |
262 | d->pageMargin = 5; | 266 | d->pageMargin = 5; |
267 | d->hoverPre = NULL; | ||
268 | d->hoverAltPre = NULL; | ||
263 | d->hoverLink = NULL; | 269 | d->hoverLink = NULL; |
264 | d->contextLink = NULL; | 270 | d->contextLink = NULL; |
265 | d->firstVisibleRun = NULL; | 271 | d->firstVisibleRun = NULL; |
@@ -267,12 +273,14 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
267 | d->visBuf = new_VisBuf(); | 273 | d->visBuf = new_VisBuf(); |
268 | d->invalidRuns = new_PtrSet(); | 274 | d->invalidRuns = new_PtrSet(); |
269 | init_Anim(&d->sideOpacity, 0); | 275 | init_Anim(&d->sideOpacity, 0); |
276 | init_Anim(&d->altTextOpacity, 0); | ||
270 | d->sourceStatus = none_GmStatusCode; | 277 | d->sourceStatus = none_GmStatusCode; |
271 | init_String(&d->sourceHeader); | 278 | init_String(&d->sourceHeader); |
272 | init_String(&d->sourceMime); | 279 | init_String(&d->sourceMime); |
273 | init_Block(&d->sourceContent, 0); | 280 | init_Block(&d->sourceContent, 0); |
274 | iZap(d->sourceTime); | 281 | iZap(d->sourceTime); |
275 | init_PtrArray(&d->visibleLinks); | 282 | init_PtrArray(&d->visibleLinks); |
283 | init_PtrArray(&d->visiblePre); | ||
276 | init_PtrArray(&d->visibleWideRuns); | 284 | init_PtrArray(&d->visibleWideRuns); |
277 | init_Array(&d->wideRunOffsets, sizeof(int)); | 285 | init_Array(&d->wideRunOffsets, sizeof(int)); |
278 | init_PtrArray(&d->visibleMedia); | 286 | init_PtrArray(&d->visibleMedia); |
@@ -322,6 +330,7 @@ void deinit_DocumentWidget(iDocumentWidget *d) { | |||
322 | deinit_Array(&d->wideRunOffsets); | 330 | deinit_Array(&d->wideRunOffsets); |
323 | deinit_PtrArray(&d->visibleMedia); | 331 | deinit_PtrArray(&d->visibleMedia); |
324 | deinit_PtrArray(&d->visibleWideRuns); | 332 | deinit_PtrArray(&d->visibleWideRuns); |
333 | deinit_PtrArray(&d->visiblePre); | ||
325 | deinit_PtrArray(&d->visibleLinks); | 334 | deinit_PtrArray(&d->visibleLinks); |
326 | delete_Block(d->certFingerprint); | 335 | delete_Block(d->certFingerprint); |
327 | delete_String(d->certSubject); | 336 | delete_String(d->certSubject); |
@@ -419,8 +428,11 @@ static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | |||
419 | } | 428 | } |
420 | d->lastVisibleRun = run; | 429 | d->lastVisibleRun = run; |
421 | } | 430 | } |
422 | if (run->preId && run->flags & wide_GmRunFlag) { | 431 | if (run->preId) { |
423 | pushBack_PtrArray(&d->visibleWideRuns, run); | 432 | pushBack_PtrArray(&d->visiblePre, run); |
433 | if (run->flags & wide_GmRunFlag) { | ||
434 | pushBack_PtrArray(&d->visibleWideRuns, run); | ||
435 | } | ||
424 | } | 436 | } |
425 | if (run->mediaType == audio_GmRunMediaType || run->mediaType == download_GmRunMediaType) { | 437 | if (run->mediaType == audio_GmRunMediaType || run->mediaType == download_GmRunMediaType) { |
426 | iAssert(run->mediaId); | 438 | iAssert(run->mediaId); |
@@ -501,10 +513,18 @@ static void invalidateWideRunsWithNonzeroOffset_DocumentWidget_(iDocumentWidget | |||
501 | } | 513 | } |
502 | } | 514 | } |
503 | 515 | ||
516 | static void animate_DocumentWidget_(void *ticker) { | ||
517 | iDocumentWidget *d = ticker; | ||
518 | if (!isFinished_Anim(&d->sideOpacity) || !isFinished_Anim(&d->altTextOpacity)) { | ||
519 | addTicker_App(animate_DocumentWidget_, d); | ||
520 | } | ||
521 | } | ||
522 | |||
504 | static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | 523 | static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { |
505 | const iWidget *w = constAs_Widget(d); | 524 | const iWidget *w = constAs_Widget(d); |
506 | const iRect docBounds = documentBounds_DocumentWidget_(d); | 525 | const iRect docBounds = documentBounds_DocumentWidget_(d); |
507 | const iGmRun * oldHoverLink = d->hoverLink; | 526 | const iGmRun * oldHoverLink = d->hoverLink; |
527 | d->hoverPre = NULL; | ||
508 | d->hoverLink = NULL; | 528 | d->hoverLink = NULL; |
509 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), value_Anim(&d->scrollY)); | 529 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), value_Anim(&d->scrollY)); |
510 | if (isHover_Widget(w) && (~d->flags & noHoverWhileScrolling_DocumentWidgetFlag) && | 530 | if (isHover_Widget(w) && (~d->flags & noHoverWhileScrolling_DocumentWidgetFlag) && |
@@ -525,11 +545,31 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
525 | if (d->hoverLink) { | 545 | if (d->hoverLink) { |
526 | invalidateLink_DocumentWidget_(d, d->hoverLink->linkId); | 546 | invalidateLink_DocumentWidget_(d, d->hoverLink->linkId); |
527 | } | 547 | } |
528 | refresh_Widget(as_Widget(d)); | 548 | refresh_Widget(w); |
549 | } | ||
550 | if (isHover_Widget(w)) { | ||
551 | iConstForEach(PtrArray, j, &d->visiblePre) { | ||
552 | const iGmRun *run = j.ptr; | ||
553 | if (contains_Rect(run->bounds, hoverPos)) { | ||
554 | d->hoverPre = run; | ||
555 | d->hoverAltPre = run; | ||
556 | break; | ||
557 | } | ||
558 | } | ||
559 | } | ||
560 | if (!d->hoverPre && targetValue_Anim(&d->altTextOpacity) > 0.5f) { | ||
561 | setValue_Anim(&d->altTextOpacity, 0.0f, 300); | ||
562 | animate_DocumentWidget_(d); | ||
563 | refresh_Widget(w); | ||
564 | } | ||
565 | else if (d->hoverPre && targetValue_Anim(&d->altTextOpacity) < 0.5f) { | ||
566 | setValue_Anim(&d->altTextOpacity, 1.0f, 0); | ||
567 | refresh_Widget(w); | ||
529 | } | 568 | } |
530 | if (isHover_Widget(w) && !contains_Widget(constAs_Widget(d->scroll), mouse)) { | 569 | if (isHover_Widget(w) && !contains_Widget(constAs_Widget(d->scroll), mouse)) { |
531 | setCursor_Window(get_Window(), | 570 | setCursor_Window(get_Window(), |
532 | d->hoverLink ? SDL_SYSTEM_CURSOR_HAND : SDL_SYSTEM_CURSOR_IBEAM); | 571 | d->hoverLink || d->hoverPre ? SDL_SYSTEM_CURSOR_HAND |
572 | : SDL_SYSTEM_CURSOR_IBEAM); | ||
533 | if (d->hoverLink && | 573 | if (d->hoverLink && |
534 | linkFlags_GmDocument(d->doc, d->hoverLink->linkId) & permanent_GmLinkFlag) { | 574 | linkFlags_GmDocument(d->doc, d->hoverLink->linkId) & permanent_GmLinkFlag) { |
535 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); /* not dismissable */ | 575 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); /* not dismissable */ |
@@ -537,13 +577,6 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
537 | } | 577 | } |
538 | } | 578 | } |
539 | 579 | ||
540 | static void animate_DocumentWidget_(void *ticker) { | ||
541 | iDocumentWidget *d = ticker; | ||
542 | if (!isFinished_Anim(&d->sideOpacity)) { | ||
543 | addTicker_App(animate_DocumentWidget_, d); | ||
544 | } | ||
545 | } | ||
546 | |||
547 | static void updateSideOpacity_DocumentWidget_(iDocumentWidget *d, iBool isAnimated) { | 580 | static void updateSideOpacity_DocumentWidget_(iDocumentWidget *d, iBool isAnimated) { |
548 | float opacity = 0.0f; | 581 | float opacity = 0.0f; |
549 | const iGmRun *banner = siteBanner_GmDocument(d->doc); | 582 | const iGmRun *banner = siteBanner_GmDocument(d->doc); |
@@ -649,6 +682,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
649 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); | 682 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); |
650 | clear_PtrArray(&d->visibleLinks); | 683 | clear_PtrArray(&d->visibleLinks); |
651 | clear_PtrArray(&d->visibleWideRuns); | 684 | clear_PtrArray(&d->visibleWideRuns); |
685 | clear_PtrArray(&d->visiblePre); | ||
652 | clear_PtrArray(&d->visibleMedia); | 686 | clear_PtrArray(&d->visibleMedia); |
653 | const iRangecc oldHeading = currentHeading_DocumentWidget_(d); | 687 | const iRangecc oldHeading = currentHeading_DocumentWidget_(d); |
654 | /* Scan for visible runs. */ { | 688 | /* Scan for visible runs. */ { |
@@ -765,6 +799,8 @@ static iRangecc bannerText_DocumentWidget_(const iDocumentWidget *d) { | |||
765 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { | 799 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { |
766 | d->foundMark = iNullRange; | 800 | d->foundMark = iNullRange; |
767 | d->selectMark = iNullRange; | 801 | d->selectMark = iNullRange; |
802 | d->hoverPre = NULL; | ||
803 | d->hoverAltPre = NULL; | ||
768 | d->hoverLink = NULL; | 804 | d->hoverLink = NULL; |
769 | d->contextLink = NULL; | 805 | d->contextLink = NULL; |
770 | d->firstVisibleRun = NULL; | 806 | d->firstVisibleRun = NULL; |
@@ -1199,6 +1235,18 @@ static void scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, | |||
1199 | } | 1235 | } |
1200 | } | 1236 | } |
1201 | 1237 | ||
1238 | static void togglePreFold_DocumentWidget_(iDocumentWidget *d, uint16_t preId) { | ||
1239 | d->hoverPre = NULL; | ||
1240 | d->hoverAltPre = NULL; | ||
1241 | d->selectMark = iNullRange; | ||
1242 | foldPre_GmDocument(d->doc, preId); | ||
1243 | redoLayout_GmDocument(d->doc); | ||
1244 | scroll_DocumentWidget_(d, 0); | ||
1245 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); | ||
1246 | invalidate_DocumentWidget_(d); | ||
1247 | refresh_Widget(as_Widget(d)); | ||
1248 | } | ||
1249 | |||
1202 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | 1250 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { |
1203 | if (!d->request) { | 1251 | if (!d->request) { |
1204 | return; | 1252 | return; |
@@ -2681,6 +2729,10 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
2681 | } | 2729 | } |
2682 | if (!isMoved_Click(&d->click)) { | 2730 | if (!isMoved_Click(&d->click)) { |
2683 | setFocus_Widget(NULL); | 2731 | setFocus_Widget(NULL); |
2732 | if (d->hoverPre) { | ||
2733 | togglePreFold_DocumentWidget_(d, d->hoverPre->preId); | ||
2734 | return iTrue; | ||
2735 | } | ||
2684 | if (d->hoverLink) { | 2736 | if (d->hoverLink) { |
2685 | const iGmLinkId linkId = d->hoverLink->linkId; | 2737 | const iGmLinkId linkId = d->hoverLink->linkId; |
2686 | const int linkFlags = linkFlags_GmDocument(d->doc, linkId); | 2738 | const int linkFlags = linkFlags_GmDocument(d->doc, linkId); |
@@ -2994,7 +3046,14 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
2994 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); /* link is inactive */ | 3046 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); /* link is inactive */ |
2995 | } | 3047 | } |
2996 | } | 3048 | } |
2997 | if (run->flags & siteBanner_GmRunFlag) { | 3049 | if (run->flags & altText_GmRunFlag) { |
3050 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); | ||
3051 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmAltTextBackground_ColorId); | ||
3052 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); | ||
3053 | drawWrapRange_Text(run->font, add_I2(visPos, margin), | ||
3054 | run->visBounds.size.x - 2 * margin.x, run->color, run->text); | ||
3055 | } | ||
3056 | else if (run->flags & siteBanner_GmRunFlag) { | ||
2998 | /* Banner background. */ | 3057 | /* Banner background. */ |
2999 | iRect bannerBack = initCorners_Rect(topLeft_Rect(d->widgetBounds), | 3058 | iRect bannerBack = initCorners_Rect(topLeft_Rect(d->widgetBounds), |
3000 | init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))), | 3059 | init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))), |
@@ -3157,8 +3216,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
3157 | } | 3216 | } |
3158 | } | 3217 | } |
3159 | } | 3218 | } |
3160 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); | 3219 | drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); |
3161 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); | 3220 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); |
3162 | } | 3221 | } |
3163 | 3222 | ||
3164 | static int drawSideRect_(iPaint *p, iRect rect) { | 3223 | static int drawSideRect_(iPaint *p, iRect rect) { |
@@ -3392,7 +3451,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
3392 | hasSiteBanner_GmDocument(d->doc) ? tmBannerBackground_ColorId | 3451 | hasSiteBanner_GmDocument(d->doc) ? tmBannerBackground_ColorId |
3393 | : tmBackground_ColorId); | 3452 | : tmBackground_ColorId); |
3394 | } | 3453 | } |
3395 | const int yBottom = yTop + size_GmDocument(d->doc).y; | 3454 | const int yBottom = yTop + size_GmDocument(d->doc).y + 1; |
3396 | if (yBottom < bottom_Rect(bounds)) { | 3455 | if (yBottom < bottom_Rect(bounds)) { |
3397 | fillRect_Paint(&ctx.paint, | 3456 | fillRect_Paint(&ctx.paint, |
3398 | init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), | 3457 | init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), |
@@ -3412,6 +3471,34 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
3412 | drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId); | 3471 | drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId); |
3413 | } | 3472 | } |
3414 | draw_Widget(w); | 3473 | draw_Widget(w); |
3474 | /* Alt text. */ | ||
3475 | const float altTextOpacity = value_Anim(&d->altTextOpacity); | ||
3476 | if (d->hoverAltPre && altTextOpacity > 0) { | ||
3477 | const iGmPreMeta *meta = preMeta_GmDocument(d->doc, d->hoverAltPre->preId); | ||
3478 | if (meta->flags & topLeft_GmPreMetaFlag && ~meta->flags & decoration_GmRunFlag && | ||
3479 | !isEmpty_Range(&meta->altText)) { | ||
3480 | const int margin = 2 * gap_UI; | ||
3481 | const int altFont = uiLabel_FontId; | ||
3482 | const int wrap = docBounds.size.x - 2 * margin; | ||
3483 | iInt2 pos = addY_I2(add_I2(docBounds.pos, meta->pixelRect.pos), | ||
3484 | -value_Anim(&d->scrollY)); | ||
3485 | const iInt2 textSize = advanceWrapRange_Text(altFont, wrap, meta->altText); | ||
3486 | pos.y -= textSize.y + gap_UI; | ||
3487 | pos.y = iMax(pos.y, top_Rect(bounds)); | ||
3488 | const iRect altRect = { pos, init_I2(docBounds.size.x, textSize.y) }; | ||
3489 | ctx.paint.alpha = altTextOpacity * 255; | ||
3490 | if (altTextOpacity < 1) { | ||
3491 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | ||
3492 | } | ||
3493 | fillRect_Paint(&ctx.paint, altRect, tmAltTextBackground_ColorId); | ||
3494 | drawRect_Paint(&ctx.paint, altRect, tmQuoteIcon_ColorId); | ||
3495 | setOpacity_Text(altTextOpacity); | ||
3496 | drawWrapRange_Text(altFont, addX_I2(pos, margin), wrap, | ||
3497 | tmQuote_ColorId, meta->altText); | ||
3498 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | ||
3499 | setOpacity_Text(1.0f); | ||
3500 | } | ||
3501 | } | ||
3415 | } | 3502 | } |
3416 | 3503 | ||
3417 | /*----------------------------------------------------------------------------------------------*/ | 3504 | /*----------------------------------------------------------------------------------------------*/ |