From 51cb44269832e9cceada2051b3669b72cd42dfa5 Mon Sep 17 00:00:00 2001 From: Miguel Mota Date: Sat, 21 Nov 2020 14:22:28 -0800 Subject: [PATCH] Add table scroll left/right shortcut --- README.md | 10 +- cointop/cointop.go | 4 +- cointop/colorscheme.go | 6 + cointop/default_shortcuts.go | 7 +- cointop/help.go | 2 +- cointop/keybindings.go | 12 ++ cointop/layout.go | 5 +- cointop/navigation.go | 55 +++++++++ cointop/table.go | 220 +++++++++++++++++++++++++---------- cointop/util.go | 10 ++ go.sum | 19 +-- pkg/table/table.go | 53 ++++++++- pkg/ui/ui.go | 22 ++-- pkg/ui/view.go | 4 +- 14 files changed, 324 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index baa9746..e5bfff1 100644 --- a/README.md +++ b/README.md @@ -458,8 +458,8 @@ refresh_rate = 60 "]" = "next_chart_range" "{" = "first_chart_range" "}" = "last_chart_range" - "<" = "previous_page" - ">" = "next_page" + "<" = "scroll_left" + ">" = "scroll_right" C = "show_currency_convert_menu" E = "show_portfolio_edit_menu" G = "move_to_page_last_row" @@ -566,6 +566,8 @@ Action|Description `quit_view`|Quit view `refresh`|Do a manual refresh on the data `save`|Save config +`scroll_left`|Scroll table to the left +`scroll_right`|Scroll table to the right `shorten_chart`|Decrease chart height `show_currency_convert_menu`|Show currency convert menu `show_favorites`|Show favorites @@ -959,6 +961,10 @@ Frequently asked questions: - A: Run cointop with the `--hide-statusbar` flag. +- Q: How do I scroll the table horizontally left or right? + + - A: Use the keys < to scroll the table to the left and < to scroll the table to the right. + - Q: How can I delete the cache? - A: Run `cointop clean` to delete the cache files. Cointop will generate new cache files after fetching data. diff --git a/cointop/cointop.go b/cointop/cointop.go index eb3c715..761b275 100644 --- a/cointop/cointop.go +++ b/cointop/cointop.go @@ -72,6 +72,7 @@ type State struct { shortcutKeys map[string]string sortDesc bool sortBy string + tableOffsetX int onlyTable bool chartHeight int } @@ -216,7 +217,8 @@ func NewCointop(config *Config) (*Cointop, error) { portfolio: &Portfolio{ Entries: make(map[string]*PortfolioEntry, 0), }, - chartHeight: 10, + chartHeight: 10, + tableOffsetX: 0, }, TableColumnOrder: TableColumnOrder(), Views: &Views{ diff --git a/cointop/colorscheme.go b/cointop/colorscheme.go index d1b3c68..f79c0af 100644 --- a/cointop/colorscheme.go +++ b/cointop/colorscheme.go @@ -1,6 +1,7 @@ package cointop import ( + "fmt" "strconv" fcolor "github.com/fatih/color" @@ -229,6 +230,11 @@ func (c *Colorscheme) TableRowFavoriteSprintf() ISprintf { return c.toSprintf("table_row_favorite") } +// Default ... +func (c *Colorscheme) Default(a ...interface{}) string { + return fmt.Sprintf(a[0].(string), a[1:]...) +} + func (c *Colorscheme) toSprintf(name string) ISprintf { if cached, ok := c.cache[name]; ok { return cached diff --git a/cointop/default_shortcuts.go b/cointop/default_shortcuts.go index ec5d254..5ee8a35 100644 --- a/cointop/default_shortcuts.go +++ b/cointop/default_shortcuts.go @@ -2,7 +2,8 @@ package cointop // DefaultShortcuts is a map of the default shortcuts func DefaultShortcuts() map[string]string { - return map[string]string{"up": "move_up", + return map[string]string{ + "up": "move_up", "down": "move_down", "left": "previous_page", "right": "next_page", @@ -75,8 +76,8 @@ func DefaultShortcuts() map[string]string { "[": "previous_chart_range", "}": "last_chart_range", "{": "first_chart_range", - ">": "next_page", - "<": "previous_page", + ">": "scroll_right", + "<": "scroll_left", "\\\\": "toggle_table_fullscreen", } } diff --git a/cointop/help.go b/cointop/help.go index cece452..cc4dd44 100644 --- a/cointop/help.go +++ b/cointop/help.go @@ -39,7 +39,7 @@ func (ct *Cointop) UpdateHelp() { if cnt%percol == 0 { cnt = 0 } - item := fmt.Sprintf("%10s %-40s", k, ct.colorscheme.MenuLabel(v)) + item := fmt.Sprintf("%10s %-45s", k, ct.colorscheme.MenuLabel(v)) cols[cnt] = append(cols[cnt], item) cnt = cnt + 1 } diff --git a/cointop/keybindings.go b/cointop/keybindings.go index 1997b61..925fa0e 100644 --- a/cointop/keybindings.go +++ b/cointop/keybindings.go @@ -279,6 +279,10 @@ func (ct *Cointop) Keybindings(g *gocui.Gui) error { fn = ct.Sortfn("marketcap", true) case "move_to_page_visible_middle_row": fn = ct.Keyfn(ct.NavigatePageMiddleLine) + case "scroll_left": + fn = ct.Keyfn(ct.TableScrollLeft) + case "scroll_right": + fn = ct.Keyfn(ct.TableScrollRight) case "sort_column_name": fn = ct.Sortfn("name", false) case "sort_column_price": @@ -375,6 +379,14 @@ func (ct *Cointop) Keybindings(g *gocui.Gui) error { ct.SetKeybindingMod(gocui.KeyEsc, gocui.ModNone, ct.Keyfn(ct.HideConvertMenu), ct.Views.ConvertMenu.Name()) ct.SetKeybindingMod('q', gocui.ModNone, ct.Keyfn(ct.HideConvertMenu), ct.Views.ConvertMenu.Name()) + // mouse events + ct.SetKeybindingMod(gocui.MouseRelease, gocui.ModNone, ct.Keyfn(ct.MouseRelease), "") + ct.SetKeybindingMod(gocui.MouseLeft, gocui.ModNone, ct.Keyfn(ct.MouseLeftClick), "") + ct.SetKeybindingMod(gocui.MouseMiddle, gocui.ModNone, ct.Keyfn(ct.MouseMiddleClick), "") + ct.SetKeybindingMod(gocui.MouseRight, gocui.ModNone, ct.Keyfn(ct.MouseRightClick), "") + ct.SetKeybindingMod(gocui.MouseWheelUp, gocui.ModNone, ct.Keyfn(ct.MouseWheelUp), "") + ct.SetKeybindingMod(gocui.MouseWheelDown, gocui.ModNone, ct.Keyfn(ct.MouseWheelDown), "") + // character key press to select option // TODO: use scrolling table keys := ct.SortedSupportedCurrencyConversions() diff --git a/cointop/layout.go b/cointop/layout.go index d4b72c2..635abe2 100644 --- a/cointop/layout.go +++ b/cointop/layout.go @@ -90,8 +90,9 @@ func (ct *Cointop) layout() error { } } + tableOffsetX := ct.State.tableOffsetX topOffset = topOffset + chartHeight - if err := ct.ui.SetView(ct.Views.TableHeader, 0, topOffset, ct.maxTableWidth, topOffset+2); err != nil { + if err := ct.ui.SetView(ct.Views.TableHeader, tableOffsetX, topOffset, ct.maxTableWidth, topOffset+2); 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())) @@ -99,7 +100,7 @@ func (ct *Cointop) layout() error { } topOffset = topOffset + headerHeight - if err := ct.ui.SetView(ct.Views.Table, 0, topOffset, ct.maxTableWidth, maxY-statusbarHeight); err != nil { + if err := ct.ui.SetView(ct.Views.Table, tableOffsetX, topOffset, ct.maxTableWidth, maxY-statusbarHeight); err != nil { ct.Views.Table.SetFrame(false) ct.Views.Table.SetHighlight(true) ct.Views.Table.SetSelFgColor(ct.colorscheme.gocuiFgColor("table_row_active")) diff --git a/cointop/navigation.go b/cointop/navigation.go index 0fb5b0e..f2528e6 100644 --- a/cointop/navigation.go +++ b/cointop/navigation.go @@ -1,5 +1,9 @@ package cointop +import ( + "math" +) + // CurrentPage returns the current page func (ct *Cointop) CurrentPage() int { ct.debuglog("currentPage()") @@ -461,3 +465,54 @@ func (ct *Cointop) CursorUpOrPreviousPage() error { return nil } + +// TableScrollLeft scrolls the table to the left +func (ct *Cointop) TableScrollLeft() error { + ct.State.tableOffsetX++ + if ct.State.tableOffsetX >= 0 { + ct.State.tableOffsetX = 0 + } + ct.UpdateTable() + return nil +} + +// TableScrollRight scrolls the the table to the right +func (ct *Cointop) TableScrollRight() error { + ct.State.tableOffsetX-- + maxX := int(math.Min(float64(1-(ct.maxTableWidth-ct.width())), 0)) + if ct.State.tableOffsetX <= maxX { + ct.State.tableOffsetX = maxX + } + ct.UpdateTable() + return nil +} + +// MouseRelease is called on mouse releae event +func (ct *Cointop) MouseRelease() error { + return nil +} + +// MouseLeftClick is called on mouse left click event +func (ct *Cointop) MouseLeftClick() error { + return nil +} + +// MouseMiddleClick is called on mouse middle click event +func (ct *Cointop) MouseMiddleClick() error { + return nil +} + +// MouseRightClick is called on mouse right click event +func (ct *Cointop) MouseRightClick() error { + return ct.OpenLink() +} + +// MouseWheelUp is called on mouse wheel up event +func (ct *Cointop) MouseWheelUp() error { + return nil +} + +// MouseWheelDown is called on mouse wheel down event +func (ct *Cointop) MouseWheelDown() error { + return nil +} diff --git a/cointop/table.go b/cointop/table.go index 6f6ef2d..a1c80e7 100644 --- a/cointop/table.go +++ b/cointop/table.go @@ -9,7 +9,6 @@ import ( "time" "github.com/miguelmota/cointop/pkg/humanize" - "github.com/miguelmota/cointop/pkg/pad" "github.com/miguelmota/cointop/pkg/table" "github.com/miguelmota/cointop/pkg/ui" ) @@ -49,20 +48,10 @@ const dots = "..." func (ct *Cointop) RefreshTable() error { ct.debuglog("refreshTable()") maxX := ct.width() - ct.table = table.New().SetWidth(maxX) + ct.table = table.NewTable().SetWidth(maxX) ct.table.HideColumHeaders = true if ct.State.portfolioVisible { - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - total := ct.GetPortfolioTotal() for _, coin := range ct.State.coins { @@ -76,17 +65,13 @@ func (ct *Cointop) RefreshTable() error { if coin.PercentChange24H < 0 { color24h = ct.colorscheme.TableColumnChangeDown } - name := coin.Name + name := TruncateString(coin.Name, 20) + symbol := TruncateString(coin.Symbol, 6) star := ct.colorscheme.TableRow(" ") if coin.Favorite { star = ct.colorscheme.TableRowFavorite("*") } - rank := fmt.Sprintf("%s%v", star, ct.colorscheme.TableRow(fmt.Sprintf("%6v ", coin.Rank))) - if len(name) > 20 { - name = fmt.Sprintf("%s%s", name[0:18], dots) - } - namecolor := ct.colorscheme.TableRow if coin.Favorite { namecolor = ct.colorscheme.TableRowFavorite @@ -97,31 +82,75 @@ func (ct *Cointop) RefreshTable() error { percentHoldings = 0 } - ct.table.AddRow( - rank, - namecolor(pad.Right(fmt.Sprintf("%.22s", name), 21, " ")), - ct.colorscheme.TableRow(pad.Right(fmt.Sprintf("%.6s", coin.Symbol), 5, " ")), - ct.colorscheme.TableRow(fmt.Sprintf("%13s", humanize.Commaf(coin.Price))), - ct.colorscheme.TableRow(fmt.Sprintf("%15s", strconv.FormatFloat(coin.Holdings, 'f', -1, 64))), - colorbalance(fmt.Sprintf("%15s", humanize.Commaf(coin.Balance))), - color24h(fmt.Sprintf("%8.2f%%", coin.PercentChange24H)), - ct.colorscheme.TableRow(fmt.Sprintf("%12.2f%%", percentHoldings)), - ct.colorscheme.TableRow(pad.Right(fmt.Sprintf("%17s", lastUpdated), 80, " ")), + rank := fmt.Sprintf("%s%v", star, ct.colorscheme.TableRow(fmt.Sprintf("%6v ", coin.Rank))) + + ct.table.AddRowCells( + &table.RowCell{ + LeftMargin: 0, + Width: 6, + LeftAlign: false, + Color: ct.colorscheme.Default, + Text: rank, + }, + &table.RowCell{ + LeftMargin: 1, + Width: 22, + LeftAlign: true, + Color: namecolor, + Text: name, + }, + &table.RowCell{ + LeftMargin: 1, + Width: 6, + LeftAlign: true, + Color: ct.colorscheme.TableRow, + Text: symbol, + }, + &table.RowCell{ + LeftMargin: 1, + Width: 14, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: humanize.Commaf(coin.Price), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 16, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: strconv.FormatFloat(coin.Holdings, 'f', -1, 64), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 16, + LeftAlign: false, + Color: colorbalance, + Text: humanize.Commaf(coin.Balance), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 10, + LeftAlign: false, + Color: color24h, + Text: fmt.Sprintf("%.2f%%", coin.PercentChange24H), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 14, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: fmt.Sprintf("%.2f%%", percentHoldings), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 18, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: lastUpdated, + }, ) } } else { - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") - ct.table.AddCol("") for _, coin := range ct.State.coins { if coin == nil { continue @@ -153,35 +182,106 @@ func (ct *Cointop) RefreshTable() error { if coin.PercentChange7D < 0 { color7d = ct.colorscheme.TableColumnChangeDown } - name := coin.Name + name := TruncateString(coin.Name, 20) + symbol := TruncateString(coin.Symbol, 6) star := ct.colorscheme.TableRow(" ") if coin.Favorite { star = ct.colorscheme.TableRowFavorite("*") } - rank := fmt.Sprintf("%s%v", star, ct.colorscheme.TableRow(fmt.Sprintf("%6v ", coin.Rank))) - if len(name) > 20 { - name = fmt.Sprintf("%s%s", name[0:18], dots) - } - symbolpadding := 5 + symbolpadding := 8 // NOTE: this is to adjust padding by 1 because when all name rows are // yellow it messes the spacing (need to debug) if ct.State.filterByFavorites { - symbolpadding = 6 + symbolpadding++ } - ct.table.AddRow( - rank, - namecolor(pad.Right(fmt.Sprintf("%.22s", name), 21, " ")), - ct.colorscheme.TableRow(pad.Right(fmt.Sprintf("%.6s", coin.Symbol), symbolpadding, " ")), - ct.colorscheme.TableColumnPrice(fmt.Sprintf("%13s", humanize.Commaf(coin.Price))), - ct.colorscheme.TableRow(fmt.Sprintf("%17s", humanize.Commaf(coin.MarketCap))), - ct.colorscheme.TableRow(fmt.Sprintf("%15s", humanize.Commaf(coin.Volume24H))), - color1h(fmt.Sprintf("%8.2f%%", coin.PercentChange1H)), - color24h(fmt.Sprintf("%8.2f%%", coin.PercentChange24H)), - color7d(fmt.Sprintf("%8.2f%%", coin.PercentChange7D)), - ct.colorscheme.TableRow(fmt.Sprintf("%21s", humanize.Commaf(coin.TotalSupply))), - ct.colorscheme.TableRow(fmt.Sprintf("%18s", humanize.Commaf(coin.AvailableSupply))), - ct.colorscheme.TableRow(fmt.Sprintf("%18s", lastUpdated)), + + rank := fmt.Sprintf("%s%v", star, ct.colorscheme.TableRow(fmt.Sprintf("%6v ", coin.Rank))) + ct.table.AddRowCells( + &table.RowCell{ + LeftMargin: 0, + Width: 6, + LeftAlign: false, + Color: ct.colorscheme.Default, + Text: rank, + }, + &table.RowCell{ + LeftMargin: 1, + Width: 22, + LeftAlign: true, + Color: namecolor, + Text: name, + }, + &table.RowCell{ + LeftMargin: 1, + Width: symbolpadding, + LeftAlign: true, + Color: ct.colorscheme.TableRow, + Text: symbol, + }, + &table.RowCell{ + LeftMargin: 1, + Width: 12, + LeftAlign: false, + Color: ct.colorscheme.TableColumnPrice, + Text: humanize.Commaf(coin.Price), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 18, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: humanize.Commaf(coin.MarketCap), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 16, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: humanize.Commaf(coin.Volume24H), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 11, + LeftAlign: false, + Color: color1h, + Text: fmt.Sprintf("%.2f%%", coin.PercentChange1H), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 10, + LeftAlign: false, + Color: color24h, + Text: fmt.Sprintf("%.2f%%", coin.PercentChange24H), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 10, + LeftAlign: false, + Color: color7d, + Text: fmt.Sprintf("%.2f%%", coin.PercentChange7D), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 22, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: humanize.Commaf(coin.TotalSupply), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 19, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: humanize.Commaf(coin.AvailableSupply), + }, + &table.RowCell{ + LeftMargin: 1, + Width: 18, + LeftAlign: false, + Color: ct.colorscheme.TableRow, + Text: lastUpdated, + }, // TODO: add %percent of cap ) } diff --git a/cointop/util.go b/cointop/util.go index 3b06774..b243db5 100644 --- a/cointop/util.go +++ b/cointop/util.go @@ -3,6 +3,7 @@ package cointop import ( "bytes" "encoding/gob" + "fmt" "strings" "github.com/miguelmota/cointop/pkg/open" @@ -31,3 +32,12 @@ func Slugify(s string) string { s = strings.TrimSpace(strings.ToLower(s)) return s } + +// TruncateString returns a truncated string +func TruncateString(value string, maxLen int) string { + dots := "..." + if len(value) > maxLen { + value = fmt.Sprintf("%s%s", value[0:maxLen-3], dots) + } + return value +} diff --git a/go.sum b/go.sum index 0d569c5..d96e396 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -48,6 +46,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -67,17 +66,12 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/maruel/panicparse v1.1.2-0.20180806203336-f20d4c4d746f h1:mtX2D0ta3lWxCvv276VVIH6mMYzm4jhSfYP70ZJSOQU= -github.com/maruel/panicparse v1.1.2-0.20180806203336-f20d4c4d746f/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI= github.com/maruel/panicparse v1.5.0 h1:etK4QAf/Spw8eyowKbOHRkOfhblp/kahGUy96RvbMjI= github.com/maruel/panicparse v1.5.0/go.mod h1:aOutY/MUjdj80R0AEVI9qE2zHqig+67t2ffUDDiLzAM= -github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= @@ -89,8 +83,6 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miguelmota/go-coinmarketcap v0.1.5 h1:NwAqQG+ClGNsYlReM06ERK8CqEkW1tF8BQsTLNp6TyU= -github.com/miguelmota/go-coinmarketcap v0.1.5/go.mod h1:Jdv/kqtKclIElmoNAZMMJn0DSQv+j7p/H1te/GGnxhA= github.com/miguelmota/go-coinmarketcap v0.1.6 h1:YIe+VdFhEgyGESfmkL7BHRDIdf6CUOAjJisml01AFqs= github.com/miguelmota/go-coinmarketcap v0.1.6/go.mod h1:Jdv/kqtKclIElmoNAZMMJn0DSQv+j7p/H1te/GGnxhA= github.com/miguelmota/gocui v0.4.2 h1:nMYnYn3RjV7FlWFcidQa9eAkX3kT7XMI6yJMxEkAz6s= @@ -161,8 +153,6 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -180,15 +170,9 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs= -golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed h1:WBkVNH1zd9jg/dK4HCM4lNANnmd12EHC9z+LmcCG4ns= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -196,6 +180,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/pkg/table/table.go b/pkg/table/table.go index 51c4db5..128aa35 100644 --- a/pkg/table/table.go +++ b/pkg/table/table.go @@ -6,6 +6,7 @@ import ( "sort" "strings" + "github.com/miguelmota/cointop/pkg/pad" "github.com/miguelmota/cointop/pkg/table/align" ) @@ -18,8 +19,8 @@ type Table struct { HideColumHeaders bool } -// New new table -func New() *Table { +// NewTable new table +func NewTable() *Table { return &Table{} } @@ -43,6 +44,23 @@ func (t *Table) AddRow(v ...interface{}) *Row { return r } +// AddRowCells add row using cells +func (t *Table) AddRowCells(cells ...*RowCell) *Row { + t.SetNumCol(len(cells)) + v := make([]interface{}, len(cells)) + for i, item := range cells { + v[i] = item.String() + } + return t.AddRow(v...) +} + +// SetNumCol sets the number of columns +func (t *Table) SetNumCol(count int) { + for i := 0; i < count; i++ { + t.AddCol("") + } +} + // SortAscFn sort ascending function func (t *Table) SortAscFn(n string, fn SortFn) *Table { i := t.cols.Index(n) @@ -126,11 +144,11 @@ func (t *Table) Format() *Table { } if c.formatFn != nil { - r.strValues[j] = fmt.Sprintf("%s", c.formatFn(v)) + " " + r.strValues[j] = fmt.Sprintf("%s", c.formatFn(v)) } else if c.format != "" { - r.strValues[j] = fmt.Sprintf(c.format, v) + " " + r.strValues[j] = fmt.Sprintf(c.format, v) } else { - r.strValues[j] = fmt.Sprintf("%v", v) + " " + r.strValues[j] = fmt.Sprintf("%v", v) } if len(r.strValues[j]) > t.cols[j].width { @@ -163,7 +181,10 @@ func (t *Table) Format() *Table { break } - t.cols[i].width += t.width - t.colWidth() + + if len(t.cols) > 0 { + t.cols[i].width += t.width - t.colWidth() + } return t } @@ -223,3 +244,23 @@ func (t *Table) Fprint(w io.Writer) { fmt.Fprintf(w, "\n") } } + +// RowCell is a row cell struct +type RowCell struct { + LeftMargin int + Width int + LeftAlign bool + Color func(a ...interface{}) string + Text string +} + +// String returns row cell as string +func (rc *RowCell) String() string { + t := strings.Repeat(" ", rc.LeftMargin) + rc.Text + if rc.LeftAlign { + t = pad.Right(t, rc.Width, " ") + } else { + t = fmt.Sprintf("%"+fmt.Sprintf("%v", rc.Width)+"s", t) + } + return rc.Color(t) +} diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index 443122c..868ae1b 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -4,12 +4,12 @@ import ( "github.com/miguelmota/gocui" ) -// UI ... +// UI is the UI view struct type UI struct { g *gocui.Gui } -// NewUI ... +// NewUI returns a new UI instance func NewUI() (*UI, error) { g, err := gocui.NewGui(gocui.Output256) if err != nil { @@ -21,44 +21,44 @@ func NewUI() (*UI, error) { }, nil } -// GetGocui ... +// GetGocui returns the underlying gocui instance func (ui *UI) GetGocui() *gocui.Gui { return ui.g } -// SetFgColor ... +// SetFgColor sets the foreground color func (ui *UI) SetFgColor(fgColor gocui.Attribute) { ui.g.FgColor = fgColor } -// SetBgColor ... +// SetBgColor sets the background color func (ui *UI) SetBgColor(bgColor gocui.Attribute) { ui.g.BgColor = bgColor } -// SetInputEsc ... +// SetInputEsc enables the escape key func (ui *UI) SetInputEsc(enabled bool) { ui.g.InputEsc = true } -// SetMouse ... +// SetMouse enables the mouse func (ui *UI) SetMouse(enabled bool) { ui.g.Mouse = true } -// SetHighlight ... +// SetHighlight enables the highlight active state func (ui *UI) SetHighlight(enabled bool) { ui.g.Highlight = true } -// SetManagerFunc ... +// SetManagerFunc sets the function to call for rendering UI func (ui *UI) SetManagerFunc(fn func() error) { ui.g.SetManagerFunc(func(*gocui.Gui) error { return fn() }) } -// MainLoop ... +// MainLoop starts the UI render loop func (ui *UI) MainLoop() error { return ui.g.MainLoop() } @@ -68,7 +68,7 @@ func (ui *UI) Close() { ui.g.Close() } -// SetView ... +// SetView sets the view layout func (ui *UI) SetView(view interface{}, x, y, w, h int) error { if v, ok := view.(*View); ok { gv, err := ui.g.SetView(v.Name(), x, y, w, h) diff --git a/pkg/ui/view.go b/pkg/ui/view.go index 6d276e8..61fb426 100644 --- a/pkg/ui/view.go +++ b/pkg/ui/view.go @@ -6,14 +6,14 @@ import ( "github.com/miguelmota/gocui" ) -// IView is a cointop view +// IView is the view interface type IView interface { Backing() *gocui.View SetBacking(gocuiView *gocui.View) Name() string } -// View is a cointop view +// View is a view sruct type View struct { backing *gocui.View name string