diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-20 12:53:12 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-20 12:53:12 +0200 |
commit | 6d7b6122e68c8d53b6e7533a6e5d5f55e33831e1 (patch) | |
tree | 80bc2867510e3c9b7bc43578fc416165dae3876c /src/ui | |
parent | 2d52f13afb15232cc9c834f0aa14284bf9072a31 (diff) | |
parent | 41f378c4b46cb5dd3599d44c81fa51d3183eefee (diff) |
Merge branch 'work/v1.11' into dev
# Conflicts:
# res/lang/es.bin
# res/lang/ie.bin
# res/lang/ru.bin
# res/lang/sr.bin
# res/lang/uk.bin
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/banner.c | 3 | ||||
-rw-r--r-- | src/ui/color.c | 4 | ||||
-rw-r--r-- | src/ui/color.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 135 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 5 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 1 | ||||
-rw-r--r-- | src/ui/keys.c | 2 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 12 | ||||
-rw-r--r-- | src/ui/linkinfo.c | 4 | ||||
-rw-r--r-- | src/ui/lookupwidget.c | 2 | ||||
-rw-r--r-- | src/ui/root.c | 16 | ||||
-rw-r--r-- | src/ui/root.h | 1 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 187 | ||||
-rw-r--r-- | src/ui/text.c | 60 | ||||
-rw-r--r-- | src/ui/text.h | 2 | ||||
-rw-r--r-- | src/ui/touch.c | 3 | ||||
-rw-r--r-- | src/ui/util.c | 200 | ||||
-rw-r--r-- | src/ui/util.h | 14 | ||||
-rw-r--r-- | src/ui/widget.c | 3 | ||||
-rw-r--r-- | src/ui/window.c | 75 | ||||
-rw-r--r-- | src/ui/window.h | 3 |
21 files changed, 564 insertions, 170 deletions
diff --git a/src/ui/banner.c b/src/ui/banner.c index 11ae1574..79d70039 100644 --- a/src/ui/banner.c +++ b/src/ui/banner.c | |||
@@ -327,7 +327,8 @@ iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | |||
327 | else { | 327 | else { |
328 | switch (item->code) { | 328 | switch (item->code) { |
329 | case missingGlyphs_GmStatusCode: | 329 | case missingGlyphs_GmStatusCode: |
330 | postCommandf_App("open newtab:1 url:about:fonts"); | 330 | //postCommandf_App("open newtab:1 url:about:fonts"); |
331 | makeGlyphFinder_Widget(); | ||
331 | break; | 332 | break; |
332 | case ansiEscapes_GmStatusCode: | 333 | case ansiEscapes_GmStatusCode: |
333 | makeQuestion_Widget( | 334 | makeQuestion_Widget( |
diff --git a/src/ui/color.c b/src/ui/color.c index 824342ae..9cba322d 100644 --- a/src/ui/color.c +++ b/src/ui/color.c | |||
@@ -522,8 +522,8 @@ iHSLColor setLum_HSLColor(iHSLColor d, float lum) { | |||
522 | } | 522 | } |
523 | 523 | ||
524 | iHSLColor addSatLum_HSLColor(iHSLColor d, float sat, float lum) { | 524 | iHSLColor addSatLum_HSLColor(iHSLColor d, float sat, float lum) { |
525 | d.sat = iClamp(d.sat + sat, 0, 1); | 525 | d.sat = iClamp(d.sat + sat, minSat_HSLColor, 1); |
526 | d.lum = iClamp(d.lum + lum, 0, 1); | 526 | d.lum = iClamp(d.lum + lum, minSat_HSLColor, 1); |
527 | return d; | 527 | return d; |
528 | } | 528 | } |
529 | 529 | ||
diff --git a/src/ui/color.h b/src/ui/color.h index 24f9e713..f46976d7 100644 --- a/src/ui/color.h +++ b/src/ui/color.h | |||
@@ -231,6 +231,8 @@ struct Impl_HSLColor { | |||
231 | float hue, sat, lum, a; | 231 | float hue, sat, lum, a; |
232 | }; | 232 | }; |
233 | 233 | ||
234 | #define minSat_HSLColor 0.013f /* Conversion to 8-bit RGB may result in saturation dropping to zero. */ | ||
235 | |||
234 | iHSLColor hsl_Color (iColor); | 236 | iHSLColor hsl_Color (iColor); |
235 | iColor rgb_HSLColor (iHSLColor); | 237 | iColor rgb_HSLColor (iHSLColor); |
236 | float luma_Color (iColor); | 238 | float luma_Color (iColor); |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index a52e99af..fdc0dd75 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -358,6 +358,7 @@ static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d); | |||
358 | static void prerender_DocumentWidget_ (iAny *); | 358 | static void prerender_DocumentWidget_ (iAny *); |
359 | static void scrollBegan_DocumentWidget_ (iAnyObject *, int, uint32_t); | 359 | static void scrollBegan_DocumentWidget_ (iAnyObject *, int, uint32_t); |
360 | static void refreshWhileScrolling_DocumentWidget_ (iAny *); | 360 | static void refreshWhileScrolling_DocumentWidget_ (iAny *); |
361 | static iBool requestMedia_DocumentWidget_ (iDocumentWidget *d, iGmLinkId linkId, iBool enableFilters); | ||
361 | 362 | ||
362 | /* TODO: The following methods are called from DocumentView, which goes the wrong way. */ | 363 | /* TODO: The following methods are called from DocumentView, which goes the wrong way. */ |
363 | 364 | ||
@@ -1824,7 +1825,7 @@ static void draw_DocumentView_(const iDocumentView *d) { | |||
1824 | } | 1825 | } |
1825 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { | 1826 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { |
1826 | updateSideIconBuf_DocumentView_(d); | 1827 | updateSideIconBuf_DocumentView_(d); |
1827 | } | 1828 | } |
1828 | const iRect docBounds = documentBounds_DocumentView_(d); | 1829 | const iRect docBounds = documentBounds_DocumentView_(d); |
1829 | const iRangei vis = visibleRange_DocumentView_(d); | 1830 | const iRangei vis = visibleRange_DocumentView_(d); |
1830 | iDrawContext ctx = { | 1831 | iDrawContext ctx = { |
@@ -2410,6 +2411,20 @@ static const char *zipPageHeading_(const iRangecc mime) { | |||
2410 | 2411 | ||
2411 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { | 2412 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { |
2412 | iWidget *w = as_Widget(d); | 2413 | iWidget *w = as_Widget(d); |
2414 | /* Embedded images in data links can be shown immediately as they are already fetched | ||
2415 | data that is part of the document. */ | ||
2416 | if (prefs_App()->openDataUrlImagesOnLoad) { | ||
2417 | iGmDocument *doc = d->view.doc; | ||
2418 | for (size_t linkId = 1; ; linkId++) { | ||
2419 | const int linkFlags = linkFlags_GmDocument(doc, linkId); | ||
2420 | const iString *linkUrl = linkUrl_GmDocument(doc, linkId); | ||
2421 | if (!linkUrl) break; | ||
2422 | if (scheme_GmLinkFlag(linkFlags) == data_GmLinkScheme && | ||
2423 | (linkFlags & imageFileExtension_GmLinkFlag)) { | ||
2424 | requestMedia_DocumentWidget_(d, linkId, 0); | ||
2425 | } | ||
2426 | } | ||
2427 | } | ||
2413 | /* Gempub page behavior and footer actions. */ { | 2428 | /* Gempub page behavior and footer actions. */ { |
2414 | /* TODO: move this to gempub.c */ | 2429 | /* TODO: move this to gempub.c */ |
2415 | delete_Gempub(d->sourceGempub); | 2430 | delete_Gempub(d->sourceGempub); |
@@ -2681,7 +2696,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
2681 | if (loadArchive_FontPack(fp, zip)) { | 2696 | if (loadArchive_FontPack(fp, zip)) { |
2682 | appendFormat_String(&str, "# " fontpack_Icon "%s\n%s", | 2697 | appendFormat_String(&str, "# " fontpack_Icon "%s\n%s", |
2683 | cstr_String(id_FontPack(fp).id), | 2698 | cstr_String(id_FontPack(fp).id), |
2684 | cstrCollect_String(infoText_FontPack(fp))); | 2699 | cstrCollect_String(infoText_FontPack(fp, iTrue))); |
2685 | } | 2700 | } |
2686 | appendCStr_String(&str, "\n"); | 2701 | appendCStr_String(&str, "\n"); |
2687 | appendCStr_String(&str, cstr_Lang("fontpack.help")); | 2702 | appendCStr_String(&str, cstr_Lang("fontpack.help")); |
@@ -3258,9 +3273,11 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
3258 | setTextColor_LabelWidget(menu, uiTextAction_ColorId); | 3273 | setTextColor_LabelWidget(menu, uiTextAction_ColorId); |
3259 | } | 3274 | } |
3260 | } | 3275 | } |
3261 | setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d); | 3276 | iInputWidget *input = findChild_Widget(dlg, "input"); |
3262 | setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"), | 3277 | setValidator_InputWidget(input, inputQueryValidator_, d); |
3263 | statusCode == sensitiveInput_GmStatusCode); | 3278 | setBackupFileName_InputWidget(input, "inputbackup.txt"); |
3279 | setSelectAllOnFocus_InputWidget(input, iTrue); | ||
3280 | setSensitiveContent_InputWidget(input, statusCode == sensitiveInput_GmStatusCode); | ||
3264 | if (document_App() != d) { | 3281 | if (document_App() != d) { |
3265 | postCommandf_App("tabs.switch page:%p", d); | 3282 | postCommandf_App("tabs.switch page:%p", d); |
3266 | } | 3283 | } |
@@ -3921,12 +3938,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3921 | const char *unchecked = red_ColorEscape "\u2610"; | 3938 | const char *unchecked = red_ColorEscape "\u2610"; |
3922 | const char *checked = green_ColorEscape "\u2611"; | 3939 | const char *checked = green_ColorEscape "\u2611"; |
3923 | const iBool haveFingerprint = (d->certFlags & haveFingerprint_GmCertFlag) != 0; | 3940 | const iBool haveFingerprint = (d->certFlags & haveFingerprint_GmCertFlag) != 0; |
3924 | const int requiredForTrust = (available_GmCertFlag | haveFingerprint_GmCertFlag | | 3941 | const int requiredForTrust = |
3925 | timeVerified_GmCertFlag); | 3942 | (available_GmCertFlag | haveFingerprint_GmCertFlag | timeVerified_GmCertFlag); |
3926 | const iBool canTrust = ~d->certFlags & trusted_GmCertFlag && | 3943 | const iBool canTrust = ~d->certFlags & trusted_GmCertFlag && |
3927 | ((d->certFlags & requiredForTrust) == requiredForTrust); | 3944 | ((d->certFlags & requiredForTrust) == requiredForTrust); |
3928 | const iRecentUrl *recent = constMostRecentUrl_History(d->mod.history); | 3945 | const iRecentUrl *recent = constMostRecentUrl_History(d->mod.history); |
3929 | const iString *meta = &d->sourceMime; | 3946 | const iString *meta = &d->sourceMime; |
3930 | if (recent && recent->cachedResponse) { | 3947 | if (recent && recent->cachedResponse) { |
3931 | meta = &recent->cachedResponse->meta; | 3948 | meta = &recent->cachedResponse->meta; |
3932 | } | 3949 | } |
@@ -3991,6 +4008,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3991 | if (haveFingerprint) { | 4008 | if (haveFingerprint) { |
3992 | pushBack_Array(items, &(iMenuItem){ "${dlg.cert.fingerprint}", 0, 0, "server.copycert" }); | 4009 | pushBack_Array(items, &(iMenuItem){ "${dlg.cert.fingerprint}", 0, 0, "server.copycert" }); |
3993 | } | 4010 | } |
4011 | const iRangecc root = urlRoot_String(d->mod.url); | ||
4012 | if (!isEmpty_Range(&root)) { | ||
4013 | pushBack_Array(items, &(iMenuItem){ "${pageinfo.settings}", 0, 0, "document.sitespec" }); | ||
4014 | } | ||
3994 | if (!isEmpty_Array(items)) { | 4015 | if (!isEmpty_Array(items)) { |
3995 | pushBack_Array(items, &(iMenuItem){ "---", 0, 0, 0 }); | 4016 | pushBack_Array(items, &(iMenuItem){ "---", 0, 0, 0 }); |
3996 | } | 4017 | } |
@@ -4014,6 +4035,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
4014 | addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok"); | 4035 | addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok"); |
4015 | return iTrue; | 4036 | return iTrue; |
4016 | } | 4037 | } |
4038 | else if (equal_Command(cmd, "document.sitespec") && d == document_App()) { | ||
4039 | if (!findWidget_App("sitespec.palette")) { | ||
4040 | makeSiteSpecificSettings_Widget(d->mod.url); | ||
4041 | } | ||
4042 | return iTrue; | ||
4043 | } | ||
4017 | else if (equal_Command(cmd, "server.unexpire") && document_App() == d) { | 4044 | else if (equal_Command(cmd, "server.unexpire") && document_App() == d) { |
4018 | const iRangecc host = urlHost_String(d->mod.url); | 4045 | const iRangecc host = urlHost_String(d->mod.url); |
4019 | const uint16_t port = urlPort_String(d->mod.url); | 4046 | const uint16_t port = urlPort_String(d->mod.url); |
@@ -4922,7 +4949,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4922 | for (size_t i = 0; i < 64; ++i) { | 4949 | for (size_t i = 0; i < 64; ++i) { |
4923 | setByte_Block(seed, i, iRandom(0, 256)); | 4950 | setByte_Block(seed, i, iRandom(0, 256)); |
4924 | } | 4951 | } |
4925 | setThemeSeed_GmDocument(view->doc, seed); | 4952 | setThemeSeed_GmDocument(view->doc, seed, NULL); |
4926 | delete_Block(seed); | 4953 | delete_Block(seed); |
4927 | invalidate_DocumentWidget_(d); | 4954 | invalidate_DocumentWidget_(d); |
4928 | refresh_Widget(w); | 4955 | refresh_Widget(w); |
@@ -5044,10 +5071,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
5044 | iArray items; | 5071 | iArray items; |
5045 | init_Array(&items, sizeof(iMenuItem)); | 5072 | init_Array(&items, sizeof(iMenuItem)); |
5046 | if (d->contextLink) { | 5073 | if (d->contextLink) { |
5047 | /* Context menu for a link. */ | 5074 | /* Construct the link context menu, depending on what kind of link was clicked. */ |
5048 | interactingWithLink_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */ | 5075 | interactingWithLink_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */ |
5049 | const iString *linkUrl = linkUrl_GmDocument(view->doc, d->contextLink->linkId); | 5076 | const iString *linkUrl = linkUrl_GmDocument(view->doc, d->contextLink->linkId); |
5050 | // const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId); | ||
5051 | const iRangecc scheme = urlScheme_String(linkUrl); | 5077 | const iRangecc scheme = urlScheme_String(linkUrl); |
5052 | const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); | 5078 | const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); |
5053 | iBool isNative = iFalse; | 5079 | iBool isNative = iFalse; |
@@ -5059,41 +5085,55 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
5059 | format_CStr("```%s", cstr_String(infoText)), | 5085 | format_CStr("```%s", cstr_String(infoText)), |
5060 | 0, 0, NULL }); | 5086 | 0, 0, NULL }); |
5061 | } | 5087 | } |
5062 | if (willUseProxy_App(scheme) || isGemini || | 5088 | if (isGemini || |
5089 | willUseProxy_App(scheme) || | ||
5090 | equalCase_Rangecc(scheme, "data") || | ||
5063 | equalCase_Rangecc(scheme, "file") || | 5091 | equalCase_Rangecc(scheme, "file") || |
5064 | equalCase_Rangecc(scheme, "finger") || | 5092 | equalCase_Rangecc(scheme, "finger") || |
5065 | equalCase_Rangecc(scheme, "gopher")) { | 5093 | equalCase_Rangecc(scheme, "gopher")) { |
5066 | isNative = iTrue; | 5094 | isNative = iTrue; |
5067 | /* Regular links that we can open. */ | 5095 | /* Regular links that we can open. */ |
5068 | pushBackN_Array( | 5096 | pushBackN_Array(&items, |
5069 | &items, | 5097 | (iMenuItem[]){ |
5070 | (iMenuItem[]){ { openTab_Icon " ${link.newtab}", | 5098 | { openTab_Icon " ${link.newtab}", |
5071 | 0, | 5099 | 0, |
5072 | 0, | 5100 | 0, |
5073 | format_CStr("!open newtab:1 origin:%s url:%s", | 5101 | format_CStr("!open newtab:1 origin:%s url:%s", |
5074 | cstr_String(id_Widget(w)), | 5102 | cstr_String(id_Widget(w)), |
5075 | cstr_String(linkUrl)) }, | 5103 | cstr_String(linkUrl)) }, |
5076 | { openTabBg_Icon " ${link.newtab.background}", | 5104 | { openTabBg_Icon " ${link.newtab.background}", |
5077 | 0, | 5105 | 0, |
5078 | 0, | 5106 | 0, |
5079 | format_CStr("!open newtab:2 origin:%s url:%s", | 5107 | format_CStr("!open newtab:2 origin:%s url:%s", |
5080 | cstr_String(id_Widget(w)), | 5108 | cstr_String(id_Widget(w)), |
5081 | cstr_String(linkUrl)) }, | 5109 | cstr_String(linkUrl)) }, |
5082 | { "${link.side}", | 5110 | { openWindow_Icon " ${link.newwindow}", |
5083 | 0, | 5111 | 0, |
5084 | 0, | 5112 | 0, |
5085 | format_CStr("!open newtab:4 origin:%s url:%s", | 5113 | format_CStr("!open newwindow:1 origin:%s url:%s", |
5086 | cstr_String(id_Widget(w)), | 5114 | cstr_String(id_Widget(w)), |
5087 | cstr_String(linkUrl)) }, | 5115 | cstr_String(linkUrl)) }, |
5088 | { "${link.side.newtab}", | 5116 | { "${link.side}", |
5089 | 0, | 5117 | 0, |
5090 | 0, | 5118 | 0, |
5091 | format_CStr("!open newtab:5 origin:%s url:%s", | 5119 | format_CStr("!open newtab:4 origin:%s url:%s", |
5092 | cstr_String(id_Widget(w)), | 5120 | cstr_String(id_Widget(w)), |
5093 | cstr_String(linkUrl)) } }, | 5121 | cstr_String(linkUrl)) }, |
5094 | 4); | 5122 | { "${link.side.newtab}", |
5123 | 0, | ||
5124 | 0, | ||
5125 | format_CStr("!open newtab:5 origin:%s url:%s", | ||
5126 | cstr_String(id_Widget(w)), | ||
5127 | cstr_String(linkUrl)) }, | ||
5128 | }, | ||
5129 | 5); | ||
5095 | if (deviceType_App() == phone_AppDeviceType) { | 5130 | if (deviceType_App() == phone_AppDeviceType) { |
5096 | removeN_Array(&items, size_Array(&items) - 2, iInvalidSize); | 5131 | /* Phones don't do windows or splits. */ |
5132 | removeN_Array(&items, size_Array(&items) - 3, iInvalidSize); | ||
5133 | } | ||
5134 | else if (deviceType_App() == tablet_AppDeviceType) { | ||
5135 | /* Tablets only do splits. */ | ||
5136 | removeN_Array(&items, size_Array(&items) - 3, 1); | ||
5097 | } | 5137 | } |
5098 | if (equalCase_Rangecc(scheme, "file")) { | 5138 | if (equalCase_Rangecc(scheme, "file")) { |
5099 | pushBack_Array(&items, &(iMenuItem){ "---" }); | 5139 | pushBack_Array(&items, &(iMenuItem){ "---" }); |
@@ -5248,6 +5288,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
5248 | "document.upload", | 5288 | "document.upload", |
5249 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") && | 5289 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") && |
5250 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")); | 5290 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")); |
5291 | setMenuItemDisabled_Widget( | ||
5292 | d->menu, | ||
5293 | "document.upload copy:1", | ||
5294 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") && | ||
5295 | !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")); | ||
5251 | } | 5296 | } |
5252 | processContextMenuEvent_Widget(d->menu, ev, {}); | 5297 | processContextMenuEvent_Widget(d->menu, ev, {}); |
5253 | } | 5298 | } |
@@ -5545,12 +5590,12 @@ static void prerender_DocumentWidget_(iAny *context) { | |||
5545 | } | 5590 | } |
5546 | const iDocumentWidget *d = context; | 5591 | const iDocumentWidget *d = context; |
5547 | iDrawContext ctx = { | 5592 | iDrawContext ctx = { |
5548 | .view = &d->view, | 5593 | .view = &d->view, |
5549 | .docBounds = documentBounds_DocumentView_(&d->view), | 5594 | .docBounds = documentBounds_DocumentView_(&d->view), |
5550 | .vis = visibleRange_DocumentView_(&d->view), | 5595 | .vis = visibleRange_DocumentView_(&d->view), |
5551 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0 | 5596 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0 |
5552 | }; | 5597 | }; |
5553 | // printf("%u prerendering\n", SDL_GetTicks()); | 5598 | // printf("%u prerendering\n", SDL_GetTicks()); |
5554 | if (d->view.visBuf->buffers[0].texture) { | 5599 | if (d->view.visBuf->buffers[0].texture) { |
5555 | makePaletteGlobal_GmDocument(d->view.doc); | 5600 | makePaletteGlobal_GmDocument(d->view.doc); |
5556 | if (render_DocumentView_(&d->view, &ctx, iTrue /* just fill up progressively */)) { | 5601 | if (render_DocumentView_(&d->view, &ctx, iTrue /* just fill up progressively */)) { |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 6a8d428a..1b68ff57 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -1201,6 +1201,11 @@ void selectAll_InputWidget(iInputWidget *d) { | |||
1201 | #endif | 1201 | #endif |
1202 | } | 1202 | } |
1203 | 1203 | ||
1204 | void deselect_InputWidget(iInputWidget *d) { | ||
1205 | iZap(d->mark); | ||
1206 | refresh_Widget(as_Widget(d)); | ||
1207 | } | ||
1208 | |||
1204 | void validate_InputWidget(iInputWidget *d) { | 1209 | void validate_InputWidget(iInputWidget *d) { |
1205 | if (d->validator) { | 1210 | if (d->validator) { |
1206 | d->validator(d, d->validatorContext); /* this may change the contents */ | 1211 | d->validator(d, d->validatorContext); /* this may change the contents */ |
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index 000fa4b7..832f7853 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -59,6 +59,7 @@ void setBackupFileName_InputWidget (iInputWidget *, const char *fileName); | |||
59 | void begin_InputWidget (iInputWidget *); | 59 | void begin_InputWidget (iInputWidget *); |
60 | void end_InputWidget (iInputWidget *, iBool accept); | 60 | void end_InputWidget (iInputWidget *, iBool accept); |
61 | void selectAll_InputWidget (iInputWidget *); | 61 | void selectAll_InputWidget (iInputWidget *); |
62 | void deselect_InputWidget (iInputWidget *); | ||
62 | void validate_InputWidget (iInputWidget *); | 63 | void validate_InputWidget (iInputWidget *); |
63 | 64 | ||
64 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); | 65 | void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); |
diff --git a/src/ui/keys.c b/src/ui/keys.c index 26a286bc..88efa98b 100644 --- a/src/ui/keys.c +++ b/src/ui/keys.c | |||
@@ -243,6 +243,8 @@ static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] = | |||
243 | { 110,{ "${menu.save.downloads}", SDLK_s, KMOD_PRIMARY, "document.save" }, 0 }, | 243 | { 110,{ "${menu.save.downloads}", SDLK_s, KMOD_PRIMARY, "document.save" }, 0 }, |
244 | { 120,{ "${keys.upload}", SDLK_u, KMOD_PRIMARY, "document.upload" }, 0 }, | 244 | { 120,{ "${keys.upload}", SDLK_u, KMOD_PRIMARY, "document.upload" }, 0 }, |
245 | { 121,{ "${keys.upload.edit}", SDLK_e, KMOD_PRIMARY, "document.upload copy:1" }, 0 }, | 245 | { 121,{ "${keys.upload.edit}", SDLK_e, KMOD_PRIMARY, "document.upload copy:1" }, 0 }, |
246 | { 125,{ "${keys.pageinfo}", SDLK_i, KMOD_PRIMARY, "document.info" }, 0 }, | ||
247 | { 126,{ "${keys.sitespec}", ',', KMOD_PRIMARY | KMOD_SHIFT, "document.sitespec" }, 0 }, | ||
246 | { 130,{ "${keys.input.precedingline}", SDLK_v, KMOD_PRIMARY | KMOD_SHIFT, "input.precedingline" }, 0 }, | 248 | { 130,{ "${keys.input.precedingline}", SDLK_v, KMOD_PRIMARY | KMOD_SHIFT, "input.precedingline" }, 0 }, |
247 | /* The following cannot currently be changed (built-in duplicates). */ | 249 | /* The following cannot currently be changed (built-in duplicates). */ |
248 | #if defined (iPlatformApple) | 250 | #if defined (iPlatformApple) |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 3454014a..75cbbf3a 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -231,7 +231,17 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int | |||
231 | *bg = uiBackgroundUnfocusedSelection_ColorId; | 231 | *bg = uiBackgroundUnfocusedSelection_ColorId; |
232 | } | 232 | } |
233 | else { | 233 | else { |
234 | *bg = uiBackgroundSelected_ColorId; | 234 | const enum iGmDocumentTheme docTheme = docTheme_Prefs(prefs_App()); |
235 | if ((docTheme == colorfulLight_GmDocumentTheme || docTheme == sepia_GmDocumentTheme) && | ||
236 | !cmp_String(&d->widget.parent->id, "tabs.buttons")) { | ||
237 | *bg = (docTheme == sepia_GmDocumentTheme && | ||
238 | colorTheme_App() == pureWhite_ColorTheme | ||
239 | ? tmBackground_ColorId | ||
240 | : tmBannerBackground_ColorId); | ||
241 | } | ||
242 | else { | ||
243 | *bg = uiBackgroundSelected_ColorId; | ||
244 | } | ||
235 | } | 245 | } |
236 | if (!isKeyRoot) { | 246 | if (!isKeyRoot) { |
237 | *bg = isDark_ColorTheme(colorTheme_App()) ? uiBackgroundUnfocusedSelection_ColorId | 247 | *bg = isDark_ColorTheme(colorTheme_App()) ? uiBackgroundUnfocusedSelection_ColorId |
diff --git a/src/ui/linkinfo.c b/src/ui/linkinfo.c index 36ab00c8..15aea16e 100644 --- a/src/ui/linkinfo.c +++ b/src/ui/linkinfo.c | |||
@@ -91,6 +91,10 @@ void infoText_LinkInfo(const iGmDocument *doc, iGmLinkId linkId, iString *text_o | |||
91 | appendCStr_String(text_out, "\x1b[0m"); | 91 | appendCStr_String(text_out, "\x1b[0m"); |
92 | appendRange_String(text_out, (iRangecc){ parts.path.start, constEnd_String(url) }); | 92 | appendRange_String(text_out, (iRangecc){ parts.path.start, constEnd_String(url) }); |
93 | } | 93 | } |
94 | else if (scheme == data_GmLinkScheme) { | ||
95 | appendCStr_String(text_out, paperclip_Icon " "); | ||
96 | append_String(text_out, prettyDataUrl_String(url, none_ColorId)); | ||
97 | } | ||
94 | else if (scheme != gemini_GmLinkScheme) { | 98 | else if (scheme != gemini_GmLinkScheme) { |
95 | const size_t maxDispLen = 300; | 99 | const size_t maxDispLen = 300; |
96 | appendCStr_String(text_out, scheme == file_GmLinkScheme ? "" : globe_Icon " "); | 100 | appendCStr_String(text_out, scheme == file_GmLinkScheme ? "" : globe_Icon " "); |
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c index f14170ad..dc3264a2 100644 --- a/src/ui/lookupwidget.c +++ b/src/ui/lookupwidget.c | |||
@@ -568,7 +568,7 @@ static void presentResults_LookupWidget_(iLookupWidget *d) { | |||
568 | cstr_String(&res->label), | 568 | cstr_String(&res->label), |
569 | uiText_ColorEscape, | 569 | uiText_ColorEscape, |
570 | cstr_String(&res->meta)); | 570 | cstr_String(&res->meta)); |
571 | const iString *cmd = feedEntryOpenCommand_String(&res->url, 0); | 571 | const iString *cmd = feedEntryOpenCommand_String(&res->url, 0, 0); |
572 | if (cmd) { | 572 | if (cmd) { |
573 | set_String(&item->command, cmd); | 573 | set_String(&item->command, cmd); |
574 | } | 574 | } |
diff --git a/src/ui/root.c b/src/ui/root.c index 6e187313..9dee50ae 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -56,7 +56,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
56 | #if defined (iPlatformPcDesktop) | 56 | #if defined (iPlatformPcDesktop) |
57 | /* TODO: Submenus wouldn't hurt here. */ | 57 | /* TODO: Submenus wouldn't hurt here. */ |
58 | static const iMenuItem navMenuItems_[] = { | 58 | static const iMenuItem navMenuItems_[] = { |
59 | { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, | 59 | { openWindow_Icon " ${menu.newwindow}", SDLK_n, KMOD_PRIMARY, "window.new" }, |
60 | { add_Icon " ${menu.newtab}", SDLK_t, KMOD_PRIMARY, "tabs.new" }, | ||
60 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, | 61 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, |
61 | { "---" }, | 62 | { "---" }, |
62 | { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" }, | 63 | { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" }, |
@@ -468,6 +469,10 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { | |||
468 | return iFalse; | 469 | return iFalse; |
469 | } | 470 | } |
470 | else if (equal_Command(cmd, "window.setrect")) { | 471 | else if (equal_Command(cmd, "window.setrect")) { |
472 | if (hasLabel_Command(cmd, "index") && | ||
473 | argU32Label_Command(cmd, "index") != windowIndex_Root(root->root)) { | ||
474 | return iFalse; | ||
475 | } | ||
471 | const int snap = argLabel_Command(cmd, "snap"); | 476 | const int snap = argLabel_Command(cmd, "snap"); |
472 | if (snap) { | 477 | if (snap) { |
473 | iMainWindow *window = get_MainWindow(); | 478 | iMainWindow *window = get_MainWindow(); |
@@ -1059,6 +1064,8 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
1059 | updateNavBarIdentity_(navBar); | 1064 | updateNavBarIdentity_(navBar); |
1060 | } | 1065 | } |
1061 | setFocus_Widget(NULL); | 1066 | setFocus_Widget(NULL); |
1067 | makePaletteGlobal_GmDocument(document_DocumentWidget(doc)); | ||
1068 | refresh_Widget(findWidget_Root("doctabs")); | ||
1062 | } | 1069 | } |
1063 | else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) { | 1070 | else if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd)) { |
1064 | iWidget *widget = pointer_Command(cmd); | 1071 | iWidget *widget = pointer_Command(cmd); |
@@ -1774,6 +1781,13 @@ void showToolbar_Root(iRoot *d, iBool show) { | |||
1774 | } | 1781 | } |
1775 | } | 1782 | } |
1776 | 1783 | ||
1784 | size_t windowIndex_Root(const iRoot *d) { | ||
1785 | if (type_Window(d->window) == main_WindowType) { | ||
1786 | return windowIndex_App(as_MainWindow(d->window)); | ||
1787 | } | ||
1788 | return iInvalidPos; | ||
1789 | } | ||
1790 | |||
1777 | iInt2 size_Root(const iRoot *d) { | 1791 | iInt2 size_Root(const iRoot *d) { |
1778 | return d && d->widget ? d->widget->rect.size : zero_I2(); | 1792 | return d && d->widget ? d->widget->rect.size : zero_I2(); |
1779 | } | 1793 | } |
diff --git a/src/ui/root.h b/src/ui/root.h index a81ebdf7..3b053c9e 100644 --- a/src/ui/root.h +++ b/src/ui/root.h | |||
@@ -47,6 +47,7 @@ void showOrHideNewTabButton_Root (iRoot *); | |||
47 | 47 | ||
48 | void notifyVisualOffsetChange_Root (iRoot *); | 48 | void notifyVisualOffsetChange_Root (iRoot *); |
49 | 49 | ||
50 | size_t windowIndex_Root (const iRoot *); | ||
50 | iInt2 size_Root (const iRoot *); | 51 | iInt2 size_Root (const iRoot *); |
51 | iRect rect_Root (const iRoot *); | 52 | iRect rect_Root (const iRoot *); |
52 | iRect safeRect_Root (const iRoot *); | 53 | iRect safeRect_Root (const iRoot *); |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index da377ac2..8a96961a 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -412,19 +412,25 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
412 | setOutline_LabelWidget(child_Widget(d->actions, 1), d->feedsMode != all_FeedsMode); | 412 | setOutline_LabelWidget(child_Widget(d->actions, 1), d->feedsMode != all_FeedsMode); |
413 | setOutline_LabelWidget(child_Widget(d->actions, 2), d->feedsMode != unread_FeedsMode); | 413 | setOutline_LabelWidget(child_Widget(d->actions, 2), d->feedsMode != unread_FeedsMode); |
414 | } | 414 | } |
415 | d->menu = makeMenu_Widget( | 415 | const iMenuItem menuItems[] = { |
416 | as_Widget(d), | 416 | { openTab_Icon " ${menu.opentab}", 0, 0, "feed.entry.open newtab:1" }, |
417 | (iMenuItem[]){ { openTab_Icon " ${feeds.entry.newtab}", 0, 0, "feed.entry.opentab" }, | 417 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "feed.entry.open newtab:2" }, |
418 | { circle_Icon " ${feeds.entry.markread}", 0, 0, "feed.entry.toggleread" }, | 418 | #if defined (iPlatformDesktop) |
419 | { bookmark_Icon " ${feeds.entry.bookmark}", 0, 0, "feed.entry.bookmark" }, | 419 | { openWindow_Icon " ${menu.openwindow}", 0, 0, "feed.entry.open newwindow:1" }, |
420 | { "---", 0, 0, NULL }, | 420 | #endif |
421 | { page_Icon " ${feeds.entry.openfeed}", 0, 0, "feed.entry.openfeed" }, | 421 | { "---", 0, 0, NULL }, |
422 | { edit_Icon " ${feeds.edit}", 0, 0, "feed.entry.edit" }, | 422 | { circle_Icon " ${feeds.entry.markread}", 0, 0, "feed.entry.toggleread" }, |
423 | { whiteStar_Icon " " uiTextCaution_ColorEscape "${feeds.unsubscribe}", 0, 0, "feed.entry.unsubscribe" }, | 423 | { bookmark_Icon " ${feeds.entry.bookmark}", 0, 0, "feed.entry.bookmark" }, |
424 | { "---", 0, 0, NULL }, | 424 | { "${menu.copyurl}", 0, 0, "feed.entry.copy" }, |
425 | { check_Icon " ${feeds.markallread}", SDLK_a, KMOD_SHIFT, "feeds.markallread" }, | 425 | { "---", 0, 0, NULL }, |
426 | { reload_Icon " ${feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" } }, | 426 | { page_Icon " ${feeds.entry.openfeed}", 0, 0, "feed.entry.openfeed" }, |
427 | 10); | 427 | { edit_Icon " ${feeds.edit}", 0, 0, "feed.entry.edit" }, |
428 | { whiteStar_Icon " " uiTextCaution_ColorEscape "${feeds.unsubscribe}", 0, 0, "feed.entry.unsubscribe" }, | ||
429 | { "---", 0, 0, NULL }, | ||
430 | { check_Icon " ${feeds.markallread}", SDLK_a, KMOD_SHIFT, "feeds.markallread" }, | ||
431 | { reload_Icon " ${feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" } | ||
432 | }; | ||
433 | d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems)); | ||
428 | d->modeMenu = makeMenu_Widget( | 434 | d->modeMenu = makeMenu_Widget( |
429 | as_Widget(d), | 435 | as_Widget(d), |
430 | (iMenuItem[]){ | 436 | (iMenuItem[]){ |
@@ -487,26 +493,29 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
487 | addItem_ListWidget(d->list, item); | 493 | addItem_ListWidget(d->list, item); |
488 | iRelease(item); | 494 | iRelease(item); |
489 | } | 495 | } |
490 | d->menu = makeMenu_Widget( | 496 | const iMenuItem menuItems[] = { |
491 | as_Widget(d), | 497 | { openTab_Icon " ${menu.opentab}", 0, 0, "bookmark.open newtab:1" }, |
492 | (iMenuItem[]){ { openTab_Icon " ${menu.opentab}", 0, 0, "bookmark.open newtab:1" }, | 498 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "bookmark.open newtab:2" }, |
493 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "bookmark.open newtab:2" }, | 499 | #if defined (iPlatformDesktop) |
494 | { "---", 0, 0, NULL }, | 500 | { openWindow_Icon " ${menu.openwindow}", 0, 0, "bookmark.open newwindow:1" }, |
495 | { edit_Icon " ${menu.edit}", 0, 0, "bookmark.edit" }, | 501 | #endif |
496 | { copy_Icon " ${menu.dup}", 0, 0, "bookmark.dup" }, | 502 | { "---", 0, 0, NULL }, |
497 | { "${menu.copyurl}", 0, 0, "bookmark.copy" }, | 503 | { edit_Icon " ${menu.edit}", 0, 0, "bookmark.edit" }, |
498 | { "---", 0, 0, NULL }, | 504 | { copy_Icon " ${menu.dup}", 0, 0, "bookmark.dup" }, |
499 | { "", 0, 0, "bookmark.tag tag:subscribed" }, | 505 | { "${menu.copyurl}", 0, 0, "bookmark.copy" }, |
500 | { "", 0, 0, "bookmark.tag tag:homepage" }, | 506 | { "---", 0, 0, NULL }, |
501 | { "", 0, 0, "bookmark.tag tag:remotesource" }, | 507 | { "", 0, 0, "bookmark.tag tag:subscribed" }, |
502 | { "---", 0, 0, NULL }, | 508 | { "", 0, 0, "bookmark.tag tag:homepage" }, |
503 | { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" }, | 509 | { "", 0, 0, "bookmark.tag tag:remotesource" }, |
504 | { "---", 0, 0, NULL }, | 510 | { "---", 0, 0, NULL }, |
505 | { add_Icon " ${menu.newfolder}", 0, 0, "bookmark.addfolder" }, | 511 | { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" }, |
506 | { upDownArrow_Icon " ${menu.sort.alpha}", 0, 0, "bookmark.sortfolder" }, | 512 | { "---", 0, 0, NULL }, |
507 | { "---", 0, 0, NULL }, | 513 | { folder_Icon " ${menu.newfolder}", 0, 0, "bookmark.addfolder" }, |
508 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } }, | 514 | { upDownArrow_Icon " ${menu.sort.alpha}", 0, 0, "bookmark.sortfolder" }, |
509 | 17); | 515 | { "---", 0, 0, NULL }, |
516 | { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } | ||
517 | }; | ||
518 | d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems)); | ||
510 | d->modeMenu = makeMenu_Widget( | 519 | d->modeMenu = makeMenu_Widget( |
511 | as_Widget(d), | 520 | as_Widget(d), |
512 | (iMenuItem[]){ { bookmark_Icon " ${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, | 521 | (iMenuItem[]){ { bookmark_Icon " ${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" }, |
@@ -520,7 +529,7 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
520 | addActionButton_SidebarWidget_(d, "${sidebar.action.bookmarks.newfolder}", | 529 | addActionButton_SidebarWidget_(d, "${sidebar.action.bookmarks.newfolder}", |
521 | "bookmarks.addfolder", !d->isEditing ? hidden_WidgetFlag : 0); | 530 | "bookmarks.addfolder", !d->isEditing ? hidden_WidgetFlag : 0); |
522 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); | 531 | addChildFlags_Widget(d->actions, iClob(new_Widget()), expand_WidgetFlag); |
523 | iLabelWidget *btn = addActionButton_SidebarWidget_(d, | 532 | addActionButton_SidebarWidget_(d, |
524 | d->isEditing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}", | 533 | d->isEditing ? "${sidebar.close}" : "${sidebar.action.bookmarks.edit}", |
525 | "sidebar.bookmarks.edit", 0); | 534 | "sidebar.bookmarks.edit", 0); |
526 | } | 535 | } |
@@ -568,16 +577,21 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct | |||
568 | addItem_ListWidget(d->list, item); | 577 | addItem_ListWidget(d->list, item); |
569 | iRelease(item); | 578 | iRelease(item); |
570 | } | 579 | } |
571 | d->menu = makeMenu_Widget( | 580 | const iMenuItem menuItems[] = { |
572 | as_Widget(d), | 581 | { openTab_Icon " ${menu.opentab}", 0, 0, "history.open newtab:1" }, |
573 | (iMenuItem[]){ | 582 | { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "history.open newtab:2" }, |
574 | { "${menu.copyurl}", 0, 0, "history.copy" }, | 583 | #if defined (iPlatformDesktop) |
575 | { bookmark_Icon " ${sidebar.entry.bookmark}", 0, 0, "history.addbookmark" }, | 584 | { openWindow_Icon " ${menu.openwindow}", 0, 0, "history.open newwindow:1" }, |
576 | { "---", 0, 0, NULL }, | 585 | #endif |
577 | { close_Icon " ${menu.forgeturl}", 0, 0, "history.delete" }, | 586 | { "---" }, |
578 | { "---", 0, 0, NULL }, | 587 | { bookmark_Icon " ${sidebar.entry.bookmark}", 0, 0, "history.addbookmark" }, |
579 | { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" }, | 588 | { "${menu.copyurl}", 0, 0, "history.copy" }, |
580 | }, 6); | 589 | { "---", 0, 0, NULL }, |
590 | { close_Icon " ${menu.forgeturl}", 0, 0, "history.delete" }, | ||
591 | { "---", 0, 0, NULL }, | ||
592 | { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" }, | ||
593 | }; | ||
594 | d->menu = makeMenu_Widget(as_Widget(d), menuItems, iElemCount(menuItems)); | ||
581 | d->modeMenu = makeMenu_Widget( | 595 | d->modeMenu = makeMenu_Widget( |
582 | as_Widget(d), | 596 | as_Widget(d), |
583 | (iMenuItem[]){ | 597 | (iMenuItem[]){ |
@@ -981,7 +995,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si | |||
981 | } | 995 | } |
982 | case feeds_SidebarMode: { | 996 | case feeds_SidebarMode: { |
983 | postCommandString_Root(get_Root(), | 997 | postCommandString_Root(get_Root(), |
984 | feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys()))); | 998 | feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys()), 0)); |
985 | break; | 999 | break; |
986 | } | 1000 | } |
987 | case bookmarks_SidebarMode: | 1001 | case bookmarks_SidebarMode: |
@@ -1641,11 +1655,20 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1641 | else if (startsWith_CStr(cmd, "feed.entry.") && d->mode == feeds_SidebarMode) { | 1655 | else if (startsWith_CStr(cmd, "feed.entry.") && d->mode == feeds_SidebarMode) { |
1642 | const iSidebarItem *item = d->contextItem; | 1656 | const iSidebarItem *item = d->contextItem; |
1643 | if (item) { | 1657 | if (item) { |
1644 | if (isCommand_Widget(w, ev, "feed.entry.opentab")) { | 1658 | if (isCommand_Widget(w, ev, "feed.entry.open")) { |
1645 | postCommandString_Root(get_Root(), feedEntryOpenCommand_String(&item->url, 1)); | 1659 | const char *cmd = command_UserEvent(ev); |
1660 | postCommandString_Root( | ||
1661 | get_Root(), | ||
1662 | feedEntryOpenCommand_String(&item->url, | ||
1663 | argLabel_Command(cmd, "newtab"), | ||
1664 | argLabel_Command(cmd, "newwindow"))); | ||
1646 | return iTrue; | 1665 | return iTrue; |
1647 | } | 1666 | } |
1648 | if (isCommand_Widget(w, ev, "feed.entry.toggleread")) { | 1667 | else if (isCommand_Widget(w, ev, "feed.entry.copy")) { |
1668 | SDL_SetClipboardText(cstr_String(&item->url)); | ||
1669 | return iTrue; | ||
1670 | } | ||
1671 | else if (isCommand_Widget(w, ev, "feed.entry.toggleread")) { | ||
1649 | iVisited *vis = visited_App(); | 1672 | iVisited *vis = visited_App(); |
1650 | const iString *url = urlFragmentStripped_String(&item->url); | 1673 | const iString *url = urlFragmentStripped_String(&item->url); |
1651 | if (containsUrl_Visited(vis, url)) { | 1674 | if (containsUrl_Visited(vis, url)) { |
@@ -1657,7 +1680,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1657 | postCommand_App("visited.changed"); | 1680 | postCommand_App("visited.changed"); |
1658 | return iTrue; | 1681 | return iTrue; |
1659 | } | 1682 | } |
1660 | if (isCommand_Widget(w, ev, "feed.entry.bookmark")) { | 1683 | else if (isCommand_Widget(w, ev, "feed.entry.bookmark")) { |
1661 | makeBookmarkCreation_Widget(&item->url, &item->label, item->icon); | 1684 | makeBookmarkCreation_Widget(&item->url, &item->label, item->icon); |
1662 | if (deviceType_App() == desktop_AppDeviceType) { | 1685 | if (deviceType_App() == desktop_AppDeviceType) { |
1663 | postCommand_App("focus.set id:bmed.title"); | 1686 | postCommand_App("focus.set id:bmed.title"); |
@@ -1706,6 +1729,18 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1706 | } | 1729 | } |
1707 | return iTrue; | 1730 | return iTrue; |
1708 | } | 1731 | } |
1732 | else if (isCommand_Widget(w, ev, "history.open")) { | ||
1733 | const iSidebarItem *item = d->contextItem; | ||
1734 | if (item && !isEmpty_String(&item->url)) { | ||
1735 | const char *cmd = command_UserEvent(ev); | ||
1736 | postCommand_Widget(d, | ||
1737 | "!open newtab:%d newwindow:%d url:%s", | ||
1738 | argLabel_Command(cmd, "newtab"), | ||
1739 | argLabel_Command(cmd, "newwindow"), | ||
1740 | cstr_String(&item->url)); | ||
1741 | } | ||
1742 | return iTrue; | ||
1743 | } | ||
1709 | else if (isCommand_Widget(w, ev, "history.copy")) { | 1744 | else if (isCommand_Widget(w, ev, "history.copy")) { |
1710 | const iSidebarItem *item = d->contextItem; | 1745 | const iSidebarItem *item = d->contextItem; |
1711 | if (item && !isEmpty_String(&item->url)) { | 1746 | if (item && !isEmpty_String(&item->url)) { |
@@ -2156,28 +2191,38 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | |||
2156 | : uiTextDim_ColorId; | 2191 | : uiTextDim_ColorId; |
2157 | iUrl parts; | 2192 | iUrl parts; |
2158 | init_Url(&parts, &d->label); | 2193 | init_Url(&parts, &d->label); |
2159 | const iBool isAbout = equalCase_Rangecc(parts.scheme, "about"); | 2194 | const iBool isAbout = equalCase_Rangecc(parts.scheme, "about"); |
2160 | const iBool isGemini = equalCase_Rangecc(parts.scheme, "gemini"); | 2195 | const iBool isGemini = equalCase_Rangecc(parts.scheme, "gemini"); |
2161 | draw_Text(font, | 2196 | const iBool isData = equalCase_Rangecc(parts.scheme, "data"); |
2162 | add_I2(topLeft_Rect(itemRect), | 2197 | const int queryColor = isPressing ? uiTextPressed_ColorId |
2163 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)), | 2198 | : isHover ? uiText_ColorId |
2164 | fg, | 2199 | : uiAnnotation_ColorId; |
2165 | "%s%s%s%s%s%s%s%s", | 2200 | const iInt2 textPos = |
2166 | isGemini ? "" : cstr_Rangecc(parts.scheme), | 2201 | add_I2(topLeft_Rect(itemRect), |
2167 | isGemini ? "" | 2202 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)); |
2168 | : isAbout ? ":" | 2203 | if (isData) { |
2169 | : "://", | 2204 | drawRange_Text( |
2170 | escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId | 2205 | font, textPos, fg, range_String(prettyDataUrl_String(&d->label, queryColor))); |
2171 | : uiTextFramelessHover_ColorId) | 2206 | } |
2172 | : uiTextStrong_ColorId), | 2207 | else { |
2173 | cstr_Rangecc(parts.host), | 2208 | draw_Text( |
2174 | escape_Color(fg), | 2209 | font, |
2175 | cstr_Rangecc(parts.path), | 2210 | textPos, |
2176 | !isEmpty_Range(&parts.query) ? escape_Color(isPressing ? uiTextPressed_ColorId | 2211 | fg, |
2177 | : isHover ? uiText_ColorId | 2212 | "%s%s%s%s%s%s%s%s", |
2178 | : uiAnnotation_ColorId) | 2213 | isGemini ? "" : cstr_Rangecc(parts.scheme), |
2179 | : "", | 2214 | isGemini ? "" |
2180 | !isEmpty_Range(&parts.query) ? cstr_Rangecc(parts.query) : ""); | 2215 | : isAbout ? ":" |
2216 | : "://", | ||
2217 | escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId | ||
2218 | : uiTextFramelessHover_ColorId) | ||
2219 | : uiTextStrong_ColorId), | ||
2220 | cstr_Rangecc(parts.host), | ||
2221 | escape_Color(fg), | ||
2222 | cstr_Rangecc(parts.path), | ||
2223 | !isEmpty_Range(&parts.query) ? escape_Color(queryColor) : "", | ||
2224 | !isEmpty_Range(&parts.query) ? cstr_Rangecc(parts.query) : ""); | ||
2225 | } | ||
2181 | } | 2226 | } |
2182 | iEndCollect(); | 2227 | iEndCollect(); |
2183 | } | 2228 | } |
diff --git a/src/ui/text.c b/src/ui/text.c index c19aed2f..83e87d0c 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -258,8 +258,6 @@ static int cmp_PrioMapItem_(const void *a, const void *b) { | |||
258 | } | 258 | } |
259 | 259 | ||
260 | struct Impl_Text { | 260 | struct Impl_Text { |
261 | // enum iTextFont contentFont; | ||
262 | // enum iTextFont headingFont; | ||
263 | float contentFontSize; | 261 | float contentFontSize; |
264 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ | 262 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ |
265 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ | 263 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ |
@@ -276,7 +274,8 @@ struct Impl_Text { | |||
276 | int ansiFlags; | 274 | int ansiFlags; |
277 | int baseFontId; /* base attributes (for restoring via escapes) */ | 275 | int baseFontId; /* base attributes (for restoring via escapes) */ |
278 | int baseFgColorId; | 276 | int baseFgColorId; |
279 | iBool missingGlyphs; /* true if a glyph couldn't be found */ | 277 | iBool missingGlyphs; /* true if a glyph couldn't be found */ |
278 | iChar missingChars[20]; /* rotating buffer of the latest missing characters */ | ||
280 | }; | 279 | }; |
281 | 280 | ||
282 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 281 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) |
@@ -296,6 +295,7 @@ static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId) | |||
296 | /* This is the highest priority override font. */ | 295 | /* This is the highest priority override font. */ |
297 | d->overrideFontId = baseId; | 296 | d->overrideFontId = baseId; |
298 | } | 297 | } |
298 | iAssert(activeText_ == d); | ||
299 | pushBack_Array(&d->fontPriorityOrder, &(iPrioMapItem){ spec->priority, baseId }); | 299 | pushBack_Array(&d->fontPriorityOrder, &(iPrioMapItem){ spec->priority, baseId }); |
300 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { | 300 | for (enum iFontStyle style = 0; style < max_FontStyle; style++) { |
301 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { | 301 | for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) { |
@@ -357,6 +357,8 @@ static void initFonts_Text_(iText *d) { | |||
357 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); | 357 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); |
358 | #endif | 358 | #endif |
359 | gap_Text = iRound(gap_UI * d->contentFontSize); | 359 | gap_Text = iRound(gap_UI * d->contentFontSize); |
360 | // d->missingGlyphs = iFalse; | ||
361 | // iZap(d->missingChars); | ||
360 | } | 362 | } |
361 | 363 | ||
362 | static void deinitFonts_Text_(iText *d) { | 364 | static void deinitFonts_Text_(iText *d) { |
@@ -424,6 +426,7 @@ void init_Text(iText *d, SDL_Renderer *render) { | |||
424 | d->baseFontId = -1; | 426 | d->baseFontId = -1; |
425 | d->baseFgColorId = -1; | 427 | d->baseFgColorId = -1; |
426 | d->missingGlyphs = iFalse; | 428 | d->missingGlyphs = iFalse; |
429 | iZap(d->missingChars); | ||
427 | d->render = render; | 430 | d->render = render; |
428 | /* A grayscale palette for rasterized glyphs. */ { | 431 | /* A grayscale palette for rasterized glyphs. */ { |
429 | SDL_Color colors[256]; | 432 | SDL_Color colors[256]; |
@@ -497,10 +500,13 @@ static void resetCache_Text_(iText *d) { | |||
497 | } | 500 | } |
498 | 501 | ||
499 | void resetFonts_Text(iText *d) { | 502 | void resetFonts_Text(iText *d) { |
503 | iText *oldActive = activeText_; | ||
504 | setCurrent_Text(d); /* some routines rely on the global `activeText_` pointer */ | ||
500 | deinitFonts_Text_(d); | 505 | deinitFonts_Text_(d); |
501 | deinitCache_Text_(d); | 506 | deinitCache_Text_(d); |
502 | initCache_Text_(d); | 507 | initCache_Text_(d); |
503 | initFonts_Text_(d); | 508 | initFonts_Text_(d); |
509 | setCurrent_Text(oldActive); | ||
504 | } | 510 | } |
505 | 511 | ||
506 | static SDL_Palette *glyphPalette_(void) { | 512 | static SDL_Palette *glyphPalette_(void) { |
@@ -610,8 +616,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
610 | } | 616 | } |
611 | } | 617 | } |
612 | if (!*glyphIndex) { | 618 | if (!*glyphIndex) { |
613 | activeText_->missingGlyphs = iTrue; | 619 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int) ch); fflush(stderr); |
614 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 620 | iText *tx = activeText_; |
621 | tx->missingGlyphs = iTrue; | ||
622 | /* Remember a few of the latest missing characters. */ | ||
623 | iBool gotIt = iFalse; | ||
624 | for (size_t i = 0; i < iElemCount(tx->missingChars); i++) { | ||
625 | if (tx->missingChars[i] == ch) { | ||
626 | gotIt = iTrue; | ||
627 | break; | ||
628 | } | ||
629 | } | ||
630 | if (!gotIt) { | ||
631 | memmove(tx->missingChars + 1, | ||
632 | tx->missingChars, | ||
633 | sizeof(tx->missingChars) - sizeof(tx->missingChars[0])); | ||
634 | tx->missingChars[0] = ch; | ||
635 | } | ||
615 | } | 636 | } |
616 | return d; | 637 | return d; |
617 | } | 638 | } |
@@ -1459,14 +1480,14 @@ static void evenMonospaceAdvances_GlyphBuffer_(iGlyphBuffer *d, iFont *baseFont) | |||
1459 | } | 1480 | } |
1460 | 1481 | ||
1461 | static iRect run_Font_(iFont *d, const iRunArgs *args) { | 1482 | static iRect run_Font_(iFont *d, const iRunArgs *args) { |
1462 | const int mode = args->mode; | 1483 | const int mode = args->mode; |
1463 | const iInt2 orig = args->pos; | 1484 | const iInt2 orig = args->pos; |
1464 | iRect bounds = { orig, init_I2(0, d->height) }; | 1485 | iRect bounds = { orig, init_I2(0, d->height) }; |
1465 | float xCursor = 0.0f; | 1486 | float xCursor = 0.0f; |
1466 | float yCursor = 0.0f; | 1487 | float yCursor = 0.0f; |
1467 | float xCursorMax = 0.0f; | 1488 | float xCursorMax = 0.0f; |
1468 | const iBool isMonospaced = isMonospaced_Font(d); | 1489 | const iBool isMonospaced = isMonospaced_Font(d); |
1469 | iWrapText *wrap = args->wrap; | 1490 | iWrapText *wrap = args->wrap; |
1470 | iAssert(args->text.end >= args->text.start); | 1491 | iAssert(args->text.end >= args->text.start); |
1471 | /* Split the text into a number of attributed runs that specify exactly which | 1492 | /* Split the text into a number of attributed runs that specify exactly which |
1472 | font is used and other attributes such as color. (HarfBuzz shaping is done | 1493 | font is used and other attributes such as color. (HarfBuzz shaping is done |
@@ -2250,6 +2271,19 @@ iBool checkMissing_Text(void) { | |||
2250 | return missing; | 2271 | return missing; |
2251 | } | 2272 | } |
2252 | 2273 | ||
2274 | iChar missing_Text(size_t index) { | ||
2275 | const iText *d = activeText_; | ||
2276 | if (index >= iElemCount(d->missingChars)) { | ||
2277 | return 0; | ||
2278 | } | ||
2279 | return d->missingChars[index]; | ||
2280 | } | ||
2281 | |||
2282 | void resetMissing_Text(iText *d) { | ||
2283 | d->missingGlyphs = iFalse; | ||
2284 | iZap(d->missingChars); | ||
2285 | } | ||
2286 | |||
2253 | SDL_Texture *glyphCache_Text(void) { | 2287 | SDL_Texture *glyphCache_Text(void) { |
2254 | return activeText_->cache; | 2288 | return activeText_->cache; |
2255 | } | 2289 | } |
diff --git a/src/ui/text.h b/src/ui/text.h index b952df84..e741880d 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -227,6 +227,8 @@ struct Impl_WrapText { | |||
227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); | 227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); |
228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); | 228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); |
229 | 229 | ||
230 | iChar missing_Text (size_t index); | ||
231 | void resetMissing_Text (iText *); | ||
230 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ | 232 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ |
231 | SDL_Texture * glyphCache_Text (void); | 233 | SDL_Texture * glyphCache_Text (void); |
232 | 234 | ||
diff --git a/src/ui/touch.c b/src/ui/touch.c index a178a913..21a92b80 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -244,7 +244,8 @@ static void dispatchNotification_Touch_(const iTouch *d, int code) { | |||
244 | .timestamp = SDL_GetTicks(), | 244 | .timestamp = SDL_GetTicks(), |
245 | .code = code, | 245 | .code = code, |
246 | .data1 = d->affinity, | 246 | .data1 = d->affinity, |
247 | .data2 = d->affinity->root | 247 | .data2 = d->affinity->root, |
248 | .windowID = id_Window(window_Widget(d->affinity)), | ||
248 | }); | 249 | }); |
249 | setCurrent_Root(oldRoot); | 250 | setCurrent_Root(oldRoot); |
250 | } | 251 | } |
diff --git a/src/ui/util.c b/src/ui/util.c index 5dd8a0bd..4f5de7f9 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
35 | #include "keys.h" | 35 | #include "keys.h" |
36 | #include "labelwidget.h" | 36 | #include "labelwidget.h" |
37 | #include "root.h" | 37 | #include "root.h" |
38 | #include "sitespec.h" | ||
38 | #include "text.h" | 39 | #include "text.h" |
39 | #include "touch.h" | 40 | #include "touch.h" |
40 | #include "widget.h" | 41 | #include "widget.h" |
@@ -903,6 +904,7 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
903 | #else | 904 | #else |
904 | /* Non-native custom popup menu. This may still be displayed inside a separate window. */ | 905 | /* Non-native custom popup menu. This may still be displayed inside a separate window. */ |
905 | setDrawBufferEnabled_Widget(menu, iTrue); | 906 | setDrawBufferEnabled_Widget(menu, iTrue); |
907 | setFrameColor_Widget(menu, uiSeparator_ColorId); | ||
906 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); | 908 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); |
907 | if (deviceType_App() != desktop_AppDeviceType) { | 909 | if (deviceType_App() != desktop_AppDeviceType) { |
908 | setPadding1_Widget(menu, 2 * gap_UI); | 910 | setPadding1_Widget(menu, 2 * gap_UI); |
@@ -1084,12 +1086,12 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) { | |||
1084 | setFlags_Widget(d, hidden_WidgetFlag, iFalse); | 1086 | setFlags_Widget(d, hidden_WidgetFlag, iFalse); |
1085 | setFlags_Widget(d, commandOnMouseMiss_WidgetFlag, iTrue); | 1087 | setFlags_Widget(d, commandOnMouseMiss_WidgetFlag, iTrue); |
1086 | setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iFalse); | 1088 | setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iFalse); |
1087 | if (!isPortraitPhone) { | 1089 | // if (!isPortraitPhone) { |
1088 | setFrameColor_Widget(d, uiBackgroundSelected_ColorId); | 1090 | // setFrameColor_Widget(d, uiSeparator_ColorId); |
1089 | } | 1091 | // } |
1090 | else { | 1092 | // else { |
1091 | setFrameColor_Widget(d, none_ColorId); | 1093 | // setFrameColor_Widget(d, none_ColorId); |
1092 | } | 1094 | // } |
1093 | arrange_Widget(d); /* need to know the height */ | 1095 | arrange_Widget(d); /* need to know the height */ |
1094 | iBool allowOverflow = iFalse; | 1096 | iBool allowOverflow = iFalse; |
1095 | /* A vertical offset determined by a possible selected label in the menu. */ | 1097 | /* A vertical offset determined by a possible selected label in the menu. */ |
@@ -1327,6 +1329,7 @@ int checkContextMenu_Widget(iWidget *menu, const SDL_Event *ev) { | |||
1327 | iLabelWidget *makeMenuButton_LabelWidget(const char *label, const iMenuItem *items, size_t n) { | 1329 | iLabelWidget *makeMenuButton_LabelWidget(const char *label, const iMenuItem *items, size_t n) { |
1328 | iLabelWidget *button = new_LabelWidget(label, "menu.open"); | 1330 | iLabelWidget *button = new_LabelWidget(label, "menu.open"); |
1329 | iWidget *menu = makeMenu_Widget(as_Widget(button), items, n); | 1331 | iWidget *menu = makeMenu_Widget(as_Widget(button), items, n); |
1332 | setFrameColor_Widget(menu, uiBackgroundSelected_ColorId); | ||
1330 | setId_Widget(menu, "menu"); | 1333 | setId_Widget(menu, "menu"); |
1331 | return button; | 1334 | return button; |
1332 | } | 1335 | } |
@@ -1383,6 +1386,9 @@ void updateDropdownSelection_LabelWidget(iLabelWidget *dropButton, const char *s | |||
1383 | updateText_LabelWidget(dropButton, | 1386 | updateText_LabelWidget(dropButton, |
1384 | replaceNewlinesWithDash_(text_LabelWidget(item))); | 1387 | replaceNewlinesWithDash_(text_LabelWidget(item))); |
1385 | checkIcon_LabelWidget(dropButton); | 1388 | checkIcon_LabelWidget(dropButton); |
1389 | if (!icon_LabelWidget(dropButton)) { | ||
1390 | setIcon_LabelWidget(dropButton, icon_LabelWidget(item)); | ||
1391 | } | ||
1386 | } | 1392 | } |
1387 | } | 1393 | } |
1388 | } | 1394 | } |
@@ -1709,13 +1715,14 @@ iLabelWidget *addDialogTitle_Widget(iWidget *dlg, const char *text, const char * | |||
1709 | } | 1715 | } |
1710 | 1716 | ||
1711 | static void acceptValueInput_(iWidget *dlg) { | 1717 | static void acceptValueInput_(iWidget *dlg) { |
1712 | const iInputWidget *input = findChild_Widget(dlg, "input"); | 1718 | iInputWidget *input = findChild_Widget(dlg, "input"); |
1713 | if (!isEmpty_String(id_Widget(dlg))) { | 1719 | if (!isEmpty_String(id_Widget(dlg))) { |
1714 | const iString *val = text_InputWidget(input); | 1720 | const iString *val = text_InputWidget(input); |
1715 | postCommandf_App("%s arg:%d value:%s", | 1721 | postCommandf_App("%s arg:%d value:%s", |
1716 | cstr_String(id_Widget(dlg)), | 1722 | cstr_String(id_Widget(dlg)), |
1717 | toInt_String(val), | 1723 | toInt_String(val), |
1718 | cstr_String(val)); | 1724 | cstr_String(val)); |
1725 | setBackupFileName_InputWidget(input, NULL); | ||
1719 | } | 1726 | } |
1720 | } | 1727 | } |
1721 | 1728 | ||
@@ -1779,6 +1786,7 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) { | |||
1779 | else if (equal_Command(cmd, "valueinput.set")) { | 1786 | else if (equal_Command(cmd, "valueinput.set")) { |
1780 | iInputWidget *input = findChild_Widget(dlg, "input"); | 1787 | iInputWidget *input = findChild_Widget(dlg, "input"); |
1781 | setTextUndoableCStr_InputWidget(input, suffixPtr_Command(cmd, "text"), iTrue); | 1788 | setTextUndoableCStr_InputWidget(input, suffixPtr_Command(cmd, "text"), iTrue); |
1789 | deselect_InputWidget(input); | ||
1782 | validate_InputWidget(input); | 1790 | validate_InputWidget(input); |
1783 | return iTrue; | 1791 | return iTrue; |
1784 | } | 1792 | } |
@@ -2495,6 +2503,7 @@ iWidget *makePreferences_Widget(void) { | |||
2495 | { "input id:prefs.searchurl url:1 noheading:1" }, | 2503 | { "input id:prefs.searchurl url:1 noheading:1" }, |
2496 | { "padding" }, | 2504 | { "padding" }, |
2497 | { "toggle id:prefs.bookmarks.addbottom" }, | 2505 | { "toggle id:prefs.bookmarks.addbottom" }, |
2506 | { "toggle id:prefs.dataurl.openimages" }, | ||
2498 | { "toggle id:prefs.archive.openindex" }, | 2507 | { "toggle id:prefs.archive.openindex" }, |
2499 | { "radio device:1 id:prefs.pinsplit", 0, 0, (const void *) pinSplitItems }, | 2508 | { "radio device:1 id:prefs.pinsplit", 0, 0, (const void *) pinSplitItems }, |
2500 | { "padding" }, | 2509 | { "padding" }, |
@@ -2567,6 +2576,7 @@ iWidget *makePreferences_Widget(void) { | |||
2567 | const iMenuItem networkPanelItems[] = { | 2576 | const iMenuItem networkPanelItems[] = { |
2568 | { "title id:heading.prefs.network" }, | 2577 | { "title id:heading.prefs.network" }, |
2569 | { "toggle id:prefs.decodeurls" }, | 2578 | { "toggle id:prefs.decodeurls" }, |
2579 | { "input id:prefs.urlsize maxlen:10 selectall:1" }, | ||
2570 | { "padding" }, | 2580 | { "padding" }, |
2571 | { "input id:prefs.cachesize maxlen:4 selectall:1 unit:mb" }, | 2581 | { "input id:prefs.cachesize maxlen:4 selectall:1 unit:mb" }, |
2572 | { "input id:prefs.memorysize maxlen:4 selectall:1 unit:mb" }, | 2582 | { "input id:prefs.memorysize maxlen:4 selectall:1 unit:mb" }, |
@@ -2640,8 +2650,9 @@ iWidget *makePreferences_Widget(void) { | |||
2640 | setUrlContent_InputWidget(searchUrl, iTrue); | 2650 | setUrlContent_InputWidget(searchUrl, iTrue); |
2641 | addDialogPadding_(headings, values); | 2651 | addDialogPadding_(headings, values); |
2642 | addDialogToggle_(headings, values, "${prefs.hoverlink}", "prefs.hoverlink"); | 2652 | addDialogToggle_(headings, values, "${prefs.hoverlink}", "prefs.hoverlink"); |
2643 | addDialogToggle_(headings, values, "${prefs.bookmarks.addbottom}", "prefs.bookmarks.addbottom"); | 2653 | addDialogToggle_(headings, values, "${prefs.dataurl.openimages}", "prefs.dataurl.openimages"); |
2644 | addDialogToggle_(headings, values, "${prefs.archive.openindex}", "prefs.archive.openindex"); | 2654 | addDialogToggle_(headings, values, "${prefs.archive.openindex}", "prefs.archive.openindex"); |
2655 | addDialogToggle_(headings, values, "${prefs.bookmarks.addbottom}", "prefs.bookmarks.addbottom"); | ||
2645 | if (deviceType_App() != phone_AppDeviceType) { | 2656 | if (deviceType_App() != phone_AppDeviceType) { |
2646 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.pinsplit}"))); | 2657 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.pinsplit}"))); |
2647 | iWidget *pinSplit = new_Widget(); | 2658 | iWidget *pinSplit = new_Widget(); |
@@ -2900,6 +2911,7 @@ iWidget *makePreferences_Widget(void) { | |||
2900 | appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.network}", '6', &headings, &values); | 2911 | appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.network}", '6', &headings, &values); |
2901 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); | 2912 | addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); |
2902 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); | 2913 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); |
2914 | addPrefsInputWithHeading_(headings, values, "prefs.urlsize", iClob(new_InputWidget(10))); | ||
2903 | /* Cache size. */ { | 2915 | /* Cache size. */ { |
2904 | iInputWidget *cache = new_InputWidget(4); | 2916 | iInputWidget *cache = new_InputWidget(4); |
2905 | setSelectAllOnFocus_InputWidget(cache, iTrue); | 2917 | setSelectAllOnFocus_InputWidget(cache, iTrue); |
@@ -3120,7 +3132,7 @@ iWidget *makeBookmarkCreation_Widget(const iString *url, const iString *title, i | |||
3120 | 3132 | ||
3121 | static iBool handleFeedSettingCommands_(iWidget *dlg, const char *cmd) { | 3133 | static iBool handleFeedSettingCommands_(iWidget *dlg, const char *cmd) { |
3122 | if (equal_Command(cmd, "cancel")) { | 3134 | if (equal_Command(cmd, "cancel")) { |
3123 | setupSheetTransition_Mobile(dlg, iFalse); | 3135 | setupSheetTransition_Mobile(dlg, 0); |
3124 | destroy_Widget(dlg); | 3136 | destroy_Widget(dlg); |
3125 | return iTrue; | 3137 | return iTrue; |
3126 | } | 3138 | } |
@@ -3163,15 +3175,14 @@ static iBool handleFeedSettingCommands_(iWidget *dlg, const char *cmd) { | |||
3163 | } | 3175 | } |
3164 | 3176 | ||
3165 | iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) { | 3177 | iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) { |
3166 | const char *headingText = bookmarkId ? uiHeading_ColorEscape "${heading.feedcfg}" | 3178 | iWidget *dlg; |
3167 | : uiHeading_ColorEscape "${heading.subscribe}"; | 3179 | const char *headingText = bookmarkId ? "${heading.feedcfg}" : "${heading.subscribe}"; |
3168 | const iMenuItem actions[] = { { "${cancel}" }, | 3180 | const iMenuItem actions[] = { { "${cancel}" }, |
3169 | { bookmarkId ? uiTextCaution_ColorEscape "${dlg.feed.save}" | 3181 | { bookmarkId ? uiTextCaution_ColorEscape "${dlg.feed.save}" |
3170 | : uiTextCaution_ColorEscape "${dlg.feed.sub}", | 3182 | : uiTextCaution_ColorEscape "${dlg.feed.sub}", |
3171 | SDLK_RETURN, | 3183 | SDLK_RETURN, |
3172 | KMOD_PRIMARY, | 3184 | KMOD_PRIMARY, |
3173 | format_CStr("feedcfg.accept bmid:%d", bookmarkId) } }; | 3185 | format_CStr("feedcfg.accept bmid:%d", bookmarkId) } }; |
3174 | iWidget *dlg; | ||
3175 | if (isUsingPanelLayout_Mobile()) { | 3186 | if (isUsingPanelLayout_Mobile()) { |
3176 | const iMenuItem typeItems[] = { | 3187 | const iMenuItem typeItems[] = { |
3177 | { "button id:feedcfg.type.gemini label:dlg.feed.type.gemini", 0, 0, "feedcfg.type arg:0" }, | 3188 | { "button id:feedcfg.type.gemini label:dlg.feed.type.gemini", 0, 0, "feedcfg.type arg:0" }, |
@@ -3228,6 +3239,111 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) { | |||
3228 | return dlg; | 3239 | return dlg; |
3229 | } | 3240 | } |
3230 | 3241 | ||
3242 | /*----------------------------------------------------------------------------------------------*/ | ||
3243 | |||
3244 | static void siteSpecificThemeChanged_(const iWidget *dlg) { | ||
3245 | iDocumentWidget *doc = document_App(); | ||
3246 | setThemeSeed_GmDocument((iGmDocument *) document_DocumentWidget(doc), | ||
3247 | urlPaletteSeed_String(url_DocumentWidget(doc)), | ||
3248 | urlThemeSeed_String(url_DocumentWidget(doc))); | ||
3249 | postCommand_App("theme.changed"); | ||
3250 | } | ||
3251 | |||
3252 | static const iString *siteSpecificRoot_(const iWidget *dlg) { | ||
3253 | return collect_String(suffix_Command(cstr_String(id_Widget(dlg)), "site")); | ||
3254 | } | ||
3255 | |||
3256 | static void updateSiteSpecificTheme_(iInputWidget *palSeed, void *context) { | ||
3257 | iWidget *dlg = context; | ||
3258 | const iString *siteRoot = siteSpecificRoot_(dlg); | ||
3259 | setValueString_SiteSpec(siteRoot, paletteSeed_SiteSpecKey, text_InputWidget(palSeed)); | ||
3260 | siteSpecificThemeChanged_(dlg); | ||
3261 | /* Allow seeing the new theme. */ | ||
3262 | setFlags_Widget(dlg, noFadeBackground_WidgetFlag, iTrue); | ||
3263 | } | ||
3264 | |||
3265 | static void closeSiteSpecific_(iWidget *dlg) { | ||
3266 | setupSheetTransition_Mobile(dlg, 0); | ||
3267 | delete_String(userData_Object(dlg)); /* saved original palette seed */ | ||
3268 | destroy_Widget(dlg); | ||
3269 | } | ||
3270 | |||
3271 | static iBool siteSpecificSettingsHandler_(iWidget *dlg, const char *cmd) { | ||
3272 | if (equal_Command(cmd, "cancel")) { | ||
3273 | const iBool wasNoFade = (flags_Widget(dlg) & noFadeBackground_WidgetFlag) != 0; | ||
3274 | iInputWidget *palSeed = findChild_Widget(dlg, "sitespec.palette"); | ||
3275 | setText_InputWidget(palSeed, userData_Object(dlg)); | ||
3276 | updateSiteSpecificTheme_(palSeed, dlg); | ||
3277 | setFlags_Widget(dlg, noFadeBackground_WidgetFlag, wasNoFade); | ||
3278 | closeSiteSpecific_(dlg); | ||
3279 | return iTrue; | ||
3280 | } | ||
3281 | if (startsWith_CStr(cmd, "input.ended id:sitespec.palette")) { | ||
3282 | setFlags_Widget(dlg, noFadeBackground_WidgetFlag, iFalse); | ||
3283 | refresh_Widget(dlg); | ||
3284 | siteSpecificThemeChanged_(dlg); | ||
3285 | return iTrue; | ||
3286 | } | ||
3287 | if (equal_Command(cmd, "sitespec.accept")) { | ||
3288 | const iInputWidget *palSeed = findChild_Widget(dlg, "sitespec.palette"); | ||
3289 | const iBool warnAnsi = isSelected_Widget(findChild_Widget(dlg, "sitespec.ansi")); | ||
3290 | const iString *siteRoot = siteSpecificRoot_(dlg); | ||
3291 | int dismissed = value_SiteSpec(siteRoot, dismissWarnings_SiteSpecKey); | ||
3292 | iChangeFlags(dismissed, ansiEscapes_GmDocumentWarning, !warnAnsi); | ||
3293 | setValue_SiteSpec(siteRoot, dismissWarnings_SiteSpecKey, dismissed); | ||
3294 | setValueString_SiteSpec(siteRoot, paletteSeed_SiteSpecKey, text_InputWidget(palSeed)); | ||
3295 | siteSpecificThemeChanged_(dlg); | ||
3296 | /* Note: The active DocumentWidget may actually be different than when opening the dialog. */ | ||
3297 | closeSiteSpecific_(dlg); | ||
3298 | return iTrue; | ||
3299 | } | ||
3300 | return iFalse; | ||
3301 | } | ||
3302 | |||
3303 | iWidget *makeSiteSpecificSettings_Widget(const iString *url) { | ||
3304 | iWidget *dlg; | ||
3305 | const iMenuItem actions[] = { | ||
3306 | { "${cancel}" }, | ||
3307 | { "${sitespec.accept}", SDLK_RETURN, KMOD_PRIMARY, "sitespec.accept" } | ||
3308 | }; | ||
3309 | if (isUsingPanelLayout_Mobile()) { | ||
3310 | iAssert(iFalse); | ||
3311 | } | ||
3312 | else { | ||
3313 | iWidget *headings, *values; | ||
3314 | dlg = makeSheet_Widget(format_CStr("sitespec site:%s", cstr_Rangecc(urlRoot_String(url)))); | ||
3315 | addDialogTitle_(dlg, "${heading.sitespec}", "heading.sitespec"); | ||
3316 | addChild_Widget(dlg, iClob(makeTwoColumns_Widget(&headings, &values))); | ||
3317 | iInputWidget *palSeed = new_InputWidget(0); | ||
3318 | setHint_InputWidget(palSeed, cstr_Block(urlThemeSeed_String(url))); | ||
3319 | addPrefsInputWithHeading_(headings, values, "sitespec.palette", iClob(palSeed)); | ||
3320 | addDialogToggle_(headings, values, "${sitespec.ansi}", "sitespec.ansi"); | ||
3321 | addChild_Widget(dlg, iClob(makeDialogButtons_Widget(actions, iElemCount(actions)))); | ||
3322 | addChild_Widget(get_Root()->widget, iClob(dlg)); | ||
3323 | as_Widget(palSeed)->rect.size.x = 60 * gap_UI; | ||
3324 | arrange_Widget(dlg); | ||
3325 | } | ||
3326 | /* Initialize. */ { | ||
3327 | const iString *site = collectNewRange_String(urlRoot_String(url)); | ||
3328 | setToggle_Widget(findChild_Widget(dlg, "sitespec.ansi"), | ||
3329 | ~value_SiteSpec(site, dismissWarnings_SiteSpecKey) & ansiEscapes_GmDocumentWarning); | ||
3330 | setText_InputWidget(findChild_Widget(dlg, "sitespec.palette"), | ||
3331 | valueString_SiteSpec(site, paletteSeed_SiteSpecKey)); | ||
3332 | /* Keep a copy of the original palette seed for restoring on cancel. */ | ||
3333 | setUserData_Object(dlg, copy_String(valueString_SiteSpec(site, paletteSeed_SiteSpecKey))); | ||
3334 | if (!isUsingPanelLayout_Mobile()) { | ||
3335 | setValidator_InputWidget(findChild_Widget(dlg, "sitespec.palette"), | ||
3336 | updateSiteSpecificTheme_, dlg); | ||
3337 | } | ||
3338 | } | ||
3339 | setCommandHandler_Widget(dlg, siteSpecificSettingsHandler_); | ||
3340 | setupSheetTransition_Mobile(dlg, incoming_TransitionFlag); | ||
3341 | setFocus_Widget(findChild_Widget(dlg, "sitespec.palette")); | ||
3342 | return dlg; | ||
3343 | } | ||
3344 | |||
3345 | /*----------------------------------------------------------------------------------------------*/ | ||
3346 | |||
3231 | iWidget *makeIdentityCreation_Widget(void) { | 3347 | iWidget *makeIdentityCreation_Widget(void) { |
3232 | const iMenuItem actions[] = { { "${dlg.newident.more}", 0, 0, "ident.showmore" }, | 3348 | const iMenuItem actions[] = { { "${dlg.newident.more}", 0, 0, "ident.showmore" }, |
3233 | { "---" }, | 3349 | { "---" }, |
@@ -3451,6 +3567,54 @@ iWidget *makeTranslation_Widget(iWidget *parent) { | |||
3451 | return dlg; | 3567 | return dlg; |
3452 | } | 3568 | } |
3453 | 3569 | ||
3570 | iWidget *makeGlyphFinder_Widget(void) { | ||
3571 | iString msg; | ||
3572 | iString command; | ||
3573 | init_String(&msg); | ||
3574 | initCStr_String(&command, "!font.find chars:"); | ||
3575 | for (size_t i = 0; ; i++) { | ||
3576 | iChar ch = missing_Text(i); | ||
3577 | if (!ch) break; | ||
3578 | appendFormat_String(&msg, " U+%04X", ch); | ||
3579 | appendChar_String(&command, ch); | ||
3580 | } | ||
3581 | iArray items; | ||
3582 | init_Array(&items, sizeof(iMenuItem)); | ||
3583 | if (!isEmpty_String(&msg)) { | ||
3584 | prependCStr_String(&msg, "${dlg.glyphfinder.missing} "); | ||
3585 | appendCStr_String(&msg, "\n\n${dlg.glyphfinder.help}"); | ||
3586 | pushBackN_Array( | ||
3587 | &items, | ||
3588 | (iMenuItem[]){ | ||
3589 | { "${menu.fonts}", 0, 0, "!open newtab:1 url:about:fonts" }, | ||
3590 | { "${dlg.glyphfinder.disable}", 0, 0, "prefs.font.warnmissing.changed arg:0" }, | ||
3591 | { "---" }, | ||
3592 | { uiTextCaution_ColorEscape magnifyingGlass_Icon " ${dlg.glyphfinder.search}", | ||
3593 | 0, | ||
3594 | 0, | ||
3595 | cstr_String(&command) }, | ||
3596 | { "${close}", 0, 0, "cancel" } }, | ||
3597 | 5); | ||
3598 | } | ||
3599 | else { | ||
3600 | setCStr_String(&msg, "${dlg.glyphfinder.help.empty}"); | ||
3601 | pushBackN_Array(&items, | ||
3602 | (iMenuItem[]){ { "${menu.reload}", 0, 0, "navigate.reload" }, | ||
3603 | { "${close}", 0, 0, "cancel" } }, | ||
3604 | 2); | ||
3605 | } | ||
3606 | iWidget *dlg = makeQuestion_Widget("${heading.glyphfinder}", cstr_String(&msg), | ||
3607 | constData_Array(&items), | ||
3608 | size_Array(&items)); | ||
3609 | arrange_Widget(dlg); | ||
3610 | deinit_Array(&items); | ||
3611 | deinit_String(&command); | ||
3612 | deinit_String(&msg); | ||
3613 | return dlg; | ||
3614 | } | ||
3615 | |||
3616 | /*----------------------------------------------------------------------------------------------*/ | ||
3617 | |||
3454 | void init_PerfTimer(iPerfTimer *d) { | 3618 | void init_PerfTimer(iPerfTimer *d) { |
3455 | d->ticks = SDL_GetPerformanceCounter(); | 3619 | d->ticks = SDL_GetPerformanceCounter(); |
3456 | } | 3620 | } |
diff --git a/src/ui/util.h b/src/ui/util.h index 98ce784c..31c8cedc 100644 --- a/src/ui/util.h +++ b/src/ui/util.h | |||
@@ -336,12 +336,14 @@ iWidget * makeQuestion_Widget (const char *title, const char *msg, | |||
336 | iWidget * makePreferences_Widget (void); | 336 | iWidget * makePreferences_Widget (void); |
337 | void updatePreferencesLayout_Widget (iWidget *prefs); | 337 | void updatePreferencesLayout_Widget (iWidget *prefs); |
338 | 338 | ||
339 | iWidget * makeBookmarkEditor_Widget (void); | 339 | iWidget * makeBookmarkEditor_Widget (void); |
340 | void setBookmarkEditorFolder_Widget(iWidget *editor, uint32_t folderId); | 340 | void setBookmarkEditorFolder_Widget (iWidget *editor, uint32_t folderId); |
341 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); | 341 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); |
342 | iWidget * makeIdentityCreation_Widget (void); | 342 | iWidget * makeIdentityCreation_Widget (void); |
343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); | 343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); |
344 | iWidget * makeTranslation_Widget (iWidget *parent); | 344 | iWidget * makeSiteSpecificSettings_Widget (const iString *url); |
345 | iWidget * makeTranslation_Widget (iWidget *parent); | ||
346 | iWidget * makeGlyphFinder_Widget (void); | ||
345 | 347 | ||
346 | const char * languageId_String (const iString *menuItemLabel); | 348 | const char * languageId_String (const iString *menuItemLabel); |
347 | int languageIndex_CStr (const char *langId); | 349 | int languageIndex_CStr (const char *langId); |
diff --git a/src/ui/widget.c b/src/ui/widget.c index fc754b7a..2e878878 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -168,7 +168,8 @@ void deinit_Widget(iWidget *d) { | |||
168 | if (d->flags & visualOffset_WidgetFlag) { | 168 | if (d->flags & visualOffset_WidgetFlag) { |
169 | removeTicker_App(visualOffsetAnimation_Widget_, d); | 169 | removeTicker_App(visualOffsetAnimation_Widget_, d); |
170 | } | 170 | } |
171 | iWindow *win = get_Window(); | 171 | iWindow *win = d->root->window; |
172 | iAssert(win); | ||
172 | if (win->lastHover == d) { | 173 | if (win->lastHover == d) { |
173 | win->lastHover = NULL; | 174 | win->lastHover = NULL; |
174 | } | 175 | } |
diff --git a/src/ui/window.c b/src/ui/window.c index 47abf878..b0de0557 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -80,6 +80,7 @@ iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect) | |||
80 | #if defined (iHaveNativeMenus) | 80 | #if defined (iHaveNativeMenus) |
81 | /* Using native menus. */ | 81 | /* Using native menus. */ |
82 | static const iMenuItem fileMenuItems_[] = { | 82 | static const iMenuItem fileMenuItems_[] = { |
83 | { "${menu.newwindow}", SDLK_n, KMOD_PRIMARY, "window.new" }, | ||
83 | { "${menu.newtab}", SDLK_t, KMOD_PRIMARY, "tabs.new" }, | 84 | { "${menu.newtab}", SDLK_t, KMOD_PRIMARY, "tabs.new" }, |
84 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, | 85 | { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" }, |
85 | { "---", 0, 0, NULL }, | 86 | { "---", 0, 0, NULL }, |
@@ -210,7 +211,9 @@ static void windowSizeChanged_MainWindow_(iMainWindow *d) { | |||
210 | 211 | ||
211 | static void setupUserInterface_MainWindow(iMainWindow *d) { | 212 | static void setupUserInterface_MainWindow(iMainWindow *d) { |
212 | #if defined (iHaveNativeMenus) | 213 | #if defined (iHaveNativeMenus) |
213 | insertMacMenus_(); | 214 | if (numWindows_App() == 0) { |
215 | insertMacMenus_(); /* TODO: Shouldn't this be in the App? */ | ||
216 | } | ||
214 | #endif | 217 | #endif |
215 | /* One root is created by default. */ | 218 | /* One root is created by default. */ |
216 | d->base.roots[0] = new_Root(); | 219 | d->base.roots[0] = new_Root(); |
@@ -246,6 +249,7 @@ static void updateSize_MainWindow_(iMainWindow *d, iBool notifyAlways) { | |||
246 | 249 | ||
247 | void drawWhileResizing_MainWindow(iMainWindow *d, int w, int h) { | 250 | void drawWhileResizing_MainWindow(iMainWindow *d, int w, int h) { |
248 | if (!isDrawing_) { | 251 | if (!isDrawing_) { |
252 | setCurrent_Window(d); | ||
249 | draw_MainWindow(d); | 253 | draw_MainWindow(d); |
250 | } | 254 | } |
251 | } | 255 | } |
@@ -647,6 +651,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
647 | } | 651 | } |
648 | 652 | ||
649 | void deinit_MainWindow(iMainWindow *d) { | 653 | void deinit_MainWindow(iMainWindow *d) { |
654 | removeWindow_App(d); | ||
650 | if (d->backBuf) { | 655 | if (d->backBuf) { |
651 | SDL_DestroyTexture(d->backBuf); | 656 | SDL_DestroyTexture(d->backBuf); |
652 | } | 657 | } |
@@ -677,6 +682,7 @@ iBool isFullscreen_MainWindow(const iMainWindow *d) { | |||
677 | } | 682 | } |
678 | 683 | ||
679 | iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { | 684 | iRoot *findRoot_Window(const iWindow *d, const iWidget *widget) { |
685 | |||
680 | while (widget->parent) { | 686 | while (widget->parent) { |
681 | widget = widget->parent; | 687 | widget = widget->parent; |
682 | } | 688 | } |
@@ -830,6 +836,9 @@ static void savePlace_MainWindow_(iAny *mainWindow) { | |||
830 | } | 836 | } |
831 | 837 | ||
832 | static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) { | 838 | static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) { |
839 | if (ev->windowID != SDL_GetWindowID(d->base.win)) { | ||
840 | return iFalse; | ||
841 | } | ||
833 | switch (ev->event) { | 842 | switch (ev->event) { |
834 | #if defined(iPlatformDesktop) | 843 | #if defined(iPlatformDesktop) |
835 | case SDL_WINDOWEVENT_EXPOSED: | 844 | case SDL_WINDOWEVENT_EXPOSED: |
@@ -857,7 +866,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
857 | if (d->base.isMinimized) { | 866 | if (d->base.isMinimized) { |
858 | return iFalse; | 867 | return iFalse; |
859 | } | 868 | } |
860 | closePopups_App(); | 869 | closePopups_App(iFalse); |
861 | checkPixelRatioChange_Window_(as_Window(d)); | 870 | checkPixelRatioChange_Window_(as_Window(d)); |
862 | const iInt2 newPos = init_I2(ev->data1, ev->data2); | 871 | const iInt2 newPos = init_I2(ev->data1, ev->data2); |
863 | if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ | 872 | if (isEqual_I2(newPos, init1_I2(-32000))) { /* magic! */ |
@@ -907,7 +916,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
907 | // updateSize_Window_(d, iTrue); | 916 | // updateSize_Window_(d, iTrue); |
908 | return iTrue; | 917 | return iTrue; |
909 | } | 918 | } |
910 | closePopups_App(); | 919 | closePopups_App(iFalse); |
911 | if (unsnap_MainWindow_(d, NULL)) { | 920 | if (unsnap_MainWindow_(d, NULL)) { |
912 | return iTrue; | 921 | return iTrue; |
913 | } | 922 | } |
@@ -929,7 +938,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
929 | return iTrue; | 938 | return iTrue; |
930 | case SDL_WINDOWEVENT_MINIMIZED: | 939 | case SDL_WINDOWEVENT_MINIMIZED: |
931 | d->base.isMinimized = iTrue; | 940 | d->base.isMinimized = iTrue; |
932 | closePopups_App(); | 941 | closePopups_App(iTrue); |
933 | return iTrue; | 942 | return iTrue; |
934 | #else /* if defined (!iPlatformDesktop) */ | 943 | #else /* if defined (!iPlatformDesktop) */ |
935 | case SDL_WINDOWEVENT_RESIZED: | 944 | case SDL_WINDOWEVENT_RESIZED: |
@@ -953,6 +962,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
953 | setCapsLockDown_Keys(iFalse); | 962 | setCapsLockDown_Keys(iFalse); |
954 | postCommand_App("window.focus.gained"); | 963 | postCommand_App("window.focus.gained"); |
955 | d->base.isExposed = iTrue; | 964 | d->base.isExposed = iTrue; |
965 | setActiveWindow_App(d); | ||
956 | #if !defined (iPlatformDesktop) | 966 | #if !defined (iPlatformDesktop) |
957 | /* Returned to foreground, may have lost buffered content. */ | 967 | /* Returned to foreground, may have lost buffered content. */ |
958 | invalidate_MainWindow_(d, iTrue); | 968 | invalidate_MainWindow_(d, iTrue); |
@@ -964,12 +974,17 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent | |||
964 | #if !defined (iPlatformDesktop) | 974 | #if !defined (iPlatformDesktop) |
965 | setFreezeDraw_MainWindow(d, iTrue); | 975 | setFreezeDraw_MainWindow(d, iTrue); |
966 | #endif | 976 | #endif |
967 | closePopups_App(); | 977 | closePopups_App(iTrue); |
968 | return iFalse; | 978 | return iFalse; |
969 | case SDL_WINDOWEVENT_TAKE_FOCUS: | 979 | case SDL_WINDOWEVENT_TAKE_FOCUS: |
970 | SDL_SetWindowInputFocus(d->base.win); | 980 | SDL_SetWindowInputFocus(d->base.win); |
971 | postRefresh_App(); | 981 | postRefresh_App(); |
972 | return iTrue; | 982 | return iTrue; |
983 | case SDL_WINDOWEVENT_CLOSE: | ||
984 | if (numWindows_App() > 1) { | ||
985 | closeWindow_App(d); | ||
986 | } | ||
987 | return iTrue; | ||
973 | default: | 988 | default: |
974 | break; | 989 | break; |
975 | } | 990 | } |
@@ -1009,7 +1024,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
1009 | } | 1024 | } |
1010 | } | 1025 | } |
1011 | case SDL_RENDER_TARGETS_RESET: | 1026 | case SDL_RENDER_TARGETS_RESET: |
1012 | case SDL_RENDER_DEVICE_RESET: { | 1027 | case SDL_RENDER_DEVICE_RESET: { |
1013 | if (mw) { | 1028 | if (mw) { |
1014 | invalidate_MainWindow_(mw, iTrue /* force full reset */); | 1029 | invalidate_MainWindow_(mw, iTrue /* force full reset */); |
1015 | } | 1030 | } |
@@ -1095,7 +1110,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
1095 | event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { | 1110 | event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { |
1096 | if (mouseGrab_Widget()) { | 1111 | if (mouseGrab_Widget()) { |
1097 | iWidget *grabbed = mouseGrab_Widget(); | 1112 | iWidget *grabbed = mouseGrab_Widget(); |
1098 | setCurrent_Root(findRoot_Window(d, grabbed)); | 1113 | setCurrent_Root(grabbed->root /* findRoot_Window(d, grabbed)*/); |
1099 | wasUsed = dispatchEvent_Widget(grabbed, &event); | 1114 | wasUsed = dispatchEvent_Widget(grabbed, &event); |
1100 | } | 1115 | } |
1101 | } | 1116 | } |
@@ -1164,7 +1179,33 @@ iLocalDef iBool isEscapeKeypress_(const SDL_Event *ev) { | |||
1164 | return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE; | 1179 | return (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && ev->key.keysym.sym == SDLK_ESCAPE; |
1165 | } | 1180 | } |
1166 | 1181 | ||
1182 | static uint32_t windowId_SDLEvent_(const SDL_Event *ev) { | ||
1183 | switch (ev->type) { | ||
1184 | case SDL_MOUSEBUTTONDOWN: | ||
1185 | case SDL_MOUSEBUTTONUP: | ||
1186 | return ev->button.windowID; | ||
1187 | case SDL_MOUSEMOTION: | ||
1188 | return ev->motion.windowID; | ||
1189 | case SDL_MOUSEWHEEL: | ||
1190 | return ev->wheel.windowID; | ||
1191 | case SDL_KEYDOWN: | ||
1192 | case SDL_KEYUP: | ||
1193 | return ev->key.windowID; | ||
1194 | case SDL_TEXTINPUT: | ||
1195 | return ev->text.windowID; | ||
1196 | case SDL_USEREVENT: | ||
1197 | return ev->user.windowID; | ||
1198 | default: | ||
1199 | return 0; | ||
1200 | } | ||
1201 | } | ||
1202 | |||
1167 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { | 1203 | iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) { |
1204 | /* For the right window? */ | ||
1205 | const uint32_t evWin = windowId_SDLEvent_(ev); | ||
1206 | if (evWin && evWin != id_Window(d)) { | ||
1207 | return iFalse; /* Meant for a different window. */ | ||
1208 | } | ||
1168 | if (ev->type == SDL_MOUSEMOTION) { | 1209 | if (ev->type == SDL_MOUSEMOTION) { |
1169 | /* Hover widget may change. */ | 1210 | /* Hover widget may change. */ |
1170 | setHover_Widget(NULL); | 1211 | setHover_Widget(NULL); |
@@ -1526,6 +1567,23 @@ void setKeyboardHeight_MainWindow(iMainWindow *d, int height) { | |||
1526 | } | 1567 | } |
1527 | } | 1568 | } |
1528 | 1569 | ||
1570 | iObjectList *listDocuments_MainWindow(iMainWindow *d, const iRoot *rootOrNull) { | ||
1571 | iObjectList *docs = new_ObjectList(); | ||
1572 | iForIndices(i, d->base.roots) { | ||
1573 | iRoot *root = d->base.roots[i]; | ||
1574 | if (!root) continue; | ||
1575 | if (!rootOrNull || root == rootOrNull) { | ||
1576 | const iWidget *tabs = findChild_Widget(root->widget, "doctabs"); | ||
1577 | iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) { | ||
1578 | if (isInstance_Object(i.object, &Class_DocumentWidget)) { | ||
1579 | pushBack_ObjectList(docs, i.object); | ||
1580 | } | ||
1581 | } | ||
1582 | } | ||
1583 | } | ||
1584 | return docs; | ||
1585 | } | ||
1586 | |||
1529 | void checkPendingSplit_MainWindow(iMainWindow *d) { | 1587 | void checkPendingSplit_MainWindow(iMainWindow *d) { |
1530 | if (d->splitMode != d->pendingSplitMode) { | 1588 | if (d->splitMode != d->pendingSplitMode) { |
1531 | setSplitMode_MainWindow(d, d->pendingSplitMode); | 1589 | setSplitMode_MainWindow(d, d->pendingSplitMode); |
@@ -1549,6 +1607,7 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) { | |||
1549 | } | 1607 | } |
1550 | iWindow *w = as_Window(d); | 1608 | iWindow *w = as_Window(d); |
1551 | iAssert(current_Root() == NULL); | 1609 | iAssert(current_Root() == NULL); |
1610 | setCurrent_Window(w); | ||
1552 | if (d->splitMode != splitMode) { | 1611 | if (d->splitMode != splitMode) { |
1553 | int oldCount = numRoots_Window(w); | 1612 | int oldCount = numRoots_Window(w); |
1554 | setFreezeDraw_MainWindow(d, iTrue); | 1613 | setFreezeDraw_MainWindow(d, iTrue); |
@@ -1577,8 +1636,8 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) { | |||
1577 | /* The last child is the [+] button for adding a tab. */ | 1636 | /* The last child is the [+] button for adding a tab. */ |
1578 | moveTabButtonToEnd_Widget(findChild_Widget(docTabs, "newtab")); | 1637 | moveTabButtonToEnd_Widget(findChild_Widget(docTabs, "newtab")); |
1579 | setFlags_Widget(findWidget_Root("navbar.unsplit"), hidden_WidgetFlag, iTrue); | 1638 | setFlags_Widget(findWidget_Root("navbar.unsplit"), hidden_WidgetFlag, iTrue); |
1580 | iRelease(tabs); | ||
1581 | postCommandf_App("tabs.switch id:%s", cstr_String(id_Widget(constAs_Widget(curPage)))); | 1639 | postCommandf_App("tabs.switch id:%s", cstr_String(id_Widget(constAs_Widget(curPage)))); |
1640 | iRelease(tabs); | ||
1582 | } | 1641 | } |
1583 | else if (oldCount == 1 && splitMode) { | 1642 | else if (oldCount == 1 && splitMode) { |
1584 | /* Add a second root. */ | 1643 | /* Add a second root. */ |
diff --git a/src/ui/window.h b/src/ui/window.h index 5abf23eb..c3c34e1b 100644 --- a/src/ui/window.h +++ b/src/ui/window.h | |||
@@ -139,7 +139,7 @@ iAnyObject * hitChild_Window (const iWindow *, iInt2 coord); | |||
139 | uint32_t frameTime_Window (const iWindow *); | 139 | uint32_t frameTime_Window (const iWindow *); |
140 | SDL_Renderer * renderer_Window (const iWindow *); | 140 | SDL_Renderer * renderer_Window (const iWindow *); |
141 | int numRoots_Window (const iWindow *); | 141 | int numRoots_Window (const iWindow *); |
142 | iRoot * findRoot_Window (const iWindow *, const iWidget *widget); | 142 | //iRoot * findRoot_Window (const iWindow *, const iWidget *widget); |
143 | iRoot * otherRoot_Window (const iWindow *, iRoot *root); | 143 | iRoot * otherRoot_Window (const iWindow *, iRoot *root); |
144 | 144 | ||
145 | iBool processEvent_Window (iWindow *, const SDL_Event *); | 145 | iBool processEvent_Window (iWindow *, const SDL_Event *); |
@@ -187,6 +187,7 @@ void setTitle_MainWindow (iMainWindow *, const iString *title | |||
187 | void setSnap_MainWindow (iMainWindow *, int snapMode); | 187 | void setSnap_MainWindow (iMainWindow *, int snapMode); |
188 | void setFreezeDraw_MainWindow (iMainWindow *, iBool freezeDraw); | 188 | void setFreezeDraw_MainWindow (iMainWindow *, iBool freezeDraw); |
189 | void setKeyboardHeight_MainWindow (iMainWindow *, int height); | 189 | void setKeyboardHeight_MainWindow (iMainWindow *, int height); |
190 | iObjectList *listDocuments_MainWindow (iMainWindow *, const iRoot *rootOrNull); | ||
190 | void setSplitMode_MainWindow (iMainWindow *, int splitMode); | 191 | void setSplitMode_MainWindow (iMainWindow *, int splitMode); |
191 | void checkPendingSplit_MainWindow (iMainWindow *); | 192 | void checkPendingSplit_MainWindow (iMainWindow *); |
192 | void swapRoots_MainWindow (iMainWindow *); | 193 | void swapRoots_MainWindow (iMainWindow *); |