From b999a932ac587e864502cf64edb84f437afa9bd1 Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Thu, 7 May 2026 17:08:50 +0200 Subject: [PATCH] no-bug: Improve swipe feel and avoid big jumps while swiping (gh-13603) --- prefs/zen/workspaces.yaml | 6 ++++ src/widget/SwipeTracker-cpp.patch | 24 +++++++++++-- src/zen/spaces/ZenSpaceManager.mjs | 16 ++------- src/zen/spaces/ZenSpacesSwipe.mjs | 47 +++++++++++++++++-------- src/zen/spaces/zen-workspaces.css | 1 + src/zen/tabs/zen-tabs/vertical-tabs.css | 1 + 6 files changed, 65 insertions(+), 30 deletions(-) diff --git a/prefs/zen/workspaces.yaml b/prefs/zen/workspaces.yaml index 58a0499ac..aeddd6802 100644 --- a/prefs/zen/workspaces.yaml +++ b/prefs/zen/workspaces.yaml @@ -17,6 +17,12 @@ - name: zen.workspaces.swipe-actions value: true +- name: zen.workspaces.swipe-actions.delta-multiplier + value: 90 + +- name: zen.workspaces.switch-animation-duration + value: 200 + - name: zen.workspaces.wrap-around-navigation value: true diff --git a/src/widget/SwipeTracker-cpp.patch b/src/widget/SwipeTracker-cpp.patch index 05969b0fb..54dd51238 100644 --- a/src/widget/SwipeTracker-cpp.patch +++ b/src/widget/SwipeTracker-cpp.patch @@ -1,5 +1,5 @@ diff --git a/widget/SwipeTracker.cpp b/widget/SwipeTracker.cpp -index 887d06d3bd9cdaa934880e0ae7a11ec8b737fb61..e2bf27c0130701f1d50990b60a5ef76e93c5a6bf 100644 +index 887d06d3bd9cdaa934880e0ae7a11ec8b737fb61..7225d4264c9ec71ef1e5e717a1a62c4ef0aff0b7 100644 --- a/widget/SwipeTracker.cpp +++ b/widget/SwipeTracker.cpp @@ -3,6 +3,7 @@ @@ -10,7 +10,17 @@ index 887d06d3bd9cdaa934880e0ae7a11ec8b737fb61..e2bf27c0130701f1d50990b60a5ef76e #include "InputData.h" #include "mozilla/FlushType.h" -@@ -90,7 +91,7 @@ bool SwipeTracker::ComputeSwipeSuccess() const { +@@ -67,6 +68,9 @@ double SwipeTracker::SwipeSuccessTargetValue() const { + } + + double SwipeTracker::ClampToAllowedRange(double aGestureAmount) const { ++ if (StaticPrefs::zen_swipe_is_fast_swipe()) { ++ return aGestureAmount; ++ } + // gestureAmount needs to stay between -1 and 0 when swiping right and + // between 0 and 1 when swiping left. + double min = +@@ -90,7 +94,7 @@ bool SwipeTracker::ComputeSwipeSuccess() const { return (mGestureAmount * targetValue + mCurrentVelocity * targetValue * @@ -19,3 +29,13 @@ index 887d06d3bd9cdaa934880e0ae7a11ec8b737fb61..e2bf27c0130701f1d50990b60a5ef76e kSwipeSuccessThreshold; } +@@ -141,7 +145,8 @@ nsEventStatus SwipeTracker::ProcessEvent( + // display the UI as if we were at the success threshold as that would + // give a false indication that navigation would happen. + if (!computedSwipeSuccess && (eventAmount >= kSwipeSuccessThreshold || +- eventAmount <= -kSwipeSuccessThreshold)) { ++ eventAmount <= -kSwipeSuccessThreshold) ++ && !StaticPrefs::zen_swipe_is_fast_swipe()) { + eventAmount = 0.999 * kSwipeSuccessThreshold; + if (mGestureAmount < 0.f) { + eventAmount = -eventAmount; diff --git a/src/zen/spaces/ZenSpaceManager.mjs b/src/zen/spaces/ZenSpaceManager.mjs index a97dd0978..7b4792ab1 100644 --- a/src/zen/spaces/ZenSpaceManager.mjs +++ b/src/zen/spaces/ZenSpaceManager.mjs @@ -137,8 +137,6 @@ class nsZenWorkspaces { document.documentElement.setAttribute("zen-private-window", "true"); } - this.popupOpenHandler = this._popupOpenHandler.bind(this); - window.addEventListener("resize", this.onWindowResize.bind(this)); this.addPopupListeners(); @@ -599,16 +597,6 @@ class nsZenWorkspaces { ); } - _popupOpenHandler() { - // If a popup is opened, we should stop the swipe gesture - if (this._swipeManager?.isGestureActive) { - document.documentElement.removeAttribute("swipe-gesture"); - gZenUIManager.tabsWrapper.style.removeProperty("scrollbar-width"); - this.updateTabsContainers(); - this._cancelSwipeAnimation(); - } - } - get activeWorkspace() { return this.#activeWorkspace; } @@ -1907,7 +1895,9 @@ class nsZenWorkspaces { } = {} ) { gZenUIManager.tabsWrapper.style.scrollbarWidth = "none"; - const kGlobalAnimationDuration = 0.2; + const kGlobalAnimationDuration = + Services.prefs.getIntPref("zen.workspaces.switch-animation-duration") / + 1000; this._animatingChange = true; const animations = []; const workspaces = this.getWorkspaces(); diff --git a/src/zen/spaces/ZenSpacesSwipe.mjs b/src/zen/spaces/ZenSpacesSwipe.mjs index fdffd64a5..7576efcc1 100644 --- a/src/zen/spaces/ZenSpacesSwipe.mjs +++ b/src/zen/spaces/ZenSpacesSwipe.mjs @@ -35,6 +35,19 @@ export class ZenSpacesSwipe { } this._attachWorkspaceSwipeGestures(element); } + + this._popupOpenHandler = this._popupOpenHandler.bind(this); + } + + get #stripWidth() { + return ( + window.windowUtils.getBoundsWithoutFlushing( + document.getElementById("navigator-toolbox") + ).width + + window.windowUtils.getBoundsWithoutFlushing( + document.getElementById("zen-sidebar-splitter") + ).width + ); } _attachWorkspaceSwipeGestures(element) { @@ -107,7 +120,7 @@ export class ZenSpacesSwipe { gZenFolders.cancelPopupTimer(); document.documentElement.setAttribute("swipe-gesture", "true"); - document.addEventListener("popupshown", ws.popupOpenHandler, { + document.addEventListener("popupshown", this._popupOpenHandler, { once: true, }); @@ -128,17 +141,16 @@ export class ZenSpacesSwipe { return; } + const stripWidth = this.#stripWidth; + event.preventDefault(); event.stopPropagation(); - const stripWidth = - window.windowUtils.getBoundsWithoutFlushing( - document.getElementById("navigator-toolbox") - ).width + - window.windowUtils.getBoundsWithoutFlushing( - document.getElementById("zen-sidebar-splitter") - ).width; - const delta = event.delta * stripWidth; + const delta = + event.delta * + Services.prefs.getIntPref( + "zen.workspaces.swipe-actions.delta-multiplier" + ); let translateX = this._swipeState.lastDelta + delta; // Add a force multiplier as we are translating the strip depending on how close to the edge we are let forceMultiplier = Math.min( @@ -152,7 +164,8 @@ export class ZenSpacesSwipe { translateX = this._swipeState.lastDelta; } - if (Math.abs(delta) > 0.8) { + if (Math.abs(delta) > 0.9) { + delete ws._hasAnimatedBackgrounds; this._swipeState.direction = delta > 0 ? "left" : "right"; } @@ -176,6 +189,10 @@ export class ZenSpacesSwipe { const rawDirection = moveForward ? 1 : -1; const direction = ws.naturalScroll ? -1 : 1; await ws.changeWorkspaceShortcut(rawDirection * direction, true); + } + + onSwipeGestureAnimationEnd() { + const ws = gZenWorkspaces; // Reset swipe state this._swipeState = { @@ -183,10 +200,6 @@ export class ZenSpacesSwipe { lastDelta: 0, direction: null, }; - } - - onSwipeGestureAnimationEnd() { - const ws = gZenWorkspaces; Services.prefs.setBoolPref("zen.swipe.is-fast-swipe", false); document.documentElement.removeAttribute("swipe-gesture"); @@ -198,11 +211,15 @@ export class ZenSpacesSwipe { ); delete ws._hasAnimatedBackgrounds; ws.updateTabsContainers(); - document.removeEventListener("popupshown", ws.popupOpenHandler, { + document.removeEventListener("popupshown", this._popupOpenHandler, { once: true, }); } + _popupOpenHandler() { + this.onSwipeGestureAnimationEnd(); + } + get isGestureActive() { return this._swipeState?.isGestureActive; } diff --git a/src/zen/spaces/zen-workspaces.css b/src/zen/spaces/zen-workspaces.css index f5d7cc796..56c1c0719 100644 --- a/src/zen/spaces/zen-workspaces.css +++ b/src/zen/spaces/zen-workspaces.css @@ -321,6 +321,7 @@ zen-workspace { height: 100%; overflow: hidden; color: var(--toolbox-textcolor); + will-change: transform; @media not (prefers-reduced-motion: reduce) { transition: padding-top 0.1s; diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index 2052aaa5e..39d555fc2 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -1131,6 +1131,7 @@ transition: max-height 0.3s ease-out, grid-template-columns 0.3s ease-out; + will-change: transform; opacity: 1; --min-essentials-width-wrap: calc(var(--tab-min-height) + 4px); grid-template-columns: repeat(auto-fit, minmax(max(23.7%, var(--min-essentials-width-wrap)), 1fr));