mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-04 08:55:35 +00:00
widget: [API] separate Float state update; remove min, max, invert parameters
This change allows users of Float to determine its state before Layout by calling Update. While here, remove the value transformation represented by the min, max, invert parameters; they're too many arguments for a computation that may as well be done by the user. Remove Float.Pos; it is better to compute its value from the dimensions returned by Float.Layout. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+29
-63
@@ -9,59 +9,29 @@ import (
|
|||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op/clip"
|
"gioui.org/op/clip"
|
||||||
|
"gioui.org/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Float is for selecting a value in a range.
|
// Float is for selecting a value in a range.
|
||||||
type Float struct {
|
type Float struct {
|
||||||
|
// Value is the value of the Float, in the [0; 1] range.
|
||||||
Value float32
|
Value float32
|
||||||
|
|
||||||
drag gesture.Drag
|
drag gesture.Drag
|
||||||
pos float32 // position normalized to [0, 1]
|
axis layout.Axis
|
||||||
length float32
|
length float32
|
||||||
changed bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dragging returns whether the value is being interacted with.
|
// Dragging returns whether the value is being interacted with.
|
||||||
func (f *Float) Dragging() bool { return f.drag.Dragging() }
|
func (f *Float) Dragging() bool { return f.drag.Dragging() }
|
||||||
|
|
||||||
// Layout updates the value according to drag events along the f's main axis.
|
func (f *Float) Layout(gtx layout.Context, axis layout.Axis, pointerMargin unit.Dp) layout.Dimensions {
|
||||||
//
|
f.Update(gtx)
|
||||||
// The range of f is set by the minimum constraints main axis value.
|
|
||||||
func (f *Float) Layout(gtx layout.Context, axis layout.Axis, min, max float32, invert bool, pointerMargin int) layout.Dimensions {
|
|
||||||
size := gtx.Constraints.Min
|
size := gtx.Constraints.Min
|
||||||
f.length = float32(axis.Convert(size).X)
|
f.length = float32(axis.Convert(size).X)
|
||||||
|
f.axis = axis
|
||||||
|
|
||||||
var de *pointer.Event
|
margin := axis.Convert(image.Pt(gtx.Dp(pointerMargin), 0))
|
||||||
for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Axis(axis)) {
|
|
||||||
if e.Kind == pointer.Press || e.Kind == pointer.Drag {
|
|
||||||
de = &e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value := f.Value
|
|
||||||
if de != nil {
|
|
||||||
xy := de.Position.X
|
|
||||||
if axis == layout.Vertical {
|
|
||||||
xy = f.length - de.Position.Y
|
|
||||||
}
|
|
||||||
if invert {
|
|
||||||
xy = f.length - xy
|
|
||||||
}
|
|
||||||
f.pos = xy / f.length
|
|
||||||
value = min + (max-min)*f.pos
|
|
||||||
} else if min != max {
|
|
||||||
f.pos = (value - min) / (max - min)
|
|
||||||
}
|
|
||||||
// Unconditionally call setValue in case min, max, or value changed.
|
|
||||||
f.setValue(value, min, max)
|
|
||||||
|
|
||||||
if f.pos < 0 {
|
|
||||||
f.pos = 0
|
|
||||||
} else if f.pos > 1 {
|
|
||||||
f.pos = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
margin := axis.Convert(image.Pt(pointerMargin, 0))
|
|
||||||
rect := image.Rectangle{
|
rect := image.Rectangle{
|
||||||
Min: margin.Mul(-1),
|
Min: margin.Mul(-1),
|
||||||
Max: size.Add(margin),
|
Max: size.Add(margin),
|
||||||
@@ -72,30 +42,26 @@ func (f *Float) Layout(gtx layout.Context, axis layout.Axis, min, max float32, i
|
|||||||
return layout.Dimensions{Size: size}
|
return layout.Dimensions{Size: size}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Float) setValue(value, min, max float32) {
|
// Update the Value according to drag events along the f's main axis.
|
||||||
if min > max {
|
// The return value reports whether the value was changed.
|
||||||
min, max = max, min
|
//
|
||||||
|
// The range of f is set by the minimum constraints main axis value.
|
||||||
|
func (f *Float) Update(gtx layout.Context) bool {
|
||||||
|
changed := false
|
||||||
|
for _, e := range f.drag.Events(gtx.Metric, gtx, gesture.Axis(f.axis)) {
|
||||||
|
if f.length > 0 && (e.Kind == pointer.Press || e.Kind == pointer.Drag) {
|
||||||
|
pos := e.Position.X
|
||||||
|
if f.axis == layout.Vertical {
|
||||||
|
pos = f.length - e.Position.Y
|
||||||
|
}
|
||||||
|
f.Value = pos / f.length
|
||||||
|
if f.Value < 0 {
|
||||||
|
f.Value = 0
|
||||||
|
} else if f.Value > 1 {
|
||||||
|
f.Value = 1
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if value < min {
|
|
||||||
value = min
|
|
||||||
} else if value > max {
|
|
||||||
value = max
|
|
||||||
}
|
|
||||||
if f.Value != value {
|
|
||||||
f.Value = value
|
|
||||||
f.changed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pos reports the selected position.
|
|
||||||
func (f *Float) Pos() float32 {
|
|
||||||
return f.pos * f.length
|
|
||||||
}
|
|
||||||
|
|
||||||
// Changed reports whether the value has changed since
|
|
||||||
// the last call to Changed.
|
|
||||||
func (f *Float) Changed() bool {
|
|
||||||
changed := f.changed
|
|
||||||
f.changed = false
|
|
||||||
return changed
|
return changed
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-20
@@ -16,10 +16,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Slider is for selecting a value in a range.
|
// Slider is for selecting a value in a range.
|
||||||
func Slider(th *Theme, float *widget.Float, min, max float32) SliderStyle {
|
func Slider(th *Theme, float *widget.Float) SliderStyle {
|
||||||
return SliderStyle{
|
return SliderStyle{
|
||||||
Min: min,
|
|
||||||
Max: max,
|
|
||||||
Color: th.Palette.ContrastBg,
|
Color: th.Palette.ContrastBg,
|
||||||
Float: float,
|
Float: float,
|
||||||
FingerSize: th.FingerSize,
|
FingerSize: th.FingerSize,
|
||||||
@@ -27,35 +25,34 @@ func Slider(th *Theme, float *widget.Float, min, max float32) SliderStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SliderStyle struct {
|
type SliderStyle struct {
|
||||||
Axis layout.Axis
|
Axis layout.Axis
|
||||||
Min, Max float32
|
Color color.NRGBA
|
||||||
Invert bool
|
Float *widget.Float
|
||||||
Color color.NRGBA
|
|
||||||
Float *widget.Float
|
|
||||||
|
|
||||||
FingerSize unit.Dp
|
FingerSize unit.Dp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
|
func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||||
thumbRadius := gtx.Dp(6)
|
const thumbRadius unit.Dp = 6
|
||||||
|
tr := gtx.Dp(thumbRadius)
|
||||||
trackWidth := gtx.Dp(2)
|
trackWidth := gtx.Dp(2)
|
||||||
|
|
||||||
axis := s.Axis
|
axis := s.Axis
|
||||||
// Keep a minimum length so that the track is always visible.
|
// Keep a minimum length so that the track is always visible.
|
||||||
minLength := thumbRadius + 3*thumbRadius + thumbRadius
|
minLength := tr + 3*tr + tr
|
||||||
// Try to expand to finger size, but only if the constraints
|
// Try to expand to finger size, but only if the constraints
|
||||||
// allow for it.
|
// allow for it.
|
||||||
touchSizePx := min(gtx.Dp(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y)
|
touchSizePx := min(gtx.Dp(s.FingerSize), axis.Convert(gtx.Constraints.Max).Y)
|
||||||
sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength)
|
sizeMain := max(axis.Convert(gtx.Constraints.Min).X, minLength)
|
||||||
sizeCross := max(2*thumbRadius, touchSizePx)
|
sizeCross := max(2*tr, touchSizePx)
|
||||||
size := axis.Convert(image.Pt(sizeMain, sizeCross))
|
size := axis.Convert(image.Pt(sizeMain, sizeCross))
|
||||||
|
|
||||||
o := axis.Convert(image.Pt(thumbRadius, 0))
|
o := axis.Convert(image.Pt(tr, 0))
|
||||||
trans := op.Offset(o).Push(gtx.Ops)
|
trans := op.Offset(o).Push(gtx.Ops)
|
||||||
gtx.Constraints.Min = axis.Convert(image.Pt(sizeMain-2*thumbRadius, sizeCross))
|
gtx.Constraints.Min = axis.Convert(image.Pt(sizeMain-2*tr, sizeCross))
|
||||||
s.Float.Layout(gtx, axis, s.Min, s.Max, s.Invert, thumbRadius)
|
dims := s.Float.Layout(gtx, axis, thumbRadius)
|
||||||
gtx.Constraints.Min = gtx.Constraints.Min.Add(axis.Convert(image.Pt(0, sizeCross)))
|
gtx.Constraints.Min = gtx.Constraints.Min.Add(axis.Convert(image.Pt(0, sizeCross)))
|
||||||
thumbPos := thumbRadius + int(s.Float.Pos())
|
thumbPos := tr + int(s.Float.Value*float32(axis.Convert(dims.Size).X))
|
||||||
trans.Pop()
|
trans.Pop()
|
||||||
|
|
||||||
color := s.Color
|
color := s.Color
|
||||||
@@ -65,7 +62,7 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
|
|||||||
|
|
||||||
rect := func(minx, miny, maxx, maxy int) image.Rectangle {
|
rect := func(minx, miny, maxx, maxy int) image.Rectangle {
|
||||||
r := image.Rect(minx, miny, maxx, maxy)
|
r := image.Rect(minx, miny, maxx, maxy)
|
||||||
if s.Invert != (axis == layout.Vertical) {
|
if axis == layout.Vertical {
|
||||||
r.Max.X, r.Min.X = sizeMain-r.Min.X, sizeMain-r.Max.X
|
r.Max.X, r.Min.X = sizeMain-r.Min.X, sizeMain-r.Max.X
|
||||||
}
|
}
|
||||||
r.Min = axis.Convert(r.Min)
|
r.Min = axis.Convert(r.Min)
|
||||||
@@ -75,7 +72,7 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
|
|||||||
|
|
||||||
// Draw track before thumb.
|
// Draw track before thumb.
|
||||||
track := rect(
|
track := rect(
|
||||||
thumbRadius, sizeCross/2-trackWidth/2,
|
tr, sizeCross/2-trackWidth/2,
|
||||||
thumbPos, sizeCross/2+trackWidth/2,
|
thumbPos, sizeCross/2+trackWidth/2,
|
||||||
)
|
)
|
||||||
paint.FillShape(gtx.Ops, color, clip.Rect(track).Op())
|
paint.FillShape(gtx.Ops, color, clip.Rect(track).Op())
|
||||||
@@ -83,15 +80,15 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
|
|||||||
// Draw track after thumb.
|
// Draw track after thumb.
|
||||||
track = rect(
|
track = rect(
|
||||||
thumbPos, axis.Convert(track.Min).Y,
|
thumbPos, axis.Convert(track.Min).Y,
|
||||||
sizeMain-thumbRadius, axis.Convert(track.Max).Y,
|
sizeMain-tr, axis.Convert(track.Max).Y,
|
||||||
)
|
)
|
||||||
paint.FillShape(gtx.Ops, f32color.MulAlpha(color, 96), clip.Rect(track).Op())
|
paint.FillShape(gtx.Ops, f32color.MulAlpha(color, 96), clip.Rect(track).Op())
|
||||||
|
|
||||||
// Draw thumb.
|
// Draw thumb.
|
||||||
pt := image.Pt(thumbPos, sizeCross/2)
|
pt := image.Pt(thumbPos, sizeCross/2)
|
||||||
thumb := rect(
|
thumb := rect(
|
||||||
pt.X-thumbRadius, pt.Y-thumbRadius,
|
pt.X-tr, pt.Y-tr,
|
||||||
pt.X+thumbRadius, pt.Y+thumbRadius,
|
pt.X+tr, pt.Y+tr,
|
||||||
)
|
)
|
||||||
paint.FillShape(gtx.Ops, color, clip.Ellipse(thumb).Op(gtx.Ops))
|
paint.FillShape(gtx.Ops, color, clip.Ellipse(thumb).Op(gtx.Ops))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user