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 func (s *Select) SetHighlightColor(c color.RGBA) {
63 s.list.SetHighlightColor(c)
64 }
65
66
67 func (s *Select) SetSelectedItem(index int) {
68 s.Lock()
69 defer s.Unlock()
70 if index < 0 || index >= len(s.items) {
71 return
72 }
73 s.list.SetSelectedItem(0, index)
74 s.updateLabel()
75 }
76
77
78 func (s *Select) Children() []Widget {
79 s.Lock()
80 defer s.Unlock()
81
82 return s.children
83 }
84
85
86 func (s *Select) AddChild(w ...Widget) {
87 s.Lock()
88 defer s.Unlock()
89
90 s.children = append(s.children, w...)
91 }
92
93
94 func (s *Select) Clear() {
95 s.Lock()
96 defer s.Unlock()
97
98 s.items = nil
99 s.list.Clear()
100 s.updateLabel()
101 }
102
103
104 func (s *Select) AddOption(label string) {
105 s.Lock()
106 defer s.Unlock()
107
108 s.items = append(s.items, label)
109 if len(s.items) == 1 {
110 s.list.selectedY = 0
111 s.updateLabel()
112 }
113
114 textColor := Style.ButtonTextColor
115 if textColor.A == 0 {
116 textColor = Style.TextColorDark
117 }
118
119 t := NewText(label)
120 t.SetVertical(AlignCenter)
121 t.SetForeground(textColor)
122 s.list.AddChildAt(t, 0, len(s.items)-1)
123 }
124
125 func (s *Select) updateLabel() {
126 var text string
127 if len(s.items) > 0 && s.list.selectedY >= 0 && s.list.selectedY < len(s.items) {
128 text = s.items[s.list.selectedY]
129 }
130 if s.open {
131 text = "▼ " + text
132 } else {
133 text = "▶ " + text
134 }
135 s.label.SetText(text)
136 }
137
138 func (s *Select) selectList(index int) (accept bool) {
139 s.Lock()
140 s.list.grid.visible = false
141 s.open = false
142 onSelect := s.onSelect
143 s.Unlock()
144
145 if onSelect != nil {
146 if !onSelect(index) {
147 return false
148 }
149 }
150
151 s.list.selectedY = index
152 s.updateLabel()
153 return true
154 }
155
156
157 func (s *Select) SetMenuVisible(visible bool) {
158 s.Lock()
159 defer s.Unlock()
160
161 s._setMenuVisible(visible)
162 }
163
164 func (s *Select) _setMenuVisible(visible bool) {
165 s.open = visible
166 s.list.SetVisible(visible)
167 s.updateLabel()
168
169 if !visible {
170 s.background = Style.ButtonBgColor
171 } else {
172 s.background = color.RGBA{uint8(float64(Style.ButtonBgColor.R) * 0.95), uint8(float64(Style.ButtonBgColor.G) * 0.95), uint8(float64(Style.ButtonBgColor.B) * 0.95), 255}
173 }
174 }
175
176
177
178 func (s *Select) Cursor() ebiten.CursorShapeType {
179 return ebiten.CursorShapePointer
180 }
181
182
183 func (s *Select) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
184 return false, nil
185 }
186
187
188 func (s *Select) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
189 s.Lock()
190 defer s.Unlock()
191
192 if clicked {
193 s._setMenuVisible(!s.open)
194 }
195 return true, nil
196 }
197
198
199 func (s *Select) Draw(screen *ebiten.Image) error {
200 s.Lock()
201 defer s.Unlock()
202
203 screen.SubImage(s.label.rect).(*ebiten.Image).Fill(s.background)
204
205
206 s.label.Draw(screen)
207
208
209 r := s.rect
210 borderSize := Scale(Style.ButtonBorderSize)
211 borderLeft, borderTop := Style.ButtonBorderLeft, Style.ButtonBorderTop
212 borderRight, borderBottom := Style.ButtonBorderRight, Style.ButtonBorderBottom
213 if !s.open {
214 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+borderSize, r.Max.Y)).(*ebiten.Image).Fill(borderLeft)
215 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+borderSize)).(*ebiten.Image).Fill(borderTop)
216 screen.SubImage(image.Rect(r.Max.X-borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderRight)
217 screen.SubImage(image.Rect(r.Min.X, r.Max.Y-borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderBottom)
218 } else {
219 borderLeft, borderTop, borderRight, borderBottom = borderRight, borderBottom, borderLeft, borderTop
220 screen.SubImage(image.Rect(r.Max.X-borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderRight)
221 screen.SubImage(image.Rect(r.Min.X, r.Max.Y-borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(borderBottom)
222 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+borderSize, r.Max.Y)).(*ebiten.Image).Fill(borderLeft)
223 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+borderSize)).(*ebiten.Image).Fill(borderTop)
224 }
225 return nil
226 }
227
View as plain text