diff --git a/locales/en-US/browser/browser/preferences/zen-preferences.ftl b/locales/en-US/browser/browser/preferences/zen-preferences.ftl index 76bc306de24..2e530d3bdd9 100644 --- a/locales/en-US/browser/browser/preferences/zen-preferences.ftl +++ b/locales/en-US/browser/browser/preferences/zen-preferences.ftl @@ -75,11 +75,43 @@ zen-settings-normal-tabs-sync = zen-settings-workspaces-sync-unpinned-tabs = .label = Sync only pinned tabs in workspaces -zen-tabs-cycle-by-attribute = - .label = Ctrl+Tab cycles within Essential or Workspace tabs only -zen-tabs-cycle-ignore-pending-tabs = - .label = Ignore Pending tabs when cycling with Ctrl+Tab -zen-tabs-cycle-by-attribute-warning = Ctrl+Tab will cycle by recently used order, as it is enabled +pane-zen-tab-switcher-title = Tab Switcher +zen-tab-switcher-header = Switching between tabs + +# Variables: +# $shortcut (String) - The shortcut that cycles through tabs, like "Ctrl+Tab", or "none" when it has no shortcut. +zen-tab-switcher-description = + { $shortcut -> + [none] Choose which tabs the tab switcher cycles through. Set a shortcut + *[other] Choose which tabs { $shortcut } cycles through. Change shortcut + } +# Variables: +# $shortcut (String) - The shortcut that cycles through tabs, like "Ctrl+Tab", or "none" when it has no shortcut. +zen-tab-switcher-recently-used-order = + .label = + { $shortcut -> + [none] Cycle through tabs in recently used order + *[other] Cycle through tabs in recently used order with { $shortcut } + } +zen-tab-switcher-cross-space = + .label = Cycle through tabs in all spaces, not just the current one +# Variables: +# $shortcut (String) - The shortcut that cycles through tabs, like "Ctrl+Tab", or "none" when it has no shortcut. +zen-tab-switcher-ignore-pending-tabs = + .label = + { $shortcut -> + [none] Ignore pending tabs when cycling through tabs + *[other] Ignore pending tabs when cycling with { $shortcut } + } +# Variables: +# $shortcut (String) - The shortcut that cycles through tabs, like "Ctrl+Tab", or "none" when it has no shortcut. +zen-tab-switcher-cycle-by-attribute = + .label = + { $shortcut -> + [none] Stay within Essential or Workspace tabs when cycling through tabs + *[other] Stay within Essential or Workspace tabs when cycling with { $shortcut } + } +zen-tab-switcher-cycle-by-attribute-warning = This has no effect while cycling in recently used order. zen-look-and-feel-compact-toolbar-themed = .label = Use themed background for compact toolbar @@ -198,6 +230,7 @@ zenCKSOption-group-zen-compact-mode = Compact Mode zenCKSOption-group-zen-workspace = Workspaces zenCKSOption-group-zen-other = Other Zen Features zenCKSOption-group-zen-split-view = Split View +zenCKSOption-group-zen-tabs = Tabs zen-text-action-copy-url-shortcut = Copy current URL zen-text-action-copy-url-markdown-shortcut = Copy current URL as Markdown @@ -227,3 +260,6 @@ zen-split-view-shortcut-unsplit = Close Split View zen-new-empty-split-view-shortcut = New Empty Split View zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-duplicate-tab-shortcut = Duplicate Tab +zen-tab-switcher-shortcut-cycle = Tab Switcher +zen-tab-switcher-shortcut-next = Next Tab +zen-tab-switcher-shortcut-previous = Previous Tab diff --git a/prefs/zen/sync.yaml b/prefs/zen/sync.yaml index 3eb3aaa558c..00f0572e4bb 100644 --- a/prefs/zen/sync.yaml +++ b/prefs/zen/sync.yaml @@ -28,6 +28,9 @@ - name: services.sync.prefs.sync.zen.tabs.ctrl-tab.ignore-pending-tabs value: true +- name: services.sync.prefs.sync.zen.tabs.ctrl-tab.cross-space + value: true + - name: services.sync.prefs.sync.zen.ctrlTab.show-pending-tabs value: true diff --git a/prefs/zen/zen.yaml b/prefs/zen/zen.yaml index 685bb3866ed..78d056b320a 100644 --- a/prefs/zen/zen.yaml +++ b/prefs/zen/zen.yaml @@ -52,3 +52,6 @@ - name: zen.tabs.ctrl-tab.ignore-pending-tabs value: false + +- name: zen.tabs.ctrl-tab.cross-space + value: false diff --git a/src/browser/base/content/zen-keysets.inc.xhtml b/src/browser/base/content/zen-keysets.inc.xhtml index 897550a2e4b..e49944d2b3e 100644 --- a/src/browser/base/content/zen-keysets.inc.xhtml +++ b/src/browser/base/content/zen-keysets.inc.xhtml @@ -28,6 +28,18 @@ + +#ifdef XP_MACOSX +#define ZEN_TAB_SWITCHER_MODIFIER control +#else +#define ZEN_TAB_SWITCHER_MODIFIER accel +#endif +#expand +#expand +#expand + diff --git a/src/browser/components/preferences/zen-settings.js b/src/browser/components/preferences/zen-settings.js index 6750869dd2a..c5e1b66de89 100644 --- a/src/browser/components/preferences/zen-settings.js +++ b/src/browser/components/preferences/zen-settings.js @@ -713,8 +713,76 @@ var gZenLooksAndFeel = { }, }; +var gZenTabSwitcherSettings = { + init() { + // Firefox's own checkbox for this pref, which this one replaces, does the + // same so a later migration doesn't undo the user's choice. + document.getElementById("zenTabSwitcherRecentlyUsedOrder").addEventListener("command", () => { + Services.prefs.clearUserPref("browser.ctrlTab.migrated"); + }); + + document.getElementById("zenTabSwitcherDescription").addEventListener("click", (event) => { + if (event.target.closest("a")) { + event.preventDefault(); + window.browsingContext.topChromeWindow.switchToTabHavingURI("about:keyboard", true); + } + }); + + const updateCrossSpace = () => { + document.getElementById("zenTabSwitcherCrossSpace").disabled = !Services.prefs.getBoolPref( + "browser.ctrlTab.sortByRecentlyUsed", + false + ); + }; + updateCrossSpace(); + Services.prefs.addObserver("browser.ctrlTab.sortByRecentlyUsed", updateCrossSpace); + + // Labels mention the cycle shortcut, which can be changed in + // about:keyboard. CustomKeys updates the key element in every window, so + // follow it there to keep them current. + const cycleKey = + window.browsingContext.topChromeWindow?.document.getElementById("zen-tab-switcher-cycle"); + this.updateShortcutLabels(cycleKey); + const keyObserver = new MutationObserver(() => this.updateShortcutLabels(cycleKey)); + if (cycleKey) { + keyObserver.observe(cycleKey, { + attributes: true, + attributeFilter: ["key", "keycode", "modifiers"], + }); + } + + window.addEventListener("unload", () => { + Services.prefs.removeObserver("browser.ctrlTab.sortByRecentlyUsed", updateCrossSpace); + keyObserver.disconnect(); + }); + }, + + /** + * @param {Element|null} cycleKey The element holding the cycle shortcut. + */ + updateShortcutLabels(cycleKey) { + const { ShortcutUtils } = ChromeUtils.importESModule( + "resource://gre/modules/ShortcutUtils.sys.mjs" + ); + const shortcut = + cycleKey?.hasAttribute("key") || cycleKey?.hasAttribute("keycode") + ? ShortcutUtils.prettifyShortcut(cycleKey) + : "none"; + for (const element of document.querySelectorAll(".zen-tab-switcher-shortcut-label")) { + // The description's translation is markup, so the shortcut is escaped + // there. Labels are plain text. + const isMarkup = element.id == "zenTabSwitcherDescription"; + document.l10n.setArgs(element, { + shortcut: isMarkup ? shortcut.replaceAll("&", "&").replaceAll("<", "<") : shortcut, + }); + } + }, +}; + var gZenWorkspacesSettings = { init() { + gZenTabSwitcherSettings.init(); + var tabsUnloaderPrefListener = { async observe() { let buttonIndex = await confirmRestartPrompt(true, 1, true, true); @@ -854,6 +922,11 @@ Preferences.addAll([ type: "bool", default: false, }, + { + id: "zen.tabs.ctrl-tab.cross-space", + type: "bool", + default: false, + }, { id: "zen.tabs.close-on-back-with-no-history", type: "bool", diff --git a/src/browser/components/preferences/zenTabsManagement.inc.xhtml b/src/browser/components/preferences/zenTabsManagement.inc.xhtml index 2cf7c9c9d8a..a8353c5672e 100644 --- a/src/browser/components/preferences/zenTabsManagement.inc.xhtml +++ b/src/browser/components/preferences/zenTabsManagement.inc.xhtml @@ -32,16 +32,43 @@ - + + + + +