on main help sign in
fix(ui): list: move focused logic to render callback
commitdd9e8ca15c72160f63fb017777a76029d65cbcef authorAyman Bagabas <ayman.bagabas@gmail.com> dateWed 21 Jan 2026, 11:48 (9 months ago) parentd5b83c0
4 files changed+30−5
internal/ui/dialog/models_list.go+1 internal/ui/list/filterable.go+1 internal/ui/list/focus.go+13 internal/ui/list/list.go+15−5
internal/ui/dialog/models_list.go+1
@@ -26,6 +26,7 @@ func NewModelsList(sty *styles.Styles, groups ...ModelGroup) *ModelsList {
2626 groups: groups,
2727 t: sty,
2828 }
29+ f.RegisterRenderCallback(list.FocusedRenderCallback(f.List))
2930 return f
3031}
3132
internal/ui/list/filterable.go+1
@@ -31,6 +31,7 @@ func NewFilterableList(items ...FilterableItem) *FilterableList {
3131 List: NewList(),
3232 items: items,
3333 }
34+ f.RegisterRenderCallback(FocusedRenderCallback(f.List))
3435 f.SetItems(items...)
3536 return f
3637}
internal/ui/list/focus.gonew file+13
@@ -0,0 +1,13 @@
1+package list
2+
3+// FocusedRenderCallback is a helper function that returns a render callback
4+// that marks items as focused during rendering.
5+func FocusedRenderCallback(list *List) RenderCallback {
6+ return func(idx, selectedIdx int, item Item) Item {
7+ if focusable, ok := item.(Focusable); ok {
8+ focusable.SetFocused(list.Focused() && idx == selectedIdx)
9+ return focusable.(Item)
10+ }
11+ return item
12+ }
13+}
internal/ui/list/list.go+15−5
@@ -49,9 +49,13 @@ func NewList(items ...Item) *List {
4949 return l
5050}
5151
52+// RenderCallback defines a function that can modify an item before it is
53+// rendered.
54+type RenderCallback func(idx, selectedIdx int, item Item) Item
55+
5256// RegisterRenderCallback registers a callback to be called when rendering
5357// items. This can be used to modify items before they are rendered.
54−func (l *List) RegisterRenderCallback(cb func(idx, selectedIdx int, item Item) Item) {
58+func (l *List) RegisterRenderCallback(cb RenderCallback) {
5559 l.renderCallbacks = append(l.renderCallbacks, cb)
5660}
5761
@@ -66,6 +70,11 @@ func (l *List) SetGap(gap int) {
6670 l.gap = gap
6771}
6872
73+// Gap returns the gap between items.
74+func (l *List) Gap() int {
75+ return l.gap
76+}
77+
6978// SetReverse shows the list in reverse order.
7079func (l *List) SetReverse(reverse bool) {
7180 l.reverse = reverse
@@ -101,10 +110,6 @@ func (l *List) getItem(idx int) renderedItem {
101110 }
102111 }
103112
104− if focusable, isFocusable := item.(Focusable); isFocusable {
105− focusable.SetFocused(l.focused && idx == l.selectedIdx)
106− }
107−
108113 rendered := item.Render(l.width)
109114 rendered = strings.TrimRight(rendered, "\n")
110115 height := countLines(rendered)
@@ -348,6 +353,11 @@ func (l *List) RemoveItem(idx int) {
348353 }
349354}
350355
356+// Focused returns whether the list is focused.
357+func (l *List) Focused() bool {
358+ return l.focused
359+}
360+
351361// Focus sets the focus state of the list.
352362func (l *List) Focus() {
353363 l.focused = true