1 package etk
2
3 import (
4 "image"
5 "image/color"
6
7 "github.com/hajimehoshi/ebiten/v2"
8 )
9
10
11 type Select struct {
12 *Box
13 label *Text
14 list *List
15 onSelect func(index int) (accept bool)
16 items []string
17 open bool
18 }
19
20
21 func NewSelect(itemHeight int, onSelect func(index int) (accept bool)) *Select {
22 textColor := Style.ButtonTextColor
23 if textColor.A == 0 {
24 textColor = Style.TextColorDark
25 }
26 s := &Select{
27 Box: NewBox(),
28 label: NewText(""),
29 onSelect: onSelect,
30 }
31 s.label.SetAutoResize(true)
32 s.label.SetVertical(AlignCenter)
33 s.label.SetForeground(textColor)
34 s.SetBackground(Style.ButtonBgColor)
35 s.list = NewList(itemHeight, s.selectList)
36 s.list.SetBackground(Style.ButtonBgColor)
37 s.list.SetDrawBorder(true)
38 s.list.SetVisible(false)
39 s.list.SetSelectionMode(SelectRow)
40 s.AddChild(s.list)
41 s.updateLabel()
42 return s
43 }
44
45
46 func (s *Select) SetRect(r image.Rectangle) {
47 s.Lock()
48 defer s.Unlock()
49 s.rect = r
50 s.label.SetRect(r)
51 listRect := r.Add(image.Point{X: 0, Y: r.Dy()})
52 itemCount := len(s.items)
53 listRect.Max.Y = listRect.Min.Y + itemCount*s.list.itemHeight
54 _, height := ScreenSize()
55 if listRect.Max.Y > height {
56 listRect.Max.Y = height
57 }
58 s.list.SetRect(listRect)
59 }
60
61
62
63
64 func (s *Select) Clip() bool {
65 return false
66 }
67
68
69 func (s *Select) SetHighlightColor(c color.RGBA) {
70 s.list.SetHighlightColor(c)
71 }
72
73
74 func (s *Select) SetSelectedItem(index int) {
75 s.Lock()
76 defer s.Unlock()
77 if index < 0 || index >= len(s.items) {
78 return
79 }
80 s.list.SetSelectedItem(0, index)
81 s.updateLabel()
82 }
83
84
85 func (s *Select) Children() []Widget {
86 s.Lock()
87 defer s.Unlock()
88
89 return s.children
90 }
91
92
93 func (s *Select) AddChild(w ...Widget) {
94 s.Lock()
95 defer s.Unlock()
96
97 s.children = append(s.children, w...)
98 }
99
100
101 func (s *Select) Clear() {
102 s.Lock()
103 defer s.Unlock()
104
105 s.items = nil
106 s.list.Clear()
107 s.updateLabel()
108 }
109
110
111 func (s *Select) AddOption(label string) {
112 s.Lock()
113 defer s.Unlock()
114
115 s.items = append(s.items, label)
116 if len(s.items) == 1 {
117 s.list.selectedY = 0
118 s.updateLabel()
119 }
120
121 textColor := Style.ButtonTextColor
122 if textColor.A == 0 {
123 textColor = Style.TextColorDark
124 }
125
126 t := NewText(label)
127 t.SetVertical(AlignCenter)
128 t.SetForeground(textColor)
129 t.SetAutoResize(true)
130 s.list.AddChildAt(t, 0, len(s.items)-1)
131 }
132
133 func (s *Select) updateLabel() {
134 var text string
135 if len(s.items) > 0 && s.list.selectedY >= 0 && s.list.selectedY < len(s.items) {
136 text = s.items[s.list.selectedY]
137 }
138 if s.open {
139 text = "▼ " + text
140 } else {
141 text = "▶ " + text
142 }
143 s.label.SetText(text)
144 }
145
146 func (s *Select) selectList(index int) (accept bool) {
147 s.Lock()
148 s.list.grid.visible = false
149 s.open = false
150 onSelect := s.onSelect
151 s.Unlock()
152
153 if onSelect != nil {
154 if !onSelect(index) {
155 return false
156 }
157 }
158
159 s.list.selectedY = index
160 s.updateLabel()
161 return true
162 }
163
164
165 func (s *Select) SetMenuVisible(visible bool) {
166 s.Lock()
167 defer s.Unlock()
168
169 s._setMenuVisible(visible)
170 }
171
172 func (s *Select) _setMenuVisible(visible bool) {
173 s.open = visible
174 s.list.SetVisible(visible)
175 s.updateLabel()
176
177 if !visible {
178 s.background = Style.ButtonBgColor
179 } else {
180 const dim = 0.9
181 s.background = color.RGBA{uint8(float64(Style.ButtonBgColor.R) * dim), uint8(float64(Style.ButtonBgColor.G) * dim), uint8(float64(Style.ButtonBgColor.B) * dim), 255}
182 }
183 }
184
185
186
187 func (s *Select) Cursor() ebiten.CursorShapeType {
188 return ebiten.CursorShapePointer
189 }
190
191
192 func (s *Select) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
193 return false, nil
194 }
195
196
197 func (s *Select) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
198 s.Lock()
199 defer s.Unlock()
200
201 if clicked {
202 s._setMenuVisible(!s.open)
203 }
204 return true, nil
205 }
206
207
208 func (s *Select) Draw(screen *ebiten.Image) error {
209 s.Lock()
210 defer s.Unlock()
211
212 screen.SubImage(s.label.rect).(*ebiten.Image).Fill(s.background)
213
214
215 s.label.Draw(screen)
216
217
218 r := s.rect
219 borderSize := Scale(Style.ButtonBorderSize)
220 borderLeft, borderTop := Style.ButtonBorderLeft, Style.ButtonBorderTop
221 borderRight, borderBottom := Style.ButtonBorderRight, Style.ButtonBorderBottom
222 if !s.open {
223 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+borderSize, r.Max.Y)).(*ebiten.Image).Fill(borderLeft)
224 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+borderSize)).(*ebiten.Image).Fill(borderTop)
225 screen.SubImage(image.Rect(r.Max.X-borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderRight)
226 screen.SubImage(image.Rect(r.Min.X, r.Max.Y-borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderBottom)
227 } else {
228 borderLeft, borderTop, borderRight, borderBottom = borderRight, borderBottom, borderLeft, borderTop
229 screen.SubImage(image.Rect(r.Max.X-borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderRight)
230 screen.SubImage(image.Rect(r.Min.X, r.Max.Y-borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderBottom)
231 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+borderSize, r.Max.Y)).(*ebiten.Image).Fill(borderLeft)
232 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+borderSize)).(*ebiten.Image).Fill(borderTop)
233 }
234 return nil
235 }
236
View as plain text