summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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. */ {