gitstack

grindlemire/go-tui code browser

5.3 KB Go 167 lines 2026-06-27 · af23124 raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package tui

// Compile-time check that Element implements the required interfaces.
var (
	_ Viewable   = (*Element)(nil)
	_ Focusable  = (*Element)(nil)
	_ Layoutable = (*Element)(nil)
)

// TextAlign specifies how text is aligned within its content area.
type TextAlign int

const (
	// TextAlignLeft aligns text to the left edge (default).
	TextAlignLeft TextAlign = iota
	// TextAlignCenter centers text horizontally.
	TextAlignCenter
	// TextAlignRight aligns text to the right edge.
	TextAlignRight
)

// ScrollMode specifies how an element scrolls its content.
type ScrollMode int

const (
	// ScrollNone disables scrolling (default).
	ScrollNone ScrollMode = iota
	// ScrollVertical enables vertical scrolling.
	ScrollVertical
	// ScrollHorizontal enables horizontal scrolling.
	ScrollHorizontal
	// ScrollBoth enables both vertical and horizontal scrolling.
	ScrollBoth
)

// OverflowMode specifies how an element handles content that exceeds its bounds.
type OverflowMode int

const (
	// OverflowVisible allows content to render outside the element's bounds (default).
	OverflowVisible OverflowMode = iota
	// OverflowHidden clips content at the element's bounds without scrollbars.
	OverflowHidden
)

// Element is a layout container with visual properties.
// It implements Layoutable and owns its children directly.
type Element struct {
	// Tree structure (single source of truth)
	children []*Element
	parent   *Element
	app      *App

	// Layout properties
	style  LayoutStyle
	layout LayoutResult
	dirty  bool

	// Visual properties
	border           BorderStyle
	borderStyle      Style
	background       *Style    // nil = transparent
	borderTitle      string    // title text drawn in the top border (DrawBoxWithTitle)
	borderTitleAlign TextAlign // alignment of the border title (default TextAlignCenter)
	borderTitleStyle *Style    // title text style (nil = use borderStyle)
	focusBorderStyle *Style    // border style when focused (nil = no change)

	// Text properties
	text         string
	richText     []TextSpan // when non-empty, takes precedence over text
	textStyle    Style
	textStyleSet bool // true if textStyle was explicitly configured (false = inherit from parent)
	textAlign    TextAlign
	truncate     bool
	noWrap       bool // true = wrapping disabled; false (default) = wrapping enabled

	// Focus properties
	focusable        bool
	tabStop          bool // whether this element appears in Tab/Shift+Tab navigation
	focused          bool
	autoFocus        bool // request initial focus on this element
	onFocus          func(*Element)
	onBlur           func(*Element)
	onActivate       func() // called when Enter is pressed while focused (e.g. modal buttons)
	savedBorderStyle Style  // border style saved before focus highlight
	hasSavedBorder   bool   // true if savedBorderStyle is valid

	// Tree notification
	onChildAdded     func(*Element)
	onFocusableAdded func(Focusable)

	// Custom render hook (used by wrappers that need custom rendering)
	onRender func(*Element, *Buffer)

	// Scroll properties
	scrollMode            ScrollMode
	scrollX               int  // Current horizontal scroll offset
	scrollY               int  // Current vertical scroll offset
	contentWidth          int  // Computed content width (may exceed viewport)
	contentHeight         int  // Computed content height (may exceed viewport)
	scrollToBottomPending bool // Scroll to bottom after next layout

	// Scrollbar styles
	scrollbarStyle      Style
	scrollbarThumbStyle Style
	scrollbarHidden     bool // true = don't draw scrollbar or reserve gutter

	// HR properties
	hr bool // true if this element is a horizontal rule

	// Visibility
	hidden bool

	// Overlay flag - element renders in overlay pass, not in normal tree
	overlay bool

	// Overflow clipping
	overflow OverflowMode

	// Gradient properties (nil = no gradient, use solid color)
	textGradient   *Gradient
	bgGradient     *Gradient
	borderGradient *Gradient

	// Pre-render hook for custom update logic (polling, animations, etc.)
	onUpdate func()

	// Watchers attached to this element (timers, channel watchers, etc.)
	watchers []Watcher

	// Tag identifies the element type for layout dispatch (e.g., "table", "tr", "td", "th")
	tag string

	// Component that produced this element (set by Mount, read during tree walks)
	component Component

	// Content-local cursor source. When set, the element reports a terminal
	// cursor position via ReportCursor. Returns (col, row) within the element's
	// content area and whether the cursor is visible. nil = no cursor.
	cursorSource cursorSource

	// cursorReport caches where the cursor landed on screen, captured by the
	// renderer at this element's draw site and returned by ReportCursor.
	cursorReport cursorReport
}

// cursorReport is the screen-space result captured for an element's cursor during
// render. visible is false when there is no source, the source reports invisible,
// or the cursor is clipped out of view.
type cursorReport struct {
	x, y    int
	visible bool
}

// New creates a new Element with the given options.
// By default, an Element has Auto width/height (flexes to fill available space).
func New(opts ...Option) *Element {
	e := &Element{
		style:            DefaultLayoutStyle(),
		borderTitleAlign: TextAlignCenter,
		dirty:            true,
	}
	for _, opt := range opts {
		opt(e)
	}
	return e
}