float.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 package widget
4
5 import (
6 "image"
7
8 "github.com/p9c/p9/pkg/gel/gio/gesture"
9 "github.com/p9c/p9/pkg/gel/gio/io/pointer"
10 "github.com/p9c/p9/pkg/gel/gio/layout"
11 "github.com/p9c/p9/pkg/gel/gio/op"
12 )
13
14 // Float is for selecting a value in a range.
15 type Float struct {
16 Value float32
17 Axis layout.Axis
18
19 drag gesture.Drag
20 pos float32 // position normalized to [0, 1]
21 length float32
22 changed bool
23 }
24
25 // Dragging returns whether the value is being interacted with.
26 func (f *Float) Dragging() bool { return f.drag.Dragging() }
27
28 // Layout updates the value according to drag events along the f's main axis.
29 //
30 // The range of f is set by the minimum constraints main axis value.
31 func (f *Float) Layout(gtx layout.Context, pointerMargin int, min, max float32) layout.Dimensions {
32 size := gtx.Constraints.Min
33 f.length = float32(f.Axis.Convert(size).X)
34
35 var de *pointer.Event
36 for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Axis(f.Axis)) {
37 if e.Type == pointer.Press || e.Type == pointer.Drag {
38 de = &e
39 }
40 }
41
42 value := f.Value
43 if de != nil {
44 xy := de.Position.X
45 if f.Axis == layout.Vertical {
46 xy = de.Position.Y
47 }
48 f.pos = xy / f.length
49 value = min + (max-min)*f.pos
50 } else if min != max {
51 f.pos = (value - min) / (max - min)
52 }
53 // Unconditionally call setValue in case min, max, or value changed.
54 f.setValue(value, min, max)
55
56 if f.pos < 0 {
57 f.pos = 0
58 } else if f.pos > 1 {
59 f.pos = 1
60 }
61
62 defer op.Save(gtx.Ops).Load()
63 margin := f.Axis.Convert(image.Pt(pointerMargin, 0))
64 rect := image.Rectangle{
65 Min: margin.Mul(-1),
66 Max: size.Add(margin),
67 }
68 pointer.Rect(rect).Add(gtx.Ops)
69 f.drag.Add(gtx.Ops)
70
71 return layout.Dimensions{Size: size}
72 }
73
74 func (f *Float) setValue(value, min, max float32) {
75 if min > max {
76 min, max = max, min
77 }
78 if value < min {
79 value = min
80 } else if value > max {
81 value = max
82 }
83 if f.Value != value {
84 f.Value = value
85 f.changed = true
86 }
87 }
88
89 // Pos reports the selected position.
90 func (f *Float) Pos() float32 {
91 return f.pos * f.length
92 }
93
94 // Changed reports whether the value has changed since
95 // the last call to Changed.
96 func (f *Float) Changed() bool {
97 changed := f.changed
98 f.changed = false
99 return changed
100 }
101