diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-09 10:42:23 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-09 10:42:23 +0200 |
commit | 0585c687a38f0d177ec4db48567ea08326094111 (patch) | |
tree | ba1feafdda3a1258f7baa18edbe7eeaf97ff5bd8 /src | |
parent | 59363d3e230a0e9702e31936309473fb576ecec5 (diff) | |
parent | 4ae755de3ff4a37763aacc22ea119edab2099e84 (diff) |
Merge branch 'dev' into work/v1.11
# Conflicts:
# res/lang/eo.bin
# res/lang/ie.bin
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 25 | ||||
-rw-r--r-- | src/gmdocument.c | 11 | ||||
-rw-r--r-- | src/gmdocument.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 50 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 17 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 2 | ||||
-rw-r--r-- | src/ui/linkinfo.c | 2 | ||||
-rw-r--r-- | src/ui/mediaui.c | 4 | ||||
-rw-r--r-- | src/ui/paint.c | 3 | ||||
-rw-r--r-- | src/ui/scrollwidget.c | 20 | ||||
-rw-r--r-- | src/ui/text.c | 61 | ||||
-rw-r--r-- | src/ui/util.c | 2 | ||||
-rw-r--r-- | src/ui/widget.c | 4 | ||||
-rw-r--r-- | src/ui/window.c | 38 | ||||
-rw-r--r-- | src/ui/window.h | 2 |
15 files changed, 174 insertions, 69 deletions
@@ -1257,7 +1257,7 @@ iLocalDef iBool isWaitingAllowed_App_(iApp *d) { | |||
1257 | return iFalse; | 1257 | return iFalse; |
1258 | } | 1258 | } |
1259 | #endif | 1259 | #endif |
1260 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); | 1260 | return !isRefreshPending_App(); |
1261 | } | 1261 | } |
1262 | 1262 | ||
1263 | static iBool nextEvent_App_(iApp *d, enum iAppEventMode eventMode, SDL_Event *event) { | 1263 | static iBool nextEvent_App_(iApp *d, enum iAppEventMode eventMode, SDL_Event *event) { |
@@ -1564,7 +1564,6 @@ static void runTickers_App_(iApp *d) { | |||
1564 | /* Tickers may add themselves again, so we'll run off a copy. */ | 1564 | /* Tickers may add themselves again, so we'll run off a copy. */ |
1565 | iSortedArray *pending = copy_SortedArray(&d->tickers); | 1565 | iSortedArray *pending = copy_SortedArray(&d->tickers); |
1566 | clear_SortedArray(&d->tickers); | 1566 | clear_SortedArray(&d->tickers); |
1567 | postRefresh_App(); | ||
1568 | iConstForEach(Array, i, &pending->values) { | 1567 | iConstForEach(Array, i, &pending->values) { |
1569 | const iTicker *ticker = i.value; | 1568 | const iTicker *ticker = i.value; |
1570 | if (ticker->callback) { | 1569 | if (ticker->callback) { |
@@ -1577,12 +1576,6 @@ static void runTickers_App_(iApp *d) { | |||
1577 | if (isEmpty_SortedArray(&d->tickers)) { | 1576 | if (isEmpty_SortedArray(&d->tickers)) { |
1578 | d->lastTickerTime = 0; | 1577 | d->lastTickerTime = 0; |
1579 | } | 1578 | } |
1580 | // iForIndices(i, d->window->base.roots) { | ||
1581 | // iRoot *root = d->window->base.roots[i]; | ||
1582 | // if (root) { | ||
1583 | // notifyVisualOffsetChange_Root(root); | ||
1584 | // } | ||
1585 | // } | ||
1586 | } | 1579 | } |
1587 | 1580 | ||
1588 | static int resizeWatcher_(void *user, SDL_Event *event) { | 1581 | static int resizeWatcher_(void *user, SDL_Event *event) { |
@@ -1649,19 +1642,20 @@ void refresh_App(void) { | |||
1649 | } | 1642 | } |
1650 | } | 1643 | } |
1651 | /* TODO: `pendingRefresh` should be window-specific. */ | 1644 | /* TODO: `pendingRefresh` should be window-specific. */ |
1652 | if (exchange_Atomic(&d->pendingRefresh, iFalse)) { | 1645 | if (d->warmupFrames || exchange_Atomic(&d->pendingRefresh, iFalse)) { |
1653 | /* Draw each window. */ | 1646 | /* Draw each window. */ |
1654 | iConstForEach(PtrArray, j, &windows) { | 1647 | iConstForEach(PtrArray, j, &windows) { |
1655 | iWindow *win = j.ptr; | 1648 | iWindow *win = j.ptr; |
1656 | setCurrent_Window(win); | 1649 | setCurrent_Window(win); |
1657 | switch (win->type) { | 1650 | switch (win->type) { |
1658 | case main_WindowType: | 1651 | case main_WindowType: { |
1659 | // iTime draw; | 1652 | // iTime draw; |
1660 | // initCurrent_Time(&draw); | 1653 | // initCurrent_Time(&draw); |
1661 | draw_MainWindow(as_MainWindow(win)); | 1654 | draw_MainWindow(as_MainWindow(win)); |
1662 | // printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000)); | 1655 | // printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000)); |
1663 | // fflush(stdout); | 1656 | // fflush(stdout); |
1664 | break; | 1657 | break; |
1658 | } | ||
1665 | default: | 1659 | default: |
1666 | draw_Window(win); | 1660 | draw_Window(win); |
1667 | break; | 1661 | break; |
@@ -1675,7 +1669,8 @@ void refresh_App(void) { | |||
1675 | } | 1669 | } |
1676 | 1670 | ||
1677 | iBool isRefreshPending_App(void) { | 1671 | iBool isRefreshPending_App(void) { |
1678 | return value_Atomic(&app_.pendingRefresh); | 1672 | const iApp *d = &app_; |
1673 | return !isEmpty_SortedArray(&d->tickers) || value_Atomic(&app_.pendingRefresh); | ||
1679 | } | 1674 | } |
1680 | 1675 | ||
1681 | iBool isFinishedLaunching_App(void) { | 1676 | iBool isFinishedLaunching_App(void) { |
diff --git a/src/gmdocument.c b/src/gmdocument.c index 69fe77f8..546dc6f5 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -316,7 +316,8 @@ static iBool isAllowedLinkIcon_Char_(iChar icon) { | |||
316 | icon == 0x2022 /* bullet */ || | 316 | icon == 0x2022 /* bullet */ || |
317 | icon == 0x2139 /* info */ || | 317 | icon == 0x2139 /* info */ || |
318 | (icon >= 0x2190 && icon <= 0x21ff /* arrows */) || | 318 | (icon >= 0x2190 && icon <= 0x21ff /* arrows */) || |
319 | icon == 0x2a2f /* close X */ || icon == 0x2b50; | 319 | icon == 0x2a2f /* close X */ || |
320 | (icon >= 0x2b00 && icon <= 0x2bff); | ||
320 | } | 321 | } |
321 | 322 | ||
322 | static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *linkId) { | 323 | static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *linkId) { |
@@ -1097,7 +1098,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
1097 | /* Image metadata caption. */ { | 1098 | /* Image metadata caption. */ { |
1098 | run.font = FONT_ID(documentBody_FontId, semiBold_FontStyle, contentSmall_FontSize); | 1099 | run.font = FONT_ID(documentBody_FontId, semiBold_FontStyle, contentSmall_FontSize); |
1099 | run.color = tmQuoteIcon_ColorId; | 1100 | run.color = tmQuoteIcon_ColorId; |
1100 | run.flags = decoration_GmRunFlag; | 1101 | run.flags = decoration_GmRunFlag | caption_GmRunFlag; |
1101 | run.mediaId = 0; | 1102 | run.mediaId = 0; |
1102 | run.mediaType = 0; | 1103 | run.mediaType = 0; |
1103 | run.visBounds.pos.y = pos.y; | 1104 | run.visBounds.pos.y = pos.y; |
@@ -2212,7 +2213,7 @@ const iGmRun *renderProgressive_GmDocument(const iGmDocument *d, const iGmRun *f | |||
2212 | setAnsiFlags_Text(d->theme.ansiEscapes); | 2213 | setAnsiFlags_Text(d->theme.ansiEscapes); |
2213 | const iGmRun *run = first; | 2214 | const iGmRun *run = first; |
2214 | while (isValidRun_GmDocument_(d, run)) { | 2215 | while (isValidRun_GmDocument_(d, run)) { |
2215 | if ((dir < 0 && bottom_Rect(run->visBounds) < visRangeY.start) || | 2216 | if ((dir < 0 && bottom_Rect(run->visBounds) <= visRangeY.start) || |
2216 | (dir > 0 && top_Rect(run->visBounds) >= visRangeY.end)) { | 2217 | (dir > 0 && top_Rect(run->visBounds) >= visRangeY.end)) { |
2217 | break; | 2218 | break; |
2218 | } | 2219 | } |
@@ -2263,6 +2264,10 @@ const iString *source_GmDocument(const iGmDocument *d) { | |||
2263 | return &d->source; | 2264 | return &d->source; |
2264 | } | 2265 | } |
2265 | 2266 | ||
2267 | iGmRunRange runRange_GmDocument(const iGmDocument *d) { | ||
2268 | return (iGmRunRange){ constFront_Array(&d->layout), constEnd_Array(&d->layout) }; | ||
2269 | } | ||
2270 | |||
2266 | size_t memorySize_GmDocument(const iGmDocument *d) { | 2271 | size_t memorySize_GmDocument(const iGmDocument *d) { |
2267 | return size_String(&d->unormSource) + | 2272 | return size_String(&d->unormSource) + |
2268 | size_String(&d->source) + | 2273 | size_String(&d->source) + |
diff --git a/src/gmdocument.h b/src/gmdocument.h index eb02a26c..0969794c 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -122,6 +122,7 @@ enum iGmRunFlags { | |||
122 | endOfLine_GmRunFlag = iBit(3), | 122 | endOfLine_GmRunFlag = iBit(3), |
123 | quoteBorder_GmRunFlag = iBit(5), | 123 | quoteBorder_GmRunFlag = iBit(5), |
124 | wide_GmRunFlag = iBit(6), /* horizontally scrollable */ | 124 | wide_GmRunFlag = iBit(6), /* horizontally scrollable */ |
125 | caption_GmRunFlag = iBit(7), | ||
125 | altText_GmRunFlag = iBit(8), | 126 | altText_GmRunFlag = iBit(8), |
126 | }; | 127 | }; |
127 | 128 | ||
@@ -211,6 +212,7 @@ const iGmRun * renderProgressive_GmDocument(const iGmDocument *d, const iGmRun | |||
211 | iInt2 size_GmDocument (const iGmDocument *); | 212 | iInt2 size_GmDocument (const iGmDocument *); |
212 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ | 213 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ |
213 | const iString * source_GmDocument (const iGmDocument *); | 214 | const iString * source_GmDocument (const iGmDocument *); |
215 | iGmRunRange runRange_GmDocument (const iGmDocument *); | ||
214 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ | 216 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ |
215 | int warnings_GmDocument (const iGmDocument *); | 217 | int warnings_GmDocument (const iGmDocument *); |
216 | 218 | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index b19958b2..3d2e31b2 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1333,7 +1333,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1333 | run->text); | 1333 | run->text); |
1334 | } | 1334 | } |
1335 | else { | 1335 | else { |
1336 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { | 1336 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag && |
1337 | ~run->flags & caption_GmRunFlag) { | ||
1337 | const size_t ord = visibleLinkOrdinal_DocumentView_(d->view, run->linkId); | 1338 | const size_t ord = visibleLinkOrdinal_DocumentView_(d->view, run->linkId); |
1338 | if (ord >= d->view->owner->ordinalBase) { | 1339 | if (ord >= d->view->owner->ordinalBase) { |
1339 | const iChar ordChar = | 1340 | const iChar ordChar = |
@@ -1656,7 +1657,6 @@ static iBool render_DocumentView_(const iDocumentView *d, iDrawContext *ctx, iBo | |||
1656 | ctx->viewPos = init_I2(left_Rect(ctx->docBounds) - left_Rect(bounds), -buf->origin); | 1657 | ctx->viewPos = init_I2(left_Rect(ctx->docBounds) - left_Rect(bounds), -buf->origin); |
1657 | // printf(" buffer %zu: buf vis range %d...%d\n", i, bufVisRange.start, bufVisRange.end); | 1658 | // printf(" buffer %zu: buf vis range %d...%d\n", i, bufVisRange.start, bufVisRange.end); |
1658 | if (!prerenderExtra && !isEmpty_Range(&bufVisRange)) { | 1659 | if (!prerenderExtra && !isEmpty_Range(&bufVisRange)) { |
1659 | didDraw = iTrue; | ||
1660 | if (isEmpty_Rangei(buf->validRange)) { | 1660 | if (isEmpty_Rangei(buf->validRange)) { |
1661 | /* Fill the required currently visible range (vis). */ | 1661 | /* Fill the required currently visible range (vis). */ |
1662 | const iRangei bufVisRange = intersect_Rangei(bufRange, vis); | 1662 | const iRangei bufVisRange = intersect_Rangei(bufRange, vis); |
@@ -1669,26 +1669,46 @@ static iBool render_DocumentView_(const iDocumentView *d, iDrawContext *ctx, iBo | |||
1669 | extend_GmRunRange_(&meta->runsDrawn); | 1669 | extend_GmRunRange_(&meta->runsDrawn); |
1670 | buf->validRange = bufVisRange; | 1670 | buf->validRange = bufVisRange; |
1671 | // printf(" buffer %zu valid %d...%d\n", i, bufRange.start, bufRange.end); | 1671 | // printf(" buffer %zu valid %d...%d\n", i, bufRange.start, bufRange.end); |
1672 | didDraw = iTrue; | ||
1672 | } | 1673 | } |
1673 | } | 1674 | } |
1674 | else { | 1675 | else { |
1675 | /* Progressively fill the required runs. */ | 1676 | /* Progressively fill the required runs. */ |
1676 | if (meta->runsDrawn.start) { | 1677 | if (meta->runsDrawn.start && buf->validRange.start > bufRange.start) { |
1677 | beginTarget_Paint(p, buf->texture); | 1678 | beginTarget_Paint(p, buf->texture); |
1678 | meta->runsDrawn.start = renderProgressive_GmDocument(d->doc, meta->runsDrawn.start, | 1679 | iZap(ctx->runsDrawn); |
1679 | -1, iInvalidSize, | 1680 | const iGmRun *newStart = renderProgressive_GmDocument(d->doc, |
1680 | bufVisRange, | 1681 | meta->runsDrawn.start, |
1681 | drawRun_DrawContext_, | 1682 | -1, |
1682 | ctx); | 1683 | iInvalidSize, |
1683 | buf->validRange.start = bufVisRange.start; | 1684 | bufVisRange, |
1685 | drawRun_DrawContext_, | ||
1686 | ctx); | ||
1687 | if (ctx->runsDrawn.start) { | ||
1688 | /* Something was actually drawn, so update the valid range. */ | ||
1689 | const int newTop = top_Rect(ctx->runsDrawn.start->visBounds); | ||
1690 | if (newTop != buf->validRange.start) { | ||
1691 | didDraw = iTrue; | ||
1692 | // printf("render: valid:%d->%d run:%p->%p\n", | ||
1693 | // buf->validRange.start, newTop, | ||
1694 | // meta->runsDrawn.start, | ||
1695 | // ctx->runsDrawn.start); fflush(stdout); | ||
1696 | buf->validRange.start = newTop; | ||
1697 | } | ||
1698 | meta->runsDrawn.start = newStart; | ||
1699 | } | ||
1684 | } | 1700 | } |
1685 | if (meta->runsDrawn.end) { | 1701 | if (meta->runsDrawn.end) { |
1686 | beginTarget_Paint(p, buf->texture); | 1702 | beginTarget_Paint(p, buf->texture); |
1703 | iZap(ctx->runsDrawn); | ||
1687 | meta->runsDrawn.end = renderProgressive_GmDocument(d->doc, meta->runsDrawn.end, | 1704 | meta->runsDrawn.end = renderProgressive_GmDocument(d->doc, meta->runsDrawn.end, |
1688 | +1, iInvalidSize, | 1705 | +1, iInvalidSize, |
1689 | bufVisRange, | 1706 | bufVisRange, |
1690 | drawRun_DrawContext_, | 1707 | drawRun_DrawContext_, |
1691 | ctx); | 1708 | ctx); |
1709 | if (ctx->runsDrawn.start) { | ||
1710 | didDraw = iTrue; | ||
1711 | } | ||
1692 | buf->validRange.end = bufVisRange.end; | 1712 | buf->validRange.end = bufVisRange.end; |
1693 | } | 1713 | } |
1694 | } | 1714 | } |
@@ -3109,12 +3129,14 @@ static const char *humanReadableStatusCode_(enum iGmStatusCode code) { | |||
3109 | return format_CStr("%d ", code); | 3129 | return format_CStr("%d ", code); |
3110 | } | 3130 | } |
3111 | 3131 | ||
3112 | static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { | 3132 | static iBool setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { |
3113 | url = canonicalUrl_String(url); | 3133 | url = canonicalUrl_String(url); |
3114 | if (!equal_String(d->mod.url, url)) { | 3134 | if (!equal_String(d->mod.url, url)) { |
3115 | d->flags |= urlChanged_DocumentWidgetFlag; | 3135 | d->flags |= urlChanged_DocumentWidgetFlag; |
3116 | set_String(d->mod.url, url); | 3136 | set_String(d->mod.url, url); |
3137 | return iTrue; | ||
3117 | } | 3138 | } |
3139 | return iFalse; | ||
3118 | } | 3140 | } |
3119 | 3141 | ||
3120 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | 3142 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { |
@@ -3139,7 +3161,9 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
3139 | iMediaRequest *mr = newReused_MediaRequest(d, d->requestLinkId, d->request); | 3161 | iMediaRequest *mr = newReused_MediaRequest(d, d->requestLinkId, d->request); |
3140 | unlockResponse_GmRequest(d->request); | 3162 | unlockResponse_GmRequest(d->request); |
3141 | d->request = NULL; /* ownership moved */ | 3163 | d->request = NULL; /* ownership moved */ |
3142 | postCommand_Widget(d, "document.request.cancelled doc:%p", d); | 3164 | if (!isFinished_GmRequest(mr->req)) { |
3165 | postCommand_Widget(d, "document.request.cancelled doc:%p", d); | ||
3166 | } | ||
3143 | pushBack_ObjectList(d->media, mr); | 3167 | pushBack_ObjectList(d->media, mr); |
3144 | iRelease(mr); | 3168 | iRelease(mr); |
3145 | /* Reset the fetch state, returning to the originating page. */ | 3169 | /* Reset the fetch state, returning to the originating page. */ |
@@ -3147,7 +3171,9 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
3147 | if (equal_String(&mostRecentUrl_History(d->mod.history)->url, url_GmRequest(mr->req))) { | 3171 | if (equal_String(&mostRecentUrl_History(d->mod.history)->url, url_GmRequest(mr->req))) { |
3148 | undo_History(d->mod.history); | 3172 | undo_History(d->mod.history); |
3149 | } | 3173 | } |
3150 | setUrl_DocumentWidget_(d, url_GmDocument(d->view.doc)); | 3174 | if (setUrl_DocumentWidget_(d, url_GmDocument(d->view.doc))) { |
3175 | postCommand_Widget(d, "!document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | ||
3176 | } | ||
3151 | updateFetchProgress_DocumentWidget_(d); | 3177 | updateFetchProgress_DocumentWidget_(d); |
3152 | postCommand_Widget(d, "media.updated link:%u request:%p", d->requestLinkId, mr); | 3178 | postCommand_Widget(d, "media.updated link:%u request:%p", d->requestLinkId, mr); |
3153 | if (isFinished_GmRequest(mr->req)) { | 3179 | if (isFinished_GmRequest(mr->req)) { |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index aa55f3f0..6a8d428a 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -1115,7 +1115,14 @@ static void updateBuffered_InputWidget_(iInputWidget *d) { | |||
1115 | } | 1115 | } |
1116 | 1116 | ||
1117 | void setText_InputWidget(iInputWidget *d, const iString *text) { | 1117 | void setText_InputWidget(iInputWidget *d, const iString *text) { |
1118 | setTextUndoable_InputWidget(d, text, iFalse); | ||
1119 | } | ||
1120 | |||
1121 | void setTextUndoable_InputWidget(iInputWidget *d, const iString *text, iBool isUndoable) { | ||
1118 | if (!d) return; | 1122 | if (!d) return; |
1123 | if (isUndoable) { | ||
1124 | pushUndo_InputWidget_(d); | ||
1125 | } | ||
1119 | if (d->inFlags & isUrl_InputWidgetFlag) { | 1126 | if (d->inFlags & isUrl_InputWidgetFlag) { |
1120 | if (prefs_App()->decodeUserVisibleURLs) { | 1127 | if (prefs_App()->decodeUserVisibleURLs) { |
1121 | iString *enc = collect_String(copy_String(text)); | 1128 | iString *enc = collect_String(copy_String(text)); |
@@ -1139,7 +1146,9 @@ void setText_InputWidget(iInputWidget *d, const iString *text) { | |||
1139 | iString *nfcText = collect_String(copy_String(text)); | 1146 | iString *nfcText = collect_String(copy_String(text)); |
1140 | normalize_String(nfcText); | 1147 | normalize_String(nfcText); |
1141 | #if !LAGRANGE_USE_SYSTEM_TEXT_INPUT | 1148 | #if !LAGRANGE_USE_SYSTEM_TEXT_INPUT |
1142 | clearUndo_InputWidget_(d); | 1149 | if (!isUndoable) { |
1150 | clearUndo_InputWidget_(d); | ||
1151 | } | ||
1143 | splitToLines_(nfcText, &d->lines); | 1152 | splitToLines_(nfcText, &d->lines); |
1144 | iAssert(!isEmpty_Array(&d->lines)); | 1153 | iAssert(!isEmpty_Array(&d->lines)); |
1145 | iForEach(Array, i, &d->lines) { | 1154 | iForEach(Array, i, &d->lines) { |
@@ -1175,6 +1184,12 @@ void setTextCStr_InputWidget(iInputWidget *d, const char *cstr) { | |||
1175 | delete_String(str); | 1184 | delete_String(str); |
1176 | } | 1185 | } |
1177 | 1186 | ||
1187 | void setTextUndoableCStr_InputWidget(iInputWidget *d, const char *cstr, iBool isUndoable) { | ||
1188 | iString *str = newCStr_String(cstr); | ||
1189 | setTextUndoable_InputWidget(d, str, isUndoable); | ||
1190 | delete_String(str); | ||
1191 | } | ||
1192 | |||
1178 | void selectAll_InputWidget(iInputWidget *d) { | 1193 | void selectAll_InputWidget(iInputWidget *d) { |
1179 | #if LAGRANGE_USE_SYSTEM_TEXT_INPUT | 1194 | #if LAGRANGE_USE_SYSTEM_TEXT_INPUT |
1180 | if (d->sysCtrl) { | 1195 | if (d->sysCtrl) { |
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index 5a61ec22..000fa4b7 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -46,6 +46,8 @@ void setMode_InputWidget (iInputWidget *, enum iInputMode mode); | |||
46 | void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); | 46 | void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); |
47 | void setText_InputWidget (iInputWidget *, const iString *text); | 47 | void setText_InputWidget (iInputWidget *, const iString *text); |
48 | void setTextCStr_InputWidget (iInputWidget *, const char *cstr); | 48 | void setTextCStr_InputWidget (iInputWidget *, const char *cstr); |
49 | void setTextUndoable_InputWidget (iInputWidget *, const iString *text, iBool isUndoable); | ||
50 | void setTextUndoableCStr_InputWidget (iInputWidget *, const char *cstr, iBool isUndoable); | ||
49 | void setFont_InputWidget (iInputWidget *, int fontId); | 51 | void setFont_InputWidget (iInputWidget *, int fontId); |
50 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ | 52 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ |
51 | void setLineLimits_InputWidget (iInputWidget *, int minLines, int maxLines); | 53 | void setLineLimits_InputWidget (iInputWidget *, int minLines, int maxLines); |
diff --git a/src/ui/linkinfo.c b/src/ui/linkinfo.c index 8974a486..72d76678 100644 --- a/src/ui/linkinfo.c +++ b/src/ui/linkinfo.c | |||
@@ -101,7 +101,7 @@ void infoText_LinkInfo(const iGmDocument *doc, iGmLinkId linkId, iString *text_o | |||
101 | } | 101 | } |
102 | else if (scheme != gemini_GmLinkScheme) { | 102 | else if (scheme != gemini_GmLinkScheme) { |
103 | appendCStr_String(text_out, scheme == file_GmLinkScheme ? "" : globe_Icon " "); | 103 | appendCStr_String(text_out, scheme == file_GmLinkScheme ? "" : globe_Icon " "); |
104 | append_String(text_out, url); | 104 | appendCStrN_String(text_out, cstr_String(url), iMin(500, size_String(url))); |
105 | } | 105 | } |
106 | else { | 106 | else { |
107 | appendCStr_String(text_out, "\x1b[1m"); | 107 | appendCStr_String(text_out, "\x1b[1m"); |
diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c index f0070688..2ddebb57 100644 --- a/src/ui/mediaui.c +++ b/src/ui/mediaui.c | |||
@@ -104,7 +104,7 @@ static int drawSevenSegmentTime_(iInt2 pos, int color, int align, int seconds) { | |||
104 | if (align == right_Alignment) { | 104 | if (align == right_Alignment) { |
105 | pos.x -= size.x; | 105 | pos.x -= size.x; |
106 | } | 106 | } |
107 | drawRange_Text(font, addY_I2(pos, gap_UI / 2), color, range_String(&num)); | 107 | drawRange_Text(font, addY_I2(pos, 0/*gap_UI / 2*/), color, range_String(&num)); |
108 | deinit_String(&num); | 108 | deinit_String(&num); |
109 | return size.x; | 109 | return size.x; |
110 | } | 110 | } |
@@ -316,7 +316,7 @@ void draw_DownloadUI(const iDownloadUI *d, iPaint *p) { | |||
316 | isFinished ? uiTextAction_ColorId : uiTextDim_ColorId, | 316 | isFinished ? uiTextAction_ColorId : uiTextDim_ColorId, |
317 | cstr_Lang(isFinished ? "media.download.complete" : "media.download.warnclose")); | 317 | cstr_Lang(isFinished ? "media.download.complete" : "media.download.warnclose")); |
318 | const int x2 = right_Rect(rect); | 318 | const int x2 = right_Rect(rect); |
319 | drawSevenSegmentBytes_MediaUI(uiLabel_FontId, init_I2(x2, y1), | 319 | drawSevenSegmentBytes_MediaUI(uiContent_FontId, init_I2(x2, y1), |
320 | uiTextStrong_ColorId, uiTextDim_ColorId, | 320 | uiTextStrong_ColorId, uiTextDim_ColorId, |
321 | info.numBytes); | 321 | info.numBytes); |
322 | const iInt2 pos = init_I2(x2, y2); | 322 | const iInt2 pos = init_I2(x2, y2); |
diff --git a/src/ui/paint.c b/src/ui/paint.c index 67b509fb..5f19ec3e 100644 --- a/src/ui/paint.c +++ b/src/ui/paint.c | |||
@@ -77,7 +77,8 @@ void setClip_Paint(iPaint *d, iRect rect) { | |||
77 | SDL_QueryTexture(target, NULL, NULL, &targetRect.size.x, &targetRect.size.y); | 77 | SDL_QueryTexture(target, NULL, NULL, &targetRect.size.x, &targetRect.size.y); |
78 | rect = intersect_Rect(rect, targetRect); | 78 | rect = intersect_Rect(rect, targetRect); |
79 | } | 79 | } |
80 | else { | 80 | /* The origin is non-zero when drawing into a widget's own buffer. */ |
81 | if (isEqual_I2(zero_I2(), origin_Paint)) { | ||
81 | rect = intersect_Rect(rect, rect_Root(get_Root())); | 82 | rect = intersect_Rect(rect, rect_Root(get_Root())); |
82 | } | 83 | } |
83 | if (isEmpty_Rect(rect)) { | 84 | if (isEmpty_Rect(rect)) { |
diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c index 651669c6..71e4873f 100644 --- a/src/ui/scrollwidget.c +++ b/src/ui/scrollwidget.c | |||
@@ -239,15 +239,17 @@ static void draw_ScrollWidget_(const iScrollWidget *d) { | |||
239 | init_Paint(&p); | 239 | init_Paint(&p); |
240 | /* Blend if opacity is not at maximum. */ | 240 | /* Blend if opacity is not at maximum. */ |
241 | p.alpha = 255 * value_Anim(&d->opacity); | 241 | p.alpha = 255 * value_Anim(&d->opacity); |
242 | SDL_Renderer *render = renderer_Window(get_Window()); | 242 | if (p.alpha > 0) { |
243 | if (p.alpha < 255) { | 243 | SDL_Renderer *render = renderer_Window(get_Window()); |
244 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND); | 244 | if (p.alpha < 255) { |
245 | } | 245 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND); |
246 | const iRect thumbRect = shrunk_Rect( | 246 | } |
247 | thumbRect_ScrollWidget_(d), init_I2(isPressed ? gap_UI : (gap_UI * 4 / 3), gap_UI / 2)); | 247 | const iRect thumbRect = shrunk_Rect( |
248 | fillRect_Paint(&p, thumbRect, isPressed ? uiBackgroundPressed_ColorId : tmQuote_ColorId); | 248 | thumbRect_ScrollWidget_(d), init_I2(isPressed ? gap_UI : (gap_UI * 4 / 3), gap_UI / 2)); |
249 | if (p.alpha < 255) { | 249 | fillRect_Paint(&p, thumbRect, isPressed ? uiBackgroundPressed_ColorId : tmQuote_ColorId); |
250 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); | 250 | if (p.alpha < 255) { |
251 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); | ||
252 | } | ||
251 | } | 253 | } |
252 | } | 254 | } |
253 | } | 255 | } |
diff --git a/src/ui/text.c b/src/ui/text.c index ab2af2b2..51531057 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -246,10 +246,22 @@ struct Impl_CacheRow { | |||
246 | iInt2 pos; | 246 | iInt2 pos; |
247 | }; | 247 | }; |
248 | 248 | ||
249 | iDeclareType(PrioMapItem) | ||
250 | struct Impl_PrioMapItem { | ||
251 | int priority; | ||
252 | uint32_t fontIndex; | ||
253 | }; | ||
254 | |||
255 | static int cmp_PrioMapItem_(const void *a, const void *b) { | ||
256 | const iPrioMapItem *i = a, *j = b; | ||
257 | return -iCmp(i->priority, j->priority); | ||
258 | } | ||
259 | |||
249 | struct Impl_Text { | 260 | struct Impl_Text { |
250 | float contentFontSize; | 261 | float contentFontSize; |
251 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ | 262 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ |
252 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ | 263 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ |
264 | iArray fontPriorityOrder; | ||
253 | SDL_Renderer * render; | 265 | SDL_Renderer * render; |
254 | SDL_Texture * cache; | 266 | SDL_Texture * cache; |
255 | iInt2 cacheSize; | 267 | iInt2 cacheSize; |
@@ -283,8 +295,9 @@ static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId) | |||
283 | /* This is the highest priority override font. */ | 295 | /* This is the highest priority override font. */ |
284 | d->overrideFontId = baseId; | 296 | d->overrideFontId = baseId; |
285 | } | 297 | } |
298 | pushBack_Array(&d->fontPriorityOrder, &(iPrioMapItem){ spec->priority, baseId }); | ||
286 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { | 299 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { |
287 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { | 300 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { |
288 | init_Font(font_Text_(FONT_ID(baseId, style, sizeId)), | 301 | init_Font(font_Text_(FONT_ID(baseId, style, sizeId)), |
289 | spec, | 302 | spec, |
290 | spec->styles[style], | 303 | spec->styles[style], |
@@ -321,6 +334,7 @@ static void initFonts_Text_(iText *d) { | |||
321 | and styles for each available font. Indices to `fonts` act as font runtime IDs. */ | 334 | and styles for each available font. Indices to `fonts` act as font runtime IDs. */ |
322 | /* First the mandatory fonts. */ | 335 | /* First the mandatory fonts. */ |
323 | d->overrideFontId = -1; | 336 | d->overrideFontId = -1; |
337 | clear_Array(&d->fontPriorityOrder); | ||
324 | resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */ | 338 | resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */ |
325 | setupFontVariants_Text_(d, tryFindSpec_(uiFont_PrefsString, "default"), default_FontId); | 339 | setupFontVariants_Text_(d, tryFindSpec_(uiFont_PrefsString, "default"), default_FontId); |
326 | setupFontVariants_Text_(d, tryFindSpec_(monospaceFont_PrefsString, "iosevka"), monospace_FontId); | 340 | setupFontVariants_Text_(d, tryFindSpec_(monospaceFont_PrefsString, "iosevka"), monospace_FontId); |
@@ -330,12 +344,14 @@ static void initFonts_Text_(iText *d) { | |||
330 | /* Check if there are auxiliary fonts available and set those up, too. */ | 344 | /* Check if there are auxiliary fonts available and set those up, too. */ |
331 | iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) { | 345 | iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) { |
332 | const iFontSpec *spec = s.ptr; | 346 | const iFontSpec *spec = s.ptr; |
347 | // printf("spec '%s': prio=%d\n", cstr_String(&spec->name), spec->priority); | ||
333 | if (spec->flags & (auxiliary_FontSpecFlag | user_FontSpecFlag)) { | 348 | if (spec->flags & (auxiliary_FontSpecFlag | user_FontSpecFlag)) { |
334 | const int fontId = size_Array(&d->fonts); | 349 | const int fontId = size_Array(&d->fonts); |
335 | resize_Array(&d->fonts, fontId + maxVariants_Fonts); | 350 | resize_Array(&d->fonts, fontId + maxVariants_Fonts); |
336 | setupFontVariants_Text_(d, spec, fontId); | 351 | setupFontVariants_Text_(d, spec, fontId); |
337 | } | 352 | } |
338 | } | 353 | } |
354 | sort_Array(&d->fontPriorityOrder, cmp_PrioMapItem_); | ||
339 | #if !defined (NDEBUG) | 355 | #if !defined (NDEBUG) |
340 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); | 356 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); |
341 | #endif | 357 | #endif |
@@ -403,6 +419,7 @@ void init_Text(iText *d, SDL_Renderer *render) { | |||
403 | iText *oldActive = activeText_; | 419 | iText *oldActive = activeText_; |
404 | activeText_ = d; | 420 | activeText_ = d; |
405 | init_Array(&d->fonts, sizeof(iFont)); | 421 | init_Array(&d->fonts, sizeof(iFont)); |
422 | init_Array(&d->fontPriorityOrder, sizeof(iPrioMapItem)); | ||
406 | d->contentFontSize = contentScale_Text_; | 423 | d->contentFontSize = contentScale_Text_; |
407 | d->ansiEscape = makeAnsiEscapePattern_Text(iFalse /* no ESC */); | 424 | d->ansiEscape = makeAnsiEscapePattern_Text(iFalse /* no ESC */); |
408 | d->baseFontId = -1; | 425 | d->baseFontId = -1; |
@@ -438,6 +455,7 @@ void deinit_Text(iText *d) { | |||
438 | deinitCache_Text_(d); | 455 | deinitCache_Text_(d); |
439 | d->render = NULL; | 456 | d->render = NULL; |
440 | iRelease(d->ansiEscape); | 457 | iRelease(d->ansiEscape); |
458 | deinit_Array(&d->fontPriorityOrder); | ||
441 | deinit_Array(&d->fonts); | 459 | deinit_Array(&d->fonts); |
442 | } | 460 | } |
443 | 461 | ||
@@ -573,25 +591,24 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
573 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { | 591 | if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { |
574 | return d; | 592 | return d; |
575 | } | 593 | } |
576 | /* As a fallback, check all other available fonts of this size. */ | 594 | /* As a fallback, check all other available fonts of this size in priority order. */ |
577 | for (int aux = 0; aux < 2; aux++) { | 595 | iConstForEach(Array, i, &activeText_->fontPriorityOrder) { |
578 | for (iFont *font = font_Text_(FONT_ID(0, styleId, sizeId)); | 596 | iFont *font = font_Text_(FONT_ID(((const iPrioMapItem *) i.value)->fontIndex, |
579 | font < (iFont *) end_Array(&activeText_->fonts); | 597 | styleId, sizeId)); |
580 | font += maxVariants_Fonts) { | 598 | if (font == d || font == overrideFont) { |
581 | const iBool isAuxiliary = (font->fontSpec->flags & auxiliary_FontSpecFlag) ? 1 : 0; | 599 | continue; /* already checked this one */ |
582 | if (aux == isAuxiliary) { | 600 | } |
583 | /* First try auxiliary fonts, then other remaining fonts. */ | 601 | if ((*glyphIndex = glyphIndex_Font_(font, ch)) != 0) { |
584 | continue; | 602 | #if 0 |
585 | } | 603 | printf("using '%s' (pr:%d) for %lc (%x) => %d [missing in '%s']\n", |
586 | if (font == d || font == overrideFont) { | 604 | cstr_String(&font->fontSpec->id), |
587 | continue; /* already checked this one */ | 605 | font->fontSpec->priority, |
588 | } | 606 | (int) ch, |
589 | if ((*glyphIndex = glyphIndex_Font_(font, ch)) != 0) { | 607 | ch, |
590 | // printf("using %s[%f] for %lc (%x) => %d\n", | 608 | glyphIndex_Font_(font, ch), |
591 | // cstr_String(&font->fontSpec->name), font->fontSpec->scaling, | 609 | cstr_String(&d->fontSpec->id)); |
592 | // (int) ch, ch, glyphIndex_Font_(font, ch)); | 610 | #endif |
593 | return font; | 611 | return font; |
594 | } | ||
595 | } | 612 | } |
596 | } | 613 | } |
597 | if (!*glyphIndex) { | 614 | if (!*glyphIndex) { |
@@ -1450,7 +1467,7 @@ static void evenMonospaceAdvances_GlyphBuffer_(iGlyphBuffer *d, iFont *baseFont) | |||
1450 | if (d->glyphPos[i].x_advance > 0 && d->font != baseFont) { | 1467 | if (d->glyphPos[i].x_advance > 0 && d->font != baseFont) { |
1451 | const iChar ch = d->logicalText[info->cluster]; | 1468 | const iChar ch = d->logicalText[info->cluster]; |
1452 | if (isPictograph_Char(ch) || isEmoji_Char(ch)) { | 1469 | if (isPictograph_Char(ch) || isEmoji_Char(ch)) { |
1453 | const float dw = d->font->xScale * d->glyphPos[i].x_advance - monoAdvance; | 1470 | const float dw = d->font->xScale * d->glyphPos[i].x_advance - (isEmoji_Char(ch) ? 2 : 1) * monoAdvance; |
1454 | d->glyphPos[i].x_offset -= dw / 2 / d->font->xScale - 1; | 1471 | d->glyphPos[i].x_offset -= dw / 2 / d->font->xScale - 1; |
1455 | d->glyphPos[i].x_advance -= dw / d->font->xScale - 1; | 1472 | d->glyphPos[i].x_advance -= dw / d->font->xScale - 1; |
1456 | } | 1473 | } |
diff --git a/src/ui/util.c b/src/ui/util.c index d0d24eb2..41f8eaa9 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -1780,7 +1780,7 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) { | |||
1780 | } | 1780 | } |
1781 | else if (equal_Command(cmd, "valueinput.set")) { | 1781 | else if (equal_Command(cmd, "valueinput.set")) { |
1782 | iInputWidget *input = findChild_Widget(dlg, "input"); | 1782 | iInputWidget *input = findChild_Widget(dlg, "input"); |
1783 | setTextCStr_InputWidget(input, suffixPtr_Command(cmd, "text")); | 1783 | setTextUndoableCStr_InputWidget(input, suffixPtr_Command(cmd, "text"), iTrue); |
1784 | validate_InputWidget(input); | 1784 | validate_InputWidget(input); |
1785 | return iTrue; | 1785 | return iTrue; |
1786 | } | 1786 | } |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 9f67b1c7..fc754b7a 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -1290,7 +1290,7 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
1290 | /* TODO: Motion events occur frequently. Maybe it would help if these were handled | 1290 | /* TODO: Motion events occur frequently. Maybe it would help if these were handled |
1291 | via audiences that specifically register to listen for motion, to minimize the | 1291 | via audiences that specifically register to listen for motion, to minimize the |
1292 | number of widgets that need to process them. */ | 1292 | number of widgets that need to process them. */ |
1293 | const int hoverScrollLimit = 1.5f * lineHeight_Text(default_FontId); | 1293 | const int hoverScrollLimit = 3.0f * lineHeight_Text(default_FontId); |
1294 | float speed = 0.0f; | 1294 | float speed = 0.0f; |
1295 | if (ev->motion.y < hoverScrollLimit) { | 1295 | if (ev->motion.y < hoverScrollLimit) { |
1296 | speed = (hoverScrollLimit - ev->motion.y) / (float) hoverScrollLimit; | 1296 | speed = (hoverScrollLimit - ev->motion.y) / (float) hoverScrollLimit; |
@@ -1315,7 +1315,7 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
1315 | if (elapsed > 100) { | 1315 | if (elapsed > 100) { |
1316 | elapsed = 16; | 1316 | elapsed = 16; |
1317 | } | 1317 | } |
1318 | int step = elapsed * gap_UI / 16 * iClamp(speed, -1.0f, 1.0f); | 1318 | int step = elapsed * gap_UI / 8 * iClamp(speed, -1.0f, 1.0f); |
1319 | if (step != 0) { | 1319 | if (step != 0) { |
1320 | lastHoverOverflowMotionTime_ = nowTime; | 1320 | lastHoverOverflowMotionTime_ = nowTime; |
1321 | scrollOverflow_Widget(d, step); | 1321 | scrollOverflow_Widget(d, step); |
diff --git a/src/ui/window.c b/src/ui/window.c index 13abc5fa..47abf878 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -539,10 +539,19 @@ void deinit_Window(iWindow *d) { | |||
539 | void init_MainWindow(iMainWindow *d, iRect rect) { | 539 | void init_MainWindow(iMainWindow *d, iRect rect) { |
540 | theWindow_ = &d->base; | 540 | theWindow_ = &d->base; |
541 | theMainWindow_ = d; | 541 | theMainWindow_ = d; |
542 | d->enableBackBuf = iFalse; | ||
542 | uint32_t flags = 0; | 543 | uint32_t flags = 0; |
543 | #if defined (iPlatformAppleDesktop) | 544 | #if defined (iPlatformAppleDesktop) |
544 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); | 545 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); |
545 | flags |= shouldDefaultToMetalRenderer_MacOS() ? SDL_WINDOW_METAL : SDL_WINDOW_OPENGL; | 546 | flags |= shouldDefaultToMetalRenderer_MacOS() ? SDL_WINDOW_METAL : SDL_WINDOW_OPENGL; |
547 | if (flags & SDL_WINDOW_METAL) { | ||
548 | /* There are some really odd refresh glitches that only occur with the Metal | ||
549 | backend. It's perhaps related to it not expecting refresh to stop intermittently | ||
550 | to wait for input events. If forcing constant refreshing at full frame rate, the | ||
551 | problems seem to go away... Rendering everything to a separate render target | ||
552 | appears to sidestep some of the glitches. */ | ||
553 | d->enableBackBuf = iTrue; | ||
554 | } | ||
546 | #elif defined (iPlatformAppleMobile) | 555 | #elif defined (iPlatformAppleMobile) |
547 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); | 556 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); |
548 | flags |= SDL_WINDOW_METAL; | 557 | flags |= SDL_WINDOW_METAL; |
@@ -566,6 +575,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
566 | d->place.lastNotifiedSize = zero_I2(); | 575 | d->place.lastNotifiedSize = zero_I2(); |
567 | d->place.snap = 0; | 576 | d->place.snap = 0; |
568 | d->keyboardHeight = 0; | 577 | d->keyboardHeight = 0; |
578 | d->backBuf = NULL; | ||
569 | #if defined(iPlatformMobile) | 579 | #if defined(iPlatformMobile) |
570 | const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */ | 580 | const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */ |
571 | #else | 581 | #else |
@@ -637,6 +647,9 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
637 | } | 647 | } |
638 | 648 | ||
639 | void deinit_MainWindow(iMainWindow *d) { | 649 | void deinit_MainWindow(iMainWindow *d) { |
650 | if (d->backBuf) { | ||
651 | SDL_DestroyTexture(d->backBuf); | ||
652 | } | ||
640 | deinitRoots_Window_(as_Window(d)); | 653 | deinitRoots_Window_(as_Window(d)); |
641 | if (theWindow_ == as_Window(d)) { | 654 | if (theWindow_ == as_Window(d)) { |
642 | theWindow_ = NULL; | 655 | theWindow_ = NULL; |
@@ -682,6 +695,10 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) { | |||
682 | static void invalidate_MainWindow_(iMainWindow *d, iBool forced) { | 695 | static void invalidate_MainWindow_(iMainWindow *d, iBool forced) { |
683 | if (d && (!d->base.isInvalidated || forced)) { | 696 | if (d && (!d->base.isInvalidated || forced)) { |
684 | d->base.isInvalidated = iTrue; | 697 | d->base.isInvalidated = iTrue; |
698 | if (d->enableBackBuf && d->backBuf) { | ||
699 | SDL_DestroyTexture(d->backBuf); | ||
700 | d->backBuf = NULL; | ||
701 | } | ||
685 | resetFonts_Text(text_Window(d)); | 702 | resetFonts_Text(text_Window(d)); |
686 | postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ | 703 | postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ |
687 | } | 704 | } |
@@ -1272,11 +1289,28 @@ void draw_MainWindow(iMainWindow *d) { | |||
1272 | d->maxDrawableHeight = renderSize.y; | 1289 | d->maxDrawableHeight = renderSize.y; |
1273 | } | 1290 | } |
1274 | } | 1291 | } |
1292 | if (d->enableBackBuf) { | ||
1293 | /* Possible resize the backing buffer. */ | ||
1294 | if (!d->backBuf || !isEqual_I2(size_SDLTexture(d->backBuf), renderSize)) { | ||
1295 | if (d->backBuf) { | ||
1296 | SDL_DestroyTexture(d->backBuf); | ||
1297 | } | ||
1298 | d->backBuf = SDL_CreateTexture(d->base.render, | ||
1299 | SDL_PIXELFORMAT_RGB888, | ||
1300 | SDL_TEXTUREACCESS_TARGET, | ||
1301 | renderSize.x, | ||
1302 | renderSize.y); | ||
1303 | // printf("NEW BACKING: %dx%d %p\n", renderSize.x, renderSize.y, d->backBuf); fflush(stdout); | ||
1304 | } | ||
1305 | } | ||
1275 | } | 1306 | } |
1276 | const int winFlags = SDL_GetWindowFlags(d->base.win); | 1307 | const int winFlags = SDL_GetWindowFlags(d->base.win); |
1277 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; | 1308 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; |
1278 | iPaint p; | 1309 | iPaint p; |
1279 | init_Paint(&p); | 1310 | init_Paint(&p); |
1311 | if (d->backBuf) { | ||
1312 | SDL_SetRenderTarget(d->base.render, d->backBuf); | ||
1313 | } | ||
1280 | /* Clear the window. The clear color is visible as a border around the window | 1314 | /* Clear the window. The clear color is visible as a border around the window |
1281 | when the custom frame is being used. */ { | 1315 | when the custom frame is being used. */ { |
1282 | setCurrent_Root(w->roots[0]); | 1316 | setCurrent_Root(w->roots[0]); |
@@ -1359,6 +1393,10 @@ void draw_MainWindow(iMainWindow *d) { | |||
1359 | drawCount_ = 0; | 1393 | drawCount_ = 0; |
1360 | #endif | 1394 | #endif |
1361 | } | 1395 | } |
1396 | if (d->backBuf) { | ||
1397 | SDL_SetRenderTarget(d->base.render, NULL); | ||
1398 | SDL_RenderCopy(d->base.render, d->backBuf, NULL, NULL); | ||
1399 | } | ||
1362 | #if 0 | 1400 | #if 0 |
1363 | /* Text cache debugging. */ { | 1401 | /* Text cache debugging. */ { |
1364 | SDL_Rect rect = { d->roots[0]->widget->rect.size.x - 640, 0, 640, 2.5 * 640 }; | 1402 | SDL_Rect rect = { d->roots[0]->widget->rect.size.x - 640, 0, 640, 2.5 * 640 }; |
diff --git a/src/ui/window.h b/src/ui/window.h index b4e348d2..5abf23eb 100644 --- a/src/ui/window.h +++ b/src/ui/window.h | |||
@@ -118,6 +118,8 @@ struct Impl_MainWindow { | |||
118 | SDL_Texture * appIcon; | 118 | SDL_Texture * appIcon; |
119 | int keyboardHeight; /* mobile software keyboards */ | 119 | int keyboardHeight; /* mobile software keyboards */ |
120 | int maxDrawableHeight; | 120 | int maxDrawableHeight; |
121 | iBool enableBackBuf; /* only used on macOS with Metal (helps with refresh glitches for some reason??) */ | ||
122 | SDL_Texture * backBuf; /* enables refreshing the window without redrawing anything */ | ||
121 | }; | 123 | }; |
122 | 124 | ||
123 | iLocalDef enum iWindowType type_Window(const iAnyWindow *d) { | 125 | iLocalDef enum iWindowType type_Window(const iAnyWindow *d) { |