summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-09-05 22:27:23 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-09-05 22:27:23 +0300
commit366e3402da641dda87c865623caa6120d0eeb1e9 (patch)
treefb701c29b30917cf1fcee98ec3dc63018f1825d0
parent3282d252439d4166ed8f95ce42c82f0d21582e17 (diff)
List scrolling using visbuffer; draw press states
-rw-r--r--src/ui/listwidget.c136
-rw-r--r--src/ui/listwidget.h3
-rw-r--r--src/ui/sidebarwidget.c16
3 files changed, 120 insertions, 35 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
45iDefineObjectConstruction(ListWidget) 45iDefineObjectConstruction(ListWidget)
46 46
47enum iBufferValidity {
48 none_BufferValidity,
49 partial_BufferValidity,
50 full_BufferValidity,
51};
52
47struct Impl_ListWidget { 53struct 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
60void init_ListWidget(iListWidget *d) { 69void 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
76void deinit_ListWidget(iListWidget *d) { 87void 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
82void invalidate_ListWidget(iListWidget *d) { 94void 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
142int itemHeight_ListWidget(const iListWidget *d) {
143 return d->itemHeight;
144}
145
130int scrollPos_ListWidget(const iListWidget *d) { 146int 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) {
134void setScrollPos_ListWidget(iListWidget *d, int pos) { 150void 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
140void scrollOffset_ListWidget(iListWidget *d, int offset) { 158void 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
155static int visCount_ListWidget_(const iListWidget *d) { 175static 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
229static void redrawHoverItem_ListWidget_(iListWidget *d) {
230 insert_IntSet(&d->invalidItems, d->hoverItem);
231 refresh_Widget(as_Widget(d));
232}
233
209static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { 234static 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
258static void allocVisBuffer_ListWidget_(iListWidget *d) { 286static 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
304static 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
318static const iListItem *item_ListWidget_(const iListWidget *d, size_t pos) {
319 return constAt_PtrArray(&d->items, pos);
320}
321
274static void draw_ListWidget_(const iListWidget *d) { 322static 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
diff --git a/src/ui/listwidget.h b/src/ui/listwidget.h
index 71635eaf..c8a07f51 100644
--- a/src/ui/listwidget.h
+++ b/src/ui/listwidget.h
@@ -54,7 +54,8 @@ void addItem_ListWidget (iListWidget *, iAnyObject *item);
54 54
55iScrollWidget * scroll_ListWidget (iListWidget *); 55iScrollWidget * scroll_ListWidget (iListWidget *);
56 56
57int scrollPos_ListWidget (const iListWidget *); 57int itemHeight_ListWidget (const iListWidget *);
58int scrollPos_ListWidget (const iListWidget *);
58 59
59void setScrollPos_ListWidget (iListWidget *, int pos); 60void setScrollPos_ListWidget (iListWidget *, int pos);
60void scrollOffset_ListWidget (iListWidget *, int offset); 61void scrollOffset_ListWidget (iListWidget *, int offset);
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 4cedd731..55538812 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -87,7 +87,6 @@ struct Impl_SidebarWidget {
87 int modeScroll[max_SidebarMode]; 87 int modeScroll[max_SidebarMode];
88 int width; 88 int width;
89 iLabelWidget *modeButtons[max_SidebarMode]; 89 iLabelWidget *modeButtons[max_SidebarMode];
90 int itemHeight;
91 int maxButtonLabelWidth; 90 int maxButtonLabelWidth;
92 iWidget *resizer; 91 iWidget *resizer;
93 SDL_Cursor *resizeCursor; 92 SDL_Cursor *resizeCursor;
@@ -163,14 +162,18 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) {
163 /* Date separator. */ 162 /* Date separator. */
164 iSidebarItem *sep = new_SidebarItem(); 163 iSidebarItem *sep = new_SidebarItem();
165 sep->listItem.isSeparator = iTrue; 164 sep->listItem.isSeparator = iTrue;
166 set_String(&sep->meta, 165 const iString *text = collect_String(format_Date(
167 collect_String(format_Date( 166 &date, date.year != thisYear ? "%b %d %Y" : "%b %d"));
168 &date, date.year != thisYear ? "%b %d %Y" : "%b %d"))); 167 set_String(&sep->meta, text);
168 const int yOffset = itemHeight_ListWidget(d->list) * 2 / 3;
169 sep->id = yOffset;
169 addItem_ListWidget(d->list, sep); 170 addItem_ListWidget(d->list, sep);
170 iRelease(sep); 171 iRelease(sep);
171 /* Date separators are two items tall. */ 172 /* Date separators are two items tall. */
172 sep = new_SidebarItem(); 173 sep = new_SidebarItem();
173 sep->listItem.isSeparator = iTrue; 174 sep->listItem.isSeparator = iTrue;
175 sep->id = -itemHeight_ListWidget(d->list) + yOffset;
176 set_String(&sep->meta, text);
174 addItem_ListWidget(d->list, sep); 177 addItem_ListWidget(d->list, sep);
175 iRelease(sep); 178 iRelease(sep);
176 } 179 }
@@ -755,7 +758,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
755 const int itemHeight = height_Rect(itemRect); 758 const int itemHeight = height_Rect(itemRect);
756 const int iconColor = 759 const int iconColor =
757 isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) : uiIcon_ColorId; 760 isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) : uiIcon_ColorId;
758 if (isHover && !d->listItem.isSeparator) { 761 if (isHover) /* && !d->listItem.isSeparator)*/ {
759 fillRect_Paint(p, 762 fillRect_Paint(p,
760 itemRect, 763 itemRect,
761 isPressing ? uiBackgroundPressed_ColorId 764 isPressing ? uiBackgroundPressed_ColorId
@@ -789,8 +792,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
789 : uiText_ColorId; 792 : uiText_ColorId;
790 if (d->listItem.isSeparator) { 793 if (d->listItem.isSeparator) {
791 if (!isEmpty_String(&d->meta)) { 794 if (!isEmpty_String(&d->meta)) {
792 unsetClip_Paint(p); 795 iInt2 drawPos = addY_I2(topLeft_Rect(itemRect), d->id);
793 iInt2 drawPos = addY_I2(topLeft_Rect(itemRect), itemHeight * 0.666f);
794 drawHLine_Paint(p, drawPos, width_Rect(itemRect), uiIcon_ColorId); 796 drawHLine_Paint(p, drawPos, width_Rect(itemRect), uiIcon_ColorId);
795 drawRange_Text( 797 drawRange_Text(
796 default_FontId, 798 default_FontId,