~pierrec/giox

2b1e5bc8592bb66180c04268d2b80ef1869f19d4 — pierre 8 months ago c959a33
widgetx: use a ratio in Draggable

Signed-off-by: pierre <pierre.curto@gmail.com>
1 files changed, 65 insertions(+), 32 deletions(-)

M widgetx/drag.go
M widgetx/drag.go => widgetx/drag.go +65 -32
@@ 7,57 7,90 @@ import (
	"gioui.org/gesture"
	"gioui.org/io/pointer"
	"gioui.org/layout"
	"gioui.org/op"
)

// Draggable makes an area draggable and reports its position in Pos.
type Draggable struct {
	Axis    gesture.Axis
	Pos     image.Point
	drag    gesture.Drag
	changed bool
	Axis gesture.Axis
	// Ratio is calculated after each layout from gtx.Constraints.Min.
	// Ratio is always in [0, 1].
	Ratio    f32.Point
	dragging bool
	id       pointer.ID
}

func (d *Draggable) Layout(gtx layout.Context) layout.Dimensions {
	defer op.Save(gtx.Ops).Load()
	size := gtx.Constraints.Min
	pointer.Rect(image.Rectangle{Max: size}).Add(gtx.Ops)
	pointer.CursorNameOp{Name: pointer.CursorGrab}.Add(gtx.Ops)
	d.drag.Add(gtx.Ops)

	var start, pos f32.Point
	for _, ev := range d.drag.Events(gtx.Metric, gtx.Queue, d.Axis) {
		if ev.Source != pointer.Mouse {
	var pos f32.Point
	for _, ev := range gtx.Events(d) {
		e, ok := ev.(pointer.Event)
		if !ok || e.Source != pointer.Mouse {
			continue
		}
		switch ev.Type {
		case pointer.Cancel:
			start = f32.Point{}
			pos = f32.Point{}
		switch e.Type {
		case pointer.Cancel, pointer.Release:
			if d.id == e.PointerID {
				d.dragging = false
			}
		case pointer.Press:
			start = ev.Position
			pos = ev.Position
		case pointer.Drag, pointer.Release:
			if start == (f32.Point{}) {
				start = ev.Position
			if d.dragging {
				break
			}
			pos = e.Position
			d.dragging = true
			d.id = e.PointerID
		case pointer.Drag:
			if d.id != e.PointerID {
				break
			}
			if pos == (f32.Point{}) {
				pos = e.Position
				break
			}
			delta := e.Position.Sub(pos)
			pos = e.Position
			if d.Axis == gesture.Both || d.Axis == gesture.Horizontal {
				d.Ratio.X = d.ratio(layout.Horizontal, delta, size)
			}
			if d.Axis == gesture.Both || d.Axis == gesture.Vertical {
				d.Ratio.Y = d.ratio(layout.Vertical, delta, size)
			}
			pos = ev.Position
		}
	}
	if start != (f32.Point{}) {
		d.changed = true
		delta := pos.Sub(start).Mul(gtx.Metric.PxPerDp)
		d.Pos.X += int(delta.X)
		d.Pos.Y += int(delta.Y)
	}

	pointer.Rect(image.Rectangle{Max: size}).Add(gtx.Ops)
	pointer.CursorNameOp{Name: pointer.CursorGrab}.Add(gtx.Ops)
	pointer.InputOp{
		Tag:   d,
		Grab:  d.dragging,
		Types: pointer.Cancel | pointer.Press | pointer.Drag | pointer.Release,
	}.Add(gtx.Ops)

	return layout.Dimensions{Size: size}
}

func (d *Draggable) Dragging() bool {
	return d.drag.Dragging()
func (d *Draggable) ratio(axis layout.Axis, delta f32.Point, size image.Point) float32 {
	main := axis.Convert(size).X
	if main == 0 {
		return 0
	}
	r := axisConvert(axis, delta) / float32(main)
	ratio := axisConvert(axis, d.Ratio) + r
	if ratio < 0 {
		return 0
	}
	if ratio > 1 {
		return 1
	}
	return ratio
}

func (d *Draggable) Changed() bool {
	changed := d.changed
	d.changed = false
	return changed
func axisConvert(axis layout.Axis, p f32.Point) float32 {
	if axis == layout.Horizontal {
		return p.X
	}
	return p.Y
}