115 lines
2.5 KiB
Go
115 lines
2.5 KiB
Go
package tui
|
|
|
|
// ScrollState tracks scroll position for a scrollable container
|
|
type ScrollState struct {
|
|
Offset int // First visible item index
|
|
Total int // Total item count
|
|
Visible int // Visible item count (viewport height)
|
|
Selection int // Currently selected item, -1 if none
|
|
}
|
|
|
|
// NewScrollState creates initialized scroll state
|
|
func NewScrollState(total, visible int) *ScrollState {
|
|
return &ScrollState{
|
|
Total: total,
|
|
Visible: visible,
|
|
Selection: -1,
|
|
}
|
|
}
|
|
|
|
// --- Scroll manipulation ---
|
|
|
|
// ScrollBy adjusts offset by delta, clamping to valid range
|
|
func (s *ScrollState) ScrollBy(delta int) {
|
|
s.Offset += delta
|
|
s.Clamp()
|
|
}
|
|
|
|
// ScrollTo sets offset to specific position
|
|
func (s *ScrollState) ScrollTo(pos int) {
|
|
s.Offset = pos
|
|
s.Clamp()
|
|
}
|
|
|
|
// EnsureVisible adjusts offset to make item at pos visible
|
|
func (s *ScrollState) EnsureVisible(pos int) {
|
|
if pos < s.Offset {
|
|
s.Offset = pos
|
|
} else if pos >= s.Offset+s.Visible {
|
|
s.Offset = pos - s.Visible + 1
|
|
}
|
|
s.Clamp()
|
|
}
|
|
|
|
// Clamp ensures offset is within valid range
|
|
func (s *ScrollState) Clamp() {
|
|
s.Offset = ClampScroll(s.Offset, s.Visible, s.Total)
|
|
}
|
|
|
|
// --- Page navigation ---
|
|
|
|
// PageUp scrolls up by half visible height
|
|
func (s *ScrollState) PageUp() {
|
|
s.ScrollBy(-PageDelta(s.Visible))
|
|
}
|
|
|
|
// PageDown scrolls down by half visible height
|
|
func (s *ScrollState) PageDown() {
|
|
s.ScrollBy(PageDelta(s.Visible))
|
|
}
|
|
|
|
// --- State updates ---
|
|
|
|
// SetTotal updates total count and reclamps
|
|
func (s *ScrollState) SetTotal(total int) {
|
|
s.Total = total
|
|
s.Clamp()
|
|
if s.Selection >= total {
|
|
s.Selection = total - 1
|
|
}
|
|
}
|
|
|
|
// SetVisible updates visible count and reclamps
|
|
func (s *ScrollState) SetVisible(visible int) {
|
|
s.Visible = visible
|
|
s.Clamp()
|
|
}
|
|
|
|
// --- Positions queries ---
|
|
|
|
// AtTop returns true if scrolled to top
|
|
func (s *ScrollState) AtTop() bool {
|
|
return s.Offset == 0
|
|
}
|
|
|
|
// AtBottom returns true if scrolled to bottom
|
|
func (s *ScrollState) AtBottom() bool {
|
|
if s.Total <= s.Visible {
|
|
return true
|
|
}
|
|
return s.Offset >= s.Total-s.Visible
|
|
}
|
|
|
|
// --- Selection management ---
|
|
|
|
// Select sets selection and ensures it's visible
|
|
func (s *ScrollState) Select(idx int) {
|
|
s.Selection = ClampCursor(idx, s.Total)
|
|
s.EnsureVisible(s.Selection)
|
|
}
|
|
|
|
// SelectNext moves selection down
|
|
func (s *ScrollState) SelectNext() {
|
|
if s.Selection < s.Total-1 {
|
|
s.Selection++
|
|
s.EnsureVisible(s.Selection)
|
|
}
|
|
}
|
|
|
|
// SelectPrev moves selection up
|
|
func (s *ScrollState) SelectPrev() {
|
|
if s.Selection > 0 {
|
|
s.Selection--
|
|
s.EnsureVisible(s.Selection)
|
|
}
|
|
} |