summaryrefslogtreecommitdiff
path: root/src/ui/documentwidget.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r--src/ui/documentwidget.c359
1 files changed, 249 insertions, 110 deletions
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
24iDeclareClass(MediaRequest) 25iDeclareClass(MediaRequest)
@@ -112,33 +113,37 @@ enum iRequestState {
112}; 113};
113 114
114struct Impl_DocumentWidget { 115struct 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
144iDefineObjectConstruction(DocumentWidget) 149iDefineObjectConstruction(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
241iLocalDef int documentToWindowY_DocumentWidget_(const iDocumentWidget *d, int docY) {
242 return docY - d->scrollY + documentBounds_DocumentWidget_(d).pos.y;
243}
244
245iLocalDef int windowToDocumentY_DocumentWidget_(const iDocumentWidget *d, int localY) {
246 return localY + d->scrollY - documentBounds_DocumentWidget_(d).pos.y;
247}
248
232static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { 249static 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
254static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { 271static 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
260static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) { 277static void addVisibleLink_DocumentWidget_(void *context, const iGmRun *run) {
@@ -453,6 +470,10 @@ static void updateTheme_DocumentWidget_(iDocumentWidget *d) {
453 } 470 }
454} 471}
455 472
473static void invalidate_DocumentWidget_(iDocumentWidget *d) {
474 iZap(d->visBufferValidRange);
475}
476
456static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response) { 477static 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
640iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { 662iDocumentWidget *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
653void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { 675void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) {
654 d->initialNormScrollY = normScrollY; 676 d->initNormScrollY = normScrollY;
655} 677}
656 678
657iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { 679iBool 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
875static 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
884static 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
851void updateSize_DocumentWidget(iDocumentWidget *d) { 905void 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
1306iDeclareType(DrawContext) 1358iDeclareType(DrawContext)
1307 1359
1360enum iDrawRunPass {
1361 static_DrawRunPass,
1362 dynamic_DrawRunPass,
1363};
1364
1308struct Impl_DrawContext { 1365struct 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
1586static 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
1593iLocalDef iBool isEmpty_Rangei_(iRangei d) {
1594 return size_Range(&d) == 0;
1595}
1596
1518static void draw_DocumentWidget_(const iDocumentWidget *d) { 1597static 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))),