Add option to keep row focus on sort

pull/94/head v1.6.2
Miguel Mota 3 years ago
parent 269b9ad5b7
commit 0a0530de5a

@ -53,6 +53,7 @@ type State struct {
hideMarketbar bool hideMarketbar bool
hideChart bool hideChart bool
hideStatusbar bool hideStatusbar bool
keepRowFocusOnSort bool
lastSelectedRowIndex int lastSelectedRowIndex int
marketBarHeight int marketBarHeight int
page int page int
@ -197,6 +198,7 @@ func NewCointop(config *Config) (*Cointop, error) {
} }
ct := &Cointop{ ct := &Cointop{
// defaults
apiChoice: CoinGecko, apiChoice: CoinGecko,
apiKeys: new(APIKeys), apiKeys: new(APIKeys),
forceRefresh: make(chan bool), forceRefresh: make(chan bool),
@ -222,6 +224,7 @@ func NewCointop(config *Config) (*Cointop, error) {
hideMarketbar: config.HideMarketbar, hideMarketbar: config.HideMarketbar,
hideChart: config.HideChart, hideChart: config.HideChart,
hideStatusbar: config.HideStatusbar, hideStatusbar: config.HideStatusbar,
keepRowFocusOnSort: false,
marketBarHeight: 1, marketBarHeight: 1,
onlyTable: config.OnlyTable, onlyTable: config.OnlyTable,
refreshRate: 60 * time.Second, refreshRate: 60 * time.Second,

@ -54,7 +54,7 @@ func (ct *Cointop) SetupConfig() error {
if err := ct.parseConfig(); err != nil { if err := ct.parseConfig(); err != nil {
return err return err
} }
if err := ct.loadTableColumnsFromConfig(); err != nil { if err := ct.loadTableConfig(); err != nil {
return err return err
} }
if err := ct.loadShortcutsFromConfig(); err != nil { if err := ct.loadShortcutsFromConfig(); err != nil {
@ -280,6 +280,8 @@ func (ct *Cointop) configToToml() ([]byte, error) {
var coinsTableColumnsIfc interface{} = ct.State.coinsTableColumns var coinsTableColumnsIfc interface{} = ct.State.coinsTableColumns
tableMapIfc := map[string]interface{}{} tableMapIfc := map[string]interface{}{}
tableMapIfc["columns"] = coinsTableColumnsIfc tableMapIfc["columns"] = coinsTableColumnsIfc
var keepRowFocusOnSortIfc interface{} = ct.State.keepRowFocusOnSort
tableMapIfc["keep_row_focus_on_sort"] = keepRowFocusOnSortIfc
var inputs = &config{ var inputs = &config{
API: apiChoiceIfc, API: apiChoiceIfc,
@ -306,6 +308,20 @@ func (ct *Cointop) configToToml() ([]byte, error) {
return b.Bytes(), nil return b.Bytes(), nil
} }
// LoadTableConfig loads table config from toml config into state struct
func (ct *Cointop) loadTableConfig() error {
err := ct.loadTableColumnsFromConfig()
if err != nil {
return err
}
keepRowFocusOnSortIfc, ok := ct.config.Table["keep_row_focus_on_sort"]
if ok {
ct.State.keepRowFocusOnSort = keepRowFocusOnSortIfc.(bool)
}
return nil
}
// LoadTableColumnsFromConfig loads preferred coins table columns from config file to struct // LoadTableColumnsFromConfig loads preferred coins table columns from config file to struct
func (ct *Cointop) loadTableColumnsFromConfig() error { func (ct *Cointop) loadTableColumnsFromConfig() error {
ct.debuglog("loadTableColumnsFromConfig()") ct.debuglog("loadTableColumnsFromConfig()")
@ -328,6 +344,7 @@ func (ct *Cointop) loadTableColumnsFromConfig() error {
ct.State.coinsTableColumns = columns ct.State.coinsTableColumns = columns
} }
} }
return nil return nil
} }

@ -1,6 +1,7 @@
package cointop package cointop
import ( import (
"fmt"
"math" "math"
) )
@ -45,7 +46,7 @@ func (ct *Cointop) SetPage(page int) int {
// CursorDown moves the cursor one row down // CursorDown moves the cursor one row down
func (ct *Cointop) CursorDown() error { func (ct *Cointop) CursorDown() error {
ct.debuglog("cursorDown()") ct.debuglog("cursorDown()")
// NOTE: return if already at the bottom // return if already at the bottom
if ct.IsLastRow() { if ct.IsLastRow() {
return nil return nil
} }
@ -65,7 +66,7 @@ func (ct *Cointop) CursorDown() error {
// CursorUp moves the cursor one row up // CursorUp moves the cursor one row up
func (ct *Cointop) CursorUp() error { func (ct *Cointop) CursorUp() error {
ct.debuglog("cursorUp()") ct.debuglog("cursorUp()")
// NOTE: return if already at the top // return if already at the top
if ct.IsFirstRow() { if ct.IsFirstRow() {
return nil return nil
} }
@ -86,7 +87,7 @@ func (ct *Cointop) CursorUp() error {
// PageDown moves the cursor one page down // PageDown moves the cursor one page down
func (ct *Cointop) PageDown() error { func (ct *Cointop) PageDown() error {
ct.debuglog("pageDown()") ct.debuglog("pageDown()")
// NOTE: return if already at the bottom // return if already at the bottom
if ct.IsLastRow() { if ct.IsLastRow() {
return nil return nil
} }
@ -94,24 +95,24 @@ func (ct *Cointop) PageDown() error {
ox, oy := ct.Views.Table.Origin() // this is prev origin position ox, oy := ct.Views.Table.Origin() // this is prev origin position
cx := ct.Views.Table.CursorX() // relative cursor position cx := ct.Views.Table.CursorX() // relative cursor position
sy := ct.Views.Table.Height() // rows in visible view sy := ct.Views.Table.Height() // rows in visible view
k := oy + sy y := oy + sy
l := ct.TableRowsLen() l := ct.TableRowsLen()
// end of table // end of table
if (oy + sy + sy) > l { if (oy + sy + sy) > l {
k = l - sy y = l - sy
} }
// select last row if next jump is out of bounds // select last row if next jump is out of bounds
if k < 0 { if y < 0 {
k = 0 y = 0
sy = l sy = l
} }
if err := ct.Views.Table.SetOrigin(ox, k); err != nil { if err := ct.Views.Table.SetOrigin(ox, y); err != nil {
return err return err
} }
// move cursor to last line if can't scroll further // move cursor to last line if can't scroll further
if k == oy { if y == oy {
if err := ct.Views.Table.SetCursor(cx, sy-1); err != nil { if err := ct.Views.Table.SetCursor(cx, sy-1); err != nil {
return err return err
} }
@ -123,7 +124,7 @@ func (ct *Cointop) PageDown() error {
// PageUp moves the cursor one page up // PageUp moves the cursor one page up
func (ct *Cointop) PageUp() error { func (ct *Cointop) PageUp() error {
ct.debuglog("pageUp()") ct.debuglog("pageUp()")
// NOTE: return if already at the top // return if already at the top
if ct.IsFirstRow() { if ct.IsFirstRow() {
return nil return nil
} }
@ -151,7 +152,7 @@ func (ct *Cointop) PageUp() error {
// NavigateFirstLine moves the cursor to the first row of the table // NavigateFirstLine moves the cursor to the first row of the table
func (ct *Cointop) NavigateFirstLine() error { func (ct *Cointop) NavigateFirstLine() error {
ct.debuglog("navigateFirstLine()") ct.debuglog("navigateFirstLine()")
// NOTE: return if already at the top // return if already at the top
if ct.IsFirstRow() { if ct.IsFirstRow() {
return nil return nil
} }
@ -172,7 +173,7 @@ func (ct *Cointop) NavigateFirstLine() error {
// NavigateLastLine moves the cursor to the last row of the table // NavigateLastLine moves the cursor to the last row of the table
func (ct *Cointop) NavigateLastLine() error { func (ct *Cointop) NavigateLastLine() error {
ct.debuglog("navigateLastLine()") ct.debuglog("navigateLastLine()")
// NOTE: return if already at the bottom // return if already at the bottom
if ct.IsLastRow() { if ct.IsLastRow() {
return nil return nil
} }
@ -196,7 +197,7 @@ func (ct *Cointop) NavigateLastLine() error {
// NavigatePageFirstLine moves the cursor to the visible first row of the table // NavigatePageFirstLine moves the cursor to the visible first row of the table
func (ct *Cointop) NavigatePageFirstLine() error { func (ct *Cointop) NavigatePageFirstLine() error {
ct.debuglog("navigatePageFirstLine()") ct.debuglog("navigatePageFirstLine()")
// NOTE: return if already at the correct line // return if already at the correct line
if ct.IsPageFirstLine() { if ct.IsPageFirstLine() {
return nil return nil
} }
@ -212,7 +213,7 @@ func (ct *Cointop) NavigatePageFirstLine() error {
// NavigatePageMiddleLine moves the cursor to the visible middle row of the table // NavigatePageMiddleLine moves the cursor to the visible middle row of the table
func (ct *Cointop) NavigatePageMiddleLine() error { func (ct *Cointop) NavigatePageMiddleLine() error {
ct.debuglog("navigatePageMiddleLine()") ct.debuglog("navigatePageMiddleLine()")
// NOTE: return if already at the correct line // return if already at the correct line
if ct.IsPageMiddleLine() { if ct.IsPageMiddleLine() {
return nil return nil
} }
@ -229,7 +230,7 @@ func (ct *Cointop) NavigatePageMiddleLine() error {
// NavigatePageLastLine moves the cursor to the visible last row of the table // NavigatePageLastLine moves the cursor to the visible last row of the table
func (ct *Cointop) navigatePageLastLine() error { func (ct *Cointop) navigatePageLastLine() error {
ct.debuglog("navigatePageLastLine()") ct.debuglog("navigatePageLastLine()")
// NOTE: return if already at the correct line // return if already at the correct line
if ct.IsPageLastLine() { if ct.IsPageLastLine() {
return nil return nil
} }
@ -247,7 +248,7 @@ func (ct *Cointop) navigatePageLastLine() error {
func (ct *Cointop) NextPage() error { func (ct *Cointop) NextPage() error {
ct.debuglog("nextPage()") ct.debuglog("nextPage()")
// NOTE: return if already at the last page // return if already at the last page
if ct.IsLastPage() { if ct.IsLastPage() {
return nil return nil
} }
@ -262,7 +263,7 @@ func (ct *Cointop) NextPage() error {
func (ct *Cointop) PrevPage() error { func (ct *Cointop) PrevPage() error {
ct.debuglog("prevPage()") ct.debuglog("prevPage()")
// NOTE: return if already at the first page // return if already at the first page
if ct.IsFirstPage() { if ct.IsFirstPage() {
return nil return nil
} }
@ -297,7 +298,7 @@ func (ct *Cointop) PrevPageTop() error {
func (ct *Cointop) FirstPage() error { func (ct *Cointop) FirstPage() error {
ct.debuglog("firstPage()") ct.debuglog("firstPage()")
// NOTE: return if already at the first page // return if already at the first page
if ct.IsFirstPage() { if ct.IsFirstPage() {
return nil return nil
} }
@ -312,7 +313,7 @@ func (ct *Cointop) FirstPage() error {
func (ct *Cointop) LastPage() error { func (ct *Cointop) LastPage() error {
ct.debuglog("lastPage()") ct.debuglog("lastPage()")
// NOTE: return if already at the last page // return if already at the last page
if ct.IsLastPage() { if ct.IsLastPage() {
return nil return nil
} }
@ -402,26 +403,57 @@ func (ct *Cointop) GoToGlobalIndex(idx int) error {
return nil return nil
} }
// HighlightRow highlights the row at index // HighlightRow highlights the row at index within page
func (ct *Cointop) HighlightRow(idx int) error { func (ct *Cointop) HighlightRow(pageRowIndex int) error {
ct.debuglog("highlightRow()") ct.debuglog("highlightRow()")
ct.Views.Table.SetOrigin(0, 0) ct.Views.Table.SetOrigin(0, 0)
ct.Views.Table.SetCursor(0, 0) ct.Views.Table.SetCursor(0, 0)
ox := ct.Views.Table.OriginX() ox := ct.Views.Table.OriginX()
cx := ct.Views.Table.CursorX() cx := ct.Views.Table.CursorX()
sy := ct.Views.Table.Height() h := ct.Views.Table.Height()
perpage := ct.TotalPerPage() perpage := ct.TotalPerPage()
p := idx % perpage oy := 0
oy := (p / sy) * sy cy := 0
cy := p % sy if h > 0 {
_ = perpage
cy = pageRowIndex % h
oy = pageRowIndex - cy
// end of page
if pageRowIndex >= perpage-h {
oy = perpage - h
cy = h - (perpage - pageRowIndex)
}
}
ct.debuglog(fmt.Sprintf("highlightRow idx:%v h:%v cy:%v oy:%v", pageRowIndex, h, cy, oy))
if oy > 0 { if oy > 0 {
ct.Views.Table.SetOrigin(ox, oy) ct.Views.Table.SetOrigin(ox, oy)
} }
ct.Views.Table.SetCursor(cx, cy) ct.Views.Table.SetCursor(cx, cy)
return nil return nil
} }
// GoToCoinRow navigates to the row of the matched coin
func (ct *Cointop) GoToCoinRow(coin *Coin) error {
ct.debuglog("goToCoinRow()")
if coin == nil {
return nil
}
idx := ct.GetGlobalCoinIndex(coin)
return ct.GoToGlobalIndex(idx)
}
// GetGlobalCoinIndex returns the index of the coin in from the coins list
func (ct *Cointop) GetGlobalCoinIndex(coin *Coin) int {
var idx int
for i, v := range ct.State.allCoins {
if v == coin {
idx = i
break
}
}
return idx
}
// CursorDownOrNextPage moves the cursor down one row or goes to the next page if cursor is on the last row // CursorDownOrNextPage moves the cursor down one row or goes to the next page if cursor is on the last row
func (ct *Cointop) CursorDownOrNextPage() error { func (ct *Cointop) CursorDownOrNextPage() error {
ct.debuglog("CursorDownOrNextPage()") ct.debuglog("CursorDownOrNextPage()")

@ -138,7 +138,18 @@ func (ct *Cointop) SortToggle(sortBy string, desc bool) error {
func (ct *Cointop) Sortfn(sortBy string, desc bool) func(g *gocui.Gui, v *gocui.View) error { func (ct *Cointop) Sortfn(sortBy string, desc bool) func(g *gocui.Gui, v *gocui.View) error {
ct.debuglog("sortfn()") ct.debuglog("sortfn()")
return func(g *gocui.Gui, v *gocui.View) error { return func(g *gocui.Gui, v *gocui.View) error {
return ct.SortToggle(sortBy, desc) coin := ct.HighlightedRowCoin()
err := ct.SortToggle(sortBy, desc)
if err != nil {
return err
}
if ct.State.keepRowFocusOnSort {
err = ct.GoToCoinRow(coin)
if err != nil {
return err
}
}
return nil
} }
} }

@ -63,10 +63,14 @@ func (ct *Cointop) RefreshTable() error {
} }
ct.table.HideColumHeaders = true ct.table.HideColumHeaders = true
// highlight last row if current row is out of bounds (can happen when switching views) // highlight last row if current row is out of bounds (can happen when switching views).
currentrow := ct.HighlightedRowIndex() // make sure to not highlight row when actively navigating, otherwise
if len(ct.State.coins) > currentrow { // table will appear glitchy since this is method is async.
ct.HighlightRow(currentrow) if ct.State.lastSelectedView != "" && ct.State.lastSelectedView != ct.State.selectedView {
currentRowIdx := ct.HighlightedRowIndex()
if len(ct.State.coins) > currentRowIdx {
ct.HighlightRow(currentRowIdx)
}
} }
ct.UpdateUI(func() error { ct.UpdateUI(func() error {
@ -161,7 +165,7 @@ func (ct *Cointop) GetTableCoinsSlice() []*Coin {
return sliced return sliced
} }
// HighlightedRowIndex returns the index of the highlighted row // HighlightedRowIndex returns the index of the highlighted row within the per-page limit
func (ct *Cointop) HighlightedRowIndex() int { func (ct *Cointop) HighlightedRowIndex() int {
ct.debuglog("HighlightedRowIndex()") ct.debuglog("HighlightedRowIndex()")
oy := ct.Views.Table.OriginY() oy := ct.Views.Table.OriginY()

@ -33,7 +33,7 @@ func NewCoinGecko() *Service {
client := gecko.NewClient(nil) client := gecko.NewClient(nil)
svc := &Service{ svc := &Service{
client: client, client: client,
maxResultsPerPage: 250, maxResultsPerPage: 250, // max is 250
maxPages: 10, maxPages: 10,
cacheMap: sync.Map{}, cacheMap: sync.Map{},
} }

@ -89,6 +89,9 @@ func (c *ChartPlot) GetChartPoints(width int) [][]rune {
func interpolateData(data []float64, width int) []float64 { func interpolateData(data []float64, width int) []float64 {
var res []float64 var res []float64
if len(data) == 0 {
return res
}
stepFactor := float64(len(data)-1) / float64(width-1) stepFactor := float64(len(data)-1) / float64(width-1)
res = append(res, data[0]) res = append(res, data[0])
for i := 1; i < width-1; i++ { for i := 1; i < width-1; i++ {

Loading…
Cancel
Save