summaryrefslogtreecommitdiff
path: root/src/ui/widget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/widget.c')
-rw-r--r--src/ui/widget.c241
1 files changed, 217 insertions, 24 deletions
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 659a00cc..66cd0e7b 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -40,6 +40,67 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
40# include "../ios.h" 40# include "../ios.h"
41#endif 41#endif
42 42
43struct Impl_WidgetDrawBuffer {
44 SDL_Texture *texture;
45 iInt2 size;
46 iBool isValid;
47 SDL_Texture *oldTarget;
48 iInt2 oldOrigin;
49};
50
51static void init_WidgetDrawBuffer(iWidgetDrawBuffer *d) {
52 d->texture = NULL;
53 d->size = zero_I2();
54 d->isValid = iFalse;
55 d->oldTarget = NULL;
56}
57
58static void deinit_WidgetDrawBuffer(iWidgetDrawBuffer *d) {
59 SDL_DestroyTexture(d->texture);
60}
61
62iDefineTypeConstruction(WidgetDrawBuffer)
63
64static void realloc_WidgetDrawBuffer(iWidgetDrawBuffer *d, SDL_Renderer *render, iInt2 size) {
65 if (!isEqual_I2(d->size, size)) {
66 d->size = size;
67 if (d->texture) {
68 SDL_DestroyTexture(d->texture);
69 }
70 d->texture = SDL_CreateTexture(render,
71 SDL_PIXELFORMAT_RGBA8888,
72 SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
73 size.x,
74 size.y);
75 SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);
76 d->isValid = iFalse;
77 }
78}
79
80static void release_WidgetDrawBuffer(iWidgetDrawBuffer *d) {
81 if (d->texture) {
82 SDL_DestroyTexture(d->texture);
83 d->texture = NULL;
84 }
85 d->size = zero_I2();
86 d->isValid = iFalse;
87}
88
89static iRect boundsForDraw_Widget_(const iWidget *d) {
90 iRect bounds = bounds_Widget(d);
91 if (d->flags & drawBackgroundToBottom_WidgetFlag) {
92 bounds.size.y = iMaxi(bounds.size.y, size_Root(d->root).y - top_Rect(bounds));
93 }
94 return bounds;
95}
96
97static iBool checkDrawBuffer_Widget_(const iWidget *d) {
98 return d->drawBuf && d->drawBuf->isValid &&
99 isEqual_I2(d->drawBuf->size, boundsForDraw_Widget_(d).size);
100}
101
102/*----------------------------------------------------------------------------------------------*/
103
43static void printInfo_Widget_(const iWidget *); 104static void printInfo_Widget_(const iWidget *);
44 105
45void releaseChildren_Widget(iWidget *d) { 106void releaseChildren_Widget(iWidget *d) {
@@ -66,6 +127,7 @@ void init_Widget(iWidget *d) {
66 d->children = NULL; 127 d->children = NULL;
67 d->parent = NULL; 128 d->parent = NULL;
68 d->commandHandler = NULL; 129 d->commandHandler = NULL;
130 d->drawBuf = NULL;
69 iZap(d->padding); 131 iZap(d->padding);
70} 132}
71 133
@@ -82,6 +144,7 @@ static void visualOffsetAnimation_Widget_(void *ptr) {
82 144
83void deinit_Widget(iWidget *d) { 145void deinit_Widget(iWidget *d) {
84 releaseChildren_Widget(d); 146 releaseChildren_Widget(d);
147 delete_WidgetDrawBuffer(d->drawBuf);
85#if 0 && !defined (NDEBUG) 148#if 0 && !defined (NDEBUG)
86 printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id), 149 printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id),
87 d->flags & keepOnTop_WidgetFlag ? 1 : 0); 150 d->flags & keepOnTop_WidgetFlag ? 1 : 0);
@@ -1036,7 +1099,8 @@ iBool scrollOverflow_Widget(iWidget *d, int delta) {
1036 const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos); 1099 const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos);
1037 if (!isEqual_I2(newPos, d->rect.pos)) { 1100 if (!isEqual_I2(newPos, d->rect.pos)) {
1038 d->rect.pos = newPos; 1101 d->rect.pos = newPos;
1039 refresh_Widget(d); 1102// refresh_Widget(d);
1103 postRefresh_App();
1040 } 1104 }
1041 return height_Rect(bounds) > height_Rect(winRect); 1105 return height_Rect(bounds) > height_Rect(winRect);
1042} 1106}
@@ -1077,6 +1141,9 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
1077 } 1141 }
1078 if (ev->user.code == command_UserEventCode) { 1142 if (ev->user.code == command_UserEventCode) {
1079 const char *cmd = command_UserEvent(ev); 1143 const char *cmd = command_UserEvent(ev);
1144 if (d->drawBuf && equal_Command(cmd, "theme.changed")) {
1145 d->drawBuf->isValid = iFalse;
1146 }
1080 if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) && 1147 if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) &&
1081 isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag && 1148 isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag &&
1082 equal_Command(cmd, "edgeswipe.moved")) { 1149 equal_Command(cmd, "edgeswipe.moved")) {
@@ -1147,14 +1214,13 @@ int backgroundFadeColor_Widget(void) {
1147 } 1214 }
1148} 1215}
1149 1216
1150void drawBackground_Widget(const iWidget *d) { 1217iLocalDef iBool isDrawn_Widget_(const iWidget *d) {
1151 if (d->flags & noBackground_WidgetFlag) { 1218 return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag;
1152 return; 1219}
1153 } 1220
1154 if (d->flags & hidden_WidgetFlag && ~d->flags & visualOffset_WidgetFlag) { 1221static void drawLayerEffects_Widget_(const iWidget *d) {
1155 return; 1222 /* Layered effects are not buffered, so they are drawn here separately. */
1156 } 1223 iAssert(isDrawn_Widget_(d));
1157 /* Popup menus have a shadowed border. */
1158 iBool shadowBorder = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0; 1224 iBool shadowBorder = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0;
1159 iBool fadeBackground = (d->bgColor >= 0 || d->frameColor >= 0) && d->flags & mouseModal_WidgetFlag; 1225 iBool fadeBackground = (d->bgColor >= 0 || d->frameColor >= 0) && d->flags & mouseModal_WidgetFlag;
1160 if (deviceType_App() == phone_AppDeviceType) { 1226 if (deviceType_App() == phone_AppDeviceType) {
@@ -1163,13 +1229,12 @@ void drawBackground_Widget(const iWidget *d) {
1163 shadowBorder = iFalse; 1229 shadowBorder = iFalse;
1164 } 1230 }
1165 } 1231 }
1232 const iBool isFaded = fadeBackground && ~d->flags & noFadeBackground_WidgetFlag;
1166 if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) { 1233 if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) {
1167 iPaint p; 1234 iPaint p;
1168 init_Paint(&p); 1235 init_Paint(&p);
1169 drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); 1236 drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30);
1170 } 1237 }
1171 const iBool isFaded = fadeBackground &&
1172 ~d->flags & noFadeBackground_WidgetFlag;
1173 if (isFaded) { 1238 if (isFaded) {
1174 iPaint p; 1239 iPaint p;
1175 init_Paint(&p); 1240 init_Paint(&p);
@@ -1183,10 +1248,20 @@ void drawBackground_Widget(const iWidget *d) {
1183 fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget()); 1248 fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget());
1184 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); 1249 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
1185 } 1250 }
1251}
1252
1253void drawBackground_Widget(const iWidget *d) {
1254 if (d->flags & noBackground_WidgetFlag) {
1255 return;
1256 }
1257 if (!isDrawn_Widget_(d)) {
1258 return;
1259 }
1260 /* Popup menus have a shadowed border. */
1186 if (d->bgColor >= 0 || d->frameColor >= 0) { 1261 if (d->bgColor >= 0 || d->frameColor >= 0) {
1187 iRect rect = bounds_Widget(d); 1262 iRect rect = bounds_Widget(d);
1188 if (d->flags & drawBackgroundToBottom_WidgetFlag) { 1263 if (d->flags & drawBackgroundToBottom_WidgetFlag) {
1189 rect.size.y = size_Root(d->root).y - top_Rect(rect); 1264 rect.size.y = iMax(rect.size.y, size_Root(d->root).y - top_Rect(rect));
1190 } 1265 }
1191 iPaint p; 1266 iPaint p;
1192 init_Paint(&p); 1267 init_Paint(&p);
@@ -1242,8 +1317,54 @@ void drawBackground_Widget(const iWidget *d) {
1242 } 1317 }
1243} 1318}
1244 1319
1245iLocalDef iBool isDrawn_Widget_(const iWidget *d) { 1320int drawCount_;
1246 return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag; 1321
1322static iBool isRoot_Widget_(const iWidget *d) {
1323 return d == d->root->widget;
1324}
1325
1326iLocalDef iBool isFullyContainedByOther_Rect(const iRect d, const iRect other) {
1327 if (isEmpty_Rect(other)) {
1328 /* Nothing is contained by empty. */
1329 return iFalse;
1330 }
1331 if (isEmpty_Rect(d)) {
1332 /* Empty is fully contained by anything. */
1333 return iTrue;
1334 }
1335 return equal_Rect(intersect_Rect(d, other), d);
1336}
1337
1338static void addToPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs, iRect *fullyMasked) {
1339 if (isDrawn_Widget_(d)) {
1340 iRect bounds = bounds_Widget(d);
1341 if (d->flags & drawBackgroundToBottom_WidgetFlag) {
1342 bounds.size.y = size_Root(d->root).y - top_Rect(bounds);
1343 }
1344 if (isFullyContainedByOther_Rect(bounds, *fullyMasked)) {
1345 return; /* can't be seen */
1346 }
1347 pushBack_PtrArray(pvs, d);
1348 if (d->bgColor >= 0 && ~d->flags & noBackground_WidgetFlag &&
1349 isFullyContainedByOther_Rect(*fullyMasked, bounds)) {
1350 *fullyMasked = bounds;
1351 }
1352 }
1353}
1354
1355static void findPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs) {
1356 iRect fullyMasked = zero_Rect();
1357 if (isRoot_Widget_(d)) {
1358 iReverseConstForEach(PtrArray, i, onTop_Root(d->root)) {
1359 addToPotentiallyVisible_Widget_(i.ptr, pvs, &fullyMasked);
1360 }
1361 }
1362 iReverseConstForEach(ObjectList, i, d->children) {
1363 const iWidget *child = i.object;
1364 if (~child->flags & keepOnTop_WidgetFlag) {
1365 addToPotentiallyVisible_Widget_(child, pvs, &fullyMasked);
1366 }
1367 }
1247} 1368}
1248 1369
1249void drawChildren_Widget(const iWidget *d) { 1370void drawChildren_Widget(const iWidget *d) {
@@ -1253,21 +1374,85 @@ void drawChildren_Widget(const iWidget *d) {
1253 iConstForEach(ObjectList, i, d->children) { 1374 iConstForEach(ObjectList, i, d->children) {
1254 const iWidget *child = constAs_Widget(i.object); 1375 const iWidget *child = constAs_Widget(i.object);
1255 if (~child->flags & keepOnTop_WidgetFlag && isDrawn_Widget_(child)) { 1376 if (~child->flags & keepOnTop_WidgetFlag && isDrawn_Widget_(child)) {
1377 drawCount_++;
1256 class_Widget(child)->draw(child); 1378 class_Widget(child)->draw(child);
1257 } 1379 }
1258 } 1380 }
1381}
1382
1383void drawRoot_Widget(const iWidget *d) {
1384 iAssert(d == d->root->widget);
1259 /* Root draws the on-top widgets on top of everything else. */ 1385 /* Root draws the on-top widgets on top of everything else. */
1260 if (d == d->root->widget) { 1386 iPtrArray pvs;
1261 iConstForEach(PtrArray, i, onTop_Root(d->root)) { 1387 init_PtrArray(&pvs);
1262 const iWidget *top = *i.value; 1388 findPotentiallyVisible_Widget_(d, &pvs);
1263 class_Widget(top)->draw(top); 1389 iReverseConstForEach(PtrArray, i, &pvs) {
1264 } 1390 drawCount_++;
1265 } 1391 class_Widget(i.ptr)->draw(i.ptr);
1392 }
1393 deinit_PtrArray(&pvs);
1394}
1395
1396void setDrawBufferEnabled_Widget(iWidget *d, iBool enable) {
1397 if (enable && !d->drawBuf) {
1398 d->drawBuf = new_WidgetDrawBuffer();
1399 }
1400 else if (!enable && d->drawBuf) {
1401 delete_WidgetDrawBuffer(d->drawBuf);
1402 d->drawBuf = NULL;
1403 }
1404}
1405
1406static void beginBufferDraw_Widget_(const iWidget *d) {
1407 if (d->drawBuf) {
1408// printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid);
1409 const iRect bounds = bounds_Widget(d);
1410 SDL_Renderer *render = renderer_Window(get_Window());
1411 d->drawBuf->oldTarget = SDL_GetRenderTarget(render);
1412 d->drawBuf->oldOrigin = origin_Paint;
1413 realloc_WidgetDrawBuffer(d->drawBuf, render, boundsForDraw_Widget_(d).size);
1414 SDL_SetRenderTarget(render, d->drawBuf->texture);
1415 //SDL_SetRenderDrawColor(render, 255, 0, 0, 128);
1416 SDL_SetRenderDrawColor(render, 0, 0, 0, 0);
1417 SDL_RenderClear(render);
1418 origin_Paint = neg_I2(bounds.pos); /* with current visual offset */
1419// printf("beginBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y);
1420// fflush(stdout);
1421 }
1422}
1423
1424static void endBufferDraw_Widget_(const iWidget *d) {
1425 if (d->drawBuf) {
1426 d->drawBuf->isValid = iTrue;
1427 SDL_SetRenderTarget(renderer_Window(get_Window()), d->drawBuf->oldTarget);
1428 origin_Paint = d->drawBuf->oldOrigin;
1429// printf("endBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y);
1430// fflush(stdout);
1431 }
1266} 1432}
1267 1433
1268void draw_Widget(const iWidget *d) { 1434void draw_Widget(const iWidget *d) {
1269 drawBackground_Widget(d); 1435 if (!isDrawn_Widget_(d)) {
1270 drawChildren_Widget(d); 1436 if (d->drawBuf) {
1437// printf("[%p] drawBuffer released\n", d);
1438 release_WidgetDrawBuffer(d->drawBuf);
1439 }
1440 return;
1441 }
1442 drawLayerEffects_Widget_(d);
1443 if (!d->drawBuf || !checkDrawBuffer_Widget_(d)) {
1444 beginBufferDraw_Widget_(d);
1445 drawBackground_Widget(d);
1446 drawChildren_Widget(d);
1447 endBufferDraw_Widget_(d);
1448 }
1449 if (d->drawBuf) {
1450 iAssert(d->drawBuf->isValid);
1451 const iRect bounds = bounds_Widget(d);
1452 SDL_RenderCopy(renderer_Window(get_Window()), d->drawBuf->texture, NULL,
1453 &(SDL_Rect){ bounds.pos.x, bounds.pos.y,
1454 d->drawBuf->size.x, d->drawBuf->size.y });
1455 }
1271} 1456}
1272 1457
1273iAny *addChild_Widget(iWidget *d, iAnyObject *child) { 1458iAny *addChild_Widget(iWidget *d, iAnyObject *child) {
@@ -1659,12 +1844,20 @@ void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) {
1659 deinit_String(&str); 1844 deinit_String(&str);
1660} 1845}
1661 1846
1662void refresh_Widget(const iAnyObject *d) { 1847void refresh_Widget(const iAnyObject *d) {
1663 /* TODO: Could be widget specific, if parts of the tree are cached. */ 1848 /* TODO: Could be widget specific, if parts of the tree are cached. */
1664 /* TODO: The visbuffer in DocumentWidget and ListWidget could be moved to be a general 1849 /* TODO: The visbuffer in DocumentWidget and ListWidget could be moved to be a general
1665 purpose feature of Widget. */ 1850 purpose feature of Widget. */
1666 iAssert(isInstance_Object(d, &Class_Widget)); 1851 iAssert(isInstance_Object(d, &Class_Widget));
1667 iUnused(d); 1852 /* Mark draw buffers invalid. */
1853 for (const iWidget *w = d; w; w = w->parent) {
1854 if (w->drawBuf) {
1855// if (w->drawBuf->isValid) {
1856// printf("[%p] drawbuffer invalidated by %p\n", w, d); fflush(stdout);
1857// }
1858 w->drawBuf->isValid = iFalse;
1859 }
1860 }
1668 postRefresh_App(); 1861 postRefresh_App();
1669} 1862}
1670 1863