From 465de39a53244119edb532c8149cd9537dd8f5c0 Mon Sep 17 00:00:00 2001 From: Bojin Li Date: Fri, 12 Sep 2025 08:53:15 +1000 Subject: [PATCH 1/3] Enable workspace horizontal scrolling compatibility for Logitech mice (#10325) * feat: allow both line and pixel scrolling in workspace switcher * fix: add cooling period for consecutive touchpad scroll events * fix: add directional detection logic to prevent accidental triggering * refactor: use cooldown const for deltaMode0 scroll events --- src/zen/workspaces/ZenWorkspaces.mjs | 59 ++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/zen/workspaces/ZenWorkspaces.mjs b/src/zen/workspaces/ZenWorkspaces.mjs index ac462be29..be12d7c61 100644 --- a/src/zen/workspaces/ZenWorkspaces.mjs +++ b/src/zen/workspaces/ZenWorkspaces.mjs @@ -596,39 +596,66 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { const scrollCooldown = 200; // Milliseconds to wait before allowing another scroll const scrollThreshold = 2; // Minimum scroll delta to trigger workspace change + const scrollDeltaMode0Cooldown = 200; // Cooldown for consecutive DOM_DELTA_PIXEL events toolbox.addEventListener( 'wheel', async (event) => { if (this.privateWindowOrDisabled) return; - // Only process non-gesture scrolls - if (event.deltaMode !== 1) return; + // Allow DOM_DELTA_LINE (1) and DOM_DELTA_PIXEL (0) events + if (event.deltaMode > 1) return; - const isVerticalScroll = event.deltaY && !event.deltaX; - - //if the scroll is vertical this checks that a modifier key is used before proceeding - if (isVerticalScroll) { - const activationKeyMap = { - ctrl: event.ctrlKey, - alt: event.altKey, - shift: event.shiftKey, - meta: event.metaKey, - }; + // Add cooling to consecutive DOM_DELTA_PIXEL events, which are usually from touchpads + if (event.deltaMode === 0) { + const now = Date.now(); + const timeSinceLastDeltaMode0 = now - (this._lastDeltaMode0Time || 0); + this._lastDeltaMode0Time = now; if ( - this.activationMethod in activationKeyMap && - !activationKeyMap[this.activationMethod] + Math.abs(event.deltaY || 0) === 0 && + timeSinceLastDeltaMode0 < scrollDeltaMode0Cooldown ) { return; } } + const absX = Math.abs(event.deltaX || 0); + const absY = Math.abs(event.deltaY || 0); + + const isVerticalScroll = absY > absX * 2; + const isHorizontalScroll = absX > absY * 2; + + const activationKeyMap = { + ctrl: event.ctrlKey, + alt: event.altKey, + shift: event.shiftKey, + meta: event.metaKey, + }; + const modifierActive = + this.activationMethod in activationKeyMap && activationKeyMap[this.activationMethod]; + + let delta; + let shouldProceed = false; + + if (isVerticalScroll && modifierActive) { + // scroll is vertical + modifier key + delta = event.deltaY; + shouldProceed = true; + } else if (isHorizontalScroll) { + // clear horizontal scrolling + delta = event.deltaX; + shouldProceed = true; + } else { + // diagonal scrolling or unclear direction, ignore + return; + } + + if (!shouldProceed) return; + const currentTime = Date.now(); if (currentTime - this._lastScrollTime < scrollCooldown) return; - //this decides which delta to use - const delta = isVerticalScroll ? event.deltaY : event.deltaX; if (Math.abs(delta) < scrollThreshold) return; // Determine scroll direction From e9b4443a9ef24206786a35dc445f9c988b5fb885 Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Fri, 12 Sep 2025 13:44:03 +0200 Subject: [PATCH 2/3] fix: Fixed double toolbar issue on twilight, b=no-bug, c=common, tabs, folders, tests --- .../tabbrowser/content/tabbrowser-js.patch | 16 ++++++++++++---- src/zen/common/styles/zen-single-components.css | 2 +- src/zen/tabs/zen-tabs.css | 2 +- .../tabs/zen-tabs/vertical-tabs-topbar.inc.css | 2 +- src/zen/tabs/zen-tabs/vertical-tabs.css | 2 +- .../folders/browser_folder_multiselected.js | 2 +- 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 46fdcd3c5..c810f9de4 100644 --- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch +++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js -index 3204f253c23551650991d3385dd256d55892a012..78727727a0c623e0213177700124869b2163b89c 100644 +index 3204f253c23551650991d3385dd256d55892a012..0285b0bcf1e5ba769011c82729e010eedf8dcc29 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -427,15 +427,64 @@ @@ -804,7 +804,15 @@ index 3204f253c23551650991d3385dd256d55892a012..78727727a0c623e0213177700124869b window.focus(); aEvent.preventDefault(); break; -@@ -8199,6 +8376,7 @@ +@@ -7264,7 +7441,6 @@ + } + case "TabGroupCollapse": + aEvent.target.tabs.forEach(tab => { +- this.removeFromMultiSelectedTabs(tab); + }); + break; + case "TabGroupCreateByUser": +@@ -8199,6 +8375,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -812,7 +820,7 @@ index 3204f253c23551650991d3385dd256d55892a012..78727727a0c623e0213177700124869b gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -9200,7 +9378,7 @@ var TabContextMenu = { +@@ -9200,7 +9377,7 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; @@ -821,7 +829,7 @@ index 3204f253c23551650991d3385dd256d55892a012..78727727a0c623e0213177700124869b // Build Ask Chat items TabContextMenu.GenAI.buildTabMenu( document.getElementById("context_askChat"), -@@ -9520,6 +9698,7 @@ var TabContextMenu = { +@@ -9520,6 +9697,7 @@ var TabContextMenu = { ) ); } else { diff --git a/src/zen/common/styles/zen-single-components.css b/src/zen/common/styles/zen-single-components.css index 9853a83da..e1599b51d 100644 --- a/src/zen/common/styles/zen-single-components.css +++ b/src/zen/common/styles/zen-single-components.css @@ -38,7 +38,7 @@ body > #confetti { } /* Bookmarks */ -#PersonalToolbar:not([collapsed='true']) { +#PersonalToolbar:not([collapsed]) { min-height: 30px; } diff --git a/src/zen/tabs/zen-tabs.css b/src/zen/tabs/zen-tabs.css index 4319b3431..bfd25a1ff 100644 --- a/src/zen/tabs/zen-tabs.css +++ b/src/zen/tabs/zen-tabs.css @@ -36,7 +36,7 @@ * - Bookmarks toolbar is visible OR * - The container is explicitly marked to hide controls (e.g., on Linux with reversed controls) */ - &:has(#PersonalToolbar[collapsed='false']) { + &:has(#PersonalToolbar:not([collapsed])) { %include zen-tabs/vertical-tabs-topbar.inc.css } &[should-hide='true'] { diff --git a/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css b/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css index ac882b445..5bd8997da 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css @@ -43,7 +43,7 @@ z-index: 1; } @media -moz-pref('zen.view.experimental-no-window-controls') { - &:has(#PersonalToolbar[collapsed='true']) { + &:has(#PersonalToolbar[collapsed]) { max-height: 0 !important; overflow: hidden; opacity: 0 !important; diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index 5db51b127..853270d05 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -498,7 +498,7 @@ padding-top: 0; /* Reset top padding */ /* Ensure Personal Toolbar has no left padding when expanded */ - #PersonalToolbar:not([collapsed='true']) { + #PersonalToolbar:not([collapsed]) { padding-left: 0 !important; } diff --git a/src/zen/tests/folders/browser_folder_multiselected.js b/src/zen/tests/folders/browser_folder_multiselected.js index 577c85a4c..39a0659c2 100644 --- a/src/zen/tests/folders/browser_folder_multiselected.js +++ b/src/zen/tests/folders/browser_folder_multiselected.js @@ -19,7 +19,7 @@ add_task(async function test_Folder_Multiselected_Tabs() { await collapseEvent; ok(tab2.multiselected, 'Tab 2 should not be multiselected'); - Assert.equal(gBrowser.multiSelectedTabsCount, 2, 'There should be 2 multiselected tabs'); + Assert.equal(gBrowser.multiSelectedTabsCount, 3, 'There should be 3 multiselected tabs'); for (const t of [tab1, tab2]) { BrowserTestUtils.removeTab(t); From 53241eb1bee130ee006d8b91a5179f14f3407509 Mon Sep 17 00:00:00 2001 From: "Mr. M" Date: Fri, 12 Sep 2025 15:00:47 +0200 Subject: [PATCH 3/3] feat: Small quality fixes, b=no-bug, c=common, compact-mode, folders, kbs, split-view --- prefs/theme.yaml | 2 +- .../components/preferences/zen-settings.js | 2 +- .../shared/in-content/common-shared-css.patch | 11 +++++++- .../common/styles/zen-single-components.css | 5 ++++ src/zen/compact-mode/ZenCompactMode.mjs | 6 +++++ src/zen/folders/zen-folders.css | 2 ++ src/zen/kbs/ZenKeyboardShortcuts.mjs | 25 ++++++++++++++----- src/zen/split-view/ZenViewSplitter.mjs | 4 +-- src/zen/split-view/zen-decks.css | 6 +++-- 9 files changed, 50 insertions(+), 13 deletions(-) diff --git a/prefs/theme.yaml b/prefs/theme.yaml index 7d912f41b..3131355aa 100644 --- a/prefs/theme.yaml +++ b/prefs/theme.yaml @@ -33,7 +33,7 @@ value: true - name: zen.theme.styled-status-panel - value: false + value: true # ==== Mark: border radius ==== diff --git a/src/browser/components/preferences/zen-settings.js b/src/browser/components/preferences/zen-settings.js index 4ed2671b8..d5d54b51d 100644 --- a/src/browser/components/preferences/zen-settings.js +++ b/src/browser/components/preferences/zen-settings.js @@ -880,7 +880,7 @@ var gZenCKSSettings = { const labelValue = zenMissingKeyboardShortcutL10n[keyID] ?? l10nID; - if (zenIgnoreKeyboardShortcutL10n.includes(labelValue)) { + if (zenIgnoreKeyboardShortcutL10n.includes(labelValue) || shortcut.shouldBeEmpty) { continue; } diff --git a/src/toolkit/themes/shared/in-content/common-shared-css.patch b/src/toolkit/themes/shared/in-content/common-shared-css.patch index 9fe5ceab3..70b644c55 100644 --- a/src/toolkit/themes/shared/in-content/common-shared-css.patch +++ b/src/toolkit/themes/shared/in-content/common-shared-css.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/themes/shared/in-content/common-shared.css b/toolkit/themes/shared/in-content/common-shared.css -index c45d48dc3106a2dc36f6dd704ebb2721817f016e..cfaf394d8ada7f4256037ead08fd338b7e28df32 100644 +index c45d48dc3106a2dc36f6dd704ebb2721817f016e..71291ab50b73872473190537fe2ce7a95a57aa12 100644 --- a/toolkit/themes/shared/in-content/common-shared.css +++ b/toolkit/themes/shared/in-content/common-shared.css @@ -4,7 +4,7 @@ @@ -11,6 +11,15 @@ index c45d48dc3106a2dc36f6dd704ebb2721817f016e..cfaf394d8ada7f4256037ead08fd338b @namespace html "http://www.w3.org/1999/xhtml"; @namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; +@@ -69,7 +69,7 @@ + * this in forced colors mode, as we should be using system colours then. + */ + :root[dialogroot] { +- --background-color-canvas: #42414d; ++ --background-color-canvas: var(--zen-dialog-background); + } + } + @@ -708,7 +708,7 @@ html|*#categories[last-input-type="mouse"] > html|button.category:focus-visible fill-opacity: 1; } diff --git a/src/zen/common/styles/zen-single-components.css b/src/zen/common/styles/zen-single-components.css index e1599b51d..87b9ae171 100644 --- a/src/zen/common/styles/zen-single-components.css +++ b/src/zen/common/styles/zen-single-components.css @@ -256,3 +256,8 @@ body > #confetti { } } } + +/* Customizable modes */ +#customization-container { + --toolbar-bgcolor: var(--zen-dialog-background); +} diff --git a/src/zen/compact-mode/ZenCompactMode.mjs b/src/zen/compact-mode/ZenCompactMode.mjs index 167240eee..1f0d70eb3 100644 --- a/src/zen/compact-mode/ZenCompactMode.mjs +++ b/src/zen/compact-mode/ZenCompactMode.mjs @@ -533,6 +533,12 @@ var gZenCompactModeManager = { for (let i = 0; i < this.hoverableElements.length; i++) { let target = this.hoverableElements[i].element; + + // Add the attribute on startup if the mouse is already over the element + if (target.matches(':hover')) { + target.setAttribute('zen-has-hover', 'true'); + } + const onEnter = (event) => { setTimeout(() => { if (event.type === 'mouseenter' && !event.target.matches(':hover')) return; diff --git a/src/zen/folders/zen-folders.css b/src/zen/folders/zen-folders.css index cabddf820..f0673244d 100644 --- a/src/zen/folders/zen-folders.css +++ b/src/zen/folders/zen-folders.css @@ -19,6 +19,8 @@ tab-group[split-view-group] { transparent 40% ); + --tab-collapsed-background-width: none; + :root[zen-sidebar-expanded='true'] & { margin-inline-start: var(--zen-folder-indent) !important; } diff --git a/src/zen/kbs/ZenKeyboardShortcuts.mjs b/src/zen/kbs/ZenKeyboardShortcuts.mjs index 0d4fe20b5..819e05ed7 100644 --- a/src/zen/kbs/ZenKeyboardShortcuts.mjs +++ b/src/zen/kbs/ZenKeyboardShortcuts.mjs @@ -296,6 +296,7 @@ class KeyShortcut { #disabled = false; #reserved = false; #internal = false; + #shouldBeEmpty = false; constructor( id, @@ -396,6 +397,17 @@ class KeyShortcut { return `zen-${id}`; } + set shouldBeEmpty(value) { + this.#shouldBeEmpty = value; + if (value) { + this.clearKeybind(); + } + } + + get shouldBeEmpty() { + return this.#shouldBeEmpty; + } + toXHTMLElement(window) { let key = window.document.createXULElement('key'); return this.replaceWithChild(key); @@ -844,12 +856,13 @@ class nsZenKeyboardShortcutsVersioner { // Hard-remove deprecated or conflicting defaults regardless of version // - Remove the built-in "Open File" keybinding; menu item remains available // - Remove default "Bookmark All Tabs" keybinding (Ctrl+Shift+D) to avoid conflict - out = out.filter( - (shortcut) => - shortcut.getAction?.() !== 'Browser:OpenFile' && - shortcut.getID?.() !== 'bookmarkAllTabsKb' && - shortcut.getID?.() !== 'key_stop' - ); + // - Remove "Stop" keybinding to avoid conflict with Firefox's built-in binding + const shouldBeEmptyShortcuts = ['openFileKb', 'bookmarkAllTabsKb', 'key_stop']; + for (let shortcut of out) { + if (shouldBeEmptyShortcuts.includes(shortcut.getID?.())) { + shortcut.shouldBeEmpty = true; + } + } return out; } diff --git a/src/zen/split-view/ZenViewSplitter.mjs b/src/zen/split-view/ZenViewSplitter.mjs index 49464588e..b50a12a93 100644 --- a/src/zen/split-view/ZenViewSplitter.mjs +++ b/src/zen/split-view/ZenViewSplitter.mjs @@ -332,7 +332,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { paddingLeft: 0, }, { - duration: 0.08, + duration: 0.1, easing: 'ease-out', } ), @@ -347,7 +347,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { : {}), }, { - duration: 0.08, + duration: 0.1, easing: 'ease-out', } ), diff --git a/src/zen/split-view/zen-decks.css b/src/zen/split-view/zen-decks.css index 6f241bf4b..b311601d3 100644 --- a/src/zen/split-view/zen-decks.css +++ b/src/zen/split-view/zen-decks.css @@ -54,7 +54,7 @@ #tabbrowser-tabpanels[zen-split-view='true'] .browserSidebarContainer.deck-selected { &:not(.zen-glance-overlay) { - outline: 1px solid var(--zen-primary-color) !important; + outline: 2px solid var(--zen-primary-color) !important; } &.zen-glance-overlay { @@ -181,10 +181,12 @@ #zen-split-view-fake-browser { position: absolute; height: 100%; - background: rgba(255, 255, 255, 0.1); + border: 2px solid var(--zen-primary-color); + background: rgba(255, 255, 255, 0.01); border-radius: var(--zen-native-inner-radius); box-shadow: var(--zen-big-shadow); overflow: hidden; + will-change: width, margin-left; &[side='right'] { right: 0;