gitstack

grindlemire/go-tui code browser

3.0 KB Go 143 lines 2026-06-03 · d15bb9f 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
package tui

import "maps"

import "sync"

// Ref is a reference to an Element, set during construction
// and accessed later in handlers. Thread-safe.
type Ref struct {
	mu    sync.RWMutex
	value *Element
}

// NewRef creates a new empty Ref.
func NewRef() *Ref {
	return &Ref{}
}

// Set stores the element in this ref.
func (r *Ref) Set(v *Element) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.value = v
}

// El returns the referenced element, or nil if not yet set.
func (r *Ref) El() *Element {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.value
}

// IsSet returns true if the ref has been set to a non-nil element.
func (r *Ref) IsSet() bool {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.value != nil
}

// RefComponent extracts a typed Component from a Ref.
// Returns the zero value of T if the ref is nil, has no element,
// has no component, or the component is not of type T.
func RefComponent[T Component](ref *Ref) T {
	var zero T
	if ref == nil {
		return zero
	}
	if el := ref.El(); el != nil {
		if comp := el.Component(); comp != nil {
			if typed, ok := comp.(T); ok {
				return typed
			}
		}
	}
	return zero
}

// RefList holds references to multiple elements created in a loop.
// Thread-safe.
type RefList struct {
	mu    sync.RWMutex
	elems []*Element
}

// NewRefList creates a new empty RefList.
func NewRefList() *RefList {
	return &RefList{}
}

// Append adds an element to this ref list.
func (r *RefList) Append(el *Element) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.elems = append(r.elems, el)
}

// All returns a copy of all referenced elements.
func (r *RefList) All() []*Element {
	r.mu.RLock()
	defer r.mu.RUnlock()
	out := make([]*Element, len(r.elems))
	copy(out, r.elems)
	return out
}

// At returns the element at the given index, or nil if out of bounds.
func (r *RefList) At(i int) *Element {
	r.mu.RLock()
	defer r.mu.RUnlock()
	if i < 0 || i >= len(r.elems) {
		return nil
	}
	return r.elems[i]
}

// Len returns the number of elements in this ref list.
func (r *RefList) Len() int {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return len(r.elems)
}

// RefMap holds keyed references to elements created in a loop.
// Thread-safe.
type RefMap[K comparable] struct {
	mu    sync.RWMutex
	elems map[K]*Element
}

// NewRefMap creates a new empty RefMap.
func NewRefMap[K comparable]() *RefMap[K] {
	return &RefMap[K]{elems: make(map[K]*Element)}
}

// Put stores an element with the given key.
func (r *RefMap[K]) Put(key K, el *Element) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.elems[key] = el
}

// Get returns the element for the given key, or nil if not found.
func (r *RefMap[K]) Get(key K) *Element {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return r.elems[key]
}

// All returns a copy of all keyed elements.
func (r *RefMap[K]) All() map[K]*Element {
	r.mu.RLock()
	defer r.mu.RUnlock()
	out := make(map[K]*Element, len(r.elems))
	maps.Copy(out, r.elems)
	return out
}

// Len returns the number of elements in this ref map.
func (r *RefMap[K]) Len() int {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return len(r.elems)
}