From a5ad9da28171c3a8ba735b0ed58be1d9d2eccc21 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 12:23:50 +0100 Subject: [PATCH 1/4] Continue scrolling after finger leaves the screen on mobile --- internal/driver/mobile/canvas.go | 11 ++++++++--- internal/driver/mobile/driver.go | 32 +++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/internal/driver/mobile/canvas.go b/internal/driver/mobile/canvas.go index 5c8c6a5db1..081153eea4 100644 --- a/internal/driver/mobile/canvas.go +++ b/internal/driver/mobile/canvas.go @@ -25,6 +25,7 @@ type canvas struct { initialized bool lastTapDown map[int]time.Time lastTapDownPos map[int]fyne.Position + lastTapDelta map[int]fyne.Delta menu fyne.CanvasObject padded bool scale float32 @@ -54,6 +55,7 @@ func newCanvas(dev fyne.Device) fyne.Canvas { device: d, lastTapDown: make(map[int]time.Time), lastTapDownPos: make(map[int]fyne.Position), + lastTapDelta: make(map[int]fyne.Delta), padded: true, scale: dev.SystemScaleForWindow(nil), // we don't need a window parameter on mobile, touched: make(map[int]mobile.Touchable), @@ -258,6 +260,8 @@ func (c *canvas) tapMove(pos fyne.Position, tapID int, return } c.lastTapDownPos[tapID] = pos + offset := fyne.Delta{DX: deltaX, DY: deltaY} + c.lastTapDelta[tapID] = offset co, objPos, _ := c.findObjectAtPositionMatching(pos, func(object fyne.CanvasObject) bool { if _, ok := object.(fyne.Draggable); ok { @@ -292,7 +296,7 @@ func (c *canvas) tapMove(pos fyne.Position, tapID int, ev := &fyne.DragEvent{} draggedObjDelta := c.dragStart.Subtract(c.dragging.(fyne.CanvasObject).Position()) ev.Position = pos.Subtract(c.dragOffset).Add(draggedObjDelta) - ev.Dragged = fyne.Delta{DX: deltaX, DY: deltaY} + ev.Dragged = offset dragCallback(c.dragging, ev) } @@ -301,10 +305,11 @@ func (c *canvas) tapUp(pos fyne.Position, tapID int, tapCallback func(fyne.Tappable, *fyne.PointEvent), tapAltCallback func(fyne.SecondaryTappable, *fyne.PointEvent), doubleTapCallback func(fyne.DoubleTappable, *fyne.PointEvent), - dragCallback func(fyne.Draggable)) { + dragCallback func(fyne.Draggable, *fyne.DragEvent)) { if c.dragging != nil { - dragCallback(c.dragging) + previousDelta := c.lastTapDelta[tapID] + dragCallback(c.dragging, &fyne.DragEvent{Dragged: previousDelta}) c.dragging = nil return diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index 5f7dd9b559..24cc5e1dbb 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -1,6 +1,7 @@ package mobile import ( + "math" "runtime" "strconv" "sync/atomic" @@ -30,9 +31,11 @@ import ( ) const ( - tapMoveThreshold = 4.0 // how far can we move before it is a drag - tapSecondaryDelay = 300 * time.Millisecond // how long before secondary tap - tapDoubleDelay = 500 * time.Millisecond // max duration between taps for a DoubleTap event + tapMoveDecay = 0.85 // how much should the scroll continue decay on each frame? + tapMoveEndThreshold = 2.0 // at what offset will we stop decaying? + tapMoveThreshold = 4.0 // how far can we move before it is a drag + tapSecondaryDelay = 300 * time.Millisecond // how long before secondary tap + tapDoubleDelay = 500 * time.Millisecond // max duration between taps for a DoubleTap event ) // Configuration is the system information about the current device @@ -399,8 +402,27 @@ func (d *driver) tapUpCanvas(w *window, x, y float32, tapID touch.Sequence) { w.QueueEvent(func() { wid.TappedSecondary(ev) }) }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { w.QueueEvent(func() { wid.DoubleTapped(ev) }) - }, func(wid fyne.Draggable) { - w.QueueEvent(wid.DragEnd) + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { + if math.Abs(float64(ev.Dragged.DX)) <= tapMoveEndThreshold && math.Abs(float64(ev.Dragged.DY)) <= tapMoveEndThreshold { + w.QueueEvent(wid.DragEnd) + return + } + + go func() { + for math.Abs(float64(ev.Dragged.DX)) > tapMoveEndThreshold || math.Abs(float64(ev.Dragged.DY)) > tapMoveEndThreshold { + if math.Abs(float64(ev.Dragged.DX)) > 0 { + ev.Dragged.DX *= tapMoveDecay + } + if math.Abs(float64(ev.Dragged.DY)) > 0 { + ev.Dragged.DY *= tapMoveDecay + } + + w.QueueEvent(func() { wid.Dragged(ev) }) + time.Sleep(time.Millisecond * 12) + } + + w.QueueEvent(wid.DragEnd) + }() }) } From a44331a069e57443f1836dbd922d6fab51a62b09 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 12:24:25 +0100 Subject: [PATCH 2/4] Also reduce mobile offset - some fingers were missing the target --- internal/driver/mobile/device_android.go | 2 +- internal/driver/mobile/device_ios.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/driver/mobile/device_android.go b/internal/driver/mobile/device_android.go index 6952e22eea..44c0398cc9 100644 --- a/internal/driver/mobile/device_android.go +++ b/internal/driver/mobile/device_android.go @@ -4,7 +4,7 @@ package mobile import "fyne.io/fyne/v2" -const tapYOffset = -12.0 // to compensate for how we hold our fingers on the device +const tapYOffset = -8.0 // to compensate for how we hold our fingers on the device func (*device) SystemScaleForWindow(_ fyne.Window) float32 { if currentDPI >= 600 { diff --git a/internal/driver/mobile/device_ios.go b/internal/driver/mobile/device_ios.go index a69bcbf164..284b6c04ec 100644 --- a/internal/driver/mobile/device_ios.go +++ b/internal/driver/mobile/device_ios.go @@ -4,7 +4,7 @@ package mobile import "fyne.io/fyne/v2" -const tapYOffset = -12.0 // to compensate for how we hold our fingers on the device +const tapYOffset = -8.0 // to compensate for how we hold our fingers on the device func (*device) SystemScaleForWindow(_ fyne.Window) float32 { if currentDPI >= 450 { From 67a4526ef4834db167e070e419371fb3dd32e003 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 12:29:20 +0100 Subject: [PATCH 3/4] Oops, fix mobile test compilation --- internal/driver/mobile/canvas_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/driver/mobile/canvas_test.go b/internal/driver/mobile/canvas_test.go index 1e0d771dd4..f566dc2636 100644 --- a/internal/driver/mobile/canvas_test.go +++ b/internal/driver/mobile/canvas_test.go @@ -249,7 +249,7 @@ func Test_canvas_Tappable(t *testing.T) { c.tapUp(fyne.NewPos(15, 15), 0, func(wid fyne.Tappable, ev *fyne.PointEvent) { }, func(wid fyne.SecondaryTappable, ev *fyne.PointEvent) { }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.True(t, content.up) @@ -286,7 +286,7 @@ func Test_canvas_Tapped(t *testing.T) { wid.TappedSecondary(ev) }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.True(t, tapped, "tap primary") @@ -340,7 +340,7 @@ func Test_canvas_TappedMulti(t *testing.T) { }, func(wid fyne.SecondaryTappable, ev *fyne.PointEvent) { }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.False(t, buttonTap, "button should not be tapped") @@ -369,7 +369,7 @@ func Test_canvas_TappedSecondary(t *testing.T) { wid.TappedSecondary(ev) }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) assert.False(t, obj.tap, "don't tap primary") @@ -464,7 +464,7 @@ func simulateTap(c *canvas) { }, func(wid fyne.SecondaryTappable, ev *fyne.PointEvent) { }, func(wid fyne.DoubleTappable, ev *fyne.PointEvent) { wid.DoubleTapped(ev) - }, func(wid fyne.Draggable) { + }, func(wid fyne.Draggable, ev *fyne.DragEvent) { }) } From 2dcc5301b069bbf429729ba8f6d632a1f33b47f6 Mon Sep 17 00:00:00 2001 From: Andy Williams Date: Sat, 12 Oct 2024 16:42:51 +0100 Subject: [PATCH 4/4] Adjust decay curve from user feedback --- internal/driver/mobile/driver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index 24cc5e1dbb..3f1c18d2a1 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -31,7 +31,7 @@ import ( ) const ( - tapMoveDecay = 0.85 // how much should the scroll continue decay on each frame? + tapMoveDecay = 0.92 // how much should the scroll continue decay on each frame? tapMoveEndThreshold = 2.0 // at what offset will we stop decaying? tapMoveThreshold = 4.0 // how far can we move before it is a drag tapSecondaryDelay = 300 * time.Millisecond // how long before secondary tap @@ -418,7 +418,7 @@ func (d *driver) tapUpCanvas(w *window, x, y float32, tapID touch.Sequence) { } w.QueueEvent(func() { wid.Dragged(ev) }) - time.Sleep(time.Millisecond * 12) + time.Sleep(time.Millisecond * 16) } w.QueueEvent(wid.DragEnd)