1 package cview
2
3 import (
4 "math"
5 "sync"
6
7 "github.com/gdamore/tcell/v2"
8 )
9
10
11 type Slider struct {
12 *ProgressBar
13
14
15 label []byte
16
17
18
19 labelWidth int
20
21
22 labelColor tcell.Color
23
24
25 labelColorFocused tcell.Color
26
27
28 fieldBackgroundColor tcell.Color
29
30
31 fieldBackgroundColorFocused tcell.Color
32
33
34 fieldTextColor tcell.Color
35
36
37 fieldTextColorFocused tcell.Color
38
39
40 increment int
41
42
43 dragging bool
44
45
46
47 changed func(value int)
48
49
50
51
52 done func(tcell.Key)
53
54
55
56 finished func(tcell.Key)
57
58 sync.RWMutex
59 }
60
61
62 func NewSlider() *Slider {
63 s := &Slider{
64 ProgressBar: NewProgressBar(),
65 increment: 10,
66 labelColor: Styles.SecondaryTextColor,
67 fieldBackgroundColor: Styles.MoreContrastBackgroundColor,
68 fieldBackgroundColorFocused: Styles.ContrastBackgroundColor,
69 fieldTextColor: Styles.PrimaryTextColor,
70 labelColorFocused: ColorUnset,
71 fieldTextColorFocused: ColorUnset,
72 }
73 return s
74 }
75
76
77 func (s *Slider) SetLabel(label string) {
78 s.Lock()
79 defer s.Unlock()
80
81 s.label = []byte(label)
82 }
83
84
85 func (s *Slider) GetLabel() string {
86 s.RLock()
87 defer s.RUnlock()
88
89 return string(s.label)
90 }
91
92
93
94 func (s *Slider) SetLabelWidth(width int) {
95 s.Lock()
96 defer s.Unlock()
97
98 s.labelWidth = width
99 }
100
101
102 func (s *Slider) SetLabelColor(color tcell.Color) {
103 s.Lock()
104 defer s.Unlock()
105
106 s.labelColor = color
107 }
108
109
110 func (s *Slider) SetLabelColorFocused(color tcell.Color) {
111 s.Lock()
112 defer s.Unlock()
113
114 s.labelColorFocused = color
115 }
116
117
118 func (s *Slider) SetFieldBackgroundColor(color tcell.Color) {
119 s.Lock()
120 defer s.Unlock()
121
122 s.fieldBackgroundColor = color
123 }
124
125
126 func (s *Slider) SetFieldBackgroundColorFocused(color tcell.Color) {
127 s.Lock()
128 defer s.Unlock()
129
130 s.fieldBackgroundColorFocused = color
131 }
132
133
134 func (s *Slider) SetFieldTextColor(color tcell.Color) {
135 s.Lock()
136 defer s.Unlock()
137
138 s.fieldTextColor = color
139 }
140
141
142 func (s *Slider) SetFieldTextColorFocused(color tcell.Color) {
143 s.Lock()
144 defer s.Unlock()
145
146 s.fieldTextColorFocused = color
147 }
148
149
150 func (s *Slider) GetFieldHeight() int {
151 return 1
152 }
153
154
155 func (s *Slider) GetFieldWidth() int {
156 return 0
157 }
158
159
160
161 func (s *Slider) SetIncrement(increment int) {
162 s.Lock()
163 defer s.Unlock()
164
165 s.increment = increment
166 }
167
168
169
170 func (s *Slider) SetChangedFunc(handler func(value int)) {
171 s.Lock()
172 defer s.Unlock()
173
174 s.changed = handler
175 }
176
177
178
179
180
181
182
183
184 func (s *Slider) SetDoneFunc(handler func(key tcell.Key)) {
185 s.Lock()
186 defer s.Unlock()
187
188 s.done = handler
189 }
190
191
192 func (s *Slider) SetFinishedFunc(handler func(key tcell.Key)) {
193 s.Lock()
194 defer s.Unlock()
195
196 s.finished = handler
197 }
198
199
200 func (s *Slider) Draw(screen tcell.Screen) {
201 if !s.GetVisible() {
202 return
203 }
204
205 s.Box.Draw(screen)
206 hasFocus := s.GetFocusable().HasFocus()
207
208 s.Lock()
209
210
211 labelColor := s.labelColor
212 fieldBackgroundColor := s.fieldBackgroundColor
213 fieldTextColor := s.fieldTextColor
214 if hasFocus {
215 if s.labelColorFocused != ColorUnset {
216 labelColor = s.labelColorFocused
217 }
218 if s.fieldBackgroundColorFocused != ColorUnset {
219 fieldBackgroundColor = s.fieldBackgroundColorFocused
220 }
221 if s.fieldTextColorFocused != ColorUnset {
222 fieldTextColor = s.fieldTextColorFocused
223 }
224 }
225
226
227 x, y, width, height := s.GetInnerRect()
228 rightLimit := x + width
229 if height < 1 || rightLimit <= x {
230 s.Unlock()
231 return
232 }
233
234
235 if len(s.label) > 0 {
236 if s.vertical {
237 height--
238
239
240 } else {
241 if s.labelWidth > 0 {
242 labelWidth := s.labelWidth
243 if labelWidth > rightLimit-x {
244 labelWidth = rightLimit - x
245 }
246 Print(screen, []byte(s.label), x, y, labelWidth, AlignLeft, labelColor)
247 x += labelWidth + 1
248 width -= labelWidth + 1
249 } else {
250 _, drawnWidth := Print(screen, []byte(s.label), x, y, rightLimit-x, AlignLeft, labelColor)
251 x += drawnWidth + 1
252 width -= drawnWidth + 1
253 }
254 }
255 }
256
257
258 s.Unlock()
259 s.ProgressBar.SetRect(x, y, width, height)
260 s.ProgressBar.SetEmptyColor(fieldBackgroundColor)
261 s.ProgressBar.SetFilledColor(fieldTextColor)
262 s.ProgressBar.Draw(screen)
263 }
264
265
266 func (s *Slider) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
267 return s.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
268 if HitShortcut(event, Keys.Cancel, Keys.MovePreviousField, Keys.MoveNextField) {
269 if s.done != nil {
270 s.done(event.Key())
271 }
272 if s.finished != nil {
273 s.finished(event.Key())
274 }
275 return
276 }
277
278 previous := s.progress
279
280 if HitShortcut(event, Keys.MoveFirst, Keys.MoveFirst2) {
281 s.SetProgress(0)
282 } else if HitShortcut(event, Keys.MoveLast, Keys.MoveLast2) {
283 s.SetProgress(s.max)
284 } else if HitShortcut(event, Keys.MoveUp, Keys.MoveUp2, Keys.MoveRight, Keys.MoveRight2, Keys.MovePreviousField) {
285 s.AddProgress(s.increment)
286 } else if HitShortcut(event, Keys.MoveDown, Keys.MoveDown2, Keys.MoveLeft, Keys.MoveLeft2, Keys.MoveNextField) {
287 s.AddProgress(s.increment * -1)
288 }
289
290 if s.progress != previous && s.changed != nil {
291 s.changed(s.progress)
292 }
293 })
294 }
295
296
297 func (s *Slider) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
298 return s.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
299 x, y := event.Position()
300 if !s.InRect(x, y) {
301 s.dragging = false
302 return false, nil
303 }
304
305
306 if action == MouseLeftClick {
307 setFocus(s)
308 consumed = true
309 }
310
311 handleMouse := func() {
312 if !s.ProgressBar.InRect(x, y) {
313 s.dragging = false
314 return
315 }
316
317 bx, by, bw, bh := s.GetInnerRect()
318 var clickPos, clickRange int
319 if s.ProgressBar.vertical {
320 clickPos = (bh - 1) - (y - by)
321 clickRange = bh - 1
322 } else {
323 clickPos = x - bx
324 clickRange = bw - 1
325 }
326 setValue := int(math.Floor(float64(s.max) * (float64(clickPos) / float64(clickRange))))
327 if setValue != s.progress {
328 s.SetProgress(setValue)
329 if s.changed != nil {
330 s.changed(s.progress)
331 }
332 }
333 }
334
335
336 switch action {
337 case MouseLeftDown:
338 setFocus(s)
339 consumed = true
340 capture = s
341 s.dragging = true
342
343 handleMouse()
344 case MouseMove:
345 if s.dragging {
346 consumed = true
347 capture = s
348
349 handleMouse()
350 }
351 case MouseLeftUp:
352 if s.dragging {
353 consumed = true
354 s.dragging = false
355
356 handleMouse()
357 }
358 }
359
360 return
361 })
362 }
363
View as plain text