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