summaryrefslogtreecommitdiff
path: root/src/ui/documentwidget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r--src/ui/documentwidget.c226
1 files changed, 203 insertions, 23 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 351d1e62..0079465f 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -127,6 +127,18 @@ iDefineTypeConstruction(Model)
127 127
128/*----------------------------------------------------------------------------------------------*/ 128/*----------------------------------------------------------------------------------------------*/
129 129
130iDeclareType(OutlineItem)
131
132struct Impl_OutlineItem {
133 iRangecc text;
134 int font;
135 iRect rect;
136 int seenColor;
137 int sepColor;
138};
139
140/*----------------------------------------------------------------------------------------------*/
141
130static const int smoothSpeed_DocumentWidget_ = 120; /* unit: gap_Text per second */ 142static const int smoothSpeed_DocumentWidget_ = 120; /* unit: gap_Text per second */
131 143
132enum iRequestState { 144enum iRequestState {
@@ -161,6 +173,7 @@ struct Impl_DocumentWidget {
161 const iGmRun * contextLink; 173 const iGmRun * contextLink;
162 iBool noHoverWhileScrolling; 174 iBool noHoverWhileScrolling;
163 iBool showLinkNumbers; 175 iBool showLinkNumbers;
176 const iGmRun * lastVisibleRun;
164 iClick click; 177 iClick click;
165 float initNormScrollY; 178 float initNormScrollY;
166 int scrollY; 179 int scrollY;
@@ -170,6 +183,8 @@ struct Impl_DocumentWidget {
170 int smoothLastOffset; 183 int smoothLastOffset;
171 iBool smoothContinue; 184 iBool smoothContinue;
172 iAnim sideOpacity; 185 iAnim sideOpacity;
186 iAnim outlineOpacity;
187 iArray outline;
173 iWidget * menu; 188 iWidget * menu;
174 iVisBuf * visBuf; 189 iVisBuf * visBuf;
175 iPtrSet * invalidRuns; 190 iPtrSet * invalidRuns;
@@ -207,8 +222,10 @@ void init_DocumentWidget(iDocumentWidget *d) {
207 d->contextLink = NULL; 222 d->contextLink = NULL;
208 d->noHoverWhileScrolling = iFalse; 223 d->noHoverWhileScrolling = iFalse;
209 d->showLinkNumbers = iFalse; 224 d->showLinkNumbers = iFalse;
225 d->lastVisibleRun = NULL;
210 d->visBuf = new_VisBuf(); 226 d->visBuf = new_VisBuf();
211 d->invalidRuns = new_PtrSet(); 227 d->invalidRuns = new_PtrSet();
228 init_Array(&d->outline, sizeof(iOutlineItem));
212 init_Anim(&d->sideOpacity, 0); 229 init_Anim(&d->sideOpacity, 0);
213 init_String(&d->sourceMime); 230 init_String(&d->sourceMime);
214 init_Block(&d->sourceContent, 0); 231 init_Block(&d->sourceContent, 0);
@@ -227,6 +244,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
227void deinit_DocumentWidget(iDocumentWidget *d) { 244void deinit_DocumentWidget(iDocumentWidget *d) {
228 delete_VisBuf(d->visBuf); 245 delete_VisBuf(d->visBuf);
229 delete_PtrSet(d->invalidRuns); 246 delete_PtrSet(d->invalidRuns);
247 deinit_Array(&d->outline);
230 iRelease(d->media); 248 iRelease(d->media);
231 iRelease(d->request); 249 iRelease(d->request);
232 deinit_Block(&d->sourceContent); 250 deinit_Block(&d->sourceContent);
@@ -314,6 +332,7 @@ static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) {
314 332
315static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { 333static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) {
316 iDocumentWidget *d = context; 334 iDocumentWidget *d = context;
335 d->lastVisibleRun = run;
317 if (run->linkId && linkFlags_GmDocument(d->doc, run->linkId) & supportedProtocol_GmLinkFlag) { 336 if (run->linkId && linkFlags_GmDocument(d->doc, run->linkId) & supportedProtocol_GmLinkFlag) {
318 pushBack_PtrArray(&d->visibleLinks, run); 337 pushBack_PtrArray(&d->visibleLinks, run);
319 } 338 }
@@ -379,7 +398,7 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) {
379 398
380static void animate_DocumentWidget_(void *ticker) { 399static void animate_DocumentWidget_(void *ticker) {
381 iDocumentWidget *d = ticker; 400 iDocumentWidget *d = ticker;
382 if (!isFinished_Anim(&d->sideOpacity)) { 401 if (!isFinished_Anim(&d->sideOpacity) || !isFinished_Anim(&d->outlineOpacity)) {
383 addTicker_App(animate_DocumentWidget_, d); 402 addTicker_App(animate_DocumentWidget_, d);
384 } 403 }
385} 404}
@@ -390,7 +409,20 @@ static void updateSideOpacity_DocumentWidget_(iDocumentWidget *d) {
390 if (banner && bottom_Rect(banner->visBounds) < d->scrollY) { 409 if (banner && bottom_Rect(banner->visBounds) < d->scrollY) {
391 opacity = 1.0f; 410 opacity = 1.0f;
392 } 411 }
393 setValue_Anim(&d->sideOpacity, opacity, opacity < 0.5f ? 166 : 333); 412 setValue_Anim(&d->sideOpacity, opacity, opacity < 0.5f ? 100 : 200);
413 animate_DocumentWidget_(d);
414}
415
416static void updateOutlineOpacity_DocumentWidget_(iDocumentWidget *d) {
417 float opacity = 0.0f;
418 if (isEmpty_Array(&d->outline)) {
419 setValue_Anim(&d->outlineOpacity, 0.0f, 0);
420 return;
421 }
422 if (contains_Widget(constAs_Widget(d->scroll), mouseCoord_Window(get_Window()))) {
423 opacity = 1.0f;
424 }
425 setValue_Anim(&d->outlineOpacity, opacity, opacity > 0.5f? 166 : 333);
394 animate_DocumentWidget_(d); 426 animate_DocumentWidget_(d);
395} 427}
396 428
@@ -506,16 +538,77 @@ static void invalidate_DocumentWidget_(iDocumentWidget *d) {
506 clear_PtrSet(d->invalidRuns); 538 clear_PtrSet(d->invalidRuns);
507} 539}
508 540
541static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */
542static const int outlineMaxWidth_DocumentWidget_ = 65; /* times gap_UI */
543static const int outlinePadding_DocumentWidget_ = 3; /* times gap_UI */
544
545static int outlineWidth_DocumentWidget_(const iDocumentWidget *d) {
546 const iWidget *w = constAs_Widget(d);
547 const iRect bounds = bounds_Widget(w);
548 const int docWidth = documentWidth_DocumentWidget_(d);
549 int width =
550 (width_Rect(bounds) - docWidth) / 2 - gap_Text * d->pageMargin - gap_UI * d->pageMargin
551 - 2 * outlinePadding_DocumentWidget_ * gap_UI;
552 if (width < outlineMinWidth_DocumentWdiget_ * gap_UI) {
553 return outlineMinWidth_DocumentWdiget_ * gap_UI;
554 }
555 return iMin(width, outlineMaxWidth_DocumentWidget_ * gap_UI);
556}
557
558static iRangecc bannerText_DocumentWidget_(const iDocumentWidget *d) {
559 return isEmpty_String(d->titleUser) ? range_String(bannerText_GmDocument(d->doc))
560 : range_String(d->titleUser);
561}
562
563static void updateOutline_DocumentWidget_(iDocumentWidget *d) {
564 iWidget *w = as_Widget(d);
565 int outWidth = outlineWidth_DocumentWidget_(d);
566 clear_Array(&d->outline);
567 if (outWidth == 0 || d->state != ready_RequestState) {
568 return;
569 }
570 if (size_GmDocument(d->doc).y < height_Rect(bounds_Widget(w)) * 2) {
571 return; /* Too short */
572 }
573 iInt2 pos = zero_I2();
574// const iRangecc topText = urlHost_String(d->mod.url);
575// iInt2 size = advanceWrapRange_Text(uiContent_FontId, outWidth, topText);
576// pushBack_Array(&d->outline, &(iOutlineItem){ topText, uiContent_FontId, (iRect){ pos, size },
577// tmBannerTitle_ColorId, none_ColorId });
578// pos.y += size.y;
579 iInt2 size;
580 iConstForEach(Array, i, headings_GmDocument(d->doc)) {
581 const iGmHeading *head = i.value;
582 const int indent = head->level * 5 * gap_UI;
583 size = advanceWrapRange_Text(uiLabel_FontId, outWidth - indent, head->text);
584 if (head->level == 0) {
585 pos.y += gap_UI * 1.5f;
586 }
587 pushBack_Array(&d->outline,
588 &(iOutlineItem){ head->text,
589 uiLabel_FontId,
590 (iRect){ addX_I2(pos, indent), size },
591 head->level == 0 ? tmHeading1_ColorId
592 : head->level == 1 ? tmHeading2_ColorId
593 : tmHeading3_ColorId,
594 head->level == 0 ? tmQuoteIcon_ColorId : none_ColorId });
595 pos.y += size.y;
596 }
597}
598
509static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) { 599static void setSource_DocumentWidget_(iDocumentWidget *d, const iString *source) {
510 setUrl_GmDocument(d->doc, d->mod.url); 600 setUrl_GmDocument(d->doc, d->mod.url);
511 setSource_GmDocument( 601 setSource_GmDocument(
512 d->doc, source, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d)); 602 d->doc, source, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d));
513 d->foundMark = iNullRange; 603 d->foundMark = iNullRange;
514 d->selectMark = iNullRange; 604 d->selectMark = iNullRange;
515 d->hoverLink = NULL; 605 d->hoverLink = NULL;
516 d->contextLink = NULL; 606 d->contextLink = NULL;
607 d->lastVisibleRun = NULL;
608 setValue_Anim(&d->outlineOpacity, 0.0f, 0);
517 updateWindowTitle_DocumentWidget_(d); 609 updateWindowTitle_DocumentWidget_(d);
518 updateVisible_DocumentWidget_(d); 610 updateVisible_DocumentWidget_(d);
611 updateOutline_DocumentWidget_(d);
519 invalidate_DocumentWidget_(d); 612 invalidate_DocumentWidget_(d);
520 refresh_Widget(as_Widget(d)); 613 refresh_Widget(as_Widget(d));
521} 614}
@@ -751,6 +844,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
751 updateDocument_DocumentWidget_(d, resp); 844 updateDocument_DocumentWidget_(d, resp);
752 d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y; 845 d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y;
753 d->state = ready_RequestState; 846 d->state = ready_RequestState;
847 updateOutline_DocumentWidget_(d);
754 updateVisible_DocumentWidget_(d); 848 updateVisible_DocumentWidget_(d);
755 postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); 849 postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url));
756 return iTrue; 850 return iTrue;
@@ -1069,6 +1163,7 @@ static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) {
1069void updateSize_DocumentWidget(iDocumentWidget *d) { 1163void updateSize_DocumentWidget(iDocumentWidget *d) {
1070 setWidth_GmDocument( 1164 setWidth_GmDocument(
1071 d->doc, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d)); 1165 d->doc, documentWidth_DocumentWidget_(d), forceBreakWidth_DocumentWidget_(d));
1166 updateOutline_DocumentWidget_(d);
1072 updateVisible_DocumentWidget_(d); 1167 updateVisible_DocumentWidget_(d);
1073 invalidate_DocumentWidget_(d); 1168 invalidate_DocumentWidget_(d);
1074} 1169}
@@ -1087,6 +1182,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1087 scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); 1182 scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue);
1088 } 1183 }
1089 } 1184 }
1185 updateOutline_DocumentWidget_(d);
1090 invalidate_DocumentWidget_(d); 1186 invalidate_DocumentWidget_(d);
1091 dealloc_VisBuf(d->visBuf); 1187 dealloc_VisBuf(d->visBuf);
1092 refresh_Widget(w); 1188 refresh_Widget(w);
@@ -1109,6 +1205,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1109 updateSize_DocumentWidget(d); 1205 updateSize_DocumentWidget(d);
1110 updateFetchProgress_DocumentWidget_(d); 1206 updateFetchProgress_DocumentWidget_(d);
1111 } 1207 }
1208 updateOutlineOpacity_DocumentWidget_(d);
1112 updateWindowTitle_DocumentWidget_(d); 1209 updateWindowTitle_DocumentWidget_(d);
1113 allocVisBuffer_DocumentWidget_(d); 1210 allocVisBuffer_DocumentWidget_(d);
1114 return iFalse; 1211 return iFalse;
@@ -1213,6 +1310,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1213 } 1310 }
1214 iReleasePtr(&d->request); 1311 iReleasePtr(&d->request);
1215 updateVisible_DocumentWidget_(d); 1312 updateVisible_DocumentWidget_(d);
1313 updateOutline_DocumentWidget_(d);
1216 postCommandf_App("document.changed url:%s", cstr_String(d->mod.url)); 1314 postCommandf_App("document.changed url:%s", cstr_String(d->mod.url));
1217 return iFalse; 1315 return iFalse;
1218 } 1316 }
@@ -1559,6 +1657,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
1559 else { 1657 else {
1560 updateHover_DocumentWidget_(d, init_I2(ev->motion.x, ev->motion.y)); 1658 updateHover_DocumentWidget_(d, init_I2(ev->motion.x, ev->motion.y));
1561 } 1659 }
1660 updateOutlineOpacity_DocumentWidget_(d);
1562 } 1661 }
1563 if (ev->type == SDL_MOUSEBUTTONDOWN) { 1662 if (ev->type == SDL_MOUSEBUTTONDOWN) {
1564 if (ev->button.button == SDL_BUTTON_X1) { 1663 if (ev->button.button == SDL_BUTTON_X1) {
@@ -1728,11 +1827,6 @@ struct Impl_DrawContext {
1728 iBool showLinkNumbers; 1827 iBool showLinkNumbers;
1729}; 1828};
1730 1829
1731static iRangecc bannerText_DocumentWidget_(const iDocumentWidget *d) {
1732 return isEmpty_String(d->titleUser) ? range_String(bannerText_GmDocument(d->doc))
1733 : range_String(d->titleUser);
1734}
1735
1736static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color, 1830static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color,
1737 iRangecc mark, iBool *isInside) { 1831 iRangecc mark, iBool *isInside) {
1738 if (mark.start > mark.end) { 1832 if (mark.start > mark.end) {
@@ -1964,6 +2058,15 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
1964// drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); 2058// drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId);
1965} 2059}
1966 2060
2061static void drawSideRect_(iPaint *p, iRect rect, int thickness) {
2062 if (equal_Color(get_Color(tmBannerBackground_ColorId), get_Color(tmBackground_ColorId))) {
2063 drawRectThickness_Paint(p, rect, thickness, tmBannerIcon_ColorId);
2064 }
2065 else {
2066 fillRect_Paint(p, rect, tmBannerBackground_ColorId);
2067 }
2068}
2069
1967static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { 2070static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
1968 const iWidget *w = constAs_Widget(d); 2071 const iWidget *w = constAs_Widget(d);
1969 const iRect bounds = bounds_Widget(w); 2072 const iRect bounds = bounds_Widget(w);
@@ -1973,17 +2076,19 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
1973 float opacity = value_Anim(&d->sideOpacity); 2076 float opacity = value_Anim(&d->sideOpacity);
1974 const int minBannerSize = lineHeight_Text(banner_FontId) * 2; 2077 const int minBannerSize = lineHeight_Text(banner_FontId) * 2;
1975 const int avail = left_Rect(docBounds) - left_Rect(bounds) - 2 * margin; 2078 const int avail = left_Rect(docBounds) - left_Rect(bounds) - 2 * margin;
2079 iPaint p;
2080 init_Paint(&p);
2081 setClip_Paint(&p, bounds);
1976 if (avail > minBannerSize) { 2082 if (avail > minBannerSize) {
1977 if (banner && opacity > 0) { 2083 if (banner && opacity > 0) {
1978 setOpacity_Text(opacity); 2084 setOpacity_Text(opacity);
1979 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); 2085 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
1980 const iChar icon = siteIcon_GmDocument(d->doc); 2086 const iChar icon = siteIcon_GmDocument(d->doc);
1981 iRect rect = { add_I2(topLeft_Rect(bounds), init1_I2(margin)), init1_I2(minBannerSize) }; 2087 iRect rect = { add_I2(topLeft_Rect(bounds), init1_I2(margin)), init1_I2(minBannerSize) };
1982 iPaint p;
1983 init_Paint(&p);
1984 p.alpha = opacity * 255; 2088 p.alpha = opacity * 255;
1985 int offset = iMax(0, bottom_Rect(banner->visBounds) - d->scrollY); 2089 //int offset = iMax(0, bottom_Rect(banner->visBounds) - d->scrollY);
1986 rect.pos.y += offset; 2090 rect.pos.y += height_Rect(bounds) / 2 - rect.size.y / 2 - (banner ? banner->visBounds.size.y / 2 : 0); // offset;
2091 drawSideRect_(&p, rect, gap_UI / 2);
1987 if (equal_Color(get_Color(tmBannerBackground_ColorId), get_Color(tmBackground_ColorId))) { 2092 if (equal_Color(get_Color(tmBannerBackground_ColorId), get_Color(tmBackground_ColorId))) {
1988 drawRectThickness_Paint(&p, rect, gap_UI / 2, tmBannerIcon_ColorId); 2093 drawRectThickness_Paint(&p, rect, gap_UI / 2, tmBannerIcon_ColorId);
1989 } 2094 }
@@ -1993,28 +2098,103 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
1993 iString str; 2098 iString str;
1994 initUnicodeN_String(&str, &icon, 1); 2099 initUnicodeN_String(&str, &icon, 1);
1995 drawCentered_Text(banner_FontId, rect, iTrue, tmBannerIcon_ColorId, "%s", cstr_String(&str)); 2100 drawCentered_Text(banner_FontId, rect, iTrue, tmBannerIcon_ColorId, "%s", cstr_String(&str));
2101#if 0
1996 if (avail >= minBannerSize * 2) { 2102 if (avail >= minBannerSize * 2) {
1997 const char *endp; 2103 const char *endp;
1998 iRangecc text = bannerText_DocumentWidget_(d); 2104 iRangecc text = bannerText_DocumentWidget_(d);
1999 iInt2 pos = addY_I2(bottomLeft_Rect(rect), gap_Text); 2105 iInt2 pos = addY_I2(bottomLeft_Rect(rect), gap_Text);
2000 const int font = banner_FontId; 2106 const int font = heading3_FontId;
2001 while (!isEmpty_Range(&text)) { 2107 while (!isEmpty_Range(&text)) {
2002 tryAdvance_Text(font, text, avail - 2 * margin, &endp); 2108 tryAdvance_Text(font, text, avail - 2 * margin, &endp);
2003 drawRange_Text(font, pos, tmBannerSideTitle_ColorId, (iRangecc){ text.start, endp }); 2109 drawRange_Text(
2110 font, pos, tmBannerTitle_ColorId, (iRangecc){ text.start, endp });
2111// drawRange_Text(font,
2112// add_I2(pos, init1_I2(-gap_UI / 4)),
2113// tmBackground_ColorId,
2114// (iRangecc){ text.start, endp });
2004 text.start = endp; 2115 text.start = endp;
2005 pos.y += lineHeight_Text(font); 2116 pos.y += lineHeight_Text(font);
2006 } 2117 }
2007 } 2118 }
2119#endif
2008 setOpacity_Text(1.0f); 2120 setOpacity_Text(1.0f);
2009 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); 2121 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
2010 } 2122 }
2011 /* Update date. */
2012 drawString_Text(default_FontId,
2013 add_I2(bottomLeft_Rect(bounds),
2014 init_I2(margin, -margin + -2 * lineHeight_Text(default_FontId))),
2015 tmQuoteIcon_ColorId,
2016 collect_String(format_Time(&d->sourceTime, "Received\n%H:%M %b %d, %Y")));
2017 } 2123 }
2124 /* Reception timestamp. */
2125 if (isValid_Time(&d->sourceTime)) {
2126 const int font = uiLabel_FontId;
2127 const iString *recv =
2128 collect_String(format_Time(&d->sourceTime, "Received at %I:%M %p\non %b %d, %Y"));
2129 const iInt2 size = advanceRange_Text(font, range_String(recv));
2130 if (size.x <= avail) {
2131 drawString_Text(font,
2132 add_I2(bottomLeft_Rect(bounds),
2133 init_I2(margin,
2134 -margin + -size.y +
2135 iMax(0, scrollMax_DocumentWidget_(d) - d->scrollY))),
2136 tmQuoteIcon_ColorId,
2137 recv);
2138 }
2139 }
2140 /* Outline on the right side. */
2141 const float outlineOpacity = value_Anim(&d->outlineOpacity);
2142 if (!isEmpty_Array(&d->outline) && outlineOpacity > 0.0f) {
2143// const int font = uiLabel_FontId;
2144 //iRect outlineRect = initCorners_Rect(topRight_Rect(docBounds), bottomRight_Rect(bounds));
2145 //const int excess = width_Rect(outlineRect) - 75 * gap_UI;
2146// if (excess > 0) {
2147// adjustEdges_Rect(&outlineRect, 0, 0, 0, excess);
2148// }
2149// const int margin = gap_UI * d->pageMargin;
2150 const int innerWidth = outlineWidth_DocumentWidget_(d);
2151 const int outWidth = innerWidth + 2 * outlinePadding_DocumentWidget_ * gap_UI;
2152 const int topMargin = 0;//d->pageMargin * gap_UI; // + (banner ? banner->visBounds.size.y : 0);
2153 const int bottomMargin = 3 * gap_UI; //d->pageMargin * gap_UI;
2154 iInt2 pos = add_I2(topRight_Rect(bounds), init_I2(-outWidth - width_Widget(d->scroll), topMargin));
2155// const int lineWidth = avail - margin;
2156// pos.y = drawRangeWrap_Text(uiContent_FontId, pos, lineWidth, tmBannerIcon_ColorId,
2157// bannerText_DocumentWidget_(d));
2158// pos.y += gap_UI;
2159 const int scrollMax = scrollMax_DocumentWidget_(d);
2160 const int outHeight = bottom_Rect(((const iOutlineItem *) constBack_Array(&d->outline))->rect);
2161 const int oversize = outHeight - height_Rect(bounds) + topMargin + bottomMargin;
2162 const int scroll =
2163 (oversize > 0 && scrollMax > 0 ? oversize * d->scrollY / scrollMax_DocumentWidget_(d)
2164 : 0);
2165 /* Center short outlines vertically. */
2166 if (oversize < 0) {
2167 pos.y -= oversize / 2;// + (banner ? banner->visBounds.size.y / 2 : 0);
2168 }
2169 pos.y -= scroll;
2170 setOpacity_Text(outlineOpacity);
2171 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
2172 p.alpha = outlineOpacity * 255;
2173 drawSideRect_(
2174 &p,
2175 (iRect){ addY_I2(pos, -outlinePadding_DocumentWidget_ * gap_UI / 2),
2176 init_I2(outWidth, outHeight + outlinePadding_DocumentWidget_ * gap_UI * 1.5f) },
2177 1);
2178 iConstForEach(Array, i, &d->outline) {
2179 const iOutlineItem *item = i.value;
2180 iInt2 visPos = addX_I2(add_I2(pos, item->rect.pos), outlinePadding_DocumentWidget_ * gap_UI);
2181// visPos.y -= scroll;
2182// if (item->sepColor != none_ColorId) {
2183// drawHLine_Paint(&p, addY_I2(visPos, -gap_UI), outWidth, item->sepColor);
2184// }
2185// drawRect_Paint(&p, (iRect){ visPos, item->rect.size }, red_ColorId);
2186 const iBool isVisible = d->lastVisibleRun && d->lastVisibleRun->text.start >= item->text.start;
2187 drawWrapRange_Text(item->font,
2188 visPos,
2189 innerWidth - left_Rect(item->rect),
2190 index_ArrayConstIterator(&i) == 0 || isVisible ? item->seenColor
2191 : tmQuoteIcon_ColorId,
2192 item->text);
2193 }
2194 setOpacity_Text(1.0f);
2195 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
2196 }
2197 unsetClip_Paint(&p);
2018} 2198}
2019 2199
2020static void draw_DocumentWidget_(const iDocumentWidget *d) { 2200static void draw_DocumentWidget_(const iDocumentWidget *d) {