diff options
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 339 |
1 files changed, 239 insertions, 100 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 47d7f755..b5fdde93 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()) { |
@@ -968,7 +1026,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
968 | else if (equalWidget_Command(cmd, w, "document.request.finished") && | 1026 | else if (equalWidget_Command(cmd, w, "document.request.finished") && |
969 | pointerLabel_Command(cmd, "request") == d->request) { | 1027 | pointerLabel_Command(cmd, "request") == d->request) { |
970 | checkResponse_DocumentWidget_(d); | 1028 | checkResponse_DocumentWidget_(d); |
971 | d->scrollY = d->initialNormScrollY * size_GmDocument(d->doc).y; | 1029 | d->scrollY = d->initNormScrollY * size_GmDocument(d->doc).y; |
972 | d->state = ready_RequestState; | 1030 | d->state = ready_RequestState; |
973 | /* The response may be cached. */ { | 1031 | /* The response may be cached. */ { |
974 | const iRangecc proto = urlProtocol_String(d->mod.url); | 1032 | const iRangecc proto = urlProtocol_String(d->mod.url); |
@@ -1002,7 +1060,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1002 | return handleMediaCommand_DocumentWidget_(d, cmd); | 1060 | return handleMediaCommand_DocumentWidget_(d, cmd); |
1003 | } | 1061 | } |
1004 | else if (equal_Command(cmd, "document.reload") && document_App() == d) { | 1062 | else if (equal_Command(cmd, "document.reload") && document_App() == d) { |
1005 | d->initialNormScrollY = normScrollPos_DocumentWidget_(d); | 1063 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); |
1006 | fetch_DocumentWidget_(d); | 1064 | fetch_DocumentWidget_(d); |
1007 | return iTrue; | 1065 | return iTrue; |
1008 | } | 1066 | } |
@@ -1100,6 +1158,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1100 | case SDLK_RALT: | 1158 | case SDLK_RALT: |
1101 | if (document_App() == d) { | 1159 | if (document_App() == d) { |
1102 | d->showLinkNumbers = iFalse; | 1160 | d->showLinkNumbers = iFalse; |
1161 | invalidate_DocumentWidget_(d); | ||
1103 | refresh_Widget(w); | 1162 | refresh_Widget(w); |
1104 | } | 1163 | } |
1105 | break; | 1164 | break; |
@@ -1127,21 +1186,10 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1127 | case SDLK_RALT: | 1186 | case SDLK_RALT: |
1128 | if (document_App() == d) { | 1187 | if (document_App() == d) { |
1129 | d->showLinkNumbers = iTrue; | 1188 | d->showLinkNumbers = iTrue; |
1189 | invalidate_DocumentWidget_(d); | ||
1130 | refresh_Widget(w); | 1190 | refresh_Widget(w); |
1131 | } | 1191 | } |
1132 | break; | 1192 | 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: | 1193 | case SDLK_HOME: |
1146 | d->scrollY = 0; | 1194 | d->scrollY = 0; |
1147 | scroll_DocumentWidget_(d, 0); | 1195 | scroll_DocumentWidget_(d, 0); |
@@ -1264,6 +1312,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1264 | d->hoverLink = NULL; | 1312 | d->hoverLink = NULL; |
1265 | scroll_DocumentWidget_(d, 0); | 1313 | scroll_DocumentWidget_(d, 0); |
1266 | updateVisible_DocumentWidget_(d); | 1314 | updateVisible_DocumentWidget_(d); |
1315 | invalidate_DocumentWidget_(d); | ||
1267 | refresh_Widget(w); | 1316 | refresh_Widget(w); |
1268 | return iTrue; | 1317 | return iTrue; |
1269 | } | 1318 | } |
@@ -1274,6 +1323,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1274 | setImage_GmDocument(d->doc, linkId, meta_GmRequest(req->req), | 1323 | setImage_GmDocument(d->doc, linkId, meta_GmRequest(req->req), |
1275 | body_GmRequest(req->req)); | 1324 | body_GmRequest(req->req)); |
1276 | updateVisible_DocumentWidget_(d); | 1325 | updateVisible_DocumentWidget_(d); |
1326 | invalidate_DocumentWidget_(d); | ||
1277 | refresh_Widget(w); | 1327 | refresh_Widget(w); |
1278 | return iTrue; | 1328 | return iTrue; |
1279 | } | 1329 | } |
@@ -1305,7 +1355,13 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1305 | 1355 | ||
1306 | iDeclareType(DrawContext) | 1356 | iDeclareType(DrawContext) |
1307 | 1357 | ||
1358 | enum iDrawRunPass { | ||
1359 | static_DrawRunPass, | ||
1360 | dynamic_DrawRunPass, | ||
1361 | }; | ||
1362 | |||
1308 | struct Impl_DrawContext { | 1363 | struct Impl_DrawContext { |
1364 | enum iDrawRunPass pass; | ||
1309 | const iDocumentWidget *widget; | 1365 | const iDocumentWidget *widget; |
1310 | iRect widgetBounds; | 1366 | iRect widgetBounds; |
1311 | iRect bounds; /* document area */ | 1367 | iRect bounds; /* document area */ |
@@ -1348,57 +1404,67 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1348 | iDrawContext *d = context; | 1404 | iDrawContext *d = context; |
1349 | const iInt2 origin = addY_I2(d->bounds.pos, -d->widget->scrollY); | 1405 | const iInt2 origin = addY_I2(d->bounds.pos, -d->widget->scrollY); |
1350 | if (run->imageId) { | 1406 | if (run->imageId) { |
1351 | SDL_Texture *tex = imageTexture_GmDocument(d->widget->doc, run->imageId); | 1407 | if (d->pass == static_DrawRunPass) { |
1352 | if (tex) { | 1408 | SDL_Texture *tex = imageTexture_GmDocument(d->widget->doc, run->imageId); |
1353 | const iRect dst = moved_Rect(run->visBounds, origin); | 1409 | if (tex) { |
1354 | SDL_RenderCopy(d->paint.dst->render, tex, NULL, | 1410 | const iRect dst = moved_Rect(run->visBounds, origin); |
1355 | &(SDL_Rect){ dst.pos.x, dst.pos.y, dst.size.x, dst.size.y }); | 1411 | SDL_RenderCopy(d->paint.dst->render, tex, NULL, |
1412 | &(SDL_Rect){ dst.pos.x, dst.pos.y, dst.size.x, dst.size.y }); | ||
1413 | } | ||
1356 | } | 1414 | } |
1357 | return; | 1415 | return; |
1358 | } | 1416 | } |
1417 | /* Text markers. */ | ||
1418 | if (d->pass == dynamic_DrawRunPass) { | ||
1419 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); | ||
1420 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); | ||
1421 | } | ||
1359 | enum iColorId fg = run->color; | 1422 | enum iColorId fg = run->color; |
1360 | const iGmDocument *doc = d->widget->doc; | 1423 | const iGmDocument *doc = d->widget->doc; |
1424 | /* Matches the current drawing pass? */ | ||
1425 | const iBool isDynamic = (run->linkId && ~run->flags & decoration_GmRunFlag); | ||
1426 | if (isDynamic ^ (d->pass == dynamic_DrawRunPass)) { | ||
1427 | return; | ||
1428 | } | ||
1361 | const iBool isHover = | 1429 | const iBool isHover = |
1362 | (run->linkId != 0 && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && | 1430 | (run->linkId && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && |
1363 | !isEmpty_Rect(run->bounds)); | 1431 | ~run->flags & decoration_GmRunFlag); |
1364 | const iInt2 visPos = add_I2(run->visBounds.pos, origin); | 1432 | const iInt2 visPos = add_I2(run->visBounds.pos, origin); |
1365 | /* Text markers. */ | 1433 | 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); | 1434 | fg = linkColor_GmDocument(doc, run->linkId, isHover ? textHover_GmLinkPart : text_GmLinkPart); |
1371 | if (linkFlags_GmDocument(doc, run->linkId) & content_GmLinkFlag) { | 1435 | if (linkFlags_GmDocument(doc, run->linkId) & content_GmLinkFlag) { |
1372 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); /* link is inactive */ | 1436 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); /* link is inactive */ |
1373 | } | 1437 | } |
1374 | } | 1438 | } |
1375 | if (run->flags & siteBanner_GmRunFlag) { | 1439 | if (run->flags & siteBanner_GmRunFlag) { |
1376 | /* Draw the site banner. */ | 1440 | if (d->pass == static_DrawRunPass) { |
1377 | fillRect_Paint( | 1441 | /* Draw the site banner. */ |
1378 | &d->paint, | 1442 | fillRect_Paint( |
1379 | initCorners_Rect(topLeft_Rect(d->widgetBounds), | 1443 | &d->paint, |
1380 | init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))), | 1444 | initCorners_Rect(topLeft_Rect(d->widgetBounds), |
1381 | visPos.y + height_Rect(run->visBounds))), | 1445 | init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))), |
1382 | tmBannerBackground_ColorId); | 1446 | visPos.y + height_Rect(run->visBounds))), |
1383 | const iChar icon = siteIcon_GmDocument(doc); | 1447 | tmBannerBackground_ColorId); |
1384 | iString bannerText; | 1448 | const iChar icon = siteIcon_GmDocument(doc); |
1385 | init_String(&bannerText); | 1449 | iString bannerText; |
1386 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | 1450 | init_String(&bannerText); |
1387 | if (icon) { | 1451 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); |
1388 | appendChar_String(&bannerText, icon); | 1452 | if (icon) { |
1389 | const iRect iconRect = visualBounds_Text(banner_FontId, range_String(&bannerText)); | 1453 | appendChar_String(&bannerText, icon); |
1454 | const iRect iconRect = visualBounds_Text(banner_FontId, range_String(&bannerText)); | ||
1455 | drawRange_Text(run->font, | ||
1456 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | ||
1457 | tmBannerIcon_ColorId, | ||
1458 | range_String(&bannerText)); | ||
1459 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | ||
1460 | } | ||
1390 | drawRange_Text(run->font, | 1461 | drawRange_Text(run->font, |
1391 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | 1462 | bpos, |
1392 | tmBannerIcon_ColorId, | 1463 | tmBannerTitle_ColorId, |
1393 | range_String(&bannerText)); | 1464 | isEmpty_String(d->widget->titleUser) ? run->text |
1394 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | 1465 | : range_String(d->widget->titleUser)); |
1466 | deinit_String(&bannerText); | ||
1395 | } | 1467 | } |
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 | } | 1468 | } |
1403 | else { | 1469 | else { |
1404 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { | 1470 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { |
@@ -1413,10 +1479,11 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1413 | } | 1479 | } |
1414 | } | 1480 | } |
1415 | drawRange_Text(run->font, visPos, fg, run->text); | 1481 | drawRange_Text(run->font, visPos, fg, run->text); |
1482 | // printf("{%s}\n", cstr_Rangecc(run->text)); | ||
1416 | runDrawn:; | 1483 | runDrawn:; |
1417 | } | 1484 | } |
1418 | /* Presentation of links. */ | 1485 | /* Presentation of links. */ |
1419 | if (run->linkId && ~run->flags & decoration_GmRunFlag) { | 1486 | if (run->linkId && ~run->flags & decoration_GmRunFlag && d->pass == dynamic_DrawRunPass) { |
1420 | const int metaFont = paragraph_FontId; | 1487 | const int metaFont = paragraph_FontId; |
1421 | /* TODO: Show status of an ongoing media request. */ | 1488 | /* TODO: Show status of an ongoing media request. */ |
1422 | const int flags = linkFlags_GmDocument(doc, run->linkId); | 1489 | const int flags = linkFlags_GmDocument(doc, run->linkId); |
@@ -1515,22 +1582,94 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1515 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); | 1582 | // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); |
1516 | } | 1583 | } |
1517 | 1584 | ||
1585 | static iRangei intersect_Rangei_(iRangei a, iRangei b) { | ||
1586 | if (a.end < b.start || a.start > b.end) { | ||
1587 | return (iRangei){ 0, 0 }; | ||
1588 | } | ||
1589 | return (iRangei){ iMax(a.start, b.start), iMin(a.end, b.end) }; | ||
1590 | } | ||
1591 | |||
1592 | iLocalDef iBool isEmpty_Rangei_(iRangei d) { | ||
1593 | return size_Range(&d) == 0; | ||
1594 | } | ||
1595 | |||
1518 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 1596 | static void draw_DocumentWidget_(const iDocumentWidget *d) { |
1519 | const iWidget *w = constAs_Widget(d); | 1597 | const iWidget *w = constAs_Widget(d); |
1520 | const iRect bounds = bounds_Widget(w); | 1598 | const iRect bounds = bounds_Widget(w); |
1599 | const iInt2 origin = topLeft_Rect(bounds); | ||
1600 | const iRangei visRange = visibleRange_DocumentWidget_(d); | ||
1521 | draw_Widget(w); | 1601 | draw_Widget(w); |
1522 | iDrawContext ctx = { | 1602 | allocVisBuffer_DocumentWidget_(iConstCast(iDocumentWidget *, d)); |
1523 | .widget = d, | 1603 | iDrawContext ctxDynamic = { |
1524 | .widgetBounds = /* omit scrollbar width */ | 1604 | .pass = dynamic_DrawRunPass, |
1525 | adjusted_Rect(bounds, zero_I2(), init_I2(-constAs_Widget(d->scroll)->rect.size.x, 0)), | 1605 | .widget = d, |
1526 | .bounds = documentBounds_DocumentWidget_(d), | 1606 | .widgetBounds = adjusted_Rect(bounds, |
1607 | zero_I2(), | ||
1608 | init_I2(-constAs_Widget(d->scroll)->rect.size.x, 0)), /* omit scrollbar width */ | ||
1609 | .bounds = documentBounds_DocumentWidget_(d), | ||
1527 | .showLinkNumbers = d->showLinkNumbers, | 1610 | .showLinkNumbers = d->showLinkNumbers, |
1528 | }; | 1611 | }; |
1529 | init_Paint(&ctx.paint); | 1612 | iDrawContext ctxStatic = ctxDynamic; |
1530 | fillRect_Paint(&ctx.paint, bounds, tmBackground_ColorId); | 1613 | ctxStatic.pass = static_DrawRunPass; |
1531 | setClip_Paint(&ctx.paint, bounds); | 1614 | subv_I2(&ctxStatic.widgetBounds.pos, origin); |
1532 | render_GmDocument(d->doc, visibleRange_DocumentWidget_(d), drawRun_DrawContext_, &ctx); | 1615 | subv_I2(&ctxStatic.bounds.pos, origin); |
1533 | unsetClip_Paint(&ctx.paint); | 1616 | SDL_Renderer *render = get_Window()->render; |
1617 | /* Static content. */ { | ||
1618 | iPaint *p = &ctxStatic.paint; | ||
1619 | init_Paint(p); | ||
1620 | const int vbSrc = d->visBufferIndex; | ||
1621 | const int vbDst = d->visBufferIndex ^ 1; | ||
1622 | iRangei drawRange = visRange; | ||
1623 | iAssert(d->visBuffer[vbDst]); | ||
1624 | beginTarget_Paint(p, d->visBuffer[vbDst]); | ||
1625 | const iRect visBufferRect = { zero_I2(), d->visBufferSize }; | ||
1626 | iRect drawRect = visBufferRect; | ||
1627 | if (!isEmpty_Rangei_(intersect_Rangei_(visRange, d->visBufferValidRange))) { | ||
1628 | const iRangei isct = 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, vbDst == 1 ? blue_ColorId : red_ColorId); //tmBackground_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))), |