90 lines
2.2 KiB
Go
90 lines
2.2 KiB
Go
package tui
|
|
|
|
import (
|
|
"github.com/lixenwraith/color"
|
|
"github.com/lixenwraith/terminal"
|
|
)
|
|
|
|
// ScrollBarOpts configures scrollbar rendering
|
|
type ScrollBarOpts struct {
|
|
ThumbFg color.RGB
|
|
TrackFg color.RGB
|
|
Bg color.RGB // Zero inherits the existing cell background
|
|
Thumb rune // 0 = '█'
|
|
Track rune // 0 = '░'
|
|
HideIdle bool // Draw nothing when content fits the viewport
|
|
}
|
|
|
|
// ScrollBar draws a vertical scrollbar track with thumb in a single color
|
|
func (r Region) ScrollBar(x int, offset, visible, total int, fg color.RGB) {
|
|
r.ScrollBarStyled(x, offset, visible, total, ScrollBarOpts{ThumbFg: fg, TrackFg: fg})
|
|
}
|
|
|
|
// ScrollBarStyled draws a vertical scrollbar at column x with explicit styling
|
|
func (r Region) ScrollBarStyled(x int, offset, visible, total int, opts ScrollBarOpts) {
|
|
if x < 0 || x >= r.W || r.H < 1 {
|
|
return
|
|
}
|
|
|
|
thumbCh, trackCh := opts.Thumb, opts.Track
|
|
if thumbCh == 0 {
|
|
thumbCh = '█'
|
|
}
|
|
if trackCh == 0 {
|
|
trackCh = '░'
|
|
}
|
|
|
|
trackH := r.H
|
|
if total <= visible || trackH < 3 {
|
|
// No scrolling needed or track too small
|
|
if opts.HideIdle {
|
|
return
|
|
}
|
|
for y := range trackH {
|
|
r.Cell(x, y, '│', opts.TrackFg, opts.Bg, terminal.AttrDim)
|
|
}
|
|
return
|
|
}
|
|
|
|
thumbH := min(max((visible*trackH)/total, 1), trackH)
|
|
maxScroll := total - visible
|
|
thumbY := 0
|
|
if maxScroll > 0 {
|
|
thumbY = (offset * (trackH - thumbH)) / maxScroll
|
|
}
|
|
thumbY = min(max(thumbY, 0), trackH-thumbH)
|
|
|
|
for y := range trackH {
|
|
if y >= thumbY && y < thumbY+thumbH {
|
|
r.Cell(x, y, thumbCh, opts.ThumbFg, opts.Bg, terminal.AttrNone)
|
|
} else {
|
|
r.Cell(x, y, trackCh, opts.TrackFg, opts.Bg, terminal.AttrDim)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ScrollIndicator draws compact indicator text (Top/Bot/XX%)
|
|
func (r Region) ScrollIndicator(y int, offset, visible, total int, fg color.RGB) {
|
|
if y < 0 || y >= r.H {
|
|
return
|
|
}
|
|
|
|
var text string
|
|
if total <= visible || offset <= 0 {
|
|
text = "Top"
|
|
} else if offset+visible >= total {
|
|
text = "Bot"
|
|
} else {
|
|
pct := ScrollPercent(offset, visible, total)
|
|
if pct >= 100 {
|
|
text = "99%"
|
|
} else if pct >= 10 {
|
|
text = string(rune('0'+pct/10)) + string(rune('0'+pct%10)) + "%"
|
|
} else {
|
|
text = " " + string(rune('0'+pct)) + "%"
|
|
}
|
|
}
|
|
|
|
r.TextRight(y, text, fg, color.RGB{}, terminal.AttrDim)
|
|
}
|