summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-01-24 15:28:35 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-01-24 15:30:26 +0200
commitb1813bc5b1a2fb68853c59c2576833435762c2ec (patch)
treefca54e8b00faddef785d9d364b8458fa77bb03d7 /src/ui
parentbc7d3e49b5d9dfc82c4c2e78ac00b3e568975183 (diff)
DocumentWidget: Fix/improve home row link navigation
Removed the second `U` from the set of home row keys for link navigation. Added a new command bound to `.` (period) for switching to the next set of links for home row navigation. This makes it possible to access all visible links via keyboard no matter how many there are. `.` can also be used to activate home row navigation if it isn't active. IssueID #111
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/documentwidget.c111
-rw-r--r--src/ui/keys.c1
2 files changed, 76 insertions, 36 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 1ee83a85..c725a8f6 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -146,6 +146,7 @@ struct Impl_DocumentWidget {
146 iPersistentDocumentState mod; 146 iPersistentDocumentState mod;
147 int flags; 147 int flags;
148 enum iDocumentLinkOrdinalMode ordinalMode; 148 enum iDocumentLinkOrdinalMode ordinalMode;
149 size_t ordinalBase;
149 iString * titleUser; 150 iString * titleUser;
150 iGmRequest * request; 151 iGmRequest * request;
151 iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ 152 iAtomicInt isRequestUpdated; /* request has new content, need to parse it */
@@ -213,6 +214,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
213 d->media = new_ObjectList(); 214 d->media = new_ObjectList();
214 d->doc = new_GmDocument(); 215 d->doc = new_GmDocument();
215 d->redirectCount = 0; 216 d->redirectCount = 0;
217 d->ordinalBase = 0;
216 d->initNormScrollY = 0; 218 d->initNormScrollY = 0;
217 init_Anim(&d->scrollY, 0); 219 init_Anim(&d->scrollY, 0);
218 d->animWideRunId = 0; 220 d->animWideRunId = 0;
@@ -380,6 +382,16 @@ static void addVisible_DocumentWidget_(void *context, const iGmRun *run) {
380 } 382 }
381} 383}
382 384
385static const iGmRun *lastVisibleLink_DocumentWidget_(const iDocumentWidget *d) {
386 iReverseConstForEach(PtrArray, i, &d->visibleLinks) {
387 const iGmRun *run = i.ptr;
388 if (run->flags & decoration_GmRunFlag && run->linkId) {
389 return run;
390 }
391 }
392 return NULL;
393}
394
383static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { 395static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) {
384 const int docSize = size_GmDocument(d->doc).y; 396 const int docSize = size_GmDocument(d->doc).y;
385 if (docSize) { 397 if (docSize) {
@@ -1489,6 +1501,34 @@ static void addAllLinks_(void *context, const iGmRun *run) {
1489 } 1501 }
1490} 1502}
1491 1503
1504static size_t visibleLinkOrdinal_DocumentWidget_(const iDocumentWidget *d, iGmLinkId linkId) {
1505 size_t ord = 0;
1506 const iRangei visRange = visibleRange_DocumentWidget_(d);
1507 iConstForEach(PtrArray, i, &d->visibleLinks) {
1508 const iGmRun *run = i.ptr;
1509 if (top_Rect(run->visBounds) >= visRange.start + gap_UI * d->pageMargin * 4 / 5) {
1510 if (run->flags & decoration_GmRunFlag && run->linkId) {
1511 if (run->linkId == linkId) return ord;
1512 ord++;
1513 }
1514 }
1515 }
1516 return iInvalidPos;
1517}
1518
1519/* Sorted by proximity to F and J. */
1520static const int homeRowKeys_[] = {
1521 'f', 'd', 's', 'a',
1522 'j', 'k', 'l',
1523 'r', 'e', 'w', 'q',
1524 'u', 'i', 'o', 'p',
1525 'v', 'c', 'x', 'z',
1526 'm', 'n',
1527 'g', 'h',
1528 'b',
1529 't', 'y',
1530};
1531
1492static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { 1532static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
1493 iWidget *w = as_Widget(d); 1533 iWidget *w = as_Widget(d);
1494 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { 1534 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) {
@@ -1785,8 +1825,30 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1785 if (argLabel_Command(cmd, "release")) { 1825 if (argLabel_Command(cmd, "release")) {
1786 iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iFalse); 1826 iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iFalse);
1787 } 1827 }
1828 else if (argLabel_Command(cmd, "more")) {
1829 if (d->flags & showLinkNumbers_DocumentWidgetFlag &&
1830 d->ordinalMode == homeRow_DocumentLinkOrdinalMode) {
1831 const size_t numKeys = iElemCount(homeRowKeys_);
1832 const iGmRun *last = lastVisibleLink_DocumentWidget_(d);
1833 if (!last) {
1834 d->ordinalBase = 0;
1835 }
1836 else {
1837 d->ordinalBase += numKeys;
1838 if (visibleLinkOrdinal_DocumentWidget_(d, last->linkId) < d->ordinalBase) {
1839 d->ordinalBase = 0;
1840 }
1841 }
1842 }
1843 else if (~d->flags & showLinkNumbers_DocumentWidgetFlag) {
1844 d->ordinalMode = homeRow_DocumentLinkOrdinalMode;
1845 d->ordinalBase = 0;
1846 iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iTrue);
1847 }
1848 }
1788 else { 1849 else {
1789 d->ordinalMode = arg_Command(cmd); 1850 d->ordinalMode = arg_Command(cmd);
1851 d->ordinalBase = 0;
1790 iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iTrue); 1852 iChangeFlags(d->flags, showLinkNumbers_DocumentWidgetFlag, iTrue);
1791 iChangeFlags(d->flags, setHoverViaKeys_DocumentWidgetFlag, 1853 iChangeFlags(d->flags, setHoverViaKeys_DocumentWidgetFlag,
1792 argLabel_Command(cmd, "hover") != 0); 1854 argLabel_Command(cmd, "hover") != 0);
@@ -1980,25 +2042,12 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1980 return iFalse; 2042 return iFalse;
1981} 2043}
1982 2044
2045#if 0
1983static int outlineHeight_DocumentWidget_(const iDocumentWidget *d) { 2046static int outlineHeight_DocumentWidget_(const iDocumentWidget *d) {
1984 if (isEmpty_Array(&d->outline)) return 0; 2047 if (isEmpty_Array(&d->outline)) return 0;
1985 return bottom_Rect(((const iOutlineItem *) constBack_Array(&d->outline))->rect); 2048 return bottom_Rect(((const iOutlineItem *) constBack_Array(&d->outline))->rect);
1986} 2049}
1987 2050#endif
1988static size_t visibleLinkOrdinal_DocumentWidget_(const iDocumentWidget *d, iGmLinkId linkId) {
1989 size_t ord = 0;
1990 const iRangei visRange = visibleRange_DocumentWidget_(d);
1991 iConstForEach(PtrArray, i, &d->visibleLinks) {
1992 const iGmRun *run = i.ptr;
1993 if (top_Rect(run->visBounds) >= visRange.start + gap_UI * d->pageMargin * 4 / 5) {
1994 if (run->flags & decoration_GmRunFlag && run->linkId) {
1995 if (run->linkId == linkId) return ord;
1996 ord++;
1997 }
1998 }
1999 }
2000 return iInvalidPos;
2001}
2002 2051
2003static iRect playerRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) { 2052static iRect playerRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) {
2004 const iRect docBounds = documentBounds_DocumentWidget_(d); 2053 const iRect docBounds = documentBounds_DocumentWidget_(d);
@@ -2110,19 +2159,6 @@ static iBool processPlayerEvents_DocumentWidget_(iDocumentWidget *d, const SDL_E
2110 return iFalse; 2159 return iFalse;
2111} 2160}
2112 2161
2113/* Sorted by proximity to F and J. */
2114static const int homeRowKeys_[] = {
2115 'f', 'd', 's', 'a',
2116 'j', 'k', 'l',
2117 'r', 'e', 'w', 'q',
2118 'u', 'i', 'o', 'p',
2119 'v', 'c', 'x', 'z',
2120 'm', 'n',
2121 'g', 'h',
2122 'b',
2123 't', 'y', 'u',
2124};
2125
2126static size_t linkOrdinalFromKey_DocumentWidget_(const iDocumentWidget *d, int key) { 2162static size_t linkOrdinalFromKey_DocumentWidget_(const iDocumentWidget *d, int key) {
2127 size_t ord = iInvalidPos; 2163 size_t ord = iInvalidPos;
2128 if (d->ordinalMode == numbersAndAlphabet_DocumentLinkOrdinalMode) { 2164 if (d->ordinalMode == numbersAndAlphabet_DocumentLinkOrdinalMode) {
@@ -2195,7 +2231,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2195 const int key = ev->key.keysym.sym; 2231 const int key = ev->key.keysym.sym;
2196 if ((d->flags & showLinkNumbers_DocumentWidgetFlag) && 2232 if ((d->flags & showLinkNumbers_DocumentWidgetFlag) &&
2197 ((key >= '1' && key <= '9') || (key >= 'a' && key <= 'z'))) { 2233 ((key >= '1' && key <= '9') || (key >= 'a' && key <= 'z'))) {
2198 const size_t ord = linkOrdinalFromKey_DocumentWidget_(d, key); 2234 const size_t ord = linkOrdinalFromKey_DocumentWidget_(d, key) + d->ordinalBase;
2199 iConstForEach(PtrArray, i, &d->visibleLinks) { 2235 iConstForEach(PtrArray, i, &d->visibleLinks) {
2200 if (ord == iInvalidPos) break; 2236 if (ord == iInvalidPos) break;
2201 const iGmRun *run = i.ptr; 2237 const iGmRun *run = i.ptr;
@@ -2801,13 +2837,16 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
2801 else { 2837 else {
2802 if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { 2838 if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) {
2803 const size_t ord = visibleLinkOrdinal_DocumentWidget_(d->widget, run->linkId); 2839 const size_t ord = visibleLinkOrdinal_DocumentWidget_(d->widget, run->linkId);
2804 const iChar ordChar = linkOrdinalChar_DocumentWidget_(d->widget, ord); 2840 if (ord >= d->widget->ordinalBase) {
2805 if (ordChar) { 2841 const iChar ordChar =
2806 drawString_Text(run->font, 2842 linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase);
2807 init_I2(d->viewPos.x - gap_UI / 3, visPos.y), 2843 if (ordChar) {
2808 tmQuote_ColorId, 2844 drawString_Text(run->font,
2809 collect_String(newUnicodeN_String(&ordChar, 1))); 2845 init_I2(d->viewPos.x - gap_UI / 3, visPos.y),
2810 goto runDrawn; 2846 tmQuote_ColorId,
2847 collect_String(newUnicodeN_String(&ordChar, 1)));
2848 goto runDrawn;
2849 }
2811 } 2850 }
2812 } 2851 }
2813 if (run->flags & quoteBorder_GmRunFlag) { 2852 if (run->flags & quoteBorder_GmRunFlag) {
diff --git a/src/ui/keys.c b/src/ui/keys.c
index 10855b8d..ec681b6f 100644
--- a/src/ui/keys.c
+++ b/src/ui/keys.c
@@ -79,6 +79,7 @@ static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] =
79 { 42, { "Open link via home row keys", 'f', 0, "document.linkkeys arg:1" }, 0 }, 79 { 42, { "Open link via home row keys", 'f', 0, "document.linkkeys arg:1" }, 0 },
80 { 45, { "Open link in new tab via home row keys", 'f', KMOD_SHIFT, "document.linkkeys arg:1 newtab:1" }, 0 }, 80 { 45, { "Open link in new tab via home row keys", 'f', KMOD_SHIFT, "document.linkkeys arg:1 newtab:1" }, 0 },
81 { 46, { "Hover on link via home row keys", 'h', 0, "document.linkkeys arg:1 hover:1" }, 0 }, 81 { 46, { "Hover on link via home row keys", 'h', 0, "document.linkkeys arg:1 hover:1" }, 0 },
82 { 47, { "Next set of home row key links", '.', 0, "document.linkkeys more:1" }, 0 },
82 { 70, { "Zoom in", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 0 }, 83 { 70, { "Zoom in", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 0 },
83 { 71, { "Zoom out", SDLK_MINUS, KMOD_PRIMARY, "zoom.delta arg:-10" }, 0 }, 84 { 71, { "Zoom out", SDLK_MINUS, KMOD_PRIMARY, "zoom.delta arg:-10" }, 0 },
84 { 72, { "Reset zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, 0 }, 85 { 72, { "Reset zoom", SDLK_0, KMOD_PRIMARY, "zoom.set arg:100" }, 0 },