mirror of
https://github.com/rivo/tview.git
synced 2024-11-15 06:12:46 +00:00
Add scroll bar to List, DropDown, Table and TreeView
This commit is contained in:
parent
3a5c6317e4
commit
4c0a19f6a1
@ -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?
|
// We prefer to drop down but if there is no space, maybe drop up?
|
||||||
lx := x
|
lx := x
|
||||||
ly := y + 1
|
ly := y + 1
|
||||||
lwidth := maxWidth
|
|
||||||
lheight := len(d.options)
|
lheight := len(d.options)
|
||||||
_, sheight := screen.Size()
|
_, sheight := screen.Size()
|
||||||
if ly+lheight >= sheight && ly-2 > lheight-ly {
|
if ly+lheight >= sheight && ly-2 > lheight-ly {
|
||||||
@ -386,6 +385,10 @@ func (d *DropDown) Draw(screen tcell.Screen) {
|
|||||||
if ly+lheight >= sheight {
|
if ly+lheight >= sheight {
|
||||||
lheight = sheight - ly
|
lheight = sheight - ly
|
||||||
}
|
}
|
||||||
|
lwidth := maxWidth
|
||||||
|
if d.list.scrollBarVisibility == ScrollBarAlways || (d.list.scrollBarVisibility == ScrollBarAuto && len(d.options) > lheight) {
|
||||||
|
lwidth++ // Add space for scroll bar
|
||||||
|
}
|
||||||
d.list.SetRect(lx, ly, lwidth, lheight)
|
d.list.SetRect(lx, ly, lwidth, lheight)
|
||||||
d.list.Draw(screen)
|
d.list.Draw(screen)
|
||||||
}
|
}
|
||||||
|
@ -422,6 +422,9 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||||||
if ly+lheight >= sheight {
|
if ly+lheight >= sheight {
|
||||||
lheight = sheight - ly
|
lheight = sheight - ly
|
||||||
}
|
}
|
||||||
|
if i.autocompleteList.scrollBarVisibility == ScrollBarAlways || (i.autocompleteList.scrollBarVisibility == ScrollBarAuto && i.autocompleteList.GetItemCount() > lheight) {
|
||||||
|
lwidth++ // Add space for scroll bar
|
||||||
|
}
|
||||||
i.autocompleteList.SetRect(lx, ly, lwidth, lheight)
|
i.autocompleteList.SetRect(lx, ly, lwidth, lheight)
|
||||||
i.autocompleteList.Draw(screen)
|
i.autocompleteList.Draw(screen)
|
||||||
}
|
}
|
||||||
|
41
list.go
41
list.go
@ -42,6 +42,12 @@ type List struct {
|
|||||||
// The text color for selected items.
|
// The text color for selected items.
|
||||||
selectedTextColor tcell.Color
|
selectedTextColor tcell.Color
|
||||||
|
|
||||||
|
// Visibility of the scroll bar.
|
||||||
|
scrollBarVisibility ScrollBarVisibility
|
||||||
|
|
||||||
|
// The scroll bar color.
|
||||||
|
scrollBarColor tcell.Color
|
||||||
|
|
||||||
// The background color for selected items.
|
// The background color for selected items.
|
||||||
selectedBackgroundColor tcell.Color
|
selectedBackgroundColor tcell.Color
|
||||||
|
|
||||||
@ -75,10 +81,12 @@ func NewList() *List {
|
|||||||
Box: NewBox(),
|
Box: NewBox(),
|
||||||
showSecondaryText: true,
|
showSecondaryText: true,
|
||||||
wrapAround: true,
|
wrapAround: true,
|
||||||
|
scrollBarVisibility: ScrollBarAuto,
|
||||||
mainTextColor: Styles.PrimaryTextColor,
|
mainTextColor: Styles.PrimaryTextColor,
|
||||||
secondaryTextColor: Styles.TertiaryTextColor,
|
secondaryTextColor: Styles.TertiaryTextColor,
|
||||||
shortcutColor: Styles.SecondaryTextColor,
|
shortcutColor: Styles.SecondaryTextColor,
|
||||||
selectedTextColor: Styles.PrimitiveBackgroundColor,
|
selectedTextColor: Styles.PrimitiveBackgroundColor,
|
||||||
|
scrollBarColor: Styles.ScrollBarColor,
|
||||||
selectedBackgroundColor: Styles.PrimaryTextColor,
|
selectedBackgroundColor: Styles.PrimaryTextColor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,6 +224,18 @@ func (l *List) ShowSecondaryText(show bool) *List {
|
|||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetScrollBarVisibility specifies the display of the scroll bar.
|
||||||
|
func (l *List) SetScrollBarVisibility(visibility ScrollBarVisibility) *List {
|
||||||
|
l.scrollBarVisibility = visibility
|
||||||
|
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
|
// SetWrapAround sets the flag that determines whether navigating the list will
|
||||||
// wrap around. That is, navigating downwards on the last item will move the
|
// 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
|
// selection to the first item (similarly in the other direction). If set to
|
||||||
@ -395,6 +415,18 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||||||
x, y, width, height := l.GetInnerRect()
|
x, y, width, height := l.GetInnerRect()
|
||||||
bottomLimit := y + height
|
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?
|
// Do we show any shortcuts?
|
||||||
var showShortcuts bool
|
var showShortcuts bool
|
||||||
for _, item := range l.items {
|
for _, item := range l.items {
|
||||||
@ -457,6 +489,10 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if l.scrollBarVisibility == ScrollBarAlways || (l.scrollBarVisibility == ScrollBarAuto && len(l.items) > scrollBarHeight) {
|
||||||
|
RenderScrollBar(screen, scrollBarX, y, scrollBarHeight, len(l.items), l.currentItem, index-l.offset, l.hasFocus, l.scrollBarColor)
|
||||||
|
}
|
||||||
|
|
||||||
y++
|
y++
|
||||||
|
|
||||||
if y >= bottomLimit {
|
if y >= bottomLimit {
|
||||||
@ -466,6 +502,11 @@ func (l *List) Draw(screen tcell.Screen) {
|
|||||||
// Secondary text.
|
// Secondary text.
|
||||||
if l.showSecondaryText {
|
if l.showSecondaryText {
|
||||||
Print(screen, item.SecondaryText, x, y, width, AlignLeft, l.secondaryTextColor)
|
Print(screen, item.SecondaryText, x, y, width, AlignLeft, l.secondaryTextColor)
|
||||||
|
|
||||||
|
if l.scrollBarVisibility == ScrollBarAlways || (l.scrollBarVisibility == ScrollBarAuto && len(l.items) > scrollBarHeight) {
|
||||||
|
RenderScrollBar(screen, scrollBarX, y, scrollBarHeight, len(l.items), l.currentItem, index-l.offset, l.hasFocus, l.scrollBarColor)
|
||||||
|
}
|
||||||
|
|
||||||
y++
|
y++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ type Theme struct {
|
|||||||
TertiaryTextColor tcell.Color // Tertiary text (e.g. subtitles, notes).
|
TertiaryTextColor tcell.Color // Tertiary text (e.g. subtitles, notes).
|
||||||
InverseTextColor tcell.Color // Text on primary-colored backgrounds.
|
InverseTextColor tcell.Color // Text on primary-colored backgrounds.
|
||||||
ContrastSecondaryTextColor tcell.Color // Secondary text on ContrastBackgroundColor-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
|
// Styles defines the theme for applications. The default is for a black
|
||||||
@ -32,4 +33,5 @@ var Styles = Theme{
|
|||||||
TertiaryTextColor: tcell.ColorGreen,
|
TertiaryTextColor: tcell.ColorGreen,
|
||||||
InverseTextColor: tcell.ColorBlue,
|
InverseTextColor: tcell.ColorBlue,
|
||||||
ContrastSecondaryTextColor: tcell.ColorDarkCyan,
|
ContrastSecondaryTextColor: tcell.ColorDarkCyan,
|
||||||
|
ScrollBarColor: tcell.ColorWhite,
|
||||||
}
|
}
|
||||||
|
66
table.go
66
table.go
@ -251,6 +251,12 @@ type Table struct {
|
|||||||
// The number of visible rows the last time the table was drawn.
|
// The number of visible rows the last time the table was drawn.
|
||||||
visibleRows int
|
visibleRows int
|
||||||
|
|
||||||
|
// Visibility of the scroll bar.
|
||||||
|
scrollBarVisibility ScrollBarVisibility
|
||||||
|
|
||||||
|
// The scroll bar color.
|
||||||
|
scrollBarColor tcell.Color
|
||||||
|
|
||||||
// The style of the selected rows. If this value is 0, selected rows are
|
// The style of the selected rows. If this value is 0, selected rows are
|
||||||
// simply inverted.
|
// simply inverted.
|
||||||
selectedStyle tcell.Style
|
selectedStyle tcell.Style
|
||||||
@ -273,10 +279,12 @@ type Table struct {
|
|||||||
// NewTable returns a new table.
|
// NewTable returns a new table.
|
||||||
func NewTable() *Table {
|
func NewTable() *Table {
|
||||||
return &Table{
|
return &Table{
|
||||||
Box: NewBox(),
|
Box: NewBox(),
|
||||||
bordersColor: Styles.GraphicsColor,
|
scrollBarVisibility: ScrollBarAuto,
|
||||||
separator: ' ',
|
scrollBarColor: Styles.ScrollBarColor,
|
||||||
lastColumn: -1,
|
bordersColor: Styles.GraphicsColor,
|
||||||
|
separator: ' ',
|
||||||
|
lastColumn: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,6 +308,18 @@ func (t *Table) SetBordersColor(color tcell.Color) *Table {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetScrollBarVisibility specifies the display of the scroll bar.
|
||||||
|
func (t *Table) SetScrollBarVisibility(visibility ScrollBarVisibility) *Table {
|
||||||
|
t.scrollBarVisibility = visibility
|
||||||
|
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
|
// 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
|
// is set, per default, selected cells are inverted (i.e. their foreground and
|
||||||
// background colors are swapped).
|
// background colors are swapped).
|
||||||
@ -570,6 +590,11 @@ func (t *Table) Draw(screen tcell.Screen) {
|
|||||||
t.visibleRows = height
|
t.visibleRows = height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showVerticalScrollBar := t.scrollBarVisibility == ScrollBarAlways || (t.scrollBarVisibility == ScrollBarAuto && len(t.cells) > t.visibleRows-t.fixedRows)
|
||||||
|
if showVerticalScrollBar {
|
||||||
|
width-- // Subtract space for scroll bar.
|
||||||
|
}
|
||||||
|
|
||||||
// Return the cell at the specified position (nil if it doesn't exist).
|
// Return the cell at the specified position (nil if it doesn't exist).
|
||||||
getCell := func(row, column int) *TableCell {
|
getCell := func(row, column int) *TableCell {
|
||||||
if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) {
|
if row < 0 || column < 0 || row >= len(t.cells) || column >= len(t.cells[row]) {
|
||||||
@ -766,6 +791,7 @@ ColumnLoop:
|
|||||||
toDistribute -= expWidth
|
toDistribute -= expWidth
|
||||||
expansionTotal -= expansion
|
expansionTotal -= expansion
|
||||||
}
|
}
|
||||||
|
tableWidth = width - toDistribute
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function which draws border runes.
|
// Helper function which draws border runes.
|
||||||
@ -861,6 +887,38 @@ ColumnLoop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if showVerticalScrollBar {
|
||||||
|
// Calculate scroll bar position and dimensions.
|
||||||
|
rows := len(t.cells)
|
||||||
|
|
||||||
|
scrollBarItems := rows - t.fixedRows
|
||||||
|
scrollBarHeight := t.visibleRows - t.fixedRows
|
||||||
|
|
||||||
|
scrollBarX := x + width
|
||||||
|
scrollBarY := y + t.fixedRows
|
||||||
|
if scrollBarX > x+tableWidth {
|
||||||
|
scrollBarX = x + tableWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
padTotalOffset := 1
|
||||||
|
if t.borders {
|
||||||
|
padTotalOffset = 2
|
||||||
|
|
||||||
|
scrollBarItems *= 2
|
||||||
|
scrollBarHeight = (scrollBarHeight * 2) - 1
|
||||||
|
|
||||||
|
scrollBarY += t.fixedRows + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw scroll bar.
|
||||||
|
cursor := int(float64(scrollBarItems) * (float64(t.rowOffset) / float64(((rows-t.fixedRows)-t.visibleRows)+padTotalOffset)))
|
||||||
|
for printed := 0; printed < scrollBarHeight; printed++ {
|
||||||
|
RenderScrollBar(screen, scrollBarX, scrollBarY+printed, scrollBarHeight, scrollBarItems, cursor, printed, t.hasFocus, t.scrollBarColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Draw horizontal scroll bar
|
||||||
|
|
||||||
// Helper function which colors the background of a box.
|
// Helper function which colors the background of a box.
|
||||||
// backgroundColor == tcell.ColorDefault => Don't color the background.
|
// backgroundColor == tcell.ColorDefault => Don't color the background.
|
||||||
// textColor == tcell.ColorDefault => Don't change the text color.
|
// textColor == tcell.ColorDefault => Don't change the text color.
|
||||||
|
37
treeview.go
37
treeview.go
@ -270,6 +270,12 @@ type TreeView struct {
|
|||||||
// The color of the lines.
|
// The color of the lines.
|
||||||
graphicsColor tcell.Color
|
graphicsColor tcell.Color
|
||||||
|
|
||||||
|
// Visibility of the scroll bar.
|
||||||
|
scrollBarVisibility ScrollBarVisibility
|
||||||
|
|
||||||
|
// The scroll bar color.
|
||||||
|
scrollBarColor tcell.Color
|
||||||
|
|
||||||
// An optional function which is called when the user has navigated to a new
|
// An optional function which is called when the user has navigated to a new
|
||||||
// tree node.
|
// tree node.
|
||||||
changed func(node *TreeNode)
|
changed func(node *TreeNode)
|
||||||
@ -288,9 +294,11 @@ type TreeView struct {
|
|||||||
// NewTreeView returns a new tree view.
|
// NewTreeView returns a new tree view.
|
||||||
func NewTreeView() *TreeView {
|
func NewTreeView() *TreeView {
|
||||||
return &TreeView{
|
return &TreeView{
|
||||||
Box: NewBox(),
|
Box: NewBox(),
|
||||||
graphics: true,
|
scrollBarVisibility: ScrollBarAuto,
|
||||||
graphicsColor: Styles.GraphicsColor,
|
graphics: true,
|
||||||
|
graphicsColor: Styles.GraphicsColor,
|
||||||
|
scrollBarColor: Styles.ScrollBarColor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,6 +373,18 @@ func (t *TreeView) SetGraphicsColor(color tcell.Color) *TreeView {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetScrollBarVisibility specifies the display of the scroll bar.
|
||||||
|
func (t *TreeView) SetScrollBarVisibility(visibility ScrollBarVisibility) *TreeView {
|
||||||
|
t.scrollBarVisibility = visibility
|
||||||
|
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
|
// SetChangedFunc sets the function which is called when the user navigates to
|
||||||
// a new tree node.
|
// a new tree node.
|
||||||
func (t *TreeView) SetChangedFunc(handler func(node *TreeNode)) *TreeView {
|
func (t *TreeView) SetChangedFunc(handler func(node *TreeNode)) *TreeView {
|
||||||
@ -601,12 +621,16 @@ func (t *TreeView) Draw(screen tcell.Screen) {
|
|||||||
t.offsetY = 0
|
t.offsetY = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate scroll bar position.
|
||||||
|
rows := len(t.nodes)
|
||||||
|
cursor := int(float64(rows) * (float64(t.offsetY) / float64(rows-height)))
|
||||||
|
|
||||||
// Draw the tree.
|
// Draw the tree.
|
||||||
posY := y
|
posY := y
|
||||||
lineStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.graphicsColor)
|
lineStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.graphicsColor)
|
||||||
for index, node := range t.nodes {
|
for index, node := range t.nodes {
|
||||||
// Skip invisible parts.
|
// Skip invisible parts.
|
||||||
if posY >= y+height+1 {
|
if posY >= y+height {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if index < t.offsetY {
|
if index < t.offsetY {
|
||||||
@ -668,6 +692,11 @@ func (t *TreeView) Draw(screen tcell.Screen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw scroll bar.
|
||||||
|
if t.scrollBarVisibility == ScrollBarAlways || (t.scrollBarVisibility == ScrollBarAuto && rows > height) {
|
||||||
|
RenderScrollBar(screen, x+(width-1), posY, height, rows, cursor, posY-y, t.hasFocus, tcell.ColorWhite)
|
||||||
|
}
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
posY++
|
posY++
|
||||||
}
|
}
|
||||||
|
47
util.go
47
util.go
@ -628,3 +628,50 @@ func iterateStringReverse(text string, callback func(main rune, comb []rune, tex
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScrollBarVisibility specifies the display of a scroll bar.
|
||||||
|
type ScrollBarVisibility int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ScrollBarNever never shows a scroll bar.
|
||||||
|
ScrollBarNever ScrollBarVisibility = iota
|
||||||
|
|
||||||
|
// ScrollBarAuto shows a scroll bar when there are items offscreen.
|
||||||
|
ScrollBarAuto
|
||||||
|
|
||||||
|
// ScrollBarAlways always shows a scroll bar.
|
||||||
|
ScrollBarAlways
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 New Issue
Block a user