feat(core): keep three-finger swipes still when animations are off
- commit
- f87efcec112cbb238bc745423e206129a22314e1
- author
- brice · · 2026-10-05 06:06 UTC
- parent
- 19a1e20
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
5 files · +52 -20
M README.md +1 -1
| @@ -18,7 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | 19 | I wanted a compositor I would never have to think about. It takes the newest Wayland protocols and kernel features as they land, gives games the whole GPU, and otherwise stays out of sight. | |
| 20 | 20 | ||
| 21 | - | It is small on purpose, and it will stay small. Drawing is kept to simple primitives: no blur, no shadows, no rounded corners, no themes. There is no built-in bar or wallpaper either; for those, bring your own: any layer-shell client works. Animations are experimental and off by default (`animations = on` turns them on). With them, windows and the view move with short springs (about 300 ms) when you scroll, switch workspaces, move columns or windows, resize them, drop a dragged tile or send a workspace to another monitor; windows fade in and out when they open, close or leave with the stash, the overview zooms its cards, and a brief pulse marks the window you just focused. They stay cheap: nothing runs while the screen is still, frames follow the display, only the region that moves is redrawn, apps are resized once, to their final size, and do not redraw during a transition, and nothing is copied or blurred. About 40–80 MB of RAM with two 4K monitors, and almost no CPU while the screen does not change. | |
| 21 | + | It is small on purpose, and it will stay small. Drawing is kept to simple primitives: no blur, no shadows, no rounded corners, no themes. There is no built-in bar or wallpaper either; for those, bring your own: any layer-shell client works. Animations are experimental and off by default (`animations = on` turns them on); off, every change is instant and touchpad swipes switch on release. With them, three-finger swipes follow the fingers, windows and the view move with short springs (about 300 ms) when you scroll, switch workspaces, move columns or windows, resize them, drop a dragged tile or send a workspace to another monitor; windows fade in and out when they open, close or leave with the stash, the overview zooms its cards, and a brief pulse marks the window you just focused. They stay cheap: nothing runs while the screen is still, frames follow the display, only the region that moves is redrawn, apps are resized once, to their final size, and do not redraw during a transition, and nothing is copied or blurred. About 40–80 MB of RAM with two 4K monitors, and almost no CPU while the screen does not change. | |
| 22 | 22 | ||
| 23 | 23 | ## OK, but why Go? | |
| 24 | 24 | ||
| @@ -18,7 +18,7 @@ | |||||
| 18 | 18 | ||||
| 19 | I wanted a compositor I would never have to think about. It takes the newest Wayland protocols and kernel features as they land, gives games the whole GPU, and otherwise stays out of sight. | 19 | I wanted a compositor I would never have to think about. It takes the newest Wayland protocols and kernel features as they land, gives games the whole GPU, and otherwise stays out of sight. | ||
| 20 | 20 | ||||
| 21 | - | It is small on purpose, and it will stay small. Drawing is kept to simple primitives: no blur, no shadows, no rounded corners, no themes. There is no built-in bar or wallpaper either; for those, bring your own: any layer-shell client works. Animations are experimental and off by default (`animations = on` turns them on). With them, windows and the view move with short springs (about 300 ms) when you scroll, switch workspaces, move columns or windows, resize them, drop a dragged tile or send a workspace to another monitor; windows fade in and out when they open, close or leave with the stash, the overview zooms its cards, and a brief pulse marks the window you just focused. They stay cheap: nothing runs while the screen is still, frames follow the display, only the region that moves is redrawn, apps are resized once, to their final size, and do not redraw during a transition, and nothing is copied or blurred. About 40–80 MB of RAM with two 4K monitors, and almost no CPU while the screen does not change. | 21 | + | It is small on purpose, and it will stay small. Drawing is kept to simple primitives: no blur, no shadows, no rounded corners, no themes. There is no built-in bar or wallpaper either; for those, bring your own: any layer-shell client works. Animations are experimental and off by default (`animations = on` turns them on); off, every change is instant and touchpad swipes switch on release. With them, three-finger swipes follow the fingers, windows and the view move with short springs (about 300 ms) when you scroll, switch workspaces, move columns or windows, resize them, drop a dragged tile or send a workspace to another monitor; windows fade in and out when they open, close or leave with the stash, the overview zooms its cards, and a brief pulse marks the window you just focused. They stay cheap: nothing runs while the screen is still, frames follow the display, only the region that moves is redrawn, apps are resized once, to their final size, and do not redraw during a transition, and nothing is copied or blurred. About 40–80 MB of RAM with two 4K monitors, and almost no CPU while the screen does not change. |
| 22 | 22 | ||||
| 23 | ## OK, but why Go? | 23 | ## OK, but why Go? | ||
| 24 | 24 | ||||
M docs/config.md +4 -2
| @@ -44,7 +44,7 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`). | |||
| 44 | 44 | | `mouse.accel-profile` | `adaptive` | `adaptive`: faster moves go further; `flat`: constant speed | | |
| 45 | 45 | | `mouse.left-handed` | `off` | Swaps the left and right mouse buttons | | |
| 46 | 46 | | `cursor.hide-after` | `5s` | Hides the pointer cursor after this long without motion; the next motion shows it. 100ms to 1h, or `off` | | |
| 47 | - | | `animations` | `off` | **Experimental.** `on` animates changes; `off` makes every change instant: swipe landings, scrolling, workspace switches, window moves and resizes, drag drops, workspaces sent to another monitor, the overview, windows opening and closing, the stash showing and hiding, the focus pulse | | |
| 47 | + | | `animations` | `off` | **Experimental.** `on` animates changes; `off` makes every change instant and the three-finger swipe no longer follows the fingers: swipe landings, scrolling, workspace switches, window moves and resizes, drag drops, workspaces sent to another monitor, the overview, windows opening and closing, the stash showing and hiding, the focus pulse | | |
| 48 | 48 | | `animations.slowdown` | `1` | Multiplies the duration of every spring (not the focus pulse), `0.1` to `10` | | |
| 49 | 49 | | `focus.follow-move` | `off` | Follow a column moved to another workspace | | |
| 50 | 50 | | `focus.animation` | `pulse` | Focus indicator on a window that keeps the focus for 150 ms: `pulse` (brief rise and fall) or `off`. Skipped for fullscreen windows, a window alone on screen (e.g. a maximized column), the overview and a window focused as it opens. Off while `animations = off` | | |
| @@ -136,7 +136,7 @@ bind.Cmd+p = workspace presentation | |||
| 136 | 136 | ||
| 137 | 137 | ## Touchpad | |
| 138 | 138 | ||
| 139 | - | A three-finger swipe follows the fingers and moves one step at most, so a quick swipe never skips a column or a workspace: | |
| 139 | + | With `animations = on`, a three-finger swipe follows the fingers and moves one step at most, so a quick swipe never skips a column or a workspace: | |
| 140 | 140 | ||
| 141 | 141 | - Left or right scrolls the columns (`scroll` overflow) to the next column edge; the focus moves to a column fully on screen. | |
| 142 | 142 | - Up or down slides to the next numbered workspace. | |
| @@ -145,6 +145,8 @@ The view sticks a little near each step and catches up between them. Past the ne | |||
| 145 | 145 | ||
| 146 | 146 | With `natural-scroll = off`, a swipe left shows the columns to the left and a swipe up shows the workspace above. `natural-scroll = on` moves the content with the fingers, so both are reversed. | |
| 147 | 147 | ||
| 148 | + | With `animations = off`, nothing moves during the swipe: when the fingers lift, a quick or long enough swipe runs `focus-column-left/right` or `focus-workspace-up/down`. | |
| 149 | + | ||
| 148 | 150 | Where the view cannot scroll (`fixed` overflow, the stash, a floating or fullscreen window, a named workspace), a quick swipe runs `focus-column-left/right` or `focus-workspace-up/down` when the fingers lift. | |
| 149 | 151 | ||
| 150 | 152 | Two-finger scroll goes to the window under the pointer with the touchpad's timestamps, so apps with kinetic scrolling keep their inertia. | |
| @@ -44,7 +44,7 @@ NeferWL reads `$XDG_CONFIG_HOME/neferwl/config` (or `~/.config/neferwl/config`). | |||||
| 44 | | `mouse.accel-profile` | `adaptive` | `adaptive`: faster moves go further; `flat`: constant speed | | 44 | | `mouse.accel-profile` | `adaptive` | `adaptive`: faster moves go further; `flat`: constant speed | | ||
| 45 | | `mouse.left-handed` | `off` | Swaps the left and right mouse buttons | | 45 | | `mouse.left-handed` | `off` | Swaps the left and right mouse buttons | | ||
| 46 | | `cursor.hide-after` | `5s` | Hides the pointer cursor after this long without motion; the next motion shows it. 100ms to 1h, or `off` | | 46 | | `cursor.hide-after` | `5s` | Hides the pointer cursor after this long without motion; the next motion shows it. 100ms to 1h, or `off` | | ||
| 47 | - | | `animations` | `off` | **Experimental.** `on` animates changes; `off` makes every change instant: swipe landings, scrolling, workspace switches, window moves and resizes, drag drops, workspaces sent to another monitor, the overview, windows opening and closing, the stash showing and hiding, the focus pulse | | 47 | + | | `animations` | `off` | **Experimental.** `on` animates changes; `off` makes every change instant and the three-finger swipe no longer follows the fingers: swipe landings, scrolling, workspace switches, window moves and resizes, drag drops, workspaces sent to another monitor, the overview, windows opening and closing, the stash showing and hiding, the focus pulse | |
| 48 | | `animations.slowdown` | `1` | Multiplies the duration of every spring (not the focus pulse), `0.1` to `10` | | 48 | | `animations.slowdown` | `1` | Multiplies the duration of every spring (not the focus pulse), `0.1` to `10` | | ||
| 49 | | `focus.follow-move` | `off` | Follow a column moved to another workspace | | 49 | | `focus.follow-move` | `off` | Follow a column moved to another workspace | | ||
| 50 | | `focus.animation` | `pulse` | Focus indicator on a window that keeps the focus for 150 ms: `pulse` (brief rise and fall) or `off`. Skipped for fullscreen windows, a window alone on screen (e.g. a maximized column), the overview and a window focused as it opens. Off while `animations = off` | | 50 | | `focus.animation` | `pulse` | Focus indicator on a window that keeps the focus for 150 ms: `pulse` (brief rise and fall) or `off`. Skipped for fullscreen windows, a window alone on screen (e.g. a maximized column), the overview and a window focused as it opens. Off while `animations = off` | | ||
| @@ -136,7 +136,7 @@ bind.Cmd+p = workspace presentation | |||||
| 136 | 136 | ||||
| 137 | ## Touchpad | 137 | ## Touchpad | ||
| 138 | 138 | ||||
| 139 | - | A three-finger swipe follows the fingers and moves one step at most, so a quick swipe never skips a column or a workspace: | 139 | + | With `animations = on`, a three-finger swipe follows the fingers and moves one step at most, so a quick swipe never skips a column or a workspace: |
| 140 | 140 | ||||
| 141 | - Left or right scrolls the columns (`scroll` overflow) to the next column edge; the focus moves to a column fully on screen. | 141 | - Left or right scrolls the columns (`scroll` overflow) to the next column edge; the focus moves to a column fully on screen. | ||
| 142 | - Up or down slides to the next numbered workspace. | 142 | - Up or down slides to the next numbered workspace. | ||
| @@ -145,6 +145,8 @@ The view sticks a little near each step and catches up between them. Past the ne | |||||
| 145 | 145 | ||||
| 146 | With `natural-scroll = off`, a swipe left shows the columns to the left and a swipe up shows the workspace above. `natural-scroll = on` moves the content with the fingers, so both are reversed. | 146 | With `natural-scroll = off`, a swipe left shows the columns to the left and a swipe up shows the workspace above. `natural-scroll = on` moves the content with the fingers, so both are reversed. | ||
| 147 | 147 | ||||
| 148 | + | With `animations = off`, nothing moves during the swipe: when the fingers lift, a quick or long enough swipe runs `focus-column-left/right` or `focus-workspace-up/down`. | |||
| 149 | + | ||||
| 148 | Where the view cannot scroll (`fixed` overflow, the stash, a floating or fullscreen window, a named workspace), a quick swipe runs `focus-column-left/right` or `focus-workspace-up/down` when the fingers lift. | 150 | Where the view cannot scroll (`fixed` overflow, the stash, a floating or fullscreen window, a named workspace), a quick swipe runs `focus-column-left/right` or `focus-workspace-up/down` when the fingers lift. | ||
| 149 | 151 | ||||
| 150 | Two-finger scroll goes to the window under the pointer with the touchpad's timestamps, so apps with kinetic scrolling keep their inertia. | 152 | Two-finger scroll goes to the window under the pointer with the touchpad's timestamps, so apps with kinetic scrolling keep their inertia. | ||
M examples/config +2 -1
| @@ -75,7 +75,8 @@ focus.follow-move = off | |||
| 75 | 75 | # Experimental, off by default. | |
| 76 | 76 | # on: windows and the view move with springs, windows fade in and out as | |
| 77 | 77 | # they open, close or leave with the stash, the overview zooms its cards; | |
| 78 | - | # off: every change is instant. | |
| 78 | + | # off: every change is instant and three-finger swipes act when the | |
| 79 | + | # fingers lift. | |
| 79 | 80 | # slowdown multiplies every spring's duration (0.1 to 10). | |
| 80 | 81 | animations = off | |
| 81 | 82 | animations.slowdown = 1 | |
| @@ -75,7 +75,8 @@ focus.follow-move = off | |||||
| 75 | # Experimental, off by default. | 75 | # Experimental, off by default. | ||
| 76 | # on: windows and the view move with springs, windows fade in and out as | 76 | # on: windows and the view move with springs, windows fade in and out as | ||
| 77 | # they open, close or leave with the stash, the overview zooms its cards; | 77 | # they open, close or leave with the stash, the overview zooms its cards; | ||
| 78 | - | # off: every change is instant. | 78 | + | # off: every change is instant and three-finger swipes act when the |
| 79 | + | # fingers lift. | |||
| 79 | # slowdown multiplies every spring's duration (0.1 to 10). | 80 | # slowdown multiplies every spring's duration (0.1 to 10). | ||
| 80 | animations = off | 81 | animations = off | ||
| 81 | animations.slowdown = 1 | 82 | animations.slowdown = 1 | ||
M internal/core/animations_off_test.go +42 -15
| @@ -9,12 +9,41 @@ import ( | |||
| 9 | 9 | ||
| 10 | 10 | func animationsOff(c *ports.Config) { c.Animations.On = false } | |
| 11 | 11 | ||
| 12 | - | func TestAnimationsOffFlickLandsAtOnce(t *testing.T) { | |
| 12 | + | // stillMoves sends n swipe updates of (dx, dy) and expects no scene: with | |
| 13 | + | // animations off the view does not follow the fingers. | |
| 14 | + | func (r *swipeRig) stillMoves(t *testing.T, n int, dx, dy float64) { | |
| 15 | + | t.Helper() | |
| 16 | + | for range n { | |
| 17 | + | r.at += 8 * time.Millisecond | |
| 18 | + | r.input <- ports.SwipeUpdate{DX: dx, DY: dy, Time: r.at} | |
| 19 | + | } | |
| 20 | + | select { | |
| 21 | + | case s := <-r.scenes: | |
| 22 | + | t.Fatalf("a swipe update published a scene (%d windows): the view follows the fingers", len(s)) | |
| 23 | + | case <-time.After(50 * time.Millisecond): | |
| 24 | + | } | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | func focusedID(s ports.Scene) ports.WindowID { | |
| 28 | + | for _, w := range s.Windows { | |
| 29 | + | if w.Focused { | |
| 30 | + | return w.ID | |
| 31 | + | } | |
| 32 | + | } | |
| 33 | + | return 0 | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func TestAnimationsOffColumnSwipeFocusesAtOnce(t *testing.T) { | |
| 13 | 37 | r := startSwipe(t, animationsOff) | |
| 14 | - | threeColumns(t, r) | |
| 15 | - | s := r.flick(t, 10, -40, 0) | |
| 16 | - | if got, ok := rectOf(s, 1); !ok || got.X != 0 { | |
| 17 | - | t.Fatalf("column 1 at %+v (shown %t) right after the lift, want the settled rect", got, ok) | |
| 38 | + | before := threeColumns(t, r) | |
| 39 | + | r.begin() | |
| 40 | + | r.stillMoves(t, 10, -40, 0) | |
| 41 | + | s := r.end(t, false) | |
| 42 | + | if got := focusedID(s); got != 2 { | |
| 43 | + | t.Fatalf("focused %d right after the lift, want 2", got) | |
| 44 | + | } | |
| 45 | + | if got, _ := rectOf(before, 3); got.X == 0 { | |
| 46 | + | t.Fatalf("bad setup: column 3 at %+v", got) | |
| 18 | 47 | } | |
| 19 | 48 | r.noFrameScene(t) | |
| 20 | 49 | } | |
| @@ -23,9 +52,7 @@ func TestAnimationsOffWorkspaceSwipeLandsAtOnce(t *testing.T) { | |||
| 23 | 52 | r := startSwipe(t, animationsOff) | |
| 24 | 53 | r.workspaces(t) | |
| 25 | 54 | r.begin() | |
| 26 | - | for range 9 { | |
| 27 | - | r.move(t, 0, 30) | |
| 28 | - | } | |
| 55 | + | r.stillMoves(t, 9, 0, 30) | |
| 29 | 56 | s := r.end(t, false) | |
| 30 | 57 | if _, ok := rectOf(s, 1); ok { | |
| 31 | 58 | t.Fatal("old workspace still shown right after the lift") | |
| @@ -35,16 +62,16 @@ func TestAnimationsOffWorkspaceSwipeLandsAtOnce(t *testing.T) { | |||
| 35 | 62 | } | |
| 36 | 63 | } | |
| 37 | 64 | ||
| 38 | - | func TestAnimationsOffFingersStillFollowed(t *testing.T) { | |
| 65 | + | func TestAnimationsOffSlowSwipeStays(t *testing.T) { | |
| 39 | 66 | r := startSwipe(t, animationsOff) | |
| 40 | - | before, _ := rectOf(threeColumns(t, r), 2) | |
| 67 | + | before := threeColumns(t, r) | |
| 41 | 68 | r.begin() | |
| 42 | - | r.input <- ports.SwipeUpdate{DX: -10, Time: r.at} | |
| 43 | - | r.move(t, -10, 0) | |
| 44 | - | s := r.move(t, -290, 0) | |
| 45 | - | if got, _ := rectOf(s, 2); got.X-before.X != 200 { | |
| 46 | - | t.Fatalf("column moved %d px with the fingers, want 200", got.X-before.X) | |
| 69 | + | r.stillMoves(t, 2, -10, 0) | |
| 70 | + | r.input <- ports.SwipeEnd{Time: r.at + time.Second} | |
| 71 | + | if s := scene(t, r.scenes); !s.SameAs(before) { | |
| 72 | + | t.Fatalf("a short slow swipe changed the scene: %+v, was %+v", s.Windows, before.Windows) | |
| 47 | 73 | } | |
| 74 | + | r.noFrameScene(t) | |
| 48 | 75 | } | |
| 49 | 76 | ||
| 50 | 77 | func TestReloadToOffSettlesALanding(t *testing.T) { | |
| @@ -9,12 +9,41 @@ import ( | |||||
| 9 | 9 | ||||
| 10 | func animationsOff(c *ports.Config) { c.Animations.On = false } | 10 | func animationsOff(c *ports.Config) { c.Animations.On = false } | ||
| 11 | 11 | ||||
| 12 | - | func TestAnimationsOffFlickLandsAtOnce(t *testing.T) { | 12 | + | // stillMoves sends n swipe updates of (dx, dy) and expects no scene: with |
| 13 | + | // animations off the view does not follow the fingers. | |||
| 14 | + | func (r *swipeRig) stillMoves(t *testing.T, n int, dx, dy float64) { | |||
| 15 | + | t.Helper() | |||
| 16 | + | for range n { | |||
| 17 | + | r.at += 8 * time.Millisecond | |||
| 18 | + | r.input <- ports.SwipeUpdate{DX: dx, DY: dy, Time: r.at} | |||
| 19 | + | } | |||
| 20 | + | select { | |||
| 21 | + | case s := <-r.scenes: | |||
| 22 | + | t.Fatalf("a swipe update published a scene (%d windows): the view follows the fingers", len(s)) | |||
| 23 | + | case <-time.After(50 * time.Millisecond): | |||
| 24 | + | } | |||
| 25 | + | } | |||
| 26 | + | ||||
| 27 | + | func focusedID(s ports.Scene) ports.WindowID { | |||
| 28 | + | for _, w := range s.Windows { | |||
| 29 | + | if w.Focused { | |||
| 30 | + | return w.ID | |||
| 31 | + | } | |||
| 32 | + | } | |||
| 33 | + | return 0 | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | func TestAnimationsOffColumnSwipeFocusesAtOnce(t *testing.T) { | |||
| 13 | r := startSwipe(t, animationsOff) | 37 | r := startSwipe(t, animationsOff) | ||
| 14 | - | threeColumns(t, r) | 38 | + | before := threeColumns(t, r) |
| 15 | - | s := r.flick(t, 10, -40, 0) | 39 | + | r.begin() |
| 16 | - | if got, ok := rectOf(s, 1); !ok || got.X != 0 { | 40 | + | r.stillMoves(t, 10, -40, 0) |
| 17 | - | t.Fatalf("column 1 at %+v (shown %t) right after the lift, want the settled rect", got, ok) | 41 | + | s := r.end(t, false) |
| 42 | + | if got := focusedID(s); got != 2 { | |||
| 43 | + | t.Fatalf("focused %d right after the lift, want 2", got) | |||
| 44 | + | } | |||
| 45 | + | if got, _ := rectOf(before, 3); got.X == 0 { | |||
| 46 | + | t.Fatalf("bad setup: column 3 at %+v", got) | |||
| 18 | } | 47 | } | ||
| 19 | r.noFrameScene(t) | 48 | r.noFrameScene(t) | ||
| 20 | } | 49 | } | ||
| @@ -23,9 +52,7 @@ func TestAnimationsOffWorkspaceSwipeLandsAtOnce(t *testing.T) { | |||||
| 23 | r := startSwipe(t, animationsOff) | 52 | r := startSwipe(t, animationsOff) | ||
| 24 | r.workspaces(t) | 53 | r.workspaces(t) | ||
| 25 | r.begin() | 54 | r.begin() | ||
| 26 | - | for range 9 { | 55 | + | r.stillMoves(t, 9, 0, 30) |
| 27 | - | r.move(t, 0, 30) | |||
| 28 | - | } | |||
| 29 | s := r.end(t, false) | 56 | s := r.end(t, false) | ||
| 30 | if _, ok := rectOf(s, 1); ok { | 57 | if _, ok := rectOf(s, 1); ok { | ||
| 31 | t.Fatal("old workspace still shown right after the lift") | 58 | t.Fatal("old workspace still shown right after the lift") | ||
| @@ -35,16 +62,16 @@ func TestAnimationsOffWorkspaceSwipeLandsAtOnce(t *testing.T) { | |||||
| 35 | } | 62 | } | ||
| 36 | } | 63 | } | ||
| 37 | 64 | ||||
| 38 | - | func TestAnimationsOffFingersStillFollowed(t *testing.T) { | 65 | + | func TestAnimationsOffSlowSwipeStays(t *testing.T) { |
| 39 | r := startSwipe(t, animationsOff) | 66 | r := startSwipe(t, animationsOff) | ||
| 40 | - | before, _ := rectOf(threeColumns(t, r), 2) | 67 | + | before := threeColumns(t, r) |
| 41 | r.begin() | 68 | r.begin() | ||
| 42 | - | r.input <- ports.SwipeUpdate{DX: -10, Time: r.at} | 69 | + | r.stillMoves(t, 2, -10, 0) |
| 43 | - | r.move(t, -10, 0) | 70 | + | r.input <- ports.SwipeEnd{Time: r.at + time.Second} |
| 44 | - | s := r.move(t, -290, 0) | 71 | + | if s := scene(t, r.scenes); !s.SameAs(before) { |
| 45 | - | if got, _ := rectOf(s, 2); got.X-before.X != 200 { | 72 | + | t.Fatalf("a short slow swipe changed the scene: %+v, was %+v", s.Windows, before.Windows) |
| 46 | - | t.Fatalf("column moved %d px with the fingers, want 200", got.X-before.X) | |||
| 47 | } | 73 | } | ||
| 74 | + | r.noFrameScene(t) | |||
| 48 | } | 75 | } | ||
| 49 | 76 | ||||
| 50 | func TestReloadToOffSettlesALanding(t *testing.T) { | 77 | func TestReloadToOffSettlesALanding(t *testing.T) { | ||
M internal/core/gesture.go +3 -1
| @@ -168,8 +168,10 @@ func (c *Core) decide(g *swipeGesture) { | |||
| 168 | 168 | case g.fingers == 4: | |
| 169 | 169 | // Four fingers sideways do nothing. | |
| 170 | 170 | g.mode = swipeDropped | |
| 171 | - | case m.ov.open: | |
| 171 | + | case m.ov.open || !c.animOn(): | |
| 172 | 172 | // The overview does not slide: the swipe moves its selection. | |
| 173 | + | // With animations off nothing slides either: the swipe runs a | |
| 174 | + | // focus action when the fingers lift. | |
| 173 | 175 | g.mode, g.snap = swipeDiscrete, newStepSwipe() | |
| 174 | 176 | case g.horizontal && w.slidable(): | |
| 175 | 177 | g.mode, g.ws, g.points = swipeColumns, w, w.snapPoints() | |
| @@ -168,8 +168,10 @@ func (c *Core) decide(g *swipeGesture) { | |||||
| 168 | case g.fingers == 4: | 168 | case g.fingers == 4: | ||
| 169 | // Four fingers sideways do nothing. | 169 | // Four fingers sideways do nothing. | ||
| 170 | g.mode = swipeDropped | 170 | g.mode = swipeDropped | ||
| 171 | - | case m.ov.open: | 171 | + | case m.ov.open || !c.animOn(): |
| 172 | // The overview does not slide: the swipe moves its selection. | 172 | // The overview does not slide: the swipe moves its selection. | ||
| 173 | + | // With animations off nothing slides either: the swipe runs a | |||
| 174 | + | // focus action when the fingers lift. | |||
| 173 | g.mode, g.snap = swipeDiscrete, newStepSwipe() | 175 | g.mode, g.snap = swipeDiscrete, newStepSwipe() | ||
| 174 | case g.horizontal && w.slidable(): | 176 | case g.horizontal && w.slidable(): | ||
| 175 | g.mode, g.ws, g.points = swipeColumns, w, w.snapPoints() | 177 | g.mode, g.ws, g.points = swipeColumns, w, w.snapPoints() | ||