diff options
-rw-r--r-- | src/gmdocument.c | 5 | ||||
-rw-r--r-- | src/gmrequest.c | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 359 | ||||
-rw-r--r-- | src/ui/paint.c | 24 | ||||
-rw-r--r-- | src/ui/paint.h | 12 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 170 | ||||
-rw-r--r-- | src/ui/text.c | 12 | ||||
-rw-r--r-- | src/ui/text.h | 8 |
8 files changed, 405 insertions, 187 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index 535a873e..a6fc39c5 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -413,8 +413,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
413 | pos.y += required - delta; | 413 | pos.y += required - delta; |
414 | } | 414 | } |
415 | } | 415 | } |
416 | /* Save the document title. */ | 416 | /* Save the document title (first high-level heading). */ |
417 | if (type == heading1_GmLineType && isEmpty_String(&d->title)) { | 417 | if ((type == heading1_GmLineType || type == heading2_GmLineType) && |
418 | isEmpty_String(&d->title)) { | ||
418 | setRange_String(&d->title, line); | 419 | setRange_String(&d->title, line); |
419 | } | 420 | } |
420 | /* List bullet. */ | 421 | /* List bullet. */ |
diff --git a/src/gmrequest.c b/src/gmrequest.c index 2b6ee9f9..0d69861d 100644 --- a/src/gmrequest.c +++ b/src/gmrequest.c | |||
@@ -330,7 +330,7 @@ void submit_GmRequest(iGmRequest *d) { | |||
330 | /* TODO: Check supported file types: images, audio */ | 330 | /* TODO: Check supported file types: images, audio */ |
331 | /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ | 331 | /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ |
332 | d->resp.statusCode = success_GmStatusCode; | 332 | d->resp.statusCode = success_GmStatusCode; |
333 | if (endsWithCase_String(path, ".gmi")) { | 333 | if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { |
334 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); | 334 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); |
335 | } | 335 | } |
336 | else if (endsWithCase_String(path, ".txt")) { | 336 | else if (endsWithCase_String(path, ".txt")) { |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 47d7f755..29803252 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <the_Foundation/stringarray.h> | 19 | #include <the_Foundation/stringarray.h> |
20 | #include <SDL_clipboard.h> | 20 | #include <SDL_clipboard.h> |
21 | #include <SDL_timer.h> | 21 | #include <SDL_timer.h> |
22 | #include <SDL_render.h> | ||
22 | #include <ctype.h> | 23 | #include <ctype.h> |
23 | 24 | ||
24 | iDeclareClass(MediaRequest) | 25 | iDeclareClass(MediaRequest) |
@@ -112,33 +113,37 @@ enum iRequestState { | |||
112 | }; | 113 | }; |
113 | 114 | ||
114 | struct Impl_DocumentWidget { | 115 | struct Impl_DocumentWidget { |
115 | iWidget widget; | 116 | iWidget widget; |
116 | enum iRequestState state; | 117 | enum iRequestState state; |
117 | iModel mod; | 118 | iModel mod; |
118 | iString *titleUser; | 119 | iString * titleUser; |
119 | iGmRequest *request; | 120 | iGmRequest * request; |
120 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ | 121 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ |
121 | iObjectList *media; | 122 | iObjectList * media; |
122 | iGmDocument *doc; | 123 | iGmDocument * doc; |
123 | int certFlags; | 124 | int certFlags; |
124 | iDate certExpiry; | 125 | iDate certExpiry; |
125 | iString * certSubject; | 126 | iString * certSubject; |
126 | iBool selecting; | 127 | iBool selecting; |
127 | iRangecc selectMark; | 128 | iRangecc selectMark; |
128 | iRangecc foundMark; | 129 | iRangecc foundMark; |
129 | int pageMargin; | 130 | int pageMargin; |
130 | iPtrArray visibleLinks; | 131 | iPtrArray visibleLinks; |
131 | const iGmRun *hoverLink; | 132 | const iGmRun * hoverLink; |
132 | iBool noHoverWhileScrolling; | 133 | iBool noHoverWhileScrolling; |
133 | iBool showLinkNumbers; | 134 | iBool showLinkNumbers; |
134 | iClick click; | 135 | iClick click; |
135 | float initialNormScrollY; | 136 | float initNormScrollY; |
136 | int scrollY; | 137 | int scrollY; |
137 | iScrollWidget *scroll; | 138 | iScrollWidget *scroll; |
138 | iWidget *menu; | 139 | iWidget * menu; |
139 | SDL_Cursor *arrowCursor; /* TODO: cursors belong in Window */ | 140 | SDL_Cursor * arrowCursor; /* TODO: cursors belong in Window */ |
140 | SDL_Cursor *beamCursor; | 141 | SDL_Cursor * beamCursor; |
141 | SDL_Cursor *handCursor; | 142 | SDL_Cursor * handCursor; |
143 | SDL_Texture * visBuffer[2]; | ||
144 | int visBufferIndex; | ||
145 | iInt2 visBufferSize; | ||
146 | iRangei visBufferValidRange; | ||
142 | }; | 147 | }; |
143 | 148 | ||
144 | iDefineObjectConstruction(DocumentWidget) | 149 | iDefineObjectConstruction(DocumentWidget) |
@@ -158,7 +163,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
158 | d->isRequestUpdated = iFalse; | 163 | d->isRequestUpdated = iFalse; |
159 | d->media = new_ObjectList(); | 164 | d->media = new_ObjectList(); |
160 | d->doc = new_GmDocument(); | 165 | d->doc = new_GmDocument(); |
161 | d->initialNormScrollY = 0; | 166 | d->initNormScrollY = 0; |
162 | d->scrollY = 0; | 167 | d->scrollY = 0; |
163 | d->selecting = iFalse; | 168 | d->selecting = iFalse; |
164 | d->selectMark = iNullRange; | 169 | d->selectMark = iNullRange; |
@@ -167,9 +172,13 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
167 | d->hoverLink = NULL; | 172 | d->hoverLink = NULL; |
168 | d->noHoverWhileScrolling = iFalse; | 173 | d->noHoverWhileScrolling = iFalse; |
169 | d->showLinkNumbers = iFalse; | 174 | d->showLinkNumbers = iFalse; |
170 | d->arrowCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); | 175 | iZap(d->visBuffer); |
171 | d->beamCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); | 176 | d->visBufferIndex = 0; |
172 | d->handCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); | 177 | d->visBufferSize = zero_I2(); |
178 | iZap(d->visBufferValidRange); | ||
179 | d->arrowCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); | ||
180 | d->beamCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); | ||
181 | d->handCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); | ||
173 | init_PtrArray(&d->visibleLinks); | 182 | init_PtrArray(&d->visibleLinks); |
174 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 183 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
175 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 184 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); |
@@ -219,7 +228,7 @@ static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | |||
219 | rect.pos.y += margin; | 228 | rect.pos.y += margin; |
220 | rect.size.y -= margin; | 229 | rect.size.y -= margin; |
221 | } | 230 | } |
222 | iInt2 docSize = addY_I2(size_GmDocument(d->doc), 0 /*-lineHeight_Text(banner_FontId) * 2*/); | 231 | const iInt2 docSize = size_GmDocument(d->doc); |
223 | if (docSize.y < rect.size.y) { | 232 | if (docSize.y < rect.size.y) { |
224 | /* Center vertically if short. */ | 233 | /* Center vertically if short. */ |
225 | int offset = (rect.size.y - docSize.y) / 2; | 234 | int offset = (rect.size.y - docSize.y) / 2; |
@@ -229,6 +238,14 @@ static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | |||
229 | return rect; | 238 | return rect; |
230 | } | 239 | } |
231 | 240 | ||
241 | iLocalDef int documentToWindowY_DocumentWidget_(const iDocumentWidget *d, int docY) { | ||
242 | return docY - d->scrollY + documentBounds_DocumentWidget_(d).pos.y; | ||
243 | } | ||
244 | |||
245 | iLocalDef int windowToDocumentY_DocumentWidget_(const iDocumentWidget *d, int localY) { | ||
246 | return localY + d->scrollY - documentBounds_DocumentWidget_(d).pos.y; | ||
247 | } | ||
248 | |||
232 | static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { | 249 | static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { |
233 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentWidget_(d))), d->scrollY); | 250 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentWidget_(d))), d->scrollY); |
234 | } | 251 | } |
@@ -252,9 +269,9 @@ static void requestFinished_DocumentWidget_(iAnyObject *obj) { | |||
252 | } | 269 | } |
253 | 270 | ||
254 | static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { | 271 | static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { |
255 | const int margin = gap_UI * d->pageMargin; | 272 | const int margin = !hasSiteBanner_GmDocument(d->doc) ? gap_UI * d->pageMargin : 0; |
256 | return (iRangei){ d->scrollY - margin, | 273 | return (iRangei){ d->scrollY - margin, |
257 | d->scrollY + height_Rect(bounds_Widget(constAs_Widget(d))) }; | 274 | d->scrollY + height_Rect(bounds_Widget(constAs_Widget(d))) - margin }; |
258 | } | 275 | } |
259 | 276 | ||
260 | static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { | 277 | static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { |
@@ -453,6 +470,10 @@ static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | |||
453 | } | 470 | } |
454 | } | 471 | } |
455 | 472 | ||
473 | static void invalidate_DocumentWidget_(iDocumentWidget *d) { | ||
474 | iZap(d->visBufferValidRange); | ||
475 | } | ||
476 | |||
456 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response) { | 477 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response) { |
457 | if (d->state == ready_RequestState) { | 478 | if (d->state == ready_RequestState) { |
458 | return; | 479 | return; |
@@ -462,6 +483,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
462 | const enum iGmStatusCode statusCode = response->statusCode; | 483 | const enum iGmStatusCode statusCode = response->statusCode; |
463 | if (category_GmStatusCode(statusCode) != categoryInput_GmStatusCode) { | 484 | if (category_GmStatusCode(statusCode) != categoryInput_GmStatusCode) { |
464 | iString str; | 485 | iString str; |
486 | invalidate_DocumentWidget_(d); | ||
465 | updateTheme_DocumentWidget_(d); | 487 | updateTheme_DocumentWidget_(d); |
466 | initBlock_String(&str, &response->body); | 488 | initBlock_String(&str, &response->body); |
467 | if (category_GmStatusCode(statusCode) == categorySuccess_GmStatusCode) { | 489 | if (category_GmStatusCode(statusCode) == categorySuccess_GmStatusCode) { |
@@ -600,11 +622,11 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | |||
600 | if (recent && recent->cachedResponse) { | 622 | if (recent && recent->cachedResponse) { |
601 | const iGmResponse *resp = recent->cachedResponse; | 623 | const iGmResponse *resp = recent->cachedResponse; |
602 | d->state = fetching_RequestState; | 624 | d->state = fetching_RequestState; |
603 | d->initialNormScrollY = recent->normScrollY; | 625 | d->initNormScrollY = recent->normScrollY; |
604 | /* Use the cached response data. */ | 626 | /* Use the cached response data. */ |
605 | updateTrust_DocumentWidget_(d, resp); | 627 | updateTrust_DocumentWidget_(d, resp); |
606 | updateDocument_DocumentWidget_(d, resp); | 628 | updateDocument_DocumentWidget_(d, resp); |
607 | d->scrollY = d->initialNormScrollY * size_GmDocument(d->doc).y; | 629 | d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y; |
608 | d->state = ready_RequestState; | 630 | d->state = ready_RequestState; |
609 | updateVisible_DocumentWidget_(d); | 631 | updateVisible_DocumentWidget_(d); |
610 | postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 632 | postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); |
@@ -640,7 +662,7 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo | |||
640 | iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { | 662 | iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { |
641 | iDocumentWidget *d = new_DocumentWidget(); | 663 | iDocumentWidget *d = new_DocumentWidget(); |
642 | delete_History(d->mod.history); | 664 | delete_History(d->mod.history); |
643 | d->initialNormScrollY = normScrollPos_DocumentWidget_(d); | 665 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); |
644 | d->mod.history = copy_History(orig->mod.history); | 666 | d->mod.history = copy_History(orig->mod.history); |
645 | setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); | 667 | setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); |
646 | return d; | 668 | return d; |
@@ -651,7 +673,7 @@ void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { | |||
651 | } | 673 | } |
652 | 674 | ||
653 | void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { | 675 | void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { |
654 | d->initialNormScrollY = normScrollY; | 676 | d->initNormScrollY = normScrollY; |
655 | } | 677 | } |
656 | 678 | ||
657 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { | 679 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { |
@@ -695,7 +717,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
695 | case categoryInput_GmStatusCode: { | 717 | case categoryInput_GmStatusCode: { |
696 | iUrl parts; | 718 | iUrl parts; |
697 | init_Url(&parts, d->mod.url); | 719 | init_Url(&parts, d->mod.url); |
698 | printf("%s\n", cstr_String(meta_GmRequest(d->request))); | 720 | // printf("%s\n", cstr_String(meta_GmRequest(d->request))); |
699 | iWidget *dlg = makeValueInput_Widget( | 721 | iWidget *dlg = makeValueInput_Widget( |
700 | as_Widget(d), | 722 | as_Widget(d), |
701 | NULL, | 723 | NULL, |
@@ -809,6 +831,7 @@ static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) | |||
809 | d->media, | 831 | d->media, |
810 | iClob(new_MediaRequest( | 832 | iClob(new_MediaRequest( |
811 | d, linkId, absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, linkId))))); | 833 | d, linkId, absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, linkId))))); |
834 | invalidate_DocumentWidget_(d); | ||
812 | return iTrue; | 835 | return iTrue; |
813 | } | 836 | } |
814 | return iFalse; | 837 | return iFalse; |
@@ -835,6 +858,7 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
835 | setImage_GmDocument(d->doc, req->linkId, meta_GmRequest(req->req), | 858 | setImage_GmDocument(d->doc, req->linkId, meta_GmRequest(req->req), |
836 | body_GmRequest(req->req)); | 859 | body_GmRequest(req->req)); |
837 | updateVisible_DocumentWidget_(d); | 860 | updateVisible_DocumentWidget_(d); |
861 | invalidate_DocumentWidget_(d); | ||
838 | refresh_Widget(as_Widget(d)); | 862 | refresh_Widget(as_Widget(d)); |
839 | } | 863 | } |
840 | } | 864 | } |
@@ -848,6 +872,36 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
848 | return iFalse; | 872 | return iFalse; |
849 | } | 873 | } |
850 | 874 | ||
875 | static void deallocVisBuffer_DocumentWidget_(iDocumentWidget *d) { | ||
876 | d->visBufferSize = zero_I2(); | ||
877 | iZap(d->visBufferValidRange); | ||
878 | iForIndices(i, d->visBuffer) { | ||
879 | SDL_DestroyTexture(d->visBuffer[i]); | ||
880 | d->visBuffer[i] = NULL; | ||
881 | } | ||
882 | } | ||
883 | |||
884 | static void allocVisBuffer_DocumentWidget_(iDocumentWidget *d) { | ||
885 | iWidget *w = as_Widget(d); | ||
886 | const iBool isVisible = isVisible_Widget(w); | ||
887 | const iInt2 size = bounds_Widget(w).size; | ||
888 | if (!isEqual_I2(size, d->visBufferSize) || !isVisible) { | ||
889 | deallocVisBuffer_DocumentWidget_(d); | ||
890 | } | ||
891 | if (isVisible && !d->visBuffer[0]) { | ||
892 | iZap(d->visBufferValidRange); | ||
893 | d->visBufferSize = size; | ||
894 | iForIndices(i, d->visBuffer) { | ||
895 | d->visBuffer[i] = SDL_CreateTexture(renderer_Window(get_Window()), | ||
896 | SDL_PIXELFORMAT_RGBA8888, | ||
897 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
898 | size.x, | ||
899 | size.y); | ||
900 | SDL_SetTextureBlendMode(d->visBuffer[i], SDL_BLENDMODE_NONE); | ||
901 | } | ||
902 | } | ||
903 | } | ||
904 | |||
851 | void updateSize_DocumentWidget(iDocumentWidget *d) { | 905 | void updateSize_DocumentWidget(iDocumentWidget *d) { |
852 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); | 906 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); |
853 | updateVisible_DocumentWidget_(d); | 907 | updateVisible_DocumentWidget_(d); |
@@ -867,11 +921,14 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
867 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); | 921 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); |
868 | } | 922 | } |
869 | } | 923 | } |
924 | invalidate_DocumentWidget_(d); | ||
925 | allocVisBuffer_DocumentWidget_(d); | ||
870 | refresh_Widget(w); | 926 | refresh_Widget(w); |
871 | updateWindowTitle_DocumentWidget_(d); | 927 | updateWindowTitle_DocumentWidget_(d); |
872 | } | 928 | } |
873 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { | 929 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { |
874 | updateTheme_DocumentWidget_(d); | 930 | updateTheme_DocumentWidget_(d); |
931 | invalidate_DocumentWidget_(d); | ||
875 | refresh_Widget(w); | 932 | refresh_Widget(w); |
876 | } | 933 | } |
877 | else if (equal_Command(cmd, "tabs.changed")) { | 934 | else if (equal_Command(cmd, "tabs.changed")) { |
@@ -884,6 +941,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
884 | updateVisible_DocumentWidget_(d); | 941 | updateVisible_DocumentWidget_(d); |
885 | } | 942 | } |
886 | updateWindowTitle_DocumentWidget_(d); | 943 | updateWindowTitle_DocumentWidget_(d); |
944 | allocVisBuffer_DocumentWidget_(d); | ||
887 | return iFalse; | 945 | return iFalse; |
888 | } | 946 | } |
889 | else if (equal_Command(cmd, "server.showcert") && d == document_App()) { | 947 | else if (equal_Command(cmd, "server.showcert") && d == document_App()) { |
@@ -943,16 +1001,18 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
943 | return iTrue; | 1001 | return iTrue; |
944 | } | 1002 | } |
945 | else if (equal_Command(cmd, "document.input.submit")) { | 1003 | else if (equal_Command(cmd, "document.input.submit")) { |
946 | iString *value = collect_String(suffix_Command(cmd, "value")); | 1004 | if (arg_Command(cmd)) { |
947 | urlEncode_String(value); | 1005 | iString *value = collect_String(suffix_Command(cmd, "value")); |
948 | iString *url = collect_String(copy_String(d->mod.url)); | 1006 | urlEncode_String(value); |
949 | const size_t qPos = indexOfCStr_String(url, "?"); | 1007 | iString *url = collect_String(copy_String(d->mod.url)); |
950 | if (qPos != iInvalidPos) { | 1008 | const size_t qPos = indexOfCStr_String(url, "?"); |
951 | remove_Block(&url->chars, qPos, iInvalidSize); | 1009 | if (qPos != iInvalidPos) { |
1010 | remove_Block(&url->chars, qPos, iInvalidSize); | ||
1011 | } | ||
1012 | appendCStr_String(url, "?"); | ||
1013 | append_String(url, value); | ||
1014 | postCommandf_App("open url:%s", cstr_String(url)); | ||
952 | } | 1015 | } |
953 | appendCStr_String(url, "?"); | ||
954 | append_String(url, value); | ||
955 | postCommandf_App("open url:%s", cstr_String(url)); | ||
956 | return iTrue; | 1016 | return iTrue; |
957 | } | 1017 | } |
958 | else if (equal_Command(cmd, "valueinput.cancelled") && | 1018 | else if (equal_Command(cmd, "valueinput.cancelled") && |
@@ -968,7 +1028,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
968 | else if (equalWidget_Command(cmd, w, "document.request.finished") && | 1028 | else if (equalWidget_Command(cmd, w, "document.request.finished") && |
969 | pointerLabel_Command(cmd, "request") == d->request) { | 1029 | pointerLabel_Command(cmd, "request") == d->request) { |
970 | checkResponse_DocumentWidget_(d); | 1030 | checkResponse_DocumentWidget_(d); |
971 | d->scrollY = d->initialNormScrollY * size_GmDocument(d->doc).y; | 1031 | d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y; |
972 | d->state = ready_RequestState; | 1032 | d->state = ready_RequestState; |
973 | /* The response may be cached. */ { | 1033 | /* The response may be cached. */ { |
974 | const iRangecc proto = urlProtocol_String(d->mod.url); | 1034 | const iRangecc proto = urlProtocol_String(d->mod.url); |
@@ -1002,7 +1062,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1002 | return handleMediaCommand_DocumentWidget_(d, cmd); | 1062 | return handleMediaCommand_DocumentWidget_(d, cmd); |
1003 | } | 1063 | } |
1004 | else if (equal_Command(cmd, "document.reload") && document_App() == d) { | 1064 | else if (equal_Command(cmd, "document.reload") && document_App() == d) { |
1005 | d->initialNormScrollY = normScrollPos_DocumentWidget_(d); | 1065 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); |
1006 | fetch_DocumentWidget_(d); | 1066 | fetch_DocumentWidget_(d); |
1007 | return iTrue; | 1067 | return iTrue; |
1008 | } | 1068 | } |
@@ -1100,6 +1160,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1100 | case SDLK_RALT: | 1160 | case SDLK_RALT: |
1101 | if (document_App() == d) { | 1161 | if (document_App() == d) { |
1102 | d->showLinkNumbers = iFalse; | 1162 | d->showLinkNumbers = iFalse; |
1163 | invalidate_DocumentWidget_(d); | ||
1103 | refresh_Widget(w); | 1164 | refresh_Widget(w); |
1104 | } | 1165 | } |
1105 | break; | 1166 | break; |
@@ -1127,21 +1188,10 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1127 | case SDLK_RALT: | 1188 | case SDLK_RALT: |
1128 | if (document_App() == d) { | 1189 | if (document_App() == d) { |
1129 | d->showLinkNumbers = iTrue; | 1190 | d->showLinkNumbers = iTrue; |
1191 | invalidate_DocumentWidget_(d); | ||
1130 | refresh_Widget(w); | 1192 | refresh_Widget(w); |
1131 | } | 1193 | } |
1132 | break; | 1194 | break; |
1133 | case SDLK_1: | ||
1134 | case SDLK_2: | ||
1135 | case SDLK_3: | ||
1136 | case SDLK_4: | ||
1137 | case SDLK_5: | ||
1138 | case SDLK_6: | ||
1139 | case SDLK_7: | ||
1140 | case SDLK_8: | ||
1141 | case SDLK_9: | ||
1142 | if (mods == KMOD_ALT || mods == (KMOD_ALT | KMOD_PRIMARY)) { | ||
1143 | } | ||
1144 | break; | ||
1145 | case SDLK_HOME: | 1195 | case SDLK_HOME: |
1146 | d->scrollY = 0; | 1196 | d->scrollY = 0; |
1147 | scroll_DocumentWidget_(d, 0); | 1197 | scroll_DocumentWidget_(d, 0); |
@@ -1264,6 +1314,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1264 | d->hoverLink = NULL; | 1314 | d->hoverLink = NULL; |
1265 | scroll_DocumentWidget_(d, 0); | 1315 | scroll_DocumentWidget_(d, 0); |
1266 | updateVisible_DocumentWidget_(d); | 1316 | updateVisible_DocumentWidget_(d); |
1317 | invalidate_DocumentWidget_(d); | ||
1267 | refresh_Widget(w); | 1318 | refresh_Widget(w); |
1268 | return iTrue; | 1319 | return iTrue; |
1269 | } | 1320 | } |
@@ -1274,6 +1325,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1274 | setImage_GmDocument(d->doc, linkId, meta_GmRequest(req->req), | 1325 | setImage_GmDocument(d->doc, linkId, meta_GmRequest(req->req), |
1275 | body_GmRequest(req->req)); | 1326 | body_GmRequest(req->req)); |
1276 | updateVisible_DocumentWidget_(d); | 1327 | updateVisible_DocumentWidget_(d); |
1328 | invalidate_DocumentWidget_(d); | ||
1277 | refresh_Widget(w); | 1329 | refresh_Widget(w); |
1278 | return iTrue; | 1330 | return iTrue; |
1279 | } | 1331 | } |
@@ -1305,7 +1357,13 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1305 | 1357 | ||
1306 | iDeclareType(DrawContext) | 1358 | iDeclareType(DrawContext) |
1307 | 1359 | ||
1360 | enum iDrawRunPass { | ||
1361 | static_DrawRunPass, | ||
1362 | dynamic_DrawRunPass, | ||
1363 | }; | ||
1364 | |||
1308 | struct Impl_DrawContext { | 1365 | struct Impl_DrawContext { |
1366 | enum iDrawRunPass pass; | ||
1309 | const iDocumentWidget *widget; | 1367 | const iDocumentWidget *widget; |
1310 | iRect widgetBounds; | 1368 | iRect widgetBounds; |
1311 | iRect bounds; /* document area */ | 1369 | iRect bounds; /* document area */ |
@@ -1348,57 +1406,67 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1348 | iDrawContext *d = context; | 1406 | iDrawContext *d = context; |
1349 | const iInt2 origin = addY_I2(d->bounds.pos, -d->widget->scrollY); | 1407 | const iInt2 origin = addY_I2(d->bounds.pos, -d->widget->scrollY); |
1350 | if (run->imageId) { | 1408 | if (run->imageId) { |
1351 | SDL_Texture *tex = imageTexture_GmDocument(d->widget->doc, run->imageId); | 1409 | if (d->pass == static_DrawRunPass) { |
1352 | if (tex) { | 1410 | SDL_Texture *tex = imageTexture_GmDocument(d->widget->doc, run->imageId); |
1353 | const iRect dst = moved_Rect(run->visBounds, origin); | 1411 | if (tex) { |
1354 | SDL_RenderCopy(d->paint.dst->render, tex, NULL, | 1412 | const iRect dst = moved_Rect(run->visBounds, origin); |
1355 | &(SDL_Rect){ dst.pos.x, dst.pos.y, dst.size.x, dst.size.y }); | 1413 | SDL_RenderCopy(d->paint.dst->render, tex, NULL, |
1414 | &(SDL_Rect){ dst.pos.x, dst.pos.y, dst.size.x, dst.size.y }); | ||
1415 | } | ||
1356 | } | 1416 | } |
1357 | return; | 1417 | return; |
1358 | } | 1418 | } |
1419 | /* Text markers. */ | ||
1420 | if (d->pass == dynamic_DrawRunPass) { | ||
1421 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); | ||
1422 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); | ||
1423 | } | ||
1359 | enum iColorId fg = run->color; | 1424 | enum iColorId fg = run->color; |
1360 | const iGmDocument *doc = d->widget->doc; | 1425 | const iGmDocument *doc = d->widget->doc; |
1426 | /* Matches the current drawing pass? */ | ||
1427 | const iBool isDynamic = (run->linkId && ~run->flags & decoration_GmRunFlag); | ||
1428 | if (isDynamic ^ (d->pass == dynamic_DrawRunPass)) { | ||
1429 | return; | ||
1430 | } | ||
1361 | const iBool isHover = | 1431 | const iBool isHover = |
1362 | (run->linkId != 0 && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && | 1432 | (run->linkId && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && |
1363 | !isEmpty_Rect(run->bounds)); | 1433 | ~run->flags & decoration_GmRunFlag); |
1364 | const iInt2 visPos = add_I2(run->visBounds.pos, origin); | 1434 | const iInt2 visPos = add_I2(run->visBounds.pos, origin); |
1365 | /* Text markers. */ | 1435 | if (run->linkId && ~run->flags & decoration_GmRunFlag) { |
1366 | /* TODO: Add themed palette entries */ | ||
1367 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); | ||
1368 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); | ||
1369 | if (run->linkId && !isEmpty_Rect(run->bounds)) { | ||
1370 | fg = linkColor_GmDocument(doc, run->linkId, isHover ? textHover_GmLinkPart : text_GmLinkPart); | 1436 | fg = linkColor_GmDocument(doc, run->linkId, isHover ? textHover_GmLinkPart : text_GmLinkPart); |
1371 | if (linkFlags_GmDocument(doc, run->linkId) & content_GmLinkFlag) { | 1437 | if (linkFlags_GmDocument(doc, run->linkId) & content_GmLinkFlag) { |
1372 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); /* link is inactive */ | 1438 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); /* link is inactive */ |
1373 | } | 1439 | } |
1374 | } | 1440 | } |
1375 | if (run->flags & siteBanner_GmRunFlag) { | 1441 | if (run->flags & siteBanner_GmRunFlag) { |
1376 | /* Draw the site banner. */ | 1442 | if (d->pass == static_DrawRunPass) { |
1377 | fillRect_Paint( | 1443 | /* Draw the site banner. */ |
1378 | &d->paint, | 1444 | fillRect_Paint( |
1379 | initCorners_Rect(topLeft_Rect(d->widgetBounds), | 1445 | &d->paint, |
1380 | init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))), | 1446 | initCorners_Rect(topLeft_Rect(d->widgetBounds), |
1381 | visPos.y + height_Rect(run->visBounds))), | 1447 | init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))), |
1382 | tmBannerBackground_ColorId); | 1448 | visPos.y + height_Rect(run->visBounds))), |
1383 | const iChar icon = siteIcon_GmDocument(doc); | 1449 | tmBannerBackground_ColorId); |
1384 | iString bannerText; | 1450 | const iChar icon = siteIcon_GmDocument(doc); |
1385 | init_String(&bannerText); | 1451 | iString bannerText; |
1386 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | 1452 | init_String(&bannerText); |
1387 | if (icon) { | 1453 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); |
1388 | appendChar_String(&bannerText, icon); | 1454 | if (icon) { |
1389 | const iRect iconRect = visualBounds_Text(banner_FontId, range_String(&bannerText)); | 1455 | appendChar_String(&bannerText, icon); |
1456 | const iRect iconRect = visualBounds_Text(banner_FontId, range_String(&bannerText)); | ||
1457 | drawRange_Text(run->font, | ||
1458 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | ||
1459 | tmBannerIcon_ColorId, | ||
1460 | range_String(&bannerText)); | ||
1461 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | ||
1462 | } | ||
1390 | drawRange_Text(run->font, | 1463 | drawRange_Text(run->font, |
1391 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | 1464 | bpos, |
1392 | tmBannerIcon_ColorId, | 1465 | tmBannerTitle_ColorId, |
1393 | range_String(&bannerText)); | 1466 | isEmpty_String(d->widget->titleUser) ? run->text |
1394 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | 1467 | : range_String(d->widget->titleUser)); |
1468 | deinit_String(&bannerText); | ||
1395 | } | 1469 | } |
1396 | drawRange_Text(run->font, | ||
1397 | bpos, | ||
1398 | tmBannerTitle_ColorId, | ||
1399 | isEmpty_String(d->widget->titleUser) ? run->text | ||
1400 | : range_String(d->widget->titleUser)); | ||
1401 | deinit_String(&bannerText); | ||
1402 | } | 1470 | } |
1403 | else { | 1471 | else { |
1404 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { | 1472 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { |
@@ -1413,10 +1481,11 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1413 | } | 1481 | } |
1414 | } | 1482 | } |
1415 | drawRange_Text(run->font, visPos, fg, run->text); | 1483 | drawRange_Text(run->font, visPos, fg, run->text); |
1484 | // printf("{%s}\n", cstr_Rangecc(run->text)); | ||
1416 | runDrawn:; | 1485 | runDrawn:; |
1417 | } | 1486 | } |
1418 | /* Presentation of links. */ | 1487 | /* Presentation of links. */ |
1419 | if (run->linkId && ~run->flags & decoration_GmRunFlag) { | 1488 | if (run->linkId && ~run->flags & decoration_GmRunFlag && d->pass == dynamic_DrawRunPass) { |
1420 | const int metaFont = paragraph_FontId; | 1489 | const int metaFont = paragraph_FontId; |
1421 | /* TODO: Show status of an ongoing media request. */ | 1490 | /* TODO: Show status of an ongoing media request. */ |
1422 | const int flags = linkFlags_GmDocument(doc, run->linkId); | 1491 | const int flags = linkFlags_GmDocument(doc, run->linkId); |
@@ -1510,27 +1579,97 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1510 | } | 1579 | } |
1511 | } | 1580 | } |
1512 | } | 1581 | } |
1513 | |||
1514 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); | 1582 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); |
1515 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); | 1583 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); |
1516 | } | 1584 | } |
1517 | 1585 | ||
1586 | static iRangei intersect_Rangei_(iRangei a, iRangei b) { | ||
1587 | if (a.end < b.start || a.start > b.end) { | ||
1588 | return (iRangei){ 0, 0 }; | ||
1589 | } | ||
1590 | return (iRangei){ iMax(a.start, b.start), iMin(a.end, b.end) }; | ||
1591 | } | ||
1592 | |||
1593 | iLocalDef iBool isEmpty_Rangei_(iRangei d) { | ||
1594 | return size_Range(&d) == 0; | ||
1595 | } | ||
1596 | |||
1518 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 1597 | static void draw_DocumentWidget_(const iDocumentWidget *d) { |
1519 | const iWidget *w = constAs_Widget(d); | 1598 | const iWidget *w = constAs_Widget(d); |
1520 | const iRect bounds = bounds_Widget(w); | 1599 | const iRect bounds = bounds_Widget(w); |
1600 | const iInt2 origin = topLeft_Rect(bounds); | ||
1601 | const iRangei visRange = visibleRange_DocumentWidget_(d); | ||
1521 | draw_Widget(w); | 1602 | draw_Widget(w); |
1522 | iDrawContext ctx = { | 1603 | allocVisBuffer_DocumentWidget_(iConstCast(iDocumentWidget *, d)); |
1523 | .widget = d, | 1604 | iDrawContext ctxDynamic = { |
1524 | .widgetBounds = /* omit scrollbar width */ | 1605 | .pass = dynamic_DrawRunPass, |
1525 | adjusted_Rect(bounds, zero_I2(), init_I2(-constAs_Widget(d->scroll)->rect.size.x, 0)), | 1606 | .widget = d, |
1526 | .bounds = documentBounds_DocumentWidget_(d), | 1607 | .widgetBounds = adjusted_Rect(bounds, |
1608 | zero_I2(), | ||
1609 | init_I2(-constAs_Widget(d->scroll)->rect.size.x, 0)), /* omit scrollbar width */ | ||
1610 | .bounds = documentBounds_DocumentWidget_(d), | ||
1527 | .showLinkNumbers = d->showLinkNumbers, | 1611 | .showLinkNumbers = d->showLinkNumbers, |
1528 | }; | 1612 | }; |
1529 | init_Paint(&ctx.paint); | 1613 | iDrawContext ctxStatic = ctxDynamic; |
1530 | fillRect_Paint(&ctx.paint, bounds, tmBackground_ColorId); | 1614 | ctxStatic.pass = static_DrawRunPass; |
1531 | setClip_Paint(&ctx.paint, bounds); | 1615 | subv_I2(&ctxStatic.widgetBounds.pos, origin); |
1532 | render_GmDocument(d->doc, visibleRange_DocumentWidget_(d), drawRun_DrawContext_, &ctx); | 1616 | subv_I2(&ctxStatic.bounds.pos, origin); |
1533 | unsetClip_Paint(&ctx.paint); | 1617 | SDL_Renderer *render = get_Window()->render; |
1618 | /* Static content. */ { | ||
1619 | iPaint *p = &ctxStatic.paint; | ||
1620 | init_Paint(p); | ||
1621 | const int vbSrc = d->visBufferIndex; | ||
1622 | const int vbDst = d->visBufferIndex ^ 1; | ||
1623 | iRangei drawRange = visRange; | ||
1624 | iAssert(d->visBuffer[vbDst]); | ||
1625 | beginTarget_Paint(p, d->visBuffer[vbDst]); | ||
1626 | const iRect visBufferRect = { zero_I2(), d->visBufferSize }; | ||
1627 | iRect drawRect = visBufferRect; | ||
1628 | if (!isEmpty_Rangei_(intersect_Rangei_(visRange, d->visBufferValidRange))) { | ||
1629 | if (visRange.start < d->visBufferValidRange.start) { | ||
1630 | drawRange = (iRangei){ visRange.start, d->visBufferValidRange.start }; | ||
1631 | } | ||
1632 | else { | ||
1633 | drawRange = (iRangei){ d->visBufferValidRange.end, visRange.end }; | ||
1634 | } | ||
1635 | if (isEmpty_Range(&drawRange)) { | ||
1636 | SDL_RenderCopy(render, d->visBuffer[vbSrc], NULL, NULL); | ||
1637 | } | ||
1638 | else { | ||
1639 | SDL_RenderCopy( | ||
1640 | render, | ||
1641 | d->visBuffer[vbSrc], | ||
1642 | NULL, | ||
1643 | &(SDL_Rect){ 0, | ||
1644 | documentToWindowY_DocumentWidget_(d, d->visBufferValidRange.start) - origin.y, | ||
1645 | d->visBufferSize.x, | ||
1646 | d->visBufferSize.y }); | ||
1647 | drawRect = init_Rect(0, | ||
1648 | documentToWindowY_DocumentWidget_(d, drawRange.start) - origin.y, | ||
1649 | d->visBufferSize.x, | ||
1650 | size_Range(&drawRange)); | ||
1651 | } | ||
1652 | } | ||
1653 | if (!isEmpty_Range(&drawRange)) { | ||
1654 | setClip_Paint(p, drawRect); | ||
1655 | fillRect_Paint(p, drawRect, tmBackground_ColorId); // vbDst == 1 ? blue_ColorId : red_ColorId | ||
1656 | render_GmDocument(d->doc, drawRange, drawRun_DrawContext_, &ctxStatic); | ||
1657 | unsetClip_Paint(p); | ||
1658 | } | ||
1659 | endTarget_Paint(p); | ||
1660 | SDL_RenderCopy(render, d->visBuffer[vbDst], NULL, | ||
1661 | &(SDL_Rect){ origin.x, origin.y, bounds.size.x, bounds.size.y } ); | ||
1662 | iConstCast(iDocumentWidget *, d)->visBufferValidRange = visRange; | ||
1663 | iConstCast(iDocumentWidget *, d)->visBufferIndex = vbDst; | ||
1664 | } | ||
1665 | /* Dynamic content. */ { | ||
1666 | iPaint *p = &ctxDynamic.paint; | ||
1667 | init_Paint(p); | ||
1668 | setClip_Paint(p, bounds); | ||
1669 | render_GmDocument(d->doc, visRange, drawRun_DrawContext_, &ctxDynamic); | ||
1670 | unsetClip_Paint(p); | ||
1671 | } | ||
1672 | |||
1534 | // drawRect_Paint(&ctx.paint, | 1673 | // drawRect_Paint(&ctx.paint, |
1535 | // moved_Rect((iRect){ zero_I2(), size_GmDocument(d->doc) }, | 1674 | // moved_Rect((iRect){ zero_I2(), size_GmDocument(d->doc) }, |
1536 | // add_I2(topLeft_Rect(ctx.bounds), init_I2(0, -d->scrollY))), | 1675 | // add_I2(topLeft_Rect(ctx.bounds), init_I2(0, -d->scrollY))), |
diff --git a/src/ui/paint.c b/src/ui/paint.c index 85e75f15..264ca0d8 100644 --- a/src/ui/paint.c +++ b/src/ui/paint.c | |||
@@ -1,5 +1,7 @@ | |||
1 | #include "paint.h" | 1 | #include "paint.h" |
2 | 2 | ||
3 | #include <SDL_version.h> | ||
4 | |||
3 | iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) { | 5 | iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) { |
4 | iAssert(d->dst); | 6 | iAssert(d->dst); |
5 | return d->dst->render; | 7 | return d->dst->render; |
@@ -12,6 +14,18 @@ static void setColor_Paint_(const iPaint *d, int color) { | |||
12 | 14 | ||
13 | void init_Paint(iPaint *d) { | 15 | void init_Paint(iPaint *d) { |
14 | d->dst = get_Window(); | 16 | d->dst = get_Window(); |
17 | d->oldTarget = NULL; | ||
18 | } | ||
19 | |||
20 | void beginTarget_Paint(iPaint *d, SDL_Texture *target) { | ||
21 | SDL_Renderer *rend = renderer_Paint_(d); | ||
22 | d->oldTarget = SDL_GetRenderTarget(rend); | ||
23 | SDL_SetRenderTarget(rend, target); | ||
24 | } | ||
25 | |||
26 | void endTarget_Paint(iPaint *d) { | ||
27 | SDL_SetRenderTarget(renderer_Paint_(d), d->oldTarget); | ||
28 | d->oldTarget = NULL; | ||
15 | } | 29 | } |
16 | 30 | ||
17 | void setClip_Paint(iPaint *d, iRect rect) { | 31 | void setClip_Paint(iPaint *d, iRect rect) { |
@@ -19,8 +33,12 @@ void setClip_Paint(iPaint *d, iRect rect) { | |||
19 | } | 33 | } |
20 | 34 | ||
21 | void unsetClip_Paint(iPaint *d) { | 35 | void unsetClip_Paint(iPaint *d) { |
36 | #if SDL_VERSION_ATLEAST(2, 0, 12) | ||
37 | SDL_RenderSetClipRect(renderer_Paint_(d), NULL); | ||
38 | #else | ||
22 | const SDL_Rect winRect = { 0, 0, d->dst->root->rect.size.x, d->dst->root->rect.size.y }; | 39 | const SDL_Rect winRect = { 0, 0, d->dst->root->rect.size.x, d->dst->root->rect.size.y }; |
23 | SDL_RenderSetClipRect(renderer_Paint_(d), &winRect); | 40 | SDL_RenderSetClipRect(renderer_Paint_(d), &winRect); |
41 | #endif | ||
24 | } | 42 | } |
25 | 43 | ||
26 | void drawRect_Paint(const iPaint *d, iRect rect, int color) { | 44 | void drawRect_Paint(const iPaint *d, iRect rect, int color) { |
@@ -56,3 +74,9 @@ void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t count, int col | |||
56 | setColor_Paint_(d, color); | 74 | setColor_Paint_(d, color); |
57 | SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) points, count); | 75 | SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) points, count); |
58 | } | 76 | } |
77 | |||
78 | iInt2 size_SDLTexture(SDL_Texture *d) { | ||
79 | iInt2 size; | ||
80 | SDL_QueryTexture(d, NULL, NULL, &size.x, &size.y); | ||
81 | return size; | ||
82 | } | ||
diff --git a/src/ui/paint.h b/src/ui/paint.h index 90c5f504..5b29b176 100644 --- a/src/ui/paint.h +++ b/src/ui/paint.h | |||
@@ -9,12 +9,16 @@ iDeclareType(Paint) | |||
9 | 9 | ||
10 | struct Impl_Paint { | 10 | struct Impl_Paint { |
11 | iWindow *dst; | 11 | iWindow *dst; |
12 | SDL_Texture *oldTarget; | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | void init_Paint (iPaint *); | 15 | void init_Paint (iPaint *); |
15 | 16 | ||
16 | void setClip_Paint (iPaint *, iRect rect); | 17 | void beginTarget_Paint (iPaint *, SDL_Texture *target); |
17 | void unsetClip_Paint (iPaint *); | 18 | void endTarget_Paint (iPaint *); |
19 | |||
20 | void setClip_Paint (iPaint *, iRect rect); | ||
21 | void unsetClip_Paint (iPaint *); | ||
18 | 22 | ||
19 | void drawRect_Paint (const iPaint *, iRect rect, int color); | 23 | void drawRect_Paint (const iPaint *, iRect rect, int color); |
20 | void drawRectThickness_Paint (const iPaint *, iRect rect, int thickness, int color); | 24 | void drawRectThickness_Paint (const iPaint *, iRect rect, int thickness, int color); |
@@ -31,3 +35,5 @@ iLocalDef void drawHLine_Paint(const iPaint *d, iInt2 pos, int len, int color) { | |||
31 | iLocalDef void drawVLine_Paint(const iPaint *d, iInt2 pos, int len, int color) { | 35 | iLocalDef void drawVLine_Paint(const iPaint *d, iInt2 pos, int len, int color) { |
32 | drawLine_Paint(d, pos, addY_I2(pos, len), color); | 36 | drawLine_Paint(d, pos, addY_I2(pos, len), color); |
33 | } | 37 | } |
38 | |||
39 | iInt2 size_SDLTexture (SDL_Texture *); | ||
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index de576f72..71b641d4 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -59,10 +59,17 @@ struct Impl_SidebarWidget { | |||
59 | iWidget *resizer; | 59 | iWidget *resizer; |
60 | SDL_Cursor *resizeCursor; | 60 | SDL_Cursor *resizeCursor; |
61 | iWidget *menu; | 61 | iWidget *menu; |
62 | SDL_Texture *visBuffer; | ||
63 | iBool visBufferValid; | ||
62 | }; | 64 | }; |
63 | 65 | ||
64 | iDefineObjectConstruction(SidebarWidget) | 66 | iDefineObjectConstruction(SidebarWidget) |
65 | 67 | ||
68 | static void invalidate_SidebarWidget_(iSidebarWidget *d) { | ||
69 | d->visBufferValid = iFalse; | ||
70 | refresh_Widget(as_Widget(d)); | ||
71 | } | ||
72 | |||
66 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { | 73 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { |
67 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; | 74 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; |
68 | } | 75 | } |
@@ -157,7 +164,7 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
157 | break; | 164 | break; |
158 | } | 165 | } |
159 | updateVisible_SidebarWidget_(d); | 166 | updateVisible_SidebarWidget_(d); |
160 | refresh_Widget(as_Widget(d)); | 167 | invalidate_SidebarWidget_(d); |
161 | } | 168 | } |
162 | 169 | ||
163 | void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | 170 | void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { |
@@ -166,8 +173,8 @@ void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | |||
166 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { | 173 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { |
167 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); | 174 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); |
168 | } | 175 | } |
169 | const float heights[max_SidebarMode] = { 1.5f, 3, 3, 1.2f }; | 176 | const float heights[max_SidebarMode] = { 1.333f, 3, 3, 1.2f }; |
170 | d->itemHeight = heights[mode] * lineHeight_Text(default_FontId); | 177 | d->itemHeight = heights[mode] * lineHeight_Text(uiContent_FontId); |
171 | } | 178 | } |
172 | 179 | ||
173 | enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { | 180 | enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { |
@@ -217,7 +224,7 @@ void init_SidebarWidget(iSidebarWidget *d) { | |||
217 | frameless_WidgetFlag | expand_WidgetFlag); | 224 | frameless_WidgetFlag | expand_WidgetFlag); |
218 | d->maxButtonLabelWidth = | 225 | d->maxButtonLabelWidth = |
219 | iMaxi(d->maxButtonLabelWidth, | 226 | iMaxi(d->maxButtonLabelWidth, |
220 | 3 * gap_UI + measure_Text(default_FontId, normalModeLabels_[i]).x); | 227 | 3 * gap_UI + measure_Text(uiLabel_FontId, normalModeLabels_[i]).x); |
221 | } | 228 | } |
222 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 229 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); |
223 | setThumb_ScrollWidget(d->scroll, 0, 0); | 230 | setThumb_ScrollWidget(d->scroll, 0, 0); |
@@ -232,12 +239,15 @@ void init_SidebarWidget(iSidebarWidget *d) { | |||
232 | setBackgroundColor_Widget(d->resizer, none_ColorId); | 239 | setBackgroundColor_Widget(d->resizer, none_ColorId); |
233 | d->resizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); | 240 | d->resizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); |
234 | d->menu = NULL; | 241 | d->menu = NULL; |
242 | d->visBuffer = NULL; | ||
243 | d->visBufferValid = iFalse; | ||
235 | } | 244 | } |
236 | 245 | ||
237 | void deinit_SidebarWidget(iSidebarWidget *d) { | 246 | void deinit_SidebarWidget(iSidebarWidget *d) { |
238 | SDL_FreeCursor(d->resizeCursor); | 247 | SDL_FreeCursor(d->resizeCursor); |
239 | clearItems_SidebarWidget_(d); | 248 | clearItems_SidebarWidget_(d); |
240 | deinit_Array(&d->items); | 249 | deinit_Array(&d->items); |
250 | SDL_DestroyTexture(d->visBuffer); | ||
241 | } | 251 | } |
242 | 252 | ||
243 | static int visCount_SidebarWidget_(const iSidebarWidget *d) { | 253 | static int visCount_SidebarWidget_(const iSidebarWidget *d) { |
@@ -277,14 +287,18 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { | |||
277 | } | 287 | } |
278 | 288 | ||
279 | static void scroll_SidebarWidget_(iSidebarWidget *d, int offset) { | 289 | static void scroll_SidebarWidget_(iSidebarWidget *d, int offset) { |
290 | const int oldScroll = d->scrollY; | ||
280 | d->scrollY += offset; | 291 | d->scrollY += offset; |
281 | if (d->scrollY < 0) { | 292 | if (d->scrollY < 0) { |
282 | d->scrollY = 0; | 293 | d->scrollY = 0; |
283 | } | 294 | } |
284 | const int scrollMax = scrollMax_SidebarWidget_(d); | 295 | const int scrollMax = scrollMax_SidebarWidget_(d); |
285 | d->scrollY = iMin(d->scrollY, scrollMax); | 296 | d->scrollY = iMin(d->scrollY, scrollMax); |
286 | updateVisible_SidebarWidget_(d); | 297 | if (oldScroll != d->scrollY) { |
287 | refresh_Widget(as_Widget(d)); | 298 | d->hoverItem = iInvalidPos; |
299 | updateVisible_SidebarWidget_(d); | ||
300 | invalidate_SidebarWidget_(d); | ||
301 | } | ||
288 | } | 302 | } |
289 | 303 | ||
290 | static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) { | 304 | static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) { |
@@ -320,7 +334,7 @@ void setWidth_SidebarWidget(iSidebarWidget *d, int width) { | |||
320 | checkModeButtonLayout_SidebarWidget_(d); | 334 | checkModeButtonLayout_SidebarWidget_(d); |
321 | if (!isRefreshPending_App()) { | 335 | if (!isRefreshPending_App()) { |
322 | updateSize_DocumentWidget(document_App()); | 336 | updateSize_DocumentWidget(document_App()); |
323 | refresh_Widget(w); | 337 | invalidate_SidebarWidget_(d); |
324 | } | 338 | } |
325 | } | 339 | } |
326 | 340 | ||
@@ -349,6 +363,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
349 | if (isResize_UserEvent(ev)) { | 363 | if (isResize_UserEvent(ev)) { |
350 | updateVisible_SidebarWidget_(d); | 364 | updateVisible_SidebarWidget_(d); |
351 | checkModeButtonLayout_SidebarWidget_(d); | 365 | checkModeButtonLayout_SidebarWidget_(d); |
366 | invalidate_SidebarWidget_(d); | ||
352 | } | 367 | } |
353 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | 368 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { |
354 | const char *cmd = command_UserEvent(ev); | 369 | const char *cmd = command_UserEvent(ev); |
@@ -392,6 +407,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
392 | setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); | 407 | setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); |
393 | if (isVisible_Widget(w)) { | 408 | if (isVisible_Widget(w)) { |
394 | w->rect.size.x = d->width; | 409 | w->rect.size.x = d->width; |
410 | invalidate_SidebarWidget_(d); | ||
395 | } | 411 | } |
396 | arrange_Widget(w->parent); | 412 | arrange_Widget(w->parent); |
397 | updateSize_DocumentWidget(document_App()); | 413 | updateSize_DocumentWidget(document_App()); |
@@ -401,13 +417,16 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
401 | else if (equal_Command(cmd, "scroll.moved")) { | 417 | else if (equal_Command(cmd, "scroll.moved")) { |
402 | d->scrollY = arg_Command(command_UserEvent(ev)); | 418 | d->scrollY = arg_Command(command_UserEvent(ev)); |
403 | d->hoverItem = iInvalidPos; | 419 | d->hoverItem = iInvalidPos; |
404 | refresh_Widget(w); | 420 | invalidate_SidebarWidget_(d); |
405 | return iTrue; | 421 | return iTrue; |
406 | } | 422 | } |
407 | else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { | 423 | else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { |
408 | d->scrollY = 0; | 424 | d->scrollY = 0; |
409 | updateItems_SidebarWidget_(d); | 425 | updateItems_SidebarWidget_(d); |
410 | } | 426 | } |
427 | else if (equal_Command(cmd, "theme.changed")) { | ||
428 | invalidate_SidebarWidget_(d); | ||
429 | } | ||
411 | else if (equal_Command(cmd, "bookmark.copy")) { | 430 | else if (equal_Command(cmd, "bookmark.copy")) { |
412 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 431 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); |
413 | if (d->mode == bookmarks_SidebarMode && item) { | 432 | if (d->mode == bookmarks_SidebarMode && item) { |
@@ -454,7 +473,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
454 | } | 473 | } |
455 | if (hover != d->hoverItem) { | 474 | if (hover != d->hoverItem) { |
456 | d->hoverItem = hover; | 475 | d->hoverItem = hover; |
457 | refresh_Widget(w); | 476 | invalidate_SidebarWidget_(d); |
458 | } | 477 | } |
459 | } | 478 | } |
460 | if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { | 479 | if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { |
@@ -464,8 +483,6 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
464 | #else | 483 | #else |
465 | scroll_SidebarWidget_(d, -ev->wheel.y * 3 * d->itemHeight); | 484 | scroll_SidebarWidget_(d, -ev->wheel.y * 3 * d->itemHeight); |
466 | #endif | 485 | #endif |
467 | d->hoverItem = iInvalidPos; | ||
468 | refresh_Widget(w); | ||
469 | return iTrue; | 486 | return iTrue; |
470 | } | 487 | } |
471 | if (d->menu && ev->type == SDL_MOUSEBUTTONDOWN) { | 488 | if (d->menu && ev->type == SDL_MOUSEBUTTONDOWN) { |
@@ -475,14 +492,14 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
475 | } | 492 | } |
476 | switch (processEvent_Click(&d->click, ev)) { | 493 | switch (processEvent_Click(&d->click, ev)) { |
477 | case started_ClickResult: | 494 | case started_ClickResult: |
478 | refresh_Widget(w); | 495 | invalidate_SidebarWidget_(d); |
479 | break; | 496 | break; |
480 | case finished_ClickResult: | 497 | case finished_ClickResult: |
481 | if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && | 498 | if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && |
482 | d->hoverItem != iInvalidSize) { | 499 | d->hoverItem != iInvalidSize) { |
483 | itemClicked_SidebarWidget_(d, d->hoverItem); | 500 | itemClicked_SidebarWidget_(d, d->hoverItem); |
484 | } | 501 | } |
485 | refresh_Widget(w); | 502 | invalidate_SidebarWidget_(d); |
486 | break; | 503 | break; |
487 | default: | 504 | default: |
488 | break; | 505 | break; |
@@ -490,64 +507,93 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
490 | return processEvent_Widget(w, ev); | 507 | return processEvent_Widget(w, ev); |
491 | } | 508 | } |
492 | 509 | ||
510 | static void allocVisBuffer_SidebarWidget_(iSidebarWidget *d) { | ||
511 | const iInt2 size = contentBounds_SidebarWidget_(d).size; | ||
512 | if (!d->visBuffer || !isEqual_I2(size_SDLTexture(d->visBuffer), size)) { | ||
513 | if (d->visBuffer) { | ||
514 | SDL_DestroyTexture(d->visBuffer); | ||
515 | } | ||
516 | d->visBuffer = SDL_CreateTexture(renderer_Window(get_Window()), | ||
517 | SDL_PIXELFORMAT_RGBA8888, | ||
518 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
519 | size.x, | ||
520 | size.y); | ||
521 | SDL_SetTextureBlendMode(d->visBuffer, SDL_BLENDMODE_NONE); | ||
522 | d->visBufferValid = iFalse; | ||
523 | } | ||
524 | } | ||
525 | |||
493 | static void draw_SidebarWidget_(const iSidebarWidget *d) { | 526 | static void draw_SidebarWidget_(const iSidebarWidget *d) { |
494 | const iWidget *w = constAs_Widget(d); | 527 | const iWidget *w = constAs_Widget(d); |
495 | const iRect bounds = contentBounds_SidebarWidget_(d); | 528 | const iRect bounds = contentBounds_SidebarWidget_(d); |
496 | const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); | 529 | const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); |
497 | iPaint p; | 530 | iPaint p; |
498 | init_Paint(&p); | 531 | init_Paint(&p); |
499 | fillRect_Paint(&p, | 532 | const int bg = |
500 | bounds_Widget(w), | 533 | d->mode == documentOutline_SidebarMode ? tmBackground_ColorId : uiBackground_ColorId; |
501 | d->mode == documentOutline_SidebarMode ? tmBackground_ColorId | 534 | fillRect_Paint(&p, bounds_Widget(w), bg); /* TODO: should do only the mode buttons area */ |
502 | : uiBackground_ColorId); | 535 | if (!d->visBufferValid) { |
503 | /* Draw the items. */ { | 536 | allocVisBuffer_SidebarWidget_(iConstCast(iSidebarWidget *, d)); |
504 | const int font = default_FontId; | 537 | iRect bufBounds = bounds; |
505 | const iRanges visRange = visRange_SidebarWidget_(d); | 538 | bufBounds.pos = zero_I2(); |
506 | iInt2 pos = addY_I2(topLeft_Rect(bounds), -(d->scrollY % d->itemHeight)); | 539 | beginTarget_Paint(&p, d->visBuffer); |
507 | for (size_t i = visRange.start; i < visRange.end; i++) { | 540 | fillRect_Paint(&p, bufBounds, bg); |
508 | const iSidebarItem *item = constAt_Array(&d->items, i); | 541 | /* Draw the items. */ { |
509 | const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; | 542 | const int font = uiContent_FontId; |
510 | const iBool isHover = (d->hoverItem == i); | 543 | const iRanges visRange = visRange_SidebarWidget_(d); |
511 | setClip_Paint(&p, intersect_Rect(itemRect, bounds)); | 544 | iInt2 pos = addY_I2(topLeft_Rect(bufBounds), -(d->scrollY % d->itemHeight)); |
512 | if (isHover) { | 545 | for (size_t i = visRange.start; i < visRange.end; i++) { |
513 | fillRect_Paint(&p, | 546 | const iSidebarItem *item = constAt_Array(&d->items, i); |
514 | itemRect, | 547 | const iRect itemRect = { pos, init_I2(width_Rect(bufBounds), d->itemHeight) }; |
515 | isPressing ? uiBackgroundPressed_ColorId | 548 | const iBool isHover = (d->hoverItem == i); |
516 | : uiBackgroundFramelessHover_ColorId); | 549 | setClip_Paint(&p, intersect_Rect(itemRect, bufBounds)); |
517 | } | 550 | if (isHover) { |
518 | if (d->mode == documentOutline_SidebarMode) { | 551 | fillRect_Paint(&p, |
519 | const int fg = | 552 | itemRect, |
520 | isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | 553 | isPressing ? uiBackgroundPressed_ColorId |
521 | : (tmHeading1_ColorId + item->indent / (4 * gap_UI)); | 554 | : uiBackgroundFramelessHover_ColorId); |
522 | drawRange_Text(font, init_I2(pos.x + 3 * gap_UI + item->indent, | 555 | } |
523 | mid_Rect(itemRect).y - lineHeight_Text(font) / 2), | 556 | if (d->mode == documentOutline_SidebarMode) { |
524 | fg, range_String(&item->label)); | 557 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId |
525 | } | 558 | : uiTextFramelessHover_ColorId) |
526 | else if (d->mode == bookmarks_SidebarMode) { | 559 | : (tmHeading1_ColorId + item->indent / (4 * gap_UI)); |
527 | const int fg = | 560 | drawRange_Text(font, |
528 | isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | 561 | init_I2(pos.x + 3 * gap_UI + item->indent, |
529 | : uiText_ColorId; | 562 | mid_Rect(itemRect).y - lineHeight_Text(font) / 2), |
530 | iString str; | 563 | fg, |
531 | init_String(&str); | 564 | range_String(&item->label)); |
532 | appendChar_String(&str, item->icon ? item->icon : 0x1f588); | 565 | } |
533 | const iRect iconArea = { addX_I2(pos, gap_UI), init_I2(7 * gap_UI, d->itemHeight) }; | 566 | else if (d->mode == bookmarks_SidebarMode) { |
534 | drawCentered_Text(font, | 567 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId |
535 | iconArea, | 568 | : uiTextFramelessHover_ColorId) |
536 | iTrue, | 569 | : uiText_ColorId; |
537 | isHover | 570 | iString str; |
538 | ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) | 571 | init_String(&str); |
539 | : uiIcon_ColorId, | 572 | appendChar_String(&str, item->icon ? item->icon : 0x1f588); |
540 | "%s", | 573 | const iRect iconArea = { addX_I2(pos, gap_UI), |
541 | cstr_String(&str)); | 574 | init_I2(7 * gap_UI, d->itemHeight) }; |
542 | deinit_String(&str); | 575 | drawCentered_Text( |
543 | iInt2 textPos = | 576 | font, |
544 | addY_I2(topRight_Rect(iconArea), (d->itemHeight - lineHeight_Text(font)) / 2); | 577 | iconArea, |
545 | drawRange_Text(font, textPos, fg, range_String(&item->label)); | 578 | iTrue, |
579 | isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) | ||
580 | : uiIcon_ColorId, | ||
581 | "%s", | ||
582 | cstr_String(&str)); | ||
583 | deinit_String(&str); | ||
584 | iInt2 textPos = addY_I2(topRight_Rect(iconArea), | ||
585 | (d->itemHeight - lineHeight_Text(font)) / 2); | ||
586 | drawRange_Text(font, textPos, fg, range_String(&item->label)); | ||
587 | } | ||
588 | unsetClip_Paint(&p); | ||
589 | pos.y += d->itemHeight; | ||
546 | } | 590 | } |
547 | unsetClip_Paint(&p); | ||
548 | pos.y += d->itemHeight; | ||
549 | } | 591 | } |
592 | endTarget_Paint(&p); | ||
593 | iConstCast(iSidebarWidget *, d)->visBufferValid = iTrue; | ||
550 | } | 594 | } |
595 | SDL_RenderCopy( | ||
596 | renderer_Window(get_Window()), d->visBuffer, NULL, (const SDL_Rect *) &bounds); | ||
551 | draw_Widget(w); | 597 | draw_Widget(w); |
552 | drawVLine_Paint(&p, | 598 | drawVLine_Paint(&p, |
553 | addX_I2(topRight_Rect(bounds_Widget(w)), -1), | 599 | addX_I2(topRight_Rect(bounds_Widget(w)), -1), |
diff --git a/src/ui/text.c b/src/ui/text.c index f947c4df..1e702eee 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -120,7 +120,7 @@ static void initFonts_Text_(iText *d) { | |||
120 | int symbolsFont; | 120 | int symbolsFont; |
121 | } fontData[max_FontId] = { | 121 | } fontData[max_FontId] = { |
122 | { &fontSourceSansProRegular_Embedded, fontSize_UI, defaultSymbols_FontId }, | 122 | { &fontSourceSansProRegular_Embedded, fontSize_UI, defaultSymbols_FontId }, |
123 | { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.666f, defaultLargeSymbols_FontId }, | 123 | { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.150f, defaultMediumSymbols_FontId }, |
124 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, defaultSymbols_FontId }, | 124 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, defaultSymbols_FontId }, |
125 | { &fontFiraSansRegular_Embedded, textSize, symbols_FontId }, | 125 | { &fontFiraSansRegular_Embedded, textSize, symbols_FontId }, |
126 | { &fontFiraMonoRegular_Embedded, textSize * 0.866f, smallSymbols_FontId }, | 126 | { &fontFiraMonoRegular_Embedded, textSize * 0.866f, smallSymbols_FontId }, |
@@ -133,14 +133,14 @@ static void initFonts_Text_(iText *d) { | |||
133 | { &fontFiraSansBold_Embedded, textSize * 2.000f, hugeSymbols_FontId }, | 133 | { &fontFiraSansBold_Embedded, textSize * 2.000f, hugeSymbols_FontId }, |
134 | { &fontFiraSansLight_Embedded, textSize * 1.666f, largeSymbols_FontId }, | 134 | { &fontFiraSansLight_Embedded, textSize * 1.666f, largeSymbols_FontId }, |
135 | { &fontSymbola_Embedded, fontSize_UI, defaultSymbols_FontId }, | 135 | { &fontSymbola_Embedded, fontSize_UI, defaultSymbols_FontId }, |
136 | { &fontSymbola_Embedded, fontSize_UI * 1.666f, defaultLargeSymbols_FontId }, | 136 | { &fontSymbola_Embedded, fontSize_UI * 1.150f, defaultMediumSymbols_FontId }, |
137 | { &fontSymbola_Embedded, textSize, symbols_FontId }, | 137 | { &fontSymbola_Embedded, textSize, symbols_FontId }, |
138 | { &fontSymbola_Embedded, textSize * 1.333f, mediumSymbols_FontId }, | 138 | { &fontSymbola_Embedded, textSize * 1.333f, mediumSymbols_FontId }, |
139 | { &fontSymbola_Embedded, textSize * 1.666f, largeSymbols_FontId }, | 139 | { &fontSymbola_Embedded, textSize * 1.666f, largeSymbols_FontId }, |
140 | { &fontSymbola_Embedded, textSize * 2.000f, hugeSymbols_FontId }, | 140 | { &fontSymbola_Embedded, textSize * 2.000f, hugeSymbols_FontId }, |
141 | { &fontSymbola_Embedded, textSize * 0.866f, smallSymbols_FontId }, | 141 | { &fontSymbola_Embedded, textSize * 0.866f, smallSymbols_FontId }, |
142 | { &fontNotoEmojiRegular_Embedded, fontSize_UI, defaultSymbols_FontId }, | 142 | { &fontNotoEmojiRegular_Embedded, fontSize_UI, defaultSymbols_FontId }, |
143 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.666f, defaultLargeSymbols_FontId }, | 143 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.150f, defaultMediumSymbols_FontId }, |
144 | { &fontNotoEmojiRegular_Embedded, textSize, symbols_FontId }, | 144 | { &fontNotoEmojiRegular_Embedded, textSize, symbols_FontId }, |
145 | { &fontNotoEmojiRegular_Embedded, textSize * 1.333f, mediumSymbols_FontId }, | 145 | { &fontNotoEmojiRegular_Embedded, textSize * 1.333f, mediumSymbols_FontId }, |
146 | { &fontNotoEmojiRegular_Embedded, textSize * 1.666f, largeSymbols_FontId }, | 146 | { &fontNotoEmojiRegular_Embedded, textSize * 1.666f, largeSymbols_FontId }, |
@@ -306,10 +306,11 @@ static void cache_Font_(iFont *d, iGlyph *glyph, int hoff) { | |||
306 | } | 306 | } |
307 | /* Determine placement in the glyph cache texture, advancing in rows. */ | 307 | /* Determine placement in the glyph cache texture, advancing in rows. */ |
308 | glRect->pos = assignCachePos_Text_(txt, glRect->size); | 308 | glRect->pos = assignCachePos_Text_(txt, glRect->size); |
309 | SDL_Texture *oldTarget = SDL_GetRenderTarget(render); | ||
309 | SDL_SetRenderTarget(render, txt->cache); | 310 | SDL_SetRenderTarget(render, txt->cache); |
310 | const SDL_Rect dstRect = sdlRect_(*glRect); | 311 | const SDL_Rect dstRect = sdlRect_(*glRect); |
311 | SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect); | 312 | SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect); |
312 | SDL_SetRenderTarget(render, NULL); | 313 | SDL_SetRenderTarget(render, oldTarget); |
313 | if (tex) { | 314 | if (tex) { |
314 | SDL_DestroyTexture(tex); | 315 | SDL_DestroyTexture(tex); |
315 | iAssert(surface); | 316 | iAssert(surface); |
@@ -670,9 +671,10 @@ void init_TextBuf(iTextBuf *d, int font, const char *text) { | |||
670 | d->size.x, | 671 | d->size.x, |
671 | d->size.y); | 672 | d->size.y); |
672 | SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); | 673 | SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND); |
674 | SDL_Texture *oldTarget = SDL_GetRenderTarget(render); | ||
673 | SDL_SetRenderTarget(render, d->texture); | 675 | SDL_SetRenderTarget(render, d->texture); |
674 | draw_Text_(font, zero_I2(), white_ColorId, range_CStr(text)); | 676 | draw_Text_(font, zero_I2(), white_ColorId, range_CStr(text)); |
675 | SDL_SetRenderTarget(render, NULL); | 677 | SDL_SetRenderTarget(render, oldTarget); |
676 | } | 678 | } |
677 | 679 | ||
678 | void deinit_TextBuf(iTextBuf *d) { | 680 | void deinit_TextBuf(iTextBuf *d) { |
diff --git a/src/ui/text.h b/src/ui/text.h index 85ab44e2..2d49a1a6 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -7,7 +7,7 @@ | |||
7 | 7 | ||
8 | enum iFontId { | 8 | enum iFontId { |
9 | default_FontId, | 9 | default_FontId, |
10 | defaultLarge_FontId, | 10 | defaultMedium_FontId, |
11 | defaultMonospace_FontId, | 11 | defaultMonospace_FontId, |
12 | regular_FontId, | 12 | regular_FontId, |
13 | monospace_FontId, | 13 | monospace_FontId, |
@@ -21,7 +21,7 @@ enum iFontId { | |||
21 | largeLight_FontId, | 21 | largeLight_FontId, |
22 | /* symbol fonts */ | 22 | /* symbol fonts */ |
23 | defaultSymbols_FontId, | 23 | defaultSymbols_FontId, |
24 | defaultLargeSymbols_FontId, | 24 | defaultMediumSymbols_FontId, |
25 | symbols_FontId, | 25 | symbols_FontId, |
26 | mediumSymbols_FontId, | 26 | mediumSymbols_FontId, |
27 | largeSymbols_FontId, | 27 | largeSymbols_FontId, |
@@ -29,7 +29,7 @@ enum iFontId { | |||
29 | smallSymbols_FontId, | 29 | smallSymbols_FontId, |
30 | /* emoji fonts */ | 30 | /* emoji fonts */ |
31 | defaultEmoji_FontId, | 31 | defaultEmoji_FontId, |
32 | defaultLargeEmoji_FontId, | 32 | defaultMediumEmoji_FontId, |
33 | emoji_FontId, | 33 | emoji_FontId, |
34 | mediumEmoji_FontId, | 34 | mediumEmoji_FontId, |
35 | largeEmoji_FontId, | 35 | largeEmoji_FontId, |
@@ -42,7 +42,7 @@ enum iFontId { | |||
42 | uiLabel_FontId = default_FontId, | 42 | uiLabel_FontId = default_FontId, |
43 | uiShortcuts_FontId = default_FontId, | 43 | uiShortcuts_FontId = default_FontId, |
44 | uiInput_FontId = defaultMonospace_FontId, | 44 | uiInput_FontId = defaultMonospace_FontId, |
45 | uiBookmarkIcon_FontId = defaultLarge_FontId, | 45 | uiContent_FontId = defaultMedium_FontId, |
46 | /* Document fonts: */ | 46 | /* Document fonts: */ |
47 | paragraph_FontId = regular_FontId, | 47 | paragraph_FontId = regular_FontId, |
48 | firstParagraph_FontId = medium_FontId, | 48 | firstParagraph_FontId = medium_FontId, |