mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
ui/app: (wayland) emulate fling behaviour for touchpads
Unlike macOS, Wayland leaves it up to the client to animate the implied fling gesture when scrolling on a touchpad. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+68
-14
@@ -20,6 +20,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"gioui.org/ui/f32"
|
"gioui.org/ui/f32"
|
||||||
|
"gioui.org/ui/internal/fling"
|
||||||
"gioui.org/ui/key"
|
"gioui.org/ui/key"
|
||||||
"gioui.org/ui/pointer"
|
"gioui.org/ui/pointer"
|
||||||
syscall "golang.org/x/sys/unix"
|
syscall "golang.org/x/sys/unix"
|
||||||
@@ -105,13 +106,19 @@ type window struct {
|
|||||||
notRead, notWrite int
|
notRead, notWrite int
|
||||||
ppdp, ppsp float32
|
ppdp, ppsp float32
|
||||||
scrollTime time.Duration
|
scrollTime time.Duration
|
||||||
discScroll struct {
|
discScroll image.Point
|
||||||
x, y int
|
|
||||||
}
|
|
||||||
scroll f32.Point
|
scroll f32.Point
|
||||||
lastPos f32.Point
|
lastPos f32.Point
|
||||||
lastTouch f32.Point
|
lastTouch f32.Point
|
||||||
|
|
||||||
|
// Flinging.
|
||||||
|
yExtrapolation fling.Extrapolation
|
||||||
|
xExtrapolation fling.Extrapolation
|
||||||
|
flinger fling.Animation
|
||||||
|
startFling bool
|
||||||
|
// Fling direction.
|
||||||
|
flingDir f32.Point
|
||||||
|
|
||||||
stage Stage
|
stage Stage
|
||||||
dead bool
|
dead bool
|
||||||
lastFrameCallback *C.struct_wl_callback
|
lastFrameCallback *C.struct_wl_callback
|
||||||
@@ -535,6 +542,7 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
|
|||||||
typ = pointer.Press
|
typ = pointer.Press
|
||||||
}
|
}
|
||||||
w.flushScroll()
|
w.flushScroll()
|
||||||
|
w.resetFling()
|
||||||
w.w.event(pointer.Event{
|
w.w.event(pointer.Event{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
Source: pointer.Mouse,
|
Source: pointer.Mouse,
|
||||||
@@ -547,6 +555,7 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
|
|||||||
func gio_onPointerAxis(data unsafe.Pointer, ptr *C.struct_wl_pointer, t, axis C.uint32_t, value C.wl_fixed_t) {
|
func gio_onPointerAxis(data unsafe.Pointer, ptr *C.struct_wl_pointer, t, axis C.uint32_t, value C.wl_fixed_t) {
|
||||||
w := winMap[ptr]
|
w := winMap[ptr]
|
||||||
v := fromFixed(value)
|
v := fromFixed(value)
|
||||||
|
w.resetFling()
|
||||||
if w.scroll == (f32.Point{}) {
|
if w.scroll == (f32.Point{}) {
|
||||||
w.scrollTime = time.Duration(t) * time.Millisecond
|
w.scrollTime = time.Duration(t) * time.Millisecond
|
||||||
}
|
}
|
||||||
@@ -562,6 +571,28 @@ func gio_onPointerAxis(data unsafe.Pointer, ptr *C.struct_wl_pointer, t, axis C.
|
|||||||
func gio_onPointerFrame(data unsafe.Pointer, pointer *C.struct_wl_pointer) {
|
func gio_onPointerFrame(data unsafe.Pointer, pointer *C.struct_wl_pointer) {
|
||||||
w := winMap[pointer]
|
w := winMap[pointer]
|
||||||
w.flushScroll()
|
w.flushScroll()
|
||||||
|
w.flushFling()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *window) flushFling() {
|
||||||
|
if !w.startFling {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.startFling = false
|
||||||
|
estx, esty := w.xExtrapolation.Estimate(), w.yExtrapolation.Estimate()
|
||||||
|
w.xExtrapolation = fling.Extrapolation{}
|
||||||
|
w.yExtrapolation = fling.Extrapolation{}
|
||||||
|
vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity)))
|
||||||
|
_, _, c := w.config()
|
||||||
|
c.now = time.Now()
|
||||||
|
if !w.flinger.Start(&c, vel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
invDist := 1 / vel
|
||||||
|
w.flingDir.X = estx.Velocity * invDist
|
||||||
|
w.flingDir.Y = esty.Velocity * invDist
|
||||||
|
// Wake up the window loop.
|
||||||
|
w.notify()
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onPointerAxisSource
|
//export gio_onPointerAxisSource
|
||||||
@@ -569,20 +600,28 @@ func gio_onPointerAxisSource(data unsafe.Pointer, pointer *C.struct_wl_pointer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onPointerAxisStop
|
//export gio_onPointerAxisStop
|
||||||
func gio_onPointerAxisStop(data unsafe.Pointer, pointer *C.struct_wl_pointer, time, axis C.uint32_t) {
|
func gio_onPointerAxisStop(data unsafe.Pointer, ptr *C.struct_wl_pointer, t, axis C.uint32_t) {
|
||||||
|
w := winMap[ptr]
|
||||||
|
w.startFling = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//export gio_onPointerAxisDiscrete
|
//export gio_onPointerAxisDiscrete
|
||||||
func gio_onPointerAxisDiscrete(data unsafe.Pointer, pointer *C.struct_wl_pointer, axis C.uint32_t, discrete C.int32_t) {
|
func gio_onPointerAxisDiscrete(data unsafe.Pointer, pointer *C.struct_wl_pointer, axis C.uint32_t, discrete C.int32_t) {
|
||||||
w := winMap[pointer]
|
w := winMap[pointer]
|
||||||
|
w.resetFling()
|
||||||
switch axis {
|
switch axis {
|
||||||
case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL:
|
case C.WL_POINTER_AXIS_HORIZONTAL_SCROLL:
|
||||||
w.discScroll.x += int(discrete)
|
w.discScroll.X += int(discrete)
|
||||||
case C.WL_POINTER_AXIS_VERTICAL_SCROLL:
|
case C.WL_POINTER_AXIS_VERTICAL_SCROLL:
|
||||||
w.discScroll.y += int(discrete)
|
w.discScroll.Y += int(discrete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *window) resetFling() {
|
||||||
|
w.startFling = false
|
||||||
|
w.flinger = fling.Animation{}
|
||||||
|
}
|
||||||
|
|
||||||
//export gio_onKeyboardKeymap
|
//export gio_onKeyboardKeymap
|
||||||
func gio_onKeyboardKeymap(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, format C.uint32_t, fd C.int32_t, size C.uint32_t) {
|
func gio_onKeyboardKeymap(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, format C.uint32_t, fd C.int32_t, size C.uint32_t) {
|
||||||
conn.repeat.Stop(0)
|
conn.repeat.Stop(0)
|
||||||
@@ -810,8 +849,9 @@ loop:
|
|||||||
func (w *window) setAnimating(anim bool) {
|
func (w *window) setAnimating(anim bool) {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
w.animating = anim
|
w.animating = anim
|
||||||
|
animating := w.isAnimating()
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
if anim {
|
if animating {
|
||||||
w.notify()
|
w.notify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -954,29 +994,38 @@ func (c *wlOutput) ppmm() (float32, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) flushScroll() {
|
func (w *window) flushScroll() {
|
||||||
if w.scroll == (f32.Point{}) {
|
var fling f32.Point
|
||||||
|
if w.flinger.Active() {
|
||||||
|
dist := float32(w.flinger.Tick(time.Now()))
|
||||||
|
fling = w.flingDir.Mul(dist)
|
||||||
|
}
|
||||||
|
total := w.scroll.Add(fling)
|
||||||
|
if total == (f32.Point{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// The Wayland reported scroll distance for
|
// The Wayland reported scroll distance for
|
||||||
// discrete scroll axis is only 10 pixels, where
|
// discrete scroll axis is only 10 pixels, where
|
||||||
// 100 seems more appropriate.
|
// 100 seems more appropriate.
|
||||||
const discreteScale = 10
|
const discreteScale = 10
|
||||||
if w.discScroll.x != 0 {
|
if w.discScroll.X != 0 {
|
||||||
w.scroll.X *= discreteScale
|
w.scroll.X *= discreteScale
|
||||||
}
|
}
|
||||||
if w.discScroll.y != 0 {
|
if w.discScroll.Y != 0 {
|
||||||
w.scroll.Y *= discreteScale
|
w.scroll.Y *= discreteScale
|
||||||
}
|
}
|
||||||
w.w.event(pointer.Event{
|
w.w.event(pointer.Event{
|
||||||
Type: pointer.Move,
|
Type: pointer.Move,
|
||||||
Source: pointer.Mouse,
|
Source: pointer.Mouse,
|
||||||
Position: w.lastPos,
|
Position: w.lastPos,
|
||||||
Scroll: w.scroll,
|
Scroll: total,
|
||||||
Time: w.scrollTime,
|
Time: w.scrollTime,
|
||||||
})
|
})
|
||||||
|
if w.discScroll == (image.Point{}) {
|
||||||
|
w.xExtrapolation.SampleDelta(w.scrollTime, -w.scroll.X)
|
||||||
|
w.yExtrapolation.SampleDelta(w.scrollTime, -w.scroll.Y)
|
||||||
|
}
|
||||||
w.scroll = f32.Point{}
|
w.scroll = f32.Point{}
|
||||||
w.discScroll.x = 0
|
w.discScroll = image.Point{}
|
||||||
w.discScroll.y = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
|
func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
|
||||||
@@ -1032,9 +1081,14 @@ func (w *window) config() (int, int, Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *window) isAnimating() bool {
|
||||||
|
return w.animating || w.flinger.Active()
|
||||||
|
}
|
||||||
|
|
||||||
func (w *window) draw(sync bool) {
|
func (w *window) draw(sync bool) {
|
||||||
|
w.flushScroll()
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
animating := w.animating
|
animating := w.isAnimating()
|
||||||
dead := w.dead
|
dead := w.dead
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
if dead || (!animating && !sync) {
|
if dead || (!animating && !sync) {
|
||||||
|
|||||||
Reference in New Issue
Block a user