diff options
Diffstat (limited to 'src/ui/listwidget.c')
-rw-r--r-- | src/ui/listwidget.c | 136 |
1 files changed, 109 insertions, 27 deletions
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index 7651171b..64e509ef 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c | |||
@@ -44,6 +44,12 @@ iDefineClass(ListItem) | |||
44 | 44 | ||
45 | iDefineObjectConstruction(ListWidget) | 45 | iDefineObjectConstruction(ListWidget) |
46 | 46 | ||
47 | enum iBufferValidity { | ||
48 | none_BufferValidity, | ||
49 | partial_BufferValidity, | ||
50 | full_BufferValidity, | ||
51 | }; | ||
52 | |||
47 | struct Impl_ListWidget { | 53 | struct Impl_ListWidget { |
48 | iWidget widget; | 54 | iWidget widget; |
49 | iScrollWidget *scroll; | 55 | iScrollWidget *scroll; |
@@ -53,8 +59,11 @@ struct Impl_ListWidget { | |||
53 | size_t hoverItem; | 59 | size_t hoverItem; |
54 | iClick click; | 60 | iClick click; |
55 | iIntSet invalidItems; | 61 | iIntSet invalidItems; |
56 | SDL_Texture *visBuffer; | 62 | SDL_Texture *visBuffer[2]; |
57 | iBool visBufferValid; | 63 | int visBufferIndex; |
64 | int visBufferScrollY; | ||
65 | // iBool visBufferValid; | ||
66 | enum iBufferValidity visBufferValid; | ||
58 | }; | 67 | }; |
59 | 68 | ||
60 | void init_ListWidget(iListWidget *d) { | 69 | void init_ListWidget(iListWidget *d) { |
@@ -69,18 +78,21 @@ void init_ListWidget(iListWidget *d) { | |||
69 | d->hoverItem = iInvalidPos; | 78 | d->hoverItem = iInvalidPos; |
70 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 79 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
71 | init_IntSet(&d->invalidItems); | 80 | init_IntSet(&d->invalidItems); |
72 | d->visBuffer = NULL; | 81 | iZap(d->visBuffer); |
73 | d->visBufferValid = iFalse; | 82 | d->visBufferIndex = 0; |
83 | d->visBufferValid = none_BufferValidity; | ||
84 | d->visBufferScrollY = 0; | ||
74 | } | 85 | } |
75 | 86 | ||
76 | void deinit_ListWidget(iListWidget *d) { | 87 | void deinit_ListWidget(iListWidget *d) { |
77 | clear_ListWidget(d); | 88 | clear_ListWidget(d); |
78 | deinit_PtrArray(&d->items); | 89 | deinit_PtrArray(&d->items); |
79 | SDL_DestroyTexture(d->visBuffer); | 90 | SDL_DestroyTexture(d->visBuffer[0]); |
91 | SDL_DestroyTexture(d->visBuffer[1]); | ||
80 | } | 92 | } |
81 | 93 | ||
82 | void invalidate_ListWidget(iListWidget *d) { | 94 | void invalidate_ListWidget(iListWidget *d) { |
83 | d->visBufferValid = iFalse; | 95 | d->visBufferValid = none_BufferValidity; |
84 | clear_IntSet(&d->invalidItems); /* all will be drawn */ | 96 | clear_IntSet(&d->invalidItems); /* all will be drawn */ |
85 | refresh_Widget(as_Widget(d)); | 97 | refresh_Widget(as_Widget(d)); |
86 | } | 98 | } |
@@ -127,6 +139,10 @@ void setItemHeight_ListWidget(iListWidget *d, int itemHeight) { | |||
127 | invalidate_ListWidget(d); | 139 | invalidate_ListWidget(d); |
128 | } | 140 | } |
129 | 141 | ||
142 | int itemHeight_ListWidget(const iListWidget *d) { | ||
143 | return d->itemHeight; | ||
144 | } | ||
145 | |||
130 | int scrollPos_ListWidget(const iListWidget *d) { | 146 | int scrollPos_ListWidget(const iListWidget *d) { |
131 | return d->scrollY; | 147 | return d->scrollY; |
132 | } | 148 | } |
@@ -134,7 +150,9 @@ int scrollPos_ListWidget(const iListWidget *d) { | |||
134 | void setScrollPos_ListWidget(iListWidget *d, int pos) { | 150 | void setScrollPos_ListWidget(iListWidget *d, int pos) { |
135 | d->scrollY = pos; | 151 | d->scrollY = pos; |
136 | d->hoverItem = iInvalidPos; | 152 | d->hoverItem = iInvalidPos; |
137 | invalidate_ListWidget(d); | 153 | // invalidate_ListWidget(d); |
154 | d->visBufferValid = partial_BufferValidity; | ||
155 | refresh_Widget(as_Widget(d)); | ||
138 | } | 156 | } |
139 | 157 | ||
140 | void scrollOffset_ListWidget(iListWidget *d, int offset) { | 158 | void scrollOffset_ListWidget(iListWidget *d, int offset) { |
@@ -148,12 +166,14 @@ void scrollOffset_ListWidget(iListWidget *d, int offset) { | |||
148 | if (oldScroll != d->scrollY) { | 166 | if (oldScroll != d->scrollY) { |
149 | d->hoverItem = iInvalidPos; | 167 | d->hoverItem = iInvalidPos; |
150 | updateVisible_ListWidget(d); | 168 | updateVisible_ListWidget(d); |
151 | invalidate_ListWidget(d); | 169 | //invalidate_ListWidget(d); |
170 | d->visBufferValid = partial_BufferValidity; | ||
171 | refresh_Widget(as_Widget(d)); | ||
152 | } | 172 | } |
153 | } | 173 | } |
154 | 174 | ||
155 | static int visCount_ListWidget_(const iListWidget *d) { | 175 | static int visCount_ListWidget_(const iListWidget *d) { |
156 | return iMin(height_Rect(innerBounds_Widget(constAs_Widget(d))) / d->itemHeight + 1, | 176 | return iMin(height_Rect(innerBounds_Widget(constAs_Widget(d))) / d->itemHeight + 2, |
157 | (int) size_PtrArray(&d->items)); | 177 | (int) size_PtrArray(&d->items)); |
158 | } | 178 | } |
159 | 179 | ||
@@ -206,6 +226,11 @@ void updateMouseHover_ListWidget(iListWidget *d) { | |||
206 | setHoverItem_ListWidget_(d, itemIndex_ListWidget(d, mouse)); | 226 | setHoverItem_ListWidget_(d, itemIndex_ListWidget(d, mouse)); |
207 | } | 227 | } |
208 | 228 | ||
229 | static void redrawHoverItem_ListWidget_(iListWidget *d) { | ||
230 | insert_IntSet(&d->invalidItems, d->hoverItem); | ||
231 | refresh_Widget(as_Widget(d)); | ||
232 | } | ||
233 | |||
209 | static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | 234 | static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { |
210 | iWidget *w = as_Widget(d); | 235 | iWidget *w = as_Widget(d); |
211 | if (isCommand_SDLEvent(ev)) { | 236 | if (isCommand_SDLEvent(ev)) { |
@@ -238,16 +263,19 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
238 | } | 263 | } |
239 | switch (processEvent_Click(&d->click, ev)) { | 264 | switch (processEvent_Click(&d->click, ev)) { |
240 | case started_ClickResult: | 265 | case started_ClickResult: |
241 | //invalidate_SidebarWidget_(d); | 266 | redrawHoverItem_ListWidget_(d); |
267 | break; | ||
268 | case aborted_ClickResult: | ||
269 | redrawHoverItem_ListWidget_(d); | ||
242 | break; | 270 | break; |
243 | case finished_ClickResult: | 271 | case finished_ClickResult: |
272 | case double_ClickResult: | ||
273 | redrawHoverItem_ListWidget_(d); | ||
244 | if (contains_Rect(innerBounds_Widget(w), pos_Click(&d->click)) && | 274 | if (contains_Rect(innerBounds_Widget(w), pos_Click(&d->click)) && |
245 | d->hoverItem != iInvalidSize) { | 275 | d->hoverItem != iInvalidSize) { |
246 | //itemClicked_SidebarWidget_(d, d->hoverItem); | ||
247 | postCommand_Widget(w, "list.clicked arg:%zu item:%p", | 276 | postCommand_Widget(w, "list.clicked arg:%zu item:%p", |
248 | d->hoverItem, constHoverItem_ListWidget(d)); | 277 | d->hoverItem, constHoverItem_ListWidget(d)); |
249 | } | 278 | } |
250 | // invalidate_SidebarWidget_(d); | ||
251 | break; | 279 | break; |
252 | default: | 280 | default: |
253 | break; | 281 | break; |
@@ -257,42 +285,95 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
257 | 285 | ||
258 | static void allocVisBuffer_ListWidget_(iListWidget *d) { | 286 | static void allocVisBuffer_ListWidget_(iListWidget *d) { |
259 | const iInt2 size = innerBounds_Widget(as_Widget(d)).size; | 287 | const iInt2 size = innerBounds_Widget(as_Widget(d)).size; |
260 | if (!d->visBuffer || !isEqual_I2(size_SDLTexture(d->visBuffer), size)) { | 288 | if (!d->visBuffer[0] || !isEqual_I2(size_SDLTexture(d->visBuffer[0]), size)) { |
261 | if (d->visBuffer) { | 289 | iForIndices(i, d->visBuffer) { |
262 | SDL_DestroyTexture(d->visBuffer); | 290 | if (d->visBuffer[i]) { |
291 | SDL_DestroyTexture(d->visBuffer[i]); | ||
292 | } | ||
293 | d->visBuffer[i] = SDL_CreateTexture(renderer_Window(get_Window()), | ||
294 | SDL_PIXELFORMAT_RGBA8888, | ||
295 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
296 | size.x, | ||
297 | size.y); | ||
298 | SDL_SetTextureBlendMode(d->visBuffer[i], SDL_BLENDMODE_NONE); | ||
263 | } | 299 | } |
264 | d->visBuffer = SDL_CreateTexture(renderer_Window(get_Window()), | 300 | d->visBufferValid = none_BufferValidity; |
265 | SDL_PIXELFORMAT_RGBA8888, | ||
266 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
267 | size.x, | ||
268 | size.y); | ||
269 | SDL_SetTextureBlendMode(d->visBuffer, SDL_BLENDMODE_NONE); | ||
270 | d->visBufferValid = iFalse; | ||
271 | } | 301 | } |
272 | } | 302 | } |
273 | 303 | ||
304 | static void drawItem_ListWidget_(const iListWidget *d, iPaint *p, size_t index, iInt2 pos) { | ||
305 | const iWidget * w = constAs_Widget(d); | ||
306 | const iRect bounds = innerBounds_Widget(w); | ||
307 | const iRect bufBounds = { zero_I2(), bounds.size }; | ||
308 | const iListItem *item = constAt_PtrArray(&d->items, index); | ||
309 | const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; | ||
310 | setClip_Paint(p, intersect_Rect(itemRect, bufBounds)); | ||
311 | if (d->visBufferValid) { | ||
312 | fillRect_Paint(p, itemRect, w->bgColor); | ||
313 | } | ||
314 | class_ListItem(item)->draw(item, p, itemRect, d); | ||
315 | unsetClip_Paint(p); | ||
316 | } | ||
317 | |||
318 | static const iListItem *item_ListWidget_(const iListWidget *d, size_t pos) { | ||
319 | return constAt_PtrArray(&d->items, pos); | ||
320 | } | ||
321 | |||
274 | static void draw_ListWidget_(const iListWidget *d) { | 322 | static void draw_ListWidget_(const iListWidget *d) { |
275 | const iWidget *w = constAs_Widget(d); | 323 | const iWidget *w = constAs_Widget(d); |
276 | const iRect bounds = innerBounds_Widget(w); | 324 | const iRect bounds = innerBounds_Widget(w); |
277 | if (!bounds.size.y || !bounds.size.x) return; | 325 | if (!bounds.size.y || !bounds.size.x) return; |
278 | iPaint p; | 326 | iPaint p; |
279 | init_Paint(&p); | 327 | init_Paint(&p); |
328 | SDL_Renderer *render = renderer_Window(get_Window()); | ||
280 | drawBackground_Widget(w); | 329 | drawBackground_Widget(w); |
281 | if (!d->visBufferValid || !isEmpty_IntSet(&d->invalidItems)) { | 330 | if (d->visBufferValid != full_BufferValidity || !isEmpty_IntSet(&d->invalidItems)) { |
282 | iListWidget *m = iConstCast(iListWidget *, d); | 331 | iListWidget *m = iConstCast(iListWidget *, d); |
283 | allocVisBuffer_ListWidget_(m); | 332 | allocVisBuffer_ListWidget_(m); |
284 | iAssert(d->visBuffer); | 333 | iAssert(d->visBuffer); |
285 | beginTarget_Paint(&p, d->visBuffer); | 334 | const int vbSrc = d->visBufferIndex; |
335 | const int vbDst = d->visBufferIndex ^ 1; | ||
336 | beginTarget_Paint(&p, d->visBuffer[vbDst]); | ||
286 | const iRect bufBounds = (iRect){ zero_I2(), bounds.size }; | 337 | const iRect bufBounds = (iRect){ zero_I2(), bounds.size }; |
338 | iRanges invalidRange = { 0, 0 }; | ||
287 | if (!d->visBufferValid) { | 339 | if (!d->visBufferValid) { |
288 | fillRect_Paint(&p, bufBounds, w->bgColor); | 340 | fillRect_Paint(&p, bufBounds, w->bgColor); |
289 | } | 341 | } |
342 | else if (d->visBufferValid == partial_BufferValidity) { | ||
343 | /* Copy previous contents. */ | ||
344 | const int delta = d->scrollY - d->visBufferScrollY; | ||
345 | SDL_RenderCopy( | ||
346 | render, | ||
347 | d->visBuffer[vbSrc], | ||
348 | NULL, | ||
349 | &(SDL_Rect){ 0, -delta, bounds.size.x, bounds.size.y }); | ||
350 | if (delta > 0) { | ||
351 | /* Scrolling down. */ | ||
352 | invalidRange.start = (d->visBufferScrollY + bounds.size.y) / d->itemHeight; | ||
353 | invalidRange.end = (d->scrollY + bounds.size.y) / d->itemHeight + 1; | ||
354 | } | ||
355 | else if (delta < 0) { | ||
356 | /* Scrolling up. */ | ||
357 | invalidRange.start = d->scrollY / d->itemHeight; | ||
358 | invalidRange.end = d->visBufferScrollY / d->itemHeight + 1; | ||
359 | } | ||
360 | #if 0 | ||
361 | /* Separators may consist of multiple items. */ | ||
362 | if (item_ListWidget_(d, invalidRange.start)->isSeparator) { | ||
363 | invalidRange.start--; | ||
364 | } | ||
365 | if (item_ListWidget_(d, invalidRange.end)->isSeparator) { | ||
366 | invalidRange.end++; | ||
367 | } | ||
368 | #endif | ||
369 | } | ||
290 | /* Draw items. */ { | 370 | /* Draw items. */ { |
291 | const iRanges visRange = visRange_ListWidget_(d); | 371 | const iRanges visRange = visRange_ListWidget_(d); |
292 | iInt2 pos = init_I2(0, -(d->scrollY % d->itemHeight)); | 372 | iInt2 pos = init_I2(0, -(d->scrollY % d->itemHeight)); |
293 | for (size_t i = visRange.start; i < visRange.end; i++) { | 373 | for (size_t i = visRange.start; i < visRange.end; i++) { |
294 | /* TODO: Refactor to loop through invalidItems only. */ | 374 | /* TODO: Refactor to loop through invalidItems only. */ |
295 | if (!d->visBufferValid || contains_IntSet(&d->invalidItems, i)) { | 375 | if (!d->visBufferValid || contains_Range(&invalidRange, i) || |
376 | contains_IntSet(&d->invalidItems, i)) { | ||
296 | const iListItem *item = constAt_PtrArray(&d->items, i); | 377 | const iListItem *item = constAt_PtrArray(&d->items, i); |
297 | const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; | 378 | const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; |
298 | setClip_Paint(&p, intersect_Rect(itemRect, bufBounds)); | 379 | setClip_Paint(&p, intersect_Rect(itemRect, bufBounds)); |
@@ -308,10 +389,11 @@ static void draw_ListWidget_(const iListWidget *d) { | |||
308 | endTarget_Paint(&p); | 389 | endTarget_Paint(&p); |
309 | /* Update state. */ | 390 | /* Update state. */ |
310 | m->visBufferValid = iTrue; | 391 | m->visBufferValid = iTrue; |
392 | m->visBufferScrollY = m->scrollY; | ||
393 | m->visBufferIndex = vbDst; | ||
311 | clear_IntSet(&m->invalidItems); | 394 | clear_IntSet(&m->invalidItems); |
312 | } | 395 | } |
313 | SDL_RenderCopy( | 396 | SDL_RenderCopy(render, d->visBuffer[d->visBufferIndex], NULL, (const SDL_Rect *) &bounds); |
314 | renderer_Window(get_Window()), d->visBuffer, NULL, (const SDL_Rect *) &bounds); | ||
315 | drawChildren_Widget(w); | 397 | drawChildren_Widget(w); |
316 | } | 398 | } |
317 | 399 | ||