Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decay mobile scroll as users expect on a touch display #5191

Merged
merged 4 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions internal/driver/mobile/canvas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions internal/driver/mobile/canvas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand 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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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) {
})
}

Expand Down
2 changes: 1 addition & 1 deletion internal/driver/mobile/device_android.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/mobile/device_ios.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
32 changes: 27 additions & 5 deletions internal/driver/mobile/driver.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mobile

import (
"math"
"runtime"
"strconv"
"sync/atomic"
Expand Down Expand Up @@ -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.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
tapDoubleDelay = 500 * time.Millisecond // max duration between taps for a DoubleTap event
)

// Configuration is the system information about the current device
Expand Down Expand Up @@ -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 * 16)
}

w.QueueEvent(wid.DragEnd)
}()
})
}

Expand Down