From e7531ca6358620dc28a65d47765f4a1398f43de2 Mon Sep 17 00:00:00 2001 From: Miguel Mota Date: Sun, 25 Apr 2021 17:29:59 -0700 Subject: [PATCH] Add keybinding to toggle chart fullscreen --- cmd/commands/root.go | 6 +++ cointop/chart.go | 42 ++++++++++++++++++- cointop/cointop.go | 13 ++++++ cointop/default_shortcuts.go | 1 + cointop/keybindings.go | 7 +++- cointop/layout.go | 78 +++++++++++++++++++++++++----------- cointop/table.go | 9 +++++ docs/content/chart.md | 2 + 8 files changed, 132 insertions(+), 26 deletions(-) diff --git a/cmd/commands/root.go b/cmd/commands/root.go index 53c129d..4aab35f 100644 --- a/cmd/commands/root.go +++ b/cmd/commands/root.go @@ -18,8 +18,10 @@ func RootCmd() *cobra.Command { reset := getEnvBool("COINTOP_RESET") hideMarketbar := getEnvBool("COINTOP_HIDE_MARKETBAR") hideChart := getEnvBool("COINTOP_HIDE_CHART") + hideTable := getEnvBool("COINTOP_HIDE_TABLE") hideStatusbar := getEnvBool("COINTOP_HIDE_STATUSBAR") onlyTable := getEnvBool("COINTOP_ONLY_TABLE") + onlyChart := getEnvBool("COINTOP_ONLY_CHART") silent := getEnvBool("COINTOP_SILENT") noCache := getEnvBool("COINTOP_NO_CACHE") colorscheme := os.Getenv("COINTOP_COLORSCHEME") @@ -97,8 +99,10 @@ See git.io/cointop for more info.`, Colorscheme: colorscheme, HideMarketbar: hideMarketbar, HideChart: hideChart, + HideTable: hideTable, HideStatusbar: hideStatusbar, OnlyTable: onlyTable, + OnlyChart: onlyChart, RefreshRate: refreshRateP, PerPage: perPage, MaxPages: maxPages, @@ -117,8 +121,10 @@ See git.io/cointop for more info.`, rootCmd.Flags().BoolVarP(&reset, "reset", "", reset, "Reset the config. Make sure to backup any relevant changes first!") rootCmd.Flags().BoolVarP(&hideMarketbar, "hide-marketbar", "", hideMarketbar, "Hide the top marketbar") rootCmd.Flags().BoolVarP(&hideChart, "hide-chart", "", hideChart, "Hide the chart view") + rootCmd.Flags().BoolVarP(&hideTable, "hide-table", "", hideTable, "Hide the table view") rootCmd.Flags().BoolVarP(&hideStatusbar, "hide-statusbar", "", hideStatusbar, "Hide the bottom statusbar") rootCmd.Flags().BoolVarP(&onlyTable, "only-table", "", onlyTable, "Show only the table. Hides the chart and top and bottom bars") + rootCmd.Flags().BoolVarP(&onlyChart, "only-chart", "", onlyChart, "Show only the chart. Hides the table and top and bottom bars") rootCmd.Flags().BoolVarP(&silent, "silent", "s", silent, "Silence log ouput") rootCmd.Flags().BoolVarP(&noCache, "no-cache", "", noCache, "No cache") rootCmd.Flags().UintVarP(&refreshRate, "refresh-rate", "r", 60, "Refresh rate in seconds. Set to 0 to not auto-refresh") diff --git a/cointop/chart.go b/cointop/chart.go index 935e330..354239b 100644 --- a/cointop/chart.go +++ b/cointop/chart.go @@ -88,6 +88,7 @@ func (ct *Cointop) UpdateChart() error { } ct.UpdateUI(func() error { + ct.Views.Chart.Clear() return ct.Views.Chart.Update(ct.colorscheme.Chart(body)) }) @@ -276,6 +277,7 @@ func (ct *Cointop) ShortenChart() error { return nil } ct.State.chartHeight = candidate + ct.State.lastChartHeight = ct.State.chartHeight go ct.UpdateChart() return nil @@ -284,11 +286,12 @@ func (ct *Cointop) ShortenChart() error { // EnlargeChart increases the chart height by one row func (ct *Cointop) EnlargeChart() error { ct.debuglog("EnlargeChart()") - candidate := ct.State.chartHeight + 1 + candidate := ct.State.lastChartHeight + 1 if candidate > 30 { return nil } ct.State.chartHeight = candidate + ct.State.lastChartHeight = ct.State.chartHeight go ct.UpdateChart() return nil @@ -394,3 +397,40 @@ func (ct *Cointop) ChartWidth() int { return w } + +// ToggleChartFullscreen toggles the chart fullscreen mode +func (ct *Cointop) ToggleChartFullscreen() error { + ct.debuglog("ToggleChartFullscreen()") + ct.State.onlyChart = !ct.State.onlyChart + ct.State.onlyTable = false + if !ct.State.onlyChart { + // NOTE: cached values are initial config settings. + // If the only-chart config was set then toggle + // all other initial hidden views. + onlyChart, _ := ct.cache.Get("onlyChart") + + if onlyChart.(bool) { + ct.State.hideMarketbar = false + ct.State.hideChart = false + ct.State.hideTable = false + ct.State.hideStatusbar = false + } else { + // NOTE: cached values store initial hidden views preferences. + hideMarketbar, _ := ct.cache.Get("hideMarketbar") + ct.State.hideMarketbar = hideMarketbar.(bool) + hideChart, _ := ct.cache.Get("hideChart") + ct.State.hideChart = hideChart.(bool) + hideTable, _ := ct.cache.Get("hideTable") + ct.State.hideTable = hideTable.(bool) + hideStatusbar, _ := ct.cache.Get("hideStatusbar") + ct.State.hideStatusbar = hideStatusbar.(bool) + } + } + + go func() { + ct.UpdateTable() + ct.UpdateChart() + }() + + return nil +} diff --git a/cointop/cointop.go b/cointop/cointop.go index 46def74..40dccf8 100644 --- a/cointop/cointop.go +++ b/cointop/cointop.go @@ -53,6 +53,7 @@ type State struct { helpVisible bool hideMarketbar bool hideChart bool + hideTable bool hideStatusbar bool keepRowFocusOnSort bool lastSelectedRowIndex int @@ -75,9 +76,11 @@ type State struct { sortBy string tableOffsetX int onlyTable bool + onlyChart bool tableColumnWidths sync.Map tableColumnAlignLeft sync.Map chartHeight int + lastChartHeight int priceAlerts *PriceAlerts priceAlertEditID string priceAlertNewID string @@ -152,9 +155,11 @@ type Config struct { NoPrompts bool HideMarketbar bool HideChart bool + HideTable bool HideStatusbar bool NoCache bool OnlyTable bool + OnlyChart bool RefreshRate *uint PerPage uint MaxPages uint @@ -244,11 +249,13 @@ func NewCointop(config *Config) (*Cointop, error) { favoritesTableColumns: DefaultCoinTableHeaders, hideMarketbar: config.HideMarketbar, hideChart: config.HideChart, + hideTable: config.HideTable, hideStatusbar: config.HideStatusbar, keepRowFocusOnSort: false, marketBarHeight: 1, maxPages: int(maxPages), onlyTable: config.OnlyTable, + onlyChart: config.OnlyChart, refreshRate: 60 * time.Second, selectedChartRange: DefaultChartRange, shortcutKeys: DefaultShortcuts(), @@ -260,6 +267,7 @@ func NewCointop(config *Config) (*Cointop, error) { }, portfolioTableColumns: DefaultPortfolioTableHeaders, chartHeight: 10, + lastChartHeight: 10, tableOffsetX: 0, tableColumnWidths: sync.Map{}, tableColumnAlignLeft: sync.Map{}, @@ -289,8 +297,13 @@ func NewCointop(config *Config) (*Cointop, error) { } ct.cache.Set("onlyTable", ct.State.onlyTable, cache.NoExpiration) + if ct.State.onlyTable && ct.State.onlyChart { + ct.State.onlyChart = false + } + ct.cache.Set("onlyChart", ct.State.onlyChart, cache.NoExpiration) ct.cache.Set("hideMarketbar", ct.State.hideMarketbar, cache.NoExpiration) ct.cache.Set("hideChart", ct.State.hideChart, cache.NoExpiration) + ct.cache.Set("hideTable", ct.State.hideTable, cache.NoExpiration) ct.cache.Set("hideStatusbar", ct.State.hideStatusbar, cache.NoExpiration) if config.RefreshRate != nil { diff --git a/cointop/default_shortcuts.go b/cointop/default_shortcuts.go index cec8486..96fe3e7 100644 --- a/cointop/default_shortcuts.go +++ b/cointop/default_shortcuts.go @@ -28,6 +28,7 @@ func DefaultShortcuts() map[string]string { "ctrl+u": "page_up", "ctrl+j": "enlarge_chart", "ctrl+k": "shorten_chart", + "|": "toggle_chart_fullscreen", "alt+up": "sort_column_asc", "alt+down": "sort_column_desc", "alt+left": "sort_left_column", diff --git a/cointop/keybindings.go b/cointop/keybindings.go index fa4056a..67def1a 100644 --- a/cointop/keybindings.go +++ b/cointop/keybindings.go @@ -12,8 +12,8 @@ func (ct *Cointop) ParseKeys(s string) (interface{}, gocui.Modifier) { mod := gocui.ModNone split := strings.Split(s, "+") if len(split) > 1 { - m := strings.ToLower(split[0]) - k := strings.ToLower(split[1]) + m := strings.ToLower(strings.TrimSpace(split[0])) + k := strings.ToLower(strings.TrimSpace(split[1])) if m == "alt" { mod = gocui.ModAlt s = k @@ -309,6 +309,9 @@ func (ct *Cointop) Keybindings(g *gocui.Gui) error { case "toggle_table_fullscreen": fn = ct.Keyfn(ct.ToggleTableFullscreen) view = "" + case "toggle_chart_fullscreen": + fn = ct.Keyfn(ct.ToggleChartFullscreen) + view = "" case "enlarge_chart": fn = ct.Keyfn(ct.EnlargeChart) case "shorten_chart": diff --git a/cointop/layout.go b/cointop/layout.go index a41c635..19597fd 100644 --- a/cointop/layout.go +++ b/cointop/layout.go @@ -18,13 +18,22 @@ func (ct *Cointop) layout() error { topOffset := 0 headerHeight := 1 marketbarHeight := ct.State.marketBarHeight - chartHeight := ct.State.chartHeight + chartHeight := ct.State.lastChartHeight statusbarHeight := 1 if ct.State.onlyTable { ct.State.hideMarketbar = true ct.State.hideChart = true + ct.State.hideTable = false ct.State.hideStatusbar = true + ct.State.onlyChart = false + marketbarHeight = 0 + } else if ct.State.onlyChart { + ct.State.hideMarketbar = true + ct.State.hideChart = false + ct.State.hideTable = true + ct.State.hideStatusbar = true + ct.State.onlyTable = false marketbarHeight = 0 } @@ -73,7 +82,15 @@ func (ct *Cointop) layout() error { ct.Views.Chart.SetBacking(nil) } } else { - if err := ct.ui.SetView(ct.Views.Chart, 0, topOffset-1, maxX, topOffset+chartHeight); err != nil { + chartTopOffset := topOffset - 1 + if ct.State.hideStatusbar { + chartTopOffset = topOffset + } + if ct.State.onlyChart { + chartHeight = maxY - topOffset + } + ct.State.chartHeight = chartHeight + if err := ct.ui.SetView(ct.Views.Chart, 0, chartTopOffset, maxX, topOffset+chartHeight); err != nil { ct.Views.Chart.Clear() ct.Views.Chart.SetFrame(false) ct.Views.Chart.SetFgColor(ct.colorscheme.gocuiFgColor(ct.Views.Chart.Name())) @@ -90,29 +107,44 @@ func (ct *Cointop) layout() error { } } - tableOffsetX := ct.State.tableOffsetX - topOffset = topOffset + chartHeight - if err := ct.ui.SetView(ct.Views.TableHeader, tableOffsetX, topOffset-1, maxX, topOffset+1); err != nil { - ct.Views.TableHeader.SetFrame(false) - ct.Views.TableHeader.SetFgColor(ct.colorscheme.gocuiFgColor(ct.Views.TableHeader.Name())) - ct.Views.TableHeader.SetBgColor(ct.colorscheme.gocuiBgColor(ct.Views.TableHeader.Name())) - go ct.UpdateTableHeader() - } + if ct.State.hideTable { + if ct.Views.TableHeader.Backing() != nil { + if err := ct.g.DeleteView(ct.Views.TableHeader.Name()); err != nil { + return err + } + ct.Views.TableHeader.SetBacking(nil) + } + if ct.Views.Table.Backing() != nil { + if err := ct.g.DeleteView(ct.Views.Table.Name()); err != nil { + return err + } + ct.Views.Table.SetBacking(nil) + } + } else { + tableOffsetX := ct.State.tableOffsetX + topOffset = topOffset + chartHeight + if err := ct.ui.SetView(ct.Views.TableHeader, tableOffsetX, topOffset-1, maxX, topOffset+1); err != nil { + ct.Views.TableHeader.SetFrame(false) + ct.Views.TableHeader.SetFgColor(ct.colorscheme.gocuiFgColor(ct.Views.TableHeader.Name())) + ct.Views.TableHeader.SetBgColor(ct.colorscheme.gocuiBgColor(ct.Views.TableHeader.Name())) + go ct.UpdateTableHeader() + } - topOffset = topOffset + headerHeight - if err := ct.ui.SetView(ct.Views.Table, tableOffsetX, topOffset-1, maxX, maxY-statusbarHeight); err != nil { - ct.Views.Table.SetFrame(false) - ct.Views.Table.SetHighlight(true) - ct.Views.Table.SetSelFgColor(ct.colorscheme.gocuiFgColor("table_row_active")) - ct.Views.Table.SetSelBgColor(ct.colorscheme.gocuiBgColor("table_row_active")) - _, found := ct.cache.Get("allCoinsSlugMap") - if found { - ct.cache.Delete("allCoinsSlugMap") + topOffset = topOffset + headerHeight + if err := ct.ui.SetView(ct.Views.Table, tableOffsetX, topOffset-1, maxX, maxY-statusbarHeight); err != nil { + ct.Views.Table.SetFrame(false) + ct.Views.Table.SetHighlight(true) + ct.Views.Table.SetSelFgColor(ct.colorscheme.gocuiFgColor("table_row_active")) + ct.Views.Table.SetSelBgColor(ct.colorscheme.gocuiBgColor("table_row_active")) + _, found := ct.cache.Get("allCoinsSlugMap") + if found { + ct.cache.Delete("allCoinsSlugMap") + } + go func() { + ct.UpdateCoins() + ct.UpdateTable() + }() } - go func() { - ct.UpdateCoins() - ct.UpdateTable() - }() } if !ct.State.hideStatusbar { diff --git a/cointop/table.go b/cointop/table.go index ac58e89..48b7cb5 100644 --- a/cointop/table.go +++ b/cointop/table.go @@ -228,6 +228,7 @@ func (ct *Cointop) RowLinkShort() string { func (ct *Cointop) ToggleTableFullscreen() error { ct.debuglog("ToggleTableFullscreen()") ct.State.onlyTable = !ct.State.onlyTable + ct.State.onlyChart = false if !ct.State.onlyTable { // NOTE: cached values are initial config settings. // If the only-table config was set then toggle @@ -237,6 +238,7 @@ func (ct *Cointop) ToggleTableFullscreen() error { if onlyTable.(bool) { ct.State.hideMarketbar = false ct.State.hideChart = false + ct.State.hideTable = false ct.State.hideStatusbar = false } else { // NOTE: cached values store initial hidden views preferences. @@ -244,11 +246,18 @@ func (ct *Cointop) ToggleTableFullscreen() error { ct.State.hideMarketbar = hideMarketbar.(bool) hideChart, _ := ct.cache.Get("hideChart") ct.State.hideChart = hideChart.(bool) + hideTable, _ := ct.cache.Get("hideTable") + ct.State.hideTable = hideTable.(bool) hideStatusbar, _ := ct.cache.Get("hideStatusbar") ct.State.hideStatusbar = hideStatusbar.(bool) } } + go func() { + ct.UpdateTable() + ct.UpdateChart() + }() + return nil } diff --git a/docs/content/chart.md b/docs/content/chart.md index c38bc26..50c660d 100644 --- a/docs/content/chart.md +++ b/docs/content/chart.md @@ -31,6 +31,8 @@ Press Ctrl+j to increase the chart height. Press Ctrl+k to decrease the chart height. +Press | to toggle chart fullscreen. + ## Hide chart Run cointop with the `--hide-chart` flag to always keep the chart hidden.