diff options
-rw-r--r-- | res/about/version.gmi | 5 | ||||
-rw-r--r-- | src/app.c | 17 | ||||
-rw-r--r-- | src/app.h | 1 | ||||
-rw-r--r-- | src/gmutil.c | 15 | ||||
-rw-r--r-- | src/gmutil.h | 1 | ||||
-rw-r--r-- | src/prefs.c | 2 | ||||
-rw-r--r-- | src/prefs.h | 1 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 66 | ||||
-rw-r--r-- | src/ui/util.c | 15 | ||||
-rw-r--r-- | src/ui/window.c | 23 |
10 files changed, 115 insertions, 31 deletions
diff --git a/res/about/version.gmi b/res/about/version.gmi index dfdf2b97..ba94f7e2 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -8,11 +8,14 @@ | |||
8 | 8 | ||
9 | ## 1.2 | 9 | ## 1.2 |
10 | * Help is opened on first run instead of the "About Lagrange" page to make it easier to discover important Gemini links like the FAQ. | 10 | * Help is opened on first run instead of the "About Lagrange" page to make it easier to discover important Gemini links like the FAQ. |
11 | * Added search engine support: non-URL text entered in the navbar is passed onto the configured search query URL (Preferences > Network). | ||
11 | * Short pages are centered vertically on actual page content excluding the top banner. | 12 | * Short pages are centered vertically on actual page content excluding the top banner. |
13 | * Added user preference for aligning all pages to the top of the window. | ||
12 | * Shift+Insert can be used for pasting clipboard contents into input fields. | 14 | * Shift+Insert can be used for pasting clipboard contents into input fields. |
13 | * Added keybinding (F11) for toggling fullscreen mode. On macOS, the shortcut is ⌃⌘F as before. | 15 | * Added keybinding (F11) for toggling fullscreen mode. On macOS, the shortcut is ⌃⌘F as before. |
14 | * Added keybinding for finding text on page. | 16 | * Added keybinding for finding text on page. |
15 | * Windows: Added a custom window frame to replace the default Windows one. This looks nicer but does not behave exactly like a native window frame. Added a setting to Preferences > Window for switching back to the default frame. | 17 | * Scroll position remains fixed while horizontally resizing the window or sidebars. |
18 | * Windows: Added a custom window frame to replace the default Windows one. This looks nicer but does not behave exactly like a native window frame. Added a setting to Preferences for switching back to the default frame. | ||
16 | * Windows: Fixed a flash of white when the window is first opened. | 19 | * Windows: Fixed a flash of white when the window is first opened. |
17 | 20 | ||
18 | ## 1.1.3 | 21 | ## 1.1.3 |
@@ -227,6 +227,7 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
227 | appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->prefs.gopherProxy)); | 227 | appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->prefs.gopherProxy)); |
228 | appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->prefs.httpProxy)); | 228 | appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->prefs.httpProxy)); |
229 | appendFormat_String(str, "downloads path:%s\n", cstr_String(&d->prefs.downloadDir)); | 229 | appendFormat_String(str, "downloads path:%s\n", cstr_String(&d->prefs.downloadDir)); |
230 | appendFormat_String(str, "searchurl address:%s\n", cstr_String(&d->prefs.searchUrl)); | ||
230 | return str; | 231 | return str; |
231 | } | 232 | } |
232 | 233 | ||
@@ -937,6 +938,8 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
937 | isSelected_Widget(findChild_Widget(d, "prefs.ostheme"))); | 938 | isSelected_Widget(findChild_Widget(d, "prefs.ostheme"))); |
938 | postCommandf_App("decodeurls arg:%d", | 939 | postCommandf_App("decodeurls arg:%d", |
939 | isSelected_Widget(findChild_Widget(d, "prefs.decodeurls"))); | 940 | isSelected_Widget(findChild_Widget(d, "prefs.decodeurls"))); |
941 | postCommandf_App("searchurl address:%s", | ||
942 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.searchurl")))); | ||
940 | postCommandf_App("cachesize.set arg:%d", | 943 | postCommandf_App("cachesize.set arg:%d", |
941 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); | 944 | toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); |
942 | postCommandf_App("proxy.gemini address:%s", | 945 | postCommandf_App("proxy.gemini address:%s", |
@@ -1122,6 +1125,15 @@ iBool willUseProxy_App(const iRangecc scheme) { | |||
1122 | return schemeProxy_App(scheme) != NULL; | 1125 | return schemeProxy_App(scheme) != NULL; |
1123 | } | 1126 | } |
1124 | 1127 | ||
1128 | const iString *searchQueryUrl_App(const iString *queryStringUnescaped) { | ||
1129 | iApp *d = &app_; | ||
1130 | if (isEmpty_String(&d->prefs.searchUrl)) { | ||
1131 | return collectNew_String(); | ||
1132 | } | ||
1133 | const iString *escaped = urlEncode_String(queryStringUnescaped); | ||
1134 | return collectNewFormat_String("%s?%s", cstr_String(&d->prefs.searchUrl), cstr_String(escaped)); | ||
1135 | } | ||
1136 | |||
1125 | iBool handleCommand_App(const char *cmd) { | 1137 | iBool handleCommand_App(const char *cmd) { |
1126 | iApp *d = &app_; | 1138 | iApp *d = &app_; |
1127 | if (equal_Command(cmd, "config.error")) { | 1139 | if (equal_Command(cmd, "config.error")) { |
@@ -1292,6 +1304,10 @@ iBool handleCommand_App(const char *cmd) { | |||
1292 | } | 1304 | } |
1293 | return iTrue; | 1305 | return iTrue; |
1294 | } | 1306 | } |
1307 | else if (equal_Command(cmd, "searchurl")) { | ||
1308 | setCStr_String(&d->prefs.searchUrl, suffixPtr_Command(cmd, "address")); | ||
1309 | return iTrue; | ||
1310 | } | ||
1295 | else if (equal_Command(cmd, "proxy.gemini")) { | 1311 | else if (equal_Command(cmd, "proxy.gemini")) { |
1296 | setCStr_String(&d->prefs.geminiProxy, suffixPtr_Command(cmd, "address")); | 1312 | setCStr_String(&d->prefs.geminiProxy, suffixPtr_Command(cmd, "address")); |
1297 | return iTrue; | 1313 | return iTrue; |
@@ -1474,6 +1490,7 @@ iBool handleCommand_App(const char *cmd) { | |||
1474 | setText_InputWidget(findChild_Widget(dlg, "prefs.cachesize"), | 1490 | setText_InputWidget(findChild_Widget(dlg, "prefs.cachesize"), |
1475 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); | 1491 | collectNewFormat_String("%d", d->prefs.maxCacheSize)); |
1476 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); | 1492 | setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); |
1493 | setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.searchUrl); | ||
1477 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gemini"), &d->prefs.geminiProxy); | 1494 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gemini"), &d->prefs.geminiProxy); |
1478 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy); | 1495 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy); |
1479 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.httpProxy); | 1496 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.httpProxy); |
@@ -75,6 +75,7 @@ iBool forceSoftwareRender_App(void); | |||
75 | enum iColorTheme colorTheme_App (void); | 75 | enum iColorTheme colorTheme_App (void); |
76 | const iString * schemeProxy_App (iRangecc scheme); | 76 | const iString * schemeProxy_App (iRangecc scheme); |
77 | iBool willUseProxy_App (const iRangecc scheme); | 77 | iBool willUseProxy_App (const iRangecc scheme); |
78 | const iString * searchQueryUrl_App (const iString *queryStringUnescaped); | ||
78 | 79 | ||
79 | typedef void (*iTickerFunc)(iAny *); | 80 | typedef void (*iTickerFunc)(iAny *); |
80 | 81 | ||
diff --git a/src/gmutil.c b/src/gmutil.c index 0e0cccc5..32bf356f 100644 --- a/src/gmutil.c +++ b/src/gmutil.c | |||
@@ -272,6 +272,21 @@ const iString *absoluteUrl_String(const iString *d, const iString *urlMaybeRelat | |||
272 | return absolute; | 272 | return absolute; |
273 | } | 273 | } |
274 | 274 | ||
275 | iBool isLikelyUrl_String(const iString *d) { | ||
276 | /* Guess whether a human intends the string to be an URL. This is supposed to be fuzzy; | ||
277 | not completely per-spec: a) begins with a scheme; b) has something that looks like a | ||
278 | hostname */ | ||
279 | iRegExp *pattern = new_RegExp("^([a-z]+:)?//.*|" | ||
280 | "^(//)?([^/?#: ]+)([/?#:].*)$|" | ||
281 | "^(\\w+(\\.\\w+)+|localhost)$", | ||
282 | caseInsensitive_RegExpOption); | ||
283 | iRegExpMatch m; | ||
284 | init_RegExpMatch(&m); | ||
285 | const iBool likelyUrl = matchString_RegExp(pattern, d, &m); | ||
286 | iRelease(pattern); | ||
287 | return likelyUrl; | ||
288 | } | ||
289 | |||
275 | static iBool equalPuny_(const iString *d, iRangecc orig) { | 290 | static iBool equalPuny_(const iString *d, iRangecc orig) { |
276 | if (!endsWith_String(d, "-")) { | 291 | if (!endsWith_String(d, "-")) { |
277 | return iFalse; /* This is a sufficient condition? */ | 292 | return iFalse; /* This is a sufficient condition? */ |
diff --git a/src/gmutil.h b/src/gmutil.h index 1caf2445..26385c02 100644 --- a/src/gmutil.h +++ b/src/gmutil.h | |||
@@ -104,6 +104,7 @@ void init_Url (iUrl *, const iString *text); | |||
104 | iRangecc urlScheme_String (const iString *); | 104 | iRangecc urlScheme_String (const iString *); |
105 | iRangecc urlHost_String (const iString *); | 105 | iRangecc urlHost_String (const iString *); |
106 | const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative); | 106 | const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative); |
107 | iBool isLikelyUrl_String (const iString *); | ||
107 | void punyEncodeUrlHost_String(iString *); | 108 | void punyEncodeUrlHost_String(iString *); |
108 | void stripDefaultUrlPort_String(iString *); | 109 | void stripDefaultUrlPort_String(iString *); |
109 | const iString * urlFragmentStripped_String(const iString *); | 110 | const iString * urlFragmentStripped_String(const iString *); |
diff --git a/src/prefs.c b/src/prefs.c index a8f34b02..f9186ab3 100644 --- a/src/prefs.c +++ b/src/prefs.c | |||
@@ -51,9 +51,11 @@ void init_Prefs(iPrefs *d) { | |||
51 | init_String(&d->gopherProxy); | 51 | init_String(&d->gopherProxy); |
52 | init_String(&d->httpProxy); | 52 | init_String(&d->httpProxy); |
53 | init_String(&d->downloadDir); | 53 | init_String(&d->downloadDir); |
54 | initCStr_String(&d->searchUrl, "gemini://gus.guru/search"); | ||
54 | } | 55 | } |
55 | 56 | ||
56 | void deinit_Prefs(iPrefs *d) { | 57 | void deinit_Prefs(iPrefs *d) { |
58 | deinit_String(&d->searchUrl); | ||
57 | deinit_String(&d->geminiProxy); | 59 | deinit_String(&d->geminiProxy); |
58 | deinit_String(&d->gopherProxy); | 60 | deinit_String(&d->gopherProxy); |
59 | deinit_String(&d->httpProxy); | 61 | deinit_String(&d->httpProxy); |
diff --git a/src/prefs.h b/src/prefs.h index dcda695f..1889c338 100644 --- a/src/prefs.h +++ b/src/prefs.h | |||
@@ -48,6 +48,7 @@ struct Impl_Prefs { | |||
48 | iBool hoverLink; | 48 | iBool hoverLink; |
49 | iBool smoothScrolling; | 49 | iBool smoothScrolling; |
50 | iBool loadImageInsteadOfScrolling; | 50 | iBool loadImageInsteadOfScrolling; |
51 | iString searchUrl; | ||
51 | /* Network */ | 52 | /* Network */ |
52 | iBool decodeUserVisibleURLs; | 53 | iBool decodeUserVisibleURLs; |
53 | int maxCacheSize; /* MB */ | 54 | int maxCacheSize; /* MB */ |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 603af076..232b4140 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -133,6 +133,7 @@ enum iDocumentWidgetFlag { | |||
133 | showLinkNumbers_DocumentWidgetFlag = iBit(3), | 133 | showLinkNumbers_DocumentWidgetFlag = iBit(3), |
134 | setHoverViaKeys_DocumentWidgetFlag = iBit(4), | 134 | setHoverViaKeys_DocumentWidgetFlag = iBit(4), |
135 | newTabViaHomeKeys_DocumentWidgetFlag = iBit(5), | 135 | newTabViaHomeKeys_DocumentWidgetFlag = iBit(5), |
136 | centerVertically_DocumentWidgetFlag = iBit(6), | ||
136 | }; | 137 | }; |
137 | 138 | ||
138 | enum iDocumentLinkOrdinalMode { | 139 | enum iDocumentLinkOrdinalMode { |
@@ -151,6 +152,7 @@ struct Impl_DocumentWidget { | |||
151 | iGmRequest * request; | 152 | iGmRequest * request; |
152 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ | 153 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ |
153 | iObjectList * media; | 154 | iObjectList * media; |
155 | enum iGmStatusCode sourceStatus; | ||
154 | iString sourceHeader; | 156 | iString sourceHeader; |
155 | iString sourceMime; | 157 | iString sourceMime; |
156 | iBlock sourceContent; /* original content as received, for saving */ | 158 | iBlock sourceContent; /* original content as received, for saving */ |
@@ -231,6 +233,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
231 | init_Array(&d->outline, sizeof(iOutlineItem)); | 233 | init_Array(&d->outline, sizeof(iOutlineItem)); |
232 | init_Anim(&d->sideOpacity, 0); | 234 | init_Anim(&d->sideOpacity, 0); |
233 | init_Anim(&d->outlineOpacity, 0); | 235 | init_Anim(&d->outlineOpacity, 0); |
236 | d->sourceStatus = none_GmStatusCode; | ||
234 | init_String(&d->sourceHeader); | 237 | init_String(&d->sourceHeader); |
235 | init_String(&d->sourceMime); | 238 | init_String(&d->sourceMime); |
236 | init_Block(&d->sourceContent, 0); | 239 | init_Block(&d->sourceContent, 0); |
@@ -332,7 +335,7 @@ static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | |||
332 | rect.pos.y += margin; | 335 | rect.pos.y += margin; |
333 | rect.size.y -= margin; | 336 | rect.size.y -= margin; |
334 | } | 337 | } |
335 | if (prefs_App()->centerShortDocs) { | 338 | if (d->flags & centerVertically_DocumentWidgetFlag) { |
336 | const iInt2 docSize = size_GmDocument(d->doc); | 339 | const iInt2 docSize = size_GmDocument(d->doc); |
337 | if (docSize.y < rect.size.y) { | 340 | if (docSize.y < rect.size.y) { |
338 | /* Center vertically if short. There is one empty paragraph line's worth of margin | 341 | /* Center vertically if short. There is one empty paragraph line's worth of margin |
@@ -592,6 +595,10 @@ static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { | |||
592 | } | 595 | } |
593 | 596 | ||
594 | static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | 597 | static void updateVisible_DocumentWidget_(iDocumentWidget *d) { |
598 | iChangeFlags(d->flags, | ||
599 | centerVertically_DocumentWidgetFlag, | ||
600 | prefs_App()->centerShortDocs || startsWithCase_String(d->mod.url, "about:") || | ||
601 | !isSuccess_GmStatusCode(d->sourceStatus)); | ||
595 | const iRangei visRange = visibleRange_DocumentWidget_(d); | 602 | const iRangei visRange = visibleRange_DocumentWidget_(d); |
596 | const iRect bounds = bounds_Widget(as_Widget(d)); | 603 | const iRect bounds = bounds_Widget(as_Widget(d)); |
597 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_DocumentWidget_(d) }); | 604 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_DocumentWidget_(d) }); |
@@ -1038,6 +1045,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | |||
1038 | /* Use the cached response data. */ | 1045 | /* Use the cached response data. */ |
1039 | updateTrust_DocumentWidget_(d, resp); | 1046 | updateTrust_DocumentWidget_(d, resp); |
1040 | d->sourceTime = resp->when; | 1047 | d->sourceTime = resp->when; |
1048 | d->sourceStatus = success_GmStatusCode; | ||
1041 | format_String(&d->sourceHeader, "(cached content)"); | 1049 | format_String(&d->sourceHeader, "(cached content)"); |
1042 | updateTimestampBuf_DocumentWidget_(d); | 1050 | updateTimestampBuf_DocumentWidget_(d); |
1043 | set_Block(&d->sourceContent, &resp->body); | 1051 | set_Block(&d->sourceContent, &resp->body); |
@@ -1113,6 +1121,9 @@ static void scroll_DocumentWidget_(iDocumentWidget *d, int offset) { | |||
1113 | } | 1121 | } |
1114 | 1122 | ||
1115 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { | 1123 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { |
1124 | if (!hasSiteBanner_GmDocument(d->doc)) { | ||
1125 | documentY += d->pageMargin * gap_UI; | ||
1126 | } | ||
1116 | init_Anim(&d->scrollY, | 1127 | init_Anim(&d->scrollY, |
1117 | documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 | 1128 | documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 |
1118 | : lineHeight_Text(paragraph_FontId))); | 1129 | : lineHeight_Text(paragraph_FontId))); |
@@ -1192,6 +1203,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1192 | updateTrust_DocumentWidget_(d, resp); | 1203 | updateTrust_DocumentWidget_(d, resp); |
1193 | init_Anim(&d->sideOpacity, 0); | 1204 | init_Anim(&d->sideOpacity, 0); |
1194 | format_String(&d->sourceHeader, "%d %s", statusCode, get_GmError(statusCode)->title); | 1205 | format_String(&d->sourceHeader, "%d %s", statusCode, get_GmError(statusCode)->title); |
1206 | d->sourceStatus = statusCode; | ||
1195 | switch (category_GmStatusCode(statusCode)) { | 1207 | switch (category_GmStatusCode(statusCode)) { |
1196 | case categoryInput_GmStatusCode: { | 1208 | case categoryInput_GmStatusCode: { |
1197 | iUrl parts; | 1209 | iUrl parts; |
@@ -1538,28 +1550,43 @@ static const int homeRowKeys_[] = { | |||
1538 | 't', 'y', | 1550 | 't', 'y', |
1539 | }; | 1551 | }; |
1540 | 1552 | ||
1553 | static void updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumentWidget *d, | ||
1554 | iBool keepCenter) { | ||
1555 | /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top | ||
1556 | of the visible area fixed. */ | ||
1557 | const iGmRun *run = keepCenter ? middleRun_DocumentWidget_(d) : d->firstVisibleRun; | ||
1558 | const char * runLoc = (run ? run->text.start : NULL); | ||
1559 | int voffset = 0; | ||
1560 | if (!keepCenter && run) { | ||
1561 | /* Keep the first visible run visible at the same position. */ | ||
1562 | /* TODO: First *fully* visible run? */ | ||
1563 | voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds); | ||
1564 | } | ||
1565 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); | ||
1566 | if (runLoc && !keepCenter) { | ||
1567 | run = findRunAtLoc_GmDocument(d->doc, runLoc); | ||
1568 | if (run) { | ||
1569 | scrollTo_DocumentWidget_(d, | ||
1570 | top_Rect(run->visBounds) + | ||
1571 | lineHeight_Text(paragraph_FontId) + voffset, | ||
1572 | iFalse); | ||
1573 | } | ||
1574 | } | ||
1575 | else if (runLoc && keepCenter) { | ||
1576 | run = findRunAtLoc_GmDocument(d->doc, runLoc); | ||
1577 | if (run) { | ||
1578 | scrollTo_DocumentWidget_(d, mid_Rect(run->bounds).y, iTrue); | ||
1579 | } | ||
1580 | } | ||
1581 | } | ||
1582 | |||
1541 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 1583 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
1542 | iWidget *w = as_Widget(d); | 1584 | iWidget *w = as_Widget(d); |
1543 | if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { | 1585 | if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { |
1544 | const iBool isVerticalOnly = | ||
1545 | !argLabel_Command(cmd, "horiz") && argLabel_Command(cmd, "vert"); | ||
1546 | /* Alt/Option key may be involved in window size changes. */ | 1586 | /* Alt/Option key may be involved in window size changes. */ |
1547 | iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iFalse); | 1587 | iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iFalse); |
1548 | if (isVerticalOnly) { | 1588 | const iBool keepCenter = equal_Command(cmd, "font.changed"); |
1549 | scroll_DocumentWidget_(d, 0); /* prevent overscroll */ | 1589 | updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, keepCenter); |
1550 | } | ||
1551 | else { | ||
1552 | const iGmRun *mid = middleRun_DocumentWidget_(d); | ||
1553 | const char *midLoc = (mid ? mid->text.start : NULL); | ||
1554 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); | ||
1555 | scroll_DocumentWidget_(d, 0); | ||
1556 | if (midLoc) { | ||
1557 | mid = findRunAtLoc_GmDocument(d->doc, midLoc); | ||
1558 | if (mid) { | ||
1559 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y, iTrue); | ||
1560 | } | ||
1561 | } | ||
1562 | } | ||
1563 | updateSideIconBuf_DocumentWidget_(d); | 1590 | updateSideIconBuf_DocumentWidget_(d); |
1564 | updateOutline_DocumentWidget_(d); | 1591 | updateOutline_DocumentWidget_(d); |
1565 | invalidate_DocumentWidget_(d); | 1592 | invalidate_DocumentWidget_(d); |
@@ -1581,6 +1608,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1581 | } | 1608 | } |
1582 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { | 1609 | else if (equal_Command(cmd, "theme.changed") && document_App() == d) { |
1583 | updateTheme_DocumentWidget_(d); | 1610 | updateTheme_DocumentWidget_(d); |
1611 | updateVisible_DocumentWidget_(d); | ||
1584 | updateTrust_DocumentWidget_(d, NULL); | 1612 | updateTrust_DocumentWidget_(d, NULL); |
1585 | updateSideIconBuf_DocumentWidget_(d); | 1613 | updateSideIconBuf_DocumentWidget_(d); |
1586 | invalidate_DocumentWidget_(d); | 1614 | invalidate_DocumentWidget_(d); |
@@ -3398,7 +3426,7 @@ iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { | |||
3398 | } | 3426 | } |
3399 | 3427 | ||
3400 | void updateSize_DocumentWidget(iDocumentWidget *d) { | 3428 | void updateSize_DocumentWidget(iDocumentWidget *d) { |
3401 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); | 3429 | updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, iFalse); |
3402 | resetWideRuns_DocumentWidget_(d); | 3430 | resetWideRuns_DocumentWidget_(d); |
3403 | updateSideIconBuf_DocumentWidget_(d); | 3431 | updateSideIconBuf_DocumentWidget_(d); |
3404 | updateOutline_DocumentWidget_(d); | 3432 | updateOutline_DocumentWidget_(d); |
diff --git a/src/ui/util.c b/src/ui/util.c index b52dea2d..553d9078 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -1123,10 +1123,10 @@ iWidget *makePreferences_Widget(void) { | |||
1123 | addChild_Widget(headings, iClob(makeHeading_Widget("Saturation:"))); | 1123 | addChild_Widget(headings, iClob(makeHeading_Widget("Saturation:"))); |
1124 | iWidget *sats = new_Widget(); | 1124 | iWidget *sats = new_Widget(); |
1125 | /* Saturation levels. */ { | 1125 | /* Saturation levels. */ { |
1126 | addRadioButton_(sats, "prefs.saturation.3", "Full", "saturation.set arg:100"); | 1126 | addRadioButton_(sats, "prefs.saturation.3", "100 %%", "saturation.set arg:100"); |
1127 | addRadioButton_(sats, "prefs.saturation.2", "Reduced", "saturation.set arg:66"); | 1127 | addRadioButton_(sats, "prefs.saturation.2", "66 %%", "saturation.set arg:66"); |
1128 | addRadioButton_(sats, "prefs.saturation.1", "Minimal", "saturation.set arg:33"); | 1128 | addRadioButton_(sats, "prefs.saturation.1", "33 %%", "saturation.set arg:33"); |
1129 | addRadioButton_(sats, "prefs.saturation.0", "Monochrome", "saturation.set arg:0"); | 1129 | addRadioButton_(sats, "prefs.saturation.0", "0 %%", "saturation.set arg:0"); |
1130 | } | 1130 | } |
1131 | addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | 1131 | addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); |
1132 | } | 1132 | } |
@@ -1175,6 +1175,10 @@ iWidget *makePreferences_Widget(void) { | |||
1175 | } | 1175 | } |
1176 | /* Network. */ { | 1176 | /* Network. */ { |
1177 | appendTwoColumnPage_(tabs, "Network", '5', &headings, &values); | 1177 | appendTwoColumnPage_(tabs, "Network", '5', &headings, &values); |
1178 | addChild_Widget(headings, iClob(makeHeading_Widget("Search URL:"))); | ||
1179 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.searchurl"); | ||
1180 | addChild_Widget(headings, iClob(makeHeading_Widget("Decode URLs:"))); | ||
1181 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); | ||
1178 | addChild_Widget(headings, iClob(makeHeading_Widget("Cache size:"))); | 1182 | addChild_Widget(headings, iClob(makeHeading_Widget("Cache size:"))); |
1179 | iWidget *cacheGroup = new_Widget(); { | 1183 | iWidget *cacheGroup = new_Widget(); { |
1180 | iInputWidget *cache = new_InputWidget(4); | 1184 | iInputWidget *cache = new_InputWidget(4); |
@@ -1183,8 +1187,6 @@ iWidget *makePreferences_Widget(void) { | |||
1183 | addChildFlags_Widget(cacheGroup, iClob(new_LabelWidget("MB", NULL)), frameless_WidgetFlag); | 1187 | addChildFlags_Widget(cacheGroup, iClob(new_LabelWidget("MB", NULL)), frameless_WidgetFlag); |
1184 | } | 1188 | } |
1185 | addChildFlags_Widget(values, iClob(cacheGroup), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); | 1189 | addChildFlags_Widget(values, iClob(cacheGroup), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); |
1186 | addChild_Widget(headings, iClob(makeHeading_Widget("Decode URLs:"))); | ||
1187 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); | ||
1188 | makeTwoColumnHeading_("PROXIES", headings, values); | 1190 | makeTwoColumnHeading_("PROXIES", headings, values); |
1189 | addChild_Widget(headings, iClob(makeHeading_Widget("Gemini proxy:"))); | 1191 | addChild_Widget(headings, iClob(makeHeading_Widget("Gemini proxy:"))); |
1190 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.gemini"); | 1192 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.gemini"); |
@@ -1201,6 +1203,7 @@ iWidget *makePreferences_Widget(void) { | |||
1201 | resizeToLargestPage_Widget(tabs); | 1203 | resizeToLargestPage_Widget(tabs); |
1202 | arrange_Widget(dlg); | 1204 | arrange_Widget(dlg); |
1203 | /* Set input field sizes. */ { | 1205 | /* Set input field sizes. */ { |
1206 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.searchurl")); | ||
1204 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.downloads")); | 1207 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.downloads")); |
1205 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gemini")); | 1208 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gemini")); |
1206 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gopher")); | 1209 | expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gopher")); |
diff --git a/src/ui/window.c b/src/ui/window.c index d6a41d3b..563d57ae 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
47 | 47 | ||
48 | #include <the_Foundation/file.h> | 48 | #include <the_Foundation/file.h> |
49 | #include <the_Foundation/path.h> | 49 | #include <the_Foundation/path.h> |
50 | #include <the_Foundation/regexp.h> | ||
50 | #include <SDL_hints.h> | 51 | #include <SDL_hints.h> |
51 | #include <SDL_timer.h> | 52 | #include <SDL_timer.h> |
52 | #include <SDL_syswm.h> | 53 | #include <SDL_syswm.h> |
@@ -419,9 +420,14 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { | |||
419 | !isFocused_Widget(findWidget_App("lookup"))) { | 420 | !isFocused_Widget(findWidget_App("lookup"))) { |
420 | iString *newUrl = copy_String(text_InputWidget(url)); | 421 | iString *newUrl = copy_String(text_InputWidget(url)); |
421 | trim_String(newUrl); | 422 | trim_String(newUrl); |
422 | postCommandf_App( | 423 | if (!isEmpty_String(&prefs_App()->searchUrl) && !isLikelyUrl_String(newUrl)) { |
423 | "open url:%s", | 424 | postCommandf_App("open url:%s", cstr_String(searchQueryUrl_App(newUrl))); |
424 | cstr_String(absoluteUrl_String(&iStringLiteral(""), collect_String(newUrl)))); | 425 | } |
426 | else { | ||
427 | postCommandf_App( | ||
428 | "open url:%s", | ||
429 | cstr_String(absoluteUrl_String(&iStringLiteral(""), collect_String(newUrl)))); | ||
430 | } | ||
425 | return iTrue; | 431 | return iTrue; |
426 | } | 432 | } |
427 | } | 433 | } |
@@ -1028,7 +1034,14 @@ static void invalidate_Window_(iWindow *d) { | |||
1028 | } | 1034 | } |
1029 | 1035 | ||
1030 | static iBool isNormalPlacement_Window_(const iWindow *d) { | 1036 | static iBool isNormalPlacement_Window_(const iWindow *d) { |
1031 | if (snap_Window(d) || d->isDrawFrozen) return iFalse; | 1037 | if (d->isDrawFrozen) return iFalse; |
1038 | #if defined (iPlatformApple) | ||
1039 | /* Maximized mode is not special on macOS. */ | ||
1040 | if (snap_Window(d) == maximized_WindowSnap) { | ||
1041 | return iTrue; | ||
1042 | } | ||
1043 | #endif | ||
1044 | if (snap_Window(d)) return iFalse; | ||
1032 | return !(SDL_GetWindowFlags(d->win) & SDL_WINDOW_MINIMIZED); | 1045 | return !(SDL_GetWindowFlags(d->win) & SDL_WINDOW_MINIMIZED); |
1033 | } | 1046 | } |
1034 | 1047 | ||
@@ -1161,7 +1174,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { | |||
1161 | //updateRootSize_Window_(d, iTrue); | 1174 | //updateRootSize_Window_(d, iTrue); |
1162 | invalidate_Window_(d); | 1175 | invalidate_Window_(d); |
1163 | d->isMinimized = iFalse; | 1176 | d->isMinimized = iFalse; |
1164 | //printf("restored %d\n", snap_Window(d)); fflush(stdout); | 1177 | printf("restored %d\n", snap_Window(d)); fflush(stdout); |
1165 | return iTrue; | 1178 | return iTrue; |
1166 | case SDL_WINDOWEVENT_MINIMIZED: | 1179 | case SDL_WINDOWEVENT_MINIMIZED: |
1167 | d->isMinimized = iTrue; | 1180 | d->isMinimized = iTrue; |