diff options
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 374 |
1 files changed, 316 insertions, 58 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 6184a75a..cb1fde28 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -130,7 +130,8 @@ void deinit_PersistentDocumentState(iPersistentDocumentState *d) { | |||
130 | 130 | ||
131 | void serialize_PersistentDocumentState(const iPersistentDocumentState *d, iStream *outs) { | 131 | void serialize_PersistentDocumentState(const iPersistentDocumentState *d, iStream *outs) { |
132 | serialize_String(d->url, outs); | 132 | serialize_String(d->url, outs); |
133 | writeU16_Stream(outs, d->reloadInterval & 7); | 133 | uint16_t params = d->reloadInterval & 7; |
134 | writeU16_Stream(outs, params); | ||
134 | serialize_History(d->history, outs); | 135 | serialize_History(d->history, outs); |
135 | } | 136 | } |
136 | 137 | ||
@@ -223,6 +224,7 @@ enum iDocumentWidgetFlag { | |||
223 | movingSelectMarkEnd_DocumentWidgetFlag = iBit(11), | 224 | movingSelectMarkEnd_DocumentWidgetFlag = iBit(11), |
224 | otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */ | 225 | otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */ |
225 | urlChanged_DocumentWidgetFlag = iBit(13), | 226 | urlChanged_DocumentWidgetFlag = iBit(13), |
227 | openedFromSidebar_DocumentWidgetFlag = iBit(14), | ||
226 | }; | 228 | }; |
227 | 229 | ||
228 | enum iDocumentLinkOrdinalMode { | 230 | enum iDocumentLinkOrdinalMode { |
@@ -315,6 +317,10 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
315 | init_Widget(w); | 317 | init_Widget(w); |
316 | setId_Widget(w, format_CStr("document%03d", ++docEnum_)); | 318 | setId_Widget(w, format_CStr("document%03d", ++docEnum_)); |
317 | setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); | 319 | setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); |
320 | if (deviceType_App() != desktop_AppDeviceType) { | ||
321 | setFlags_Widget(w, leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag | | ||
322 | horizontalOffset_WidgetFlag, iTrue); | ||
323 | } | ||
318 | init_PersistentDocumentState(&d->mod); | 324 | init_PersistentDocumentState(&d->mod); |
319 | d->flags = 0; | 325 | d->flags = 0; |
320 | d->phoneToolbar = NULL; | 326 | d->phoneToolbar = NULL; |
@@ -392,6 +398,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
392 | } | 398 | } |
393 | 399 | ||
394 | void deinit_DocumentWidget(iDocumentWidget *d) { | 400 | void deinit_DocumentWidget(iDocumentWidget *d) { |
401 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | ||
395 | removeTicker_App(animate_DocumentWidget_, d); | 402 | removeTicker_App(animate_DocumentWidget_, d); |
396 | removeTicker_App(prerender_DocumentWidget_, d); | 403 | removeTicker_App(prerender_DocumentWidget_, d); |
397 | remove_Periodic(periodic_App(), d); | 404 | remove_Periodic(periodic_App(), d); |
@@ -976,6 +983,9 @@ static void updateTimestampBuf_DocumentWidget_(const iDocumentWidget *d) { | |||
976 | } | 983 | } |
977 | 984 | ||
978 | static void invalidate_DocumentWidget_(iDocumentWidget *d) { | 985 | static void invalidate_DocumentWidget_(iDocumentWidget *d) { |
986 | if (flags_Widget(as_Widget(d)) & destroyPending_WidgetFlag) { | ||
987 | return; | ||
988 | } | ||
979 | invalidate_VisBuf(d->visBuf); | 989 | invalidate_VisBuf(d->visBuf); |
980 | clear_PtrSet(d->invalidRuns); | 990 | clear_PtrSet(d->invalidRuns); |
981 | } | 991 | } |
@@ -1016,9 +1026,7 @@ static void showOrHidePinningIndicator_DocumentWidget_(iDocumentWidget *d) { | |||
1016 | isPinned_DocumentWidget_(d)); | 1026 | isPinned_DocumentWidget_(d)); |
1017 | } | 1027 | } |
1018 | 1028 | ||
1019 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | 1029 | static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { |
1020 | setUrl_GmDocument(d->doc, d->mod.url); | ||
1021 | setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); | ||
1022 | documentRunsInvalidated_DocumentWidget_(d); | 1030 | documentRunsInvalidated_DocumentWidget_(d); |
1023 | updateWindowTitle_DocumentWidget_(d); | 1031 | updateWindowTitle_DocumentWidget_(d); |
1024 | updateVisible_DocumentWidget_(d); | 1032 | updateVisible_DocumentWidget_(d); |
@@ -1035,6 +1043,26 @@ void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | |||
1035 | } | 1043 | } |
1036 | } | 1044 | } |
1037 | showOrHidePinningIndicator_DocumentWidget_(d); | 1045 | showOrHidePinningIndicator_DocumentWidget_(d); |
1046 | setCachedDocument_History(d->mod.history, | ||
1047 | d->doc, /* keeps a ref */ | ||
1048 | (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); | ||
1049 | } | ||
1050 | |||
1051 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | ||
1052 | setUrl_GmDocument(d->doc, d->mod.url); | ||
1053 | setSource_GmDocument(d->doc, | ||
1054 | source, | ||
1055 | documentWidth_DocumentWidget_(d), | ||
1056 | isFinished_GmRequest(d->request) ? final_GmDocumentUpdate | ||
1057 | : partial_GmDocumentUpdate); | ||
1058 | documentWasChanged_DocumentWidget_(d); | ||
1059 | } | ||
1060 | |||
1061 | static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *newDoc) { | ||
1062 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | ||
1063 | iRelease(d->doc); | ||
1064 | d->doc = ref_Object(newDoc); | ||
1065 | documentWasChanged_DocumentWidget_(d); | ||
1038 | } | 1066 | } |
1039 | 1067 | ||
1040 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | 1068 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { |
@@ -1143,16 +1171,22 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1143 | { person_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" } }, | 1171 | { person_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" } }, |
1144 | 2); | 1172 | 2); |
1145 | } | 1173 | } |
1146 | setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); | 1174 | /* Make a new document for the error page.*/ { |
1147 | setFormat_GmDocument(d->doc, gemini_GmDocumentFormat); | 1175 | iGmDocument *errorDoc = new_GmDocument(); |
1176 | setUrl_GmDocument(errorDoc, d->mod.url); | ||
1177 | setBanner_GmDocument(errorDoc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); | ||
1178 | setFormat_GmDocument(errorDoc, gemini_SourceFormat); | ||
1179 | replaceDocument_DocumentWidget_(d, errorDoc); | ||
1180 | iRelease(errorDoc); | ||
1181 | } | ||
1148 | translate_Lang(src); | 1182 | translate_Lang(src); |
1183 | d->state = ready_RequestState; | ||
1149 | setSource_DocumentWidget(d, src); | 1184 | setSource_DocumentWidget(d, src); |
1150 | updateTheme_DocumentWidget_(d); | 1185 | updateTheme_DocumentWidget_(d); |
1151 | reset_SmoothScroll(&d->scrollY); | 1186 | reset_SmoothScroll(&d->scrollY); |
1152 | init_Anim(&d->sideOpacity, 0); | 1187 | init_Anim(&d->sideOpacity, 0); |
1153 | init_Anim(&d->altTextOpacity, 0); | 1188 | init_Anim(&d->altTextOpacity, 0); |
1154 | resetWideRuns_DocumentWidget_(d); | 1189 | resetWideRuns_DocumentWidget_(d); |
1155 | d->state = ready_RequestState; | ||
1156 | } | 1190 | } |
1157 | 1191 | ||
1158 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { | 1192 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { |
@@ -1264,9 +1298,9 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool | |||
1264 | 2); | 1298 | 2); |
1265 | } | 1299 | } |
1266 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { | 1300 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { |
1267 | redoLayout_GmDocument(d->doc); | 1301 | redoLayout_GmDocument(d->doc); |
1268 | updateVisible_DocumentWidget_(d); | 1302 | updateVisible_DocumentWidget_(d); |
1269 | invalidate_DocumentWidget_(d); | 1303 | invalidate_DocumentWidget_(d); |
1270 | } | 1304 | } |
1271 | } | 1305 | } |
1272 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1306 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { |
@@ -1348,7 +1382,9 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool | |||
1348 | } | 1382 | } |
1349 | } | 1383 | } |
1350 | 1384 | ||
1351 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response, | 1385 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, |
1386 | const iGmResponse *response, | ||
1387 | iGmDocument *cachedDoc, | ||
1352 | const iBool isInitialUpdate) { | 1388 | const iBool isInitialUpdate) { |
1353 | if (d->state == ready_RequestState) { | 1389 | if (d->state == ready_RequestState) { |
1354 | return; | 1390 | return; |
@@ -1371,7 +1407,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1371 | if (isSuccess_GmStatusCode(statusCode)) { | 1407 | if (isSuccess_GmStatusCode(statusCode)) { |
1372 | /* Check the MIME type. */ | 1408 | /* Check the MIME type. */ |
1373 | iRangecc charset = range_CStr("utf-8"); | 1409 | iRangecc charset = range_CStr("utf-8"); |
1374 | enum iGmDocumentFormat docFormat = undefined_GmDocumentFormat; | 1410 | enum iSourceFormat docFormat = undefined_SourceFormat; |
1375 | const iString *mimeStr = collect_String(lower_String(&response->meta)); /* for convenience */ | 1411 | const iString *mimeStr = collect_String(lower_String(&response->meta)); /* for convenience */ |
1376 | set_String(&d->sourceMime, mimeStr); | 1412 | set_String(&d->sourceMime, mimeStr); |
1377 | iRangecc mime = range_String(mimeStr); | 1413 | iRangecc mime = range_String(mimeStr); |
@@ -1380,20 +1416,20 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1380 | iRangecc param = seg; | 1416 | iRangecc param = seg; |
1381 | trim_Rangecc(¶m); | 1417 | trim_Rangecc(¶m); |
1382 | if (equal_Rangecc(param, "text/gemini")) { | 1418 | if (equal_Rangecc(param, "text/gemini")) { |
1383 | docFormat = gemini_GmDocumentFormat; | 1419 | docFormat = gemini_SourceFormat; |
1384 | setRange_String(&d->sourceMime, param); | 1420 | setRange_String(&d->sourceMime, param); |
1385 | } | 1421 | } |
1386 | else if (startsWith_Rangecc(param, "text/") || | 1422 | else if (startsWith_Rangecc(param, "text/") || |
1387 | equal_Rangecc(param, "application/json") || | 1423 | equal_Rangecc(param, "application/json") || |
1388 | equal_Rangecc(param, "application/x-pem-file") || | 1424 | equal_Rangecc(param, "application/x-pem-file") || |
1389 | equal_Rangecc(param, "application/pem-certificate-chain")) { | 1425 | equal_Rangecc(param, "application/pem-certificate-chain")) { |
1390 | docFormat = plainText_GmDocumentFormat; | 1426 | docFormat = plainText_SourceFormat; |
1391 | setRange_String(&d->sourceMime, param); | 1427 | setRange_String(&d->sourceMime, param); |
1392 | } | 1428 | } |
1393 | else if (equal_Rangecc(param, "application/zip") || | 1429 | else if (equal_Rangecc(param, "application/zip") || |
1394 | (startsWith_Rangecc(param, "application/") && | 1430 | (startsWith_Rangecc(param, "application/") && |
1395 | endsWithCase_Rangecc(param, "+zip"))) { | 1431 | endsWithCase_Rangecc(param, "+zip"))) { |
1396 | docFormat = gemini_GmDocumentFormat; | 1432 | docFormat = gemini_SourceFormat; |
1397 | setRange_String(&d->sourceMime, param); | 1433 | setRange_String(&d->sourceMime, param); |
1398 | iString *key = collectNew_String(); | 1434 | iString *key = collectNew_String(); |
1399 | toString_Sym(SDLK_s, KMOD_PRIMARY, key); | 1435 | toString_Sym(SDLK_s, KMOD_PRIMARY, key); |
@@ -1420,9 +1456,10 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1420 | startsWith_Rangecc(param, "audio/")) { | 1456 | startsWith_Rangecc(param, "audio/")) { |
1421 | const iBool isAudio = startsWith_Rangecc(param, "audio/"); | 1457 | const iBool isAudio = startsWith_Rangecc(param, "audio/"); |
1422 | /* Make a simple document with an image or audio player. */ | 1458 | /* Make a simple document with an image or audio player. */ |
1423 | docFormat = gemini_GmDocumentFormat; | 1459 | docFormat = gemini_SourceFormat; |
1424 | setRange_String(&d->sourceMime, param); | 1460 | setRange_String(&d->sourceMime, param); |
1425 | const iGmLinkId imgLinkId = 1; /* there's only the one link */ | 1461 | const iGmLinkId imgLinkId = 1; /* there's only the one link */ |
1462 | /* TODO: Do the image loading in `postProcessRequestContent_DocumentWidget_()` */ | ||
1426 | if ((isAudio && isInitialUpdate) || (!isAudio && isRequestFinished)) { | 1463 | if ((isAudio && isInitialUpdate) || (!isAudio && isRequestFinished)) { |
1427 | const char *linkTitle = | 1464 | const char *linkTitle = |
1428 | startsWith_String(mimeStr, "image/") ? "Image" : "Audio"; | 1465 | startsWith_String(mimeStr, "image/") ? "Image" : "Audio"; |
@@ -1432,7 +1469,9 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1432 | linkTitle = | 1469 | linkTitle = |
1433 | baseName_Path(collect_String(newRange_String(parts.path))).start; | 1470 | baseName_Path(collect_String(newRange_String(parts.path))).start; |
1434 | } | 1471 | } |
1435 | format_String(&str, "=> %s %s\n", cstr_String(d->mod.url), linkTitle); | 1472 | format_String(&str, "=> %s %s\n", |
1473 | cstr_String(withSpacesEncoded_String(d->mod.url)), | ||
1474 | linkTitle); | ||
1436 | setData_Media(media_GmDocument(d->doc), | 1475 | setData_Media(media_GmDocument(d->doc), |
1437 | imgLinkId, | 1476 | imgLinkId, |
1438 | mimeStr, | 1477 | mimeStr, |
@@ -1464,7 +1503,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1464 | } | 1503 | } |
1465 | } | 1504 | } |
1466 | } | 1505 | } |
1467 | if (docFormat == undefined_GmDocumentFormat) { | 1506 | if (docFormat == undefined_SourceFormat) { |
1468 | showErrorPage_DocumentWidget_(d, unsupportedMimeType_GmStatusCode, &response->meta); | 1507 | showErrorPage_DocumentWidget_(d, unsupportedMimeType_GmStatusCode, &response->meta); |
1469 | deinit_String(&str); | 1508 | deinit_String(&str); |
1470 | return; | 1509 | return; |
@@ -1476,7 +1515,10 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse | |||
1476 | collect_String(decode_Block(&str.chars, cstr_Rangecc(charset)))); | 1515 | collect_String(decode_Block(&str.chars, cstr_Rangecc(charset)))); |
1477 | } | 1516 | } |
1478 | } | 1517 | } |
1479 | if (setSource) { | 1518 | if (cachedDoc) { |
1519 | replaceDocument_DocumentWidget_(d, cachedDoc); | ||
1520 | } | ||
1521 | else if (setSource) { | ||
1480 | setSource_DocumentWidget(d, &str); | 1522 | setSource_DocumentWidget(d, &str); |
1481 | } | 1523 | } |
1482 | deinit_String(&str); | 1524 | deinit_String(&str); |
@@ -1556,14 +1598,16 @@ static void cacheDocumentGlyphs_DocumentWidget_(const iDocumentWidget *d) { | |||
1556 | } | 1598 | } |
1557 | 1599 | ||
1558 | static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, | 1600 | static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, |
1559 | const iGmResponse *resp) { | 1601 | const iGmResponse *resp, iGmDocument *cachedDoc) { |
1560 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 1602 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
1561 | clear_ObjectList(d->media); | 1603 | clear_ObjectList(d->media); |
1562 | delete_Gempub(d->sourceGempub); | 1604 | delete_Gempub(d->sourceGempub); |
1563 | d->sourceGempub = NULL; | 1605 | d->sourceGempub = NULL; |
1564 | reset_GmDocument(d->doc); | 1606 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); |
1607 | iRelease(d->doc); | ||
1565 | destroy_Widget(d->footerButtons); | 1608 | destroy_Widget(d->footerButtons); |
1566 | d->footerButtons = NULL; | 1609 | d->footerButtons = NULL; |
1610 | d->doc = new_GmDocument(); | ||
1567 | resetWideRuns_DocumentWidget_(d); | 1611 | resetWideRuns_DocumentWidget_(d); |
1568 | d->state = fetching_RequestState; | 1612 | d->state = fetching_RequestState; |
1569 | /* Do the fetch. */ { | 1613 | /* Do the fetch. */ { |
@@ -1574,10 +1618,12 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1574 | d->sourceStatus = success_GmStatusCode; | 1618 | d->sourceStatus = success_GmStatusCode; |
1575 | format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); | 1619 | format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); |
1576 | set_Block(&d->sourceContent, &resp->body); | 1620 | set_Block(&d->sourceContent, &resp->body); |
1577 | updateDocument_DocumentWidget_(d, resp, iTrue); | 1621 | updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); |
1578 | postProcessRequestContent_DocumentWidget_(d, iTrue); | 1622 | // setCachedDocument_History(d->mod.history, d->doc, |
1623 | // (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); | ||
1579 | } | 1624 | } |
1580 | d->state = ready_RequestState; | 1625 | d->state = ready_RequestState; |
1626 | postProcessRequestContent_DocumentWidget_(d, iTrue); | ||
1581 | init_Anim(&d->altTextOpacity, 0); | 1627 | init_Anim(&d->altTextOpacity, 0); |
1582 | reset_SmoothScroll(&d->scrollY); | 1628 | reset_SmoothScroll(&d->scrollY); |
1583 | init_Anim(&d->scrollY.pos, d->initNormScrollY * size_GmDocument(d->doc).y); | 1629 | init_Anim(&d->scrollY.pos, d->initNormScrollY * size_GmDocument(d->doc).y); |
@@ -1587,13 +1633,18 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1587 | cacheDocumentGlyphs_DocumentWidget_(d); | 1633 | cacheDocumentGlyphs_DocumentWidget_(d); |
1588 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; | 1634 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; |
1589 | d->flags &= ~urlChanged_DocumentWidgetFlag; | 1635 | d->flags &= ~urlChanged_DocumentWidgetFlag; |
1590 | postCommandf_Root(as_Widget(d)->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | 1636 | postCommandf_Root( |
1637 | as_Widget(d)->root, "document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); | ||
1591 | } | 1638 | } |
1592 | 1639 | ||
1593 | static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | 1640 | static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { |
1594 | const iRecentUrl *recent = findUrl_History(d->mod.history, withSpacesEncoded_String(d->mod.url)); | 1641 | const iRecentUrl *recent = findUrl_History(d->mod.history, withSpacesEncoded_String(d->mod.url)); |
1595 | if (recent && recent->cachedResponse) { | 1642 | if (recent && recent->cachedResponse) { |
1596 | updateFromCachedResponse_DocumentWidget_(d, recent->normScrollY, recent->cachedResponse); | 1643 | iChangeFlags(d->flags, |
1644 | openedFromSidebar_DocumentWidgetFlag, | ||
1645 | recent->flags.openedFromSidebar); | ||
1646 | updateFromCachedResponse_DocumentWidget_( | ||
1647 | d, recent->normScrollY, recent->cachedResponse, recent->cachedDoc); | ||
1597 | return iTrue; | 1648 | return iTrue; |
1598 | } | 1649 | } |
1599 | else if (!isEmpty_String(d->mod.url)) { | 1650 | else if (!isEmpty_String(d->mod.url)) { |
@@ -1634,7 +1685,7 @@ static void scrollBegan_DocumentWidget_(iAnyObject *any, int offset, uint32_t du | |||
1634 | if (deviceType_App() == phone_AppDeviceType) { | 1685 | if (deviceType_App() == phone_AppDeviceType) { |
1635 | const float normPos = normScrollPos_DocumentWidget_(d); | 1686 | const float normPos = normScrollPos_DocumentWidget_(d); |
1636 | if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5 && normPos >= 0) { | 1687 | if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5 && normPos >= 0) { |
1637 | showToolbars_Root(as_Widget(d)->root, offset < 0); | 1688 | showToolbar_Root(as_Widget(d)->root, offset < 0); |
1638 | } | 1689 | } |
1639 | } | 1690 | } |
1640 | updateVisible_DocumentWidget_(d); | 1691 | updateVisible_DocumentWidget_(d); |
@@ -1843,13 +1894,15 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1843 | /* Keep scroll position when reloading the same page. */ | 1894 | /* Keep scroll position when reloading the same page. */ |
1844 | reset_SmoothScroll(&d->scrollY); | 1895 | reset_SmoothScroll(&d->scrollY); |
1845 | } | 1896 | } |
1846 | reset_GmDocument(d->doc); /* new content incoming */ | 1897 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); |
1898 | iRelease(d->doc); /* new content incoming */ | ||
1899 | d->doc = new_GmDocument(); | ||
1847 | delete_Gempub(d->sourceGempub); | 1900 | delete_Gempub(d->sourceGempub); |
1848 | d->sourceGempub = NULL; | 1901 | d->sourceGempub = NULL; |
1849 | destroy_Widget(d->footerButtons); | 1902 | destroy_Widget(d->footerButtons); |
1850 | d->footerButtons = NULL; | 1903 | d->footerButtons = NULL; |
1851 | resetWideRuns_DocumentWidget_(d); | 1904 | resetWideRuns_DocumentWidget_(d); |
1852 | updateDocument_DocumentWidget_(d, resp, iTrue); | 1905 | updateDocument_DocumentWidget_(d, resp, NULL, iTrue); |
1853 | break; | 1906 | break; |
1854 | case categoryRedirect_GmStatusCode: | 1907 | case categoryRedirect_GmStatusCode: |
1855 | if (isEmpty_String(&resp->meta)) { | 1908 | if (isEmpty_String(&resp->meta)) { |
@@ -1900,7 +1953,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1900 | switch (category_GmStatusCode(statusCode)) { | 1953 | switch (category_GmStatusCode(statusCode)) { |
1901 | case categorySuccess_GmStatusCode: | 1954 | case categorySuccess_GmStatusCode: |
1902 | /* More content available. */ | 1955 | /* More content available. */ |
1903 | updateDocument_DocumentWidget_(d, resp, iFalse); | 1956 | updateDocument_DocumentWidget_(d, resp, NULL, iFalse); |
1904 | break; | 1957 | break; |
1905 | default: | 1958 | default: |
1906 | break; | 1959 | break; |
@@ -2085,16 +2138,16 @@ static const iString *saveToDownloads_(const iString *url, const iString *mime, | |||
2085 | exportDownloadedFile_iOS(savePath); | 2138 | exportDownloadedFile_iOS(savePath); |
2086 | #else | 2139 | #else |
2087 | if (showDialog) { | 2140 | if (showDialog) { |
2088 | const iMenuItem items[2] = { | 2141 | const iMenuItem items[2] = { |
2089 | { "${dlg.save.opendownload}", 0, 0, | 2142 | { "${dlg.save.opendownload}", 0, 0, |
2090 | format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) }, | 2143 | format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) }, |
2091 | { "${dlg.message.ok}", 0, 0, "message.ok" }, | 2144 | { "${dlg.message.ok}", 0, 0, "message.ok" }, |
2092 | }; | 2145 | }; |
2093 | makeMessage_Widget(uiHeading_ColorEscape "${heading.save}", | 2146 | makeMessage_Widget(uiHeading_ColorEscape "${heading.save}", |
2094 | format_CStr("%s\n${dlg.save.size} %.3f %s", | 2147 | format_CStr("%s\n${dlg.save.size} %.3f %s", |
2095 | cstr_String(path_File(f)), | 2148 | cstr_String(path_File(f)), |
2096 | isMega ? size / 1.0e6f : (size / 1.0e3f), | 2149 | isMega ? size / 1.0e6f : (size / 1.0e3f), |
2097 | isMega ? "${mb}" : "${kb}"), | 2150 | isMega ? "${mb}" : "${kb}"), |
2098 | items, | 2151 | items, |
2099 | iElemCount(items)); | 2152 | iElemCount(items)); |
2100 | } | 2153 | } |
@@ -2208,6 +2261,147 @@ static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
2208 | return iTrue; | 2261 | return iTrue; |
2209 | } | 2262 | } |
2210 | 2263 | ||
2264 | static void swap_DocumentWidget_(iDocumentWidget *d, iGmDocument *doc, | ||
2265 | iDocumentWidget *swapBuffersWith) { | ||
2266 | if (doc) { | ||
2267 | iAssert(isInstance_Object(doc, &Class_GmDocument)); | ||
2268 | iGmDocument *copy = ref_Object(doc); | ||
2269 | iRelease(d->doc); | ||
2270 | d->doc = copy; | ||
2271 | d->scrollY = swapBuffersWith->scrollY; | ||
2272 | updateVisible_DocumentWidget_(d); | ||
2273 | iSwap(iVisBuf *, d->visBuf, swapBuffersWith->visBuf); | ||
2274 | iSwap(iVisBufMeta *, d->visBufMeta, swapBuffersWith->visBufMeta); | ||
2275 | iSwap(iDrawBufs *, d->drawBufs, swapBuffersWith->drawBufs); | ||
2276 | invalidate_DocumentWidget_(swapBuffersWith); | ||
2277 | } | ||
2278 | } | ||
2279 | |||
2280 | static iWidget *swipeParent_DocumentWidget_(iDocumentWidget *d) { | ||
2281 | return findChild_Widget(as_Widget(d)->root->widget, "doctabs"); | ||
2282 | } | ||
2283 | |||
2284 | static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | ||
2285 | iWidget *w = as_Widget(d); | ||
2286 | /* Swipe animations are rather complex and utilize both cached GmDocument content | ||
2287 | and temporary DocumentWidgets. Depending on the swipe direction, this DocumentWidget | ||
2288 | may wait until the finger is released to actually perform the navigation action. */ | ||
2289 | if (equal_Command(cmd, "edgeswipe.moved")) { | ||
2290 | //printf("[%p] responds to edgeswipe.moved\n", d); | ||
2291 | as_Widget(d)->offsetRef = NULL; | ||
2292 | const int side = argLabel_Command(cmd, "side"); | ||
2293 | const int offset = arg_Command(cmd); | ||
2294 | if (side == 1) { /* left edge */ | ||
2295 | if (atOldest_History(d->mod.history)) { | ||
2296 | return iTrue; | ||
2297 | } | ||
2298 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2299 | /* The temporary "swipeIn" will display the previous page until the finger is lifted. */ | ||
2300 | iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); | ||
2301 | if (!swipeIn) { | ||
2302 | const iBool sidebarSwipe = (isPortraitPhone_App() && | ||
2303 | d->flags & openedFromSidebar_DocumentWidgetFlag && | ||
2304 | !isVisible_Widget(findWidget_App("sidebar"))); | ||
2305 | swipeIn = new_DocumentWidget(); | ||
2306 | setId_Widget(as_Widget(swipeIn), "swipein"); | ||
2307 | setFlags_Widget(as_Widget(swipeIn), | ||
2308 | disabled_WidgetFlag | refChildrenOffset_WidgetFlag | | ||
2309 | fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); | ||
2310 | swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); | ||
2311 | swipeIn->widget.rect.size = d->widget.rect.size; | ||
2312 | swipeIn->widget.offsetRef = parent_Widget(w); | ||
2313 | if (!sidebarSwipe) { | ||
2314 | iRecentUrl *recent = new_RecentUrl(); | ||
2315 | preceding_History(d->mod.history, recent); | ||
2316 | if (recent->cachedDoc) { | ||
2317 | iChangeRef(swipeIn->doc, recent->cachedDoc); | ||
2318 | updateScrollMax_DocumentWidget_(d); | ||
2319 | setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); | ||
2320 | updateVisible_DocumentWidget_(swipeIn); | ||
2321 | swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; | ||
2322 | } | ||
2323 | delete_RecentUrl(recent); | ||
2324 | } | ||
2325 | addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos); | ||
2326 | } | ||
2327 | } | ||
2328 | if (side == 2) { /* right edge */ | ||
2329 | if (offset < -get_Window()->pixelRatio * 10) { | ||
2330 | int animSpan = 10; | ||
2331 | if (!atLatest_History(d->mod.history) && | ||
2332 | ~flags_Widget(w) & dragged_WidgetFlag) { | ||
2333 | animSpan = 0; | ||
2334 | postCommand_Widget(d, "navigate.forward"); | ||
2335 | setFlags_Widget(w, dragged_WidgetFlag, iTrue); | ||
2336 | /* Set up the swipe dummy. */ | ||
2337 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2338 | iDocumentWidget *target = new_DocumentWidget(); | ||
2339 | setId_Widget(as_Widget(target), "swipeout"); | ||
2340 | /* The target takes the old document and jumps on top. */ | ||
2341 | target->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); | ||
2342 | target->widget.rect.size = d->widget.rect.size; | ||
2343 | setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); | ||
2344 | swap_DocumentWidget_(target, d->doc, d); | ||
2345 | addChildPos_Widget(swipeParent, iClob(target), front_WidgetAddPos); | ||
2346 | setFlags_Widget(as_Widget(target), refChildrenOffset_WidgetFlag, iTrue); | ||
2347 | as_Widget(target)->offsetRef = parent_Widget(w); | ||
2348 | destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ | ||
2349 | } | ||
2350 | if (flags_Widget(w) & dragged_WidgetFlag) { | ||
2351 | setVisualOffset_Widget(w, width_Widget(w) + | ||
2352 | width_Widget(d) * offset / size_Root(w->root).x, | ||
2353 | animSpan, 0); | ||
2354 | } | ||
2355 | else { | ||
2356 | setVisualOffset_Widget(w, offset / 4, animSpan, 0); | ||
2357 | } | ||
2358 | } | ||
2359 | return iTrue; | ||
2360 | } | ||
2361 | } | ||
2362 | if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 2) { | ||
2363 | if (argLabel_Command(cmd, "abort") && flags_Widget(w) & dragged_WidgetFlag) { | ||
2364 | postCommand_Widget(d, "navigate.back"); | ||
2365 | } | ||
2366 | setFlags_Widget(w, dragged_WidgetFlag, iFalse); | ||
2367 | setVisualOffset_Widget(w, 0, 100, 0); | ||
2368 | return iTrue; | ||
2369 | } | ||
2370 | if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 1) { | ||
2371 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2372 | iWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); | ||
2373 | if (swipeIn) { | ||
2374 | swipeIn->offsetRef = NULL; | ||
2375 | destroy_Widget(swipeIn); | ||
2376 | } | ||
2377 | } | ||
2378 | if (equal_Command(cmd, "swipe.back")) { | ||
2379 | if (atOldest_History(d->mod.history)) { | ||
2380 | setVisualOffset_Widget(w, 0, 100, 0); | ||
2381 | return iTrue; | ||
2382 | } | ||
2383 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | ||
2384 | iDocumentWidget *target = new_DocumentWidget(); | ||
2385 | setId_Widget(as_Widget(target), "swipeout"); | ||
2386 | /* The target takes the old document and jumps on top. */ | ||
2387 | target->widget.rect.pos = windowToInner_Widget(swipeParent, innerToWindow_Widget(w, zero_I2())); | ||
2388 | /* Note: `innerToWindow_Widget` does not apply visual offset. */ | ||
2389 | target->widget.rect.size = w->rect.size; | ||
2390 | setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); | ||
2391 | swap_DocumentWidget_(target, d->doc, d); | ||
2392 | addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos); | ||
2393 | setFlags_Widget(as_Widget(d), refChildrenOffset_WidgetFlag, iTrue); | ||
2394 | as_Widget(d)->offsetRef = swipeParent; | ||
2395 | setVisualOffset_Widget(as_Widget(target), value_Anim(&w->visualOffset), 0, 0); | ||
2396 | setVisualOffset_Widget(as_Widget(target), width_Widget(target), 150, 0); | ||
2397 | destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ | ||
2398 | setVisualOffset_Widget(w, 0, 0, 0); | ||
2399 | postCommand_Widget(d, "navigate.back"); | ||
2400 | return iTrue; | ||
2401 | } | ||
2402 | return iFalse; | ||
2403 | } | ||
2404 | |||
2211 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 2405 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
2212 | iWidget *w = as_Widget(d); | 2406 | iWidget *w = as_Widget(d); |
2213 | if (equal_Command(cmd, "document.openurls.changed")) { | 2407 | if (equal_Command(cmd, "document.openurls.changed")) { |
@@ -2256,6 +2450,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2256 | return iFalse; | 2450 | return iFalse; |
2257 | } | 2451 | } |
2258 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { | 2452 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { |
2453 | // invalidateTheme_History(d->mod.history); /* cached colors */ | ||
2259 | updateTheme_DocumentWidget_(d); | 2454 | updateTheme_DocumentWidget_(d); |
2260 | updateVisible_DocumentWidget_(d); | 2455 | updateVisible_DocumentWidget_(d); |
2261 | updateTrust_DocumentWidget_(d, NULL); | 2456 | updateTrust_DocumentWidget_(d, NULL); |
@@ -2345,6 +2540,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2345 | msg, "%s\n", formatCStrs_Lang("num.bytes.n", size_Block(&d->sourceContent))); | 2540 | msg, "%s\n", formatCStrs_Lang("num.bytes.n", size_Block(&d->sourceContent))); |
2346 | } | 2541 | } |
2347 | } | 2542 | } |
2543 | /* TODO: On mobile, omit the CA status. */ | ||
2348 | appendFormat_String( | 2544 | appendFormat_String( |
2349 | msg, | 2545 | msg, |
2350 | "\n%s${pageinfo.cert.status}\n" | 2546 | "\n%s${pageinfo.cert.status}\n" |
@@ -2649,6 +2845,18 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2649 | return iTrue; | 2845 | return iTrue; |
2650 | } | 2846 | } |
2651 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { | 2847 | else if (equal_Command(cmd, "navigate.back") && document_App() == d) { |
2848 | if (isPortraitPhone_App()) { | ||
2849 | if (d->flags & openedFromSidebar_DocumentWidgetFlag && | ||
2850 | !isVisible_Widget(findWidget_App("sidebar"))) { | ||
2851 | postCommand_App("sidebar.toggle"); | ||
2852 | showToolbar_Root(get_Root(), iTrue); | ||
2853 | #if defined (iPlatformAppleMobile) | ||
2854 | playHapticEffect_iOS(gentleTap_HapticEffect); | ||
2855 | #endif | ||
2856 | return iTrue; | ||
2857 | } | ||
2858 | d->flags &= ~openedFromSidebar_DocumentWidgetFlag; | ||
2859 | } | ||
2652 | if (d->request) { | 2860 | if (d->request) { |
2653 | postCommandf_Root(w->root, | 2861 | postCommandf_Root(w->root, |
2654 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); | 2862 | "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); |
@@ -2811,7 +3019,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2811 | uiHeading_ColorEscape "${heading.import.bookmarks}", | 3019 | uiHeading_ColorEscape "${heading.import.bookmarks}", |
2812 | formatCStrs_Lang("dlg.import.found.n", count), | 3020 | formatCStrs_Lang("dlg.import.found.n", count), |
2813 | (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, | 3021 | (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, |
2814 | { format_CStr(cstrCount_Lang("dlg.import.add.n", count), | 3022 | { format_CStr(cstrCount_Lang("dlg.import.add.n", (int) count), |
2815 | uiTextAction_ColorEscape, | 3023 | uiTextAction_ColorEscape, |
2816 | count), | 3024 | count), |
2817 | 0, | 3025 | 0, |
@@ -2870,6 +3078,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2870 | else if (startsWith_CStr(cmd, "pinch.") && document_Command(cmd) == d) { | 3078 | else if (startsWith_CStr(cmd, "pinch.") && document_Command(cmd) == d) { |
2871 | return handlePinch_DocumentWidget_(d, cmd); | 3079 | return handlePinch_DocumentWidget_(d, cmd); |
2872 | } | 3080 | } |
3081 | else if ((startsWith_CStr(cmd, "edgeswipe.") || startsWith_CStr(cmd, "swipe.")) && | ||
3082 | document_App() == d) { | ||
3083 | return handleSwipe_DocumentWidget_(d, cmd); | ||
3084 | } | ||
2873 | return iFalse; | 3085 | return iFalse; |
2874 | } | 3086 | } |
2875 | 3087 | ||
@@ -3318,6 +3530,18 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3318 | d->contextLink->linkId) }); | 3530 | d->contextLink->linkId) }); |
3319 | } | 3531 | } |
3320 | } | 3532 | } |
3533 | if (equalCase_Rangecc(scheme, "file")) { | ||
3534 | /* Local files may be deleted. */ | ||
3535 | pushBack_Array( | ||
3536 | &items, | ||
3537 | &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape | ||
3538 | "${link.file.delete}", | ||
3539 | 0, | ||
3540 | 0, | ||
3541 | format_CStr("!file.delete confirm:1 path:%s", | ||
3542 | cstrCollect_String( | ||
3543 | localFilePathFromUrl_String(linkUrl))) }); | ||
3544 | } | ||
3321 | } | 3545 | } |
3322 | else if (deviceType_App() == desktop_AppDeviceType) { | 3546 | else if (deviceType_App() == desktop_AppDeviceType) { |
3323 | if (!isEmpty_Range(&d->selectMark)) { | 3547 | if (!isEmpty_Range(&d->selectMark)) { |
@@ -3870,14 +4094,14 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
3870 | } | 4094 | } |
3871 | #endif | 4095 | #endif |
3872 | /* Fill the background. */ { | 4096 | /* Fill the background. */ { |
3873 | if (run->linkId && linkFlags & isOpen_GmLinkFlag) { | 4097 | if (run->linkId && linkFlags & isOpen_GmLinkFlag && ~linkFlags & content_GmLinkFlag) { |
3874 | /* Open links get a highlighted background. */ | 4098 | /* Open links get a highlighted background. */ |
3875 | int bg = tmBackgroundOpenLink_ColorId; | 4099 | int bg = tmBackgroundOpenLink_ColorId; |
3876 | const int frame = tmFrameOpenLink_ColorId; | 4100 | const int frame = tmFrameOpenLink_ColorId; |
3877 | iRect wideRect = { init_I2(left_Rect(d->widgetBounds), visPos.y), | 4101 | iRect wideRect = { init_I2(left_Rect(d->widgetBounds), visPos.y), |
3878 | init_I2(width_Rect(d->widgetBounds) + | 4102 | init_I2(width_Rect(d->widgetBounds) + |
3879 | width_Widget(d->widget->scroll), | 4103 | width_Widget(d->widget->scroll), |
3880 | height_Rect(run->visBounds)) }; | 4104 | height_Rect(run->visBounds)) }; |
3881 | /* The first line is composed of two runs that may be drawn in either order, so | 4105 | /* The first line is composed of two runs that may be drawn in either order, so |
3882 | only draw half of the background. */ | 4106 | only draw half of the background. */ |
3883 | if (run->flags & decoration_GmRunFlag) { | 4107 | if (run->flags & decoration_GmRunFlag) { |
@@ -3931,16 +4155,17 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
3931 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); | 4155 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); |
3932 | if (ordChar) { | 4156 | if (ordChar) { |
3933 | const char *circle = "\u25ef"; /* Large Circle */ | 4157 | const char *circle = "\u25ef"; /* Large Circle */ |
4158 | const int circleFont = defaultContentRegular_FontId; | ||
3934 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), | 4159 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), |
3935 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(run->font)) }; | 4160 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; |
3936 | drawRange_Text( | 4161 | drawRange_Text( |
3937 | run->font, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); | 4162 | circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); |
3938 | iRect circleArea = visualBounds_Text(run->font, range_CStr(circle)); | 4163 | iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); |
3939 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); | 4164 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); |
3940 | drawCentered_Text(defaultContentSmall_FontId, | 4165 | drawCentered_Text(defaultContentSmall_FontId, |
3941 | circleArea, | 4166 | circleArea, |
3942 | iTrue, | 4167 | iTrue, |
3943 | tmQuote_ColorId, | 4168 | tmQuote_ColorId, |
3944 | "%lc", | 4169 | "%lc", |
3945 | (int) ordChar); | 4170 | (int) ordChar); |
3946 | goto runDrawn; | 4171 | goto runDrawn; |
@@ -4178,7 +4403,7 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
4178 | iDrawBufs * dbuf = d->drawBufs; | 4403 | iDrawBufs * dbuf = d->drawBufs; |
4179 | iPaint p; | 4404 | iPaint p; |
4180 | init_Paint(&p); | 4405 | init_Paint(&p); |
4181 | setClip_Paint(&p, bounds); | 4406 | setClip_Paint(&p, boundsWithoutVisualOffset_Widget(w)); |
4182 | /* Side icon and current heading. */ | 4407 | /* Side icon and current heading. */ |
4183 | if (prefs_App()->sideIcon && opacity > 0 && dbuf->sideIconBuf) { | 4408 | if (prefs_App()->sideIcon && opacity > 0 && dbuf->sideIconBuf) { |
4184 | const iInt2 texSize = size_SDLTexture(dbuf->sideIconBuf); | 4409 | const iInt2 texSize = size_SDLTexture(dbuf->sideIconBuf); |
@@ -4263,7 +4488,8 @@ static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx, | |||
4263 | /* Swap buffers around to have room available both before and after the visible region. */ | 4488 | /* Swap buffers around to have room available both before and after the visible region. */ |
4264 | allocVisBuffer_DocumentWidget_(d); | 4489 | allocVisBuffer_DocumentWidget_(d); |
4265 | reposition_VisBuf(visBuf, vis); | 4490 | reposition_VisBuf(visBuf, vis); |
4266 | /* Redraw the invalid ranges. */ { | 4491 | /* Redraw the invalid ranges. */ |
4492 | if (~flags_Widget(constAs_Widget(d)) & destroyPending_WidgetFlag) { | ||
4267 | iPaint *p = &ctx->paint; | 4493 | iPaint *p = &ctx->paint; |
4268 | init_Paint(p); | 4494 | init_Paint(p); |
4269 | iForIndices(i, visBuf->buffers) { | 4495 | iForIndices(i, visBuf->buffers) { |
@@ -4427,12 +4653,17 @@ static void prerender_DocumentWidget_(iAny *context) { | |||
4427 | } | 4653 | } |
4428 | 4654 | ||
4429 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 4655 | static void draw_DocumentWidget_(const iDocumentWidget *d) { |
4430 | const iWidget *w = constAs_Widget(d); | 4656 | const iWidget *w = constAs_Widget(d); |
4431 | const iRect bounds = bounds_Widget(w); | 4657 | const iRect bounds = bounds_Widget(w); |
4658 | const iRect boundsWithoutVisOff = boundsWithoutVisualOffset_Widget(w); | ||
4659 | const iRect clipBounds = intersect_Rect(bounds, boundsWithoutVisOff); | ||
4432 | if (width_Rect(bounds) <= 0) { | 4660 | if (width_Rect(bounds) <= 0) { |
4433 | return; | 4661 | return; |
4434 | } | 4662 | } |
4435 | // draw_Widget(w); | 4663 | /* TODO: Come up with a better palette caching system. |
4664 | It should be able to recompute cached colors in `History` when the theme has changed. | ||
4665 | Cache the theme seed in `GmDocument`? */ | ||
4666 | // makePaletteGlobal_GmDocument(d->doc); | ||
4436 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { | 4667 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { |
4437 | updateTimestampBuf_DocumentWidget_(d); | 4668 | updateTimestampBuf_DocumentWidget_(d); |
4438 | } | 4669 | } |
@@ -4447,14 +4678,15 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4447 | .vis = vis, | 4678 | .vis = vis, |
4448 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, | 4679 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, |
4449 | }; | 4680 | }; |
4681 | init_Paint(&ctx.paint); | ||
4450 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); | 4682 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); |
4451 | setClip_Paint(&ctx.paint, bounds); | 4683 | setClip_Paint(&ctx.paint, clipBounds); |
4452 | int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); | 4684 | int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); |
4453 | draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); | 4685 | draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); |
4454 | /* Text markers. */ | 4686 | /* Text markers. */ |
4455 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; | 4687 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; |
4456 | if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { | 4688 | if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { |
4457 | SDL_Renderer *render = renderer_Window(get_Window()); | 4689 | SDL_Renderer *render = renderer_Window(get_Window()); |
4458 | ctx.firstMarkRect = zero_Rect(); | 4690 | ctx.firstMarkRect = zero_Rect(); |
4459 | ctx.lastMarkRect = zero_Rect(); | 4691 | ctx.lastMarkRect = zero_Rect(); |
4460 | SDL_SetRenderDrawBlendMode(render, | 4692 | SDL_SetRenderDrawBlendMode(render, |
@@ -4483,7 +4715,6 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4483 | } | 4715 | } |
4484 | } | 4716 | } |
4485 | drawMedia_DocumentWidget_(d, &ctx.paint); | 4717 | drawMedia_DocumentWidget_(d, &ctx.paint); |
4486 | unsetClip_Paint(&ctx.paint); | ||
4487 | /* Fill the top and bottom, in case the document is short. */ | 4718 | /* Fill the top and bottom, in case the document is short. */ |
4488 | if (yTop > top_Rect(bounds)) { | 4719 | if (yTop > top_Rect(bounds)) { |
4489 | fillRect_Paint(&ctx.paint, | 4720 | fillRect_Paint(&ctx.paint, |
@@ -4497,6 +4728,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4497 | init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), | 4728 | init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), |
4498 | tmBackground_ColorId); | 4729 | tmBackground_ColorId); |
4499 | } | 4730 | } |
4731 | unsetClip_Paint(&ctx.paint); | ||
4500 | drawSideElements_DocumentWidget_(d); | 4732 | drawSideElements_DocumentWidget_(d); |
4501 | if (prefs_App()->hoverLink && d->hoverLink) { | 4733 | if (prefs_App()->hoverLink && d->hoverLink) { |
4502 | const int font = uiLabel_FontId; | 4734 | const int font = uiLabel_FontId; |
@@ -4558,6 +4790,23 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4558 | drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected", | 4790 | drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected", |
4559 | size_Range(&mark)); | 4791 | size_Range(&mark)); |
4560 | } | 4792 | } |
4793 | if (w->offsetRef) { | ||
4794 | const int offX = visualOffsetByReference_Widget(w); | ||
4795 | if (offX) { | ||
4796 | setClip_Paint(&ctx.paint, clipBounds); | ||
4797 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | ||
4798 | ctx.paint.alpha = iAbs(offX) / (float) get_Window()->size.x * 300; | ||
4799 | fillRect_Paint(&ctx.paint, bounds, backgroundFadeColor_Widget()); | ||
4800 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); | ||
4801 | unsetClip_Paint(&ctx.paint); | ||
4802 | } | ||
4803 | else { | ||
4804 | /* TODO: Should have a better place to do this; drawing is supposed to be immutable. */ | ||
4805 | iWidget *mut = iConstCast(iWidget *, w); | ||
4806 | mut->offsetRef = NULL; | ||
4807 | mut->flags &= ~refChildrenOffset_WidgetFlag; | ||
4808 | } | ||
4809 | } | ||
4561 | } | 4810 | } |
4562 | 4811 | ||
4563 | /*----------------------------------------------------------------------------------------------*/ | 4812 | /*----------------------------------------------------------------------------------------------*/ |
@@ -4625,9 +4874,12 @@ static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { | |||
4625 | d->flags |= urlChanged_DocumentWidgetFlag; | 4874 | d->flags |= urlChanged_DocumentWidgetFlag; |
4626 | set_String(d->mod.url, url); | 4875 | set_String(d->mod.url, url); |
4627 | } | 4876 | } |
4628 | } | 4877 | } |
4629 | 4878 | ||
4630 | void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { | 4879 | void setUrlFlags_DocumentWidget(iDocumentWidget *d, const iString *url, int setUrlFlags) { |
4880 | iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, | ||
4881 | (setUrlFlags & openedFromSidebar_DocumentWidgetSetUrlFlag) != 0); | ||
4882 | const iBool isFromCache = (setUrlFlags & useCachedContentIfAvailable_DocumentWidgetSetUrlFlag) != 0; | ||
4631 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 4883 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
4632 | setUrl_DocumentWidget_(d, urlFragmentStripped_String(url)); | 4884 | setUrl_DocumentWidget_(d, urlFragmentStripped_String(url)); |
4633 | /* See if there a username in the URL. */ | 4885 | /* See if there a username in the URL. */ |
@@ -4639,6 +4891,7 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo | |||
4639 | 4891 | ||
4640 | void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, const iString *mime, | 4892 | void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, const iString *mime, |
4641 | const iBlock *source) { | 4893 | const iBlock *source) { |
4894 | d->flags &= ~openedFromSidebar_DocumentWidgetFlag; | ||
4642 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 4895 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
4643 | setUrl_DocumentWidget_(d, url); | 4896 | setUrl_DocumentWidget_(d, url); |
4644 | parseUser_DocumentWidget_(d); | 4897 | parseUser_DocumentWidget_(d); |
@@ -4647,7 +4900,7 @@ void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, cons | |||
4647 | initCurrent_Time(&resp->when); | 4900 | initCurrent_Time(&resp->when); |
4648 | set_String(&resp->meta, mime); | 4901 | set_String(&resp->meta, mime); |
4649 | set_Block(&resp->body, source); | 4902 | set_Block(&resp->body, source); |
4650 | updateFromCachedResponse_DocumentWidget_(d, 0, resp); | 4903 | updateFromCachedResponse_DocumentWidget_(d, 0, resp, NULL); |
4651 | delete_GmResponse(resp); | 4904 | delete_GmResponse(resp); |
4652 | } | 4905 | } |
4653 | 4906 | ||
@@ -4656,12 +4909,12 @@ iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { | |||
4656 | delete_History(d->mod.history); | 4909 | delete_History(d->mod.history); |
4657 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); | 4910 | d->initNormScrollY = normScrollPos_DocumentWidget_(d); |
4658 | d->mod.history = copy_History(orig->mod.history); | 4911 | d->mod.history = copy_History(orig->mod.history); |
4659 | setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); | 4912 | setUrlFlags_DocumentWidget(d, orig->mod.url, useCachedContentIfAvailable_DocumentWidgetSetUrlFlag); |
4660 | return d; | 4913 | return d; |
4661 | } | 4914 | } |
4662 | 4915 | ||
4663 | void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { | 4916 | void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { |
4664 | setUrlFromCache_DocumentWidget(d, url, iFalse); | 4917 | setUrlFlags_DocumentWidget(d, url, 0); |
4665 | } | 4918 | } |
4666 | 4919 | ||
4667 | void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { | 4920 | void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { |
@@ -4672,6 +4925,11 @@ void setRedirectCount_DocumentWidget(iDocumentWidget *d, int count) { | |||
4672 | d->redirectCount = count; | 4925 | d->redirectCount = count; |
4673 | } | 4926 | } |
4674 | 4927 | ||
4928 | void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *d, iBool fromSidebar) { | ||
4929 | iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, fromSidebar); | ||
4930 | // setCachedDocument_History(d->mod.history, d->doc, fromSidebar); | ||
4931 | } | ||
4932 | |||
4675 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { | 4933 | iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { |
4676 | return d->request != NULL; | 4934 | return d->request != NULL; |
4677 | } | 4935 | } |