summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/ui/listwidget.c131
-rw-r--r--src/ui/visbuf.c116
-rw-r--r--src/ui/visbuf.h30
4 files changed, 179 insertions, 100 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 46d418e8..f0968831 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -104,6 +104,8 @@ set (SOURCES
104 src/ui/text.h 104 src/ui/text.h
105 src/ui/util.c 105 src/ui/util.c
106 src/ui/util.h 106 src/ui/util.h
107 src/ui/visbuf.c
108 src/ui/visbuf.h
107 src/ui/window.c 109 src/ui/window.c
108 src/ui/window.h 110 src/ui/window.h
109 # Widgets: 111 # Widgets:
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c
index 5cb2754f..e0047179 100644
--- a/src/ui/listwidget.c
+++ b/src/ui/listwidget.c
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25#include "paint.h" 25#include "paint.h"
26#include "util.h" 26#include "util.h"
27#include "command.h" 27#include "command.h"
28#include "visbuf.h"
28 29
29#include <the_Foundation/intset.h> 30#include <the_Foundation/intset.h>
30 31
@@ -44,22 +45,6 @@ iDefineClass(ListItem)
44 45
45iDefineObjectConstruction(ListWidget) 46iDefineObjectConstruction(ListWidget)
46 47
47enum iBufferValidity {
48 none_BufferValidity,
49 partial_BufferValidity,
50 full_BufferValidity,
51};
52
53#define numVisBuffers_ListWidget_ 3
54
55iDeclareType(ListVisBuffer)
56
57struct Impl_ListVisBuffer {
58 SDL_Texture *texture;
59 int origin;
60 iRangei validRange;
61};
62
63struct Impl_ListWidget { 48struct Impl_ListWidget {
64 iWidget widget; 49 iWidget widget;
65 iScrollWidget *scroll; 50 iScrollWidget *scroll;
@@ -69,8 +54,7 @@ struct Impl_ListWidget {
69 size_t hoverItem; 54 size_t hoverItem;
70 iClick click; 55 iClick click;
71 iIntSet invalidItems; 56 iIntSet invalidItems;
72 iInt2 visBufSize; 57 iVisBuf *visBuf;
73 iListVisBuffer visBuffers[numVisBuffers_ListWidget_];
74}; 58};
75 59
76void init_ListWidget(iListWidget *d) { 60void init_ListWidget(iListWidget *d) {
@@ -86,22 +70,17 @@ void init_ListWidget(iListWidget *d) {
86 d->hoverItem = iInvalidPos; 70 d->hoverItem = iInvalidPos;
87 init_Click(&d->click, d, SDL_BUTTON_LEFT); 71 init_Click(&d->click, d, SDL_BUTTON_LEFT);
88 init_IntSet(&d->invalidItems); 72 init_IntSet(&d->invalidItems);
89 d->visBufSize = zero_I2(); 73 d->visBuf = new_VisBuf();
90 iZap(d->visBuffers);
91} 74}
92 75
93void deinit_ListWidget(iListWidget *d) { 76void deinit_ListWidget(iListWidget *d) {
94 clear_ListWidget(d); 77 clear_ListWidget(d);
95 deinit_PtrArray(&d->items); 78 deinit_PtrArray(&d->items);
96 iForIndices(i, d->visBuffers) { 79 delete_VisBuf(d->visBuf);
97 SDL_DestroyTexture(d->visBuffers[i].texture);
98 }
99} 80}
100 81
101void invalidate_ListWidget(iListWidget *d) { 82void invalidate_ListWidget(iListWidget *d) {
102 iForIndices(i, d->visBuffers) { 83 invalidate_VisBuf(d->visBuf);
103 iZap(d->visBuffers[i].validRange);
104 }
105 clear_IntSet(&d->invalidItems); /* all will be drawn */ 84 clear_IntSet(&d->invalidItems); /* all will be drawn */
106 refresh_Widget(as_Widget(d)); 85 refresh_Widget(as_Widget(d));
107} 86}
@@ -323,9 +302,10 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) {
323 return processEvent_Widget(w, ev); 302 return processEvent_Widget(w, ev);
324} 303}
325 304
305#if 0
326static void allocVisBuffer_ListWidget_(iListWidget *d) { 306static void allocVisBuffer_ListWidget_(iListWidget *d) {
327 /* Make sure two buffers cover the entire visible area. */ 307 /* Make sure two buffers cover the entire visible area. */
328 const iRect inner = innerBounds_Widget(as_Widget(d)); 308// const iRect inner = innerBounds_Widget(as_Widget(d));
329 const iInt2 size = init_I2(inner.size.x, (inner.size.y / 2 / d->itemHeight + 1) * d->itemHeight); 309 const iInt2 size = init_I2(inner.size.x, (inner.size.y / 2 / d->itemHeight + 1) * d->itemHeight);
330 if (!d->visBuffers[0].texture || !isEqual_I2(size, d->visBufSize)) { 310 if (!d->visBuffers[0].texture || !isEqual_I2(size, d->visBufSize)) {
331 d->visBufSize = size; 311 d->visBufSize = size;
@@ -345,6 +325,7 @@ static void allocVisBuffer_ListWidget_(iListWidget *d) {
345 } 325 }
346 } 326 }
347} 327}
328#endif
348 329
349static void drawItem_ListWidget_(const iListWidget *d, iPaint *p, size_t index, iInt2 pos) { 330static void drawItem_ListWidget_(const iListWidget *d, iPaint *p, size_t index, iInt2 pos) {
350 const iWidget * w = constAs_Widget(d); 331 const iWidget * w = constAs_Widget(d);
@@ -361,89 +342,48 @@ static const iListItem *item_ListWidget_(const iListWidget *d, size_t pos) {
361static void draw_ListWidget_(const iListWidget *d) { 342static void draw_ListWidget_(const iListWidget *d) {
362 const iWidget *w = constAs_Widget(d); 343 const iWidget *w = constAs_Widget(d);
363 const iRect bounds = innerBounds_Widget(w); 344 const iRect bounds = innerBounds_Widget(w);
364 if (!bounds.size.y || !bounds.size.x) return; 345 if (!bounds.size.y || !bounds.size.x || !d->itemHeight) {
346 return;
347 }
365 iPaint p; 348 iPaint p;
366 init_Paint(&p); 349 init_Paint(&p);
367 SDL_Renderer *render = renderer_Window(get_Window());
368 drawBackground_Widget(w); 350 drawBackground_Widget(w);
369 iListWidget *m = iConstCast(iListWidget *, d); 351// iListWidget *m = iConstCast(iListWidget *, d);
370 allocVisBuffer_ListWidget_(m); 352 alloc_VisBuf(d->visBuf, bounds.size, d->itemHeight);
371 /* Update invalid regions/items. */ 353 /* Update invalid regions/items. */ {
372 /* TODO: This seems to draw two items per each shift of the visible region, even though 354 /* TODO: This seems to draw two items per each shift of the visible region, even though
373 one should be enough. Probably an off-by-one error in the calculation of the 355 one should be enough. Probably an off-by-one error in the calculation of the
374 invalid range. */ 356 invalid range. */
375 if (d->visBufSize.y > 0) { 357 iAssert(d->visBuf->buffers[0].texture);
376 iAssert(d->visBuffers[0].texture); 358 iAssert(d->visBuf->buffers[1].texture);
377 iAssert(d->visBuffers[1].texture); 359 iAssert(d->visBuf->buffers[2].texture);
378 iAssert(d->visBuffers[2].texture);
379 const int bg[3] = { w->bgColor, w->bgColor, w->bgColor }; 360 const int bg[3] = { w->bgColor, w->bgColor, w->bgColor };
380// const int bg[3] = { red_ColorId, magenta_ColorId, blue_ColorId }; 361// const int bg[3] = { red_ColorId, magenta_ColorId, blue_ColorId };
381 const int bottom = numItems_ListWidget(d) * d->itemHeight; 362 const int bottom = numItems_ListWidget(d) * d->itemHeight;
382 const iRangei vis = { d->scrollY / d->itemHeight * d->itemHeight, 363 const iRangei vis = { d->scrollY / d->itemHeight * d->itemHeight,
383 ((d->scrollY + bounds.size.y) / d->itemHeight + 1) * d->itemHeight }; 364 ((d->scrollY + bounds.size.y) / d->itemHeight + 1) * d->itemHeight };
384 iRangei good = { 0, 0 };
385// printf("visBufSize.y = %d\n", d->visBufSize.y); 365// printf("visBufSize.y = %d\n", d->visBufSize.y);
386 size_t avail[3], numAvail = 0; 366 reposition_VisBuf(d->visBuf, vis);
387 /* Check which buffers are available for reuse. */ {
388 iForIndices(i, d->visBuffers) {
389 iListVisBuffer *buf = m->visBuffers + i;
390 const iRangei region = { buf->origin, buf->origin + d->visBufSize.y };
391 if (region.start >= vis.end || region.end <= vis.start) {
392 avail[numAvail++] = i;
393 iZap(buf->validRange);
394 }
395 else {
396 good = union_Rangei(good, region);
397 }
398 }
399 }
400 if (numAvail == numVisBuffers_ListWidget_) {
401 /* All buffers are outside the visible range, do a reset. */
402 m->visBuffers[0].origin = vis.start;
403 m->visBuffers[1].origin = vis.start + d->visBufSize.y;
404 }
405 else {
406 /* Extend to cover the visible range. */
407 while (vis.start < good.start) {
408 iAssert(numAvail > 0);
409 m->visBuffers[avail[--numAvail]].origin = good.start - d->visBufSize.y;
410 good.start -= d->visBufSize.y;
411 }
412 while (vis.end > good.end) {
413 iAssert(numAvail > 0);
414 m->visBuffers[avail[--numAvail]].origin = good.end;
415 good.end += d->visBufSize.y;
416 }
417 }
418 /* Check which parts are invalid. */ 367 /* Check which parts are invalid. */
419 iRangei invalidRange[3]; 368 iRangei invalidRange[3];
420 iForIndices(i, d->visBuffers) { 369 invalidRanges_VisBuf(d->visBuf, (iRangei){ 0, bottom }, invalidRange);
421 const iListVisBuffer *buf = d->visBuffers + i; 370 iForIndices(i, d->visBuf->buffers) {
422 const iRangei region = intersect_Rangei(vis, (iRangei){ buf->origin, buf->origin + d->visBufSize.y }); 371 iVisBufTexture *buf = &d->visBuf->buffers[i];
423 const iRangei before = { 0, buf->validRange.start };
424 const iRangei after = { buf->validRange.end, bottom };
425 invalidRange[i] = intersect_Rangei(before, region);
426 if (isEmpty_Rangei(invalidRange[i])) {
427 invalidRange[i] = intersect_Rangei(after, region);
428 }
429 }
430 iForIndices(i, d->visBuffers) {
431 iListVisBuffer *buf = m->visBuffers + i;
432// printf("%zu: orig %d, invalid %d ... %d\n", i, buf->origin, invalidRange[i].start, invalidRange[i].end); 372// printf("%zu: orig %d, invalid %d ... %d\n", i, buf->origin, invalidRange[i].start, invalidRange[i].end);
433 iRanges drawItems = { iMax(0, buf->origin) / d->itemHeight, 373 iRanges drawItems = { iMax(0, buf->origin) / d->itemHeight,
434 iMax(0, buf->origin + d->visBufSize.y) / d->itemHeight }; 374 iMax(0, buf->origin + d->visBuf->texSize.y) / d->itemHeight };
435 iBool isTargetSet = iFalse; 375 iBool isTargetSet = iFalse;
436 if (isEmpty_Rangei(buf->validRange)) { 376 if (isEmpty_Rangei(buf->validRange)) {
437 isTargetSet = iTrue; 377 isTargetSet = iTrue;
438 beginTarget_Paint(&p, buf->texture); 378 beginTarget_Paint(&p, buf->texture);
439 fillRect_Paint(&p, (iRect){ zero_I2(), d->visBufSize }, bg[i]); 379 fillRect_Paint(&p, (iRect){ zero_I2(), d->visBuf->texSize }, bg[i]);
440 } 380 }
441 iConstForEach(IntSet, v, &d->invalidItems) { 381 iConstForEach(IntSet, v, &d->invalidItems) {
442 const size_t index = *v.value; 382 const size_t index = *v.value;
443 if (contains_Range(&drawItems, index)) { 383 if (contains_Range(&drawItems, index)) {
444 const iListItem *item = constAt_PtrArray(&d->items, index); 384 const iListItem *item = constAt_PtrArray(&d->items, index);
445 const iRect itemRect = { init_I2(0, index * d->itemHeight - buf->origin), 385 const iRect itemRect = { init_I2(0, index * d->itemHeight - buf->origin),
446 init_I2(d->visBufSize.x, d->itemHeight) }; 386 init_I2(d->visBuf->texSize.x, d->itemHeight) };
447 if (!isTargetSet) { 387 if (!isTargetSet) {
448 beginTarget_Paint(&p, buf->texture); 388 beginTarget_Paint(&p, buf->texture);
449 isTargetSet = iTrue; 389 isTargetSet = iTrue;
@@ -464,7 +404,7 @@ static void draw_ListWidget_(const iListWidget *d) {
464 for (size_t j = drawItems.start; j < drawItems.end && j < size_PtrArray(&d->items); j++) { 404 for (size_t j = drawItems.start; j < drawItems.end && j < size_PtrArray(&d->items); j++) {
465 const iListItem *item = constAt_PtrArray(&d->items, j); 405 const iListItem *item = constAt_PtrArray(&d->items, j);
466 const iRect itemRect = { init_I2(0, j * d->itemHeight - buf->origin), 406 const iRect itemRect = { init_I2(0, j * d->itemHeight - buf->origin),
467 init_I2(d->visBufSize.x, d->itemHeight) }; 407 init_I2(d->visBuf->texSize.x, d->itemHeight) };
468 fillRect_Paint(&p, itemRect, bg[i]); 408 fillRect_Paint(&p, itemRect, bg[i]);
469 class_ListItem(item)->draw(item, &p, itemRect, d); 409 class_ListItem(item)->draw(item, &p, itemRect, d);
470// printf("- drawing item %zu\n", j); 410// printf("- drawing item %zu\n", j);
@@ -474,22 +414,13 @@ static void draw_ListWidget_(const iListWidget *d) {
474 endTarget_Paint(&p); 414 endTarget_Paint(&p);
475 } 415 }
476 buf->validRange = 416 buf->validRange =
477 intersect_Rangei(vis, (iRangei){ buf->origin, buf->origin + d->visBufSize.y }); 417 intersect_Rangei(vis, (iRangei){ buf->origin, buf->origin + d->visBuf->texSize.y });
478// fflush(stdout); 418// fflush(stdout);
479 } 419 }
480 clear_IntSet(&m->invalidItems); 420 clear_IntSet(&iConstCast(iListWidget *, d)->invalidItems);
481 } 421 }
482 setClip_Paint(&p, bounds_Widget(w)); 422 setClip_Paint(&p, bounds_Widget(w));
483 iForIndices(i, d->visBuffers) { 423 draw_VisBuf(d->visBuf, addY_I2(topLeft_Rect(bounds), -d->scrollY));
484 const iListVisBuffer *buf = d->visBuffers + i;
485 SDL_RenderCopy(render,
486 buf->texture,
487 NULL,
488 &(SDL_Rect){ left_Rect(bounds),
489 top_Rect(bounds) - d->scrollY + buf->origin,
490 d->visBufSize.x,
491 d->visBufSize.y });
492 }
493 unsetClip_Paint(&p); 424 unsetClip_Paint(&p);
494 drawChildren_Widget(w); 425 drawChildren_Widget(w);
495} 426}
diff --git a/src/ui/visbuf.c b/src/ui/visbuf.c
new file mode 100644
index 00000000..82346ff0
--- /dev/null
+++ b/src/ui/visbuf.c
@@ -0,0 +1,116 @@
1#include "visbuf.h"
2#include "window.h"
3#include "util.h"
4
5iDefineTypeConstruction(VisBuf)
6
7void init_VisBuf(iVisBuf *d) {
8 d->texSize = zero_I2();
9 iZap(d->buffers);
10}
11
12void deinit_VisBuf(iVisBuf *d) {
13 dealloc_VisBuf(d);
14}
15
16void invalidate_VisBuf(iVisBuf *d) {
17 iForIndices(i, d->buffers) {
18 iZap(d->buffers[i].validRange);
19 }
20}
21
22void alloc_VisBuf(iVisBuf *d, const iInt2 size, int granularity) {
23 const iInt2 texSize = init_I2(size.x, (size.y / 2 / granularity + 1) * granularity);
24 if (!d->buffers[0].texture || !isEqual_I2(texSize, d->texSize)) {
25 d->texSize = texSize;
26 iForIndices(i, d->buffers) {
27 iVisBufTexture *tex = &d->buffers[i];
28 if (tex->texture) {
29 SDL_DestroyTexture(tex->texture);
30 }
31 tex->texture =
32 SDL_CreateTexture(renderer_Window(get_Window()),
33 SDL_PIXELFORMAT_RGBA8888,
34 SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
35 texSize.x,
36 texSize.y);
37 SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_NONE);
38 tex->origin = i * texSize.y;
39 iZap(tex->validRange);
40 }
41 }
42}
43
44void dealloc_VisBuf(iVisBuf *d) {
45 d->texSize = zero_I2();
46 iForIndices(i, d->buffers) {
47 SDL_DestroyTexture(d->buffers[i].texture);
48 d->buffers[i].texture = NULL;
49 }
50}
51
52void reposition_VisBuf(iVisBuf *d, const iRangei vis) {
53 d->vis = vis;
54 iRangei good = { 0, 0 };
55 size_t avail[3], numAvail = 0;
56 /* Check which buffers are available for reuse. */ {
57 iForIndices(i, d->buffers) {
58 iVisBufTexture *buf = d->buffers + i;
59 const iRangei region = { buf->origin, buf->origin + d->texSize.y };
60 if (region.start >= vis.end || region.end <= vis.start) {
61 avail[numAvail++] = i;
62 iZap(buf->validRange);
63 }
64 else {
65 good = union_Rangei(good, region);
66 }
67 }
68 }
69 if (numAvail == iElemCount(d->buffers)) {
70 /* All buffers are outside the visible range, do a reset. */
71 d->buffers[0].origin = vis.start;
72 d->buffers[1].origin = vis.start + d->texSize.y;
73 d->buffers[2].origin = vis.start + 2 * d->texSize.y;
74 }
75 else {
76 /* Extend to cover the visible range. */
77 while (vis.start < good.start) {
78 iAssert(numAvail > 0);
79 d->buffers[avail[--numAvail]].origin = good.start - d->texSize.y;
80 good.start -= d->texSize.y;
81 }
82 while (vis.end > good.end) {
83 iAssert(numAvail > 0);
84 d->buffers[avail[--numAvail]].origin = good.end;
85 good.end += d->texSize.y;
86 }
87 }
88}
89
90void invalidRanges_VisBuf(const iVisBuf *d, const iRangei full, iRangei *out_invalidRanges) {
91 iForIndices(i, d->buffers) {
92 const iVisBufTexture *buf = d->buffers + i;
93 const iRangei before = { full.start, buf->validRange.start };
94 const iRangei after = { buf->validRange.end, full.end };
95 const iRangei region = intersect_Rangei(d->vis, (iRangei){ buf->origin,
96 buf->origin + d->texSize.y });
97 out_invalidRanges[i] = intersect_Rangei(before, region);
98 if (isEmpty_Rangei(out_invalidRanges[i])) {
99 out_invalidRanges[i] = intersect_Rangei(after, region);
100 }
101 }
102}
103
104void draw_VisBuf(const iVisBuf *d, iInt2 topLeft) {
105 SDL_Renderer *render = renderer_Window(get_Window());
106 iForIndices(i, d->buffers) {
107 const iVisBufTexture *buf = d->buffers + i;
108 SDL_RenderCopy(render,
109 buf->texture,
110 NULL,
111 &(SDL_Rect){ topLeft.x,
112 topLeft.y + buf->origin,
113 d->texSize.x,
114 d->texSize.y });
115 }
116}
diff --git a/src/ui/visbuf.h b/src/ui/visbuf.h
new file mode 100644
index 00000000..80370b29
--- /dev/null
+++ b/src/ui/visbuf.h
@@ -0,0 +1,30 @@
1#pragma once
2
3#include <the_Foundation/range.h>
4#include <the_Foundation/vec2.h>
5#include <SDL_render.h>
6
7iDeclareType(VisBuf)
8iDeclareType(VisBufTexture)
9
10struct Impl_VisBufTexture {
11 SDL_Texture *texture;
12 int origin;
13 iRangei validRange;
14};
15
16struct Impl_VisBuf {
17 iInt2 texSize;
18 iRangei vis;
19 iVisBufTexture buffers[3];
20};
21
22iDeclareTypeConstruction(VisBuf)
23
24void invalidate_VisBuf (iVisBuf *);
25void alloc_VisBuf (iVisBuf *, const iInt2 size, int granularity);
26void dealloc_VisBuf (iVisBuf *);
27void reposition_VisBuf (iVisBuf *, const iRangei vis);
28
29void invalidRanges_VisBuf (const iVisBuf *, const iRangei full, iRangei *out_invalidRanges);
30void draw_VisBuf (const iVisBuf *, iInt2 topLeft);