=> 33620846cca5678fbd662ea1a48fad302727dae7
[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[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 # include "../ios.h"[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/gemini; charset=utf-8
This content has been proxied by September (ba2dc).