Add scroll bar to List, DropDown, Table and TreeView
This commit is contained in:
parent
339db80f66
commit
96f3c15040
10 changed files with 168 additions and 15 deletions
|
@ -1,4 +1,5 @@
|
|||
v1.4.2 (WIP)
|
||||
- Add scroll bar to List, DropDown, Table and TreeView
|
||||
- Add SetDoneFunc to TreeView
|
||||
- Fix rendering issues with TextViews which have their background color set to
|
||||
ColorDefault
|
||||
|
|
|
@ -374,7 +374,6 @@ func (d *DropDown) Draw(screen tcell.Screen) {
|
|||
// We prefer to drop down but if there is no space, maybe drop up?
|
||||
lx := x
|
||||
ly := y + 1
|
||||
lwidth := maxWidth
|
||||
lheight := len(d.options)
|
||||
_, sheight := screen.Size()
|
||||
if ly+lheight >= sheight && ly-2 > lheight-ly {
|
||||
|
@ -386,6 +385,10 @@ func (d *DropDown) Draw(screen tcell.Screen) {
|
|||
if ly+lheight >= sheight {
|
||||
lheight = sheight - ly
|
||||
}
|
||||
lwidth := maxWidth
|
||||
if len(d.options) > lheight {
|
||||
lwidth++ // Add space for scroll bar
|
||||
}
|
||||
d.list.SetRect(lx, ly, lwidth, lheight)
|
||||
d.list.Draw(screen)
|
||||
}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -5,8 +5,8 @@ go 1.12
|
|||
require (
|
||||
github.com/gdamore/tcell v1.3.0
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3
|
||||
github.com/mattn/go-runewidth v0.0.7
|
||||
github.com/mattn/go-runewidth v0.0.8
|
||||
github.com/rivo/uniseg v0.1.0
|
||||
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 // indirect
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
)
|
||||
|
|
8
go.sum
8
go.sum
|
@ -10,14 +10,14 @@ github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tW
|
|||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
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=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 h1:/W9OPMnnpmFXHYkcp2rQsbFUbRlRzfECQjmAFiOyHE8=
|
||||
golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
|
|
|
@ -422,6 +422,9 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||
if ly+lheight >= sheight {
|
||||
lheight = sheight - ly
|
||||
}
|
||||
if i.autocompleteList.GetItemCount() > lheight {
|
||||
lwidth++ // Add space for scroll bar
|
||||
}
|
||||
i.autocompleteList.SetRect(lx, ly, lwidth, lheight)
|
||||
i.autocompleteList.Draw(screen)
|
||||
}
|
||||
|
|
42
list.go
42
list.go
|
@ -42,6 +42,12 @@ type List struct {
|
|||
// The text color for selected items.
|
||||
selectedTextColor tcell.Color
|
||||
|
||||
// Whether or not to render a scroll bar.
|
||||
showScrollBar bool
|
||||
|
||||
// The scroll bar color.
|
||||
scrollBarColor tcell.Color
|
||||
|
||||
// The background color for selected items.
|
||||
selectedBackgroundColor tcell.Color
|
||||
|
||||
|
@ -74,11 +80,13 @@ func NewList() *List {
|
|||
return &List{
|
||||
Box: NewBox(),
|
||||
showSecondaryText: true,
|
||||
showScrollBar: true,
|
||||
wrapAround: true,
|
||||
mainTextColor: Styles.PrimaryTextColor,
|
||||
secondaryTextColor: Styles.TertiaryTextColor,
|
||||
shortcutColor: Styles.SecondaryTextColor,
|
||||
selectedTextColor: Styles.PrimitiveBackgroundColor,
|
||||
scrollBarColor: Styles.ScrollBarColor,
|
||||
selectedBackgroundColor: Styles.PrimaryTextColor,
|
||||
}
|
||||
}
|
||||
|
@ -216,6 +224,19 @@ func (l *List) ShowSecondaryText(show bool) *List {
|
|||
return l
|
||||
}
|
||||
|
||||
// ShowScrollBar determines whether or not to render a scroll bar when there
|
||||
// are additional items offscreen.
|
||||
func (l *List) ShowScrollBar(show bool) *List {
|
||||
l.showScrollBar = show
|
||||
return l
|
||||
}
|
||||
|
||||
// SetScrollBarColor sets the color of the scroll bar.
|
||||
func (l *List) SetScrollBarColor(color tcell.Color) *List {
|
||||
l.scrollBarColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetWrapAround sets the flag that determines whether navigating the list will
|
||||
// wrap around. That is, navigating downwards on the last item will move the
|
||||
// selection to the first item (similarly in the other direction). If set to
|
||||
|
@ -395,6 +416,18 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
x, y, width, height := l.GetInnerRect()
|
||||
bottomLimit := y + height
|
||||
|
||||
screenWidth, _ := screen.Size()
|
||||
scrollBarHeight := height
|
||||
scrollBarX := x + (width - 1)
|
||||
if scrollBarX > screenWidth-1 {
|
||||
scrollBarX = screenWidth - 1
|
||||
}
|
||||
|
||||
// Halve scroll bar height when drawing two lines per list item.
|
||||
if l.showSecondaryText {
|
||||
scrollBarHeight /= 2
|
||||
}
|
||||
|
||||
// Do we show any shortcuts?
|
||||
var showShortcuts bool
|
||||
for _, item := range l.items {
|
||||
|
@ -457,6 +490,10 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
}
|
||||
}
|
||||
|
||||
if l.showScrollBar {
|
||||
RenderScrollBar(screen, scrollBarX, y, scrollBarHeight, len(l.items), l.currentItem, index-l.offset, l.hasFocus, l.scrollBarColor)
|
||||
}
|
||||
|
||||
y++
|
||||
|
||||
if y >= bottomLimit {
|
||||
|
@ -466,6 +503,11 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||
// Secondary text.
|
||||
if l.showSecondaryText {
|
||||
Print(screen, item.SecondaryText, x, y, width, AlignLeft, l.secondaryTextColor)
|
||||
|
||||
if l.showScrollBar {
|
||||
RenderScrollBar(screen, scrollBarX, y, scrollBarHeight, len(l.items), l.currentItem, index-l.offset, l.hasFocus, l.scrollBarColor)
|
||||
}
|
||||
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ type Theme struct {
|
|||
TertiaryTextColor tcell.Color // Tertiary text (e.g. subtitles, notes).
|
||||
InverseTextColor tcell.Color // Text on primary-colored backgrounds.
|
||||
ContrastSecondaryTextColor tcell.Color // Secondary text on ContrastBackgroundColor-colored backgrounds.
|
||||
ScrollBarColor tcell.Color // Scroll bar.
|
||||
}
|
||||
|
||||
// Styles defines the theme for applications. The default is for a black
|
||||
|
@ -32,4 +33,5 @@ var Styles = Theme{
|
|||
TertiaryTextColor: tcell.ColorGreen,
|
||||
InverseTextColor: tcell.ColorBlue,
|
||||
ContrastSecondaryTextColor: tcell.ColorDarkCyan,
|
||||
ScrollBarColor: tcell.ColorWhite,
|
||||
}
|
||||
|
|
47
table.go
47
table.go
|
@ -251,6 +251,12 @@ type Table struct {
|
|||
// The number of visible rows the last time the table was drawn.
|
||||
visibleRows int
|
||||
|
||||
// Whether or not to render a scroll bar.
|
||||
showScrollBar bool
|
||||
|
||||
// The scroll bar color.
|
||||
scrollBarColor tcell.Color
|
||||
|
||||
// The style of the selected rows. If this value is 0, selected rows are
|
||||
// simply inverted.
|
||||
selectedStyle tcell.Style
|
||||
|
@ -273,10 +279,12 @@ type Table struct {
|
|||
// NewTable returns a new table.
|
||||
func NewTable() *Table {
|
||||
return &Table{
|
||||
Box: NewBox(),
|
||||
bordersColor: Styles.GraphicsColor,
|
||||
separator: ' ',
|
||||
lastColumn: -1,
|
||||
Box: NewBox(),
|
||||
showScrollBar: true,
|
||||
scrollBarColor: Styles.ScrollBarColor,
|
||||
bordersColor: Styles.GraphicsColor,
|
||||
separator: ' ',
|
||||
lastColumn: -1,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,6 +308,19 @@ func (t *Table) SetBordersColor(color tcell.Color) *Table {
|
|||
return t
|
||||
}
|
||||
|
||||
// ShowScrollBar determines whether or not to render a scroll bar when there
|
||||
// are additional rows and/or columns offscreen.
|
||||
func (t *Table) ShowScrollBar(show bool) *Table {
|
||||
t.showScrollBar = show
|
||||
return t
|
||||
}
|
||||
|
||||
// SetScrollBarColor sets the color of the scroll bar.
|
||||
func (t *Table) SetScrollBarColor(color tcell.Color) *Table {
|
||||
t.scrollBarColor = color
|
||||
return t
|
||||
}
|
||||
|
||||
// SetSelectedStyle sets a specific style for selected cells. If no such style
|
||||
// is set, per default, selected cells are inverted (i.e. their foreground and
|
||||
// background colors are swapped).
|
||||
|
@ -570,6 +591,11 @@ func (t *Table) Draw(screen tcell.Screen) {
|
|||
t.visibleRows = height
|
||||
}
|
||||
|
||||
showVerticalScrollBar := t.showScrollBar && len(t.cells) > height
|
||||
if showVerticalScrollBar {
|
||||
width-- // Subtract space for scroll bar.
|
||||
}
|
||||
|
||||
// Return the cell at the specified position (nil if it doesn't exist).
|
||||
getCell := func(row, column int) *TableCell {
|
||||
if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) {
|
||||
|
@ -861,6 +887,19 @@ ColumnLoop:
|
|||
}
|
||||
}
|
||||
|
||||
if showVerticalScrollBar {
|
||||
// Calculate scroll bar position.
|
||||
rows := len(t.cells)
|
||||
cursor := int(float64(rows-t.fixedRows) * (float64(t.rowOffset) / float64(((rows-t.fixedRows)-t.visibleRows)+1)))
|
||||
|
||||
// Draw scroll bar.
|
||||
for printed := 0; printed < (t.visibleRows - t.fixedRows); printed++ {
|
||||
RenderScrollBar(screen, x+width, y+t.fixedRows+printed, t.visibleRows-t.fixedRows, rows-t.fixedRows, cursor, printed, t.hasFocus, t.scrollBarColor)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Draw horizontal scroll bar
|
||||
|
||||
// Helper function which colors the background of a box.
|
||||
// backgroundColor == tcell.ColorDefault => Don't color the background.
|
||||
// textColor == tcell.ColorDefault => Don't change the text color.
|
||||
|
|
38
treeview.go
38
treeview.go
|
@ -270,6 +270,12 @@ type TreeView struct {
|
|||
// The color of the lines.
|
||||
graphicsColor tcell.Color
|
||||
|
||||
// Whether or not to render a scroll bar.
|
||||
showScrollBar bool
|
||||
|
||||
// The scroll bar color.
|
||||
scrollBarColor tcell.Color
|
||||
|
||||
// An optional function which is called when the user has navigated to a new
|
||||
// tree node.
|
||||
changed func(node *TreeNode)
|
||||
|
@ -288,9 +294,11 @@ type TreeView struct {
|
|||
// NewTreeView returns a new tree view.
|
||||
func NewTreeView() *TreeView {
|
||||
return &TreeView{
|
||||
Box: NewBox(),
|
||||
graphics: true,
|
||||
graphicsColor: Styles.GraphicsColor,
|
||||
Box: NewBox(),
|
||||
showScrollBar: true,
|
||||
graphics: true,
|
||||
graphicsColor: Styles.GraphicsColor,
|
||||
scrollBarColor: Styles.ScrollBarColor,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,6 +373,19 @@ func (t *TreeView) SetGraphicsColor(color tcell.Color) *TreeView {
|
|||
return t
|
||||
}
|
||||
|
||||
// ShowScrollBar determines whether or not to render a scroll bar when there
|
||||
// are additional nodes offscreen.
|
||||
func (t *TreeView) ShowScrollBar(show bool) *TreeView {
|
||||
t.showScrollBar = show
|
||||
return t
|
||||
}
|
||||
|
||||
// SetScrollBarColor sets the color of the scroll bar.
|
||||
func (t *TreeView) SetScrollBarColor(color tcell.Color) *TreeView {
|
||||
t.scrollBarColor = color
|
||||
return t
|
||||
}
|
||||
|
||||
// SetChangedFunc sets the function which is called when the user navigates to
|
||||
// a new tree node.
|
||||
func (t *TreeView) SetChangedFunc(handler func(node *TreeNode)) *TreeView {
|
||||
|
@ -601,12 +622,16 @@ func (t *TreeView) Draw(screen tcell.Screen) {
|
|||
t.offsetY = 0
|
||||
}
|
||||
|
||||
// Calculate scroll bar position.
|
||||
rows := len(t.nodes)
|
||||
cursor := int(float64(rows) * (float64(t.offsetY) / float64(rows-height)))
|
||||
|
||||
// Draw the tree.
|
||||
posY := y
|
||||
lineStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.graphicsColor)
|
||||
for index, node := range t.nodes {
|
||||
// Skip invisible parts.
|
||||
if posY >= y+height+1 {
|
||||
if posY >= y+height {
|
||||
break
|
||||
}
|
||||
if index < t.offsetY {
|
||||
|
@ -668,6 +693,11 @@ func (t *TreeView) Draw(screen tcell.Screen) {
|
|||
}
|
||||
}
|
||||
|
||||
// Draw scroll bar.
|
||||
if t.showScrollBar {
|
||||
RenderScrollBar(screen, x+(width-1), posY, height, rows, cursor, posY-y, t.hasFocus, tcell.ColorWhite)
|
||||
}
|
||||
|
||||
// Advance.
|
||||
posY++
|
||||
}
|
||||
|
|
33
util.go
33
util.go
|
@ -630,3 +630,36 @@ func iterateStringReverse(text string, callback func(main rune, comb []rune, tex
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
// RenderScrollBar renders a scroll bar character at the specified position.
|
||||
func RenderScrollBar(screen tcell.Screen, x int, y int, height int, items int, cursor int, printed int, focused bool, color tcell.Color) {
|
||||
// Do not render a scroll bar when all items are visible.
|
||||
if items <= height {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle negative cursor.
|
||||
if cursor < 0 {
|
||||
cursor = 0
|
||||
}
|
||||
|
||||
// Calculate handle position.
|
||||
handlePosition := int(float64(height-1) * (float64(cursor) / float64(items-1)))
|
||||
|
||||
// Print character.
|
||||
var scrollBar string
|
||||
if printed == handlePosition {
|
||||
if focused {
|
||||
scrollBar = "[::r] [-:-:-]"
|
||||
} else {
|
||||
scrollBar = "▓"
|
||||
}
|
||||
} else {
|
||||
if focused {
|
||||
scrollBar = "▒"
|
||||
} else {
|
||||
scrollBar = "░"
|
||||
}
|
||||
}
|
||||
Print(screen, scrollBar, x, y, 1, AlignLeft, color)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue