diff options
-rw-r--r-- | res/about/version.gmi | 1 | ||||
-rw-r--r-- | src/app.c | 8 | ||||
-rw-r--r-- | src/gmdocument.c | 10 | ||||
-rw-r--r-- | src/prefs.c | 12 | ||||
-rw-r--r-- | src/prefs.h | 50 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 44 | ||||
-rw-r--r-- | src/ui/keys.c | 67 | ||||
-rw-r--r-- | src/ui/keys.h | 1 | ||||
-rw-r--r-- | src/ui/util.c | 2 |
9 files changed, 131 insertions, 64 deletions
diff --git a/res/about/version.gmi b/res/about/version.gmi index 3679a465..f3d40e76 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi | |||
@@ -7,6 +7,7 @@ | |||
7 | # Release notes | 7 | # Release notes |
8 | 8 | ||
9 | ## 0.10 | 9 | ## 0.10 |
10 | * Added option to load inline images when pressing Space or ↓. If an image link is visible, the image will be loaded instead of scrolling. This option is disabled by default. | ||
10 | * Added an option to use a proxy server for Gemini requests. | 11 | * Added an option to use a proxy server for Gemini requests. |
11 | * Added a keybinding to activate keyboard link navigation mode (default is "F"). | 12 | * Added a keybinding to activate keyboard link navigation mode (default is "F"). |
12 | * Clearing and resetting keybindings via a context menu. | 13 | * Clearing and resetting keybindings via a context menu. |
@@ -183,6 +183,7 @@ static iString *serializePrefs_App_(const iApp *d) { | |||
183 | appendFormat_String(str, "prefs.mono.gopher.changed arg:%d\n", d->prefs.monospaceGopher); | 183 | appendFormat_String(str, "prefs.mono.gopher.changed arg:%d\n", d->prefs.monospaceGopher); |
184 | appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent); | 184 | appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent); |
185 | appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); | 185 | appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); |
186 | appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); | ||
186 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); | 187 | appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); |
187 | appendFormat_String(str, "prefs.biglede.changed arg:%d\n", d->prefs.bigFirstParagraph); | 188 | appendFormat_String(str, "prefs.biglede.changed arg:%d\n", d->prefs.bigFirstParagraph); |
188 | appendFormat_String(str, "prefs.sideicon.changed arg:%d\n", d->prefs.sideIcon); | 189 | appendFormat_String(str, "prefs.sideicon.changed arg:%d\n", d->prefs.sideIcon); |
@@ -746,6 +747,8 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
746 | isSelected_Widget(findChild_Widget(d, "prefs.retainwindow"))); | 747 | isSelected_Widget(findChild_Widget(d, "prefs.retainwindow"))); |
747 | postCommandf_App("smoothscroll arg:%d", | 748 | postCommandf_App("smoothscroll arg:%d", |
748 | isSelected_Widget(findChild_Widget(d, "prefs.smoothscroll"))); | 749 | isSelected_Widget(findChild_Widget(d, "prefs.smoothscroll"))); |
750 | postCommandf_App("imageloadscroll arg:%d", | ||
751 | isSelected_Widget(findChild_Widget(d, "prefs.imageloadscroll"))); | ||
749 | postCommandf_App("ostheme arg:%d", | 752 | postCommandf_App("ostheme arg:%d", |
750 | isSelected_Widget(findChild_Widget(d, "prefs.ostheme"))); | 753 | isSelected_Widget(findChild_Widget(d, "prefs.ostheme"))); |
751 | postCommandf_App("proxy.gemini address:%s", | 754 | postCommandf_App("proxy.gemini address:%s", |
@@ -956,6 +959,10 @@ iBool handleCommand_App(const char *cmd) { | |||
956 | d->prefs.smoothScrolling = arg_Command(cmd); | 959 | d->prefs.smoothScrolling = arg_Command(cmd); |
957 | return iTrue; | 960 | return iTrue; |
958 | } | 961 | } |
962 | else if (equal_Command(cmd, "imageloadscroll")) { | ||
963 | d->prefs.loadImageInsteadOfScrolling = arg_Command(cmd); | ||
964 | return iTrue; | ||
965 | } | ||
959 | else if (equal_Command(cmd, "forcewrap.toggle")) { | 966 | else if (equal_Command(cmd, "forcewrap.toggle")) { |
960 | d->prefs.forceLineWrap = !d->prefs.forceLineWrap; | 967 | d->prefs.forceLineWrap = !d->prefs.forceLineWrap; |
961 | updateSize_DocumentWidget(document_App()); | 968 | updateSize_DocumentWidget(document_App()); |
@@ -1153,6 +1160,7 @@ iBool handleCommand_App(const char *cmd) { | |||
1153 | setText_InputWidget(findChild_Widget(dlg, "prefs.downloads"), &d->prefs.downloadDir); | 1160 | setText_InputWidget(findChild_Widget(dlg, "prefs.downloads"), &d->prefs.downloadDir); |
1154 | setToggle_Widget(findChild_Widget(dlg, "prefs.hoveroutline"), d->prefs.hoverOutline); | 1161 | setToggle_Widget(findChild_Widget(dlg, "prefs.hoveroutline"), d->prefs.hoverOutline); |
1155 | setToggle_Widget(findChild_Widget(dlg, "prefs.smoothscroll"), d->prefs.smoothScrolling); | 1162 | setToggle_Widget(findChild_Widget(dlg, "prefs.smoothscroll"), d->prefs.smoothScrolling); |
1163 | setToggle_Widget(findChild_Widget(dlg, "prefs.imageloadscroll"), d->prefs.loadImageInsteadOfScrolling); | ||
1156 | setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); | 1164 | setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); |
1157 | setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); | 1165 | setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); |
1158 | setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), | 1166 | setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), |
diff --git a/src/gmdocument.c b/src/gmdocument.c index 923c20c9..62a0ba55 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -1366,8 +1366,14 @@ enum iColorId linkColor_GmDocument(const iGmDocument *d, iGmLinkId linkId, enum | |||
1366 | } | 1366 | } |
1367 | 1367 | ||
1368 | iBool isMediaLink_GmDocument(const iGmDocument *d, iGmLinkId linkId) { | 1368 | iBool isMediaLink_GmDocument(const iGmDocument *d, iGmLinkId linkId) { |
1369 | return (linkFlags_GmDocument(d, linkId) & | 1369 | const iString *dstUrl = absoluteUrl_String(&d->url, linkUrl_GmDocument(d, linkId)); |
1370 | (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag)) != 0; | 1370 | const iRangecc scheme = urlScheme_String(dstUrl); |
1371 | if (equalCase_Rangecc(scheme, "gemini") || equalCase_Rangecc(scheme, "gopher") || | ||
1372 | willUseProxy_App(scheme)) { | ||
1373 | return (linkFlags_GmDocument(d, linkId) & | ||
1374 | (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag)) != 0; | ||
1375 | } | ||
1376 | return iFalse; | ||
1371 | } | 1377 | } |
1372 | 1378 | ||
1373 | const iString *title_GmDocument(const iGmDocument *d) { | 1379 | const iString *title_GmDocument(const iGmDocument *d) { |
diff --git a/src/prefs.c b/src/prefs.c index 80b11c30..dc2bd601 100644 --- a/src/prefs.c +++ b/src/prefs.c | |||
@@ -24,21 +24,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | 24 | ||
25 | void init_Prefs(iPrefs *d) { | 25 | void init_Prefs(iPrefs *d) { |
26 | d->dialogTab = 0; | 26 | d->dialogTab = 0; |
27 | d->theme = dark_ColorTheme; | ||
28 | d->useSystemTheme = iTrue; | 27 | d->useSystemTheme = iTrue; |
28 | d->theme = dark_ColorTheme; | ||
29 | d->retainWindowSize = iTrue; | 29 | d->retainWindowSize = iTrue; |
30 | d->uiScale = 1.0f; /* default set elsewhere */ | ||
30 | d->zoomPercent = 100; | 31 | d->zoomPercent = 100; |
32 | d->sideIcon = iTrue; | ||
33 | d->hoverOutline = iFalse; | ||
31 | d->smoothScrolling = iTrue; | 34 | d->smoothScrolling = iTrue; |
32 | d->forceLineWrap = iFalse; | 35 | d->loadImageInsteadOfScrolling = iFalse; |
33 | d->quoteIcon = iTrue; | ||
34 | d->font = nunito_TextFont; | 36 | d->font = nunito_TextFont; |
35 | d->headingFont = nunito_TextFont; | 37 | d->headingFont = nunito_TextFont; |
36 | d->monospaceGemini = iFalse; | 38 | d->monospaceGemini = iFalse; |
37 | d->monospaceGopher = iFalse; | 39 | d->monospaceGopher = iFalse; |
38 | d->lineWidth = 40; | 40 | d->lineWidth = 40; |
39 | d->bigFirstParagraph = iTrue; | 41 | d->bigFirstParagraph = iTrue; |
40 | d->sideIcon = iTrue; | 42 | d->forceLineWrap = iFalse; |
41 | d->hoverOutline = iFalse; | 43 | d->quoteIcon = iTrue; |
42 | d->docThemeDark = colorfulDark_GmDocumentTheme; | 44 | d->docThemeDark = colorfulDark_GmDocumentTheme; |
43 | d->docThemeLight = white_GmDocumentTheme; | 45 | d->docThemeLight = white_GmDocumentTheme; |
44 | d->saturation = 1.0f; | 46 | d->saturation = 1.0f; |
diff --git a/src/prefs.h b/src/prefs.h index 33ce8b41..a3993629 100644 --- a/src/prefs.h +++ b/src/prefs.h | |||
@@ -33,31 +33,37 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
33 | iDeclareType(Prefs) | 33 | iDeclareType(Prefs) |
34 | 34 | ||
35 | struct Impl_Prefs { | 35 | struct Impl_Prefs { |
36 | int dialogTab; | 36 | /* UI state */ |
37 | iBool retainWindowSize; | 37 | int dialogTab; |
38 | float uiScale; | 38 | /* Window */ |
39 | int zoomPercent; | 39 | iBool useSystemTheme; |
40 | iBool smoothScrolling; | ||
41 | iBool useSystemTheme; | ||
42 | enum iColorTheme theme; | 40 | enum iColorTheme theme; |
43 | iString geminiProxy; | 41 | iBool retainWindowSize; |
44 | iString gopherProxy; | 42 | float uiScale; |
45 | iString httpProxy; | 43 | int zoomPercent; |
46 | iString downloadDir; | 44 | iBool sideIcon; |
47 | /* Content */ | 45 | /* Behavior */ |
48 | enum iTextFont font; | 46 | iString downloadDir; |
49 | enum iTextFont headingFont; | 47 | iBool hoverOutline; |
50 | iBool monospaceGemini; | 48 | iBool smoothScrolling; |
51 | iBool monospaceGopher; | 49 | iBool loadImageInsteadOfScrolling; |
52 | int lineWidth; | 50 | /* Network */ |
53 | iBool bigFirstParagraph; | 51 | iString geminiProxy; |
54 | iBool forceLineWrap; | 52 | iString gopherProxy; |
55 | iBool quoteIcon; | 53 | iString httpProxy; |
56 | iBool sideIcon; | 54 | /* Style */ |
57 | iBool hoverOutline; | 55 | enum iTextFont font; |
56 | enum iTextFont headingFont; | ||
57 | iBool monospaceGemini; | ||
58 | iBool monospaceGopher; | ||
59 | int lineWidth; | ||
60 | iBool bigFirstParagraph; | ||
61 | iBool forceLineWrap; | ||
62 | iBool quoteIcon; | ||
63 | /* Colors */ | ||
58 | enum iGmDocumentTheme docThemeDark; | 64 | enum iGmDocumentTheme docThemeDark; |
59 | enum iGmDocumentTheme docThemeLight; | 65 | enum iGmDocumentTheme docThemeLight; |
60 | float saturation; | 66 | float saturation; |
61 | }; | 67 | }; |
62 | 68 | ||
63 | iDeclareTypeConstruction(Prefs) | 69 | iDeclareTypeConstruction(Prefs) |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 59f5a92c..0fc969ba 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1151,10 +1151,8 @@ static iMediaRequest *findMediaRequest_DocumentWidget_(const iDocumentWidget *d, | |||
1151 | 1151 | ||
1152 | static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) { | 1152 | static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) { |
1153 | if (!findMediaRequest_DocumentWidget_(d, linkId)) { | 1153 | if (!findMediaRequest_DocumentWidget_(d, linkId)) { |
1154 | pushBack_ObjectList( | 1154 | const iString *imageUrl = absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, linkId)); |
1155 | d->media, | 1155 | pushBack_ObjectList(d->media, iClob(new_MediaRequest(d, linkId, imageUrl))); |
1156 | iClob(new_MediaRequest( | ||
1157 | d, linkId, absoluteUrl_String(d->mod.url, linkUrl_GmDocument(d->doc, linkId))))); | ||
1158 | invalidate_DocumentWidget_(d); | 1156 | invalidate_DocumentWidget_(d); |
1159 | return iTrue; | 1157 | return iTrue; |
1160 | } | 1158 | } |
@@ -1235,6 +1233,22 @@ static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) { | |||
1235 | } | 1233 | } |
1236 | } | 1234 | } |
1237 | 1235 | ||
1236 | static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { | ||
1237 | iConstForEach(PtrArray, i, &d->visibleLinks) { | ||
1238 | const iGmRun *run = i.ptr; | ||
1239 | if (run->linkId && !run->imageId && ~run->flags & decoration_GmRunFlag) { | ||
1240 | const int linkFlags = linkFlags_GmDocument(d->doc, run->linkId); | ||
1241 | if (isMediaLink_GmDocument(d->doc, run->linkId) && | ||
1242 | ~linkFlags & content_GmLinkFlag && ~linkFlags & permanent_GmLinkFlag ) { | ||
1243 | if (requestMedia_DocumentWidget_(d, run->linkId)) { | ||
1244 | return iTrue; | ||
1245 | } | ||
1246 | } | ||
1247 | } | ||
1248 | } | ||
1249 | return iFalse; | ||
1250 | } | ||
1251 | |||
1238 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 1252 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
1239 | iWidget *w = as_Widget(d); | 1253 | iWidget *w = as_Widget(d); |
1240 | if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { | 1254 | if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { |
@@ -1572,13 +1586,16 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1572 | return iTrue; | 1586 | return iTrue; |
1573 | } | 1587 | } |
1574 | else if (equal_Command(cmd, "scroll.page") && document_App() == d) { | 1588 | else if (equal_Command(cmd, "scroll.page") && document_App() == d) { |
1575 | if (argLabel_Command(cmd, "repeat")) { | 1589 | const int dir = arg_Command(cmd); |
1576 | /* TODO: Adjust scroll animation to be linear during repeated scroll? */ | 1590 | if (!argLabel_Command(cmd, "repeat") && prefs_App()->loadImageInsteadOfScrolling && |
1591 | dir > 0) { | ||
1592 | if (fetchNextUnfetchedImage_DocumentWidget_(d)) { | ||
1593 | return iTrue; | ||
1594 | } | ||
1577 | } | 1595 | } |
1578 | smoothScroll_DocumentWidget_(d, | 1596 | smoothScroll_DocumentWidget_(d, |
1579 | arg_Command(cmd) * | 1597 | dir * (0.5f * height_Rect(documentBounds_DocumentWidget_(d)) - |
1580 | (0.5f * height_Rect(documentBounds_DocumentWidget_(d)) - | 1598 | 0 * lineHeight_Text(paragraph_FontId)), |
1581 | 0 * lineHeight_Text(paragraph_FontId)), | ||
1582 | smoothDuration_DocumentWidget_); | 1599 | smoothDuration_DocumentWidget_); |
1583 | return iTrue; | 1600 | return iTrue; |
1584 | } | 1601 | } |
@@ -1599,8 +1616,15 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1599 | return iTrue; | 1616 | return iTrue; |
1600 | } | 1617 | } |
1601 | else if (equal_Command(cmd, "scroll.step") && document_App() == d) { | 1618 | else if (equal_Command(cmd, "scroll.step") && document_App() == d) { |
1619 | const int dir = arg_Command(cmd); | ||
1620 | if (!argLabel_Command(cmd, "repeat") && prefs_App()->loadImageInsteadOfScrolling && | ||
1621 | dir > 0) { | ||
1622 | if (fetchNextUnfetchedImage_DocumentWidget_(d)) { | ||
1623 | return iTrue; | ||
1624 | } | ||
1625 | } | ||
1602 | smoothScroll_DocumentWidget_(d, | 1626 | smoothScroll_DocumentWidget_(d, |
1603 | 3 * lineHeight_Text(paragraph_FontId) * arg_Command(cmd), | 1627 | 3 * lineHeight_Text(paragraph_FontId) * dir, |
1604 | smoothDuration_DocumentWidget_); | 1628 | smoothDuration_DocumentWidget_); |
1605 | return iTrue; | 1629 | return iTrue; |
1606 | } | 1630 | } |
diff --git a/src/ui/keys.c b/src/ui/keys.c index 5b88bfcf..ea874343 100644 --- a/src/ui/keys.c +++ b/src/ui/keys.c | |||
@@ -56,25 +56,46 @@ static void clear_Keys_(iKeys *d) { | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | enum iBindFlag { | ||
60 | argRepeat_BindFlag = iBit(1), | ||
61 | }; | ||
62 | |||
59 | /* TODO: This indirection could be used for localization, although all UI strings | 63 | /* TODO: This indirection could be used for localization, although all UI strings |
60 | would need to be similarly handled. */ | 64 | would need to be similarly handled. */ |
61 | static const struct { int id; iMenuItem bind; } defaultBindings_[] = { | 65 | static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] = { |
62 | { 1, { "Jump to top", SDLK_HOME, 0, "scroll.top" } }, | 66 | { 1, { "Jump to top", SDLK_HOME, 0, "scroll.top" }, 0 }, |
63 | { 2, { "Jump to bottom", SDLK_END, 0, "scroll.bottom" } }, | 67 | { 2, { "Jump to bottom", SDLK_END, 0, "scroll.bottom" }, 0 }, |
64 | { 10, { "Scroll up", SDLK_UP, 0, "scroll.step arg:-1" } }, | 68 | { 10, { "Scroll up", SDLK_UP, 0, "scroll.step arg:-1" }, argRepeat_BindFlag }, |
65 | { 11, { "Scroll down", SDLK_DOWN, 0, "scroll.step arg:1" } }, | 69 | { 11, { "Scroll down", SDLK_DOWN, 0, "scroll.step arg:1" }, argRepeat_BindFlag }, |
66 | { 20, { "Scroll up half a page", SDLK_PAGEUP, 0, "scroll.page arg:-1" } }, | 70 | { 20, { "Scroll up half a page", SDLK_PAGEUP, 0, "scroll.page arg:-1" }, argRepeat_BindFlag }, |
67 | { 21, { "Scroll down half a page", SDLK_PAGEDOWN, 0, "scroll.page arg:1" } }, | 71 | { 21, { "Scroll down half a page", SDLK_PAGEDOWN, 0, "scroll.page arg:1" }, argRepeat_BindFlag }, |
68 | { 30, { "Go back", navigateBack_KeyShortcut, "navigate.back" } }, | 72 | { 30, { "Go back", navigateBack_KeyShortcut, "navigate.back" }, 0 }, |
69 | { 31, { "Go forward", navigateForward_KeyShortcut, "navigate.forward" } }, | 73 | { 31, { "Go forward", navigateForward_KeyShortcut, "navigate.forward" }, 0 }, |
70 | { 32, { "Go to parent directory", navigateParent_KeyShortcut, "navigate.parent" } }, | 74 | { 32, { "Go to parent directory", navigateParent_KeyShortcut, "navigate.parent" }, 0 }, |
71 | { 33, { "Go to site root", navigateRoot_KeyShortcut, "navigate.root" } }, | 75 | { 33, { "Go to site root", navigateRoot_KeyShortcut, "navigate.root" }, 0 }, |
72 | { 40, { "Open link via keyboard", 'f', 0, "document.linkkeys"} }, | 76 | { 40, { "Open link via keyboard", 'f', 0, "document.linkkeys" }, 0 }, |
73 | /* The following cannot currently be changed (built-in duplicates). */ | 77 | /* The following cannot currently be changed (built-in duplicates). */ |
74 | { 1000, { NULL, SDLK_SPACE, KMOD_SHIFT, "scroll.page arg:-1" } }, | 78 | { 1000, { NULL, SDLK_SPACE, KMOD_SHIFT, "scroll.page arg:-1" }, argRepeat_BindFlag }, |
75 | { 1001, { NULL, SDLK_SPACE, 0, "scroll.page arg:1" } }, | 79 | { 1001, { NULL, SDLK_SPACE, 0, "scroll.page arg:1" }, argRepeat_BindFlag }, |
76 | }; | 80 | }; |
77 | 81 | ||
82 | static iBinding *findId_Keys_(iKeys *d, int id) { | ||
83 | iForEach(Array, i, &d->bindings) { | ||
84 | iBinding *bind = i.value; | ||
85 | if (bind->id == id) { | ||
86 | return bind; | ||
87 | } | ||
88 | } | ||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | static void setFlags_Keys_(int id, int bindFlags) { | ||
93 | iBinding *bind = findId_Keys_(&keys_, id); | ||
94 | if (bind) { | ||
95 | bind->flags = bindFlags; | ||
96 | } | ||
97 | } | ||
98 | |||
78 | static void bindDefaults_(void) { | 99 | static void bindDefaults_(void) { |
79 | iForIndices(i, defaultBindings_) { | 100 | iForIndices(i, defaultBindings_) { |
80 | const int id = defaultBindings_[i].id; | 101 | const int id = defaultBindings_[i].id; |
@@ -83,6 +104,7 @@ static void bindDefaults_(void) { | |||
83 | if (bind.label) { | 104 | if (bind.label) { |
84 | setLabel_Keys(id, bind.label); | 105 | setLabel_Keys(id, bind.label); |
85 | } | 106 | } |
107 | setFlags_Keys_(id, defaultBindings_[i].flags); | ||
86 | } | 108 | } |
87 | } | 109 | } |
88 | 110 | ||
@@ -95,16 +117,6 @@ static iBinding *find_Keys_(iKeys *d, int key, int mods) { | |||
95 | return NULL; | 117 | return NULL; |
96 | } | 118 | } |
97 | 119 | ||
98 | static iBinding *findId_Keys_(iKeys *d, int id) { | ||
99 | iForEach(Array, i, &d->bindings) { | ||
100 | iBinding *bind = i.value; | ||
101 | if (bind->id == id) { | ||
102 | return bind; | ||
103 | } | ||
104 | } | ||
105 | return NULL; | ||
106 | } | ||
107 | |||
108 | static iBinding *findCommand_Keys_(iKeys *d, const char *command) { | 120 | static iBinding *findCommand_Keys_(iKeys *d, const char *command) { |
109 | /* Note: O(n) */ | 121 | /* Note: O(n) */ |
110 | iForEach(Array, i, &d->bindings) { | 122 | iForEach(Array, i, &d->bindings) { |
@@ -259,7 +271,12 @@ iBool processEvent_Keys(const SDL_Event *ev) { | |||
259 | if (ev->type == SDL_KEYDOWN) { | 271 | if (ev->type == SDL_KEYDOWN) { |
260 | const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod)); | 272 | const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod)); |
261 | if (bind) { | 273 | if (bind) { |
262 | postCommandString_App(&bind->command); | 274 | if (ev->key.repeat && (bind->flags & argRepeat_BindFlag)) { |
275 | postCommandf_App("%s repeat:1", cstr_String(&bind->command)); | ||
276 | } | ||
277 | else { | ||
278 | postCommandString_App(&bind->command); | ||
279 | } | ||
263 | return iTrue; | 280 | return iTrue; |
264 | } | 281 | } |
265 | } | 282 | } |
diff --git a/src/ui/keys.h b/src/ui/keys.h index 0cd97e2a..1e676c59 100644 --- a/src/ui/keys.h +++ b/src/ui/keys.h | |||
@@ -52,6 +52,7 @@ iDeclareType(Binding) | |||
52 | 52 | ||
53 | struct Impl_Binding { | 53 | struct Impl_Binding { |
54 | int id; | 54 | int id; |
55 | int flags; | ||
55 | int key; | 56 | int key; |
56 | int mods; | 57 | int mods; |
57 | iString command; | 58 | iString command; |
diff --git a/src/ui/util.c b/src/ui/util.c index 85d3562f..559c5381 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -1013,6 +1013,8 @@ iWidget *makePreferences_Widget(void) { | |||
1013 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoveroutline"))); | 1013 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoveroutline"))); |
1014 | addChild_Widget(headings, iClob(makeHeading_Widget("Smooth scrolling:"))); | 1014 | addChild_Widget(headings, iClob(makeHeading_Widget("Smooth scrolling:"))); |
1015 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.smoothscroll"))); | 1015 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.smoothscroll"))); |
1016 | addChild_Widget(headings, iClob(makeHeading_Widget("Load image on scroll:"))); | ||
1017 | addChild_Widget(values, iClob(makeToggle_Widget("prefs.imageloadscroll"))); | ||
1016 | } | 1018 | } |
1017 | /* Window. */ { | 1019 | /* Window. */ { |
1018 | appendTwoColumnPage_(tabs, "Window", '2', &headings, &values); | 1020 | appendTwoColumnPage_(tabs, "Window", '2', &headings, &values); |