summaryrefslogtreecommitdiff
path: root/src/ui/documentwidget.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-14 16:19:42 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-15 10:23:06 +0300
commitfefbd1c259c912fe126a5f34245a8b4c494cb753 (patch)
tree4f1add012361fc9c8430ee43987562252d123bb9 /src/ui/documentwidget.c
parentd3b4292242dd938503a31ba68066d53b29f364a4 (diff)
Progressive document rendering
VisBuf now guarantees that all the buffers are sequentially ordered. This ensure complete utilization of all the buffers. Previously some buffers were not used at all, or allocated to the same origin as another one (!). DocumentWidget is able to progressively fill up a buffer while scrolling. After any change to the visible region has been detected (and there are no ongoing changes), prerender the remaining runs one at a time. This solves the issue where one text run would be always unnecessarily rendered while scrolling, even if the scroll distance was just one pixel.
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r--src/ui/documentwidget.c276
1 files changed, 213 insertions, 63 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 70499ed5..ddca557b 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -20,7 +20,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23/* TODO: This file is a little too large. DocumentWidget could be split into 23/* TODO: This file is a little (!) too large. DocumentWidget could be split into
24 a couple of smaller objects. One for rendering the document, for instance. */ 24 a couple of smaller objects. One for rendering the document, for instance. */
25 25
26#include "documentwidget.h" 26#include "documentwidget.h"
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
41#include "labelwidget.h" 41#include "labelwidget.h"
42#include "media.h" 42#include "media.h"
43#include "paint.h" 43#include "paint.h"
44#include "periodic.h"
44#include "mediaui.h" 45#include "mediaui.h"
45#include "scrollwidget.h" 46#include "scrollwidget.h"
46#include "touch.h" 47#include "touch.h"
@@ -156,12 +157,14 @@ struct Impl_DrawBufs {
156 int flags; 157 int flags;
157 SDL_Texture * sideIconBuf; 158 SDL_Texture * sideIconBuf;
158 iTextBuf * timestampBuf; 159 iTextBuf * timestampBuf;
160 iRangei lastVis;
159}; 161};
160 162
161static void init_DrawBufs(iDrawBufs *d) { 163static void init_DrawBufs(iDrawBufs *d) {
162 d->flags = 0; 164 d->flags = 0;
163 d->sideIconBuf = NULL; 165 d->sideIconBuf = NULL;
164 d->timestampBuf = NULL; 166 d->timestampBuf = NULL;
167 iZap(d->lastVis);
165} 168}
166 169
167static void deinit_DrawBufs(iDrawBufs *d) { 170static void deinit_DrawBufs(iDrawBufs *d) {
@@ -175,9 +178,23 @@ iDefineTypeConstruction(DrawBufs)
175 178
176/*----------------------------------------------------------------------------------------------*/ 179/*----------------------------------------------------------------------------------------------*/
177 180
181iDeclareType(VisBufMeta)
182
183struct Impl_VisBufMeta {
184 iGmRunRange runsDrawn;
185};
186
187static void visBufInvalidated_(iVisBuf *d, size_t index) {
188 iVisBufMeta *meta = d->buffers[index].user;
189 iZap(meta->runsDrawn);
190}
191
192/*----------------------------------------------------------------------------------------------*/
193
178static void animate_DocumentWidget_ (void *ticker); 194static void animate_DocumentWidget_ (void *ticker);
179static void animateMedia_DocumentWidget_ (iDocumentWidget *d); 195static void animateMedia_DocumentWidget_ (iDocumentWidget *d);
180static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d); 196static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d);
197static void prerender_DocumentWidget_ (iAny *);
181 198
182static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */ 199static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */
183static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */ 200static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */
@@ -251,8 +268,8 @@ struct Impl_DocumentWidget {
251 const iGmRun * hoverAltPre; /* for drawing alt text */ 268 const iGmRun * hoverAltPre; /* for drawing alt text */
252 const iGmRun * hoverLink; 269 const iGmRun * hoverLink;
253 const iGmRun * contextLink; 270 const iGmRun * contextLink;
254 const iGmRun * firstVisibleRun; 271 iGmRunRange visibleRuns;
255 const iGmRun * lastVisibleRun; 272 iGmRunRange renderRuns;
256 iClick click; 273 iClick click;
257 iInt2 contextPos; /* coordinates of latest right click */ 274 iInt2 contextPos; /* coordinates of latest right click */
258 iString pendingGotoHeading; 275 iString pendingGotoHeading;
@@ -265,6 +282,7 @@ struct Impl_DocumentWidget {
265 iWidget * playerMenu; 282 iWidget * playerMenu;
266 iWidget * copyMenu; 283 iWidget * copyMenu;
267 iVisBuf * visBuf; 284 iVisBuf * visBuf;
285 iGmRunRange * visBufMeta;
268 iPtrSet * invalidRuns; 286 iPtrSet * invalidRuns;
269 iDrawBufs * drawBufs; /* dynamic state for drawing */ 287 iDrawBufs * drawBufs; /* dynamic state for drawing */
270 iTranslation * translation; 288 iTranslation * translation;
@@ -306,10 +324,17 @@ void init_DocumentWidget(iDocumentWidget *d) {
306 d->hoverAltPre = NULL; 324 d->hoverAltPre = NULL;
307 d->hoverLink = NULL; 325 d->hoverLink = NULL;
308 d->contextLink = NULL; 326 d->contextLink = NULL;
309 d->firstVisibleRun = NULL; 327 iZap(d->renderRuns);
310 d->lastVisibleRun = NULL; 328 iZap(d->visibleRuns);
311 d->visBuf = new_VisBuf(); 329 d->visBuf = new_VisBuf(); {
312 d->invalidRuns = new_PtrSet(); 330 d->visBufMeta = malloc(sizeof(iVisBufMeta) * numBuffers_VisBuf);
331 /* Additional metadata for each buffer. */
332 d->visBuf->bufferInvalidated = visBufInvalidated_;
333 for (size_t i = 0; i < numBuffers_VisBuf; i++) {
334 d->visBuf->buffers[i].user = d->visBufMeta + i;
335 }
336 }
337 d->invalidRuns = new_PtrSet();
313 init_Anim(&d->sideOpacity, 0); 338 init_Anim(&d->sideOpacity, 0);
314 init_Anim(&d->altTextOpacity, 0); 339 init_Anim(&d->altTextOpacity, 0);
315 d->sourceStatus = none_GmStatusCode; 340 d->sourceStatus = none_GmStatusCode;
@@ -349,9 +374,12 @@ void init_DocumentWidget(iDocumentWidget *d) {
349 374
350void deinit_DocumentWidget(iDocumentWidget *d) { 375void deinit_DocumentWidget(iDocumentWidget *d) {
351 removeTicker_App(animate_DocumentWidget_, d); 376 removeTicker_App(animate_DocumentWidget_, d);
377 removeTicker_App(prerender_DocumentWidget_, d);
378 remove_Periodic(periodic_App(), d);
352 delete_Translation(d->translation); 379 delete_Translation(d->translation);
353 delete_DrawBufs(d->drawBufs); 380 delete_DrawBufs(d->drawBufs);
354 delete_VisBuf(d->visBuf); 381 delete_VisBuf(d->visBuf);
382 free(d->visBufMeta);
355 delete_PtrSet(d->invalidRuns); 383 delete_PtrSet(d->invalidRuns);
356 iRelease(d->media); 384 iRelease(d->media);
357 iRelease(d->request); 385 iRelease(d->request);
@@ -486,10 +514,10 @@ static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) {
486static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { 514static void addVisible_DocumentWidget_(void *context, const iGmRun *run) {
487 iDocumentWidget *d = context; 515 iDocumentWidget *d = context;
488 if (~run->flags & decoration_GmRunFlag && !run->mediaId) { 516 if (~run->flags & decoration_GmRunFlag && !run->mediaId) {
489 if (!d->firstVisibleRun) { 517 if (!d->visibleRuns.start) {
490 d->firstVisibleRun = run; 518 d->visibleRuns.start = run;
491 } 519 }
492 d->lastVisibleRun = run; 520 d->visibleRuns.end = run;
493 } 521 }
494 if (run->preId) { 522 if (run->preId) {
495 pushBack_PtrArray(&d->visiblePre, run); 523 pushBack_PtrArray(&d->visiblePre, run);
@@ -743,14 +771,14 @@ static void animateMedia_DocumentWidget_(iDocumentWidget *d) {
743 771
744static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { 772static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) {
745 iRangecc heading = iNullRange; 773 iRangecc heading = iNullRange;
746 if (d->firstVisibleRun) { 774 if (d->visibleRuns.start) {
747 iConstForEach(Array, i, headings_GmDocument(d->doc)) { 775 iConstForEach(Array, i, headings_GmDocument(d->doc)) {
748 const iGmHeading *head = i.value; 776 const iGmHeading *head = i.value;
749 if (head->level == 0) { 777 if (head->level == 0) {
750 if (head->text.start <= d->firstVisibleRun->text.start) { 778 if (head->text.start <= d->visibleRuns.start->text.start) {
751 heading = head->text; 779 heading = head->text;
752 } 780 }
753 if (d->lastVisibleRun && head->text.start > d->lastVisibleRun->text.start) { 781 if (d->visibleRuns.end && head->text.start > d->visibleRuns.end->text.start) {
754 break; 782 break;
755 } 783 }
756 } 784 }
@@ -766,6 +794,14 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
766 !isSuccess_GmStatusCode(d->sourceStatus)); 794 !isSuccess_GmStatusCode(d->sourceStatus));
767 const iRangei visRange = visibleRange_DocumentWidget_(d); 795 const iRangei visRange = visibleRange_DocumentWidget_(d);
768 const iRect bounds = bounds_Widget(as_Widget(d)); 796 const iRect bounds = bounds_Widget(as_Widget(d));
797 if (!equal_Rangei(visRange, d->drawBufs->lastVis)) {
798 /* After scrolling/resizing stops, begin prendering the visbuf contents.
799 `Periodic` is used for detecting when the visible range has been modified. */
800 removeTicker_App(prerender_DocumentWidget_, d);
801 remove_Periodic(periodic_App(), d);
802 add_Periodic(periodic_App(), d,
803 format_CStr("document.render from:%d to:%d", visRange.start, visRange.end));
804 }
769 setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_DocumentWidget_(d) }); 805 setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_DocumentWidget_(d) });
770 const int docSize = size_GmDocument(d->doc).y; 806 const int docSize = size_GmDocument(d->doc).y;
771 setThumb_ScrollWidget(d->scroll, 807 setThumb_ScrollWidget(d->scroll,
@@ -777,7 +813,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
777 clear_PtrArray(&d->visibleMedia); 813 clear_PtrArray(&d->visibleMedia);
778 const iRangecc oldHeading = currentHeading_DocumentWidget_(d); 814 const iRangecc oldHeading = currentHeading_DocumentWidget_(d);
779 /* Scan for visible runs. */ { 815 /* Scan for visible runs. */ {
780 d->firstVisibleRun = NULL; 816 iZap(d->visibleRuns);
781 render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d); 817 render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d);
782 } 818 }
783 const iRangecc newHeading = currentHeading_DocumentWidget_(d); 819 const iRangecc newHeading = currentHeading_DocumentWidget_(d);
@@ -898,8 +934,8 @@ static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) {
898 d->hoverAltPre = NULL; 934 d->hoverAltPre = NULL;
899 d->hoverLink = NULL; 935 d->hoverLink = NULL;
900 d->contextLink = NULL; 936 d->contextLink = NULL;
901 d->firstVisibleRun = NULL; 937 iZap(d->visibleRuns);
902 d->lastVisibleRun = NULL; 938 iZap(d->renderRuns);
903} 939}
904 940
905void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { 941void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) {
@@ -1675,7 +1711,7 @@ static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumen
1675 } 1711 }
1676 /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top 1712 /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top
1677 of the visible area fixed. */ 1713 of the visible area fixed. */
1678 const iGmRun *run = keepCenter ? middleRun_DocumentWidget_(d) : d->firstVisibleRun; 1714 const iGmRun *run = keepCenter ? middleRun_DocumentWidget_(d) : d->visibleRuns.start;
1679 const char * runLoc = (run ? run->text.start : NULL); 1715 const char * runLoc = (run ? run->text.start : NULL);
1680 int voffset = 0; 1716 int voffset = 0;
1681 if (!keepCenter && run) { 1717 if (!keepCenter && run) {
@@ -1727,7 +1763,17 @@ static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
1727 1763
1728static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { 1764static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
1729 iWidget *w = as_Widget(d); 1765 iWidget *w = as_Widget(d);
1730 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { 1766 if (equal_Command(cmd, "document.render")) {
1767 const iRangei vis = { argLabel_Command(cmd, "from"), argLabel_Command(cmd, "to") };
1768 const iRangei current = visibleRange_DocumentWidget_(d);
1769 if (equal_Rangei(vis, current)) {
1770 remove_Periodic(periodic_App(), d);
1771 /* Scrolling has stopped, begin filling up the buffer. */
1772 addTicker_App(prerender_DocumentWidget_, d);
1773 }
1774 return iTrue;
1775 }
1776 else if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) {
1731 /* Alt/Option key may be involved in window size changes. */ 1777 /* Alt/Option key may be involved in window size changes. */
1732 setLinkNumberMode_DocumentWidget_(d, iFalse); 1778 setLinkNumberMode_DocumentWidget_(d, iFalse);
1733 d->phoneToolbar = findWidget_App("toolbar"); 1779 d->phoneToolbar = findWidget_App("toolbar");
@@ -3103,6 +3149,8 @@ iDeclareType(DrawContext)
3103struct Impl_DrawContext { 3149struct Impl_DrawContext {
3104 const iDocumentWidget *widget; 3150 const iDocumentWidget *widget;
3105 iRect widgetBounds; 3151 iRect widgetBounds;
3152 iRect docBounds;
3153 iRangei vis;
3106 iInt2 viewPos; /* document area origin */ 3154 iInt2 viewPos; /* document area origin */
3107 iPaint paint; 3155 iPaint paint;
3108 iBool inSelectMark; 3156 iBool inSelectMark;
@@ -3110,6 +3158,7 @@ struct Impl_DrawContext {
3110 iBool showLinkNumbers; 3158 iBool showLinkNumbers;
3111 iRect firstMarkRect; 3159 iRect firstMarkRect;
3112 iRect lastMarkRect; 3160 iRect lastMarkRect;
3161 iGmRunRange runsDrawn;
3113}; 3162};
3114 3163
3115static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color, 3164static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color,
@@ -3269,6 +3318,14 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2
3269static void drawRun_DrawContext_(void *context, const iGmRun *run) { 3318static void drawRun_DrawContext_(void *context, const iGmRun *run) {
3270 iDrawContext *d = context; 3319 iDrawContext *d = context;
3271 const iInt2 origin = d->viewPos; 3320 const iInt2 origin = d->viewPos;
3321 /* Keep track of the drawn visible runs. */ {
3322 if (!d->runsDrawn.start || run < d->runsDrawn.start) {
3323 d->runsDrawn.start = run;
3324 }
3325 if (!d->runsDrawn.end || run > d->runsDrawn.end) {
3326 d->runsDrawn.end = run;
3327 }
3328 }
3272 if (run->mediaType == image_GmRunMediaType) { 3329 if (run->mediaType == image_GmRunMediaType) {
3273 SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->mediaId); 3330 SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->mediaId);
3274 const iRect dst = moved_Rect(run->visBounds, origin); 3331 const iRect dst = moved_Rect(run->visBounds, origin);
@@ -3291,6 +3348,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
3291 /* Media UIs are drawn afterwards as a dynamic overlay. */ 3348 /* Media UIs are drawn afterwards as a dynamic overlay. */
3292 return; 3349 return;
3293 } 3350 }
3351// printf(" drawRun: {%s}\n", cstr_Rangecc(run->text));
3294 enum iColorId fg = run->color; 3352 enum iColorId fg = run->color;
3295 const iGmDocument *doc = d->widget->doc; 3353 const iGmDocument *doc = d->widget->doc;
3296 iBool isHover = 3354 iBool isHover =
@@ -3644,48 +3702,100 @@ static void drawPin_(iPaint *p, iRect rangeRect, int dir) {
3644 init1_I2(gap_UI * 2)), pinColor); 3702 init1_I2(gap_UI * 2)), pinColor);
3645} 3703}
3646 3704
3647static void draw_DocumentWidget_(const iDocumentWidget *d) { 3705static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx, iBool prerenderExtra) {
3648 const iWidget *w = constAs_Widget(d); 3706 iBool didDraw = iFalse;
3649 const iRect bounds = bounds_Widget(w); 3707 const iRect bounds = bounds_Widget(constAs_Widget(d));
3650 iVisBuf * visBuf = d->visBuf; /* will be updated now */
3651 if (width_Rect(bounds) <= 0) {
3652 return;
3653 }
3654 draw_Widget(w);
3655 if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) {
3656 updateTimestampBuf_DocumentWidget_(d);
3657 }
3658 if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) {
3659 updateSideIconBuf_DocumentWidget_(d);
3660 }
3661 allocVisBuffer_DocumentWidget_(d);
3662 const iRect ctxWidgetBounds = init_Rect( 3708 const iRect ctxWidgetBounds = init_Rect(
3663 0, 0, width_Rect(bounds) - constAs_Widget(d->scroll)->rect.size.x, height_Rect(bounds)); 3709 0, 0, width_Rect(bounds) - constAs_Widget(d->scroll)->rect.size.x, height_Rect(bounds));
3664 const iRect docBounds = documentBounds_DocumentWidget_(d);
3665 iDrawContext ctx = {
3666 .widget = d,
3667 .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0,
3668 };
3669 const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0;
3670 /* Currently visible region. */
3671 const iRangei vis = visibleRange_DocumentWidget_(d);
3672 const iRangei full = { 0, size_GmDocument(d->doc).y }; 3710 const iRangei full = { 0, size_GmDocument(d->doc).y };
3711 const iRangei vis = ctx->vis;
3712 iVisBuf *visBuf = d->visBuf; /* will be updated now */
3713 /* Swap buffers around to have room available both before and after the visible region. */
3714 allocVisBuffer_DocumentWidget_(d);
3673 reposition_VisBuf(visBuf, vis); 3715 reposition_VisBuf(visBuf, vis);
3674 iRangei invalidRange[iElemCount(d->visBuf->buffers)];
3675 invalidRanges_VisBuf(visBuf, full, invalidRange);
3676 /* Redraw the invalid ranges. */ { 3716 /* Redraw the invalid ranges. */ {
3677 iPaint *p = &ctx.paint; 3717 iPaint *p = &ctx->paint;
3678 init_Paint(p); 3718 init_Paint(p);
3679 iForIndices(i, visBuf->buffers) { 3719 iForIndices(i, visBuf->buffers) {
3680 iVisBufTexture *buf = &visBuf->buffers[i]; 3720 iVisBufTexture *buf = &visBuf->buffers[i];
3681 ctx.widgetBounds = moved_Rect(ctxWidgetBounds, init_I2(0, -buf->origin)); 3721 iVisBufMeta * meta = buf->user;
3682 ctx.viewPos = init_I2(left_Rect(docBounds) - left_Rect(bounds), -buf->origin); 3722 const iRangei bufRange = bufferRange_VisBuf(visBuf, i);
3683 if (!isEmpty_Rangei(invalidRange[i])) { 3723 const iRangei bufVisRange = intersect_Rangei(bufRange, vis);
3684 beginTarget_Paint(p, buf->texture); 3724 ctx->widgetBounds = moved_Rect(ctxWidgetBounds, init_I2(0, -buf->origin));
3725 ctx->viewPos = init_I2(left_Rect(ctx->docBounds) - left_Rect(bounds), -buf->origin);
3726 //printf(" buffer %zu: invalid range %d...%d\n", i, invalidRange[i].start, invalidRange[i].end);
3727 if (!prerenderExtra && !isEmpty_Range(&bufVisRange)) {
3728 didDraw = iTrue;
3685 if (isEmpty_Rangei(buf->validRange)) { 3729 if (isEmpty_Rangei(buf->validRange)) {
3686 fillRect_Paint(p, (iRect){ zero_I2(), visBuf->texSize }, tmBackground_ColorId); 3730 /* Fill the required currently visible range (vis). */
3731 const iRangei bufVisRange = intersect_Rangei(bufRange, vis);
3732 if (!isEmpty_Range(&bufVisRange)) {
3733 beginTarget_Paint(p, buf->texture);
3734 fillRect_Paint(p, (iRect){ zero_I2(), visBuf->texSize }, tmBackground_ColorId);
3735 iZap(ctx->runsDrawn);
3736 render_GmDocument(d->doc, bufVisRange, drawRun_DrawContext_, ctx);
3737 meta->runsDrawn = ctx->runsDrawn;
3738 meta->runsDrawn.start--;
3739 meta->runsDrawn.end++;
3740 buf->validRange = bufVisRange;
3741 // printf(" buffer %zu valid %d...%d\n", i, bufRange.start, bufRange.end);
3742 }
3743 }
3744 else {
3745 /* Progressively fill the required runs. */
3746 if (meta->runsDrawn.start) {
3747 beginTarget_Paint(p, buf->texture);
3748 meta->runsDrawn.start = renderProgressive_GmDocument(d->doc, meta->runsDrawn.start,
3749 -1, iInvalidSize,
3750 bufVisRange,
3751 drawRun_DrawContext_,
3752 ctx);
3753 buf->validRange.start = bufVisRange.start;
3754 }
3755 if (meta->runsDrawn.end) {
3756 beginTarget_Paint(p, buf->texture);
3757 meta->runsDrawn.end = renderProgressive_GmDocument(d->doc, meta->runsDrawn.end,
3758 +1, iInvalidSize,
3759 bufVisRange,
3760 drawRun_DrawContext_,
3761 ctx);
3762 buf->validRange.end = bufVisRange.end;
3763 }
3764 }
3765 }
3766 /* Progressively draw the rest of the buffer if it isn't fully valid. */
3767 if (prerenderExtra && !equal_Rangei(bufRange, buf->validRange)) {
3768 const iGmRun *next;
3769 if (meta->runsDrawn.start) {
3770 const iRangei upper = intersect_Rangei(bufRange, (iRangei){ full.start, buf->validRange.start });
3771 if (upper.end > upper.start) {
3772 beginTarget_Paint(p, buf->texture);
3773 next = renderProgressive_GmDocument(d->doc, meta->runsDrawn.start,
3774 -1, 1, upper,
3775 drawRun_DrawContext_,
3776 ctx);
3777 if (meta->runsDrawn.start != next) {
3778 meta->runsDrawn.start = next;
3779 didDraw = iTrue;
3780 }
3781 buf->validRange.start = bufRange.start;
3782 }
3783 }
3784 if (meta->runsDrawn.end) {
3785 const iRangei lower = intersect_Rangei(bufRange, (iRangei){ buf->validRange.end, full.end });
3786 if (lower.end > lower.start) {
3787 beginTarget_Paint(p, buf->texture);
3788 next = renderProgressive_GmDocument(d->doc, meta->runsDrawn.end,
3789 +1, 1, lower,
3790 drawRun_DrawContext_,
3791 ctx);
3792 if (meta->runsDrawn.end != next) {
3793 meta->runsDrawn.end = next;
3794 didDraw = iTrue;
3795 }
3796 buf->validRange.end = bufRange.end;
3797 }
3687 } 3798 }
3688 render_GmDocument(d->doc, invalidRange[i], drawRun_DrawContext_, &ctx);
3689 } 3799 }
3690 /* Draw any invalidated runs that fall within this buffer. */ { 3800 /* Draw any invalidated runs that fall within this buffer. */ {
3691 const iRangei bufRange = { buf->origin, buf->origin + visBuf->texSize.y }; 3801 const iRangei bufRange = { buf->origin, buf->origin + visBuf->texSize.y };
@@ -3694,7 +3804,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
3694 const iGmRun *run = *r.value; 3804 const iGmRun *run = *r.value;
3695 if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) { 3805 if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) {
3696 beginTarget_Paint(p, buf->texture); 3806 beginTarget_Paint(p, buf->texture);
3697 fillRect_Paint(&ctx.paint, 3807 fillRect_Paint(p,
3698 init_Rect(0, 3808 init_Rect(0,
3699 run->visBounds.pos.y - buf->origin, 3809 run->visBounds.pos.y - buf->origin,
3700 visBuf->texSize.x, 3810 visBuf->texSize.x,
@@ -3707,21 +3817,61 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
3707 const iGmRun *run = *r.value; 3817 const iGmRun *run = *r.value;
3708 if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) { 3818 if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) {
3709 beginTarget_Paint(p, buf->texture); 3819 beginTarget_Paint(p, buf->texture);
3710 drawRun_DrawContext_(&ctx, run); 3820 drawRun_DrawContext_(ctx, run);
3711 } 3821 }
3712 } 3822 }
3713 } 3823 }
3714 endTarget_Paint(&ctx.paint); 3824 endTarget_Paint(p);
3715 } 3825 }
3716 validate_VisBuf(visBuf);
3717 clear_PtrSet(d->invalidRuns); 3826 clear_PtrSet(d->invalidRuns);
3718 } 3827 }
3828 return didDraw;
3829}
3830
3831static void prerender_DocumentWidget_(iAny *context) {
3832 const iDocumentWidget *d = context;
3833 iDrawContext ctx = {
3834 .widget = d,
3835 .docBounds = documentBounds_DocumentWidget_(d),
3836 .vis = visibleRange_DocumentWidget_(d),
3837 .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0
3838 };
3839// printf("%u prerendering\n", SDL_GetTicks());
3840 if (render_DocumentWidget_(d, &ctx, iTrue /* just fill up progressively */)) {
3841 /* Something was drawn, should check if there is still more to do. */
3842 addTicker_App(prerender_DocumentWidget_, context);
3843 }
3844}
3845
3846static void draw_DocumentWidget_(const iDocumentWidget *d) {
3847 const iWidget *w = constAs_Widget(d);
3848 const iRect bounds = bounds_Widget(w);
3849 if (width_Rect(bounds) <= 0) {
3850 return;
3851 }
3852 draw_Widget(w);
3853 if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) {
3854 updateTimestampBuf_DocumentWidget_(d);
3855 }
3856 if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) {
3857 updateSideIconBuf_DocumentWidget_(d);
3858 }
3859 const iRect docBounds = documentBounds_DocumentWidget_(d);
3860 const iRangei vis = visibleRange_DocumentWidget_(d);
3861 iDrawContext ctx = {
3862 .widget = d,
3863 .docBounds = docBounds,
3864 .vis = vis,
3865 .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0,
3866 };
3867 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */);
3719 setClip_Paint(&ctx.paint, bounds); 3868 setClip_Paint(&ctx.paint, bounds);
3720 const int yTop = docBounds.pos.y - value_Anim(&d->scrollY); 3869 const int yTop = docBounds.pos.y - value_Anim(&d->scrollY);
3721 draw_VisBuf(visBuf, init_I2(bounds.pos.x, yTop)); 3870 draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop));
3722 /* Text markers. */ 3871 /* Text markers. */
3872 const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0;
3723 if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { 3873 if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) {
3724 SDL_Renderer *render = renderer_Window(get_Window()); 3874 SDL_Renderer *render = renderer_Window(get_Window());
3725 ctx.firstMarkRect = zero_Rect(); 3875 ctx.firstMarkRect = zero_Rect();
3726 ctx.lastMarkRect = zero_Rect(); 3876 ctx.lastMarkRect = zero_Rect();
3727 SDL_SetRenderDrawBlendMode(render, 3877 SDL_SetRenderDrawBlendMode(render,
@@ -3729,15 +3879,15 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
3729 : SDL_BLENDMODE_BLEND); 3879 : SDL_BLENDMODE_BLEND);
3730 ctx.viewPos = topLeft_Rect(docBounds); 3880 ctx.viewPos = topLeft_Rect(docBounds);
3731 /* Marker starting outside the visible range? */ 3881 /* Marker starting outside the visible range? */
3732 if (d->firstVisibleRun) { 3882 if (d->visibleRuns.start) {
3733 if (!isEmpty_Range(&d->selectMark) && 3883 if (!isEmpty_Range(&d->selectMark) &&
3734 d->selectMark.start < d->firstVisibleRun->text.start && 3884 d->selectMark.start < d->visibleRuns.start->text.start &&
3735 d->selectMark.end > d->firstVisibleRun->text.start) { 3885 d->selectMark.end > d->visibleRuns.start->text.start) {
3736 ctx.inSelectMark = iTrue; 3886 ctx.inSelectMark = iTrue;
3737 } 3887 }
3738 if (isEmpty_Range(&d->foundMark) && 3888 if (isEmpty_Range(&d->foundMark) &&
3739 d->foundMark.start < d->firstVisibleRun->text.start && 3889 d->foundMark.start < d->visibleRuns.start->text.start &&
3740 d->foundMark.end > d->firstVisibleRun->text.start) { 3890 d->foundMark.end > d->visibleRuns.start->text.start) {
3741 ctx.inFoundMark = iTrue; 3891 ctx.inFoundMark = iTrue;
3742 } 3892 }
3743 } 3893 }