[1mdiff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c[m
[1mindex 6e9ef6c2..802a2d6c 100644[m
[1m--- a/src/ui/inputwidget.c[m
[1m+++ b/src/ui/inputwidget.c[m
[36m@@ -1565,6 +1565,11 @@[m [mstatic iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {[m
}[m
switch (processEvent_Click(&d->click, ev)) {[m
case none_ClickResult:[m
[32m+[m[32m if (ev->type == SDL_MOUSEBUTTONUP &&[m
[32m+[m[32m deviceType_App() != desktop_AppDeviceType && isFocused_Widget(d)) {[m
[32m+[m[32m setFocus_Widget(NULL);[m
[32m+[m[32m return iTrue;[m
[32m+[m[32m }[m
break;[m
case started_ClickResult: {[m
setFocus_Widget(w);[m
[1mdiff --git a/src/ui/mobile.c b/src/ui/mobile.c[m
[1mindex 6ea672e6..9e2dc4f7 100644[m
[1m--- a/src/ui/mobile.c[m
[1m+++ b/src/ui/mobile.c[m
[36m@@ -357,6 +357,7 @@[m [mstatic iWidget *addChildPanel_(iWidget *parent, iLabelWidget *panelButton,[m
setId_Widget(panel, "panel");[m
setUserData_Object(panelButton, panel);[m
setBackgroundColor_Widget(panel, uiBackground_ColorId);[m
[32m+[m[32m setDrawBufferEnabled_Widget(panel, iTrue);[m
setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad");[m
if (titleText) {[m
iLabelWidget *title =[m
[36m@@ -601,6 +602,7 @@[m [mvoid initPanels_Mobile(iWidget *panels, iWidget *parentWidget,[m
/* The panel roots. */[m
iWidget *topPanel = new_Widget(); {[m
setId_Widget(topPanel, "panel.top");[m
[32m+[m[32m setDrawBufferEnabled_Widget(topPanel, iTrue);[m
setCommandHandler_Widget(topPanel, topPanelHandler_);[m
setFlags_Widget(topPanel,[m
arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag |[m
[36m@@ -730,7 +732,7 @@[m [mvoid initPanels_Mobile(iWidget *panels, iWidget *parentWidget,[m
updatePanelSheetMetrics_(panels);[m
arrange_Widget(panels);[m
postCommand_App("widget.overflow"); /* with the correct dimensions */ [m
[31m- printTree_Widget(panels);[m
[32m+[m[32m// printTree_Widget(panels);[m
}[m
[m
#if 0[m
[1mdiff --git a/src/ui/paint.c b/src/ui/paint.c[m
[1mindex 79adb7d1..af62f908 100644[m
[1m--- a/src/ui/paint.c[m
[1m+++ b/src/ui/paint.c[m
[36m@@ -24,6 +24,8 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m
[m
#include <SDL_version.h>[m
[m
[32m+[m[32miInt2 origin_Paint;[m
[32m+[m
iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) {[m
iAssert(d->dst);[m
return d->dst->render;[m
[36m@@ -62,10 +64,11 @@[m [mvoid endTarget_Paint(iPaint *d) {[m
}[m
[m
void setClip_Paint(iPaint *d, iRect rect) {[m
[31m- rect = intersect_Rect(rect, rect_Root(get_Root()));[m
[32m+[m[32m //rect = intersect_Rect(rect, rect_Root(get_Root()));[m
if (isEmpty_Rect(rect)) {[m
rect = init_Rect(0, 0, 1, 1);[m
}[m
[32m+[m[32m addv_I2(&rect.pos, origin_Paint);[m
SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect);[m
}[m
[m
[36m@@ -85,6 +88,7 @@[m [mvoid unsetClip_Paint(iPaint *d) {[m
}[m
[m
void drawRect_Paint(const iPaint *d, iRect rect, int color) {[m
[32m+[m[32m addv_I2(&rect.pos, origin_Paint);[m
iInt2 br = bottomRight_Rect(rect);[m
/* Keep the right/bottom edge visible in the window. */[m
if (br.x == d->dst->size.x) br.x--;[m
[36m@@ -115,11 +119,14 @@[m [mvoid drawRectThickness_Paint(const iPaint *d, iRect rect, int thickness, int col[m
}[m
[m
void fillRect_Paint(const iPaint *d, iRect rect, int color) {[m
[32m+[m[32m addv_I2(&rect.pos, origin_Paint);[m
setColor_Paint_(d, color);[m
[32m+[m[32m// printf("fillRect_Paint: %d,%d %dx%d\n", rect.pos.x, rect.pos.y, rect.size.x, rect.size.y);[m
SDL_RenderFillRect(renderer_Paint_(d), (SDL_Rect *) &rect);[m
}[m
[m
void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color, int alpha) {[m
[32m+[m[32m addv_I2(&inner.pos, origin_Paint);[m
SDL_Renderer *render = renderer_Paint_(d);[m
SDL_Texture *shadow = get_Window()->borderShadow;[m
const iInt2 size = size_SDLTexture(shadow);[m
[36m@@ -146,9 +153,14 @@[m [mvoid drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color[m
&(SDL_Rect){ outer.pos.x, inner.pos.y, thickness, inner.size.y });[m
}[m
[m
[31m-void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t count, int color) {[m
[32m+[m[32mvoid drawLines_Paint(const iPaint *d, const iInt2 *points, size_t n, int color) {[m
setColor_Paint_(d, color);[m
[31m- SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) points, count);[m
[32m+[m[32m iInt2 *offsetPoints = malloc(sizeof(iInt2) * n);[m
[32m+[m[32m for (size_t i = 0; i < n; i++) {[m
[32m+[m[32m offsetPoints[i] = add_I2(points[i], origin_Paint);[m
[32m+[m[32m }[m
[32m+[m[32m SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) offsetPoints, n);[m
[32m+[m[32m free(offsetPoints);[m
}[m
[m
iInt2 size_SDLTexture(SDL_Texture *d) {[m
[1mdiff --git a/src/ui/paint.h b/src/ui/paint.h[m
[1mindex 90cc2aef..e6701635 100644[m
[1m--- a/src/ui/paint.h[m
[1m+++ b/src/ui/paint.h[m
[36m@@ -36,6 +36,8 @@[m [mstruct Impl_Paint {[m
uint8_t alpha;[m
};[m
[m
[32m+[m[32mextern iInt2 origin_Paint; /* add this to all drawn positions so buffered graphics are correctly offset */[m
[32m+[m
void init_Paint (iPaint *);[m
[m
void beginTarget_Paint (iPaint *, SDL_Texture *target);[m
[1mdiff --git a/src/ui/root.c b/src/ui/root.c[m
[1mindex a792e93d..7b2b5b15 100644[m
[1m--- a/src/ui/root.c[m
[1m+++ b/src/ui/root.c[m
[36m@@ -685,6 +685,34 @@[m [mstatic iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {[m
}[m
return iTrue;[m
}[m
[32m+[m[32m else if (deviceType_App() != desktop_AppDeviceType &&[m
[32m+[m[32m (equal_Command(cmd, "focus.gained") || equal_Command(cmd, "focus.lost"))) {[m
[32m+[m[32m iInputWidget *url = findChild_Widget(navBar, "url");[m
[32m+[m[32m if (pointer_Command(cmd) == url) {[m
[32m+[m[32m const iBool isFocused = equal_Command(cmd, "focus.gained");[m
[32m+[m[32m setFlags_Widget(findChild_Widget(navBar, "navbar.clear"), hidden_WidgetFlag, !isFocused);[m
[32m+[m[32m showCollapsed_Widget(findChild_Widget(navBar, "navbar.cancel"), isFocused);[m
[32m+[m[32m showCollapsed_Widget(findChild_Widget(navBar, "pagemenubutton"), !isFocused);[m
[32m+[m[32m showCollapsed_Widget(findChild_Widget(navBar, "reload"), !isFocused);[m
[32m+[m[32m }[m
[32m+[m[32m return iFalse;[m
[32m+[m[32m }[m
[32m+[m[32m else if (equal_Command(cmd, "navbar.clear")) {[m
[32m+[m[32m iInputWidget *url = findChild_Widget(navBar, "url");[m
[32m+[m[32m selectAll_InputWidget(url);[m
[32m+[m[32m /* Emulate a Backspace keypress. */[m
[32m+[m[32m class_InputWidget(url)->processEvent([m
[32m+[m[32m as_Widget(url),[m
[32m+[m[32m (SDL_Event *) &(SDL_KeyboardEvent){ .type = SDL_KEYDOWN,[m
[32m+[m[32m .timestamp = SDL_GetTicks(),[m
[32m+[m[32m .state = SDL_PRESSED,[m
[32m+[m[32m .keysym = { .sym = SDLK_BACKSPACE } });[m
[32m+[m[32m return iTrue;[m
[32m+[m[32m }[m
[32m+[m[32m else if (equal_Command(cmd, "navbar.cancel")) {[m
[32m+[m[32m setFocus_Widget(NULL);[m
[32m+[m[32m return iTrue;[m
[32m+[m[32m }[m
else if (equal_Command(cmd, "input.edited")) {[m
iAnyObject * url = findChild_Widget(navBar, "url");[m
const iString *text = text_InputWidget(url);[m
[36m@@ -941,7 +969,7 @@[m [mvoid updateMetrics_Root(iRoot *d) {[m
setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y));[m
}[m
iWidget *navBar = findChild_Widget(d->widget, "navbar");[m
[31m- iWidget *lock = findChild_Widget(navBar, "navbar.lock");[m
[32m+[m[32m// iWidget *lock = findChild_Widget(navBar, "navbar.lock");[m
iWidget *url = findChild_Widget(d->widget, "url");[m
iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed");[m
iWidget *embedPad = findChild_Widget(navBar, "url.embedpad");[m
[36m@@ -1044,6 +1072,7 @@[m [mvoid createUserInterface_Root(iRoot *d) {[m
/* Navigation bar. */ {[m
navBar = new_Widget();[m
setId_Widget(navBar, "navbar");[m
[32m+[m[32m setDrawBufferEnabled_Widget(navBar, iTrue);[m
setFlags_Widget(navBar,[m
hittable_WidgetFlag | /* context menu */[m
arrangeHeight_WidgetFlag |[m
[36m@@ -1095,6 +1124,16 @@[m [mvoid createUserInterface_Root(iRoot *d) {[m
setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize);[m
updateTextCStr_LabelWidget(lock, "\U0001f512");[m
}[m
[32m+[m[32m /* Button for clearing the URL bar contents. */ {[m
[32m+[m[32m iLabelWidget *clear = addChildFlags_Widget([m
[32m+[m[32m as_Widget(url),[m
[32m+[m[32m iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")),[m
[32m+[m[32m hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag);[m
[32m+[m[32m setId_Widget(as_Widget(clear), "navbar.clear");[m
[32m+[m[32m setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize);[m
[32m+[m[32m setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse);[m
[32m+[m[32m setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId);[m
[32m+[m[32m }[m
iWidget *rightEmbed = new_Widget();[m
setId_Widget(rightEmbed, "url.rightembed");[m
addChildFlags_Widget(as_Widget(url),[m
[36m@@ -1151,6 +1190,13 @@[m [mvoid createUserInterface_Root(iRoot *d) {[m
setFlags_Widget(urlButtons, embedFlags | arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);[m
/* Mobile page menu. */[m
if (deviceType_App() != desktop_AppDeviceType) {[m
[32m+[m[32m iLabelWidget *navCancel = new_LabelWidget("${cancel}", "navbar.cancel");[m
[32m+[m[32m addChildFlags_Widget(urlButtons, iClob(navCancel),[m
[32m+[m[32m (embedFlags | tight_WidgetFlag | hidden_WidgetFlag |[m
[32m+[m[32m collapse_WidgetFlag) & ~noBackground_WidgetFlag);[m
[32m+[m[32m as_Widget(navCancel)->sizeRef = as_Widget(url);[m
[32m+[m[32m// setFont_LabelWidget(navCancel, defaultBold_FontId);[m
[32m+[m[32m setId_Widget(as_Widget(navCancel), "navbar.cancel");[m
iLabelWidget *pageMenuButton;[m
/* In a mobile layout, the reload button is replaced with the Page/Ellipsis menu. */[m
pageMenuButton = makeMenuButton_LabelWidget(pageMenuCStr_,[m
[36m@@ -1172,13 +1218,14 @@[m [mvoid createUserInterface_Root(iRoot *d) {[m
setId_Widget(as_Widget(pageMenuButton), "pagemenubutton");[m
setFont_LabelWidget(pageMenuButton, uiContentBold_FontId);[m
setAlignVisually_LabelWidget(pageMenuButton, iTrue);[m
[31m- addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags | tight_WidgetFlag);[m
[32m+[m[32m addChildFlags_Widget(urlButtons, iClob(pageMenuButton),[m
[32m+[m[32m embedFlags | tight_WidgetFlag | collapse_WidgetFlag);[m
updateSize_LabelWidget(pageMenuButton);[m
}[m
/* Reload button. */ {[m
iLabelWidget *reload = newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload");[m
setId_Widget(as_Widget(reload), "reload");[m
[31m- addChildFlags_Widget(urlButtons, iClob(reload), embedFlags);[m
[32m+[m[32m addChildFlags_Widget(urlButtons, iClob(reload), embedFlags | collapse_WidgetFlag);[m
updateSize_LabelWidget(reload);[m
}[m
addChildFlags_Widget(as_Widget(url), iClob(urlButtons), moveToParentRightEdge_WidgetFlag);[m
[36m@@ -1287,6 +1334,7 @@[m [mvoid createUserInterface_Root(iRoot *d) {[m
iWidget *toolBar = new_Widget();[m
addChild_Widget(root, iClob(toolBar));[m
setId_Widget(toolBar, "toolbar");[m
[32m+[m[32m setDrawBufferEnabled_Widget(toolBar, iTrue);[m
setCommandHandler_Widget(toolBar, handleToolBarCommands_);[m
setFlags_Widget(toolBar, moveToParentBottomEdge_WidgetFlag |[m
parentCannotResizeHeight_WidgetFlag |[m
[1mdiff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c[m
[1mindex b816b572..eb129424 100644[m
[1m--- a/src/ui/sidebarwidget.c[m
[1m+++ b/src/ui/sidebarwidget.c[m
[36m@@ -663,8 +663,9 @@[m [mvoid init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {[m
/* On a phone, the right sidebar is used exclusively for Identities. */[m
const iBool isPhone = deviceType_App() == phone_AppDeviceType;[m
if (!isPhone || d->side == left_SidebarSide) {[m
[31m- iWidget *buttons = new_Widget();[m
[32m+[m[32m iWidget *buttons = new_Widget();[m[41m [m
setId_Widget(buttons, "buttons");[m
[32m+[m[32m setDrawBufferEnabled_Widget(buttons, iTrue);[m
for (int i = 0; i < max_SidebarMode; i++) {[m
if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) {[m
continue;[m
[1mdiff --git a/src/ui/text.c b/src/ui/text.c[m
[1mindex 231281eb..f7fff4bc 100644[m
[1m--- a/src/ui/text.c[m
[1m+++ b/src/ui/text.c[m
[36m@@ -25,6 +25,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m
#include "metrics.h"[m
#include "embedded.h"[m
#include "window.h"[m
[32m+[m[32m#include "paint.h"[m
#include "app.h"[m
[m
#define STB_TRUETYPE_IMPLEMENTATION[m
[36m@@ -1712,6 +1713,8 @@[m [mstatic iRect run_Font_(iFont *d, const iRunArgs *args) {[m
}[m
SDL_Rect src;[m
memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect));[m
[32m+[m[32m dst.x += origin_Paint.x;[m
[32m+[m[32m dst.y += origin_Paint.y;[m
if (args->mode & fillBackground_RunMode) {[m
/* Alpha blending looks much better if the RGB components don't change in[m
the partially transparent pixels. */[m
[36m@@ -2182,6 +2185,8 @@[m [mvoid init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {[m
}[m
if (d->texture) {[m
SDL_Texture *oldTarget = SDL_GetRenderTarget(render);[m
[32m+[m[32m const iInt2 oldOrigin = origin_Paint;[m
[32m+[m[32m origin_Paint = zero_I2();[m
SDL_SetRenderTarget(render, d->texture);[m
SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);[m
SDL_SetRenderDrawColor(render, 255, 255, 255, 0);[m
[36m@@ -2190,6 +2195,7 @@[m [mvoid init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {[m
draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId);[m
SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND);[m
SDL_SetRenderTarget(render, oldTarget);[m
[32m+[m[32m origin_Paint = oldOrigin;[m
SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);[m
}[m
}[m
[36m@@ -2203,6 +2209,7 @@[m [miTextBuf *newRange_TextBuf(int font, int color, iRangecc text) {[m
}[m
[m
void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) {[m
[32m+[m[32m addv_I2(&pos, origin_Paint);[m
const iColor clr = get_Color(color);[m
SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b);[m
SDL_RenderCopy(text_.render,[m
[1mdiff --git a/src/ui/text_simple.c b/src/ui/text_simple.c[m
[1mindex e88b09a8..bf33b4be 100644[m
[1m--- a/src/ui/text_simple.c[m
[1m+++ b/src/ui/text_simple.c[m
[36m@@ -306,6 +306,8 @@[m [mstatic iRect runSimple_Font_(iFont *d, const iRunArgs *args) {[m
src.y += over;[m
src.h -= over;[m
}[m
[32m+[m[32m dst.x += origin_Paint.x;[m
[32m+[m[32m dst.y += origin_Paint.y;[m
if (args->mode & fillBackground_RunMode) {[m
/* Alpha blending looks much better if the RGB components don't change in[m
the partially transparent pixels. */[m
[1mdiff --git a/src/ui/touch.c b/src/ui/touch.c[m
[1mindex dac1152e..f0456acb 100644[m
[1m--- a/src/ui/touch.c[m
[1m+++ b/src/ui/touch.c[m
[36m@@ -614,7 +614,7 @@[m [miBool processEvent_Touch(const SDL_Event *ev) {[m
// pixels.y, y_F3(amount), y_F3(touch->accum),[m
// touch->edge);[m
if (pixels.x || pixels.y) {[m
[31m- setFocus_Widget(NULL);[m
[32m+[m[32m //setFocus_Widget(NULL);[m
dispatchMotion_Touch_(touch->pos[0], 0);[m
setCurrent_Root(touch->affinity->root);[m
dispatchEvent_Widget(touch->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){[m
[1mdiff --git a/src/ui/translation.c b/src/ui/translation.c[m
[1mindex cef68dce..b86e6e52 100644[m
[1m--- a/src/ui/translation.c[m
[1m+++ b/src/ui/translation.c[m
[36m@@ -136,7 +136,8 @@[m [mstatic void draw_TranslationProgressWidget_(const iTranslationProgressWidget *d)[m
get_Color(palette[palCur]), get_Color(palette[palNext]), palPos - (int) palPos);[m
SDL_SetRenderDrawColor(renderer_Window(get_Window()), back.r, back.g, back.b, p.alpha);[m
SDL_RenderFillRect(renderer_Window(get_Window()),[m
[31m- &(SDL_Rect){ pos.x, pos.y, spr->size.x, spr->size.y });[m
[32m+[m[32m &(SDL_Rect){ pos.x + origin_Paint.x, pos.y + origin_Paint.y,[m
[32m+[m[32m spr->size.x, spr->size.y });[m
if (fg >= 0) {[m
setOpacity_Text(opacity * 2);[m
drawRange_Text(d->font, addX_I2(pos, spr->xoff), fg, range_String(&spr->text));[m
[1mdiff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c[m
[1mindex fb8aaf0a..78a1196a 100644[m
[1m--- a/src/ui/uploadwidget.c[m
[1m+++ b/src/ui/uploadwidget.c[m
[36m@@ -376,11 +376,11 @@[m [mstatic iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {[m
return processEvent_Widget(w, ev);[m
}[m
[m
[31m-static void draw_UploadWidget_(const iUploadWidget *d) {[m
[31m- draw_Widget(constAs_Widget(d));[m
[31m-}[m
[32m+[m[32m//static void draw_UploadWidget_(const iUploadWidget *d) {[m
[32m+[m[32m// draw_Widget(constAs_Widget(d));[m
[32m+[m[32m//}[m
[m
iBeginDefineSubclass(UploadWidget, Widget)[m
.processEvent = (iAny *) processEvent_UploadWidget_,[m
[31m- .draw = (iAny *) draw_UploadWidget_,[m
[32m+[m[32m .draw = draw_Widget,[m
iEndDefineSubclass(UploadWidget)[m
[1mdiff --git a/src/ui/util.c b/src/ui/util.c[m
[1mindex 6069e800..05d39c01 100644[m
[1m--- a/src/ui/util.c[m
[1m+++ b/src/ui/util.c[m
[36m@@ -685,6 +685,7 @@[m [mstatic iWidget *makeMenuSeparator_(void) {[m
[m
iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {[m
iWidget *menu = new_Widget();[m
[32m+[m[32m setDrawBufferEnabled_Widget(menu, iTrue);[m
setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId);[m
if (deviceType_App() != desktop_AppDeviceType) {[m
setPadding1_Widget(menu, 2 * gap_UI);[m
[1mdiff --git a/src/ui/widget.c b/src/ui/widget.c[m
[1mindex 659a00cc..66cd0e7b 100644[m
[1m--- a/src/ui/widget.c[m
[1m+++ b/src/ui/widget.c[m
[36m@@ -40,6 +40,67 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m
#endif[m
[m
[32m+[m[32mstruct Impl_WidgetDrawBuffer {[m
[32m+[m[32m SDL_Texture *texture;[m
[32m+[m[32m iInt2 size;[m
[32m+[m[32m iBool isValid;[m
[32m+[m[32m SDL_Texture *oldTarget;[m
[32m+[m[32m iInt2 oldOrigin;[m
[32m+[m[32m};[m
[32m+[m
[32m+[m[32mstatic void init_WidgetDrawBuffer(iWidgetDrawBuffer *d) {[m
[32m+[m[32m d->texture = NULL;[m
[32m+[m[32m d->size = zero_I2();[m
[32m+[m[32m d->isValid = iFalse;[m
[32m+[m[32m d->oldTarget = NULL;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void deinit_WidgetDrawBuffer(iWidgetDrawBuffer *d) {[m
[32m+[m[32m SDL_DestroyTexture(d->texture);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32miDefineTypeConstruction(WidgetDrawBuffer)[m
[32m+[m[41m [m
[32m+[m[32mstatic void realloc_WidgetDrawBuffer(iWidgetDrawBuffer *d, SDL_Renderer *render, iInt2 size) {[m
[32m+[m[32m if (!isEqual_I2(d->size, size)) {[m
[32m+[m[32m d->size = size;[m
[32m+[m[32m if (d->texture) {[m
[32m+[m[32m SDL_DestroyTexture(d->texture);[m
[32m+[m[32m }[m
[32m+[m[32m d->texture = SDL_CreateTexture(render,[m
[32m+[m[32m SDL_PIXELFORMAT_RGBA8888,[m
[32m+[m[32m SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,[m
[32m+[m[32m size.x,[m
[32m+[m[32m size.y);[m
[32m+[m[32m SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);[m
[32m+[m[32m d->isValid = iFalse;[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void release_WidgetDrawBuffer(iWidgetDrawBuffer *d) {[m
[32m+[m[32m if (d->texture) {[m
[32m+[m[32m SDL_DestroyTexture(d->texture);[m
[32m+[m[32m d->texture = NULL;[m
[32m+[m[32m }[m
[32m+[m[32m d->size = zero_I2();[m
[32m+[m[32m d->isValid = iFalse;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic iRect boundsForDraw_Widget_(const iWidget *d) {[m
[32m+[m[32m iRect bounds = bounds_Widget(d);[m
[32m+[m[32m if (d->flags & drawBackgroundToBottom_WidgetFlag) {[m
[32m+[m[32m bounds.size.y = iMaxi(bounds.size.y, size_Root(d->root).y - top_Rect(bounds));[m
[32m+[m[32m }[m
[32m+[m[32m return bounds;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic iBool checkDrawBuffer_Widget_(const iWidget *d) {[m
[32m+[m[32m return d->drawBuf && d->drawBuf->isValid &&[m
[32m+[m[32m isEqual_I2(d->drawBuf->size, boundsForDraw_Widget_(d).size);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/----------------------------------------------------------------------------------------------/[m
[32m+[m
static void printInfo_Widget_(const iWidget *);[m
[m
void releaseChildren_Widget(iWidget *d) {[m
[36m@@ -66,6 +127,7 @@[m [mvoid init_Widget(iWidget *d) {[m
d->children = NULL;[m
d->parent = NULL;[m
d->commandHandler = NULL;[m
[32m+[m[32m d->drawBuf = NULL;[m
iZap(d->padding);[m
}[m
[m
[36m@@ -82,6 +144,7 @@[m [mstatic void visualOffsetAnimation_Widget_(void *ptr) {[m
[m
void deinit_Widget(iWidget *d) {[m
releaseChildren_Widget(d);[m
[32m+[m[32m delete_WidgetDrawBuffer(d->drawBuf);[m
#if 0 && !defined (NDEBUG)[m
printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id),[m
d->flags & keepOnTop_WidgetFlag ? 1 : 0);[m
[36m@@ -1036,7 +1099,8 @@[m [miBool scrollOverflow_Widget(iWidget *d, int delta) {[m
const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos);[m
if (!isEqual_I2(newPos, d->rect.pos)) {[m
d->rect.pos = newPos;[m
[31m- refresh_Widget(d);[m
[32m+[m[32m// refresh_Widget(d);[m
[32m+[m[32m postRefresh_App();[m
}[m
return height_Rect(bounds) > height_Rect(winRect);[m
}[m
[36m@@ -1077,6 +1141,9 @@[m [miBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {[m
}[m
if (ev->user.code == command_UserEventCode) {[m
const char *cmd = command_UserEvent(ev);[m
[32m+[m[32m if (d->drawBuf && equal_Command(cmd, "theme.changed")) {[m
[32m+[m[32m d->drawBuf->isValid = iFalse;[m
[32m+[m[32m }[m
if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) &&[m
isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag &&[m
equal_Command(cmd, "edgeswipe.moved")) {[m
[36m@@ -1147,14 +1214,13 @@[m [mint backgroundFadeColor_Widget(void) {[m
}[m
}[m
[m
[31m-void drawBackground_Widget(const iWidget *d) {[m
[31m- if (d->flags & noBackground_WidgetFlag) {[m
[31m- return;[m
[31m- }[m
[31m- if (d->flags & hidden_WidgetFlag && ~d->flags & visualOffset_WidgetFlag) {[m
[31m- return;[m
[31m- }[m
[31m- /* Popup menus have a shadowed border. */[m
[32m+[m[32miLocalDef iBool isDrawn_Widget_(const iWidget *d) {[m
[32m+[m[32m return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void drawLayerEffects_Widget_(const iWidget *d) {[m
[32m+[m[32m /* Layered effects are not buffered, so they are drawn here separately. */[m
[32m+[m[32m iAssert(isDrawn_Widget_(d));[m
iBool shadowBorder = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0;[m
iBool fadeBackground = (d->bgColor >= 0 || d->frameColor >= 0) && d->flags & mouseModal_WidgetFlag;[m
if (deviceType_App() == phone_AppDeviceType) {[m
[36m@@ -1163,13 +1229,12 @@[m [mvoid drawBackground_Widget(const iWidget *d) {[m
shadowBorder = iFalse;[m
}[m
}[m
[32m+[m[32m const iBool isFaded = fadeBackground && ~d->flags & noFadeBackground_WidgetFlag;[m
if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) {[m
iPaint p;[m
init_Paint(&p);[m
drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30);[m
}[m
[31m- const iBool isFaded = fadeBackground &&[m
[31m- ~d->flags & noFadeBackground_WidgetFlag;[m
if (isFaded) {[m
iPaint p;[m
init_Paint(&p);[m
[36m@@ -1183,10 +1248,20 @@[m [mvoid drawBackground_Widget(const iWidget *d) {[m
fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget());[m
SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);[m
}[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid drawBackground_Widget(const iWidget *d) {[m
[32m+[m[32m if (d->flags & noBackground_WidgetFlag) {[m
[32m+[m[32m return;[m
[32m+[m[32m }[m
[32m+[m[32m if (!isDrawn_Widget_(d)) {[m
[32m+[m[32m return;[m
[32m+[m[32m }[m
[32m+[m[32m /* Popup menus have a shadowed border. */[m
if (d->bgColor >= 0 || d->frameColor >= 0) {[m
iRect rect = bounds_Widget(d);[m
if (d->flags & drawBackgroundToBottom_WidgetFlag) {[m
[31m- rect.size.y = size_Root(d->root).y - top_Rect(rect);[m
[32m+[m[32m rect.size.y = iMax(rect.size.y, size_Root(d->root).y - top_Rect(rect));[m
}[m
iPaint p;[m
init_Paint(&p);[m
[36m@@ -1242,8 +1317,54 @@[m [mvoid drawBackground_Widget(const iWidget *d) {[m
}[m
}[m
[m
[31m-iLocalDef iBool isDrawn_Widget_(const iWidget *d) {[m
[31m- return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag;[m
[32m+[m[32mint drawCount_;[m
[32m+[m
[32m+[m[32mstatic iBool isRoot_Widget_(const iWidget *d) {[m
[32m+[m[32m return d == d->root->widget;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32miLocalDef iBool isFullyContainedByOther_Rect(const iRect d, const iRect other) {[m
[32m+[m[32m if (isEmpty_Rect(other)) {[m
[32m+[m[32m /* Nothing is contained by empty. */[m
[32m+[m[32m return iFalse;[m
[32m+[m[32m }[m
[32m+[m[32m if (isEmpty_Rect(d)) {[m
[32m+[m[32m /* Empty is fully contained by anything. */[m
[32m+[m[32m return iTrue;[m
[32m+[m[32m }[m
[32m+[m[32m return equal_Rect(intersect_Rect(d, other), d);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void addToPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs, iRect *fullyMasked) {[m
[32m+[m[32m if (isDrawn_Widget_(d)) {[m
[32m+[m[32m iRect bounds = bounds_Widget(d);[m
[32m+[m[32m if (d->flags & drawBackgroundToBottom_WidgetFlag) {[m
[32m+[m[32m bounds.size.y = size_Root(d->root).y - top_Rect(bounds);[m
[32m+[m[32m }[m
[32m+[m[32m if (isFullyContainedByOther_Rect(bounds, *fullyMasked)) {[m
[32m+[m[32m return; /* can't be seen */[m
[32m+[m[32m }[m[41m [m
[32m+[m[32m pushBack_PtrArray(pvs, d);[m
[32m+[m[32m if (d->bgColor >= 0 && ~d->flags & noBackground_WidgetFlag &&[m
[32m+[m[32m isFullyContainedByOther_Rect(*fullyMasked, bounds)) {[m
[32m+[m[32m *fullyMasked = bounds;[m
[32m+[m[32m }[m
[32m+[m[32m }[m[41m [m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void findPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs) {[m
[32m+[m[32m iRect fullyMasked = zero_Rect();[m
[32m+[m[32m if (isRoot_Widget_(d)) {[m
[32m+[m[32m iReverseConstForEach(PtrArray, i, onTop_Root(d->root)) {[m
[32m+[m[32m addToPotentiallyVisible_Widget_(i.ptr, pvs, &fullyMasked);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m iReverseConstForEach(ObjectList, i, d->children) {[m
[32m+[m[32m const iWidget *child = i.object;[m
[32m+[m[32m if (~child->flags & keepOnTop_WidgetFlag) {[m
[32m+[m[32m addToPotentiallyVisible_Widget_(child, pvs, &fullyMasked);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
}[m
[m
void drawChildren_Widget(const iWidget *d) {[m
[36m@@ -1253,21 +1374,85 @@[m [mvoid drawChildren_Widget(const iWidget *d) {[m
iConstForEach(ObjectList, i, d->children) {[m
const iWidget *child = constAs_Widget(i.object);[m
if (~child->flags & keepOnTop_WidgetFlag && isDrawn_Widget_(child)) {[m
[32m+[m[32m drawCount_++;[m
class_Widget(child)->draw(child);[m
}[m
}[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid drawRoot_Widget(const iWidget *d) {[m
[32m+[m[32m iAssert(d == d->root->widget);[m
/* Root draws the on-top widgets on top of everything else. */[m
[31m- if (d == d->root->widget) {[m
[31m- iConstForEach(PtrArray, i, onTop_Root(d->root)) {[m
[31m- const iWidget *top = *i.value;[m
[31m- class_Widget(top)->draw(top);[m
[31m- }[m
[31m- }[m
[32m+[m[32m iPtrArray pvs;[m
[32m+[m[32m init_PtrArray(&pvs);[m
[32m+[m[32m findPotentiallyVisible_Widget_(d, &pvs);[m
[32m+[m[32m iReverseConstForEach(PtrArray, i, &pvs) {[m
[32m+[m[32m drawCount_++;[m
[32m+[m[32m class_Widget(i.ptr)->draw(i.ptr);[m
[32m+[m[32m }[m
[32m+[m[32m deinit_PtrArray(&pvs);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid setDrawBufferEnabled_Widget(iWidget *d, iBool enable) {[m
[32m+[m[32m if (enable && !d->drawBuf) {[m
[32m+[m[32m d->drawBuf = new_WidgetDrawBuffer();[m[41m [m
[32m+[m[32m }[m
[32m+[m[32m else if (!enable && d->drawBuf) {[m
[32m+[m[32m delete_WidgetDrawBuffer(d->drawBuf);[m
[32m+[m[32m d->drawBuf = NULL;[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void beginBufferDraw_Widget_(const iWidget *d) {[m
[32m+[m[32m if (d->drawBuf) {[m
[32m+[m[32m// printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid);[m
[32m+[m[32m const iRect bounds = bounds_Widget(d);[m
[32m+[m[32m SDL_Renderer *render = renderer_Window(get_Window());[m
[32m+[m[32m d->drawBuf->oldTarget = SDL_GetRenderTarget(render);[m
[32m+[m[32m d->drawBuf->oldOrigin = origin_Paint;[m
[32m+[m[32m realloc_WidgetDrawBuffer(d->drawBuf, render, boundsForDraw_Widget_(d).size);[m
[32m+[m[32m SDL_SetRenderTarget(render, d->drawBuf->texture);[m
[32m+[m[32m //SDL_SetRenderDrawColor(render, 255, 0, 0, 128);[m
[32m+[m[32m SDL_SetRenderDrawColor(render, 0, 0, 0, 0);[m
[32m+[m[32m SDL_RenderClear(render);[m
[32m+[m[32m origin_Paint = neg_I2(bounds.pos); /* with current visual offset */[m
[32m+[m[32m// printf("beginBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y);[m
[32m+[m[32m// fflush(stdout);[m
[32m+[m[32m }[m[41m [m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void endBufferDraw_Widget_(const iWidget *d) {[m
[32m+[m[32m if (d->drawBuf) {[m
[32m+[m[32m d->drawBuf->isValid = iTrue;[m
[32m+[m[32m SDL_SetRenderTarget(renderer_Window(get_Window()), d->drawBuf->oldTarget);[m
[32m+[m[32m origin_Paint = d->drawBuf->oldOrigin;[m
[32m+[m[32m// printf("endBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y);[m
[32m+[m[32m// fflush(stdout);[m
[32m+[m[32m }[m[41m [m
}[m
[m
void draw_Widget(const iWidget *d) {[m
[31m- drawBackground_Widget(d);[m
[31m- drawChildren_Widget(d);[m
[32m+[m[32m if (!isDrawn_Widget_(d)) {[m
[32m+[m[32m if (d->drawBuf) {[m
[32m+[m[32m// printf("[%p] drawBuffer released\n", d);[m
[32m+[m[32m release_WidgetDrawBuffer(d->drawBuf);[m
[32m+[m[32m }[m
[32m+[m[32m return;[m
[32m+[m[32m }[m
[32m+[m[32m drawLayerEffects_Widget_(d);[m
[32m+[m[32m if (!d->drawBuf || !checkDrawBuffer_Widget_(d)) {[m
[32m+[m[32m beginBufferDraw_Widget_(d);[m
[32m+[m[32m drawBackground_Widget(d);[m
[32m+[m[32m drawChildren_Widget(d);[m
[32m+[m[32m endBufferDraw_Widget_(d);[m
[32m+[m[32m }[m
[32m+[m[32m if (d->drawBuf) {[m
[32m+[m[32m iAssert(d->drawBuf->isValid);[m
[32m+[m[32m const iRect bounds = bounds_Widget(d);[m
[32m+[m[32m SDL_RenderCopy(renderer_Window(get_Window()), d->drawBuf->texture, NULL,[m
[32m+[m[32m &(SDL_Rect){ bounds.pos.x, bounds.pos.y,[m
[32m+[m[32m d->drawBuf->size.x, d->drawBuf->size.y });[m
[32m+[m[32m }[m
}[m
[m
iAny *addChild_Widget(iWidget *d, iAnyObject *child) {[m
[36m@@ -1659,12 +1844,20 @@[m [mvoid postCommand_Widget(const iAnyObject *d, const char *cmd, ...) {[m
deinit_String(&str);[m
}[m
[m
[31m-void refresh_Widget(const iAnyObject *d) {[m
[32m+[m[32mvoid refresh_Widget(const iAnyObject *d) {[m[41m [m
/* TODO: Could be widget specific, if parts of the tree are cached. */[m
/* TODO: The visbuffer in DocumentWidget and ListWidget could be moved to be a general[m
purpose feature of Widget. */[m
iAssert(isInstance_Object(d, &Class_Widget));[m
[31m- iUnused(d);[m
[32m+[m[32m /* Mark draw buffers invalid. */[m
[32m+[m[32m for (const iWidget *w = d; w; w = w->parent) {[m
[32m+[m[32m if (w->drawBuf) {[m
[32m+[m[32m// if (w->drawBuf->isValid) {[m
[32m+[m[32m// printf("[%p] drawbuffer invalidated by %p\n", w, d); fflush(stdout);[m
[32m+[m[32m// }[m
[32m+[m[32m w->drawBuf->isValid = iFalse;[m
[32m+[m[32m }[m
[32m+[m[32m }[m
postRefresh_App();[m
}[m
[m
[1mdiff --git a/src/ui/widget.h b/src/ui/widget.h[m
[1mindex 1a944c0a..fd4d8898 100644[m
[1m--- a/src/ui/widget.h[m
[1m+++ b/src/ui/widget.h[m
[36m@@ -131,6 +131,8 @@[m [menum iWidgetFocusDir {[m
backward_WidgetFocusDir,[m
};[m
[m
[32m+[m[32miDeclareType(WidgetDrawBuffer)[m
[32m+[m
struct Impl_Widget {[m
iObject object;[m
iString id;[m
[36m@@ -148,6 +150,7 @@[m [mstruct Impl_Widget {[m
iWidget * parent;[m
iBool (*commandHandler)(iWidget *, const char *);[m
iRoot * root;[m
[32m+[m[32m iWidgetDrawBuffer *drawBuf;[m
};[m
[m
iDeclareObjectConstruction(Widget)[m
[36m@@ -203,6 +206,12 @@[m [msize_t childCount_Widget (const iWidget *);[m
void draw_Widget (const iWidget *);[m
void drawBackground_Widget (const iWidget *);[m
void drawChildren_Widget (const iWidget *);[m
[32m+[m[32mvoid drawRoot_Widget (const iWidget ); / root only */[m
[32m+[m[32mvoid setDrawBufferEnabled_Widget (iWidget *, iBool enable);[m
[32m+[m
[32m+[m[32miLocalDef iBool isDrawBufferEnabled_Widget(const iWidget *d) {[m
[32m+[m[32m return d && d->drawBuf;[m
[32m+[m[32m}[m
[m
iLocalDef int width_Widget(const iAnyObject *d) {[m
if (d) {[m
[1mdiff --git a/src/ui/window.c b/src/ui/window.c[m
[1mindex 096853cc..3385f436 100644[m
[1m--- a/src/ui/window.c[m
[1m+++ b/src/ui/window.c[m
[36m@@ -1060,12 +1060,13 @@[m [mvoid draw_Window(iWindow *d) {[m
d->frameTime = SDL_GetTicks();[m
if (isExposed_Window(d)) {[m
d->isInvalidated = iFalse;[m
[32m+[m[32m extern int drawCount_;[m
iForIndices(i, d->roots) {[m
iRoot *root = d->roots[i];[m
if (root) {[m
setCurrent_Root(root);[m
unsetClip_Paint(&p); /* update clip to current root */[m
[31m- draw_Widget(root->widget);[m
[32m+[m[32m drawRoot_Widget(root->widget);[m
#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)[m
/* App icon. */[m
const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon");[m
[36m@@ -1105,6 +1106,10 @@[m [mvoid draw_Window(iWindow *d) {[m
}[m
}[m
setCurrent_Root(NULL);[m
[32m+[m[32m#if !defined (NDEBUG)[m
[32m+[m[32m draw_Text(defaultBold_FontId, zero_I2(), red_ColorId, "%d", drawCount_);[m
[32m+[m[32m drawCount_ = 0;[m
[32m+[m[32m#endif[m
}[m
#if 0[m
/* Text cache debugging. */ {[m
text/plain
This content has been proxied by September (ba2dc).