diff options
-rw-r--r-- | src/ui/inputwidget.c | 5 | ||||
-rw-r--r-- | src/ui/mobile.c | 4 | ||||
-rw-r--r-- | src/ui/paint.c | 18 | ||||
-rw-r--r-- | src/ui/paint.h | 2 | ||||
-rw-r--r-- | src/ui/root.c | 54 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 3 | ||||
-rw-r--r-- | src/ui/text.c | 7 | ||||
-rw-r--r-- | src/ui/text_simple.c | 2 | ||||
-rw-r--r-- | src/ui/touch.c | 2 | ||||
-rw-r--r-- | src/ui/translation.c | 3 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 8 | ||||
-rw-r--r-- | src/ui/util.c | 1 | ||||
-rw-r--r-- | src/ui/widget.c | 241 | ||||
-rw-r--r-- | src/ui/widget.h | 9 | ||||
-rw-r--r-- | src/ui/window.c | 7 |
15 files changed, 327 insertions, 39 deletions
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 6e9ef6c2..802a2d6c 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -1565,6 +1565,11 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1565 | } | 1565 | } |
1566 | switch (processEvent_Click(&d->click, ev)) { | 1566 | switch (processEvent_Click(&d->click, ev)) { |
1567 | case none_ClickResult: | 1567 | case none_ClickResult: |
1568 | if (ev->type == SDL_MOUSEBUTTONUP && | ||
1569 | deviceType_App() != desktop_AppDeviceType && isFocused_Widget(d)) { | ||
1570 | setFocus_Widget(NULL); | ||
1571 | return iTrue; | ||
1572 | } | ||
1568 | break; | 1573 | break; |
1569 | case started_ClickResult: { | 1574 | case started_ClickResult: { |
1570 | setFocus_Widget(w); | 1575 | setFocus_Widget(w); |
diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 6ea672e6..9e2dc4f7 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c | |||
@@ -357,6 +357,7 @@ static iWidget *addChildPanel_(iWidget *parent, iLabelWidget *panelButton, | |||
357 | setId_Widget(panel, "panel"); | 357 | setId_Widget(panel, "panel"); |
358 | setUserData_Object(panelButton, panel); | 358 | setUserData_Object(panelButton, panel); |
359 | setBackgroundColor_Widget(panel, uiBackground_ColorId); | 359 | setBackgroundColor_Widget(panel, uiBackground_ColorId); |
360 | setDrawBufferEnabled_Widget(panel, iTrue); | ||
360 | setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad"); | 361 | setId_Widget(addChild_Widget(panel, iClob(makePadding_Widget(0))), "panel.toppad"); |
361 | if (titleText) { | 362 | if (titleText) { |
362 | iLabelWidget *title = | 363 | iLabelWidget *title = |
@@ -601,6 +602,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget, | |||
601 | /* The panel roots. */ | 602 | /* The panel roots. */ |
602 | iWidget *topPanel = new_Widget(); { | 603 | iWidget *topPanel = new_Widget(); { |
603 | setId_Widget(topPanel, "panel.top"); | 604 | setId_Widget(topPanel, "panel.top"); |
605 | setDrawBufferEnabled_Widget(topPanel, iTrue); | ||
604 | setCommandHandler_Widget(topPanel, topPanelHandler_); | 606 | setCommandHandler_Widget(topPanel, topPanelHandler_); |
605 | setFlags_Widget(topPanel, | 607 | setFlags_Widget(topPanel, |
606 | arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag | | 608 | arrangeVertical_WidgetFlag | resizeWidthOfChildren_WidgetFlag | |
@@ -730,7 +732,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget, | |||
730 | updatePanelSheetMetrics_(panels); | 732 | updatePanelSheetMetrics_(panels); |
731 | arrange_Widget(panels); | 733 | arrange_Widget(panels); |
732 | postCommand_App("widget.overflow"); /* with the correct dimensions */ | 734 | postCommand_App("widget.overflow"); /* with the correct dimensions */ |
733 | printTree_Widget(panels); | 735 | // printTree_Widget(panels); |
734 | } | 736 | } |
735 | 737 | ||
736 | #if 0 | 738 | #if 0 |
diff --git a/src/ui/paint.c b/src/ui/paint.c index 79adb7d1..af62f908 100644 --- a/src/ui/paint.c +++ b/src/ui/paint.c | |||
@@ -24,6 +24,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | 24 | ||
25 | #include <SDL_version.h> | 25 | #include <SDL_version.h> |
26 | 26 | ||
27 | iInt2 origin_Paint; | ||
28 | |||
27 | iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) { | 29 | iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) { |
28 | iAssert(d->dst); | 30 | iAssert(d->dst); |
29 | return d->dst->render; | 31 | return d->dst->render; |
@@ -62,10 +64,11 @@ void endTarget_Paint(iPaint *d) { | |||
62 | } | 64 | } |
63 | 65 | ||
64 | void setClip_Paint(iPaint *d, iRect rect) { | 66 | void setClip_Paint(iPaint *d, iRect rect) { |
65 | rect = intersect_Rect(rect, rect_Root(get_Root())); | 67 | //rect = intersect_Rect(rect, rect_Root(get_Root())); |
66 | if (isEmpty_Rect(rect)) { | 68 | if (isEmpty_Rect(rect)) { |
67 | rect = init_Rect(0, 0, 1, 1); | 69 | rect = init_Rect(0, 0, 1, 1); |
68 | } | 70 | } |
71 | addv_I2(&rect.pos, origin_Paint); | ||
69 | SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect); | 72 | SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect); |
70 | } | 73 | } |
71 | 74 | ||
@@ -85,6 +88,7 @@ void unsetClip_Paint(iPaint *d) { | |||
85 | } | 88 | } |
86 | 89 | ||
87 | void drawRect_Paint(const iPaint *d, iRect rect, int color) { | 90 | void drawRect_Paint(const iPaint *d, iRect rect, int color) { |
91 | addv_I2(&rect.pos, origin_Paint); | ||
88 | iInt2 br = bottomRight_Rect(rect); | 92 | iInt2 br = bottomRight_Rect(rect); |
89 | /* Keep the right/bottom edge visible in the window. */ | 93 | /* Keep the right/bottom edge visible in the window. */ |
90 | if (br.x == d->dst->size.x) br.x--; | 94 | if (br.x == d->dst->size.x) br.x--; |
@@ -115,11 +119,14 @@ void drawRectThickness_Paint(const iPaint *d, iRect rect, int thickness, int col | |||
115 | } | 119 | } |
116 | 120 | ||
117 | void fillRect_Paint(const iPaint *d, iRect rect, int color) { | 121 | void fillRect_Paint(const iPaint *d, iRect rect, int color) { |
122 | addv_I2(&rect.pos, origin_Paint); | ||
118 | setColor_Paint_(d, color); | 123 | setColor_Paint_(d, color); |
124 | // printf("fillRect_Paint: %d,%d %dx%d\n", rect.pos.x, rect.pos.y, rect.size.x, rect.size.y); | ||
119 | SDL_RenderFillRect(renderer_Paint_(d), (SDL_Rect *) &rect); | 125 | SDL_RenderFillRect(renderer_Paint_(d), (SDL_Rect *) &rect); |
120 | } | 126 | } |
121 | 127 | ||
122 | void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color, int alpha) { | 128 | void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color, int alpha) { |
129 | addv_I2(&inner.pos, origin_Paint); | ||
123 | SDL_Renderer *render = renderer_Paint_(d); | 130 | SDL_Renderer *render = renderer_Paint_(d); |
124 | SDL_Texture *shadow = get_Window()->borderShadow; | 131 | SDL_Texture *shadow = get_Window()->borderShadow; |
125 | const iInt2 size = size_SDLTexture(shadow); | 132 | const iInt2 size = size_SDLTexture(shadow); |
@@ -146,9 +153,14 @@ void drawSoftShadow_Paint(const iPaint *d, iRect inner, int thickness, int color | |||
146 | &(SDL_Rect){ outer.pos.x, inner.pos.y, thickness, inner.size.y }); | 153 | &(SDL_Rect){ outer.pos.x, inner.pos.y, thickness, inner.size.y }); |
147 | } | 154 | } |
148 | 155 | ||
149 | void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t count, int color) { | 156 | void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t n, int color) { |
150 | setColor_Paint_(d, color); | 157 | setColor_Paint_(d, color); |
151 | SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) points, count); | 158 | iInt2 *offsetPoints = malloc(sizeof(iInt2) * n); |
159 | for (size_t i = 0; i < n; i++) { | ||
160 | offsetPoints[i] = add_I2(points[i], origin_Paint); | ||
161 | } | ||
162 | SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) offsetPoints, n); | ||
163 | free(offsetPoints); | ||
152 | } | 164 | } |
153 | 165 | ||
154 | iInt2 size_SDLTexture(SDL_Texture *d) { | 166 | 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 { | |||
36 | uint8_t alpha; | 36 | uint8_t alpha; |
37 | }; | 37 | }; |
38 | 38 | ||
39 | extern iInt2 origin_Paint; /* add this to all drawn positions so buffered graphics are correctly offset */ | ||
40 | |||
39 | void init_Paint (iPaint *); | 41 | void init_Paint (iPaint *); |
40 | 42 | ||
41 | void beginTarget_Paint (iPaint *, SDL_Texture *target); | 43 | 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) { | |||
685 | } | 685 | } |
686 | return iTrue; | 686 | return iTrue; |
687 | } | 687 | } |
688 | else if (deviceType_App() != desktop_AppDeviceType && | ||
689 | (equal_Command(cmd, "focus.gained") || equal_Command(cmd, "focus.lost"))) { | ||
690 | iInputWidget *url = findChild_Widget(navBar, "url"); | ||
691 | if (pointer_Command(cmd) == url) { | ||
692 | const iBool isFocused = equal_Command(cmd, "focus.gained"); | ||
693 | setFlags_Widget(findChild_Widget(navBar, "navbar.clear"), hidden_WidgetFlag, !isFocused); | ||
694 | showCollapsed_Widget(findChild_Widget(navBar, "navbar.cancel"), isFocused); | ||
695 | showCollapsed_Widget(findChild_Widget(navBar, "pagemenubutton"), !isFocused); | ||
696 | showCollapsed_Widget(findChild_Widget(navBar, "reload"), !isFocused); | ||
697 | } | ||
698 | return iFalse; | ||
699 | } | ||
700 | else if (equal_Command(cmd, "navbar.clear")) { | ||
701 | iInputWidget *url = findChild_Widget(navBar, "url"); | ||
702 | selectAll_InputWidget(url); | ||
703 | /* Emulate a Backspace keypress. */ | ||
704 | class_InputWidget(url)->processEvent( | ||
705 | as_Widget(url), | ||
706 | (SDL_Event *) &(SDL_KeyboardEvent){ .type = SDL_KEYDOWN, | ||
707 | .timestamp = SDL_GetTicks(), | ||
708 | .state = SDL_PRESSED, | ||
709 | .keysym = { .sym = SDLK_BACKSPACE } }); | ||
710 | return iTrue; | ||
711 | } | ||
712 | else if (equal_Command(cmd, "navbar.cancel")) { | ||
713 | setFocus_Widget(NULL); | ||
714 | return iTrue; | ||
715 | } | ||
688 | else if (equal_Command(cmd, "input.edited")) { | 716 | else if (equal_Command(cmd, "input.edited")) { |
689 | iAnyObject * url = findChild_Widget(navBar, "url"); | 717 | iAnyObject * url = findChild_Widget(navBar, "url"); |
690 | const iString *text = text_InputWidget(url); | 718 | const iString *text = text_InputWidget(url); |
@@ -941,7 +969,7 @@ void updateMetrics_Root(iRoot *d) { | |||
941 | setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y)); | 969 | setFixedSize_Widget(appIcon, init_I2(appIconSize_Root(), appMin->rect.size.y)); |
942 | } | 970 | } |
943 | iWidget *navBar = findChild_Widget(d->widget, "navbar"); | 971 | iWidget *navBar = findChild_Widget(d->widget, "navbar"); |
944 | iWidget *lock = findChild_Widget(navBar, "navbar.lock"); | 972 | // iWidget *lock = findChild_Widget(navBar, "navbar.lock"); |
945 | iWidget *url = findChild_Widget(d->widget, "url"); | 973 | iWidget *url = findChild_Widget(d->widget, "url"); |
946 | iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed"); | 974 | iWidget *rightEmbed = findChild_Widget(navBar, "url.rightembed"); |
947 | iWidget *embedPad = findChild_Widget(navBar, "url.embedpad"); | 975 | iWidget *embedPad = findChild_Widget(navBar, "url.embedpad"); |
@@ -1044,6 +1072,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1044 | /* Navigation bar. */ { | 1072 | /* Navigation bar. */ { |
1045 | navBar = new_Widget(); | 1073 | navBar = new_Widget(); |
1046 | setId_Widget(navBar, "navbar"); | 1074 | setId_Widget(navBar, "navbar"); |
1075 | setDrawBufferEnabled_Widget(navBar, iTrue); | ||
1047 | setFlags_Widget(navBar, | 1076 | setFlags_Widget(navBar, |
1048 | hittable_WidgetFlag | /* context menu */ | 1077 | hittable_WidgetFlag | /* context menu */ |
1049 | arrangeHeight_WidgetFlag | | 1078 | arrangeHeight_WidgetFlag | |
@@ -1095,6 +1124,16 @@ void createUserInterface_Root(iRoot *d) { | |||
1095 | setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize); | 1124 | setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize); |
1096 | updateTextCStr_LabelWidget(lock, "\U0001f512"); | 1125 | updateTextCStr_LabelWidget(lock, "\U0001f512"); |
1097 | } | 1126 | } |
1127 | /* Button for clearing the URL bar contents. */ { | ||
1128 | iLabelWidget *clear = addChildFlags_Widget( | ||
1129 | as_Widget(url), | ||
1130 | iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")), | ||
1131 | hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag); | ||
1132 | setId_Widget(as_Widget(clear), "navbar.clear"); | ||
1133 | setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize); | ||
1134 | setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse); | ||
1135 | setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId); | ||
1136 | } | ||
1098 | iWidget *rightEmbed = new_Widget(); | 1137 | iWidget *rightEmbed = new_Widget(); |
1099 | setId_Widget(rightEmbed, "url.rightembed"); | 1138 | setId_Widget(rightEmbed, "url.rightembed"); |
1100 | addChildFlags_Widget(as_Widget(url), | 1139 | addChildFlags_Widget(as_Widget(url), |
@@ -1151,6 +1190,13 @@ void createUserInterface_Root(iRoot *d) { | |||
1151 | setFlags_Widget(urlButtons, embedFlags | arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); | 1190 | setFlags_Widget(urlButtons, embedFlags | arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue); |
1152 | /* Mobile page menu. */ | 1191 | /* Mobile page menu. */ |
1153 | if (deviceType_App() != desktop_AppDeviceType) { | 1192 | if (deviceType_App() != desktop_AppDeviceType) { |
1193 | iLabelWidget *navCancel = new_LabelWidget("${cancel}", "navbar.cancel"); | ||
1194 | addChildFlags_Widget(urlButtons, iClob(navCancel), | ||
1195 | (embedFlags | tight_WidgetFlag | hidden_WidgetFlag | | ||
1196 | collapse_WidgetFlag) & ~noBackground_WidgetFlag); | ||
1197 | as_Widget(navCancel)->sizeRef = as_Widget(url); | ||
1198 | // setFont_LabelWidget(navCancel, defaultBold_FontId); | ||
1199 | setId_Widget(as_Widget(navCancel), "navbar.cancel"); | ||
1154 | iLabelWidget *pageMenuButton; | 1200 | iLabelWidget *pageMenuButton; |
1155 | /* In a mobile layout, the reload button is replaced with the Page/Ellipsis menu. */ | 1201 | /* In a mobile layout, the reload button is replaced with the Page/Ellipsis menu. */ |
1156 | pageMenuButton = makeMenuButton_LabelWidget(pageMenuCStr_, | 1202 | pageMenuButton = makeMenuButton_LabelWidget(pageMenuCStr_, |
@@ -1172,13 +1218,14 @@ void createUserInterface_Root(iRoot *d) { | |||
1172 | setId_Widget(as_Widget(pageMenuButton), "pagemenubutton"); | 1218 | setId_Widget(as_Widget(pageMenuButton), "pagemenubutton"); |
1173 | setFont_LabelWidget(pageMenuButton, uiContentBold_FontId); | 1219 | setFont_LabelWidget(pageMenuButton, uiContentBold_FontId); |
1174 | setAlignVisually_LabelWidget(pageMenuButton, iTrue); | 1220 | setAlignVisually_LabelWidget(pageMenuButton, iTrue); |
1175 | addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags | tight_WidgetFlag); | 1221 | addChildFlags_Widget(urlButtons, iClob(pageMenuButton), |
1222 | embedFlags | tight_WidgetFlag | collapse_WidgetFlag); | ||
1176 | updateSize_LabelWidget(pageMenuButton); | 1223 | updateSize_LabelWidget(pageMenuButton); |
1177 | } | 1224 | } |
1178 | /* Reload button. */ { | 1225 | /* Reload button. */ { |
1179 | iLabelWidget *reload = newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload"); | 1226 | iLabelWidget *reload = newIcon_LabelWidget(reloadCStr_, 0, 0, "navigate.reload"); |
1180 | setId_Widget(as_Widget(reload), "reload"); | 1227 | setId_Widget(as_Widget(reload), "reload"); |
1181 | addChildFlags_Widget(urlButtons, iClob(reload), embedFlags); | 1228 | addChildFlags_Widget(urlButtons, iClob(reload), embedFlags | collapse_WidgetFlag); |
1182 | updateSize_LabelWidget(reload); | 1229 | updateSize_LabelWidget(reload); |
1183 | } | 1230 | } |
1184 | addChildFlags_Widget(as_Widget(url), iClob(urlButtons), moveToParentRightEdge_WidgetFlag); | 1231 | addChildFlags_Widget(as_Widget(url), iClob(urlButtons), moveToParentRightEdge_WidgetFlag); |
@@ -1287,6 +1334,7 @@ void createUserInterface_Root(iRoot *d) { | |||
1287 | iWidget *toolBar = new_Widget(); | 1334 | iWidget *toolBar = new_Widget(); |
1288 | addChild_Widget(root, iClob(toolBar)); | 1335 | addChild_Widget(root, iClob(toolBar)); |
1289 | setId_Widget(toolBar, "toolbar"); | 1336 | setId_Widget(toolBar, "toolbar"); |
1337 | setDrawBufferEnabled_Widget(toolBar, iTrue); | ||
1290 | setCommandHandler_Widget(toolBar, handleToolBarCommands_); | 1338 | setCommandHandler_Widget(toolBar, handleToolBarCommands_); |
1291 | setFlags_Widget(toolBar, moveToParentBottomEdge_WidgetFlag | | 1339 | setFlags_Widget(toolBar, moveToParentBottomEdge_WidgetFlag | |
1292 | parentCannotResizeHeight_WidgetFlag | | 1340 | parentCannotResizeHeight_WidgetFlag | |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index b816b572..eb129424 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -663,8 +663,9 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { | |||
663 | /* On a phone, the right sidebar is used exclusively for Identities. */ | 663 | /* On a phone, the right sidebar is used exclusively for Identities. */ |
664 | const iBool isPhone = deviceType_App() == phone_AppDeviceType; | 664 | const iBool isPhone = deviceType_App() == phone_AppDeviceType; |
665 | if (!isPhone || d->side == left_SidebarSide) { | 665 | if (!isPhone || d->side == left_SidebarSide) { |
666 | iWidget *buttons = new_Widget(); | 666 | iWidget *buttons = new_Widget(); |
667 | setId_Widget(buttons, "buttons"); | 667 | setId_Widget(buttons, "buttons"); |
668 | setDrawBufferEnabled_Widget(buttons, iTrue); | ||
668 | for (int i = 0; i < max_SidebarMode; i++) { | 669 | for (int i = 0; i < max_SidebarMode; i++) { |
669 | if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) { | 670 | if (deviceType_App() == phone_AppDeviceType && i == identities_SidebarMode) { |
670 | continue; | 671 | continue; |
diff --git a/src/ui/text.c b/src/ui/text.c index 231281eb..f7fff4bc 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
25 | #include "metrics.h" | 25 | #include "metrics.h" |
26 | #include "embedded.h" | 26 | #include "embedded.h" |
27 | #include "window.h" | 27 | #include "window.h" |
28 | #include "paint.h" | ||
28 | #include "app.h" | 29 | #include "app.h" |
29 | 30 | ||
30 | #define STB_TRUETYPE_IMPLEMENTATION | 31 | #define STB_TRUETYPE_IMPLEMENTATION |
@@ -1712,6 +1713,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1712 | } | 1713 | } |
1713 | SDL_Rect src; | 1714 | SDL_Rect src; |
1714 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); | 1715 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); |
1716 | dst.x += origin_Paint.x; | ||
1717 | dst.y += origin_Paint.y; | ||
1715 | if (args->mode & fillBackground_RunMode) { | 1718 | if (args->mode & fillBackground_RunMode) { |
1716 | /* Alpha blending looks much better if the RGB components don't change in | 1719 | /* Alpha blending looks much better if the RGB components don't change in |
1717 | the partially transparent pixels. */ | 1720 | the partially transparent pixels. */ |
@@ -2182,6 +2185,8 @@ void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) { | |||
2182 | } | 2185 | } |
2183 | if (d->texture) { | 2186 | if (d->texture) { |
2184 | SDL_Texture *oldTarget = SDL_GetRenderTarget(render); | 2187 | SDL_Texture *oldTarget = SDL_GetRenderTarget(render); |
2188 | const iInt2 oldOrigin = origin_Paint; | ||
2189 | origin_Paint = zero_I2(); | ||
2185 | SDL_SetRenderTarget(render, d->texture); | 2190 | SDL_SetRenderTarget(render, d->texture); |
2186 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); | 2191 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); |
2187 | SDL_SetRenderDrawColor(render, 255, 255, 255, 0); | 2192 | SDL_SetRenderDrawColor(render, 255, 255, 255, 0); |
@@ -2190,6 +2195,7 @@ void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) { | |||
2190 | draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId); | 2195 | draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId); |
2191 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND); | 2196 | SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND); |
2192 | SDL_SetRenderTarget(render, oldTarget); | 2197 | SDL_SetRenderTarget(render, oldTarget); |
2198 | origin_Paint = oldOrigin; | ||
2193 | SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); | 2199 | SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); |
2194 | } | 2200 | } |
2195 | } | 2201 | } |
@@ -2203,6 +2209,7 @@ iTextBuf *newRange_TextBuf(int font, int color, iRangecc text) { | |||
2203 | } | 2209 | } |
2204 | 2210 | ||
2205 | void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) { | 2211 | void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) { |
2212 | addv_I2(&pos, origin_Paint); | ||
2206 | const iColor clr = get_Color(color); | 2213 | const iColor clr = get_Color(color); |
2207 | SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b); | 2214 | SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b); |
2208 | SDL_RenderCopy(text_.render, | 2215 | SDL_RenderCopy(text_.render, |
diff --git a/src/ui/text_simple.c b/src/ui/text_simple.c index e88b09a8..bf33b4be 100644 --- a/src/ui/text_simple.c +++ b/src/ui/text_simple.c | |||
@@ -306,6 +306,8 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) { | |||
306 | src.y += over; | 306 | src.y += over; |
307 | src.h -= over; | 307 | src.h -= over; |
308 | } | 308 | } |
309 | dst.x += origin_Paint.x; | ||
310 | dst.y += origin_Paint.y; | ||
309 | if (args->mode & fillBackground_RunMode) { | 311 | if (args->mode & fillBackground_RunMode) { |
310 | /* Alpha blending looks much better if the RGB components don't change in | 312 | /* Alpha blending looks much better if the RGB components don't change in |
311 | the partially transparent pixels. */ | 313 | the partially transparent pixels. */ |
diff --git a/src/ui/touch.c b/src/ui/touch.c index dac1152e..f0456acb 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -614,7 +614,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
614 | // pixels.y, y_F3(amount), y_F3(touch->accum), | 614 | // pixels.y, y_F3(amount), y_F3(touch->accum), |
615 | // touch->edge); | 615 | // touch->edge); |
616 | if (pixels.x || pixels.y) { | 616 | if (pixels.x || pixels.y) { |
617 | setFocus_Widget(NULL); | 617 | //setFocus_Widget(NULL); |
618 | dispatchMotion_Touch_(touch->pos[0], 0); | 618 | dispatchMotion_Touch_(touch->pos[0], 0); |
619 | setCurrent_Root(touch->affinity->root); | 619 | setCurrent_Root(touch->affinity->root); |
620 | dispatchEvent_Widget(touch->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){ | 620 | dispatchEvent_Widget(touch->affinity, (SDL_Event *) &(SDL_MouseWheelEvent){ |
diff --git a/src/ui/translation.c b/src/ui/translation.c index cef68dce..b86e6e52 100644 --- a/src/ui/translation.c +++ b/src/ui/translation.c | |||
@@ -136,7 +136,8 @@ static void draw_TranslationProgressWidget_(const iTranslationProgressWidget *d) | |||
136 | get_Color(palette[palCur]), get_Color(palette[palNext]), palPos - (int) palPos); | 136 | get_Color(palette[palCur]), get_Color(palette[palNext]), palPos - (int) palPos); |
137 | SDL_SetRenderDrawColor(renderer_Window(get_Window()), back.r, back.g, back.b, p.alpha); | 137 | SDL_SetRenderDrawColor(renderer_Window(get_Window()), back.r, back.g, back.b, p.alpha); |
138 | SDL_RenderFillRect(renderer_Window(get_Window()), | 138 | SDL_RenderFillRect(renderer_Window(get_Window()), |
139 | &(SDL_Rect){ pos.x, pos.y, spr->size.x, spr->size.y }); | 139 | &(SDL_Rect){ pos.x + origin_Paint.x, pos.y + origin_Paint.y, |
140 | spr->size.x, spr->size.y }); | ||
140 | if (fg >= 0) { | 141 | if (fg >= 0) { |
141 | setOpacity_Text(opacity * 2); | 142 | setOpacity_Text(opacity * 2); |
142 | drawRange_Text(d->font, addX_I2(pos, spr->xoff), fg, range_String(&spr->text)); | 143 | drawRange_Text(d->font, addX_I2(pos, spr->xoff), fg, range_String(&spr->text)); |
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index fb8aaf0a..78a1196a 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c | |||
@@ -376,11 +376,11 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) { | |||
376 | return processEvent_Widget(w, ev); | 376 | return processEvent_Widget(w, ev); |
377 | } | 377 | } |
378 | 378 | ||
379 | static void draw_UploadWidget_(const iUploadWidget *d) { | 379 | //static void draw_UploadWidget_(const iUploadWidget *d) { |
380 | draw_Widget(constAs_Widget(d)); | 380 | // draw_Widget(constAs_Widget(d)); |
381 | } | 381 | //} |
382 | 382 | ||
383 | iBeginDefineSubclass(UploadWidget, Widget) | 383 | iBeginDefineSubclass(UploadWidget, Widget) |
384 | .processEvent = (iAny *) processEvent_UploadWidget_, | 384 | .processEvent = (iAny *) processEvent_UploadWidget_, |
385 | .draw = (iAny *) draw_UploadWidget_, | 385 | .draw = draw_Widget, |
386 | iEndDefineSubclass(UploadWidget) | 386 | 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) { | |||
685 | 685 | ||
686 | iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | 686 | iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { |
687 | iWidget *menu = new_Widget(); | 687 | iWidget *menu = new_Widget(); |
688 | setDrawBufferEnabled_Widget(menu, iTrue); | ||
688 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); | 689 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); |
689 | if (deviceType_App() != desktop_AppDeviceType) { | 690 | if (deviceType_App() != desktop_AppDeviceType) { |
690 | setPadding1_Widget(menu, 2 * gap_UI); | 691 | setPadding1_Widget(menu, 2 * gap_UI); |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 659a00cc..66cd0e7b 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -40,6 +40,67 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
40 | # include "../ios.h" | 40 | # include "../ios.h" |
41 | #endif | 41 | #endif |
42 | 42 | ||
43 | struct Impl_WidgetDrawBuffer { | ||
44 | SDL_Texture *texture; | ||
45 | iInt2 size; | ||
46 | iBool isValid; | ||
47 | SDL_Texture *oldTarget; | ||
48 | iInt2 oldOrigin; | ||
49 | }; | ||
50 | |||
51 | static void init_WidgetDrawBuffer(iWidgetDrawBuffer *d) { | ||
52 | d->texture = NULL; | ||
53 | d->size = zero_I2(); | ||
54 | d->isValid = iFalse; | ||
55 | d->oldTarget = NULL; | ||
56 | } | ||
57 | |||
58 | static void deinit_WidgetDrawBuffer(iWidgetDrawBuffer *d) { | ||
59 | SDL_DestroyTexture(d->texture); | ||
60 | } | ||
61 | |||
62 | iDefineTypeConstruction(WidgetDrawBuffer) | ||
63 | |||
64 | static void realloc_WidgetDrawBuffer(iWidgetDrawBuffer *d, SDL_Renderer *render, iInt2 size) { | ||
65 | if (!isEqual_I2(d->size, size)) { | ||
66 | d->size = size; | ||
67 | if (d->texture) { | ||
68 | SDL_DestroyTexture(d->texture); | ||
69 | } | ||
70 | d->texture = SDL_CreateTexture(render, | ||
71 | SDL_PIXELFORMAT_RGBA8888, | ||
72 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
73 | size.x, | ||
74 | size.y); | ||
75 | SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); | ||
76 | d->isValid = iFalse; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static void release_WidgetDrawBuffer(iWidgetDrawBuffer *d) { | ||
81 | if (d->texture) { | ||
82 | SDL_DestroyTexture(d->texture); | ||
83 | d->texture = NULL; | ||
84 | } | ||
85 | d->size = zero_I2(); | ||
86 | d->isValid = iFalse; | ||
87 | } | ||
88 | |||
89 | static iRect boundsForDraw_Widget_(const iWidget *d) { | ||
90 | iRect bounds = bounds_Widget(d); | ||
91 | if (d->flags & drawBackgroundToBottom_WidgetFlag) { | ||
92 | bounds.size.y = iMaxi(bounds.size.y, size_Root(d->root).y - top_Rect(bounds)); | ||
93 | } | ||
94 | return bounds; | ||
95 | } | ||
96 | |||
97 | static iBool checkDrawBuffer_Widget_(const iWidget *d) { | ||
98 | return d->drawBuf && d->drawBuf->isValid && | ||
99 | isEqual_I2(d->drawBuf->size, boundsForDraw_Widget_(d).size); | ||
100 | } | ||
101 | |||
102 | /*----------------------------------------------------------------------------------------------*/ | ||
103 | |||
43 | static void printInfo_Widget_(const iWidget *); | 104 | static void printInfo_Widget_(const iWidget *); |
44 | 105 | ||
45 | void releaseChildren_Widget(iWidget *d) { | 106 | void releaseChildren_Widget(iWidget *d) { |
@@ -66,6 +127,7 @@ void init_Widget(iWidget *d) { | |||
66 | d->children = NULL; | 127 | d->children = NULL; |
67 | d->parent = NULL; | 128 | d->parent = NULL; |
68 | d->commandHandler = NULL; | 129 | d->commandHandler = NULL; |
130 | d->drawBuf = NULL; | ||
69 | iZap(d->padding); | 131 | iZap(d->padding); |
70 | } | 132 | } |
71 | 133 | ||
@@ -82,6 +144,7 @@ static void visualOffsetAnimation_Widget_(void *ptr) { | |||
82 | 144 | ||
83 | void deinit_Widget(iWidget *d) { | 145 | void deinit_Widget(iWidget *d) { |
84 | releaseChildren_Widget(d); | 146 | releaseChildren_Widget(d); |
147 | delete_WidgetDrawBuffer(d->drawBuf); | ||
85 | #if 0 && !defined (NDEBUG) | 148 | #if 0 && !defined (NDEBUG) |
86 | printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id), | 149 | printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id), |
87 | d->flags & keepOnTop_WidgetFlag ? 1 : 0); | 150 | d->flags & keepOnTop_WidgetFlag ? 1 : 0); |
@@ -1036,7 +1099,8 @@ iBool scrollOverflow_Widget(iWidget *d, int delta) { | |||
1036 | const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos); | 1099 | const iInt2 newPos = windowToInner_Widget(d->parent, bounds.pos); |
1037 | if (!isEqual_I2(newPos, d->rect.pos)) { | 1100 | if (!isEqual_I2(newPos, d->rect.pos)) { |
1038 | d->rect.pos = newPos; | 1101 | d->rect.pos = newPos; |
1039 | refresh_Widget(d); | 1102 | // refresh_Widget(d); |
1103 | postRefresh_App(); | ||
1040 | } | 1104 | } |
1041 | return height_Rect(bounds) > height_Rect(winRect); | 1105 | return height_Rect(bounds) > height_Rect(winRect); |
1042 | } | 1106 | } |
@@ -1077,6 +1141,9 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
1077 | } | 1141 | } |
1078 | if (ev->user.code == command_UserEventCode) { | 1142 | if (ev->user.code == command_UserEventCode) { |
1079 | const char *cmd = command_UserEvent(ev); | 1143 | const char *cmd = command_UserEvent(ev); |
1144 | if (d->drawBuf && equal_Command(cmd, "theme.changed")) { | ||
1145 | d->drawBuf->isValid = iFalse; | ||
1146 | } | ||
1080 | if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) && | 1147 | if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) && |
1081 | isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag && | 1148 | isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag && |
1082 | equal_Command(cmd, "edgeswipe.moved")) { | 1149 | equal_Command(cmd, "edgeswipe.moved")) { |
@@ -1147,14 +1214,13 @@ int backgroundFadeColor_Widget(void) { | |||
1147 | } | 1214 | } |
1148 | } | 1215 | } |
1149 | 1216 | ||
1150 | void drawBackground_Widget(const iWidget *d) { | 1217 | iLocalDef iBool isDrawn_Widget_(const iWidget *d) { |
1151 | if (d->flags & noBackground_WidgetFlag) { | 1218 | return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag; |
1152 | return; | 1219 | } |
1153 | } | 1220 | |
1154 | if (d->flags & hidden_WidgetFlag && ~d->flags & visualOffset_WidgetFlag) { | 1221 | static void drawLayerEffects_Widget_(const iWidget *d) { |
1155 | return; | 1222 | /* Layered effects are not buffered, so they are drawn here separately. */ |
1156 | } | 1223 | iAssert(isDrawn_Widget_(d)); |
1157 | /* Popup menus have a shadowed border. */ | ||
1158 | iBool shadowBorder = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0; | 1224 | iBool shadowBorder = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0; |
1159 | iBool fadeBackground = (d->bgColor >= 0 || d->frameColor >= 0) && d->flags & mouseModal_WidgetFlag; | 1225 | iBool fadeBackground = (d->bgColor >= 0 || d->frameColor >= 0) && d->flags & mouseModal_WidgetFlag; |
1160 | if (deviceType_App() == phone_AppDeviceType) { | 1226 | if (deviceType_App() == phone_AppDeviceType) { |
@@ -1163,13 +1229,12 @@ void drawBackground_Widget(const iWidget *d) { | |||
1163 | shadowBorder = iFalse; | 1229 | shadowBorder = iFalse; |
1164 | } | 1230 | } |
1165 | } | 1231 | } |
1232 | const iBool isFaded = fadeBackground && ~d->flags & noFadeBackground_WidgetFlag; | ||
1166 | if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) { | 1233 | if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) { |
1167 | iPaint p; | 1234 | iPaint p; |
1168 | init_Paint(&p); | 1235 | init_Paint(&p); |
1169 | drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); | 1236 | drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); |
1170 | } | 1237 | } |
1171 | const iBool isFaded = fadeBackground && | ||
1172 | ~d->flags & noFadeBackground_WidgetFlag; | ||
1173 | if (isFaded) { | 1238 | if (isFaded) { |
1174 | iPaint p; | 1239 | iPaint p; |
1175 | init_Paint(&p); | 1240 | init_Paint(&p); |
@@ -1183,10 +1248,20 @@ void drawBackground_Widget(const iWidget *d) { | |||
1183 | fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget()); | 1248 | fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget()); |
1184 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | 1249 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); |
1185 | } | 1250 | } |
1251 | } | ||
1252 | |||
1253 | void drawBackground_Widget(const iWidget *d) { | ||
1254 | if (d->flags & noBackground_WidgetFlag) { | ||
1255 | return; | ||
1256 | } | ||
1257 | if (!isDrawn_Widget_(d)) { | ||
1258 | return; | ||
1259 | } | ||
1260 | /* Popup menus have a shadowed border. */ | ||
1186 | if (d->bgColor >= 0 || d->frameColor >= 0) { | 1261 | if (d->bgColor >= 0 || d->frameColor >= 0) { |
1187 | iRect rect = bounds_Widget(d); | 1262 | iRect rect = bounds_Widget(d); |
1188 | if (d->flags & drawBackgroundToBottom_WidgetFlag) { | 1263 | if (d->flags & drawBackgroundToBottom_WidgetFlag) { |
1189 | rect.size.y = size_Root(d->root).y - top_Rect(rect); | 1264 | rect.size.y = iMax(rect.size.y, size_Root(d->root).y - top_Rect(rect)); |
1190 | } | 1265 | } |
1191 | iPaint p; | 1266 | iPaint p; |
1192 | init_Paint(&p); | 1267 | init_Paint(&p); |
@@ -1242,8 +1317,54 @@ void drawBackground_Widget(const iWidget *d) { | |||
1242 | } | 1317 | } |
1243 | } | 1318 | } |
1244 | 1319 | ||
1245 | iLocalDef iBool isDrawn_Widget_(const iWidget *d) { | 1320 | int drawCount_; |
1246 | return ~d->flags & hidden_WidgetFlag || d->flags & visualOffset_WidgetFlag; | 1321 | |
1322 | static iBool isRoot_Widget_(const iWidget *d) { | ||
1323 | return d == d->root->widget; | ||
1324 | } | ||
1325 | |||
1326 | iLocalDef iBool isFullyContainedByOther_Rect(const iRect d, const iRect other) { | ||
1327 | if (isEmpty_Rect(other)) { | ||
1328 | /* Nothing is contained by empty. */ | ||
1329 | return iFalse; | ||
1330 | } | ||
1331 | if (isEmpty_Rect(d)) { | ||
1332 | /* Empty is fully contained by anything. */ | ||
1333 | return iTrue; | ||
1334 | } | ||
1335 | return equal_Rect(intersect_Rect(d, other), d); | ||
1336 | } | ||
1337 | |||
1338 | static void addToPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs, iRect *fullyMasked) { | ||
1339 | if (isDrawn_Widget_(d)) { | ||
1340 | iRect bounds = bounds_Widget(d); | ||
1341 | if (d->flags & drawBackgroundToBottom_WidgetFlag) { | ||
1342 | bounds.size.y = size_Root(d->root).y - top_Rect(bounds); | ||
1343 | } | ||
1344 | if (isFullyContainedByOther_Rect(bounds, *fullyMasked)) { | ||
1345 | return; /* can't be seen */ | ||
1346 | } | ||
1347 | pushBack_PtrArray(pvs, d); | ||
1348 | if (d->bgColor >= 0 && ~d->flags & noBackground_WidgetFlag && | ||
1349 | isFullyContainedByOther_Rect(*fullyMasked, bounds)) { | ||
1350 | *fullyMasked = bounds; | ||
1351 | } | ||
1352 | } | ||
1353 | } | ||
1354 | |||
1355 | static void findPotentiallyVisible_Widget_(const iWidget *d, iPtrArray *pvs) { | ||
1356 | iRect fullyMasked = zero_Rect(); | ||
1357 | if (isRoot_Widget_(d)) { | ||
1358 | iReverseConstForEach(PtrArray, i, onTop_Root(d->root)) { | ||
1359 | addToPotentiallyVisible_Widget_(i.ptr, pvs, &fullyMasked); | ||
1360 | } | ||
1361 | } | ||
1362 | iReverseConstForEach(ObjectList, i, d->children) { | ||
1363 | const iWidget *child = i.object; | ||
1364 | if (~child->flags & keepOnTop_WidgetFlag) { | ||
1365 | addToPotentiallyVisible_Widget_(child, pvs, &fullyMasked); | ||
1366 | } | ||
1367 | } | ||
1247 | } | 1368 | } |
1248 | 1369 | ||
1249 | void drawChildren_Widget(const iWidget *d) { | 1370 | void drawChildren_Widget(const iWidget *d) { |
@@ -1253,21 +1374,85 @@ void drawChildren_Widget(const iWidget *d) { | |||
1253 | iConstForEach(ObjectList, i, d->children) { | 1374 | iConstForEach(ObjectList, i, d->children) { |
1254 | const iWidget *child = constAs_Widget(i.object); | 1375 | const iWidget *child = constAs_Widget(i.object); |
1255 | if (~child->flags & keepOnTop_WidgetFlag && isDrawn_Widget_(child)) { | 1376 | if (~child->flags & keepOnTop_WidgetFlag && isDrawn_Widget_(child)) { |
1377 | drawCount_++; | ||
1256 | class_Widget(child)->draw(child); | 1378 | class_Widget(child)->draw(child); |
1257 | } | 1379 | } |
1258 | } | 1380 | } |
1381 | } | ||
1382 | |||
1383 | void drawRoot_Widget(const iWidget *d) { | ||
1384 | iAssert(d == d->root->widget); | ||
1259 | /* Root draws the on-top widgets on top of everything else. */ | 1385 | /* Root draws the on-top widgets on top of everything else. */ |
1260 | if (d == d->root->widget) { | 1386 | iPtrArray pvs; |
1261 | iConstForEach(PtrArray, i, onTop_Root(d->root)) { | 1387 | init_PtrArray(&pvs); |
1262 | const iWidget *top = *i.value; | 1388 | findPotentiallyVisible_Widget_(d, &pvs); |
1263 | class_Widget(top)->draw(top); | 1389 | iReverseConstForEach(PtrArray, i, &pvs) { |
1264 | } | 1390 | drawCount_++; |
1265 | } | 1391 | class_Widget(i.ptr)->draw(i.ptr); |
1392 | } | ||
1393 | deinit_PtrArray(&pvs); | ||
1394 | } | ||
1395 | |||
1396 | void setDrawBufferEnabled_Widget(iWidget *d, iBool enable) { | ||
1397 | if (enable && !d->drawBuf) { | ||
1398 | d->drawBuf = new_WidgetDrawBuffer(); | ||
1399 | } | ||
1400 | else if (!enable && d->drawBuf) { | ||
1401 | delete_WidgetDrawBuffer(d->drawBuf); | ||
1402 | d->drawBuf = NULL; | ||
1403 | } | ||
1404 | } | ||
1405 | |||
1406 | static void beginBufferDraw_Widget_(const iWidget *d) { | ||
1407 | if (d->drawBuf) { | ||
1408 | // printf("[%p] drawbuffer update %d\n", d, d->drawBuf->isValid); | ||
1409 | const iRect bounds = bounds_Widget(d); | ||
1410 | SDL_Renderer *render = renderer_Window(get_Window()); | ||
1411 | d->drawBuf->oldTarget = SDL_GetRenderTarget(render); | ||
1412 | d->drawBuf->oldOrigin = origin_Paint; | ||
1413 | realloc_WidgetDrawBuffer(d->drawBuf, render, boundsForDraw_Widget_(d).size); | ||
1414 | SDL_SetRenderTarget(render, d->drawBuf->texture); | ||
1415 | //SDL_SetRenderDrawColor(render, 255, 0, 0, 128); | ||
1416 | SDL_SetRenderDrawColor(render, 0, 0, 0, 0); | ||
1417 | SDL_RenderClear(render); | ||
1418 | origin_Paint = neg_I2(bounds.pos); /* with current visual offset */ | ||
1419 | // printf("beginBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y); | ||
1420 | // fflush(stdout); | ||
1421 | } | ||
1422 | } | ||
1423 | |||
1424 | static void endBufferDraw_Widget_(const iWidget *d) { | ||
1425 | if (d->drawBuf) { | ||
1426 | d->drawBuf->isValid = iTrue; | ||
1427 | SDL_SetRenderTarget(renderer_Window(get_Window()), d->drawBuf->oldTarget); | ||
1428 | origin_Paint = d->drawBuf->oldOrigin; | ||
1429 | // printf("endBufferDraw: origin %d,%d\n", origin_Paint.x, origin_Paint.y); | ||
1430 | // fflush(stdout); | ||
1431 | } | ||
1266 | } | 1432 | } |
1267 | 1433 | ||
1268 | void draw_Widget(const iWidget *d) { | 1434 | void draw_Widget(const iWidget *d) { |
1269 | drawBackground_Widget(d); | 1435 | if (!isDrawn_Widget_(d)) { |
1270 | drawChildren_Widget(d); | 1436 | if (d->drawBuf) { |
1437 | // printf("[%p] drawBuffer released\n", d); | ||
1438 | release_WidgetDrawBuffer(d->drawBuf); | ||
1439 | } | ||
1440 | return; | ||
1441 | } | ||
1442 | drawLayerEffects_Widget_(d); | ||
1443 | if (!d->drawBuf || !checkDrawBuffer_Widget_(d)) { | ||
1444 | beginBufferDraw_Widget_(d); | ||
1445 | drawBackground_Widget(d); | ||
1446 | drawChildren_Widget(d); | ||
1447 | endBufferDraw_Widget_(d); | ||
1448 | } | ||
1449 | if (d->drawBuf) { | ||
1450 | iAssert(d->drawBuf->isValid); | ||
1451 | const iRect bounds = bounds_Widget(d); | ||
1452 | SDL_RenderCopy(renderer_Window(get_Window()), d->drawBuf->texture, NULL, | ||
1453 | &(SDL_Rect){ bounds.pos.x, bounds.pos.y, | ||
1454 | d->drawBuf->size.x, d->drawBuf->size.y }); | ||
1455 | } | ||
1271 | } | 1456 | } |
1272 | 1457 | ||
1273 | iAny *addChild_Widget(iWidget *d, iAnyObject *child) { | 1458 | iAny *addChild_Widget(iWidget *d, iAnyObject *child) { |
@@ -1659,12 +1844,20 @@ void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) { | |||
1659 | deinit_String(&str); | 1844 | deinit_String(&str); |
1660 | } | 1845 | } |
1661 | 1846 | ||
1662 | void refresh_Widget(const iAnyObject *d) { | 1847 | void refresh_Widget(const iAnyObject *d) { |
1663 | /* TODO: Could be widget specific, if parts of the tree are cached. */ | 1848 | /* TODO: Could be widget specific, if parts of the tree are cached. */ |
1664 | /* TODO: The visbuffer in DocumentWidget and ListWidget could be moved to be a general | 1849 | /* TODO: The visbuffer in DocumentWidget and ListWidget could be moved to be a general |
1665 | purpose feature of Widget. */ | 1850 | purpose feature of Widget. */ |
1666 | iAssert(isInstance_Object(d, &Class_Widget)); | 1851 | iAssert(isInstance_Object(d, &Class_Widget)); |
1667 | iUnused(d); | 1852 | /* Mark draw buffers invalid. */ |
1853 | for (const iWidget *w = d; w; w = w->parent) { | ||
1854 | if (w->drawBuf) { | ||
1855 | // if (w->drawBuf->isValid) { | ||
1856 | // printf("[%p] drawbuffer invalidated by %p\n", w, d); fflush(stdout); | ||
1857 | // } | ||
1858 | w->drawBuf->isValid = iFalse; | ||
1859 | } | ||
1860 | } | ||
1668 | postRefresh_App(); | 1861 | postRefresh_App(); |
1669 | } | 1862 | } |
1670 | 1863 | ||
diff --git a/src/ui/widget.h b/src/ui/widget.h index 1a944c0a..fd4d8898 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -131,6 +131,8 @@ enum iWidgetFocusDir { | |||
131 | backward_WidgetFocusDir, | 131 | backward_WidgetFocusDir, |
132 | }; | 132 | }; |
133 | 133 | ||
134 | iDeclareType(WidgetDrawBuffer) | ||
135 | |||
134 | struct Impl_Widget { | 136 | struct Impl_Widget { |
135 | iObject object; | 137 | iObject object; |
136 | iString id; | 138 | iString id; |
@@ -148,6 +150,7 @@ struct Impl_Widget { | |||
148 | iWidget * parent; | 150 | iWidget * parent; |
149 | iBool (*commandHandler)(iWidget *, const char *); | 151 | iBool (*commandHandler)(iWidget *, const char *); |
150 | iRoot * root; | 152 | iRoot * root; |
153 | iWidgetDrawBuffer *drawBuf; | ||
151 | }; | 154 | }; |
152 | 155 | ||
153 | iDeclareObjectConstruction(Widget) | 156 | iDeclareObjectConstruction(Widget) |
@@ -203,6 +206,12 @@ size_t childCount_Widget (const iWidget *); | |||
203 | void draw_Widget (const iWidget *); | 206 | void draw_Widget (const iWidget *); |
204 | void drawBackground_Widget (const iWidget *); | 207 | void drawBackground_Widget (const iWidget *); |
205 | void drawChildren_Widget (const iWidget *); | 208 | void drawChildren_Widget (const iWidget *); |
209 | void drawRoot_Widget (const iWidget *); /* root only */ | ||
210 | void setDrawBufferEnabled_Widget (iWidget *, iBool enable); | ||
211 | |||
212 | iLocalDef iBool isDrawBufferEnabled_Widget(const iWidget *d) { | ||
213 | return d && d->drawBuf; | ||
214 | } | ||
206 | 215 | ||
207 | iLocalDef int width_Widget(const iAnyObject *d) { | 216 | iLocalDef int width_Widget(const iAnyObject *d) { |
208 | if (d) { | 217 | if (d) { |
diff --git a/src/ui/window.c b/src/ui/window.c index 096853cc..3385f436 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -1060,12 +1060,13 @@ void draw_Window(iWindow *d) { | |||
1060 | d->frameTime = SDL_GetTicks(); | 1060 | d->frameTime = SDL_GetTicks(); |
1061 | if (isExposed_Window(d)) { | 1061 | if (isExposed_Window(d)) { |
1062 | d->isInvalidated = iFalse; | 1062 | d->isInvalidated = iFalse; |
1063 | extern int drawCount_; | ||
1063 | iForIndices(i, d->roots) { | 1064 | iForIndices(i, d->roots) { |
1064 | iRoot *root = d->roots[i]; | 1065 | iRoot *root = d->roots[i]; |
1065 | if (root) { | 1066 | if (root) { |
1066 | setCurrent_Root(root); | 1067 | setCurrent_Root(root); |
1067 | unsetClip_Paint(&p); /* update clip to current root */ | 1068 | unsetClip_Paint(&p); /* update clip to current root */ |
1068 | draw_Widget(root->widget); | 1069 | drawRoot_Widget(root->widget); |
1069 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) | 1070 | #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) |
1070 | /* App icon. */ | 1071 | /* App icon. */ |
1071 | const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon"); | 1072 | const iWidget *appIcon = findChild_Widget(root->widget, "winbar.icon"); |
@@ -1105,6 +1106,10 @@ void draw_Window(iWindow *d) { | |||
1105 | } | 1106 | } |
1106 | } | 1107 | } |
1107 | setCurrent_Root(NULL); | 1108 | setCurrent_Root(NULL); |
1109 | #if !defined (NDEBUG) | ||
1110 | draw_Text(defaultBold_FontId, zero_I2(), red_ColorId, "%d", drawCount_); | ||
1111 | drawCount_ = 0; | ||
1112 | #endif | ||
1108 | } | 1113 | } |
1109 | #if 0 | 1114 | #if 0 |
1110 | /* Text cache debugging. */ { | 1115 | /* Text cache debugging. */ { |