diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-12 13:49:38 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-12 13:49:38 +0300 |
commit | 33620846cca5678fbd662ea1a48fad302727dae7 (patch) | |
tree | 322fb4ab2c1dd7dd3eceee0834f3e90d27838de4 | |
parent | 1410bbde7779efe3a20f603523547c8b8f55b6a1 (diff) |
Mobile: Draw optimizations; focus handling
Widgets can now be marked for buffering their contents, which is useful if their contents change seldom but they are drawn often.
For example, the navbar is always visible but doesn't change very often, and during animations menu contents are static but there is a moving animation so everything gets drawn 60 FPS.
Focus handling was also improved so the lookup results can be scrolled while entering text, and one can tap outside an input field to unfocus it.
-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. */ { |