diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 2 | ||||
-rw-r--r-- | src/gmdocument.c | 24 | ||||
-rw-r--r-- | src/gmdocument.h | 6 | ||||
-rw-r--r-- | src/periodic.c | 13 | ||||
-rw-r--r-- | src/periodic.h | 6 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 276 | ||||
-rw-r--r-- | src/ui/visbuf.c | 149 | ||||
-rw-r--r-- | src/ui/visbuf.h | 10 |
8 files changed, 369 insertions, 117 deletions
@@ -906,7 +906,7 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
906 | iApp *d = &app_; | 906 | iApp *d = &app_; |
907 | SDL_Event ev; | 907 | SDL_Event ev; |
908 | iBool gotEvents = iFalse; | 908 | iBool gotEvents = iFalse; |
909 | postCommands_Periodic(&d->periodic); | 909 | dispatchCommands_Periodic(&d->periodic); |
910 | while (nextEvent_App_(d, eventMode, &ev)) { | 910 | while (nextEvent_App_(d, eventMode, &ev)) { |
911 | #if defined (iPlatformAppleMobile) | 911 | #if defined (iPlatformAppleMobile) |
912 | if (processEvent_iOS(&ev)) { | 912 | if (processEvent_iOS(&ev)) { |
diff --git a/src/gmdocument.c b/src/gmdocument.c index e756a9d9..1e05f438 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -1494,6 +1494,30 @@ void render_GmDocument(const iGmDocument *d, iRangei visRangeY, iGmDocumentRende | |||
1494 | } | 1494 | } |
1495 | } | 1495 | } |
1496 | 1496 | ||
1497 | static iBool isValidRun_GmDocument_(const iGmDocument *d, const iGmRun *run) { | ||
1498 | return run >= (const iGmRun *) constAt_Array(&d->layout, 0) && | ||
1499 | run < (const iGmRun *) constEnd_Array(&d->layout); | ||
1500 | } | ||
1501 | |||
1502 | const iGmRun *renderProgressive_GmDocument(const iGmDocument *d, const iGmRun *first, int dir, | ||
1503 | size_t maxCount, | ||
1504 | iRangei visRangeY, iGmDocumentRenderFunc render, | ||
1505 | void *context) { | ||
1506 | const iGmRun *run = first; | ||
1507 | while (isValidRun_GmDocument_(d, run)) { | ||
1508 | if ((dir < 0 && bottom_Rect(run->visBounds) < visRangeY.start) || | ||
1509 | (dir > 0 && top_Rect(run->visBounds) >= visRangeY.end)) { | ||
1510 | break; | ||
1511 | } | ||
1512 | if (maxCount-- == 0) { | ||
1513 | break; | ||
1514 | } | ||
1515 | render(context, run); | ||
1516 | run += dir; | ||
1517 | } | ||
1518 | return isValidRun_GmDocument_(d, run) ? run : NULL; | ||
1519 | } | ||
1520 | |||
1497 | iInt2 size_GmDocument(const iGmDocument *d) { | 1521 | iInt2 size_GmDocument(const iGmDocument *d) { |
1498 | return d->size; | 1522 | return d->size; |
1499 | } | 1523 | } |
diff --git a/src/gmdocument.h b/src/gmdocument.h index ce148164..5d34dbaf 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -176,7 +176,11 @@ iMedia * media_GmDocument (iGmDocument *); | |||
176 | const iMedia * constMedia_GmDocument (const iGmDocument *); | 176 | const iMedia * constMedia_GmDocument (const iGmDocument *); |
177 | 177 | ||
178 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, | 178 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, |
179 | iGmDocumentRenderFunc render, void *); | 179 | iGmDocumentRenderFunc render, void *); /* includes partial overlaps */ |
180 | const iGmRun * renderProgressive_GmDocument(const iGmDocument *d, const iGmRun *first, int dir, | ||
181 | size_t maxCount, | ||
182 | iRangei visRangeY, iGmDocumentRenderFunc render, | ||
183 | void *context); | ||
180 | iInt2 size_GmDocument (const iGmDocument *); | 184 | iInt2 size_GmDocument (const iGmDocument *); |
181 | const iGmRun * siteBanner_GmDocument (const iGmDocument *); | 185 | const iGmRun * siteBanner_GmDocument (const iGmDocument *); |
182 | iBool hasSiteBanner_GmDocument (const iGmDocument *); | 186 | iBool hasSiteBanner_GmDocument (const iGmDocument *); |
diff --git a/src/periodic.c b/src/periodic.c index 29c26de9..9e9dfdba 100644 --- a/src/periodic.c +++ b/src/periodic.c | |||
@@ -21,10 +21,12 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "periodic.h" | 23 | #include "periodic.h" |
24 | #include "ui/widget.h" | ||
24 | #include "app.h" | 25 | #include "app.h" |
25 | 26 | ||
26 | #include <the_Foundation/string.h> | 27 | #include <the_Foundation/string.h> |
27 | #include <the_Foundation/thread.h> | 28 | #include <the_Foundation/thread.h> |
29 | #include <SDL_events.h> | ||
28 | #include <SDL_timer.h> | 30 | #include <SDL_timer.h> |
29 | 31 | ||
30 | iDeclareType(PeriodicCommand) | 32 | iDeclareType(PeriodicCommand) |
@@ -54,7 +56,7 @@ iDefineTypeConstructionArgs(PeriodicCommand, (iAny *ctx, const char *cmd), ctx, | |||
54 | 56 | ||
55 | static const uint32_t postingInterval_Periodic_ = 500; | 57 | static const uint32_t postingInterval_Periodic_ = 500; |
56 | 58 | ||
57 | iBool postCommands_Periodic(iPeriodic *d) { | 59 | iBool dispatchCommands_Periodic(iPeriodic *d) { |
58 | const uint32_t now = SDL_GetTicks(); | 60 | const uint32_t now = SDL_GetTicks(); |
59 | if (now - d->lastPostTime < postingInterval_Periodic_) { | 61 | if (now - d->lastPostTime < postingInterval_Periodic_) { |
60 | return iFalse; | 62 | return iFalse; |
@@ -63,7 +65,14 @@ iBool postCommands_Periodic(iPeriodic *d) { | |||
63 | iBool wasPosted = iFalse; | 65 | iBool wasPosted = iFalse; |
64 | lock_Mutex(d->mutex); | 66 | lock_Mutex(d->mutex); |
65 | iConstForEach(Array, i, &d->commands.values) { | 67 | iConstForEach(Array, i, &d->commands.values) { |
66 | postCommandString_App(&((const iPeriodicCommand *) i.value)->command); | 68 | const iPeriodicCommand *pc = i.value; |
69 | const SDL_UserEvent ev = { | ||
70 | .type = SDL_USEREVENT, | ||
71 | .code = command_UserEventCode, | ||
72 | .data1 = (void *) cstr_String(&pc->command) | ||
73 | }; | ||
74 | iAssert(isInstance_Object(pc->context, &Class_Widget)); | ||
75 | dispatchEvent_Widget(pc->context, (const SDL_Event *) &ev); | ||
67 | wasPosted = iTrue; | 76 | wasPosted = iTrue; |
68 | } | 77 | } |
69 | unlock_Mutex(d->mutex); | 78 | unlock_Mutex(d->mutex); |
diff --git a/src/periodic.h b/src/periodic.h index db90b848..8886617b 100644 --- a/src/periodic.h +++ b/src/periodic.h | |||
@@ -40,7 +40,7 @@ iLocalDef iBool isEmpty_Periodic(const iPeriodic *d) { | |||
40 | return isEmpty_SortedArray(&d->commands); | 40 | return isEmpty_SortedArray(&d->commands); |
41 | } | 41 | } |
42 | 42 | ||
43 | void add_Periodic (iPeriodic *, iAny *context, const char *command); | 43 | void add_Periodic (iPeriodic *, iAnyObject *context, const char *command); |
44 | void remove_Periodic (iPeriodic *, iAny *context); | 44 | void remove_Periodic (iPeriodic *, iAnyObject *context); |
45 | 45 | ||
46 | iBool postCommands_Periodic (iPeriodic *); | 46 | iBool dispatchCommands_Periodic( iPeriodic *); |
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 |
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, 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 | ||
161 | static void init_DrawBufs(iDrawBufs *d) { | 163 | static 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 | ||
167 | static void deinit_DrawBufs(iDrawBufs *d) { | 170 | static void deinit_DrawBufs(iDrawBufs *d) { |
@@ -175,9 +178,23 @@ iDefineTypeConstruction(DrawBufs) | |||
175 | 178 | ||
176 | /*----------------------------------------------------------------------------------------------*/ | 179 | /*----------------------------------------------------------------------------------------------*/ |
177 | 180 | ||
181 | iDeclareType(VisBufMeta) | ||
182 | |||
183 | struct Impl_VisBufMeta { | ||
184 | iGmRunRange runsDrawn; | ||
185 | }; | ||
186 | |||
187 | static void visBufInvalidated_(iVisBuf *d, size_t index) { | ||
188 | iVisBufMeta *meta = d->buffers[index].user; | ||
189 | iZap(meta->runsDrawn); | ||
190 | } | ||
191 | |||
192 | /*----------------------------------------------------------------------------------------------*/ | ||
193 | |||
178 | static void animate_DocumentWidget_ (void *ticker); | 194 | static void animate_DocumentWidget_ (void *ticker); |
179 | static void animateMedia_DocumentWidget_ (iDocumentWidget *d); | 195 | static void animateMedia_DocumentWidget_ (iDocumentWidget *d); |
180 | static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d); | 196 | static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d); |
197 | static void prerender_DocumentWidget_ (iAny *); | ||
181 | 198 | ||
182 | static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */ | 199 | static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */ |
183 | static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */ | 200 | static 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 | ||
350 | void deinit_DocumentWidget(iDocumentWidget *d) { | 375 | void 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) { | |||
486 | static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | 514 | static 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 | ||
744 | static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { | 772 | static 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 | ||
905 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | 941 | void 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 | ||
1728 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 1764 | static 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) | |||
3103 | struct Impl_DrawContext { | 3149 | struct 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 | ||
3115 | static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color, | 3164 | static 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 | |||
3269 | static void drawRun_DrawContext_(void *context, const iGmRun *run) { | 3318 | static 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 | ||
3647 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 3705 | static 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 | |||
3831 | static 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 | |||
3846 | static 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 | } |
diff --git a/src/ui/visbuf.c b/src/ui/visbuf.c index 198ad574..a9443b34 100644 --- a/src/ui/visbuf.c +++ b/src/ui/visbuf.c | |||
@@ -29,6 +29,8 @@ iDefineTypeConstruction(VisBuf) | |||
29 | void init_VisBuf(iVisBuf *d) { | 29 | void init_VisBuf(iVisBuf *d) { |
30 | d->texSize = zero_I2(); | 30 | d->texSize = zero_I2(); |
31 | iZap(d->buffers); | 31 | iZap(d->buffers); |
32 | iZap(d->vis); | ||
33 | d->bufferInvalidated = NULL; | ||
32 | } | 34 | } |
33 | 35 | ||
34 | void deinit_VisBuf(iVisBuf *d) { | 36 | void deinit_VisBuf(iVisBuf *d) { |
@@ -36,9 +38,14 @@ void deinit_VisBuf(iVisBuf *d) { | |||
36 | } | 38 | } |
37 | 39 | ||
38 | void invalidate_VisBuf(iVisBuf *d) { | 40 | void invalidate_VisBuf(iVisBuf *d) { |
41 | int origin = d->vis.start - d->texSize.y; | ||
39 | iForIndices(i, d->buffers) { | 42 | iForIndices(i, d->buffers) { |
40 | d->buffers[i].origin = i * d->texSize.y; | 43 | d->buffers[i].origin = origin; |
44 | origin += d->texSize.y; | ||
41 | iZap(d->buffers[i].validRange); | 45 | iZap(d->buffers[i].validRange); |
46 | if (d->bufferInvalidated) { | ||
47 | d->bufferInvalidated(d, i); | ||
48 | } | ||
42 | } | 49 | } |
43 | } | 50 | } |
44 | 51 | ||
@@ -58,9 +65,13 @@ void alloc_VisBuf(iVisBuf *d, const iInt2 size, int granularity) { | |||
58 | texSize.x, | 65 | texSize.x, |
59 | texSize.y); | 66 | texSize.y); |
60 | SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_NONE); | 67 | SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_NONE); |
61 | tex->origin = i * texSize.y; | 68 | // tex->origin = i * texSize.y; |
62 | iZap(tex->validRange); | 69 | // iZap(tex->validRange); |
70 | // if (d->invalidUserData) { | ||
71 | // d->invalidUserData(i, d->buffers[i].user); | ||
72 | // } | ||
63 | } | 73 | } |
74 | invalidate_VisBuf(d); | ||
64 | } | 75 | } |
65 | } | 76 | } |
66 | 77 | ||
@@ -72,53 +83,74 @@ void dealloc_VisBuf(iVisBuf *d) { | |||
72 | } | 83 | } |
73 | } | 84 | } |
74 | 85 | ||
75 | void reposition_VisBuf(iVisBuf *d, const iRangei vis) { | 86 | #if 0 |
76 | d->vis = vis; | 87 | static size_t findMostDistant_VisBuf_(const iVisBuf *d, const size_t *avail, size_t numAvail, |
77 | iRangei good = { 0, 0 }; | 88 | const iRangei vis) { |
78 | size_t avail[iElemCount(d->buffers)], numAvail = 0; | 89 | size_t chosen = 0; |
79 | /* Check which buffers are available for reuse. */ { | 90 | int distChosen = iAbsi(d->buffers[0].origin - vis.start); |
80 | iForIndices(i, d->buffers) { | 91 | printf(" avail (got %zu): %zu", numAvail, avail[0]); |
81 | iVisBufTexture *buf = d->buffers + i; | 92 | for (size_t i = 1; i < numAvail; i++) { |
82 | const iRangei region = { buf->origin, buf->origin + d->texSize.y }; | 93 | printf(" %zu", avail[i]); |
83 | if (isEmpty_Rangei(buf->validRange) || | 94 | const int dist = iAbsi(d->buffers[i].origin - vis.start); |
84 | buf->validRange.start >= vis.end || buf->validRange.end <= vis.start) { | 95 | if (dist > distChosen) { |
85 | avail[numAvail++] = i; | 96 | chosen = i; |
86 | iZap(buf->validRange); | 97 | distChosen = dist; |
87 | } | ||
88 | else { | ||
89 | good = union_Rangei(good, region); | ||
90 | } | ||
91 | } | 98 | } |
92 | } | 99 | } |
100 | printf("\n chose index %zu (%d)\n", chosen, distChosen); | ||
101 | return chosen; | ||
102 | } | ||
103 | |||
104 | static size_t take_(size_t *avail, size_t *numAvail, size_t index) { | ||
105 | const size_t value = avail[index]; | ||
106 | memmove(avail + index, avail + index + 1, sizeof(size_t) * (*numAvail - index - 1)); | ||
107 | (*numAvail)--; | ||
108 | return value; | ||
109 | } | ||
110 | #endif | ||
111 | |||
112 | iBool reposition_VisBuf(iVisBuf *d, const iRangei vis) { | ||
113 | if (equal_Rangei(vis, d->vis)) { | ||
114 | return iFalse; | ||
115 | } | ||
116 | d->vis = vis; | ||
93 | iBool wasChanged = iFalse; | 117 | iBool wasChanged = iFalse; |
94 | iBool doReset = (numAvail == iElemCount(d->buffers)); | 118 | const size_t lastPos = iElemCount(d->buffers) - 1; |
95 | /* Try to extend to cover the visible range. */ | 119 | if (d->buffers[0].origin > vis.end || d->buffers[lastPos].origin + d->texSize.y <= vis.start) { |
96 | while (!doReset && vis.start < good.start) { | 120 | /* All buffers outside the visible region. */ |
97 | if (numAvail == 0) { | 121 | invalidate_VisBuf(d); |
98 | doReset = iTrue; | ||
99 | break; | ||
100 | } | ||
101 | good.start -= d->texSize.y; | ||
102 | d->buffers[avail[--numAvail]].origin = good.start; | ||
103 | wasChanged = iTrue; | 122 | wasChanged = iTrue; |
104 | } | 123 | } |
105 | while (!doReset && vis.end > good.end) { | 124 | else { |
106 | if (numAvail == 0) { | 125 | /* Roll up. */ |
107 | doReset = iTrue; | 126 | while (d->buffers[0].origin > vis.start) { |
108 | break; | 127 | SDL_Texture *last = d->buffers[lastPos].texture; |
128 | void * user = d->buffers[lastPos].user; | ||
129 | memmove(d->buffers + 1, d->buffers, sizeof(iVisBufTexture) * lastPos); | ||
130 | d->buffers[0].texture = last; | ||
131 | d->buffers[0].user = user; | ||
132 | d->buffers[0].origin = d->buffers[1].origin - d->texSize.y; | ||
133 | iZap(d->buffers[0].validRange); | ||
134 | if (d->bufferInvalidated) { | ||
135 | d->bufferInvalidated(d, 0); | ||
136 | } | ||
137 | wasChanged = iTrue; | ||
109 | } | 138 | } |
110 | d->buffers[avail[--numAvail]].origin = good.end; | 139 | if (!wasChanged) { |
111 | good.end += d->texSize.y; | 140 | /* Roll down. */ |
112 | wasChanged = iTrue; | 141 | while (d->buffers[lastPos].origin + d->texSize.y < vis.end) { |
113 | } | 142 | SDL_Texture *first = d->buffers[0].texture; |
114 | if (doReset) { | 143 | void * user = d->buffers[0].user; |
115 | // puts("VisBuf reset!"); | 144 | memmove(d->buffers, d->buffers + 1, sizeof(iVisBufTexture) * lastPos); |
116 | // fflush(stdout); | 145 | d->buffers[lastPos].texture = first; |
117 | wasChanged = iTrue; | 146 | d->buffers[lastPos].user = user; |
118 | int pos = -1; | 147 | d->buffers[lastPos].origin = d->buffers[lastPos - 1].origin + d->texSize.y; |
119 | iForIndices(i, d->buffers) { | 148 | iZap(d->buffers[lastPos].validRange); |
120 | iZap(d->buffers[i].validRange); | 149 | if (d->bufferInvalidated) { |
121 | d->buffers[i].origin = vis.start + pos++ * d->texSize.y; | 150 | d->bufferInvalidated(d, lastPos); |
151 | } | ||
152 | wasChanged = iTrue; | ||
153 | } | ||
122 | } | 154 | } |
123 | } | 155 | } |
124 | #if 0 | 156 | #if 0 |
@@ -134,6 +166,33 @@ void reposition_VisBuf(iVisBuf *d, const iRangei vis) { | |||
134 | fflush(stdout); | 166 | fflush(stdout); |
135 | } | 167 | } |
136 | #endif | 168 | #endif |
169 | #if !defined (NDEBUG) | ||
170 | /* Buffers must not overlap. */ | ||
171 | iForIndices(m, d->buffers) { | ||
172 | const iRangei M = { d->buffers[m].origin, d->buffers[m].origin + d->texSize.y }; | ||
173 | iForIndices(n, d->buffers) { | ||
174 | if (m == n) continue; | ||
175 | const iRangei N = { d->buffers[n].origin, d->buffers[n].origin + d->texSize.y }; | ||
176 | const iRangei is = intersect_Rangei(M, N); | ||
177 | if (size_Range(&is) != 0) { | ||
178 | printf("buffers %zu (%i) and %zu (%i) overlap\n", | ||
179 | m, M.start, n, N.start); | ||
180 | fflush(stdout); | ||
181 | } | ||
182 | iAssert(size_Range(&is) == 0); | ||
183 | } | ||
184 | } | ||
185 | #endif | ||
186 | return iTrue; /* at least the visible range changed */ | ||
187 | } | ||
188 | |||
189 | iRangei allocRange_VisBuf(const iVisBuf *d) { | ||
190 | return (iRangei){ d->buffers[0].origin, | ||
191 | d->buffers[iElemCount(d->buffers) - 1].origin + d->texSize.y }; | ||
192 | } | ||
193 | |||
194 | iRangei bufferRange_VisBuf(const iVisBuf *d, size_t index) { | ||
195 | return (iRangei){ d->buffers[index].origin, d->buffers[index].origin + d->texSize.y }; | ||
137 | } | 196 | } |
138 | 197 | ||
139 | void invalidRanges_VisBuf(const iVisBuf *d, const iRangei full, iRangei *out_invalidRanges) { | 198 | void invalidRanges_VisBuf(const iVisBuf *d, const iRangei full, iRangei *out_invalidRanges) { |
@@ -177,5 +236,5 @@ void draw_VisBuf(const iVisBuf *d, iInt2 topLeft) { | |||
177 | dst.y += get_Window()->root->rect.size.y / 4; | 236 | dst.y += get_Window()->root->rect.size.y / 4; |
178 | #endif | 237 | #endif |
179 | SDL_RenderCopy(render, buf->texture, NULL, &dst); | 238 | SDL_RenderCopy(render, buf->texture, NULL, &dst); |
180 | } | 239 | } |
181 | } | 240 | } |
diff --git a/src/ui/visbuf.h b/src/ui/visbuf.h index b285e90b..7efa36a7 100644 --- a/src/ui/visbuf.h +++ b/src/ui/visbuf.h | |||
@@ -33,12 +33,16 @@ struct Impl_VisBufTexture { | |||
33 | SDL_Texture *texture; | 33 | SDL_Texture *texture; |
34 | int origin; | 34 | int origin; |
35 | iRangei validRange; | 35 | iRangei validRange; |
36 | void *user; | ||
36 | }; | 37 | }; |
37 | 38 | ||
39 | #define numBuffers_VisBuf ((size_t) 4) | ||
40 | |||
38 | struct Impl_VisBuf { | 41 | struct Impl_VisBuf { |
39 | iInt2 texSize; | 42 | iInt2 texSize; |
40 | iRangei vis; | 43 | iRangei vis; |
41 | iVisBufTexture buffers[4]; | 44 | iVisBufTexture buffers[numBuffers_VisBuf]; |
45 | void (*bufferInvalidated)(iVisBuf *, size_t index); | ||
42 | }; | 46 | }; |
43 | 47 | ||
44 | iDeclareTypeConstruction(VisBuf) | 48 | iDeclareTypeConstruction(VisBuf) |
@@ -46,8 +50,10 @@ iDeclareTypeConstruction(VisBuf) | |||
46 | void invalidate_VisBuf (iVisBuf *); | 50 | void invalidate_VisBuf (iVisBuf *); |
47 | void alloc_VisBuf (iVisBuf *, const iInt2 size, int granularity); | 51 | void alloc_VisBuf (iVisBuf *, const iInt2 size, int granularity); |
48 | void dealloc_VisBuf (iVisBuf *); | 52 | void dealloc_VisBuf (iVisBuf *); |
49 | void reposition_VisBuf (iVisBuf *, const iRangei vis); | 53 | iBool reposition_VisBuf (iVisBuf *, const iRangei vis); /* returns true if `vis` changes */ |
50 | void validate_VisBuf (iVisBuf *); | 54 | void validate_VisBuf (iVisBuf *); |
51 | 55 | ||
56 | iRangei allocRange_VisBuf (const iVisBuf *); | ||
57 | iRangei bufferRange_VisBuf (const iVisBuf *, size_t index); | ||
52 | void invalidRanges_VisBuf (const iVisBuf *, const iRangei full, iRangei *out_invalidRanges); | 58 | void invalidRanges_VisBuf (const iVisBuf *, const iRangei full, iRangei *out_invalidRanges); |
53 | void draw_VisBuf (const iVisBuf *, iInt2 topLeft); | 59 | void draw_VisBuf (const iVisBuf *, iInt2 topLeft); |