From ed36eee83c5ee9987692b2d04ae04d8c2c538adb Mon Sep 17 00:00:00 2001 From: makeworld Date: Fri, 14 May 2021 18:09:04 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Capture=20all=20scrolling=20keys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cache/page.go | 26 ++++----- config/config.go | 2 + config/default.go | 2 + config/keybindings.go | 9 ++++ default-config.toml | 2 + display/display.go | 75 -------------------------- display/help.go | 6 ++- display/tab.go | 121 +++++++++++++++++++++++++++++++++++++++--- 8 files changed, 147 insertions(+), 96 deletions(-) diff --git a/cache/page.go b/cache/page.go index 4e80b89..9c33fb4 100644 --- a/cache/page.go +++ b/cache/page.go @@ -13,7 +13,7 @@ var pages = make(map[string]*structs.Page) // The actual cache var urls = make([]string, 0) // Duplicate of the keys in the `pages` map, but in order of being added var maxPages = 0 // Max allowed number of pages in cache var maxSize = 0 // Max allowed cache size in bytes -var lock = sync.RWMutex{} +var mu = sync.RWMutex{} var timeout = time.Duration(0) // SetMaxPages sets the max number of pages the cache can hold. @@ -79,8 +79,8 @@ func AddPage(p *structs.Page) { RemovePage(urls[0]) } - lock.Lock() - defer lock.Unlock() + mu.Lock() + defer mu.Unlock() pages[p.URL] = p // Remove the URL if it was already there, then add it to the end removeURL(p.URL) @@ -90,24 +90,24 @@ func AddPage(p *structs.Page) { // RemovePage will remove a page from the cache. // Even if the page doesn't exist there will be no error. func RemovePage(url string) { - lock.Lock() - defer lock.Unlock() + mu.Lock() + defer mu.Unlock() delete(pages, url) removeURL(url) } // ClearPages removes all pages from the cache. func ClearPages() { - lock.Lock() - defer lock.Unlock() + mu.Lock() + defer mu.Unlock() pages = make(map[string]*structs.Page) urls = make([]string, 0) } // SizePages returns the approx. current size of the cache in bytes. func SizePages() int { - lock.RLock() - defer lock.RUnlock() + mu.RLock() + defer mu.RUnlock() n := 0 for _, page := range pages { n += page.Size() @@ -116,16 +116,16 @@ func SizePages() int { } func NumPages() int { - lock.RLock() - defer lock.RUnlock() + mu.RLock() + defer mu.RUnlock() return len(pages) } // GetPage returns the page struct, and a bool indicating if the page was in the cache or not. // (nil, false) is returned if the page isn't in the cache. func GetPage(url string) (*structs.Page, bool) { - lock.RLock() - defer lock.RUnlock() + mu.RLock() + defer mu.RUnlock() p, ok := pages[url] if ok && (timeout == 0 || time.Since(p.MadeAt) < timeout) { diff --git a/config/config.go b/config/config.go index 7c56a1b..e7cd7d4 100644 --- a/config/config.go +++ b/config/config.go @@ -248,6 +248,8 @@ func Init() error { viper.SetDefault("keybindings.bind_tab0", ")") viper.SetDefault("keybindings.bind_copy_page_url", "C") viper.SetDefault("keybindings.bind_copy_target_url", "c") + viper.SetDefault("keybindings.bind_beginning", []string{"Home", "g"}) + viper.SetDefault("keybindings.bind_end", []string{"End", "G"}) viper.SetDefault("keybindings.shift_numbers", "") viper.SetDefault("url-handlers.other", "off") viper.SetDefault("cache.max_size", 0) diff --git a/config/default.go b/config/default.go index 4de5f15..96d968e 100644 --- a/config/default.go +++ b/config/default.go @@ -164,6 +164,8 @@ scrollbar = "auto" # bind_add_sub # bind_copy_page_url # bind_copy_target_url +# bind_beginning: moving to beginning of page (top left) +# bind_end: same but the for the end (bottom left) [url-handlers] # Allows setting the commands to run for various URL schemes. diff --git a/config/keybindings.go b/config/keybindings.go index 348a412..f5e048a 100644 --- a/config/keybindings.go +++ b/config/keybindings.go @@ -59,6 +59,8 @@ const ( CmdAddSub CmdCopyPageURL CmdCopyTargetURL + CmdBeginning + CmdEnd ) type keyBinding struct { @@ -189,6 +191,8 @@ func KeyInit() { CmdAddSub: "keybindings.bind_add_sub", CmdCopyPageURL: "keybindings.bind_copy_page_url", CmdCopyTargetURL: "keybindings.bind_copy_target_url", + CmdBeginning: "keybindings.bind_beginning", + CmdEnd: "keybindings.bind_end", } // This is split off to allow shift_numbers to override bind_tab[1-90] // (This is needed for older configs so that the default bind_tab values @@ -212,10 +216,15 @@ func KeyInit() { tcellKeys[kname] = k } + // Set cview navigation keys to use user-set ones cview.Keys.MoveUp2 = viper.GetStringSlice(configBindings[CmdMoveUp]) cview.Keys.MoveDown2 = viper.GetStringSlice(configBindings[CmdMoveDown]) cview.Keys.MoveLeft2 = viper.GetStringSlice(configBindings[CmdMoveLeft]) cview.Keys.MoveRight2 = viper.GetStringSlice(configBindings[CmdMoveRight]) + cview.Keys.MoveFirst = viper.GetStringSlice(configBindings[CmdBeginning]) + cview.Keys.MoveFirst2 = nil + cview.Keys.MoveLast = viper.GetStringSlice(configBindings[CmdEnd]) + cview.Keys.MoveLast2 = nil for c, allb := range configBindings { for _, b := range viper.GetStringSlice(allb) { diff --git a/default-config.toml b/default-config.toml index 5cf386f..93c3f70 100644 --- a/default-config.toml +++ b/default-config.toml @@ -161,6 +161,8 @@ scrollbar = "auto" # bind_add_sub # bind_copy_page_url # bind_copy_target_url +# bind_beginning: moving to beginning of page (top left) +# bind_end: same but the for the end (bottom left) [url-handlers] # Allows setting the commands to run for various URL schemes. diff --git a/display/display.go b/display/display.go index 0368de9..0524443 100644 --- a/display/display.go +++ b/display/display.go @@ -9,7 +9,6 @@ import ( "sync" "code.rocketnine.space/tslocum/cview" - "github.com/atotto/clipboard" "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/config" @@ -302,31 +301,6 @@ func Init(version, commit, builtBy string) { case config.CmdHome: URL(viper.GetString("a-general.home")) return nil - case config.CmdBookmarks: - Bookmarks(tabs[curTab]) - tabs[curTab].addToHistory("about:bookmarks") - return nil - case config.CmdAddBookmark: - go addBookmark() - return nil - case config.CmdPgup: - tabs[curTab].pageUp() - return nil - case config.CmdPgdn: - tabs[curTab].pageDown() - return nil - case config.CmdSave: - if tabs[curTab].hasContent() { - savePath, err := downloadPage(tabs[curTab].page) - if err != nil { - Error("Download Error", fmt.Sprintf("Error saving page content: %v", err)) - } else { - Info(fmt.Sprintf("Page content saved to %s. ", savePath)) - } - } else { - Info("The current page has no content, so it couldn't be downloaded.") - } - return nil case config.CmdBottom: // Space starts typing, like Bombadillo bottomBar.SetLabel("[::b]URL/Num./Search: [::-]") @@ -340,58 +314,9 @@ func Init(version, commit, builtBy string) { bottomBar.SetText(tabs[curTab].page.URL) App.SetFocus(bottomBar) return nil - case config.CmdBack: - histBack(tabs[curTab]) - return nil - case config.CmdForward: - histForward(tabs[curTab]) - return nil - case config.CmdSub: - Subscriptions(tabs[curTab], "about:subscriptions") - tabs[curTab].addToHistory("about:subscriptions") - return nil case config.CmdAddSub: go addSubscription() return nil - case config.CmdCopyPageURL: - currentURL := tabs[curTab].page.URL - err := clipboard.WriteAll(currentURL) - if err != nil { - Error("Copy Error", err.Error()) - return nil - } - return nil - case config.CmdCopyTargetURL: - currentURL := tabs[curTab].page.URL - selectedURL := tabs[curTab].HighlightedURL() - if selectedURL == "" { - return nil - } - u, _ := url.Parse(currentURL) - copiedURL, err := u.Parse(selectedURL) - if err != nil { - err := clipboard.WriteAll(selectedURL) - if err != nil { - Error("Copy Error", err.Error()) - return nil - } - return nil - } - err = clipboard.WriteAll(copiedURL.String()) - if err != nil { - Error("Copy Error", err.Error()) - return nil - } - return nil - } - - // Number key: 1-9, 0, LINK1-LINK10 - if cmd >= config.CmdLink1 && cmd <= config.CmdLink0 { - if int(cmd) <= len(tabs[curTab].page.Links) { - // It's a valid link number - followLink(tabs[curTab], tabs[curTab].page.URL, tabs[curTab].page.Links[cmd-1]) - return nil - } } } diff --git a/display/help.go b/display/help.go index 378741b..209a6df 100644 --- a/display/help.go +++ b/display/help.go @@ -16,8 +16,8 @@ var helpCells = strings.TrimSpace( "Arrow keys, %s(left)/%s(down)/%s(up)/%s(right)\tScroll and move a page.\n" + "%s\tGo up a page in document\n" + "%s\tGo down a page in document\n" + - "g\tGo to top of document\n" + - "G\tGo to bottom of document\n" + + "%s\tGo to top of document\n" + + "%s\tGo to bottom of document\n" + "Tab\tNavigate to the next item in a popup.\n" + "Shift-Tab\tNavigate to the previous item in a popup.\n" + "%s\tGo back in the history\n" + @@ -86,6 +86,8 @@ func helpInit() { config.GetKeyBinding(config.CmdMoveRight), config.GetKeyBinding(config.CmdPgup), config.GetKeyBinding(config.CmdPgdn), + config.GetKeyBinding(config.CmdBeginning), + config.GetKeyBinding(config.CmdEnd), config.GetKeyBinding(config.CmdBack), config.GetKeyBinding(config.CmdForward), config.GetKeyBinding(config.CmdBottom), diff --git a/display/tab.go b/display/tab.go index dbe75a9..1ef5a54 100644 --- a/display/tab.go +++ b/display/tab.go @@ -1,10 +1,13 @@ package display import ( + "fmt" + "net/url" "strconv" "strings" "code.rocketnine.space/tslocum/cview" + "github.com/atotto/clipboard" "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/structs" @@ -124,11 +127,96 @@ func makeNewTab() *tab { t.view.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { // Capture scrolling and change the left margin size accordingly, see #197 // This was also touched by #222 + // This also captures any tab-specific events now + + if t.mode != tabModeDone { + // Any events that should be caught when the tab is loading is handled in display.go + return nil + } + + cmd := config.TranslateKeyEvent(event) + + // Cmds that aren't single row/column scrolling + switch cmd { + case config.CmdBookmarks: + Bookmarks(&t) + t.addToHistory("about:bookmarks") + return nil + case config.CmdAddBookmark: + go addBookmark() + return nil + case config.CmdPgup: + t.pageUp() + return nil + case config.CmdPgdn: + t.pageDown() + return nil + case config.CmdSave: + if t.hasContent() { + savePath, err := downloadPage(t.page) + if err != nil { + Error("Download Error", fmt.Sprintf("Error saving page content: %v", err)) + } else { + Info(fmt.Sprintf("Page content saved to %s. ", savePath)) + } + } else { + Info("The current page has no content, so it couldn't be downloaded.") + } + return nil + case config.CmdBack: + histBack(&t) + return nil + case config.CmdForward: + histForward(&t) + return nil + case config.CmdSub: + Subscriptions(&t, "about:subscriptions") + tabs[curTab].addToHistory("about:subscriptions") + return nil + case config.CmdCopyPageURL: + currentURL := tabs[curTab].page.URL + err := clipboard.WriteAll(currentURL) + if err != nil { + Error("Copy Error", err.Error()) + return nil + } + return nil + case config.CmdCopyTargetURL: + currentURL := t.page.URL + selectedURL := t.HighlightedURL() + if selectedURL == "" { + return nil + } + u, _ := url.Parse(currentURL) + copiedURL, err := u.Parse(selectedURL) + if err != nil { + err := clipboard.WriteAll(selectedURL) + if err != nil { + Error("Copy Error", err.Error()) + return nil + } + return nil + } + err = clipboard.WriteAll(copiedURL.String()) + if err != nil { + Error("Copy Error", err.Error()) + return nil + } + return nil + } + // Number key: 1-9, 0, LINK1-LINK10 + if cmd >= config.CmdLink1 && cmd <= config.CmdLink0 { + if int(cmd) <= len(t.page.Links) { + // It's a valid link number + followLink(&t, t.page.URL, t.page.Links[cmd-1]) + return nil + } + } + + // Scrolling stuff key := event.Key() mod := event.Modifiers() - cmd := config.TranslateKeyEvent(event) - height, width := t.view.GetBufferSize() _, _, boxW, boxH := t.view.GetInnerRect() @@ -175,6 +263,18 @@ func makeNewTab() *tab { t.page.Row++ } return event + } else if cmd == config.CmdBeginning { + t.page.Row = 0 + // This is required because cview will also set the column (incorrectly) + // if it handles this event itself + t.applyScroll() + App.Draw() + return nil + } else if cmd == config.CmdEnd { + t.page.Row = height + t.applyScroll() + App.Draw() + return nil } else { // Some other key, stop processing it return event @@ -202,14 +302,23 @@ func (t *tab) addToHistory(u string) { // pageUp scrolls up 75% of the height of the terminal, like Bombadillo. func (t *tab) pageUp() { - row, col := t.view.GetScrollOffset() - t.view.ScrollTo(row-(termH/4)*3, col) + t.page.Row -= (termH / 4) * 3 + if t.page.Row < 0 { + t.page.Row = 0 + } + t.applyScroll() } // pageDown scrolls down 75% of the height of the terminal, like Bombadillo. func (t *tab) pageDown() { - row, col := t.view.GetScrollOffset() - t.view.ScrollTo(row+(termH/4)*3, col) + height, _ := t.view.GetBufferSize() + + t.page.Row += (termH / 4) * 3 + if t.page.Row > height { + t.page.Row = height + } + + t.applyScroll() } // hasContent returns false when the tab's page is malformed,