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) {

 }

 switch (processEvent_Click(&d->click, ev)) {

     case none_ClickResult:

+ if (ev->type == SDL_MOUSEBUTTONUP &&

+ deviceType_App() != desktop_AppDeviceType && isFocused_Widget(d)) {

+ setFocus_Widget(NULL);

+ return iTrue;

+ }

         break;

     case started_ClickResult: {

         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,

 setId_Widget(panel, "panel");

 setUserData_Object(panelButton, panel);

 setBackgroundColor_Widget(panel, uiBackground_ColorId);

+ setDrawBufferEnabled_Widget(panel, iTrue);

 setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad");

 if (titleText) {

     iLabelWidget *title =

@@ -601,6 +602,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,

 /* The panel roots. */

 iWidget *topPanel = new_Widget(); {

     setId_Widget(topPanel, "panel.top");

+ setDrawBufferEnabled_Widget(topPanel, iTrue);

     setCommandHandler_Widget(topPanel, topPanelHandler_);

     setFlags_Widget(topPanel,

                     arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag |

@@ -730,7 +732,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,

 updatePanelSheetMetrics_(panels);

 arrange_Widget(panels);

 postCommand_App("widget.overflow"); /* with the correct dimensions */    

- printTree_Widget(panels);

+// printTree_Widget(panels);

}



#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. */



#include <SDL_version.h>



+iInt2 origin_Paint;

+

iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) {

 iAssert(d->dst);

 return d->dst->render;

@@ -62,10 +64,11 @@ void endTarget_Paint(iPaint *d) {

}



void setClip_Paint(iPaint *d, iRect rect) {

- rect = intersect_Rect(rect, rect_Root(get_Root()));

+ //rect = intersect_Rect(rect, rect_Root(get_Root()));

 if (isEmpty_Rect(rect)) {

     rect = init_Rect(0, 0, 1, 1);

 }

+ addv_I2(&rect.pos, origin_Paint);

 SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect);

}



@@ -85,6 +88,7 @@ void unsetClip_Paint(iPaint *d) {

}



void drawRect_Paint(const iPaint *d, iRect rect, int color) {

+ addv_I2(&rect.pos, origin_Paint);

 iInt2 br = bottomRight_Rect(rect);

 /* Keep the right/bottom edge visible in the window. */

 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

}



void fillRect_Paint(const iPaint *d, iRect rect, int color) {

+ addv_I2(&rect.pos, origin_Paint);

 setColor_Paint_(d, color);

+// printf("fillRect_Paint: %d,%d %dx%d\n", rect.pos.x, rect.pos.y, rect.size.x, rect.size.y);

 SDL_RenderFillRect(renderer_Paint_(d), (SDL_Rect *) &rect);

}



void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color, int alpha) {

+ addv_I2(&inner.pos, origin_Paint);

 SDL_Renderer *render = renderer_Paint_(d);

 SDL_Texture *shadow = get_Window()->borderShadow;

 const iInt2 size = size_SDLTexture(shadow);

@@ -146,9 +153,14 @@ void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color

                &(SDL_Rect){ outer.pos.x, inner.pos.y, thickness, inner.size.y });

}



-void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t count, int color) {

+void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t n, int color) {

 setColor_Paint_(d, color);

- SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) points, count);

+ iInt2 *offsetPoints = malloc(sizeof(iInt2) * n);

+ for (size_t i = 0; i < n; i++) {

+ offsetPoints[i] = add_I2(points[i], origin_Paint);

+ }

+ SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) offsetPoints, n);

+ free(offsetPoints);

}



iInt2 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 {

 uint8_t      alpha;

};



+extern iInt2 origin_Paint; /* add this to all drawn positions so buffered graphics are correctly offset */

+

void init_Paint (iPaint *);



void 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) {

     }

     return iTrue;

 }

+ else if (deviceType_App() != desktop_AppDeviceType &&

+ (equal_Command(cmd, "focus.gained") || equal_Command(cmd, "focus.lost"))) {

+ iInputWidget *url = findChild_Widget(navBar, "url");

+ if (pointer_Command(cmd) == url) {

+ const iBool isFocused = equal_Command(cmd, "focus.gained");

+ setFlags_Widget(findChild_Widget(navBar, "navbar.clear"), hidden_WidgetFlag, !isFocused);

+ showCollapsed_Widget(findChild_Widget(navBar, "navbar.cancel"), isFocused);

+ showCollapsed_Widget(findChild_Widget(navBar, "pagemenubutton"), !isFocused);

+ showCollapsed_Widget(findChild_Widget(navBar, "reload"), !isFocused);

+ }

+ return iFalse;

+ }

+ else if (equal_Command(cmd, "navbar.clear")) {

+ iInputWidget *url = findChild_Widget(navBar, "url");

+ selectAll_InputWidget(url);

+ /* Emulate a Backspace keypress. */

+ class_InputWidget(url)->processEvent(

+ as_Widget(url),

+ (SDL_Event *) &(SDL_KeyboardEvent){ .type = SDL_KEYDOWN,

+ .timestamp = SDL_GetTicks(),

+ .state = SDL_PRESSED,

+ .keysym = { .sym = SDLK_BACKSPACE } });

+ return iTrue;

+ }

+ else if (equal_Command(cmd, "navbar.cancel")) {

+ setFocus_Widget(NULL);

+ return iTrue;

+ }

 else if (equal_Command(cmd, "input.edited")) {

     iAnyObject *   url  = findChild_Widget(navBar, "url");

     const iString *text = text_InputWidget(url);

@@ -941,7 +969,7 @@ void updateMetrics_Root(iRoot *d) {

     setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y));

 }

 iWidget *navBar     = findChild_Widget(d->widget, "navbar");

- iWidget *lock = findChild_Widget(navBar, "navbar.lock");

+// iWidget *lock = findChild_Widget(navBar, "navbar.lock");

 iWidget *url        = findChild_Widget(d->widget, "url");

 iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed");

 iWidget *embedPad   = findChild_Widget(navBar, "url.embedpad");

@@ -1044,6 +1072,7 @@ void createUserInterface_Root(iRoot *d) {

 /* Navigation bar. */ {

     navBar = new_Widget();

     setId_Widget(navBar, "navbar");

+ setDrawBufferEnabled_Widget(navBar, iTrue);

     setFlags_Widget(navBar,

                     hittable_WidgetFlag | /* context menu */

                         arrangeHeight_WidgetFlag |

@@ -1095,6 +1124,16 @@ void createUserInterface_Root(iRoot *d) {

             setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize);

             updateTextCStr_LabelWidget(lock, "\U0001f512");

         }

+ /* Button for clearing the URL bar contents. */ {

+ iLabelWidget *clear = addChildFlags_Widget(

+ as_Widget(url),

+ iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")),

+ hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag);

+ setId_Widget(as_Widget(clear), "navbar.clear");

+ setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize);

+ setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse);

+ setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId);

+ }

         iWidget *rightEmbed = new_Widget();

         setId_Widget(rightEmbed, "url.rightembed");

         addChildFlags_Widget(as_Widget(url),

@@ -1151,6 +1190,13 @@ void createUserInterface_Root(iRoot *d) {

         setFlags_Widget(urlButtons, embedFlags | arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);

         /* Mobile page menu. */

         if (deviceType_App() != desktop_AppDeviceType) {

+ iLabelWidget *navCancel = new_LabelWidget("${cancel}", "navbar.cancel");

+ addChildFlags_Widget(urlButtons, iClob(navCancel),

+ (embedFlags | tight_WidgetFlag | hidden_WidgetFlag |

+ collapse_WidgetFlag) & ~noBackground_WidgetFlag);

+ as_Widget(navCancel)->sizeRef = as_Widget(url);

+// setFont_LabelWidget(navCancel, defaultBold_FontId);

+ setId_Widget(as_Widget(navCancel), "navbar.cancel");

             iLabelWidget *pageMenuButton;

             /* In a mobile layout, the reload button is replaced with the Page/Ellipsis menu. */

             pageMenuButton = makeMenuButton_LabelWidget(pageMenuCStr_,

@@ -1172,13 +1218,14 @@ void createUserInterface_Root(iRoot *d) {

             setId_Widget(as_Widget(pageMenuButton), "pagemenubutton");

             setFont_LabelWidget(pageMenuButton, uiContentBold_FontId);

             setAlignVisually_LabelWidget(pageMenuButton, iTrue);

- addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags | tight_WidgetFlag);

+ addChildFlags_Widget(urlButtons, iClob(pageMenuButton),

+ embedFlags | tight_WidgetFlag | collapse_WidgetFlag);

             updateSize_LabelWidget(pageMenuButton);

         }

         /* Reload button. */ {

             iLabelWidget *reload = newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload");

             setId_Widget(as_Widget(reload), "reload");

- addChildFlags_Widget(urlButtons, iClob(reload), embedFlags);

+ addChildFlags_Widget(urlButtons, iClob(reload), embedFlags | collapse_WidgetFlag);

             updateSize_LabelWidget(reload);

         }

         addChildFlags_Widget(as_Widget(url), iClob(urlButtons), moveToParentRightEdge_WidgetFlag);

@@ -1287,6 +1334,7 @@ void createUserInterface_Root(iRoot *d) {

     iWidget *toolBar = new_Widget();

     addChild_Widget(root, iClob(toolBar));

     setId_Widget(toolBar, "toolbar");

+ setDrawBufferEnabled_Widget(toolBar, iTrue);

     setCommandHandler_Widget(toolBar, handleToolBarCommands_);

     setFlags_Widget(toolBar, moveToParentBottomEdge_WidgetFlag |

                                  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) {

 /* On a phone, the right sidebar is used exclusively for Identities. */

 const iBool isPhone = deviceType_App() == phone_AppDeviceType;

 if (!isPhone || d->side == left_SidebarSide) {

- iWidget *buttons = new_Widget();

+ iWidget *buttons = new_Widget(); 

     setId_Widget(buttons, "buttons");

+ setDrawBufferEnabled_Widget(buttons, iTrue);

     for (int i = 0; i < max_SidebarMode; i++) {

         if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) {

             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. */

#include "metrics.h"

#include "embedded.h"

#include "window.h"

+#include "paint.h"

#include "app.h"



#define STB_TRUETYPE_IMPLEMENTATION

@@ -1712,6 +1713,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {

                 }

                 SDL_Rect src;

                 memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect));

+ dst.x += origin_Paint.x;

+ dst.y += origin_Paint.y;

                 if (args->mode & fillBackground_RunMode) {

                     /* Alpha blending looks much better if the RGB components don't change in

                        the partially transparent pixels. */

@@ -2182,6 +2185,8 @@ void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {

 }

 if (d->texture) {

     SDL_Texture *oldTarget = SDL_GetRenderTarget(render);

+ const iInt2 oldOrigin = origin_Paint;

+ origin_Paint = zero_I2();

     SDL_SetRenderTarget(render, d->texture);

     SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);

     SDL_SetRenderDrawColor(render, 255, 255, 255, 0);

@@ -2190,6 +2195,7 @@ void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {

     draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId);

     SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND);

     SDL_SetRenderTarget(render, oldTarget);

+ origin_Paint = oldOrigin;

     SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);

 }

}

@@ -2203,6 +2209,7 @@ iTextBuf *newRange_TextBuf(int font, int color, iRangecc text) {

}



void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) {

+ addv_I2(&pos, origin_Paint);

 const iColor clr = get_Color(color);

 SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b);

 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) {

             src.y += over;

             src.h -= over;

         }

+ dst.x += origin_Paint.x;

+ dst.y += origin_Paint.y;

         if (args->mode & fillBackground_RunMode) {

             /* Alpha blending looks much better if the RGB components don't change in

                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) {

// pixels.y, y_F3(amount), y_F3(touch->accum),

// touch->edge);

         if (pixels.x || pixels.y) {

- setFocus_Widget(NULL);

+ //setFocus_Widget(NULL);

             dispatchMotion_Touch_(touch->pos[0], 0);

             setCurrent_Root(touch->affinity->root);

             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)

         get_Color(palette[palCur]), get_Color(palette[palNext]), palPos - (int) palPos);

     SDL_SetRenderDrawColor(renderer_Window(get_Window()), back.r, back.g, back.b, p.alpha);

     SDL_RenderFillRect(renderer_Window(get_Window()),

- &(SDL_Rect){ pos.x, pos.y, spr->size.x, spr->size.y });

+ &(SDL_Rect){ pos.x + origin_Paint.x, pos.y + origin_Paint.y,

+ spr->size.x, spr->size.y });

     if (fg >= 0) {

         setOpacity_Text(opacity * 2);

         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) {

 return processEvent_Widget(w, ev);

}



-static void draw_UploadWidget_(const iUploadWidget *d) {

- draw_Widget(constAs_Widget(d));

-}

+//static void draw_UploadWidget_(const iUploadWidget *d) {

+// draw_Widget(constAs_Widget(d));

+//}



iBeginDefineSubclass(UploadWidget, Widget)

 .processEvent = (iAny *) processEvent_UploadWidget_,

- .draw = (iAny *) draw_UploadWidget_,

+ .draw = draw_Widget,

iEndDefineSubclass(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) {



iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {

 iWidget *menu = new_Widget();

+ setDrawBufferEnabled_Widget(menu, iTrue);

 setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId);

 if (deviceType_App() != desktop_AppDeviceType) {

     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. */

include "../ios.h"

#endif



+struct Impl_WidgetDrawBuffer {

+ SDL_Texture *texture;

+ iInt2 size;

+ iBool isValid;

+ SDL_Texture *oldTarget;

+ iInt2 oldOrigin;

+};

+

+static void init_WidgetDrawBuffer(iWidgetDrawBuffer *d) {

+ d->texture = NULL;

+ d->size = zero_I2();

+ d->isValid = iFalse;

+ d->oldTarget = NULL;

+}

+

+static void deinit_WidgetDrawBuffer(iWidgetDrawBuffer *d) {

+ SDL_DestroyTexture(d->texture);

+}

+

+iDefineTypeConstruction(WidgetDrawBuffer)

+ 

+static void realloc_WidgetDrawBuffer(iWidgetDrawBuffer *d, SDL_Renderer *render, iInt2 size) {

+ if (!isEqual_I2(d->size, size)) {

+ d->size = size;

+ if (d->texture) {

+ SDL_DestroyTexture(d->texture);

+ }

+ d->texture = SDL_CreateTexture(render,

+ SDL_PIXELFORMAT_RGBA8888,

+ SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,

+ size.x,

+ size.y);

+ SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);

+ d->isValid = iFalse;

+ }

+}

+

+static void release_WidgetDrawBuffer(iWidgetDrawBuffer *d) {

+ if (d->texture) {

+ SDL_DestroyTexture(d->texture);

+ d->texture = NULL;

+ }

+ d->size = zero_I2();

+ d->isValid = iFalse;

+}

+

+static iRect boundsForDraw_Widget_(const iWidget *d) {

+ iRect bounds = bounds_Widget(d);

+ if (d->flags & drawBackgroundToBottom_WidgetFlag) {

+ bounds.size.y = iMaxi(bounds.size.y, size_Root(d->root).y - top_Rect(bounds));

+ }

+ return bounds;

+}

+

+static iBool checkDrawBuffer_Widget_(const iWidget *d) {

+ return d->drawBuf && d->drawBuf->isValid &&

+ isEqual_I2(d->drawBuf->size, boundsForDraw_Widget_(d).size);

+}

+

+/----------------------------------------------------------------------------------------------/

+

static void printInfo_Widget_(const iWidget *);



void releaseChildren_Widget(iWidget *d) {

@@ -66,6 +127,7 @@ void init_Widget(iWidget *d) {

 d->children       = NULL;

 d->parent         = NULL;

 d->commandHandler = NULL;

+ d->drawBuf = NULL;

 iZap(d->padding);

}



@@ -82,6 +144,7 @@ static void visualOffsetAnimation_Widget_(void *ptr) {



void deinit_Widget(iWidget *d) {

 releaseChildren_Widget(d);

+ delete_WidgetDrawBuffer(d->drawBuf);

#if 0 && !defined (NDEBUG)

 printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id),

        d->flags & keepOnTop_WidgetFlag ? 1 : 0);

@@ -1036,7 +1099,8 @@ iBool scrollOverflow_Widget(iWidget *d, int delta) {

 const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos);

 if (!isEqual_I2(newPos, d->rect.pos)) {

     d->rect.pos = newPos;

- refresh_Widget(d);

+// refresh_Widget(d);

+ postRefresh_App();

 }

 return height_Rect(bounds) > height_Rect(winRect);

}

@@ -1077,6 +1141,9 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {

         }

         if (ev->user.code == command_UserEventCode) {

             const char *cmd = command_UserEvent(ev);

+ if (d->drawBuf && equal_Command(cmd, "theme.changed")) {

+ d->drawBuf->isValid = iFalse;

+ }

             if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) &&

                 isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag &&

                 equal_Command(cmd, "edgeswipe.moved")) {

@@ -1147,14 +1214,13 @@ int backgroundFadeColor_Widget(void) {

 }

}



-void drawBackground_Widget(const iWidget *d) {

- if (d->flags & noBackground_WidgetFlag) {

- return;

- }

- if (d->flags & hidden_WidgetFlag && ~d->flags & visualOffset_WidgetFlag) {

- return;

- }

- /* Popup menus have a shadowed border. */

+iLocalDef iBool isDrawn_Widget_(const iWidget *d) {

+ return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag;

+}

+

+static void drawLayerEffects_Widget_(const iWidget *d) {

+ /* Layered effects are not buffered, so they are drawn here separately. */

+ iAssert(isDrawn_Widget_(d));

 iBool shadowBorder   = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0;

 iBool fadeBackground = (d->bgColor >= 0 || d->frameColor >= 0) && d->flags & mouseModal_WidgetFlag;

 if (deviceType_App() == phone_AppDeviceType) {

@@ -1163,13 +1229,12 @@ void drawBackground_Widget(const iWidget *d) {

         shadowBorder = iFalse;

     }

 }

+ const iBool isFaded = fadeBackground && ~d->flags & noFadeBackground_WidgetFlag;

 if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) {

     iPaint p;

     init_Paint(&p);

     drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30);

 }

- const iBool isFaded = fadeBackground &&

- ~d->flags & noFadeBackground_WidgetFlag;

 if (isFaded) {

     iPaint p;

     init_Paint(&p);

@@ -1183,10 +1248,20 @@ void drawBackground_Widget(const iWidget *d) {

     fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget());

     SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);

 }

+}

+

+void drawBackground_Widget(const iWidget *d) {

+ if (d->flags & noBackground_WidgetFlag) {

+ return;

+ }

+ if (!isDrawn_Widget_(d)) {

+ return;

+ }

+ /* Popup menus have a shadowed border. */

 if (d->bgColor >= 0 || d->frameColor >= 0) {

     iRect rect = bounds_Widget(d);

     if (d->flags & drawBackgroundToBottom_WidgetFlag) {

- rect.size.y = size_Root(d->root).y - top_Rect(rect);

+ rect.size.y = iMax(rect.size.y, size_Root(d->root).y - top_Rect(rect));

     }

     iPaint p;

     init_Paint(&p);

@@ -1242,8 +1317,54 @@ void drawBackground_Widget(const iWidget *d) {

 }

}



-iLocalDef iBool isDrawn_Widget_(const iWidget *d) {

- return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag;

+int drawCount_;

+

+static iBool isRoot_Widget_(const iWidget *d) {

+ return d == d->root->widget;

+}

+

+iLocalDef iBool isFullyContainedByOther_Rect(const iRect d, const iRect other) {

+ if (isEmpty_Rect(other)) {

+ /* Nothing is contained by empty. */

+ return iFalse;

+ }

+ if (isEmpty_Rect(d)) {

+ /* Empty is fully contained by anything. */

+ return iTrue;

+ }

+ return equal_Rect(intersect_Rect(d, other), d);

+}

+

+static void addToPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs, iRect *fullyMasked) {

+ if (isDrawn_Widget_(d)) {

+ iRect bounds = bounds_Widget(d);

+ if (d->flags & drawBackgroundToBottom_WidgetFlag) {

+ bounds.size.y = size_Root(d->root).y - top_Rect(bounds);

+ }

+ if (isFullyContainedByOther_Rect(bounds, *fullyMasked)) {

+ return; /* can't be seen */

+ } 

+ pushBack_PtrArray(pvs, d);

+ if (d->bgColor >= 0 && ~d->flags & noBackground_WidgetFlag &&

+ isFullyContainedByOther_Rect(*fullyMasked, bounds)) {

+ *fullyMasked = bounds;

+ }

+ } 

+}

+

+static void findPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs) {

+ iRect fullyMasked = zero_Rect();

+ if (isRoot_Widget_(d)) {

+ iReverseConstForEach(PtrArray, i, onTop_Root(d->root)) {

+ addToPotentiallyVisible_Widget_(i.ptr, pvs, &fullyMasked);

+ }

+ }

+ iReverseConstForEach(ObjectList, i, d->children) {

+ const iWidget *child = i.object;

+ if (~child->flags & keepOnTop_WidgetFlag) {

+ addToPotentiallyVisible_Widget_(child, pvs, &fullyMasked);

+ }

+ }

}



void drawChildren_Widget(const iWidget *d) {

@@ -1253,21 +1374,85 @@ void drawChildren_Widget(const iWidget *d) {

 iConstForEach(ObjectList, i, d->children) {

     const iWidget *child = constAs_Widget(i.object);

     if (~child->flags & keepOnTop_WidgetFlag && isDrawn_Widget_(child)) {

+ drawCount_++;

         class_Widget(child)->draw(child);

     }

 }

+}

+

+void drawRoot_Widget(const iWidget *d) {

+ iAssert(d == d->root->widget);

 /* Root draws the on-top widgets on top of everything else. */

- if (d == d->root->widget) {

- iConstForEach(PtrArray, i, onTop_Root(d->root)) {

- const iWidget *top = *i.value;

- class_Widget(top)->draw(top);

- }

- }

+ iPtrArray pvs;

+ init_PtrArray(&pvs);

+ findPotentiallyVisible_Widget_(d, &pvs);

+ iReverseConstForEach(PtrArray, i, &pvs) {

+ drawCount_++;

+ class_Widget(i.ptr)->draw(i.ptr);

+ }

+ deinit_PtrArray(&pvs);

+}

+

+void setDrawBufferEnabled_Widget(iWidget *d, iBool enable) {

+ if (enable && !d->drawBuf) {

+ d->drawBuf = new_WidgetDrawBuffer(); 

+ }

+ else if (!enable && d->drawBuf) {

+ delete_WidgetDrawBuffer(d->drawBuf);

+ d->drawBuf = NULL;

+ }

+}

+

+static void beginBufferDraw_Widget_(const iWidget *d) {

+ if (d->drawBuf) {

+// printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid);

+ const iRect bounds = bounds_Widget(d);

+ SDL_Renderer *render = renderer_Window(get_Window());

+ d->drawBuf->oldTarget = SDL_GetRenderTarget(render);

+ d->drawBuf->oldOrigin = origin_Paint;

+ realloc_WidgetDrawBuffer(d->drawBuf, render, boundsForDraw_Widget_(d).size);

+ SDL_SetRenderTarget(render, d->drawBuf->texture);

+ //SDL_SetRenderDrawColor(render, 255, 0, 0, 128);

+ SDL_SetRenderDrawColor(render, 0, 0, 0, 0);

+ SDL_RenderClear(render);

+ origin_Paint = neg_I2(bounds.pos); /* with current visual offset */

+// printf("beginBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y);

+// fflush(stdout);

+ } 

+}

+

+static void endBufferDraw_Widget_(const iWidget *d) {

+ if (d->drawBuf) {

+ d->drawBuf->isValid = iTrue;

+ SDL_SetRenderTarget(renderer_Window(get_Window()), d->drawBuf->oldTarget);

+ origin_Paint = d->drawBuf->oldOrigin;

+// printf("endBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y);

+// fflush(stdout);

+ } 

}



void draw_Widget(const iWidget *d) {

- drawBackground_Widget(d);

- drawChildren_Widget(d);

+ if (!isDrawn_Widget_(d)) {

+ if (d->drawBuf) {

+// printf("[%p] drawBuffer released\n", d);

+ release_WidgetDrawBuffer(d->drawBuf);

+ }

+ return;

+ }

+ drawLayerEffects_Widget_(d);

+ if (!d->drawBuf || !checkDrawBuffer_Widget_(d)) {

+ beginBufferDraw_Widget_(d);

+ drawBackground_Widget(d);

+ drawChildren_Widget(d);

+ endBufferDraw_Widget_(d);

+ }

+ if (d->drawBuf) {

+ iAssert(d->drawBuf->isValid);

+ const iRect bounds = bounds_Widget(d);

+ SDL_RenderCopy(renderer_Window(get_Window()), d->drawBuf->texture, NULL,

+ &(SDL_Rect){ bounds.pos.x, bounds.pos.y,

+ d->drawBuf->size.x, d->drawBuf->size.y });

+ }

}



iAny *addChild_Widget(iWidget *d, iAnyObject *child) {

@@ -1659,12 +1844,20 @@ void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) {

 deinit_String(&str);

}



-void refresh_Widget(const iAnyObject *d) {

+void refresh_Widget(const iAnyObject *d) { 

 /* TODO: Could be widget specific, if parts of the tree are cached. */

 /* TODO: The visbuffer in DocumentWidget and ListWidget could be moved to be a general

    purpose feature of Widget. */

 iAssert(isInstance_Object(d, &Class_Widget));

- iUnused(d);

+ /* Mark draw buffers invalid. */

+ for (const iWidget *w = d; w; w = w->parent) {

+ if (w->drawBuf) {

+// if (w->drawBuf->isValid) {

+// printf("[%p] drawbuffer invalidated by %p\n", w, d); fflush(stdout);

+// }

+ w->drawBuf->isValid = iFalse;

+ }

+ }

 postRefresh_App();

}



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 {

 backward_WidgetFocusDir,

};



+iDeclareType(WidgetDrawBuffer)

+

struct Impl_Widget {

 iObject      object;

 iString      id;

@@ -148,6 +150,7 @@ struct Impl_Widget {

 iWidget *    parent;

 iBool      (*commandHandler)(iWidget *, const char *);

 iRoot *      root;

+ iWidgetDrawBuffer *drawBuf;

};



iDeclareObjectConstruction(Widget)

@@ -203,6 +206,12 @@ size_t childCount_Widget (const iWidget *);

void draw_Widget (const iWidget *);

void drawBackground_Widget (const iWidget *);

void drawChildren_Widget (const iWidget *);

+void drawRoot_Widget (const iWidget ); / root only */

+void setDrawBufferEnabled_Widget (iWidget *, iBool enable);

+

+iLocalDef iBool isDrawBufferEnabled_Widget(const iWidget *d) {

+ return d && d->drawBuf;

+}



iLocalDef int width_Widget(const iAnyObject *d) {

 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) {

 d->frameTime = SDL_GetTicks();

 if (isExposed_Window(d)) {

     d->isInvalidated = iFalse;

+ extern int drawCount_;

     iForIndices(i, d->roots) {

         iRoot *root = d->roots[i];

         if (root) {

             setCurrent_Root(root);

             unsetClip_Paint(&p); /* update clip to current root */

- draw_Widget(root->widget);

+ drawRoot_Widget(root->widget);

#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)

             /* App icon. */

             const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon");

@@ -1105,6 +1106,10 @@ void draw_Window(iWindow *d) {

         }

     }

     setCurrent_Root(NULL);

+#if !defined (NDEBUG)

+ draw_Text(defaultBold_FontId, zero_I2(), red_ColorId, "%d", drawCount_);

+ drawCount_ = 0;

+#endif

 }

#if 0

 /* Text cache debugging. */ {

Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.7/pcdiff/33620846cca5678fbd662ea1a48fad302727dae7
Status Code
Success (20)
Meta
text/plain
Capsule Response Time
78.077818 milliseconds
Gemini-to-HTML Time
8.160818 milliseconds

This content has been proxied by September (ba2dc).