package ui import ( "gioui.org/layout" "gioui.org/unit" ) // Region defines a screen area in device-independent pixels (Dp). // All element positions and sizes use Dp for device independence. type Region struct { X, Y Dp W, H Dp } // Element is the base interface for all UI elements. // Elements know how to draw themselves when given a Renderer. type Element interface { Region() Region Visible() bool Draw(gtx layout.Context, r *Renderer) } // Container holds child elements and draws them within its bounds. // Children's regions are screen-space; clipping handles containment. type Container struct { id string region Region visible bool background Color children []Element } func (c Container) Region() Region { return c.region } func (c Container) Visible() bool { return c.visible } func (c Container) Draw(gtx layout.Context, r *Renderer) { // Draw background — children are drawn by drawElement, not here if c.background != (Color{}) { r.drawBg(gtx, c.region, c.background) } } // NewContainer creates a Container with the given region, background, and children. func NewContainer(region Region, bg Color, children []Element) Container { return Container{ region: region, visible: true, background: bg, children: children, } } // --- Leaf element types (each implements Element and knows how to Draw itself) --- // Label displays static text. type Label struct { id string region Region visible bool interactions []Interaction Text string Align TextAlign FontSize unit.Sp // 0 = theme default Color Color // 0 = theme default Bold bool } func (l Label) Region() Region { return l.region } func (l Label) Visible() bool { return l.visible } func (l Label) Interactions() []Interaction { return l.interactions } func (l Label) Draw(gtx layout.Context, r *Renderer) { col := l.Color if col == (Color{}) { col = Color{R: 0, G: 0, B: 0, A: 255} } r.drawText(gtx, l.Text, l.FontSize, l.region, l.Align, col, l.id) } func (l Label) ID() string { return l.id } // NewLabel creates a visible Label element. func NewLabel(text string, fontSize unit.Sp, region Region, align TextAlign, id string, interactions []Interaction) Label { return Label{ id: id, region: region, visible: true, interactions: interactions, Text: text, FontSize: fontSize, Align: align, } } // Icon displays a named icon image. type Icon struct { id string region Region visible bool interactions []Interaction Name string // icon name, e.g. "cut" Size Dp // 0 = default icon size } func (i Icon) Region() Region { return i.region } func (i Icon) Visible() bool { return i.visible } func (i Icon) Interactions() []Interaction { return i.interactions } func (i Icon) Draw(gtx layout.Context, r *Renderer) { img := r.icon(i.Name) if img == nil { return } // Auto-scale: if Size is 0, use region dimensions so the icon fills its region w, h := Dp(0), Dp(0) if i.Size == 0 { w, h = i.region.W, i.region.H } r.drawPng(gtx, img, i.region, w, h) } func (i Icon) ID() string { return i.id } // NewIcon creates a visible Icon element. func NewIcon(name string, region Region, size Dp) Icon { return Icon{ region: region, visible: true, Name: name, Size: size, } } // TextField accepts text input or displays multiline text. type TextField struct { id string region Region visible bool interactions []Interaction Value string Placeholder string Focused bool Multiline bool ScrollOffset Dp VisibleLines []Line WordWrap bool WrapWidth Dp } func (tf TextField) Region() Region { return tf.region } func (tf TextField) Visible() bool { return tf.visible } func (tf TextField) ID() string { return tf.id } func (tf TextField) Interactions() []Interaction { return tf.interactions } // Line represents a single line of text in a multiline TextField. type Line struct { Text string LineNumber int // 1-indexed, for display } // ListView displays a scrollable list of items. type ListView struct { id string region Region visible bool interactions []Interaction Items []ListItem ScrollOffset int Selected int } func (lv ListView) Region() Region { return lv.region } func (lv ListView) Visible() bool { return lv.visible } func (lv ListView) ID() string { return lv.id } func (lv ListView) Interactions() []Interaction { return lv.interactions } // ListItem is a single entry in a ListView. type ListItem struct { Text string Subtext string Selected bool } // AlphaIndex displays an alphabetical index for quick navigation. type AlphaIndex struct { id string region Region visible bool interactions []Interaction Letters []string ActiveLetter string } func (ai AlphaIndex) Region() Region { return ai.region } func (ai AlphaIndex) Visible() bool { return ai.visible } func (ai AlphaIndex) ID() string { return ai.id } func (ai AlphaIndex) Interactions() []Interaction { return ai.interactions } // NewAlphaIndex creates a visible AlphaIndex element. func NewAlphaIndex(region Region, letters []string) AlphaIndex { return AlphaIndex{ region: region, visible: true, Letters: letters, } } // Button is an interactive button element. type Button struct { id string region Region visible bool interactions []Interaction Text string Enabled bool Primary bool } func (b Button) Region() Region { return b.region } func (b Button) Visible() bool { return b.visible } func (b Button) Interactions() []Interaction { return b.interactions } func (b Button) Draw(gtx layout.Context, r *Renderer) { col := Color{R: 0, G: 0, B: 0, A: 255} r.drawText(gtx, b.Text, r.theme.FontSize, b.region, AlignStart, col, b.id) } func (b Button) ID() string { return b.id } // NewButton creates a visible Button element. func NewButton(text string, enabled bool, primary bool, region Region) Button { return Button{ region: region, visible: true, Text: text, Enabled: enabled, Primary: primary, } } // SearchBar displays an in-editor search interface. type SearchBar struct { id string region Region visible bool interactions []Interaction Query string Match int Total int Forward bool } func (sb SearchBar) Region() Region { return sb.region } func (sb SearchBar) Visible() bool { return sb.visible } func (sb SearchBar) ID() string { return sb.id } func (sb SearchBar) Interactions() []Interaction { return sb.interactions } // NewSearchBar creates a visible SearchBar element. func NewSearchBar(region Region, query string, match, total int, forward bool) SearchBar { return SearchBar{ region: region, visible: true, Query: query, Match: match, Total: total, Forward: forward, } } // Cursor displays the text cursor and optional selection highlight. type Cursor struct { id string region Region visible bool interactions []Interaction Line int Column int Blinking bool Selection *Selection } func (c Cursor) Region() Region { return c.region } func (c Cursor) Visible() bool { return c.visible } func (c Cursor) ID() string { return c.id } func (c Cursor) Interactions() []Interaction { return c.interactions } // Selection represents a text selection range. type Selection struct { StartLine, StartCol int EndLine, EndCol int } // MergeHunk displays a conflict resolution hunk. type MergeHunk struct { id string region Region visible bool interactions []Interaction HunkNumber int TotalHunks int LineRange string ContextLines []string OurLines []string TheirLines []string Resolution HunkResolution } func (mh MergeHunk) Region() Region { return mh.region } func (mh MergeHunk) Visible() bool { return mh.visible } func (mh MergeHunk) ID() string { return mh.id } func (mh MergeHunk) Interactions() []Interaction { return mh.interactions } // HunkResolution represents the resolution state of a merge hunk. type HunkResolution int const ( Unresolved HunkResolution = iota KeepOurs KeepTheirs MergeBoth ) // Toast displays a temporary notification. type Toast struct { id string region Region visible bool interactions []Interaction Text string Timeout int // milliseconds } func (t Toast) Region() Region { return t.region } func (t Toast) Visible() bool { return t.visible } func (t Toast) ID() string { return t.id } func (t Toast) Interactions() []Interaction { return t.interactions } // NewToast creates a visible Toast element. func NewToast(region Region, text string, timeout int) Toast { return Toast{ region: region, visible: true, Text: text, Timeout: timeout, } } // Spacer adds vertical or horizontal space. type Spacer struct { id string region Region visible bool interactions []Interaction } func (s Spacer) Region() Region { return s.region } func (s Spacer) Visible() bool { return s.visible } func (s Spacer) ID() string { return s.id } func (s Spacer) Interactions() []Interaction { return s.interactions } // NewSpacer creates a visible Spacer element. func NewSpacer(height Dp) Spacer { return Spacer{ region: Region{H: height}, visible: true, } } // --- Utility types --- // Color is an RGBA color. type Color struct { R, G, B, A uint8 } // Theme holds styling defaults for the UI. type Theme struct { FontSize unit.Sp // Gio's shaper requires unit.Sp for font sizes } // TextAlign specifies horizontal text alignment. type TextAlign int const ( AlignStart TextAlign = iota AlignCenter AlignEnd ) // InputType specifies the type of user input event. type InputType int const ( Tap InputType = iota DoubleTap LongPress Scroll KeyDown KeyUp ) // Interaction pairs a gesture type with a handler function. type Interaction struct { Gesture InputType Handler func(any) } // Interactive is implemented by elements that respond to input events. type Interactive interface { ID() string Interactions() []Interaction } // InputEvent represents a user input event with its handler. type InputEvent struct { Handler func(any) Data any } // ConfigEvent represents a window configuration change (resize, orientation). // Width and Height are in device-independent pixels (Dp). type ConfigEvent struct { Width Dp Height Dp }