summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-12 13:49:38 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-12 13:49:38 +0300
commit33620846cca5678fbd662ea1a48fad302727dae7 (patch)
tree322fb4ab2c1dd7dd3eceee0834f3e90d27838de4
parent1410bbde7779efe3a20f603523547c8b8f55b6a1 (diff)
Mobile: Draw optimizations; focus handling
Widgets can now be marked for buffering their contents, which is useful if their contents change seldom but they are drawn often. For example, the navbar is always visible but doesn't change very often, and during animations menu contents are static but there is a moving animation so everything gets drawn 60 FPS. Focus handling was also improved so the lookup results can be scrolled while entering text, and one can tap outside an input field to unfocus it.
-rw-r--r--src/ui/inputwidget.c5
-rw-r--r--src/ui/mobile.c4
-rw-r--r--src/ui/paint.c18
-rw-r--r--src/ui/paint.h2
-rw-r--r--src/ui/root.c54
-rw-r--r--src/ui/sidebarwidget.c3
-rw-r--r--src/ui/text.c7
-rw-r--r--src/ui/text_simple.c2
-rw-r--r--src/ui/touch.c2
-rw-r--r--src/ui/translation.c3
-rw-r--r--src/ui/uploadwidget.c8
-rw-r--r--src/ui/util.c1
-rw-r--r--src/ui/widget.c241
-rw-r--r--src/ui/widget.h9
-rw-r--r--src/ui/window.c7
15 files changed, 327 insertions, 39 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 6e9ef6c2..802a2d6c 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -1565,6 +1565,11 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
1565 } 1565 }
1566 switch (processEvent_Click(&d->click, ev)) { 1566 switch (processEvent_Click(&d->click, ev)) {
1567 case none_ClickResult: 1567 case none_ClickResult:
1568 if (ev->type == SDL_MOUSEBUTTONUP &&
1569 deviceType_App() != desktop_AppDeviceType && isFocused_Widget(d)) {
1570 setFocus_Widget(NULL);
1571 return iTrue;
1572 }
1568 break; 1573 break;
1569 case started_ClickResult: { 1574 case started_ClickResult: {
1570 setFocus_Widget(w); 1575 setFocus_Widget(w);
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index 6ea672e6..9e2dc4f7 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -357,6 +357,7 @@ static iWidget *addChildPanel_(iWidget *parent, iLabelWidget *panelButton,
357 setId_Widget(panel, "panel"); 357 setId_Widget(panel, "panel");
358 setUserData_Object(panelButton, panel); 358 setUserData_Object(panelButton, panel);
359 setBackgroundColor_Widget(panel, uiBackground_ColorId); 359 setBackgroundColor_Widget(panel, uiBackground_ColorId);
360 setDrawBufferEnabled_Widget(panel, iTrue);
360 setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad"); 361 setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad");
361 if (titleText) { 362 if (titleText) {
362 iLabelWidget *title = 363 iLabelWidget *title =
@@ -601,6 +602,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,
601 /* The panel roots. */ 602 /* The panel roots. */
602 iWidget *topPanel = new_Widget(); { 603 iWidget *topPanel = new_Widget(); {
603 setId_Widget(topPanel, "panel.top"); 604 setId_Widget(topPanel, "panel.top");
605 setDrawBufferEnabled_Widget(topPanel, iTrue);
604 setCommandHandler_Widget(topPanel, topPanelHandler_); 606 setCommandHandler_Widget(topPanel, topPanelHandler_);
605 setFlags_Widget(topPanel, 607 setFlags_Widget(topPanel,
606 arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag | 608 arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
@@ -730,7 +732,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,
730 updatePanelSheetMetrics_(panels); 732 updatePanelSheetMetrics_(panels);
731 arrange_Widget(panels); 733 arrange_Widget(panels);
732 postCommand_App("widget.overflow"); /* with the correct dimensions */ 734 postCommand_App("widget.overflow"); /* with the correct dimensions */
733 printTree_Widget(panels); 735// printTree_Widget(panels);
734} 736}
735 737
736#if 0 738#if 0
diff --git a/src/ui/paint.c b/src/ui/paint.c
index 79adb7d1..af62f908 100644
--- a/src/ui/paint.c
+++ b/src/ui/paint.c
@@ -24,6 +24,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
24 24
25#include <SDL_version.h> 25#include <SDL_version.h>
26 26
27iInt2 origin_Paint;
28
27iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) { 29iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) {
28 iAssert(d->dst); 30 iAssert(d->dst);
29 return d->dst->render; 31 return d->dst->render;
@@ -62,10 +64,11 @@ void endTarget_Paint(iPaint *d) {
62} 64}
63 65
64void setClip_Paint(iPaint *d, iRect rect) { 66void setClip_Paint(iPaint *d, iRect rect) {
65 rect = intersect_Rect(rect, rect_Root(get_Root())); 67 //rect = intersect_Rect(rect, rect_Root(get_Root()));
66 if (isEmpty_Rect(rect)) { 68 if (isEmpty_Rect(rect)) {
67 rect = init_Rect(0, 0, 1, 1); 69 rect = init_Rect(0, 0, 1, 1);
68 } 70 }
71 addv_I2(&rect.pos, origin_Paint);
69 SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect); 72 SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect);
70} 73}
71 74
@@ -85,6 +88,7 @@ void unsetClip_Paint(iPaint *d) {
85} 88}
86 89
87void drawRect_Paint(const iPaint *d, iRect rect, int color) { 90void drawRect_Paint(const iPaint *d, iRect rect, int color) {
91 addv_I2(&rect.pos, origin_Paint);
88 iInt2 br = bottomRight_Rect(rect); 92 iInt2 br = bottomRight_Rect(rect);
89 /* Keep the right/bottom edge visible in the window. */ 93 /* Keep the right/bottom edge visible in the window. */
90 if (br.x == d->dst->size.x) br.x--; 94 if (br.x == d->dst->size.x) br.x--;
@@ -115,11 +119,14 @@ void drawRectThickness_Paint(const iPaint *d, iRect rect, int thickness, int col
115} 119}
116 120
117void fillRect_Paint(const iPaint *d, iRect rect, int color) { 121void fillRect_Paint(const iPaint *d, iRect rect, int color) {
122 addv_I2(&rect.pos, origin_Paint);
118 setColor_Paint_(d, color); 123 setColor_Paint_(d, color);
124// printf("fillRect_Paint: %d,%d %dx%d\n", rect.pos.x, rect.pos.y, rect.size.x, rect.size.y);
119 SDL_RenderFillRect(renderer_Paint_(d), (SDL_Rect *) &rect); 125 SDL_RenderFillRect(renderer_Paint_(d), (SDL_Rect *) &rect);
120} 126}
121 127
122void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color, int alpha) { 128void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color, int alpha) {
129 addv_I2(&inner.pos, origin_Paint);
123 SDL_Renderer *render = renderer_Paint_(d); 130 SDL_Renderer *render = renderer_Paint_(d);
124 SDL_Texture *shadow = get_Window()->borderShadow; 131 SDL_Texture *shadow = get_Window()->borderShadow;
125 const iInt2 size = size_SDLTexture(shadow); 132 const iInt2 size = size_SDLTexture(shadow);
@@ -146,9 +153,14 @@ void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color
146 &(SDL_Rect){ outer.pos.x, inner.pos.y, thickness, inner.size.y }); 153 &(SDL_Rect){ outer.pos.x, inner.pos.y, thickness, inner.size.y });
147} 154}
148 155
149void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t count, int color) { 156void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t n, int color) {
150 setColor_Paint_(d, color); 157 setColor_Paint_(d, color);
151 SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) points, count); 158 iInt2 *offsetPoints = malloc(sizeof(iInt2) * n);
159 for (size_t i = 0; i < n; i++) {
160 offsetPoints[i] = add_I2(points[i], origin_Paint);
161 }
162 SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) offsetPoints, n);
163 free(offsetPoints);
152} 164}
153 165
154iInt2 size_SDLTexture(SDL_Texture *d) { 166iInt2 size_SDLTexture(SDL_Texture *d) {
diff --git a/src/ui/paint.h b/src/ui/paint.h
index 90cc2aef..e6701635 100644
--- a/src/ui/paint.h
+++ b/src/ui/paint.h
@@ -36,6 +36,8 @@ struct Impl_Paint {
36 uint8_t alpha; 36 uint8_t alpha;
37}; 37};
38 38
39extern iInt2 origin_Paint; /* add this to all drawn positions so buffered graphics are correctly offset */
40
39void init_Paint (iPaint *); 41void init_Paint (iPaint *);
40 42
41void beginTarget_Paint (iPaint *, SDL_Texture *target); 43void beginTarget_Paint (iPaint *, SDL_Texture *target);
diff --git a/src/ui/root.c b/src/ui/root.c
index a792e93d..7b2b5b15 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -685,6 +685,34 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
685 } 685 }
686 return iTrue; 686 return iTrue;
687 } 687 }
688 else if (deviceType_App() != desktop_AppDeviceType &&
689 (equal_Command(cmd, "focus.gained") || equal_Command(cmd, "focus.lost"))) {
690 iInputWidget *url = findChild_Widget(navBar, "url");
691 if (pointer_Command(cmd) == url) {
692 const iBool isFocused = equal_Command(cmd, "focus.gained");
693 setFlags_Widget(findChild_Widget(navBar, "navbar.clear"), hidden_WidgetFlag, !isFocused);
694 showCollapsed_Widget(findChild_Widget(navBar, "navbar.cancel"), isFocused);
695 showCollapsed_Widget(findChild_Widget(navBar, "pagemenubutton"), !isFocused);
696 showCollapsed_Widget(findChild_Widget(navBar, "reload"), !isFocused);
697 }
698 return iFalse;
699 }
700 else if (equal_Command(cmd, "navbar.clear")) {
701 iInputWidget *url = findChild_Widget(navBar, "url");
702 selectAll_InputWidget(url);
703 /* Emulate a Backspace keypress. */
704 class_InputWidget(url)->processEvent(
705 as_Widget(url),
706 (SDL_Event *) &(SDL_KeyboardEvent){ .type = SDL_KEYDOWN,
707 .timestamp = SDL_GetTicks(),
708 .state = SDL_PRESSED,
709 .keysym = { .sym = SDLK_BACKSPACE } });
710 return iTrue;
711 }
712 else if (equal_Command(cmd, "navbar.cancel")) {
713 setFocus_Widget(NULL);
714 return iTrue;
715 }
688 else if (equal_Command(cmd, "input.edited")) { 716 else if (equal_Command(cmd, "input.edited")) {
689 iAnyObject * url = findChild_Widget(navBar, "url"); 717 iAnyObject * url = findChild_Widget(navBar, "url");
690 const iString *text = text_InputWidget(url); 718 const iString *text = text_InputWidget(url);
@@ -941,7 +969,7 @@ void updateMetrics_Root(iRoot *d) {
941 setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y)); 969 setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y));
942 } 970 }
943 iWidget *navBar = findChild_Widget(d->widget, "navbar"); 971 iWidget *navBar = findChild_Widget(d->widget, "navbar");
944 iWidget *lock = findChild_Widget(navBar, "navbar.lock"); 972// iWidget *lock = findChild_Widget(navBar, "navbar.lock");
945 iWidget *url = findChild_Widget(d->widget, "url"); 973 iWidget *url = findChild_Widget(d->widget, "url");
946 iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed"); 974 iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed");
947 iWidget *embedPad = findChild_Widget(navBar, "url.embedpad"); 975 iWidget *embedPad = findChild_Widget(navBar, "url.embedpad");
@@ -1044,6 +1072,7 @@ void createUserInterface_Root(iRoot *d) {
1044 /* Navigation bar. */ { 1072 /* Navigation bar. */ {
1045 navBar = new_Widget(); 1073 navBar = new_Widget();
1046 setId_Widget(navBar, "navbar"); 1074 setId_Widget(navBar, "navbar");
1075 setDrawBufferEnabled_Widget(navBar, iTrue);
1047 setFlags_Widget(navBar, 1076 setFlags_Widget(navBar,
1048 hittable_WidgetFlag | /* context menu */ 1077 hittable_WidgetFlag | /* context menu */
1049 arrangeHeight_WidgetFlag | 1078 arrangeHeight_WidgetFlag |
@@ -1095,6 +1124,16 @@ void createUserInterface_Root(iRoot *d) {
1095 setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize); 1124 setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize);
1096 updateTextCStr_LabelWidget(lock, "\U0001f512"); 1125 updateTextCStr_LabelWidget(lock, "\U0001f512");
1097 } 1126 }
1127 /* Button for clearing the URL bar contents. */ {
1128 iLabelWidget *clear = addChildFlags_Widget(
1129 as_Widget(url),
1130 iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")),
1131 hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag);
1132 setId_Widget(as_Widget(clear), "navbar.clear");
1133 setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize);
1134 setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse);
1135 setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId);
1136 }
1098 iWidget *rightEmbed = new_Widget(); 1137 iWidget *rightEmbed = new_Widget();
1099 setId_Widget(rightEmbed, "url.rightembed"); 1138 setId_Widget(rightEmbed, "url.rightembed");
1100 addChildFlags_Widget(as_Widget(url), 1139 addChildFlags_Widget(as_Widget(url),
@@ -1151,6 +1190,13 @@ void createUserInterface_Root(iRoot *d) {
1151 setFlags_Widget(urlButtons, embedFlags | arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); 1190 setFlags_Widget(urlButtons, embedFlags | arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
1152 /* Mobile page menu. */ 1191 /* Mobile page menu. */
1153 if (deviceType_App() != desktop_AppDeviceType) { 1192 if (deviceType_App() != desktop_AppDeviceType) {
1193 iLabelWidget *navCancel = new_LabelWidget("${cancel}", "navbar.cancel");
1194 addChildFlags_Widget(urlButtons, iClob(navCancel),
1195 (embedFlags | tight_WidgetFlag | hidden_WidgetFlag |
1196 collapse_WidgetFlag) & ~noBackground_WidgetFlag);
1197 as_Widget(navCancel)->sizeRef = as_Widget(url);
1198// setFont_LabelWidget(navCancel, defaultBold_FontId);
1199 setId_Widget(as_Widget(navCancel), "navbar.cancel");
1154 iLabelWidget *pageMenuButton; 1200 iLabelWidget *pageMenuButton;
1155 /* In a mobile layout, the reload button is replaced with the Page/Ellipsis menu. */ 1201 /* In a mobile layout, the reload button is replaced with the Page/Ellipsis menu. */
1156 pageMenuButton = makeMenuButton_LabelWidget(pageMenuCStr_, 1202 pageMenuButton = makeMenuButton_LabelWidget(pageMenuCStr_,
@@ -1172,13 +1218,14 @@ void createUserInterface_Root(iRoot *d) {
1172 setId_Widget(as_Widget(pageMenuButton), "pagemenubutton"); 1218 setId_Widget(as_Widget(pageMenuButton), "pagemenubutton");
1173 setFont_LabelWidget(pageMenuButton, uiContentBold_FontId); 1219 setFont_LabelWidget(pageMenuButton, uiContentBold_FontId);
1174 setAlignVisually_LabelWidget(pageMenuButton, iTrue); 1220 setAlignVisually_LabelWidget(pageMenuButton, iTrue);
1175 addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags | tight_WidgetFlag); 1221 addChildFlags_Widget(urlButtons, iClob(pageMenuButton),
1222 embedFlags | tight_WidgetFlag | collapse_WidgetFlag);
1176 updateSize_LabelWidget(pageMenuButton); 1223 updateSize_LabelWidget(pageMenuButton);
1177 } 1224 }
1178 /* Reload button. */ { 1225 /* Reload button. */ {
1179 iLabelWidget *reload = newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload"); 1226 iLabelWidget *reload = newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload");
1180 setId_Widget(as_Widget(reload), "reload"); 1227 setId_Widget(as_Widget(reload), "reload");
1181 addChildFlags_Widget(urlButtons, iClob(reload), embedFlags); 1228 addChildFlags_Widget(urlButtons, iClob(reload), embedFlags | collapse_WidgetFlag);
1182 updateSize_LabelWidget(reload); 1229 updateSize_LabelWidget(reload);
1183 } 1230 }
1184 addChildFlags_Widget(as_Widget(url), iClob(urlButtons), moveToParentRightEdge_WidgetFlag); 1231 addChildFlags_Widget(as_Widget(url), iClob(urlButtons), moveToParentRightEdge_WidgetFlag);
@@ -1287,6 +1334,7 @@ void createUserInterface_Root(iRoot *d) {
1287 iWidget *toolBar = new_Widget(); 1334 iWidget *toolBar = new_Widget();
1288 addChild_Widget(root, iClob(toolBar)); 1335 addChild_Widget(root, iClob(toolBar));
1289 setId_Widget(toolBar, "toolbar"); 1336 setId_Widget(toolBar, "toolbar");
1337 setDrawBufferEnabled_Widget(toolBar, iTrue);
1290 setCommandHandler_Widget(toolBar, handleToolBarCommands_); 1338 setCommandHandler_Widget(toolBar, handleToolBarCommands_);
1291 setFlags_Widget(toolBar, moveToParentBottomEdge_WidgetFlag | 1339 setFlags_Widget(toolBar, moveToParentBottomEdge_WidgetFlag |
1292 parentCannotResizeHeight_WidgetFlag | 1340 parentCannotResizeHeight_WidgetFlag |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index b816b572..eb129424 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -663,8 +663,9 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
663 /* On a phone, the right sidebar is used exclusively for Identities. */ 663 /* On a phone, the right sidebar is used exclusively for Identities. */
664 const iBool isPhone = deviceType_App() == phone_AppDeviceType; 664 const iBool isPhone = deviceType_App() == phone_AppDeviceType;
665 if (!isPhone || d->side == left_SidebarSide) { 665 if (!isPhone || d->side == left_SidebarSide) {
666 iWidget *buttons = new_Widget(); 666 iWidget *buttons = new_Widget();
667 setId_Widget(buttons, "buttons"); 667 setId_Widget(buttons, "buttons");
668 setDrawBufferEnabled_Widget(buttons, iTrue);
668 for (int i = 0; i < max_SidebarMode; i++) { 669 for (int i = 0; i < max_SidebarMode; i++) {
669 if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) { 670 if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) {
670 continue; 671 continue;
diff --git a/src/ui/text.c b/src/ui/text.c
index 231281eb..f7fff4bc 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25#include "metrics.h" 25#include "metrics.h"
26#include "embedded.h" 26#include "embedded.h"
27#include "window.h" 27#include "window.h"
28#include "paint.h"
28#include "app.h" 29#include "app.h"
29 30
30#define STB_TRUETYPE_IMPLEMENTATION 31#define STB_TRUETYPE_IMPLEMENTATION
@@ -1712,6 +1713,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1712 } 1713 }
1713 SDL_Rect src; 1714 SDL_Rect src;
1714 memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); 1715 memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect));
1716 dst.x += origin_Paint.x;
1717 dst.y += origin_Paint.y;
1715 if (args->mode & fillBackground_RunMode) { 1718 if (args->mode & fillBackground_RunMode) {
1716 /* Alpha blending looks much better if the RGB components don't change in 1719 /* Alpha blending looks much better if the RGB components don't change in
1717 the partially transparent pixels. */ 1720 the partially transparent pixels. */
@@ -2182,6 +2185,8 @@ void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {
2182 } 2185 }
2183 if (d->texture) { 2186 if (d->texture) {
2184 SDL_Texture *oldTarget = SDL_GetRenderTarget(render); 2187 SDL_Texture *oldTarget = SDL_GetRenderTarget(render);
2188 const iInt2 oldOrigin = origin_Paint;
2189 origin_Paint = zero_I2();
2185 SDL_SetRenderTarget(render, d->texture); 2190 SDL_SetRenderTarget(render, d->texture);
2186 SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); 2191 SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);
2187 SDL_SetRenderDrawColor(render, 255, 255, 255, 0); 2192 SDL_SetRenderDrawColor(render, 255, 255, 255, 0);
@@ -2190,6 +2195,7 @@ void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {
2190 draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId); 2195 draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId);
2191 SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND); 2196 SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND);
2192 SDL_SetRenderTarget(render, oldTarget); 2197 SDL_SetRenderTarget(render, oldTarget);
2198 origin_Paint = oldOrigin;
2193 SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); 2199 SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);
2194 } 2200 }
2195} 2201}
@@ -2203,6 +2209,7 @@ iTextBuf *newRange_TextBuf(int font, int color, iRangecc text) {
2203} 2209}
2204 2210
2205void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) { 2211void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) {
2212 addv_I2(&pos, origin_Paint);
2206 const iColor clr = get_Color(color); 2213 const iColor clr = get_Color(color);
2207 SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b); 2214 SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b);
2208 SDL_RenderCopy(text_.render, 2215 SDL_RenderCopy(text_.render,
diff --git a/src/ui/text_simple.c b/src/ui/text_simple.c
index e88b09a8..bf33b4be 100644
--- a/src/ui/text_simple.c
+++ b/src/ui/text_simple.c
@@ -306,6 +306,8 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
306 src.y += over; 306 src.y += over;
307 src.h -= over; 307 src.h -= over;
308 } 308 }
309 dst.x += origin_Paint.x;
310 dst.y += origin_Paint.y;
309 if (args->mode & fillBackground_RunMode) { 311 if (args->mode & fillBackground_RunMode) {
310 /* Alpha blending looks much better if the RGB components don't change in 312 /* Alpha blending looks much better if the RGB components don't change in
311 the partially transparent pixels. */ 313 the partially transparent pixels. */
diff --git a/src/ui/touch.c b/src/ui/touch.c
index dac1152e..f0456acb 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -614,7 +614,7 @@ iBool processEvent_Touch(const SDL_Event *ev) {
614// pixels.y, y_F3(amount), y_F3(touch->accum), 614// pixels.y, y_F3(amount), y_F3(touch->accum),
615// touch->edge); 615// touch->edge);
616 if (pixels.x || pixels.y) { 616 if (pixels.x || pixels.y) {
617 setFocus_Widget(NULL); 617 //setFocus_Widget(NULL);
618 dispatchMotion_Touch_(touch->pos[0], 0); 618 dispatchMotion_Touch_(touch->pos[0], 0);
619 setCurrent_Root(touch->affinity->root); 619 setCurrent_Root(touch->affinity->root);
620 dispatchEvent_Widget(touch->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){ 620 dispatchEvent_Widget(touch->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){
diff --git a/src/ui/translation.c b/src/ui/translation.c
index cef68dce..b86e6e52 100644
--- a/src/ui/translation.c
+++ b/src/ui/translation.c
@@ -136,7 +136,8 @@ static void draw_TranslationProgressWidget_(const iTranslationProgressWidget *d)
136 get_Color(palette[palCur]), get_Color(palette[palNext]), palPos - (int) palPos); 136 get_Color(palette[palCur]), get_Color(palette[palNext]), palPos - (int) palPos);
137 SDL_SetRenderDrawColor(renderer_Window(get_Window()), back.r, back.g, back.b, p.alpha); 137 SDL_SetRenderDrawColor(renderer_Window(get_Window()), back.r, back.g, back.b, p.alpha);
138 SDL_RenderFillRect(renderer_Window(get_Window()), 138 SDL_RenderFillRect(renderer_Window(get_Window()),
139 &(SDL_Rect){ pos.x, pos.y, spr->size.x, spr->size.y }); 139 &(SDL_Rect){ pos.x + origin_Paint.x, pos.y + origin_Paint.y,
140 spr->size.x, spr->size.y });
140 if (fg >= 0) { 141 if (fg >= 0) {
141 setOpacity_Text(opacity * 2); 142 setOpacity_Text(opacity * 2);
142 drawRange_Text(d->font, addX_I2(pos, spr->xoff), fg, range_String(&spr->text)); 143 drawRange_Text(d->font, addX_I2(pos, spr->xoff), fg, range_String(&spr->text));
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index fb8aaf0a..78a1196a 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -376,11 +376,11 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
376 return processEvent_Widget(w, ev); 376 return processEvent_Widget(w, ev);
377} 377}
378 378
379static void draw_UploadWidget_(const iUploadWidget *d) { 379//static void draw_UploadWidget_(const iUploadWidget *d) {
380 draw_Widget(constAs_Widget(d)); 380// draw_Widget(constAs_Widget(d));
381} 381//}
382 382
383iBeginDefineSubclass(UploadWidget, Widget) 383iBeginDefineSubclass(UploadWidget, Widget)
384 .processEvent = (iAny *) processEvent_UploadWidget_, 384 .processEvent = (iAny *) processEvent_UploadWidget_,
385 .draw = (iAny *) draw_UploadWidget_, 385 .draw = draw_Widget,
386iEndDefineSubclass(UploadWidget) 386iEndDefineSubclass(UploadWidget)
diff --git a/src/ui/util.c b/src/ui/util.c
index 6069e800..05d39c01 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -685,6 +685,7 @@ static iWidget *makeMenuSeparator_(void) {
685 685
686iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { 686iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
687 iWidget *menu = new_Widget(); 687 iWidget *menu = new_Widget();
688 setDrawBufferEnabled_Widget(menu, iTrue);
688 setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); 689 setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId);
689 if (deviceType_App() != desktop_AppDeviceType) { 690 if (deviceType_App() != desktop_AppDeviceType) {
690 setPadding1_Widget(menu, 2 * gap_UI); 691 setPadding1_Widget(menu, 2 * gap_UI);
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
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 1a944c0a..fd4d8898 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -131,6 +131,8 @@ enum iWidgetFocusDir {
131 backward_WidgetFocusDir, 131 backward_WidgetFocusDir,
132}; 132};
133 133
134iDeclareType(WidgetDrawBuffer)
135
134struct Impl_Widget { 136struct Impl_Widget {
135 iObject object; 137 iObject object;
136 iString id; 138 iString id;
@@ -148,6 +150,7 @@ struct Impl_Widget {
148 iWidget * parent; 150 iWidget * parent;
149 iBool (*commandHandler)(iWidget *, const char *); 151 iBool (*commandHandler)(iWidget *, const char *);
150 iRoot * root; 152 iRoot * root;
153 iWidgetDrawBuffer *drawBuf;
151}; 154};
152 155
153iDeclareObjectConstruction(Widget) 156iDeclareObjectConstruction(Widget)
@@ -203,6 +206,12 @@ size_t childCount_Widget (const iWidget *);
203void draw_Widget (const iWidget *); 206void draw_Widget (const iWidget *);
204void drawBackground_Widget (const iWidget *); 207void drawBackground_Widget (const iWidget *);
205void drawChildren_Widget (const iWidget *); 208void drawChildren_Widget (const iWidget *);
209void drawRoot_Widget (const iWidget *); /* root only */
210void setDrawBufferEnabled_Widget (iWidget *, iBool enable);
211
212iLocalDef iBool isDrawBufferEnabled_Widget(const iWidget *d) {
213 return d && d->drawBuf;
214}
206 215
207iLocalDef int width_Widget(const iAnyObject *d) { 216iLocalDef int width_Widget(const iAnyObject *d) {
208 if (d) { 217 if (d) {
diff --git a/src/ui/window.c b/src/ui/window.c
index 096853cc..3385f436 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1060,12 +1060,13 @@ void draw_Window(iWindow *d) {
1060 d->frameTime = SDL_GetTicks(); 1060 d->frameTime = SDL_GetTicks();
1061 if (isExposed_Window(d)) { 1061 if (isExposed_Window(d)) {
1062 d->isInvalidated = iFalse; 1062 d->isInvalidated = iFalse;
1063 extern int drawCount_;
1063 iForIndices(i, d->roots) { 1064 iForIndices(i, d->roots) {
1064 iRoot *root = d->roots[i]; 1065 iRoot *root = d->roots[i];
1065 if (root) { 1066 if (root) {
1066 setCurrent_Root(root); 1067 setCurrent_Root(root);
1067 unsetClip_Paint(&p); /* update clip to current root */ 1068 unsetClip_Paint(&p); /* update clip to current root */
1068 draw_Widget(root->widget); 1069 drawRoot_Widget(root->widget);
1069#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 1070#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
1070 /* App icon. */ 1071 /* App icon. */
1071 const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon"); 1072 const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon");
@@ -1105,6 +1106,10 @@ void draw_Window(iWindow *d) {
1105 } 1106 }
1106 } 1107 }
1107 setCurrent_Root(NULL); 1108 setCurrent_Root(NULL);
1109#if !defined (NDEBUG)
1110 draw_Text(defaultBold_FontId, zero_I2(), red_ColorId, "%d", drawCount_);
1111 drawCount_ = 0;
1112#endif
1108 } 1113 }
1109#if 0 1114#if 0
1110 /* Text cache debugging. */ { 1115 /* Text cache debugging. */ {