v0.1.0 initial commit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user