feat: Better menu bar integration, p=#11780, c=workspaces, common, kbs

* feat: Better menu bar integration, b=no-bug, c=workspaces, common, kbs
This commit is contained in:
mr. m
2025-12-31 18:34:30 +01:00
committed by GitHub
parent 7af0aab07f
commit be0668561f
14 changed files with 210 additions and 46 deletions

View File

@@ -8,6 +8,8 @@ files:
translation: browser/browser/zen-general.ftl translation: browser/browser/zen-general.ftl
- source: en-US/browser/browser/zen-split-view.ftl - source: en-US/browser/browser/zen-split-view.ftl
translation: browser/browser/zen-split-view.ftl translation: browser/browser/zen-split-view.ftl
- source: en-US/browser/browser/zen-menubar.ftl
translation: browser/browser/zen-menubar.ftl
- source: en-US/browser/browser/zen-vertical-tabs.ftl - source: en-US/browser/browser/zen-vertical-tabs.ftl
translation: browser/browser/zen-vertical-tabs.ftl translation: browser/browser/zen-vertical-tabs.ftl
- source: en-US/browser/browser/zen-welcome.ftl - source: en-US/browser/browser/zen-welcome.ftl

View File

@@ -50,12 +50,6 @@ zen-tabs-renamed = Tab has been successfully renamed!
zen-background-tab-opened-toast = New background tab opened! zen-background-tab-opened-toast = New background tab opened!
zen-workspace-renamed-toast = Workspace has been successfully renamed! zen-workspace-renamed-toast = Workspace has been successfully renamed!
zen-library-sidebar-workspaces =
.label = Spaces
zen-library-sidebar-mods =
.label = Mods
zen-toggle-compact-mode-button = zen-toggle-compact-mode-button =
.label = Compact Mode .label = Compact Mode
.tooltiptext = Toggle Compact Mode .tooltiptext = Toggle Compact Mode

View File

@@ -0,0 +1,20 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
zen-menubar-toggle-pinned-tabs =
.label =
{ $pinnedAreCollapsed ->
[true] Expand Pinned Tabs
*[false] Collapse Pinned Tabs
}
zen-menubar-appearance =
.label = Website Appearance
zen-menubar-appearance-auto =
.label = Automatic
zen-menubar-appearance-light =
.label = Light
zen-menubar-appearance-dark =
.label = Dark

View File

@@ -4,6 +4,9 @@
zen-panel-ui-workspaces-text = Spaces zen-panel-ui-workspaces-text = Spaces
zen-panel-ui-spaces-label =
.label = Spaces
zen-panel-ui-workspaces-create = zen-panel-ui-workspaces-create =
.label = Create Space .label = Create Space
@@ -83,3 +86,9 @@ zen-workspaces-close-all-unpinned-tabs-toast = Tabs Closed! Use <span>{ $shortcu
zen-workspaces-close-all-unpinned-tabs-title = zen-workspaces-close-all-unpinned-tabs-title =
.label = Clear .label = Clear
.tooltiptext = Close all unpinned tabs .tooltiptext = Close all unpinned tabs
zen-panel-ui-workspaces-change-forward =
.label = Next Space
zen-panel-ui-workspaces-change-back =
.label = Previous Space

View File

@@ -6,6 +6,7 @@
<link rel="localization" href="browser/zen-workspaces.ftl"/> <link rel="localization" href="browser/zen-workspaces.ftl"/>
<link rel="localization" href="browser/zen-split-view.ftl"/> <link rel="localization" href="browser/zen-split-view.ftl"/>
<link rel="localization" href="browser/zen-general.ftl"/> <link rel="localization" href="browser/zen-general.ftl"/>
<link rel="localization" href="browser/zen-menubar.ftl"/>
<link rel="localization" href="browser/zen-vertical-tabs.ftl"/> <link rel="localization" href="browser/zen-vertical-tabs.ftl"/>
<link rel="localization" href="browser/zen-folders.ftl"/> <link rel="localization" href="browser/zen-folders.ftl"/>
</linkset> </linkset>

View File

@@ -196,6 +196,7 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
this.#currentPromiseReject = null; this.#currentPromiseReject = null;
this.#anchor.removeAttribute('zen-emoji-open'); this.#anchor.removeAttribute('zen-emoji-open');
this.#anchor.parentElement.removeAttribute('zen-emoji-open');
this.#anchor = null; this.#anchor = null;
} }
@@ -222,6 +223,7 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
}); });
this.#anchor = anchor; this.#anchor = anchor;
this.#anchor.setAttribute('zen-emoji-open', 'true'); this.#anchor.setAttribute('zen-emoji-open', 'true');
this.#anchor.parentElement.setAttribute('zen-emoji-open', 'true');
if (onlySvgIcons) { if (onlySvgIcons) {
this.#panel.setAttribute('only-svg-icons', 'true'); this.#panel.setAttribute('only-svg-icons', 'true');
} else { } else {

View File

@@ -13,6 +13,7 @@
content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/modules/ZenSessionStore.mjs) content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/modules/ZenSessionStore.mjs)
content/browser/zen-components/ZenHasPolyfill.mjs (../../zen/common/modules/ZenHasPolyfill.mjs) content/browser/zen-components/ZenHasPolyfill.mjs (../../zen/common/modules/ZenHasPolyfill.mjs)
content/browser/zen-components/ZenSidebarNotification.mjs (../../zen/common/modules/ZenSidebarNotification.mjs) content/browser/zen-components/ZenSidebarNotification.mjs (../../zen/common/modules/ZenSidebarNotification.mjs)
content/browser/zen-components/ZenMenubar.mjs (../../zen/common/modules/ZenMenubar.mjs)
content/browser/zen-components/ZenEmojisData.min.mjs (../../zen/common/emojis/ZenEmojisData.min.mjs) content/browser/zen-components/ZenEmojisData.min.mjs (../../zen/common/emojis/ZenEmojisData.min.mjs)
content/browser/zen-components/ZenEmojiPicker.mjs (../../zen/common/emojis/ZenEmojiPicker.mjs) content/browser/zen-components/ZenEmojiPicker.mjs (../../zen/common/emojis/ZenEmojiPicker.mjs)

View File

@@ -0,0 +1,91 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
const WINDOW_SCHEME_PREF = 'zen.view.window.scheme';
const WINDOW_SCHEME_MAPPING = {
dark: 0,
light: 1,
auto: 2,
};
class nsZenMenuBar {
init() {
this.#initViewMenu();
this.#initSpacesMenu();
}
#initViewMenu() {
let appearanceMenu = window.MozXULElement.parseXULToFragment(`
<menu data-l10n-id="zen-menubar-appearance">
<menupopup>
<menuitem data-l10n-id="zen-menubar-appearance-auto" data-type="auto" type="radio" checked="true" />
<menuitem data-l10n-id="zen-menubar-appearance-light" data-type="light" type="radio" />
<menuitem data-l10n-id="zen-menubar-appearance-dark" data-type="dark" type="radio" />
</menupopup>
</menu>`);
const menu = appearanceMenu.querySelector('menu');
menu.addEventListener('command', (event) => {
const type = event.target.getAttribute('data-type');
const schemeValue = WINDOW_SCHEME_MAPPING[type];
Services.prefs.setIntPref(WINDOW_SCHEME_PREF, schemeValue);
});
const parent = document.getElementById('view-menu');
const parentPopup = parent.querySelector('menupopup');
parentPopup.prepend(document.createXULElement('menuseparator'));
parentPopup.prepend(menu);
const sibling = document.getElementById('viewSidebarMenuMenu');
const togglePinnedItem = window.MozXULElement.parseXULToFragment(
'<menuitem data-l10n-id="zen-menubar-toggle-pinned-tabs" />'
).querySelector('menuitem');
if (!gZenWorkspaces.privateWindowOrDisabled) sibling.after(togglePinnedItem);
parentPopup.addEventListener('popupshowing', () => {
const currentScheme = Services.prefs.getIntPref(WINDOW_SCHEME_PREF);
for (const [type, value] of Object.entries(WINDOW_SCHEME_MAPPING)) {
let menuItem = menu.querySelector(`menuitem[data-type="${type}"]`);
if (value === currentScheme) {
menuItem.setAttribute('checked', 'true');
} else {
menuItem.removeAttribute('checked');
}
}
const pinnedAreCollapsed =
gZenWorkspaces.activeWorkspaceElement?.hasCollapsedPinnedTabs ?? false;
const args = { pinnedAreCollapsed };
document.l10n.setArgs(togglePinnedItem, args);
});
togglePinnedItem.addEventListener('command', () => {
gZenWorkspaces.activeWorkspaceElement?.collapsiblePins.toggle();
});
}
#initSpacesMenu() {
let menubar = window.MozXULElement.parseXULToFragment(`
<menu id="zen-spaces-menubar" data-l10n-id="zen-panel-ui-spaces-label">
<menupopup>
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation"/>
<menuitem data-l10n-id="zen-workspaces-change-theme" command="cmd_zenOpenZenThemePicker"/>
<menuitem data-l10n-id="zen-workspaces-panel-change-name" command="cmd_zenChangeWorkspaceName"/>
<menuitem data-l10n-id="zen-workspaces-panel-change-icon" command="cmd_zenChangeWorkspaceIcon"/>
<menuseparator/>
<menuitem
data-l10n-id="zen-panel-ui-workspaces-change-forward"
command="cmd_zenWorkspaceForward"
key="zen-workspace-forward"/>
<menuitem
data-l10n-id="zen-panel-ui-workspaces-change-back"
command="cmd_zenWorkspaceBack"
key="zen-workspace-backward"/>
</menupopup>
</menu>`);
document.getElementById('view-menu').after(menubar);
document.getElementById('zen-spaces-menubar').addEventListener('popupshowing', () => {
gZenWorkspaces.updateWorkspacesChangeContextMenu();
});
}
}
export const ZenMenubar = new nsZenMenuBar();

View File

@@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
import { nsZenMultiWindowFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs'; import { nsZenMultiWindowFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
import { ZenMenubar } from 'chrome://browser/content/zen-components/ZenMenubar.mjs';
window.gZenUIManager = { window.gZenUIManager = {
_popupTrackingElements: [], _popupTrackingElements: [],
@@ -60,6 +61,8 @@ window.gZenUIManager = {
this._addNewCustomizableButtonsIfNeeded(); this._addNewCustomizableButtonsIfNeeded();
this._initOmnibox(); this._initOmnibox();
this._initBookmarkCollapseListener(); this._initBookmarkCollapseListener();
ZenMenubar.init();
}, },
_addNewCustomizableButtonsIfNeeded() { _addNewCustomizableButtonsIfNeeded() {

View File

@@ -694,10 +694,12 @@ class nsZenKeyboardShortcutsLoader {
newShortcutList.push( newShortcutList.push(
new KeyShortcut( new KeyShortcut(
`zen-workspace-switch-${i}`, `zen-workspace-switch-${i}`,
'', AppConstants.platform == 'macosx' ? `${i === 10 ? 0 : i}` : '',
'', '',
ZEN_WORKSPACE_SHORTCUTS_GROUP, ZEN_WORKSPACE_SHORTCUTS_GROUP,
nsKeyShortcutModifiers.fromObject({}), nsKeyShortcutModifiers.fromObject(
AppConstants.platform == 'macosx' ? { ctrl: true } : {}
),
`cmd_zenWorkspaceSwitch${i}`, `cmd_zenWorkspaceSwitch${i}`,
`zen-workspace-shortcut-switch-${i}` `zen-workspace-shortcut-switch-${i}`
) )

View File

@@ -175,17 +175,23 @@ class nsZenWindowSync {
// assign one and sync it to other windows. // assign one and sync it to other windows.
// This should only happen really when updating from an older version // This should only happen really when updating from an older version
// that didn't have this feature. // that didn't have this feature.
this.#runOnAllWindows(null, (aWindow) => { let previousWindowPromise = Promise.resolve();
const { gZenWorkspaces } = aWindow; this.#runOnAllWindows(null, async (aWindow) => {
for (let tab of gZenWorkspaces.allStoredTabs) { await previousWindowPromise;
if (!tab.id) { previousWindowPromise = new Promise((resolve) => {
tab.id = this.#newTabSyncId; const { gZenWorkspaces } = aWindow;
lazy.TabStateFlusher.flush(tab.linkedBrowser); let allPromises = [];
for (let tab of gZenWorkspaces.allStoredTabs) {
if (!tab.id) {
tab.id = this.#newTabSyncId;
lazy.TabStateFlusher.flush(tab.linkedBrowser);
}
if (tab.pinned && !tab._zenPinnedInitialState) {
allPromises.push(this.setPinnedTabState(tab));
}
} }
if (tab.pinned && !tab._zenPinnedInitialState) { Promise.all(allPromises).then(resolve);
this.setPinnedTabState(tab); });
}
}
}); });
} }
@@ -793,6 +799,7 @@ class nsZenWindowSync {
*/ */
setPinnedTabState(aTab) { setPinnedTabState(aTab) {
return lazy.TabStateFlusher.flush(aTab.linkedBrowser).finally(() => { return lazy.TabStateFlusher.flush(aTab.linkedBrowser).finally(() => {
this.log(`Setting pinned initial state for tab ${aTab.id}`);
const state = this.#getTabState(aTab); const state = this.#getTabState(aTab);
const initialState = { const initialState = {
entry: state.entries[state.index - 1], entry: state.entries[state.index - 1],

View File

@@ -37,6 +37,10 @@ class nsZenCollapsiblePins extends nsZenFolder {
super.collapsed = value; super.collapsed = value;
gBrowser.tabContainer._invalidateCachedVisibleTabs(); gBrowser.tabContainer._invalidateCachedVisibleTabs();
} }
toggle() {
this.collapsed = !this.collapsed;
}
} }
export class nsZenWorkspace extends MozXULElement { export class nsZenWorkspace extends MozXULElement {

View File

@@ -920,7 +920,7 @@ class nsZenWorkspaces {
window.addEventListener('TabSelect', this.onLocationChange.bind(this)); window.addEventListener('TabSelect', this.onLocationChange.bind(this));
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this)); window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
this.#updateWorkspacesChangeContextMenu(); this.updateWorkspacesChangeContextMenu();
} }
async selectStartPage() { async selectStartPage() {
@@ -1173,12 +1173,15 @@ class nsZenWorkspaces {
); );
} }
generateMenuItemForWorkspace(workspace) { generateMenuItemForWorkspace(workspace, disableCurrent = false) {
const item = document.createXULElement('menuitem'); const item = document.createXULElement('menuitem');
item.className = 'zen-workspace-context-menu-item'; item.className = 'zen-workspace-context-menu-item';
item.setAttribute('zen-workspace-id', workspace.uuid); item.setAttribute('zen-workspace-id', workspace.uuid);
if (!disableCurrent) {
item.setAttribute('type', 'radio');
}
if (workspace.uuid === this.activeWorkspace) { if (workspace.uuid === this.activeWorkspace) {
item.setAttribute('disabled', true); item.setAttribute(disableCurrent ? 'disabled' : 'checked', true);
} }
let name = workspace.name; let name = workspace.name;
const iconIsSvg = workspace.icon && workspace.icon.endsWith('.svg'); const iconIsSvg = workspace.icon && workspace.icon.endsWith('.svg');
@@ -1381,7 +1384,7 @@ class nsZenWorkspaces {
this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache()).finally(() => { this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache()).finally(() => {
this.updateTabsContainers(); this.updateTabsContainers();
}); });
this.#updateWorkspacesChangeContextMenu(); this.updateWorkspacesChangeContextMenu();
} }
async reorderWorkspace(id, newPosition) { async reorderWorkspace(id, newPosition) {
@@ -2335,24 +2338,49 @@ class nsZenWorkspaces {
} }
} }
#updateWorkspacesChangeContextMenu() { updateWorkspacesChangeContextMenu() {
if (gZenWorkspaces.privateWindowOrDisabled) return; if (gZenWorkspaces.privateWindowOrDisabled) return;
const workspaces = this.getWorkspaces(); const workspaces = this.getWorkspaces();
const menuPopup = document.getElementById('moveTabOptionsMenu'); let menuPopupID = 'moveTabOptionsMenu';
if (!menuPopup) { const menuPopup = document.getElementById(menuPopupID);
let menubar = document.getElementById('zen-spaces-menubar');
if (!menuPopup || !menubar) {
return; return;
} }
for (const item of menuPopup.querySelectorAll('.zen-workspace-context-menu-item')) { let itemsToFill = [menubar.querySelector('menupopup'), menuPopup];
item.remove(); for (const popup of itemsToFill) {
} let isMoveTabPopup = popup.id === menuPopupID;
const separator = document.createXULElement('menuseparator'); for (const item of popup.querySelectorAll('.zen-workspace-context-menu-item')) {
separator.classList.add('zen-workspace-context-menu-item'); item.remove();
menuPopup.prepend(separator); }
for (let workspace of workspaces.reverse()) { const separator = document.createXULElement('menuseparator');
const menuItem = this.generateMenuItemForWorkspace(workspace); separator.classList.add('zen-workspace-context-menu-item');
menuItem.setAttribute('command', 'cmd_zenChangeWorkspaceTab'); if (isMoveTabPopup) {
menuPopup.prepend(menuItem); popup.prepend(separator);
} else {
popup.appendChild(separator);
}
let i = 0;
for (let workspace of isMoveTabPopup ? workspaces.reverse() : workspaces) {
const menuItem = this.generateMenuItemForWorkspace(
workspace,
/* disableCurrent = */ isMoveTabPopup
);
if (isMoveTabPopup) {
popup.prepend(menuItem);
menuItem.setAttribute('command', 'cmd_zenChangeWorkspaceTab');
} else {
if (i < 10) {
menuItem.setAttribute('key', `zen-workspace-switch-${i + 1}`);
}
menuItem.addEventListener('command', () => {
this.changeWorkspace(workspace);
});
popup.appendChild(menuItem);
}
i++;
}
} }
} }

View File

@@ -387,7 +387,7 @@ zen-workspace {
.zen-current-workspace-indicator-stack { .zen-current-workspace-indicator-stack {
transition: margin-inline-end 0.1s; transition: margin-inline-end 0.1s;
&[no-icon='true'] { &[no-icon='true']:not([zen-emoji-open='true']) {
margin-inline-end: calc(-1 * (var(--indicator-gap) + 16px)); margin-inline-end: calc(-1 * (var(--indicator-gap) + 16px));
} }
} }
@@ -399,7 +399,7 @@ zen-workspace {
transform: rotate(90deg); transform: rotate(90deg);
padding: 1px; padding: 1px;
.zen-current-workspace-indicator-stack[no-icon='true'] & { .zen-current-workspace-indicator-stack[no-icon='true']:not([zen-emoji-open='true']) & {
display: flex; display: flex;
opacity: 0; opacity: 0;
} }
@@ -407,17 +407,17 @@ zen-workspace {
& zen-workspace[haspinnedtabs] .zen-current-workspace-indicator:hover, & zen-workspace[haspinnedtabs] .zen-current-workspace-indicator:hover,
& zen-workspace[collapsedpinnedtabs] .zen-current-workspace-indicator { & zen-workspace[collapsedpinnedtabs] .zen-current-workspace-indicator {
.zen-current-workspace-indicator-chevron { .zen-current-workspace-indicator-stack:not([zen-emoji-open='true']) {
display: flex;
opacity: 1;
}
.zen-current-workspace-indicator-stack {
margin-inline-end: 0; margin-inline-end: 0;
}
.zen-current-workspace-indicator-icon { .zen-current-workspace-indicator-chevron {
display: none; display: flex;
opacity: 1;
}
.zen-current-workspace-indicator-icon {
display: none;
}
} }
} }