pull/29/head
Miguel Mota 5 years ago
parent b7ea461a07
commit 6eb6236104

@ -60,7 +60,7 @@ In action
There are multiple ways you can install cointop depending on the platform you're on.
### From source (always latest)
### From source (always latest and recommeded)
Make sure to have [go](https://golang.org/) (1.10+) installed, then do:
@ -508,6 +508,10 @@ Frequently asked questions:
- A: Running cointop for the first time will fetch the data and populate the cache which may take a few seconds.
- Q: I'm no longer seeing any data!
- A: Run `cointop --clean` to delete the cache and then rerun cointop. If you're still not seeing any data, then please [submit an issue](https://github.com/miguelmota/cointop/issues/new).
- Q: I installed cointop without errors but the command is not found.
- A: Make sure your `GOPATH` and `PATH` is set correctly.

@ -6,10 +6,10 @@ import (
"sync"
"time"
"github.com/miguelmota/cointop/pkg/color"
"github.com/miguelmota/cointop/pkg/fcache"
"github.com/miguelmota/cointop/pkg/now"
"github.com/miguelmota/cointop/pkg/termui"
"github.com/gizak/termui"
"github.com/miguelmota/cointop/cointop/common/color"
"github.com/miguelmota/cointop/cointop/common/filecache"
"github.com/miguelmota/cointop/cointop/common/timeutil"
)
var chartlock sync.Mutex
@ -67,7 +67,7 @@ func (ct *Cointop) chartPoints(coin string) error {
rangeseconds := ct.chartrangesmap[ct.selectedchartrange]
if ct.selectedchartrange == "YTD" {
ytd := time.Now().Unix() - int64(now.BeginningOfYear().Unix())
ytd := time.Now().Unix() - int64(timeutil.BeginningOfYear().Unix())
rangeseconds = time.Duration(ytd) * time.Second
}
@ -114,7 +114,7 @@ func (ct *Cointop) chartPoints(coin string) error {
ct.cache.Set(cachekey, data, 10*time.Second)
go func() {
_ = fcache.Set(cachekey, data, 24*time.Hour)
filecache.Set(cachekey, data, 24*time.Hour)
}()
}
@ -163,7 +163,7 @@ func (ct *Cointop) portfolioChart() error {
rangeseconds := ct.chartrangesmap[ct.selectedchartrange]
if ct.selectedchartrange == "YTD" {
ytd := time.Now().Unix() - int64(now.BeginningOfYear().Unix())
ytd := time.Now().Unix() - int64(timeutil.BeginningOfYear().Unix())
rangeseconds = time.Duration(ytd) * time.Second
}
@ -196,7 +196,7 @@ func (ct *Cointop) portfolioChart() error {
graphData, _ = cached.([]float64)
ct.debuglog("soft cache hit")
} else {
_ = fcache.Get(cachekey, &graphData)
filecache.Get(cachekey, &graphData)
if len(graphData) == 0 {
time.Sleep(2 * time.Second)
@ -212,7 +212,7 @@ func (ct *Cointop) portfolioChart() error {
ct.cache.Set(cachekey, graphData, 10*time.Second)
go func() {
_ = fcache.Set(cachekey, graphData, 24*time.Hour)
filecache.Set(cachekey, graphData, 24*time.Hour)
}()
}

@ -9,13 +9,13 @@ import (
"sync"
"time"
"github.com/miguelmota/cointop/pkg/api"
apitypes "github.com/miguelmota/cointop/pkg/api/types"
"github.com/miguelmota/cointop/pkg/cache"
"github.com/miguelmota/cointop/pkg/fcache"
"github.com/miguelmota/cointop/pkg/gocui"
"github.com/miguelmota/cointop/pkg/table"
"github.com/miguelmota/cointop/pkg/termui"
"github.com/gizak/termui"
"github.com/jroimartin/gocui"
"github.com/miguelmota/cointop/cointop/common/api"
"github.com/miguelmota/cointop/cointop/common/api/types"
"github.com/miguelmota/cointop/cointop/common/filecache"
"github.com/miguelmota/cointop/cointop/common/table"
"github.com/patrickmn/go-cache"
)
// TODO: clean up and optimize codebase
@ -195,9 +195,9 @@ func NewCointop(config *Config) *Cointop {
log.Fatal(err)
}
allcoinsslugmap := map[string]apitypes.Coin{}
allcoinsslugmap := map[string]types.Coin{}
coinscachekey := "allcoinsslugmap"
fcache.Get(coinscachekey, &allcoinsslugmap)
filecache.Get(coinscachekey, &allcoinsslugmap)
ct.cache.Set(coinscachekey, allcoinsslugmap, 10*time.Second)
// DEPRECATED: favorites by 'symbol' is deprecated because of collisions. Kept for backward compatibility.
@ -214,12 +214,12 @@ func NewCointop(config *Config) *Cointop {
var globaldata []float64
chartcachekey := strings.ToLower(fmt.Sprintf("%s_%s", "globaldata", strings.Replace(ct.selectedchartrange, " ", "", -1)))
fcache.Get(chartcachekey, &globaldata)
filecache.Get(chartcachekey, &globaldata)
ct.cache.Set(chartcachekey, globaldata, 10*time.Second)
var market apitypes.GlobalMarketData
var market types.GlobalMarketData
marketcachekey := "market"
fcache.Get(marketcachekey, &market)
filecache.Get(marketcachekey, &market)
ct.cache.Set(marketcachekey, market, 10*time.Second)
err = ct.api.Ping()
if err != nil {

@ -1,6 +1,8 @@
package cointop
import "testing"
import (
"testing"
)
func TestRun(t *testing.T) {
// Run()

@ -1,7 +1,7 @@
package api
import (
cmc "github.com/miguelmota/cointop/pkg/api/impl/coinmarketcap"
cmc "github.com/miguelmota/cointop/cointop/common/api/impl/coinmarketcap"
)
// NewCMC new CoinMarketCap API

@ -8,8 +8,8 @@ import (
"sync"
"time"
apitypes "github.com/miguelmota/cointop/pkg/api/types"
cmc "github.com/miguelmota/cointop/pkg/cmc"
apitypes "github.com/miguelmota/cointop/cointop/common/api/types"
cmc "github.com/miguelmota/cointop/cointop/common/cmc"
)
// Service service

@ -1,7 +1,7 @@
package api
import (
types "github.com/miguelmota/cointop/pkg/api/types"
types "github.com/miguelmota/cointop/cointop/common/api/types"
)
// Interface interface

@ -11,7 +11,7 @@ import (
"strings"
"github.com/anaskhan96/soup"
"github.com/miguelmota/cointop/pkg/cmc/types"
"github.com/miguelmota/cointop/cointop/common/cmc/types"
)
var (

@ -6,7 +6,7 @@ import (
"strconv"
"strings"
"github.com/miguelmota/cointop/pkg/cmc/types"
"github.com/miguelmota/cointop/cointop/common/cmc/types"
)
// V1Tickers get information about all coins listed in Coin Market Cap

@ -1,4 +1,4 @@
package fcache
package filecache
import (
"bytes"

@ -6,7 +6,7 @@ import (
"sort"
"strings"
"github.com/miguelmota/cointop/pkg/table/align"
"github.com/miguelmota/cointop/cointop/common/table/align"
)
// Table table

@ -0,0 +1,24 @@
package timeutil
import "time"
// Now now struct
type Now struct {
time.Time
}
// New initialize Now with time
func New(t time.Time) *Now {
return &Now{t}
}
// BeginningOfYear beginning of year
func BeginningOfYear() time.Time {
return New(time.Now()).BeginningOfYear()
}
// BeginningOfYear BeginningOfYear beginning of year
func (now *Now) BeginningOfYear() time.Time {
y, _, _ := now.Date()
return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location())
}

@ -76,8 +76,9 @@ func (ct *Cointop) configPath() string {
func (ct *Cointop) makeConfigDir() error {
path := ct.configDirPath()
if _, err := os.Stat(path); os.IsNotExist(err) {
_ = os.Mkdir(path, os.ModePerm)
return os.Mkdir(path, os.ModePerm)
}
return nil
}

@ -4,8 +4,8 @@ import (
"fmt"
"sort"
"github.com/miguelmota/cointop/pkg/color"
"github.com/miguelmota/cointop/pkg/pad"
"github.com/miguelmota/cointop/cointop/common/color"
"github.com/miguelmota/cointop/cointop/common/pad"
)
var supportedfiatconversions = map[string]string{

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/miguelmota/cointop/pkg/color"
"github.com/miguelmota/cointop/cointop/common/color"
)
func (ct *Cointop) updateHeaders() {

@ -4,8 +4,8 @@ import (
"fmt"
"sort"
"github.com/miguelmota/cointop/pkg/color"
"github.com/miguelmota/cointop/pkg/pad"
"github.com/miguelmota/cointop/cointop/common/color"
"github.com/miguelmota/cointop/cointop/common/pad"
)
func (ct *Cointop) toggleHelp() error {

@ -3,7 +3,7 @@ package cointop
import (
"strings"
"github.com/miguelmota/cointop/pkg/gocui"
"github.com/jroimartin/gocui"
)
func (ct *Cointop) parseKeys(s string) (interface{}, gocui.Modifier) {

@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/miguelmota/cointop/pkg/gocui"
"github.com/jroimartin/gocui"
)
// layout sets initial layout

@ -4,8 +4,8 @@ import (
"sync"
"time"
types "github.com/miguelmota/cointop/pkg/api/types"
"github.com/miguelmota/cointop/pkg/fcache"
types "github.com/miguelmota/cointop/cointop/common/api/types"
"github.com/miguelmota/cointop/cointop/common/filecache"
)
var coinslock sync.Mutex
@ -34,7 +34,7 @@ func (ct *Cointop) updateCoins() error {
}
ct.cache.Set(cachekey, allcoinsslugmap, 10*time.Second)
go func() {
_ = fcache.Set(cachekey, allcoinsslugmap, 24*time.Hour)
filecache.Set(cachekey, allcoinsslugmap, 24*time.Hour)
}()
}

@ -5,11 +5,11 @@ import (
"math"
"time"
types "github.com/miguelmota/cointop/pkg/api/types"
"github.com/miguelmota/cointop/pkg/color"
"github.com/miguelmota/cointop/pkg/fcache"
"github.com/miguelmota/cointop/pkg/humanize"
"github.com/miguelmota/cointop/pkg/pad"
types "github.com/miguelmota/cointop/cointop/common/api/types"
"github.com/miguelmota/cointop/cointop/common/color"
"github.com/miguelmota/cointop/cointop/common/filecache"
"github.com/miguelmota/cointop/cointop/common/humanize"
"github.com/miguelmota/cointop/cointop/common/pad"
)
func (ct *Cointop) updateMarketbar() error {
@ -82,7 +82,7 @@ func (ct *Cointop) updateMarketbar() error {
ct.cache.Set(cachekey, market, 10*time.Second)
go func() {
_ = fcache.Set(cachekey, market, 24*time.Hour)
filecache.Set(cachekey, market, 24*time.Hour)
}()
}

@ -7,8 +7,8 @@ import (
"strconv"
"strings"
"github.com/miguelmota/cointop/pkg/color"
"github.com/miguelmota/cointop/pkg/pad"
"github.com/miguelmota/cointop/cointop/common/color"
"github.com/miguelmota/cointop/cointop/common/pad"
)
func (ct *Cointop) togglePortfolio() error {

@ -3,7 +3,7 @@ package cointop
import (
"os"
"github.com/miguelmota/cointop/pkg/gocui"
"github.com/jroimartin/gocui"
)
func (ct *Cointop) quit() error {

@ -4,7 +4,7 @@ import (
"regexp"
"strings"
"github.com/miguelmota/cointop/pkg/levenshtein"
"github.com/miguelmota/cointop/cointop/common/levenshtein"
)
func (ct *Cointop) openSearch() error {

@ -3,7 +3,7 @@ package cointop
import (
"sort"
"github.com/miguelmota/cointop/pkg/gocui"
"github.com/jroimartin/gocui"
)
func (ct *Cointop) sort(sortby string, desc bool, list []*coin) {

@ -3,7 +3,7 @@ package cointop
import (
"fmt"
"github.com/miguelmota/cointop/pkg/pad"
"github.com/miguelmota/cointop/cointop/common/pad"
)
func (ct *Cointop) updateStatusbar(s string) {

@ -7,10 +7,10 @@ import (
"strings"
"time"
"github.com/miguelmota/cointop/pkg/color"
"github.com/miguelmota/cointop/pkg/humanize"
"github.com/miguelmota/cointop/pkg/pad"
"github.com/miguelmota/cointop/pkg/table"
"github.com/miguelmota/cointop/cointop/common/color"
"github.com/miguelmota/cointop/cointop/common/humanize"
"github.com/miguelmota/cointop/cointop/common/pad"
"github.com/miguelmota/cointop/cointop/common/table"
)
func (ct *Cointop) refreshTable() error {

@ -1,7 +1,7 @@
package cointop
import (
"github.com/miguelmota/cointop/pkg/gocui"
"github.com/jroimartin/gocui"
)
// update update view

@ -8,7 +8,7 @@ import (
"runtime"
"strings"
"github.com/miguelmota/cointop/pkg/open"
"github.com/miguelmota/cointop/cointop/common/open"
)
func (ct *Cointop) openLink() error {

@ -1,7 +1,7 @@
package cointop
// TODO: make dynamic based on git tag
const version = "1.1.2"
const version = "1.1.3"
func (ct *Cointop) version() string {
return version

@ -4,10 +4,16 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/anaskhan96/soup v1.1.1
github.com/fatih/color v1.7.0
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/mattn/go-runewidth v0.0.4
github.com/gizak/termui v2.3.0+incompatible
github.com/jroimartin/gocui v0.4.0
github.com/maruel/panicparse v1.1.2-0.20180806203336-f20d4c4d746f // indirect
github.com/mattn/go-colorable v0.1.1 // indirect
github.com/mattn/go-isatty v0.0.6 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 // indirect
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6 // indirect
golang.org/x/text v0.3.0
)

@ -1,25 +1,36 @@
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anaskhan96/soup v0.0.0-20180328123631-cd07662ec930/go.mod h1:pT5vs4HXDwA5y4KQCsKvnkpQd3D+joP7IqpiGskfWW0=
github.com/anaskhan96/soup v1.1.1 h1:Duux/0htS2Va7XLJ9qIakCSey790hg9OFRm2FwlMTy0=
github.com/anaskhan96/soup v1.1.1/go.mod h1:pT5vs4HXDwA5y4KQCsKvnkpQd3D+joP7IqpiGskfWW0=
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/gizak/termui v2.3.0+incompatible h1:S8wJoNumYfc/rR5UezUM4HsPEo3RJh0LKdiuDWQpjqw=
github.com/gizak/termui v2.3.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA=
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
github.com/maruel/panicparse v1.1.1 h1:k62YPcEoLncEEpjMt92GtG5ugb8WL/510Ys3/h5IkRc=
github.com/maruel/panicparse v1.1.1/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI=
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/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-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
golang.org/x/net v0.0.0-20180420171651-5f9ae10d9af5/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20180425194835-bb9c189858d9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6 h1:IcgEB62HYgAhX0Nd/QrVgZlxlcyxbGQHElLUhW2X4Fo=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

@ -1,138 +0,0 @@
// Package now is a time toolkit for golang.
//
// More details README here: https://github.com/jinzhu/now
//
// import "github.com/jinzhu/now"
//
// now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon
// now.BeginningOfDay() // 2013-11-18 00:00:00 Mon
// now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon
package now
import "time"
// WeekStartDay set week start day, default is sunday
var WeekStartDay = time.Sunday
// TimeFormats default time formats will be parsed as
var TimeFormats = []string{"1/2/2006", "1/2/2006 15:4:5", "2006", "2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "1-2", "15:4:5", "15:4", "15", "15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST"}
// Now now struct
type Now struct {
time.Time
}
// New initialize Now with time
func New(t time.Time) *Now {
return &Now{t}
}
// BeginningOfMinute beginning of minute
func BeginningOfMinute() time.Time {
return New(time.Now()).BeginningOfMinute()
}
// BeginningOfHour beginning of hour
func BeginningOfHour() time.Time {
return New(time.Now()).BeginningOfHour()
}
// BeginningOfDay beginning of day
func BeginningOfDay() time.Time {
return New(time.Now()).BeginningOfDay()
}
// BeginningOfWeek beginning of week
func BeginningOfWeek() time.Time {
return New(time.Now()).BeginningOfWeek()
}
// BeginningOfMonth beginning of month
func BeginningOfMonth() time.Time {
return New(time.Now()).BeginningOfMonth()
}
// BeginningOfQuarter beginning of quarter
func BeginningOfQuarter() time.Time {
return New(time.Now()).BeginningOfQuarter()
}
// BeginningOfYear beginning of year
func BeginningOfYear() time.Time {
return New(time.Now()).BeginningOfYear()
}
// EndOfMinute end of minute
func EndOfMinute() time.Time {
return New(time.Now()).EndOfMinute()
}
// EndOfHour end of hour
func EndOfHour() time.Time {
return New(time.Now()).EndOfHour()
}
// EndOfDay end of day
func EndOfDay() time.Time {
return New(time.Now()).EndOfDay()
}
// EndOfWeek end of week
func EndOfWeek() time.Time {
return New(time.Now()).EndOfWeek()
}
// EndOfMonth end of month
func EndOfMonth() time.Time {
return New(time.Now()).EndOfMonth()
}
// EndOfQuarter end of quarter
func EndOfQuarter() time.Time {
return New(time.Now()).EndOfQuarter()
}
// EndOfYear end of year
func EndOfYear() time.Time {
return New(time.Now()).EndOfYear()
}
// Monday monday
func Monday() time.Time {
return New(time.Now()).Monday()
}
// Sunday sunday
func Sunday() time.Time {
return New(time.Now()).Sunday()
}
// EndOfSunday end of sunday
func EndOfSunday() time.Time {
return New(time.Now()).EndOfSunday()
}
// Parse parse string to time
func Parse(strs ...string) (time.Time, error) {
return New(time.Now()).Parse(strs...)
}
// ParseInLocation parse string to time in location
func ParseInLocation(loc *time.Location, strs ...string) (time.Time, error) {
return New(time.Now().In(loc)).Parse(strs...)
}
// MustParse must parse string to time or will panic
func MustParse(strs ...string) time.Time {
return New(time.Now()).MustParse(strs...)
}
// MustParseInLocation must parse string to time in location or will panic
func MustParseInLocation(loc *time.Location, strs ...string) time.Time {
return New(time.Now().In(loc)).MustParse(strs...)
}
// Between check now between the begin, end time or not
func Between(time1, time2 string) bool {
return New(time.Now()).Between(time1, time2)
}

@ -1,204 +0,0 @@
package now
import (
"errors"
"regexp"
"time"
)
// BeginningOfMinute beginning of minute
func (now *Now) BeginningOfMinute() time.Time {
return now.Truncate(time.Minute)
}
// BeginningOfHour beginning of hour
func (now *Now) BeginningOfHour() time.Time {
y, m, d := now.Date()
return time.Date(y, m, d, now.Time.Hour(), 0, 0, 0, now.Time.Location())
}
// BeginningOfDay beginning of day
func (now *Now) BeginningOfDay() time.Time {
y, m, d := now.Date()
return time.Date(y, m, d, 0, 0, 0, 0, now.Time.Location())
}
// BeginningOfWeek beginning of week
func (now *Now) BeginningOfWeek() time.Time {
t := now.BeginningOfDay()
weekday := int(t.Weekday())
if WeekStartDay != time.Sunday {
weekStartDayInt := int(WeekStartDay)
if weekday < weekStartDayInt {
weekday = weekday + 7 - weekStartDayInt
} else {
weekday = weekday - weekStartDayInt
}
}
return t.AddDate(0, 0, -weekday)
}
// BeginningOfMonth beginning of month
func (now *Now) BeginningOfMonth() time.Time {
y, m, _ := now.Date()
return time.Date(y, m, 1, 0, 0, 0, 0, now.Location())
}
// BeginningOfQuarter beginning of quarter
func (now *Now) BeginningOfQuarter() time.Time {
month := now.BeginningOfMonth()
offset := (int(month.Month()) - 1) % 3
return month.AddDate(0, -offset, 0)
}
// BeginningOfYear BeginningOfYear beginning of year
func (now *Now) BeginningOfYear() time.Time {
y, _, _ := now.Date()
return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location())
}
// EndOfMinute end of minute
func (now *Now) EndOfMinute() time.Time {
return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond)
}
// EndOfHour end of hour
func (now *Now) EndOfHour() time.Time {
return now.BeginningOfHour().Add(time.Hour - time.Nanosecond)
}
// EndOfDay end of day
func (now *Now) EndOfDay() time.Time {
y, m, d := now.Date()
return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), now.Location())
}
// EndOfWeek end of week
func (now *Now) EndOfWeek() time.Time {
return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond)
}
// EndOfMonth end of month
func (now *Now) EndOfMonth() time.Time {
return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond)
}
// EndOfQuarter end of quarter
func (now *Now) EndOfQuarter() time.Time {
return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond)
}
// EndOfYear end of year
func (now *Now) EndOfYear() time.Time {
return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond)
}
// Monday monday
func (now *Now) Monday() time.Time {
t := now.BeginningOfDay()
weekday := int(t.Weekday())
if weekday == 0 {
weekday = 7
}
return t.AddDate(0, 0, -weekday+1)
}
// Sunday sunday
func (now *Now) Sunday() time.Time {
t := now.BeginningOfDay()
weekday := int(t.Weekday())
if weekday == 0 {
return t
}
return t.AddDate(0, 0, (7 - weekday))
}
// EndOfSunday end of sunday
func (now *Now) EndOfSunday() time.Time {
return New(now.Sunday()).EndOfDay()
}
func parseWithFormat(str string) (t time.Time, err error) {
for _, format := range TimeFormats {
t, err = time.Parse(format, str)
if err == nil {
return
}
}
err = errors.New("Can't parse string as time: " + str)
return
}
var hasTimeRegexp = regexp.MustCompile(`(\s+|^\s*)\d{1,2}(:\d{1,2})*\s*$`) // match 15:04:05, 15, 2017-01-01 15:04
var onlyTimeRegexp = regexp.MustCompile(`^\s*\d{1,2}(:\d{1,2})*\s*$`) // match 15:04:05, 15
// Parse parse string to time
func (now *Now) Parse(strs ...string) (t time.Time, err error) {
var (
setCurrentTime bool
parseTime []int
currentTime = []int{now.Second(), now.Minute(), now.Hour(), now.Day(), int(now.Month()), now.Year()}
currentLocation = now.Location()
onlyTimeInStr = true
)
for _, str := range strs {
hasTimeInStr := hasTimeRegexp.MatchString(str) // match 15:04:05, 15
onlyTimeInStr = hasTimeInStr && onlyTimeInStr && onlyTimeRegexp.MatchString(str)
if t, err = parseWithFormat(str); err == nil {
location := t.Location()
if location.String() == "UTC" {
location = currentLocation
}
parseTime = []int{t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()}
for i, v := range parseTime {
// Don't reset hour, minute, second if current time str including time
if hasTimeInStr && i <= 2 {
continue
}
// If value is zero, replace it with current time
if v == 0 {
if setCurrentTime {
parseTime[i] = currentTime[i]
}
} else {
setCurrentTime = true
}
// if current time only includes time, should change day, month to current time
if onlyTimeInStr {
if i == 3 || i == 4 {
parseTime[i] = currentTime[i]
continue
}
}
}
t = time.Date(parseTime[5], time.Month(parseTime[4]), parseTime[3], parseTime[2], parseTime[1], parseTime[0], 0, location)
currentTime = []int{t.Second(), t.Minute(), t.Hour(), t.Day(), int(t.Month()), t.Year()}
}
}
return
}
// MustParse must parse string to time or it will panic
func (now *Now) MustParse(strs ...string) (t time.Time) {
t, err := now.Parse(strs...)
if err != nil {
panic(err)
}
return t
}
// Between check time between the begin, end time or not
func (now *Now) Between(begin, end string) bool {
beginTime := now.MustParse(begin)
endTime := now.MustParse(end)
return now.After(beginTime) && now.Before(endTime)
}

@ -1,300 +0,0 @@
package main
import (
"github.com/mattn/go-runewidth"
"github.com/nsf/termbox-go"
"unicode/utf8"
)
func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
for _, c := range msg {
termbox.SetCell(x, y, c, fg, bg)
x += runewidth.RuneWidth(c)
}
}
func fill(x, y, w, h int, cell termbox.Cell) {
for ly := 0; ly < h; ly++ {
for lx := 0; lx < w; lx++ {
termbox.SetCell(x+lx, y+ly, cell.Ch, cell.Fg, cell.Bg)
}
}
}
func rune_advance_len(r rune, pos int) int {
if r == '\t' {
return tabstop_length - pos%tabstop_length
}
return runewidth.RuneWidth(r)
}
func voffset_coffset(text []byte, boffset int) (voffset, coffset int) {
text = text[:boffset]
for len(text) > 0 {
r, size := utf8.DecodeRune(text)
text = text[size:]
coffset += 1
voffset += rune_advance_len(r, voffset)
}
return
}
func byte_slice_grow(s []byte, desired_cap int) []byte {
if cap(s) < desired_cap {
ns := make([]byte, len(s), desired_cap)
copy(ns, s)
return ns
}
return s
}
func byte_slice_remove(text []byte, from, to int) []byte {
size := to - from
copy(text[from:], text[to:])
text = text[:len(text)-size]
return text
}
func byte_slice_insert(text []byte, offset int, what []byte) []byte {
n := len(text) + len(what)
text = byte_slice_grow(text, n)
text = text[:n]
copy(text[offset+len(what):], text[offset:])
copy(text[offset:], what)
return text
}
const preferred_horizontal_threshold = 5
const tabstop_length = 8
type EditBox struct {
text []byte
line_voffset int
cursor_boffset int // cursor offset in bytes
cursor_voffset int // visual cursor offset in termbox cells
cursor_coffset int // cursor offset in unicode code points
}
// Draws the EditBox in the given location, 'h' is not used at the moment
func (eb *EditBox) Draw(x, y, w, h int) {
eb.AdjustVOffset(w)
const coldef = termbox.ColorDefault
fill(x, y, w, h, termbox.Cell{Ch: ' '})
t := eb.text
lx := 0
tabstop := 0
for {
rx := lx - eb.line_voffset
if len(t) == 0 {
break
}
if lx == tabstop {
tabstop += tabstop_length
}
if rx >= w {
termbox.SetCell(x+w-1, y, '→',
coldef, coldef)
break
}
r, size := utf8.DecodeRune(t)
if r == '\t' {
for ; lx < tabstop; lx++ {
rx = lx - eb.line_voffset
if rx >= w {
goto next
}
if rx >= 0 {
termbox.SetCell(x+rx, y, ' ', coldef, coldef)
}
}
} else {
if rx >= 0 {
termbox.SetCell(x+rx, y, r, coldef, coldef)
}
lx += runewidth.RuneWidth(r)
}
next:
t = t[size:]
}
if eb.line_voffset != 0 {
termbox.SetCell(x, y, '←', coldef, coldef)
}
}
// Adjusts line visual offset to a proper value depending on width
func (eb *EditBox) AdjustVOffset(width int) {
ht := preferred_horizontal_threshold
max_h_threshold := (width - 1) / 2
if ht > max_h_threshold {
ht = max_h_threshold
}
threshold := width - 1
if eb.line_voffset != 0 {
threshold = width - ht
}
if eb.cursor_voffset-eb.line_voffset >= threshold {
eb.line_voffset = eb.cursor_voffset + (ht - width + 1)
}
if eb.line_voffset != 0 && eb.cursor_voffset-eb.line_voffset < ht {
eb.line_voffset = eb.cursor_voffset - ht
if eb.line_voffset < 0 {
eb.line_voffset = 0
}
}
}
func (eb *EditBox) MoveCursorTo(boffset int) {
eb.cursor_boffset = boffset
eb.cursor_voffset, eb.cursor_coffset = voffset_coffset(eb.text, boffset)
}
func (eb *EditBox) RuneUnderCursor() (rune, int) {
return utf8.DecodeRune(eb.text[eb.cursor_boffset:])
}
func (eb *EditBox) RuneBeforeCursor() (rune, int) {
return utf8.DecodeLastRune(eb.text[:eb.cursor_boffset])
}
func (eb *EditBox) MoveCursorOneRuneBackward() {
if eb.cursor_boffset == 0 {
return
}
_, size := eb.RuneBeforeCursor()
eb.MoveCursorTo(eb.cursor_boffset - size)
}
func (eb *EditBox) MoveCursorOneRuneForward() {
if eb.cursor_boffset == len(eb.text) {
return
}
_, size := eb.RuneUnderCursor()
eb.MoveCursorTo(eb.cursor_boffset + size)
}
func (eb *EditBox) MoveCursorToBeginningOfTheLine() {
eb.MoveCursorTo(0)
}
func (eb *EditBox) MoveCursorToEndOfTheLine() {
eb.MoveCursorTo(len(eb.text))
}
func (eb *EditBox) DeleteRuneBackward() {
if eb.cursor_boffset == 0 {
return
}
eb.MoveCursorOneRuneBackward()
_, size := eb.RuneUnderCursor()
eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size)
}
func (eb *EditBox) DeleteRuneForward() {
if eb.cursor_boffset == len(eb.text) {
return
}
_, size := eb.RuneUnderCursor()
eb.text = byte_slice_remove(eb.text, eb.cursor_boffset, eb.cursor_boffset+size)
}
func (eb *EditBox) DeleteTheRestOfTheLine() {
eb.text = eb.text[:eb.cursor_boffset]
}
func (eb *EditBox) InsertRune(r rune) {
var buf [utf8.UTFMax]byte
n := utf8.EncodeRune(buf[:], r)
eb.text = byte_slice_insert(eb.text, eb.cursor_boffset, buf[:n])
eb.MoveCursorOneRuneForward()
}
// Please, keep in mind that cursor depends on the value of line_voffset, which
// is being set on Draw() call, so.. call this method after Draw() one.
func (eb *EditBox) CursorX() int {
return eb.cursor_voffset - eb.line_voffset
}
var edit_box EditBox
const edit_box_width = 30
func redraw_all() {
const coldef = termbox.ColorDefault
termbox.Clear(coldef, coldef)
w, h := termbox.Size()
midy := h / 2
midx := (w - edit_box_width) / 2
// unicode box drawing chars around the edit box
termbox.SetCell(midx-1, midy, '│', coldef, coldef)
termbox.SetCell(midx+edit_box_width, midy, '│', coldef, coldef)
termbox.SetCell(midx-1, midy-1, '┌', coldef, coldef)
termbox.SetCell(midx-1, midy+1, '└', coldef, coldef)
termbox.SetCell(midx+edit_box_width, midy-1, '┐', coldef, coldef)
termbox.SetCell(midx+edit_box_width, midy+1, '┘', coldef, coldef)
fill(midx, midy-1, edit_box_width, 1, termbox.Cell{Ch: '─'})
fill(midx, midy+1, edit_box_width, 1, termbox.Cell{Ch: '─'})
edit_box.Draw(midx, midy, edit_box_width, 1)
termbox.SetCursor(midx+edit_box.CursorX(), midy)
tbprint(midx+6, midy+3, coldef, coldef, "Press ESC to quit")
termbox.Flush()
}
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
defer termbox.Close()
termbox.SetInputMode(termbox.InputEsc)
redraw_all()
mainloop:
for {
switch ev := termbox.PollEvent(); ev.Type {
case termbox.EventKey:
switch ev.Key {
case termbox.KeyEsc:
break mainloop
case termbox.KeyArrowLeft, termbox.KeyCtrlB:
edit_box.MoveCursorOneRuneBackward()
case termbox.KeyArrowRight, termbox.KeyCtrlF:
edit_box.MoveCursorOneRuneForward()
case termbox.KeyBackspace, termbox.KeyBackspace2:
edit_box.DeleteRuneBackward()
case termbox.KeyDelete, termbox.KeyCtrlD:
edit_box.DeleteRuneForward()
case termbox.KeyTab:
edit_box.InsertRune('\t')
case termbox.KeySpace:
edit_box.InsertRune(' ')
case termbox.KeyCtrlK:
edit_box.DeleteTheRestOfTheLine()
case termbox.KeyHome, termbox.KeyCtrlA:
edit_box.MoveCursorToBeginningOfTheLine()
case termbox.KeyEnd, termbox.KeyCtrlE:
edit_box.MoveCursorToEndOfTheLine()
default:
if ev.Ch != 0 {
edit_box.InsertRune(ev.Ch)
}
}
case termbox.EventError:
panic(ev.Err)
}
redraw_all()
}
}

@ -1,69 +0,0 @@
package main
import (
"fmt"
"github.com/nsf/termbox-go"
"time"
)
func tbPrint(x, y int, fg, bg termbox.Attribute, msg string) {
for _, c := range msg {
termbox.SetCell(x, y, c, fg, bg)
x++
}
}
func draw(i int) {
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
defer termbox.Flush()
w, h := termbox.Size()
s := fmt.Sprintf("count = %d", i)
tbPrint((w/2)-(len(s)/2), h/2, termbox.ColorRed, termbox.ColorDefault, s)
}
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
termbox.SetInputMode(termbox.InputEsc)
go func() {
time.Sleep(5 * time.Second)
termbox.Interrupt()
// This should never run - the Interrupt(), above, should cause the event
// loop below to exit, which then exits the process. If something goes
// wrong, this panic will trigger and show what happened.
time.Sleep(1 * time.Second)
panic("this should never run")
}()
var count int
draw(count)
mainloop:
for {
switch ev := termbox.PollEvent(); ev.Type {
case termbox.EventKey:
if ev.Ch == '+' {
count++
} else if ev.Ch == '-' {
count--
}
case termbox.EventError:
panic(ev.Err)
case termbox.EventInterrupt:
break mainloop
}
draw(count)
}
termbox.Close()
fmt.Println("Finished")
}

@ -1,722 +0,0 @@
package main
import "github.com/nsf/termbox-go"
import "fmt"
type key struct {
x int
y int
ch rune
}
var K_ESC = []key{{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}}
var K_F1 = []key{{6, 1, 'F'}, {7, 1, '1'}}
var K_F2 = []key{{9, 1, 'F'}, {10, 1, '2'}}
var K_F3 = []key{{12, 1, 'F'}, {13, 1, '3'}}
var K_F4 = []key{{15, 1, 'F'}, {16, 1, '4'}}
var K_F5 = []key{{19, 1, 'F'}, {20, 1, '5'}}
var K_F6 = []key{{22, 1, 'F'}, {23, 1, '6'}}
var K_F7 = []key{{25, 1, 'F'}, {26, 1, '7'}}
var K_F8 = []key{{28, 1, 'F'}, {29, 1, '8'}}
var K_F9 = []key{{33, 1, 'F'}, {34, 1, '9'}}
var K_F10 = []key{{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}}
var K_F11 = []key{{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}}
var K_F12 = []key{{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}}
var K_PRN = []key{{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}}
var K_SCR = []key{{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}}
var K_BRK = []key{{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}}
var K_LED1 = []key{{66, 1, '-'}}
var K_LED2 = []key{{70, 1, '-'}}
var K_LED3 = []key{{74, 1, '-'}}
var K_TILDE = []key{{1, 4, '`'}}
var K_TILDE_SHIFT = []key{{1, 4, '~'}}
var K_1 = []key{{4, 4, '1'}}
var K_1_SHIFT = []key{{4, 4, '!'}}
var K_2 = []key{{7, 4, '2'}}
var K_2_SHIFT = []key{{7, 4, '@'}}
var K_3 = []key{{10, 4, '3'}}
var K_3_SHIFT = []key{{10, 4, '#'}}
var K_4 = []key{{13, 4, '4'}}
var K_4_SHIFT = []key{{13, 4, '$'}}
var K_5 = []key{{16, 4, '5'}}
var K_5_SHIFT = []key{{16, 4, '%'}}
var K_6 = []key{{19, 4, '6'}}
var K_6_SHIFT = []key{{19, 4, '^'}}
var K_7 = []key{{22, 4, '7'}}
var K_7_SHIFT = []key{{22, 4, '&'}}
var K_8 = []key{{25, 4, '8'}}
var K_8_SHIFT = []key{{25, 4, '*'}}
var K_9 = []key{{28, 4, '9'}}
var K_9_SHIFT = []key{{28, 4, '('}}
var K_0 = []key{{31, 4, '0'}}
var K_0_SHIFT = []key{{31, 4, ')'}}
var K_MINUS = []key{{34, 4, '-'}}
var K_MINUS_SHIFT = []key{{34, 4, '_'}}
var K_EQUALS = []key{{37, 4, '='}}
var K_EQUALS_SHIFT = []key{{37, 4, '+'}}
var K_BACKSLASH = []key{{40, 4, '\\'}}
var K_BACKSLASH_SHIFT = []key{{40, 4, '|'}}
var K_BACKSPACE = []key{{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}}
var K_INS = []key{{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}}
var K_HOM = []key{{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}}
var K_PGU = []key{{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}}
var K_K_NUMLOCK = []key{{65, 4, 'N'}}
var K_K_SLASH = []key{{68, 4, '/'}}
var K_K_STAR = []key{{71, 4, '*'}}
var K_K_MINUS = []key{{74, 4, '-'}}
var K_TAB = []key{{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}}
var K_q = []key{{6, 6, 'q'}}
var K_Q = []key{{6, 6, 'Q'}}
var K_w = []key{{9, 6, 'w'}}
var K_W = []key{{9, 6, 'W'}}
var K_e = []key{{12, 6, 'e'}}
var K_E = []key{{12, 6, 'E'}}
var K_r = []key{{15, 6, 'r'}}
var K_R = []key{{15, 6, 'R'}}
var K_t = []key{{18, 6, 't'}}
var K_T = []key{{18, 6, 'T'}}
var K_y = []key{{21, 6, 'y'}}
var K_Y = []key{{21, 6, 'Y'}}
var K_u = []key{{24, 6, 'u'}}
var K_U = []key{{24, 6, 'U'}}
var K_i = []key{{27, 6, 'i'}}
var K_I = []key{{27, 6, 'I'}}
var K_o = []key{{30, 6, 'o'}}
var K_O = []key{{30, 6, 'O'}}
var K_p = []key{{33, 6, 'p'}}
var K_P = []key{{33, 6, 'P'}}
var K_LSQB = []key{{36, 6, '['}}
var K_LCUB = []key{{36, 6, '{'}}
var K_RSQB = []key{{39, 6, ']'}}
var K_RCUB = []key{{39, 6, '}'}}
var K_ENTER = []key{
{43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591},
{43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591},
{41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591},
{45, 8, 0x2591}, {46, 8, 0x2591},
}
var K_DEL = []key{{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}}
var K_END = []key{{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}}
var K_PGD = []key{{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}}
var K_K_7 = []key{{65, 6, '7'}}
var K_K_8 = []key{{68, 6, '8'}}
var K_K_9 = []key{{71, 6, '9'}}
var K_K_PLUS = []key{{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}}
var K_CAPS = []key{{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}}
var K_a = []key{{7, 8, 'a'}}
var K_A = []key{{7, 8, 'A'}}
var K_s = []key{{10, 8, 's'}}
var K_S = []key{{10, 8, 'S'}}
var K_d = []key{{13, 8, 'd'}}
var K_D = []key{{13, 8, 'D'}}
var K_f = []key{{16, 8, 'f'}}
var K_F = []key{{16, 8, 'F'}}
var K_g = []key{{19, 8, 'g'}}
var K_G = []key{{19, 8, 'G'}}
var K_h = []key{{22, 8, 'h'}}
var K_H = []key{{22, 8, 'H'}}
var K_j = []key{{25, 8, 'j'}}
var K_J = []key{{25, 8, 'J'}}
var K_k = []key{{28, 8, 'k'}}
var K_K = []key{{28, 8, 'K'}}
var K_l = []key{{31, 8, 'l'}}
var K_L = []key{{31, 8, 'L'}}
var K_SEMICOLON = []key{{34, 8, ';'}}
var K_PARENTHESIS = []key{{34, 8, ':'}}
var K_QUOTE = []key{{37, 8, '\''}}
var K_DOUBLEQUOTE = []key{{37, 8, '"'}}
var K_K_4 = []key{{65, 8, '4'}}
var K_K_5 = []key{{68, 8, '5'}}
var K_K_6 = []key{{71, 8, '6'}}
var K_LSHIFT = []key{{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}}
var K_z = []key{{9, 10, 'z'}}
var K_Z = []key{{9, 10, 'Z'}}
var K_x = []key{{12, 10, 'x'}}
var K_X = []key{{12, 10, 'X'}}
var K_c = []key{{15, 10, 'c'}}
var K_C = []key{{15, 10, 'C'}}
var K_v = []key{{18, 10, 'v'}}
var K_V = []key{{18, 10, 'V'}}
var K_b = []key{{21, 10, 'b'}}
var K_B = []key{{21, 10, 'B'}}
var K_n = []key{{24, 10, 'n'}}
var K_N = []key{{24, 10, 'N'}}
var K_m = []key{{27, 10, 'm'}}
var K_M = []key{{27, 10, 'M'}}
var K_COMMA = []key{{30, 10, ','}}
var K_LANB = []key{{30, 10, '<'}}
var K_PERIOD = []key{{33, 10, '.'}}
var K_RANB = []key{{33, 10, '>'}}
var K_SLASH = []key{{36, 10, '/'}}
var K_QUESTION = []key{{36, 10, '?'}}
var K_RSHIFT = []key{{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}}
var K_ARROW_UP = []key{{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}}
var K_K_1 = []key{{65, 10, '1'}}
var K_K_2 = []key{{68, 10, '2'}}
var K_K_3 = []key{{71, 10, '3'}}
var K_K_ENTER = []key{{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}}
var K_LCTRL = []key{{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}}
var K_LWIN = []key{{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}}
var K_LALT = []key{{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}}
var K_SPACE = []key{
{14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '},
{19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'},
{24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '},
}
var K_RALT = []key{{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}}
var K_RWIN = []key{{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}}
var K_RPROP = []key{{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}}
var K_RCTRL = []key{{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}}
var K_ARROW_LEFT = []key{{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}}
var K_ARROW_DOWN = []key{{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}}
var K_ARROW_RIGHT = []key{{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}}
var K_K_0 = []key{{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}}
var K_K_PERIOD = []key{{71, 12, '.'}}
type combo struct {
keys [][]key
}
var combos = []combo{
{[][]key{K_TILDE, K_2, K_SPACE, K_LCTRL, K_RCTRL}},
{[][]key{K_A, K_LCTRL, K_RCTRL}},
{[][]key{K_B, K_LCTRL, K_RCTRL}},
{[][]key{K_C, K_LCTRL, K_RCTRL}},
{[][]key{K_D, K_LCTRL, K_RCTRL}},
{[][]key{K_E, K_LCTRL, K_RCTRL}},
{[][]key{K_F, K_LCTRL, K_RCTRL}},
{[][]key{K_G, K_LCTRL, K_RCTRL}},
{[][]key{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL}},
{[][]key{K_I, K_TAB, K_LCTRL, K_RCTRL}},
{[][]key{K_J, K_LCTRL, K_RCTRL}},
{[][]key{K_K, K_LCTRL, K_RCTRL}},
{[][]key{K_L, K_LCTRL, K_RCTRL}},
{[][]key{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL}},
{[][]key{K_N, K_LCTRL, K_RCTRL}},
{[][]key{K_O, K_LCTRL, K_RCTRL}},
{[][]key{K_P, K_LCTRL, K_RCTRL}},
{[][]key{K_Q, K_LCTRL, K_RCTRL}},
{[][]key{K_R, K_LCTRL, K_RCTRL}},
{[][]key{K_S, K_LCTRL, K_RCTRL}},
{[][]key{K_T, K_LCTRL, K_RCTRL}},
{[][]key{K_U, K_LCTRL, K_RCTRL}},
{[][]key{K_V, K_LCTRL, K_RCTRL}},
{[][]key{K_W, K_LCTRL, K_RCTRL}},
{[][]key{K_X, K_LCTRL, K_RCTRL}},
{[][]key{K_Y, K_LCTRL, K_RCTRL}},
{[][]key{K_Z, K_LCTRL, K_RCTRL}},
{[][]key{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL}},
{[][]key{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL}},
{[][]key{K_RSQB, K_5, K_LCTRL, K_RCTRL}},
{[][]key{K_6, K_LCTRL, K_RCTRL}},
{[][]key{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL}},
{[][]key{K_SPACE}},
{[][]key{K_1_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT}},
{[][]key{K_3_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_4_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_5_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_7_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_QUOTE}},
{[][]key{K_9_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_0_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT}},
{[][]key{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT}},
{[][]key{K_COMMA}},
{[][]key{K_MINUS, K_K_MINUS}},
{[][]key{K_PERIOD, K_K_PERIOD}},
{[][]key{K_SLASH, K_K_SLASH}},
{[][]key{K_0, K_K_0}},
{[][]key{K_1, K_K_1}},
{[][]key{K_2, K_K_2}},
{[][]key{K_3, K_K_3}},
{[][]key{K_4, K_K_4}},
{[][]key{K_5, K_K_5}},
{[][]key{K_6, K_K_6}},
{[][]key{K_7, K_K_7}},
{[][]key{K_8, K_K_8}},
{[][]key{K_9, K_K_9}},
{[][]key{K_PARENTHESIS, K_LSHIFT, K_RSHIFT}},
{[][]key{K_SEMICOLON}},
{[][]key{K_LANB, K_LSHIFT, K_RSHIFT}},
{[][]key{K_EQUALS}},
{[][]key{K_RANB, K_LSHIFT, K_RSHIFT}},
{[][]key{K_QUESTION, K_LSHIFT, K_RSHIFT}},
{[][]key{K_2_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_A, K_LSHIFT, K_RSHIFT}},
{[][]key{K_B, K_LSHIFT, K_RSHIFT}},
{[][]key{K_C, K_LSHIFT, K_RSHIFT}},
{[][]key{K_D, K_LSHIFT, K_RSHIFT}},
{[][]key{K_E, K_LSHIFT, K_RSHIFT}},
{[][]key{K_F, K_LSHIFT, K_RSHIFT}},
{[][]key{K_G, K_LSHIFT, K_RSHIFT}},
{[][]key{K_H, K_LSHIFT, K_RSHIFT}},
{[][]key{K_I, K_LSHIFT, K_RSHIFT}},
{[][]key{K_J, K_LSHIFT, K_RSHIFT}},
{[][]key{K_K, K_LSHIFT, K_RSHIFT}},
{[][]key{K_L, K_LSHIFT, K_RSHIFT}},
{[][]key{K_M, K_LSHIFT, K_RSHIFT}},
{[][]key{K_N, K_LSHIFT, K_RSHIFT}},
{[][]key{K_O, K_LSHIFT, K_RSHIFT}},
{[][]key{K_P, K_LSHIFT, K_RSHIFT}},
{[][]key{K_Q, K_LSHIFT, K_RSHIFT}},
{[][]key{K_R, K_LSHIFT, K_RSHIFT}},
{[][]key{K_S, K_LSHIFT, K_RSHIFT}},
{[][]key{K_T, K_LSHIFT, K_RSHIFT}},
{[][]key{K_U, K_LSHIFT, K_RSHIFT}},
{[][]key{K_V, K_LSHIFT, K_RSHIFT}},
{[][]key{K_W, K_LSHIFT, K_RSHIFT}},
{[][]key{K_X, K_LSHIFT, K_RSHIFT}},
{[][]key{K_Y, K_LSHIFT, K_RSHIFT}},
{[][]key{K_Z, K_LSHIFT, K_RSHIFT}},
{[][]key{K_LSQB}},
{[][]key{K_BACKSLASH}},
{[][]key{K_RSQB}},
{[][]key{K_6_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_TILDE}},
{[][]key{K_a}},
{[][]key{K_b}},
{[][]key{K_c}},
{[][]key{K_d}},
{[][]key{K_e}},
{[][]key{K_f}},
{[][]key{K_g}},
{[][]key{K_h}},
{[][]key{K_i}},
{[][]key{K_j}},
{[][]key{K_k}},
{[][]key{K_l}},
{[][]key{K_m}},
{[][]key{K_n}},
{[][]key{K_o}},
{[][]key{K_p}},
{[][]key{K_q}},
{[][]key{K_r}},
{[][]key{K_s}},
{[][]key{K_t}},
{[][]key{K_u}},
{[][]key{K_v}},
{[][]key{K_w}},
{[][]key{K_x}},
{[][]key{K_y}},
{[][]key{K_z}},
{[][]key{K_LCUB, K_LSHIFT, K_RSHIFT}},
{[][]key{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_RCUB, K_LSHIFT, K_RSHIFT}},
{[][]key{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT}},
{[][]key{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL}},
}
var func_combos = []combo{
{[][]key{K_F1}},
{[][]key{K_F2}},
{[][]key{K_F3}},
{[][]key{K_F4}},
{[][]key{K_F5}},
{[][]key{K_F6}},
{[][]key{K_F7}},
{[][]key{K_F8}},
{[][]key{K_F9}},
{[][]key{K_F10}},
{[][]key{K_F11}},
{[][]key{K_F12}},
{[][]key{K_INS}},
{[][]key{K_DEL}},
{[][]key{K_HOM}},
{[][]key{K_END}},
{[][]key{K_PGU}},
{[][]key{K_PGD}},
{[][]key{K_ARROW_UP}},
{[][]key{K_ARROW_DOWN}},
{[][]key{K_ARROW_LEFT}},
{[][]key{K_ARROW_RIGHT}},
}
func print_tb(x, y int, fg, bg termbox.Attribute, msg string) {
for _, c := range msg {
termbox.SetCell(x, y, c, fg, bg)
x++
}
}
func printf_tb(x, y int, fg, bg termbox.Attribute, format string, args ...interface{}) {
s := fmt.Sprintf(format, args...)
print_tb(x, y, fg, bg, s)
}
func draw_key(k []key, fg, bg termbox.Attribute) {
for _, k := range k {
termbox.SetCell(k.x+2, k.y+4, k.ch, fg, bg)
}
}
func draw_keyboard() {
termbox.SetCell(0, 0, 0x250C, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(79, 0, 0x2510, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(0, 23, 0x2514, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(79, 23, 0x2518, termbox.ColorWhite, termbox.ColorBlack)
for i := 1; i < 79; i++ {
termbox.SetCell(i, 0, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(i, 23, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(i, 17, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(i, 4, 0x2500, termbox.ColorWhite, termbox.ColorBlack)
}
for i := 1; i < 23; i++ {
termbox.SetCell(0, i, 0x2502, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(79, i, 0x2502, termbox.ColorWhite, termbox.ColorBlack)
}
termbox.SetCell(0, 17, 0x251C, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(79, 17, 0x2524, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(0, 4, 0x251C, termbox.ColorWhite, termbox.ColorBlack)
termbox.SetCell(79, 4, 0x2524, termbox.ColorWhite, termbox.ColorBlack)
for i := 5; i < 17; i++ {
termbox.SetCell(1, i, 0x2588, termbox.ColorYellow, termbox.ColorYellow)
termbox.SetCell(78, i, 0x2588, termbox.ColorYellow, termbox.ColorYellow)
}
draw_key(K_ESC, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F1, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F2, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F3, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F4, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F5, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F6, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F7, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F8, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F9, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F10, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F11, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_F12, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_PRN, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_SCR, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_BRK, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LED1, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LED2, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LED3, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_TILDE, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_1, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_2, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_3, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_4, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_5, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_6, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_7, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_8, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_9, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_0, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_MINUS, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_EQUALS, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_BACKSLASH, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_BACKSPACE, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_INS, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_HOM, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_PGU, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_NUMLOCK, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_SLASH, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_STAR, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_MINUS, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_TAB, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_q, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_w, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_e, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_r, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_t, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_y, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_u, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_i, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_o, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_p, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LSQB, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_RSQB, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_ENTER, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_DEL, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_END, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_PGD, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_7, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_8, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_9, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_PLUS, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_CAPS, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_a, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_s, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_d, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_f, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_g, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_h, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_j, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_k, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_l, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_SEMICOLON, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_QUOTE, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_4, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_5, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_6, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LSHIFT, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_z, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_x, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_c, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_v, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_b, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_n, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_m, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_COMMA, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_PERIOD, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_SLASH, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_RSHIFT, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_ARROW_UP, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_1, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_2, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_3, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_ENTER, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LCTRL, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LWIN, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_LALT, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_SPACE, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_RCTRL, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_RPROP, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_RWIN, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_RALT, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_ARROW_LEFT, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_ARROW_DOWN, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_ARROW_RIGHT, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_0, termbox.ColorWhite, termbox.ColorBlue)
draw_key(K_K_PERIOD, termbox.ColorWhite, termbox.ColorBlue)
printf_tb(33, 1, termbox.ColorMagenta|termbox.AttrBold, termbox.ColorBlack, "Keyboard demo!")
printf_tb(21, 2, termbox.ColorMagenta, termbox.ColorBlack, "(press CTRL+X and then CTRL+Q to exit)")
printf_tb(15, 3, termbox.ColorMagenta, termbox.ColorBlack, "(press CTRL+X and then CTRL+C to change input mode)")
inputmode := termbox.SetInputMode(termbox.InputCurrent)
inputmode_str := ""
switch {
case inputmode&termbox.InputEsc != 0:
inputmode_str = "termbox.InputEsc"
case inputmode&termbox.InputAlt != 0:
inputmode_str = "termbox.InputAlt"
}
if inputmode&termbox.InputMouse != 0 {
inputmode_str += " | termbox.InputMouse"
}
printf_tb(3, 18, termbox.ColorWhite, termbox.ColorBlack, "Input mode: %s", inputmode_str)
}
var fcmap = []string{
"CTRL+2, CTRL+~",
"CTRL+A",
"CTRL+B",
"CTRL+C",
"CTRL+D",
"CTRL+E",
"CTRL+F",
"CTRL+G",
"CTRL+H, BACKSPACE",
"CTRL+I, TAB",
"CTRL+J",
"CTRL+K",
"CTRL+L",
"CTRL+M, ENTER",
"CTRL+N",
"CTRL+O",
"CTRL+P",
"CTRL+Q",
"CTRL+R",
"CTRL+S",
"CTRL+T",
"CTRL+U",
"CTRL+V",
"CTRL+W",
"CTRL+X",
"CTRL+Y",
"CTRL+Z",
"CTRL+3, ESC, CTRL+[",
"CTRL+4, CTRL+\\",
"CTRL+5, CTRL+]",
"CTRL+6",
"CTRL+7, CTRL+/, CTRL+_",
"SPACE",
}
var fkmap = []string{
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"INSERT",
"DELETE",
"HOME",
"END",
"PGUP",
"PGDN",
"ARROW UP",
"ARROW DOWN",
"ARROW LEFT",
"ARROW RIGHT",
}
func funckeymap(k termbox.Key) string {
if k == termbox.KeyCtrl8 {
return "CTRL+8, BACKSPACE 2" /* 0x7F */
} else if k >= termbox.KeyArrowRight && k <= 0xFFFF {
return fkmap[0xFFFF-k]
} else if k <= termbox.KeySpace {
return fcmap[k]
}
return "UNKNOWN"
}
func pretty_print_press(ev *termbox.Event) {
printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Key: ")
printf_tb(8, 19, termbox.ColorYellow, termbox.ColorBlack, "decimal: %d", ev.Key)
printf_tb(8, 20, termbox.ColorGreen, termbox.ColorBlack, "hex: 0x%X", ev.Key)
printf_tb(8, 21, termbox.ColorCyan, termbox.ColorBlack, "octal: 0%o", ev.Key)
printf_tb(8, 22, termbox.ColorRed, termbox.ColorBlack, "string: %s", funckeymap(ev.Key))
printf_tb(54, 19, termbox.ColorWhite, termbox.ColorBlack, "Char: ")
printf_tb(60, 19, termbox.ColorYellow, termbox.ColorBlack, "decimal: %d", ev.Ch)
printf_tb(60, 20, termbox.ColorGreen, termbox.ColorBlack, "hex: 0x%X", ev.Ch)
printf_tb(60, 21, termbox.ColorCyan, termbox.ColorBlack, "octal: 0%o", ev.Ch)
printf_tb(60, 22, termbox.ColorRed, termbox.ColorBlack, "string: %s", string(ev.Ch))
modifier := "none"
if ev.Mod != 0 {
modifier = "termbox.ModAlt"
}
printf_tb(54, 18, termbox.ColorWhite, termbox.ColorBlack, "Modifier: %s", modifier)
}
func pretty_print_resize(ev *termbox.Event) {
printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Resize event: %d x %d", ev.Width, ev.Height)
}
var counter = 0
func pretty_print_mouse(ev *termbox.Event) {
printf_tb(3, 19, termbox.ColorWhite, termbox.ColorBlack, "Mouse event: %d x %d", ev.MouseX, ev.MouseY)
button := ""
switch ev.Key {
case termbox.MouseLeft:
button = "MouseLeft: %d"
case termbox.MouseMiddle:
button = "MouseMiddle: %d"
case termbox.MouseRight:
button = "MouseRight: %d"
case termbox.MouseWheelUp:
button = "MouseWheelUp: %d"
case termbox.MouseWheelDown:
button = "MouseWheelDown: %d"
case termbox.MouseRelease:
button = "MouseRelease: %d"
}
if ev.Mod&termbox.ModMotion != 0 {
button += "*"
}
counter++
printf_tb(43, 19, termbox.ColorWhite, termbox.ColorBlack, "Key: ")
printf_tb(48, 19, termbox.ColorYellow, termbox.ColorBlack, button, counter)
}
func dispatch_press(ev *termbox.Event) {
if ev.Mod&termbox.ModAlt != 0 {
draw_key(K_LALT, termbox.ColorWhite, termbox.ColorRed)
draw_key(K_RALT, termbox.ColorWhite, termbox.ColorRed)
}
var k *combo
if ev.Key >= termbox.KeyArrowRight {
k = &func_combos[0xFFFF-ev.Key]
} else if ev.Ch < 128 {
if ev.Ch == 0 && ev.Key < 128 {
k = &combos[ev.Key]
} else {
k = &combos[ev.Ch]
}
}
if k == nil {
return
}
keys := k.keys
for _, k := range keys {
draw_key(k, termbox.ColorWhite, termbox.ColorRed)
}
}
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
defer termbox.Close()
termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
draw_keyboard()
termbox.Flush()
inputmode := 0
ctrlxpressed := false
loop:
for {
switch ev := termbox.PollEvent(); ev.Type {
case termbox.EventKey:
if ev.Key == termbox.KeyCtrlS && ctrlxpressed {
termbox.Sync()
}
if ev.Key == termbox.KeyCtrlQ && ctrlxpressed {
break loop
}
if ev.Key == termbox.KeyCtrlC && ctrlxpressed {
chmap := []termbox.InputMode{
termbox.InputEsc | termbox.InputMouse,
termbox.InputAlt | termbox.InputMouse,
termbox.InputEsc,
termbox.InputAlt,
}
inputmode++
if inputmode >= len(chmap) {
inputmode = 0
}
termbox.SetInputMode(chmap[inputmode])
}
if ev.Key == termbox.KeyCtrlX {
ctrlxpressed = true
} else {
ctrlxpressed = false
}
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
draw_keyboard()
dispatch_press(&ev)
pretty_print_press(&ev)
termbox.Flush()
case termbox.EventResize:
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
draw_keyboard()
pretty_print_resize(&ev)
termbox.Flush()
case termbox.EventMouse:
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
draw_keyboard()
pretty_print_mouse(&ev)
termbox.Flush()
case termbox.EventError:
panic(ev.Err)
}
}
}

@ -1,228 +0,0 @@
package main
import "github.com/mattn/go-runewidth"
import "github.com/nsf/termbox-go"
const chars = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB"
var output_mode = termbox.OutputNormal
func next_char(current int) int {
current++
if current >= len(chars) {
return 0
}
return current
}
func print_combinations_table(sx, sy int, attrs []termbox.Attribute) {
var bg termbox.Attribute
current_char := 0
y := sy
all_attrs := []termbox.Attribute{
0,
termbox.AttrBold,
termbox.AttrUnderline,
termbox.AttrBold | termbox.AttrUnderline,
}
draw_line := func() {
x := sx
for _, a := range all_attrs {
for c := termbox.ColorDefault; c <= termbox.ColorWhite; c++ {
fg := a | c
termbox.SetCell(x, y, rune(chars[current_char]), fg, bg)
current_char = next_char(current_char)
x++
}
}
}
for _, a := range attrs {
for c := termbox.ColorDefault; c <= termbox.ColorWhite; c++ {
bg = a | c
draw_line()
y++
}
}
}
func print_wide(x, y int, s string) {
red := false
for _, r := range s {
c := termbox.ColorDefault
if red {
c = termbox.ColorRed
}
termbox.SetCell(x, y, r, termbox.ColorDefault, c)
w := runewidth.RuneWidth(r)
if w == 0 || (w == 2 && runewidth.IsAmbiguousWidth(r)) {
w = 1
}
x += w
red = !red
}
}
const hello_world = "こんにちは世界"
func draw_all() {
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
switch output_mode {
case termbox.OutputNormal:
print_combinations_table(1, 1, []termbox.Attribute{
0,
termbox.AttrBold,
})
print_combinations_table(2+len(chars), 1, []termbox.Attribute{
termbox.AttrReverse,
})
print_wide(2+len(chars), 11, hello_world)
case termbox.OutputGrayscale:
for y := 0; y < 26; y++ {
for x := 0; x < 26; x++ {
termbox.SetCell(x, y, 'n',
termbox.Attribute(x+1),
termbox.Attribute(y+1))
termbox.SetCell(x+27, y, 'b',
termbox.Attribute(x+1)|termbox.AttrBold,
termbox.Attribute(26-y))
termbox.SetCell(x+54, y, 'u',
termbox.Attribute(x+1)|termbox.AttrUnderline,
termbox.Attribute(y+1))
}
termbox.SetCell(82, y, 'd',
termbox.Attribute(y+1),
termbox.ColorDefault)
termbox.SetCell(83, y, 'd',
termbox.ColorDefault,
termbox.Attribute(26-y))
}
case termbox.Output216:
for r := 0; r < 6; r++ {
for g := 0; g < 6; g++ {
for b := 0; b < 6; b++ {
y := r
x := g + 6*b
c1 := termbox.Attribute(1 + r*36 + g*6 + b)
bg := termbox.Attribute(1 + g*36 + b*6 + r)
c2 := termbox.Attribute(1 + b*36 + r*6 + g)
bc1 := c1 | termbox.AttrBold
uc1 := c1 | termbox.AttrUnderline
bc2 := c2 | termbox.AttrBold
uc2 := c2 | termbox.AttrUnderline
termbox.SetCell(x, y, 'n', c1, bg)
termbox.SetCell(x, y+6, 'b', bc1, bg)
termbox.SetCell(x, y+12, 'u', uc1, bg)
termbox.SetCell(x, y+18, 'B', bc1|uc1, bg)
termbox.SetCell(x+37, y, 'n', c2, bg)
termbox.SetCell(x+37, y+6, 'b', bc2, bg)
termbox.SetCell(x+37, y+12, 'u', uc2, bg)
termbox.SetCell(x+37, y+18, 'B', bc2|uc2, bg)
}
c1 := termbox.Attribute(1 + g*6 + r*36)
c2 := termbox.Attribute(6 + g*6 + r*36)
termbox.SetCell(74+g, r, 'd', c1, termbox.ColorDefault)
termbox.SetCell(74+g, r+6, 'd', c2, termbox.ColorDefault)
termbox.SetCell(74+g, r+12, 'd', termbox.ColorDefault, c1)
termbox.SetCell(74+g, r+18, 'd', termbox.ColorDefault, c2)
}
}
case termbox.Output256:
for y := 0; y < 4; y++ {
for x := 0; x < 8; x++ {
for z := 0; z < 8; z++ {
bg := termbox.Attribute(1 + y*64 + x*8 + z)
c1 := termbox.Attribute(256 - y*64 - x*8 - z)
c2 := termbox.Attribute(1 + y*64 + z*8 + x)
c3 := termbox.Attribute(256 - y*64 - z*8 - x)
c4 := termbox.Attribute(1 + y*64 + x*4 + z*4)
bold := c2 | termbox.AttrBold
under := c3 | termbox.AttrUnderline
both := c1 | termbox.AttrBold | termbox.AttrUnderline
termbox.SetCell(z+8*x, y, ' ', 0, bg)
termbox.SetCell(z+8*x, y+5, 'n', c4, bg)
termbox.SetCell(z+8*x, y+10, 'b', bold, bg)
termbox.SetCell(z+8*x, y+15, 'u', under, bg)
termbox.SetCell(z+8*x, y+20, 'B', both, bg)
}
}
}
for x := 0; x < 12; x++ {
for y := 0; y < 2; y++ {
c1 := termbox.Attribute(233 + y*12 + x)
termbox.SetCell(66+x, y, 'd', c1, termbox.ColorDefault)
termbox.SetCell(66+x, 2+y, 'd', termbox.ColorDefault, c1)
}
}
for x := 0; x < 6; x++ {
for y := 0; y < 6; y++ {
c1 := termbox.Attribute(17 + x*6 + y*36)
c2 := termbox.Attribute(17 + 5 + x*6 + y*36)
termbox.SetCell(66+x, 6+y, 'd', c1, termbox.ColorDefault)
termbox.SetCell(66+x, 12+y, 'd', c2, termbox.ColorDefault)
termbox.SetCell(72+x, 6+y, 'd', termbox.ColorDefault, c1)
termbox.SetCell(72+x, 12+y, 'd', termbox.ColorDefault, c2)
}
}
}
termbox.Flush()
}
var available_modes = []termbox.OutputMode{
termbox.OutputNormal,
termbox.OutputGrayscale,
termbox.Output216,
termbox.Output256,
}
var output_mode_index = 0
func switch_output_mode(direction int) {
output_mode_index += direction
if output_mode_index < 0 {
output_mode_index = len(available_modes) - 1
} else if output_mode_index >= len(available_modes) {
output_mode_index = 0
}
output_mode = termbox.SetOutputMode(available_modes[output_mode_index])
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
termbox.Sync()
}
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
defer termbox.Close()
draw_all()
loop:
for {
switch ev := termbox.PollEvent(); ev.Type {
case termbox.EventKey:
switch ev.Key {
case termbox.KeyEsc:
break loop
case termbox.KeyArrowUp, termbox.KeyArrowRight:
switch_output_mode(1)
draw_all()
case termbox.KeyArrowDown, termbox.KeyArrowLeft:
switch_output_mode(-1)
draw_all()
}
case termbox.EventResize:
draw_all()
}
}
}

@ -1,105 +0,0 @@
package main
import (
"github.com/nsf/termbox-go"
)
var curCol = 0
var curRune = 0
var backbuf []termbox.Cell
var bbw, bbh int
var runes = []rune{' ', '░', '▒', '▓', '█'}
var colors = []termbox.Attribute{
termbox.ColorBlack,
termbox.ColorRed,
termbox.ColorGreen,
termbox.ColorYellow,
termbox.ColorBlue,
termbox.ColorMagenta,
termbox.ColorCyan,
termbox.ColorWhite,
}
type attrFunc func(int) (rune, termbox.Attribute, termbox.Attribute)
func updateAndDrawButtons(current *int, x, y int, mx, my int, n int, attrf attrFunc) {
lx, ly := x, y
for i := 0; i < n; i++ {
if lx <= mx && mx <= lx+3 && ly <= my && my <= ly+1 {
*current = i
}
r, fg, bg := attrf(i)
termbox.SetCell(lx+0, ly+0, r, fg, bg)
termbox.SetCell(lx+1, ly+0, r, fg, bg)
termbox.SetCell(lx+2, ly+0, r, fg, bg)
termbox.SetCell(lx+3, ly+0, r, fg, bg)
termbox.SetCell(lx+0, ly+1, r, fg, bg)
termbox.SetCell(lx+1, ly+1, r, fg, bg)
termbox.SetCell(lx+2, ly+1, r, fg, bg)
termbox.SetCell(lx+3, ly+1, r, fg, bg)
lx += 4
}
lx, ly = x, y
for i := 0; i < n; i++ {
if *current == i {
fg := termbox.ColorRed | termbox.AttrBold
bg := termbox.ColorDefault
termbox.SetCell(lx+0, ly+2, '^', fg, bg)
termbox.SetCell(lx+1, ly+2, '^', fg, bg)
termbox.SetCell(lx+2, ly+2, '^', fg, bg)
termbox.SetCell(lx+3, ly+2, '^', fg, bg)
}
lx += 4
}
}
func update_and_redraw_all(mx, my int) {
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
if mx != -1 && my != -1 {
backbuf[bbw*my+mx] = termbox.Cell{Ch: runes[curRune], Fg: colors[curCol]}
}
copy(termbox.CellBuffer(), backbuf)
_, h := termbox.Size()
updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), func(i int) (rune, termbox.Attribute, termbox.Attribute) {
return runes[i], termbox.ColorDefault, termbox.ColorDefault
})
updateAndDrawButtons(&curCol, 0, h-3, mx, my, len(colors), func(i int) (rune, termbox.Attribute, termbox.Attribute) {
return ' ', termbox.ColorDefault, colors[i]
})
termbox.Flush()
}
func reallocBackBuffer(w, h int) {
bbw, bbh = w, h
backbuf = make([]termbox.Cell, w*h)
}
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
defer termbox.Close()
termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
reallocBackBuffer(termbox.Size())
update_and_redraw_all(-1, -1)
mainloop:
for {
mx, my := -1, -1
switch ev := termbox.PollEvent(); ev.Type {
case termbox.EventKey:
if ev.Key == termbox.KeyEsc {
break mainloop
}
case termbox.EventMouse:
if ev.Key == termbox.MouseLeft {
mx, my = ev.MouseX, ev.MouseY
}
case termbox.EventResize:
reallocBackBuffer(ev.Width, ev.Height)
}
update_and_redraw_all(mx, my)
}
}

@ -1,46 +0,0 @@
package main
import "github.com/nsf/termbox-go"
import "math/rand"
import "time"
func draw() {
w, h := termbox.Size()
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
termbox.SetCell(x, y, ' ', termbox.ColorDefault,
termbox.Attribute(rand.Int()%8)+1)
}
}
termbox.Flush()
}
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
defer termbox.Close()
event_queue := make(chan termbox.Event)
go func() {
for {
event_queue <- termbox.PollEvent()
}
}()
draw()
loop:
for {
select {
case ev := <-event_queue:
if ev.Type == termbox.EventKey && ev.Key == termbox.KeyEsc {
break loop
}
default:
draw()
time.Sleep(10 * time.Millisecond)
}
}
}

@ -1,109 +0,0 @@
package main
import (
"fmt"
"github.com/nsf/termbox-go"
"strings"
)
func tbprint(x, y int, fg, bg termbox.Attribute, msg string) {
for _, c := range msg {
termbox.SetCell(x, y, c, fg, bg)
x++
}
}
var current string
var curev termbox.Event
func mouse_button_str(k termbox.Key) string {
switch k {
case termbox.MouseLeft:
return "MouseLeft"
case termbox.MouseMiddle:
return "MouseMiddle"
case termbox.MouseRight:
return "MouseRight"
case termbox.MouseRelease:
return "MouseRelease"
case termbox.MouseWheelUp:
return "MouseWheelUp"
case termbox.MouseWheelDown:
return "MouseWheelDown"
}
return "Key"
}
func mod_str(m termbox.Modifier) string {
var out []string
if m&termbox.ModAlt != 0 {
out = append(out, "ModAlt")
}
if m&termbox.ModMotion != 0 {
out = append(out, "ModMotion")
}
return strings.Join(out, " | ")
}
func redraw_all() {
const coldef = termbox.ColorDefault
termbox.Clear(coldef, coldef)
tbprint(0, 0, termbox.ColorMagenta, coldef, "Press 'q' to quit")
tbprint(0, 1, coldef, coldef, current)
switch curev.Type {
case termbox.EventKey:
tbprint(0, 2, coldef, coldef,
fmt.Sprintf("EventKey: k: %d, c: %c, mod: %s", curev.Key, curev.Ch, mod_str(curev.Mod)))
case termbox.EventMouse:
tbprint(0, 2, coldef, coldef,
fmt.Sprintf("EventMouse: x: %d, y: %d, b: %s, mod: %s",
curev.MouseX, curev.MouseY, mouse_button_str(curev.Key), mod_str(curev.Mod)))
case termbox.EventNone:
tbprint(0, 2, coldef, coldef, "EventNone")
}
tbprint(0, 3, coldef, coldef, fmt.Sprintf("%d", curev.N))
termbox.Flush()
}
func main() {
err := termbox.Init()
if err != nil {
panic(err)
}
defer termbox.Close()
termbox.SetInputMode(termbox.InputAlt | termbox.InputMouse)
redraw_all()
data := make([]byte, 0, 64)
mainloop:
for {
if cap(data)-len(data) < 32 {
newdata := make([]byte, len(data), len(data)+32)
copy(newdata, data)
data = newdata
}
beg := len(data)
d := data[beg : beg+32]
switch ev := termbox.PollRawEvent(d); ev.Type {
case termbox.EventRaw:
data = data[:beg+ev.N]
current = fmt.Sprintf("%q", data)
if current == `"q"` {
break mainloop
}
for {
ev := termbox.ParseEvent(data)
if ev.N == 0 {
break
}
curev = ev
copy(data, data[curev.N:])
data = data[:len(data)-curev.N]
}
case termbox.EventError:
panic(ev.Err)
}
redraw_all()
}
}

@ -1,68 +0,0 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import (
"image"
"sync"
tm "github.com/miguelmota/cointop/pkg/termbox"
)
// Bufferer should be implemented by all renderable components.
type Bufferer interface {
Buffer() Buffer
}
// Close finalizes termui library,
// should be called after successful initialization when termui's functionality isn't required anymore.
func Close() {
tm.Close()
}
var renderLock sync.Mutex
func termSync() {
renderLock.Lock()
tm.Sync()
termWidth, termHeight = tm.Size()
renderLock.Unlock()
}
// TermWidth returns the current terminal's width.
func TermWidth() int {
termSync()
return termWidth
}
// TermHeight returns the current terminal's height.
func TermHeight() int {
termSync()
return termHeight
}
func Clear() {
tm.Clear(tm.ColorDefault, toTmAttr(ThemeAttr("bg")))
}
func clearArea(r image.Rectangle, bg Attribute) {
for i := r.Min.X; i < r.Max.X; i++ {
for j := r.Min.Y; j < r.Max.Y; j++ {
tm.SetCell(i, j, ' ', tm.ColorDefault, toTmAttr(bg))
}
}
}
func ClearArea(r image.Rectangle, bg Attribute) {
clearArea(r, bg)
tm.Flush()
}
var renderJobs chan []Bufferer
func Render(bs ...Bufferer) {
//go func() { renderJobs <- bs }()
renderJobs <- bs
}

@ -0,0 +1,26 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
.DS_Store
/vendor

@ -0,0 +1,6 @@
language: go
go:
- tip
script: go test -v ./

@ -0,0 +1,81 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:2eeeebccad4f052e6037527e86b8114c6bfd184a97f84d4449a5ea6ad202c216"
name = "github.com/maruel/panicparse"
packages = ["stack"]
pruneopts = "UT"
revision = "4417700b5a8d33fbb4416bb04359d20329f60008"
[[projects]]
digest = "1:e2d1d410fb367567c2b53ed9e2d719d3c1f0891397bb2fa49afd747cfbf1e8e4"
name = "github.com/mattn/go-runewidth"
packages = ["."]
pruneopts = "UT"
revision = "9e777a8366cce605130a531d2cd6363d07ad7317"
version = "v0.0.2"
[[projects]]
branch = "master"
digest = "1:e68cd472b96cdf7c9f6971ac41bcc1d4d3b23d67c2a31d2399446e295bc88ae9"
name = "github.com/mitchellh/go-wordwrap"
packages = ["."]
pruneopts = "UT"
revision = "ad45545899c7b13c020ea92b2072220eefad42b8"
[[projects]]
branch = "master"
digest = "1:c9b6e36dbd23f8403a04493376916ca5dad8c01b2da5ae0a05e6a468eb0b6f24"
name = "github.com/nsf/termbox-go"
packages = ["."]
pruneopts = "UT"
revision = "5c94acc5e6eb520f1bcd183974e01171cc4c23b3"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83"
name = "github.com/stretchr/testify"
packages = ["assert"]
pruneopts = "UT"
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
[[projects]]
branch = "master"
digest = "1:7427036b6d926b3d1fa0773771820735b35554a98296f1074df440ee30a97296"
name = "golang.org/x/net"
packages = ["websocket"]
pruneopts = "UT"
revision = "f4c29de78a2a91c00474a2e689954305c350adf9"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/davecgh/go-spew/spew",
"github.com/maruel/panicparse/stack",
"github.com/mattn/go-runewidth",
"github.com/mitchellh/go-wordwrap",
"github.com/nsf/termbox-go",
"github.com/stretchr/testify/assert",
"golang.org/x/net/websocket",
]
solver-name = "gps-cdcl"
solver-version = 1

@ -0,0 +1,15 @@
[[constraint]]
name = "github.com/maruel/panicparse"
branch = "master"
[[constraint]]
name = "github.com/davecgh/go-spew"
version = "1.1.0"
[[constraint]]
name = "github.com/mitchellh/go-wordwrap"
branch = "master"
[prune]
go-tests = true
unused-packages = true

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Zack Guo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,151 @@
# termui [![Build Status](https://travis-ci.org/gizak/termui.svg?branch=master)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui)
<img src="./_example/dashboard.gif" alt="demo cast under osx 10.10; Terminal.app; Menlo Regular 12pt.)" width="80%">
`termui` is a cross-platform, easy-to-compile, and fully-customizable terminal dashboard. It is inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib), but purely in Go.
Now version v2 has arrived! It brings new event system, new theme system, new `Buffer` interface and specific colour text rendering. (some docs are missing, but it will be completed soon!)
## Installation
`master` mirrors v2 branch, to install:
go get -u github.com/gizak/termui
It is recommanded to use locked deps by using [dep](https://golang.github.io/dep/): move to `termui` src directory then run `dep ensure`.
For the compatible reason, you can choose to install the legacy version of `termui`:
go get gopkg.in/gizak/termui.v1
## Usage
### Layout
To use `termui`, the very first thing you may want to know is how to manage layout. `termui` offers two ways of doing this, known as absolute layout and grid layout.
__Absolute layout__
Each widget has an underlying block structure which basically is a box model. It has border, label and padding properties. A border of a widget can be chosen to hide or display (with its border label), you can pick a different front/back colour for the border as well. To display such a widget at a specific location in terminal window, you need to assign `.X`, `.Y`, `.Height`, `.Width` values for each widget before sending it to `.Render`. Let's demonstrate these by a code snippet:
`````go
import ui "github.com/gizak/termui" // <- ui shortcut, optional
func main() {
err := ui.Init()
if err != nil {
panic(err)
}
defer ui.Close()
p := ui.NewPar(":PRESS q TO QUIT DEMO")
p.Height = 3
p.Width = 50
p.TextFgColor = ui.ColorWhite
p.BorderLabel = "Text Box"
p.BorderFg = ui.ColorCyan
g := ui.NewGauge()
g.Percent = 50
g.Width = 50
g.Height = 3
g.Y = 11
g.BorderLabel = "Gauge"
g.BarColor = ui.ColorRed
g.BorderFg = ui.ColorWhite
g.BorderLabelFg = ui.ColorCyan
ui.Render(p, g) // feel free to call Render, it's async and non-block
// event handler...
}
`````
Note that components can be overlapped (I'd rather call this a feature...), `Render(rs ...Renderer)` renders its args from left to right (i.e. each component's weight is arising from left to right).
__Grid layout:__
<img src="./_example/grid.gif" alt="grid" width="60%">
Grid layout uses [12 columns grid system](http://www.w3schools.com/bootstrap/bootstrap_grid_system.asp) with expressive syntax. To use `Grid`, all we need to do is build a widget tree consisting of `Row`s and `Col`s (Actually a `Col` is also a `Row` but with a widget endpoint attached).
```go
import ui "github.com/gizak/termui"
// init and create widgets...
// build
ui.Body.AddRows(
ui.NewRow(
ui.NewCol(6, 0, widget0),
ui.NewCol(6, 0, widget1)),
ui.NewRow(
ui.NewCol(3, 0, widget2),
ui.NewCol(3, 0, widget30, widget31, widget32),
ui.NewCol(6, 0, widget4)))
// calculate layout
ui.Body.Align()
ui.Render(ui.Body)
```
### Events
`termui` ships with a http-like event mux handling system. All events are channeled up from different sources (typing, click, windows resize, custom event) and then encoded as universal `Event` object. `Event.Path` indicates the event type and `Event.Data` stores the event data struct. Add a handler to a certain event is easy as below:
```go
// handle key q pressing
ui.Handle("/sys/kbd/q", func(ui.Event) {
// press q to quit
ui.StopLoop()
})
ui.Handle("/sys/kbd/C-x", func(ui.Event) {
// handle Ctrl + x combination
})
ui.Handle("/sys/kbd", func(ui.Event) {
// handle all other key pressing
})
// handle a 1s timer
ui.Handle("/timer/1s", func(e ui.Event) {
t := e.Data.(ui.EvtTimer)
// t is a EvtTimer
if t.Count%2 ==0 {
// do something
}
})
ui.Loop() // block until StopLoop is called
```
### Widgets
Click image to see the corresponding demo codes.
[<img src="./_example/par.png" alt="par" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/par.go)
[<img src="./_example/list.png" alt="list" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/list.go)
[<img src="./_example/gauge.png" alt="gauge" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/gauge.go)
[<img src="./_example/linechart.png" alt="linechart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/linechart.go)
[<img src="./_example/barchart.png" alt="barchart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/barchart.go)
[<img src="./_example/mbarchart.png" alt="barchart" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/mbarchart.go)
[<img src="./_example/sparklines.png" alt="sparklines" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/sparklines.go)
[<img src="./_example/table.png" alt="table" type="image/png" width="45%">](https://github.com/gizak/termui/blob/master/_example/table.go)
## GoDoc
[godoc](https://godoc.org/github.com/gizak/termui)
## TODO
- [x] Grid layout
- [x] Event system
- [x] Canvas widget
- [x] Refine APIs
- [ ] Focusable widgets
## Changelog
## License
This library is under the [MIT License](http://opensource.org/licenses/MIT)

@ -0,0 +1,149 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import "fmt"
// BarChart creates multiple bars in a widget:
/*
bc := termui.NewBarChart()
data := []int{3, 2, 5, 3, 9, 5}
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
bc.BorderLabel = "Bar Chart"
bc.Data = data
bc.Width = 26
bc.Height = 10
bc.DataLabels = bclabels
bc.TextColor = termui.ColorGreen
bc.BarColor = termui.ColorRed
bc.NumColor = termui.ColorYellow
*/
type BarChart struct {
Block
BarColor Attribute
TextColor Attribute
NumColor Attribute
Data []int
DataLabels []string
BarWidth int
BarGap int
CellChar rune
labels [][]rune
dataNum [][]rune
numBar int
scale float64
max int
}
// NewBarChart returns a new *BarChart with current theme.
func NewBarChart() *BarChart {
bc := &BarChart{Block: *NewBlock()}
bc.BarColor = ThemeAttr("barchart.bar.bg")
bc.NumColor = ThemeAttr("barchart.num.fg")
bc.TextColor = ThemeAttr("barchart.text.fg")
bc.BarGap = 1
bc.BarWidth = 3
bc.CellChar = ' '
return bc
}
func (bc *BarChart) layout() {
bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
bc.labels = make([][]rune, bc.numBar)
bc.dataNum = make([][]rune, len(bc.Data))
for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ {
bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
n := bc.Data[i]
s := fmt.Sprint(n)
bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth)
}
//bc.max = bc.Data[0] // what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range
// Assign a negative value to get maxvalue auto-populates
if bc.max == 0 {
bc.max = -1
}
for i := 0; i < len(bc.Data); i++ {
if bc.max < bc.Data[i] {
bc.max = bc.Data[i]
}
}
bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
}
func (bc *BarChart) SetMax(max int) {
if max > 0 {
bc.max = max
}
}
// Buffer implements Bufferer interface.
func (bc *BarChart) Buffer() Buffer {
buf := bc.Block.Buffer()
bc.layout()
for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
h := int(float64(bc.Data[i]) / bc.scale)
oftX := i * (bc.BarWidth + bc.BarGap)
barBg := bc.Bg
barFg := bc.BarColor
if bc.CellChar == ' ' {
barBg = bc.BarColor
barFg = ColorDefault
if bc.BarColor == ColorDefault { // the same as above
barBg |= AttrReverse
}
}
// plot bar
for j := 0; j < bc.BarWidth; j++ {
for k := 0; k < h; k++ {
c := Cell{
Ch: bc.CellChar,
Bg: barBg,
Fg: barFg,
}
x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k
buf.Set(x, y, c)
}
}
// plot text
for j, k := 0, 0; j < len(bc.labels[i]); j++ {
w := charWidth(bc.labels[i][j])
c := Cell{
Ch: bc.labels[i][j],
Bg: bc.Bg,
Fg: bc.TextColor,
}
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
x := bc.innerArea.Min.X + oftX + k
buf.Set(x, y, c)
k += w
}
// plot num
for j := 0; j < len(bc.dataNum[i]); j++ {
c := Cell{
Ch: bc.dataNum[i][j],
Fg: bc.NumColor,
Bg: barBg,
}
if h == 0 {
c.Bg = bc.Bg
}
x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
buf.Set(x, y, c)
}
}
return buf
}

@ -83,6 +83,16 @@ func (b Block) drawBorder(buf Buffer) {
}
}
func (b Block) drawBorderLabel(buf Buffer) {
maxTxtW := b.area.Dx() - 2
tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW)
for i, w := 0, 0; i < len(tx); i++ {
buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i])
w += tx[i].Width()
}
}
// Block is a base struct for all other upper level widgets,
// consider it as css: display:block.
// Normally you do not need to create it manually.
@ -189,6 +199,7 @@ func (b *Block) Buffer() Buffer {
buf.Fill(' ', ColorDefault, b.Bg)
b.drawBorder(buf)
b.drawBorderLabel(buf)
return buf
}

@ -12,3 +12,9 @@ const HORIZONTAL_LINE = '-'
const TOP_LEFT = '+'
const BOTTOM_RIGHT = '+'
const BOTTOM_LEFT = '+'
const VERTICAL_LEFT = '+'
const VERTICAL_RIGHT = '+'
const HORIZONTAL_DOWN = '+'
const HORIZONTAL_UP = '+'
const QUOTA_LEFT = '<'
const QUOTA_RIGHT = '>'

@ -0,0 +1,29 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
/*
Package termui is a library designed for creating command line UI. For more info, goto http://github.com/gizak/termui
A simplest example:
package main
import ui "github.com/gizak/termui"
func main() {
if err:=ui.Init(); err != nil {
panic(err)
}
defer ui.Close()
g := ui.NewGauge()
g.Percent = 50
g.Width = 50
g.BorderLabel = "Gauge"
ui.Render(g)
ui.Loop()
}
*/
package termui

@ -10,7 +10,7 @@ import (
"sync"
"time"
"github.com/miguelmota/cointop/pkg/termbox"
"github.com/nsf/termbox-go"
)
type Event struct {
@ -126,7 +126,7 @@ func hookTermboxEvt() {
e := termbox.PollEvent()
for _, c := range sysEvtChs {
go func(ch chan Event) {
func(ch chan Event) {
ch <- crtTermboxEvt(e)
}(c)
}
@ -244,7 +244,7 @@ func (es *EvtStream) Loop() {
case "/sig/stoploop":
return
}
go func(a Event) {
func(a Event) {
es.RLock()
defer es.RUnlock()
if pattern := es.match(a.Path); pattern != "" {
@ -274,6 +274,10 @@ func Handle(path string, handler func(Event)) {
DefaultEvtStream.Handle(path, handler)
}
func ResetHandlers() {
DefaultEvtStream.ResetHandlers()
}
func Loop() {
DefaultEvtStream.Loop()
}
@ -310,7 +314,7 @@ func NewTimerCh(du time.Duration) chan Event {
return t
}
var DefualtHandler = func(e Event) {
var DefaultHandler = func(e Event) {
}
var usrEvtCh = make(chan Event)

@ -0,0 +1,109 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import (
"strconv"
"strings"
)
// Gauge is a progress bar like widget.
// A simple example:
/*
g := termui.NewGauge()
g.Percent = 40
g.Width = 50
g.Height = 3
g.BorderLabel = "Slim Gauge"
g.BarColor = termui.ColorRed
g.PercentColor = termui.ColorBlue
*/
const ColorUndef Attribute = Attribute(^uint16(0))
type Gauge struct {
Block
Percent int
BarColor Attribute
PercentColor Attribute
PercentColorHighlighted Attribute
Label string
LabelAlign Align
}
// NewGauge return a new gauge with current theme.
func NewGauge() *Gauge {
g := &Gauge{
Block: *NewBlock(),
PercentColor: ThemeAttr("gauge.percent.fg"),
BarColor: ThemeAttr("gauge.bar.bg"),
Label: "{{percent}}%",
LabelAlign: AlignCenter,
PercentColorHighlighted: ColorUndef,
}
g.Width = 12
g.Height = 5
return g
}
// Buffer implements Bufferer interface.
func (g *Gauge) Buffer() Buffer {
buf := g.Block.Buffer()
// plot bar
w := g.Percent * g.innerArea.Dx() / 100
for i := 0; i < g.innerArea.Dy(); i++ {
for j := 0; j < w; j++ {
c := Cell{}
c.Ch = ' '
c.Bg = g.BarColor
if c.Bg == ColorDefault {
c.Bg |= AttrReverse
}
buf.Set(g.innerArea.Min.X+j, g.innerArea.Min.Y+i, c)
}
}
// plot percentage
s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1)
pry := g.innerArea.Min.Y + g.innerArea.Dy()/2
rs := str2runes(s)
var pos int
switch g.LabelAlign {
case AlignLeft:
pos = 0
case AlignCenter:
pos = (g.innerArea.Dx() - strWidth(s)) / 2
case AlignRight:
pos = g.innerArea.Dx() - strWidth(s) - 1
}
pos += g.innerArea.Min.X
for i, v := range rs {
c := Cell{
Ch: v,
Fg: g.PercentColor,
}
if w+g.innerArea.Min.X > pos+i {
c.Bg = g.BarColor
if c.Bg == ColorDefault {
c.Bg |= AttrReverse
}
if g.PercentColorHighlighted != ColorUndef {
c.Fg = g.PercentColorHighlighted
}
} else {
c.Bg = g.Block.Bg
}
buf.Set(1+pos+i, pry, c)
}
return buf
}

@ -48,7 +48,7 @@ func (r *Row) assignWidth(w int) {
accW := 0 // acc span and offset
calcW := make([]int, len(r.Cols)) // calculated width
calcOftX := make([]int, len(r.Cols)) // computated start position of x
calcOftX := make([]int, len(r.Cols)) // computed start position of x
for i, c := range r.Cols {
accW += c.Span + c.Offset
@ -266,7 +266,7 @@ func (g *Grid) Align() {
}
}
// Buffer implments Bufferer interface.
// Buffer implements Bufferer interface.
func (g Grid) Buffer() Buffer {
buf := NewBuffer()

@ -8,7 +8,7 @@ import (
"regexp"
"strings"
tm "github.com/miguelmota/cointop/pkg/termbox"
tm "github.com/nsf/termbox-go"
)
import rw "github.com/mattn/go-runewidth"
@ -100,7 +100,7 @@ func charWidth(ch rune) int {
var whiteSpaceRegex = regexp.MustCompile(`\s`)
// StringToAttribute converts text to a termui attribute. You may specifiy more
// StringToAttribute converts text to a termui attribute. You may specify more
// then one attribute like that: "BLACK, BOLD, ...". All whitespaces
// are ignored.
func StringToAttribute(text string) Attribute {

@ -35,7 +35,7 @@ var braillePatterns = map[[2]int]rune{
var lSingleBraille = [4]rune{'\u2840', '⠄', '⠂', '⠁'}
var rSingleBraille = [4]rune{'\u2880', '⠠', '⠐', '⠈'}
// LineChart has two modes: braille(default) and dot. Using braille gives 2x capicity as dot mode,
// LineChart has two modes: braille(default) and dot. Using braille gives 2x capacity as dot mode,
// because one braille char can represent two data points.
/*
lc := termui.NewLineChart()
@ -87,7 +87,7 @@ func NewLineChart() *LineChart {
}
// one cell contains two data points
// so the capicity is 2x as dot-mode
// so the capacity is 2x as dot-mode
func (lc *LineChart) renderBraille() Buffer {
buf := NewBuffer()
@ -135,6 +135,7 @@ func (lc *LineChart) renderBraille() Buffer {
func (lc *LineChart) renderDot() Buffer {
buf := NewBuffer()
lasty := -1 // previous y val
for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ {
c := Cell{
Ch: lc.DotStyle,
@ -143,6 +144,22 @@ func (lc *LineChart) renderDot() Buffer {
}
x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5)
if lasty != -1 && lasty != y {
u := 1 // direction
if lasty > y {
u = -1 // put dot below
}
for fy := lasty + u; fy != y; fy += u { // fy: filling point's y val
dx := -1 // lastx := x-1 = x+dx
if u*(fy-lasty) >= u*(y-lasty)/2 {
dx = 0 // cancel the horizontal backspace when getting close to (x,y)
}
buf.Set(x+dx, fy, c)
}
}
lasty = y
buf.Set(x, y, c)
}
@ -183,8 +200,7 @@ func (lc *LineChart) calcLabelX() {
func shortenFloatVal(x float64) string {
s := fmt.Sprintf("%.2f", x)
if len(s)-3 > 3 {
// don't shorten
//s = fmt.Sprintf("%.2e", x)
s = fmt.Sprintf("%.2e", x)
}
if x < 0 {
@ -226,6 +242,9 @@ func (lc *LineChart) calcLayout() {
lc.minY = lc.Data[0]
lc.maxY = lc.Data[0]
lc.bottomValue = lc.minY
lc.topValue = lc.maxY
// valid visible range
vrange := lc.innerArea.Dx()
if lc.Mode == "braille" {

@ -0,0 +1,89 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import "strings"
// List displays []string as its items,
// it has a Overflow option (default is "hidden"), when set to "hidden",
// the item exceeding List's width is truncated, but when set to "wrap",
// the overflowed text breaks into next line.
/*
strs := []string{
"[0] github.com/gizak/termui",
"[1] editbox.go",
"[2] interrupt.go",
"[3] keyboard.go",
"[4] output.go",
"[5] random_out.go",
"[6] dashboard.go",
"[7] nsf/termbox-go"}
ls := termui.NewList()
ls.Items = strs
ls.ItemFgColor = termui.ColorYellow
ls.BorderLabel = "List"
ls.Height = 7
ls.Width = 25
ls.Y = 0
*/
type List struct {
Block
Items []string
Overflow string
ItemFgColor Attribute
ItemBgColor Attribute
}
// NewList returns a new *List with current theme.
func NewList() *List {
l := &List{Block: *NewBlock()}
l.Overflow = "hidden"
l.ItemFgColor = ThemeAttr("list.item.fg")
l.ItemBgColor = ThemeAttr("list.item.bg")
return l
}
// Buffer implements Bufferer interface.
func (l *List) Buffer() Buffer {
buf := l.Block.Buffer()
switch l.Overflow {
case "wrap":
cs := DefaultTxBuilder.Build(strings.Join(l.Items, "\n"), l.ItemFgColor, l.ItemBgColor)
i, j, k := 0, 0, 0
for i < l.innerArea.Dy() && k < len(cs) {
w := cs[k].Width()
if cs[k].Ch == '\n' || j+w > l.innerArea.Dx() {
i++
j = 0
if cs[k].Ch == '\n' {
k++
}
continue
}
buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, cs[k])
k++
j++
}
case "hidden":
trimItems := l.Items
if len(trimItems) > l.innerArea.Dy() {
trimItems = trimItems[:l.innerArea.Dy()]
}
for i, v := range trimItems {
cs := DTrimTxCls(DefaultTxBuilder.Build(v, l.ItemFgColor, l.ItemBgColor), l.innerArea.Dx())
j := 0
for _, vv := range cs {
w := vv.Width()
buf.Set(l.innerArea.Min.X+j, l.innerArea.Min.Y+i, vv)
j += w
}
}
}
return buf
}

@ -0,0 +1,242 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import (
"fmt"
)
// This is the implementation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go
// Multi-Colored-BarChart creates multiple bars in a widget:
/*
bc := termui.NewMBarChart()
data := make([][]int, 2)
data[0] := []int{3, 2, 5, 7, 9, 4}
data[1] := []int{7, 8, 5, 3, 1, 6}
bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
bc.BorderLabel = "Bar Chart"
bc.Data = data
bc.Width = 26
bc.Height = 10
bc.DataLabels = bclabels
bc.TextColor = termui.ColorGreen
bc.BarColor = termui.ColorRed
bc.NumColor = termui.ColorYellow
*/
type MBarChart struct {
Block
BarColor [NumberofColors]Attribute
TextColor Attribute
NumColor [NumberofColors]Attribute
Data [NumberofColors][]int
DataLabels []string
BarWidth int
BarGap int
labels [][]rune
dataNum [NumberofColors][][]rune
numBar int
scale float64
max int
minDataLen int
numStack int
ShowScale bool
maxScale []rune
}
// NewBarChart returns a new *BarChart with current theme.
func NewMBarChart() *MBarChart {
bc := &MBarChart{Block: *NewBlock()}
bc.BarColor[0] = ThemeAttr("mbarchart.bar.bg")
bc.NumColor[0] = ThemeAttr("mbarchart.num.fg")
bc.TextColor = ThemeAttr("mbarchart.text.fg")
bc.BarGap = 1
bc.BarWidth = 3
return bc
}
func (bc *MBarChart) layout() {
bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
bc.labels = make([][]rune, bc.numBar)
DataLen := 0
LabelLen := len(bc.DataLabels)
bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least length
// We need to know how many stack/data array data[0] , data[1] are there
for i := 0; i < len(bc.Data); i++ {
if bc.Data[i] == nil {
break
}
DataLen++
}
bc.numStack = DataLen
//We need to know what is the minimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs
for i := 0; i < DataLen; i++ {
if bc.minDataLen > len(bc.Data[i]) {
bc.minDataLen = len(bc.Data[i])
}
}
if LabelLen > bc.minDataLen {
LabelLen = bc.minDataLen
}
for i := 0; i < LabelLen && i < bc.numBar; i++ {
bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
}
for i := 0; i < bc.numStack; i++ {
bc.dataNum[i] = make([][]rune, len(bc.Data[i]))
//For each stack of bar calculate the rune
for j := 0; j < LabelLen && i < bc.numBar; j++ {
n := bc.Data[i][j]
s := fmt.Sprint(n)
bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth)
}
//If color is not defined by default then populate a color that is different from the previous bar
if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault {
if i == 0 {
bc.BarColor[i] = ColorBlack
} else {
bc.BarColor[i] = bc.BarColor[i-1] + 1
if bc.BarColor[i] > NumberofColors {
bc.BarColor[i] = ColorBlack
}
}
bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility
}
}
//If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n]))
if bc.max == 0 {
bc.max = -1
}
for i := 0; i < bc.minDataLen && i < LabelLen; i++ {
var dsum int
for j := 0; j < bc.numStack; j++ {
dsum += bc.Data[j][i]
}
if dsum > bc.max {
bc.max = dsum
}
}
//Finally Calculate max sale
if bc.ShowScale {
s := fmt.Sprintf("%d", bc.max)
bc.maxScale = trimStr2Runes(s, len(s))
bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-2)
} else {
bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
}
}
func (bc *MBarChart) SetMax(max int) {
if max > 0 {
bc.max = max
}
}
// Buffer implements Bufferer interface.
func (bc *MBarChart) Buffer() Buffer {
buf := bc.Block.Buffer()
bc.layout()
var oftX int
for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ {
ph := 0 //Previous Height to stack up
oftX = i * (bc.BarWidth + bc.BarGap)
for i1 := 0; i1 < bc.numStack; i1++ {
h := int(float64(bc.Data[i1][i]) / bc.scale)
// plot bars
for j := 0; j < bc.BarWidth; j++ {
for k := 0; k < h; k++ {
c := Cell{
Ch: ' ',
Bg: bc.BarColor[i1],
}
if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
c.Bg |= AttrReverse
}
x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k - ph
buf.Set(x, y, c)
}
}
ph += h
}
// plot text
for j, k := 0, 0; j < len(bc.labels[i]); j++ {
w := charWidth(bc.labels[i][j])
c := Cell{
Ch: bc.labels[i][j],
Bg: bc.Bg,
Fg: bc.TextColor,
}
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
x := bc.innerArea.Max.X + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k
buf.Set(x, y, c)
k += w
}
// plot num
ph = 0 //re-initialize previous height
for i1 := 0; i1 < bc.numStack; i1++ {
h := int(float64(bc.Data[i1][i]) / bc.scale)
for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ {
c := Cell{
Ch: bc.dataNum[i1][i][j],
Fg: bc.NumColor[i1],
Bg: bc.BarColor[i1],
}
if bc.BarColor[i1] == ColorDefault { // the same as above
c.Bg |= AttrReverse
}
if h == 0 {
c.Bg = bc.Bg
}
x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - ph
buf.Set(x, y, c)
}
ph += h
}
}
if bc.ShowScale {
//Currently bar graph only supprts data range from 0 to MAX
//Plot 0
c := Cell{
Ch: '0',
Bg: bc.Bg,
Fg: bc.TextColor,
}
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
x := bc.X
buf.Set(x, y, c)
//Plot the maximum sacle value
for i := 0; i < len(bc.maxScale); i++ {
c := Cell{
Ch: bc.maxScale[i],
Bg: bc.Bg,
Fg: bc.TextColor,
}
y := bc.innerArea.Min.Y
x := bc.X + i
buf.Set(x, y, c)
}
}
return buf
}

@ -0,0 +1,28 @@
pages:
- Home: 'index.md'
- Quickstart: 'quickstart.md'
- Recipes: 'recipes.md'
- References:
- Layouts: 'layouts.md'
- Components: 'components.md'
- Events: 'events.md'
- Themes: 'themes.md'
- Versions: 'versions.md'
- About: 'about.md'
site_name: termui
repo_url: https://github.com/gizak/termui/
site_description: 'termui user guide'
site_author: gizak
docs_dir: '_docs'
theme: readthedocs
markdown_extensions:
- smarty
- admonition
- toc
extra:
version: 1.0

@ -0,0 +1,73 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
// Par displays a paragraph.
/*
par := termui.NewPar("Simple Text")
par.Height = 3
par.Width = 17
par.BorderLabel = "Label"
*/
type Par struct {
Block
Text string
TextFgColor Attribute
TextBgColor Attribute
WrapLength int // words wrap limit. Note it may not work properly with multi-width char
}
// NewPar returns a new *Par with given text as its content.
func NewPar(s string) *Par {
return &Par{
Block: *NewBlock(),
Text: s,
TextFgColor: ThemeAttr("par.text.fg"),
TextBgColor: ThemeAttr("par.text.bg"),
WrapLength: 0,
}
}
// Buffer implements Bufferer interface.
func (p *Par) Buffer() Buffer {
buf := p.Block.Buffer()
fg, bg := p.TextFgColor, p.TextBgColor
cs := DefaultTxBuilder.Build(p.Text, fg, bg)
// wrap if WrapLength set
if p.WrapLength < 0 {
cs = wrapTx(cs, p.Width-2)
} else if p.WrapLength > 0 {
cs = wrapTx(cs, p.WrapLength)
}
y, x, n := 0, 0, 0
for y < p.innerArea.Dy() && n < len(cs) {
w := cs[n].Width()
if cs[n].Ch == '\n' || x+w > p.innerArea.Dx() {
y++
x = 0 // set x = 0
if cs[n].Ch == '\n' {
n++
}
if y >= p.innerArea.Dy() {
buf.Set(p.innerArea.Min.X+p.innerArea.Dx()-1,
p.innerArea.Min.Y+p.innerArea.Dy()-1,
Cell{Ch: '…', Fg: p.TextFgColor, Bg: p.TextBgColor})
break
}
continue
}
buf.Set(p.innerArea.Min.X+x, p.innerArea.Min.Y+y, cs[n])
n++
x += w
}
return buf
}

@ -0,0 +1,190 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import (
"image"
"io"
"sync"
"time"
"fmt"
"os"
"runtime/debug"
"bytes"
"github.com/maruel/panicparse/stack"
tm "github.com/nsf/termbox-go"
)
// Bufferer should be implemented by all renderable components.
type Bufferer interface {
Buffer() Buffer
}
// Init initializes termui library. This function should be called before any others.
// After initialization, the library must be finalized by 'Close' function.
func Init() error {
if err := tm.Init(); err != nil {
return err
}
sysEvtChs = make([]chan Event, 0)
go hookTermboxEvt()
renderJobs = make(chan []Bufferer)
//renderLock = new(sync.RWMutex)
Body = NewGrid()
Body.X = 0
Body.Y = 0
Body.BgColor = ThemeAttr("bg")
Body.Width = TermWidth()
DefaultEvtStream.Init()
DefaultEvtStream.Merge("termbox", NewSysEvtCh())
DefaultEvtStream.Merge("timer", NewTimerCh(time.Second))
DefaultEvtStream.Merge("custom", usrEvtCh)
DefaultEvtStream.Handle("/", DefaultHandler)
DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) {
w := e.Data.(EvtWnd)
Body.Width = w.Width
})
DefaultWgtMgr = NewWgtMgr()
DefaultEvtStream.Hook(DefaultWgtMgr.WgtHandlersHook())
go func() {
for bs := range renderJobs {
render(bs...)
}
}()
return nil
}
// Close finalizes termui library,
// should be called after successful initialization when termui's functionality isn't required anymore.
func Close() {
tm.Close()
}
var renderLock sync.Mutex
func termSync() {
renderLock.Lock()
tm.Sync()
termWidth, termHeight = tm.Size()
renderLock.Unlock()
}
// TermWidth returns the current terminal's width.
func TermWidth() int {
termSync()
return termWidth
}
// TermHeight returns the current terminal's height.
func TermHeight() int {
termSync()
return termHeight
}
// Render renders all Bufferer in the given order from left to right,
// right could overlap on left ones.
func render(bs ...Bufferer) {
defer func() {
if e := recover(); e != nil {
Close()
fmt.Fprintf(os.Stderr, "Captured a panic(value=%v) when rendering Bufferer. Exit termui and clean terminal...\nPrint stack trace:\n\n", e)
//debug.PrintStack()
gs, err := stack.ParseDump(bytes.NewReader(debug.Stack()), os.Stderr, false)
if err != nil {
debug.PrintStack()
os.Exit(1)
}
buckets := stack.Aggregate(gs.Goroutines, stack.AnyValue)
for _, bucket := range buckets {
srcLen, pkgLen := 0, 0
for _, line := range bucket.Signature.Stack.Calls {
if l := len(line.SrcLine()); l > srcLen {
srcLen = l
}
if l := len(line.Func.PkgName()); l > pkgLen {
pkgLen = l
}
}
extra := ""
if s := bucket.SleepString(); s != "" {
extra += " [" + s + "]"
}
if bucket.Locked {
extra += " [locked]"
}
if c := bucket.CreatedByString(false); c != "" {
extra += " [Created by " + c + "]"
}
io.WriteString(os.Stdout, fmt.Sprintf("%d: %s%s\n", len(bucket.IDs), bucket.State, extra))
for _, line := range bucket.Stack.Calls {
io.WriteString(os.Stdout, fmt.Sprintf(
" %-*s %-*s %s(%s)\n",
pkgLen, line.Func.PkgName(), srcLen, line.SrcLine(),
line.Func.Name(), &line.Args))
}
if bucket.Stack.Elided {
io.WriteString(os.Stdout, " (...)\n")
}
}
os.Exit(1)
}
}()
for _, b := range bs {
buf := b.Buffer()
// set cels in buf
for p, c := range buf.CellMap {
if p.In(buf.Area) {
tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
}
}
}
renderLock.Lock()
// render
tm.Flush()
renderLock.Unlock()
}
func Clear() {
tm.Clear(tm.ColorDefault, toTmAttr(ThemeAttr("bg")))
}
func clearArea(r image.Rectangle, bg Attribute) {
for i := r.Min.X; i < r.Max.X; i++ {
for j := r.Min.Y; j < r.Max.Y; j++ {
tm.SetCell(i, j, ' ', tm.ColorDefault, toTmAttr(bg))
}
}
}
func ClearArea(r image.Rectangle, bg Attribute) {
clearArea(r, bg)
tm.Flush()
}
var renderJobs chan []Bufferer
func Render(bs ...Bufferer) {
//go func() { renderJobs <- bs }()
renderJobs <- bs
}

@ -51,7 +51,7 @@ func NewSparkline() Sparkline {
LineColor: ThemeAttr("sparkline.line.fg")}
}
// NewSparklines return a new *Spaklines with given Sparkline(s), you can always add a new Sparkline later.
// NewSparklines return a new *Sparklines with given Sparkline(s), you can always add a new Sparkline later.
func NewSparklines(ss ...Sparkline) *Sparklines {
s := &Sparklines{Block: *NewBlock(), Lines: ss}
return s

@ -0,0 +1,185 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import "strings"
/* Table is like:
Awesome Table
Col0 | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 |
Some Item #1 | AAA | 123 | CCCCC | EEEEE | GGGGG | IIIII |
Some Item #2 | BBB | 456 | DDDDD | FFFFF | HHHHH | JJJJJ |
Datapoints are a two dimensional array of strings: [][]string
Example:
data := [][]string{
{"Col0", "Col1", "Col3", "Col4", "Col5", "Col6"},
{"Some Item #1", "AAA", "123", "CCCCC", "EEEEE", "GGGGG", "IIIII"},
{"Some Item #2", "BBB", "456", "DDDDD", "FFFFF", "HHHHH", "JJJJJ"},
}
table := termui.NewTable()
table.Rows = data // type [][]string
table.FgColor = termui.ColorWhite
table.BgColor = termui.ColorDefault
table.Height = 7
table.Width = 62
table.Y = 0
table.X = 0
table.Border = true
*/
// Table tracks all the attributes of a Table instance
type Table struct {
Block
Rows [][]string
CellWidth []int
FgColor Attribute
BgColor Attribute
FgColors []Attribute
BgColors []Attribute
Separator bool
TextAlign Align
}
// NewTable returns a new Table instance
func NewTable() *Table {
table := &Table{Block: *NewBlock()}
table.FgColor = ColorWhite
table.BgColor = ColorDefault
table.Separator = true
return table
}
// CellsWidth calculates the width of a cell array and returns an int
func cellsWidth(cells []Cell) int {
width := 0
for _, c := range cells {
width += c.Width()
}
return width
}
// Analysis generates and returns an array of []Cell that represent all columns in the Table
func (table *Table) Analysis() [][]Cell {
var rowCells [][]Cell
length := len(table.Rows)
if length < 1 {
return rowCells
}
if len(table.FgColors) == 0 {
table.FgColors = make([]Attribute, len(table.Rows))
}
if len(table.BgColors) == 0 {
table.BgColors = make([]Attribute, len(table.Rows))
}
cellWidths := make([]int, len(table.Rows[0]))
for y, row := range table.Rows {
if table.FgColors[y] == 0 {
table.FgColors[y] = table.FgColor
}
if table.BgColors[y] == 0 {
table.BgColors[y] = table.BgColor
}
for x, str := range row {
cells := DefaultTxBuilder.Build(str, table.FgColors[y], table.BgColors[y])
cw := cellsWidth(cells)
if cellWidths[x] < cw {
cellWidths[x] = cw
}
rowCells = append(rowCells, cells)
}
}
table.CellWidth = cellWidths
return rowCells
}
// SetSize calculates the table size and sets the internal value
func (table *Table) SetSize() {
length := len(table.Rows)
if table.Separator {
table.Height = length*2 + 1
} else {
table.Height = length + 2
}
table.Width = 2
if length != 0 {
for _, cellWidth := range table.CellWidth {
table.Width += cellWidth + 3
}
}
}
// CalculatePosition ...
func (table *Table) CalculatePosition(x int, y int, coordinateX *int, coordinateY *int, cellStart *int) {
if table.Separator {
*coordinateY = table.innerArea.Min.Y + y*2
} else {
*coordinateY = table.innerArea.Min.Y + y
}
if x == 0 {
*cellStart = table.innerArea.Min.X
} else {
*cellStart += table.CellWidth[x-1] + 3
}
switch table.TextAlign {
case AlignRight:
*coordinateX = *cellStart + (table.CellWidth[x] - len(table.Rows[y][x])) + 2
case AlignCenter:
*coordinateX = *cellStart + (table.CellWidth[x]-len(table.Rows[y][x]))/2 + 2
default:
*coordinateX = *cellStart + 2
}
}
// Buffer ...
func (table *Table) Buffer() Buffer {
buffer := table.Block.Buffer()
rowCells := table.Analysis()
pointerX := table.innerArea.Min.X + 2
pointerY := table.innerArea.Min.Y
borderPointerX := table.innerArea.Min.X
for y, row := range table.Rows {
for x := range row {
table.CalculatePosition(x, y, &pointerX, &pointerY, &borderPointerX)
background := DefaultTxBuilder.Build(strings.Repeat(" ", table.CellWidth[x]+3), table.BgColors[y], table.BgColors[y])
cells := rowCells[y*len(row)+x]
for i, back := range background {
buffer.Set(borderPointerX+i, pointerY, back)
}
coordinateX := pointerX
for _, printer := range cells {
buffer.Set(coordinateX, pointerY, printer)
coordinateX += printer.Width()
}
if x != 0 {
dividors := DefaultTxBuilder.Build("|", table.FgColors[y], table.BgColors[y])
for _, dividor := range dividors {
buffer.Set(borderPointerX, pointerY, dividor)
}
}
}
if table.Separator {
border := DefaultTxBuilder.Build(strings.Repeat("─", table.Width-2), table.FgColor, table.BgColor)
for i, cell := range border {
buffer.Set(i+1, pointerY+1, cell)
}
}
}
return buffer
}

@ -0,0 +1,278 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
package termui
import (
"regexp"
"strings"
"github.com/mitchellh/go-wordwrap"
)
// TextBuilder is a minimal interface to produce text []Cell using specific syntax (markdown).
type TextBuilder interface {
Build(s string, fg, bg Attribute) []Cell
}
// DefaultTxBuilder is set to be MarkdownTxBuilder.
var DefaultTxBuilder = NewMarkdownTxBuilder()
// MarkdownTxBuilder implements TextBuilder interface, using markdown syntax.
type MarkdownTxBuilder struct {
baseFg Attribute
baseBg Attribute
plainTx []rune
markers []marker
}
type marker struct {
st int
ed int
fg Attribute
bg Attribute
}
var colorMap = map[string]Attribute{
"red": ColorRed,
"blue": ColorBlue,
"black": ColorBlack,
"cyan": ColorCyan,
"yellow": ColorYellow,
"white": ColorWhite,
"default": ColorDefault,
"green": ColorGreen,
"magenta": ColorMagenta,
}
var attrMap = map[string]Attribute{
"bold": AttrBold,
"underline": AttrUnderline,
"reverse": AttrReverse,
}
func rmSpc(s string) string {
reg := regexp.MustCompile(`\s+`)
return reg.ReplaceAllString(s, "")
}
// readAttr translates strings like `fg-red,fg-bold,bg-white` to fg and bg Attribute
func (mtb MarkdownTxBuilder) readAttr(s string) (Attribute, Attribute) {
fg := mtb.baseFg
bg := mtb.baseBg
updateAttr := func(a Attribute, attrs []string) Attribute {
for _, s := range attrs {
// replace the color
if c, ok := colorMap[s]; ok {
a &= 0xFF00 // erase clr 0 ~ 8 bits
a |= c // set clr
}
// add attrs
if c, ok := attrMap[s]; ok {
a |= c
}
}
return a
}
ss := strings.Split(s, ",")
fgs := []string{}
bgs := []string{}
for _, v := range ss {
subs := strings.Split(v, "-")
if len(subs) > 1 {
if subs[0] == "fg" {
fgs = append(fgs, subs[1])
}
if subs[0] == "bg" {
bgs = append(bgs, subs[1])
}
}
}
fg = updateAttr(fg, fgs)
bg = updateAttr(bg, bgs)
return fg, bg
}
func (mtb *MarkdownTxBuilder) reset() {
mtb.plainTx = []rune{}
mtb.markers = []marker{}
}
// parse streams and parses text into normalized text and render sequence.
func (mtb *MarkdownTxBuilder) parse(str string) {
rs := str2runes(str)
normTx := []rune{}
square := []rune{}
brackt := []rune{}
accSquare := false
accBrackt := false
cntSquare := 0
reset := func() {
square = []rune{}
brackt = []rune{}
accSquare = false
accBrackt = false
cntSquare = 0
}
// pipe stacks into normTx and clear
rollback := func() {
normTx = append(normTx, square...)
normTx = append(normTx, brackt...)
reset()
}
// chop first and last
chop := func(s []rune) []rune {
return s[1 : len(s)-1]
}
for i, r := range rs {
switch {
// stacking brackt
case accBrackt:
brackt = append(brackt, r)
if ')' == r {
fg, bg := mtb.readAttr(string(chop(brackt)))
st := len(normTx)
ed := len(normTx) + len(square) - 2
mtb.markers = append(mtb.markers, marker{st, ed, fg, bg})
normTx = append(normTx, chop(square)...)
reset()
} else if i+1 == len(rs) {
rollback()
}
// stacking square
case accSquare:
switch {
// squares closed and followed by a '('
case cntSquare == 0 && '(' == r:
accBrackt = true
brackt = append(brackt, '(')
// squares closed but not followed by a '('
case cntSquare == 0:
rollback()
if '[' == r {
accSquare = true
cntSquare = 1
brackt = append(brackt, '[')
} else {
normTx = append(normTx, r)
}
// hit the end
case i+1 == len(rs):
square = append(square, r)
rollback()
case '[' == r:
cntSquare++
square = append(square, '[')
case ']' == r:
cntSquare--
square = append(square, ']')
// normal char
default:
square = append(square, r)
}
// stacking normTx
default:
if '[' == r {
accSquare = true
cntSquare = 1
square = append(square, '[')
} else {
normTx = append(normTx, r)
}
}
}
mtb.plainTx = normTx
}
func wrapTx(cs []Cell, wl int) []Cell {
tmpCell := make([]Cell, len(cs))
copy(tmpCell, cs)
// get the plaintext
plain := CellsToStr(cs)
// wrap
plainWrapped := wordwrap.WrapString(plain, uint(wl))
// find differences and insert
finalCell := tmpCell // finalcell will get the inserts and is what is returned
plainRune := []rune(plain)
plainWrappedRune := []rune(plainWrapped)
trigger := "go"
plainRuneNew := plainRune
for trigger != "stop" {
plainRune = plainRuneNew
for i := range plainRune {
if plainRune[i] == plainWrappedRune[i] {
trigger = "stop"
} else if plainRune[i] != plainWrappedRune[i] && plainWrappedRune[i] == 10 {
trigger = "go"
cell := Cell{10, 0, 0}
j := i - 0
// insert a cell into the []Cell in correct position
tmpCell[i] = cell
// insert the newline into plain so we avoid indexing errors
plainRuneNew = append(plainRune, 10)
copy(plainRuneNew[j+1:], plainRuneNew[j:])
plainRuneNew[j] = plainWrappedRune[j]
// restart the inner for loop until plain and plain wrapped are
// the same; yeah, it's inefficient, but the text amounts
// should be small
break
} else if plainRune[i] != plainWrappedRune[i] &&
plainWrappedRune[i-1] == 10 && // if the prior rune is a newline
plainRune[i] == 32 { // and this rune is a space
trigger = "go"
// need to delete plainRune[i] because it gets rid of an extra
// space
plainRuneNew = append(plainRune[:i], plainRune[i+1:]...)
break
} else {
trigger = "stop" // stops the outer for loop
}
}
}
finalCell = tmpCell
return finalCell
}
// Build implements TextBuilder interface.
func (mtb MarkdownTxBuilder) Build(s string, fg, bg Attribute) []Cell {
mtb.baseFg = fg
mtb.baseBg = bg
mtb.reset()
mtb.parse(s)
cs := make([]Cell, len(mtb.plainTx))
for i := range cs {
cs[i] = Cell{Ch: mtb.plainTx[i], Fg: fg, Bg: bg}
}
for _, mrk := range mtb.markers {
for i := mrk.st; i < mrk.ed; i++ {
cs[i].Fg = mrk.fg
cs[i].Bg = mrk.bg
}
}
return cs
}
// NewMarkdownTxBuilder returns a TextBuilder employing markdown syntax.
func NewMarkdownTxBuilder() TextBuilder {
return MarkdownTxBuilder{}
}

@ -0,0 +1,30 @@
# This is the official list of gocui authors for copyright purposes.
# Names should be added to this file as
# Name or Organization <email address> contribution
# Contribution
# The email address is not required for organizations.
Roi Martin <jroi.martin@gmail.com>
Main developer
Ryan Sullivan <kayoticsully@gmail.com>
Toggleable view frames
Matthieu Rakotojaona <matthieu.rakotojaona@gmail.com>
Wrapped views
Harry Lawrence <hazbo@gmx.com>
Basic mouse support
Danny Tylman <dtylman@gmail.com>
Masked views
Frederik Deweerdt <frederik.deweerdt@gmail.com>
Colored fonts
Henri Koski <henri.t.koski@gmail.com>
Custom current view color
Dustin Willis Webber <dustin.webber@gmail.com>
256-colors output mode support

@ -0,0 +1,23 @@
Copyright (c) 2014 The gocui Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the gocui Authors nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,110 @@
# GOCUI - Go Console User Interface
[![GoDoc](https://godoc.org/github.com/jroimartin/gocui?status.svg)](https://godoc.org/github.com/jroimartin/gocui)
Minimalist Go package aimed at creating Console User Interfaces.
## Features
* Minimalist API.
* Views (the "windows" in the GUI) implement the interface io.ReadWriter.
* Support for overlapping views.
* The GUI can be modified at runtime (concurrent-safe).
* Global and view-level keybindings.
* Mouse support.
* Colored text.
* Customizable edition mode.
* Easy to build reusable widgets, complex layouts...
## Installation
Execute:
```
$ go get github.com/jroimartin/gocui
```
## Documentation
Execute:
```
$ go doc github.com/jroimartin/gocui
```
Or visit [godoc.org](https://godoc.org/github.com/jroimartin/gocui) to read it
online.
## Example
```go
package main
import (
"fmt"
"log"
"github.com/jroimartin/gocui"
)
func main() {
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
log.Panicln(err)
}
defer g.Close()
g.SetManagerFunc(layout)
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln(err)
}
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln(err)
}
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
if err != gocui.ErrUnknownView {
return err
}
fmt.Fprintln(v, "Hello world!")
}
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
```
## Screenshots
![r2cui](https://cloud.githubusercontent.com/assets/1223476/19418932/63645052-93ce-11e6-867c-da5e97e37237.png)
![_examples/demo.go](https://cloud.githubusercontent.com/assets/1223476/5992750/720b84f0-aa36-11e4-88ec-296fa3247b52.png)
![_examples/dynamic.go](https://cloud.githubusercontent.com/assets/1223476/5992751/76ad5cc2-aa36-11e4-8204-6a90269db827.png)
## Projects using gocui
* [komanda-cli](https://github.com/mephux/komanda-cli): IRC Client For Developers.
* [vuls](https://github.com/future-architect/vuls): Agentless vulnerability scanner for Linux/FreeBSD.
* [wuzz](https://github.com/asciimoo/wuzz): Interactive cli tool for HTTP inspection.
* [httplab](https://github.com/gchaincl/httplab): Interactive web server.
* [domainr](https://github.com/MichaelThessel/domainr): Tool that checks the availability of domains based on keywords.
* [gotime](https://github.com/nanohard/gotime): Time tracker for projects and tasks.
* [claws](https://github.com/thehowl/claws): Interactive command line client for testing websockets.
* [terminews](http://github.com/antavelos/terminews): Terminal based RSS reader.
* [diagram](https://github.com/esimov/diagram): Tool to convert ascii arts into hand drawn diagrams.
* [pody](https://github.com/JulienBreux/pody): CLI app to manage Pods in a Kubernetes cluster.
* [kubexp](https://github.com/alitari/kubexp): Kubernetes client.
* [kcli](https://github.com/cswank/kcli): Tool for inspecting kafka topics/partitions/messages.
* [fac](https://github.com/mkchoi212/fac): git merge conflict resolver
* [jsonui](https://github.com/gulyasm/jsonui): Interactive JSON explorer for your terminal.
* [cointop](https://github.com/miguelmota/cointop): Interactive terminal based UI application for tracking cryptocurrencies.
Note: if your project is not listed here, let us know! :)

@ -4,7 +4,7 @@
package gocui
import "github.com/miguelmota/cointop/pkg/termbox"
import "github.com/nsf/termbox-go"
// Attribute represents a terminal attribute, like color, font style, etc. They
// can be combined using bitwise OR (|). Note that it is not possible to

@ -0,0 +1,118 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package gocui allows to create console user interfaces.
Create a new GUI:
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
// handle error
}
defer g.Close()
// Set GUI managers and key bindings
// ...
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
// handle error
}
Set GUI managers:
g.SetManager(mgr1, mgr2)
Managers are in charge of GUI's layout and can be used to build widgets. On
each iteration of the GUI's main loop, the Layout function of each configured
manager is executed. Managers are used to set-up and update the application's
main views, being possible to freely change them during execution. Also, it is
important to mention that a main loop iteration is executed on each reported
event (key-press, mouse event, window resize, etc).
GUIs are composed by Views, you can think of it as buffers. Views implement the
io.ReadWriter interface, so you can just write to them if you want to modify
their content. The same is valid for reading.
Create and initialize a view with absolute coordinates:
if v, err := g.SetView("viewname", 2, 2, 22, 7); err != nil {
if err != gocui.ErrUnknownView {
// handle error
}
fmt.Fprintln(v, "This is a new view")
// ...
}
Views can also be created using relative coordinates:
maxX, maxY := g.Size()
if v, err := g.SetView("viewname", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
// ...
}
Configure keybindings:
if err := g.SetKeybinding("viewname", gocui.KeyEnter, gocui.ModNone, fcn); err != nil {
// handle error
}
gocui implements full mouse support that can be enabled with:
g.Mouse = true
Mouse events are handled like any other keybinding:
if err := g.SetKeybinding("viewname", gocui.MouseLeft, gocui.ModNone, fcn); err != nil {
// handle error
}
IMPORTANT: Views can only be created, destroyed or updated in three ways: from
the Layout function within managers, from keybinding callbacks or via
*Gui.Update(). The reason for this is that it allows gocui to be
concurrent-safe. So, if you want to update your GUI from a goroutine, you must
use *Gui.Update(). For example:
g.Update(func(g *gocui.Gui) error {
v, err := g.View("viewname")
if err != nil {
// handle error
}
v.Clear()
fmt.Fprintln(v, "Writing from different goroutines")
return nil
})
By default, gocui provides a basic edition mode. This mode can be extended
and customized creating a new Editor and assigning it to *View.Editor:
type Editor interface {
Edit(v *View, key Key, ch rune, mod Modifier)
}
DefaultEditor can be taken as example to create your own custom Editor:
var DefaultEditor Editor = EditorFunc(simpleEditor)
func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
switch {
case ch != 0 && mod == 0:
v.EditWrite(ch)
case key == KeySpace:
v.EditWrite(' ')
case key == KeyBackspace || key == KeyBackspace2:
v.EditDelete(true)
// ...
}
}
Colored text:
Views allow to add colored text using ANSI colors. For example:
fmt.Fprintln(v, "\x1b[0;31mHello world")
For more information, see the examples in folder "_examples/".
*/
package gocui

@ -7,7 +7,7 @@ package gocui
import (
"errors"
"github.com/miguelmota/cointop/pkg/termbox"
"github.com/nsf/termbox-go"
)
var (

@ -4,7 +4,7 @@
package gocui
import "github.com/miguelmota/cointop/pkg/termbox"
import "github.com/nsf/termbox-go"
// Keybidings are used to link a given key-press event with a handler.
type keybinding struct {

@ -10,7 +10,7 @@ import (
"io"
"strings"
"github.com/miguelmota/cointop/pkg/termbox"
"github.com/nsf/termbox-go"
)
// A View is a window. It maintains its own internal buffer and cursor

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save