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:
@@ -8,6 +8,8 @@ files:
|
||||
translation: browser/browser/zen-general.ftl
|
||||
- source: en-US/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
|
||||
translation: browser/browser/zen-vertical-tabs.ftl
|
||||
- source: en-US/browser/browser/zen-welcome.ftl
|
||||
|
||||
@@ -50,12 +50,6 @@ zen-tabs-renamed = Tab has been successfully renamed!
|
||||
zen-background-tab-opened-toast = New background tab opened!
|
||||
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 =
|
||||
.label = Compact Mode
|
||||
.tooltiptext = Toggle Compact Mode
|
||||
|
||||
20
locales/en-US/browser/browser/zen-menubar.ftl
Normal file
20
locales/en-US/browser/browser/zen-menubar.ftl
Normal 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
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
zen-panel-ui-workspaces-text = Spaces
|
||||
|
||||
zen-panel-ui-spaces-label =
|
||||
.label = Spaces
|
||||
|
||||
zen-panel-ui-workspaces-create =
|
||||
.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 =
|
||||
.label = Clear
|
||||
.tooltiptext = Close all unpinned tabs
|
||||
|
||||
zen-panel-ui-workspaces-change-forward =
|
||||
.label = Next Space
|
||||
|
||||
zen-panel-ui-workspaces-change-back =
|
||||
.label = Previous Space
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<link rel="localization" href="browser/zen-workspaces.ftl"/>
|
||||
<link rel="localization" href="browser/zen-split-view.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-folders.ftl"/>
|
||||
</linkset>
|
||||
|
||||
@@ -196,6 +196,7 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
this.#currentPromiseReject = null;
|
||||
|
||||
this.#anchor.removeAttribute('zen-emoji-open');
|
||||
this.#anchor.parentElement.removeAttribute('zen-emoji-open');
|
||||
this.#anchor = null;
|
||||
}
|
||||
|
||||
@@ -222,6 +223,7 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
});
|
||||
this.#anchor = anchor;
|
||||
this.#anchor.setAttribute('zen-emoji-open', 'true');
|
||||
this.#anchor.parentElement.setAttribute('zen-emoji-open', 'true');
|
||||
if (onlySvgIcons) {
|
||||
this.#panel.setAttribute('only-svg-icons', 'true');
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
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/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/ZenEmojiPicker.mjs (../../zen/common/emojis/ZenEmojiPicker.mjs)
|
||||
|
||||
91
src/zen/common/modules/ZenMenubar.mjs
Normal file
91
src/zen/common/modules/ZenMenubar.mjs
Normal 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();
|
||||
@@ -3,6 +3,7 @@
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import { nsZenMultiWindowFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
|
||||
import { ZenMenubar } from 'chrome://browser/content/zen-components/ZenMenubar.mjs';
|
||||
|
||||
window.gZenUIManager = {
|
||||
_popupTrackingElements: [],
|
||||
@@ -60,6 +61,8 @@ window.gZenUIManager = {
|
||||
this._addNewCustomizableButtonsIfNeeded();
|
||||
this._initOmnibox();
|
||||
this._initBookmarkCollapseListener();
|
||||
|
||||
ZenMenubar.init();
|
||||
},
|
||||
|
||||
_addNewCustomizableButtonsIfNeeded() {
|
||||
|
||||
@@ -694,10 +694,12 @@ class nsZenKeyboardShortcutsLoader {
|
||||
newShortcutList.push(
|
||||
new KeyShortcut(
|
||||
`zen-workspace-switch-${i}`,
|
||||
'',
|
||||
AppConstants.platform == 'macosx' ? `${i === 10 ? 0 : i}` : '',
|
||||
'',
|
||||
ZEN_WORKSPACE_SHORTCUTS_GROUP,
|
||||
nsKeyShortcutModifiers.fromObject({}),
|
||||
nsKeyShortcutModifiers.fromObject(
|
||||
AppConstants.platform == 'macosx' ? { ctrl: true } : {}
|
||||
),
|
||||
`cmd_zenWorkspaceSwitch${i}`,
|
||||
`zen-workspace-shortcut-switch-${i}`
|
||||
)
|
||||
|
||||
@@ -175,17 +175,23 @@ class nsZenWindowSync {
|
||||
// assign one and sync it to other windows.
|
||||
// This should only happen really when updating from an older version
|
||||
// that didn't have this feature.
|
||||
this.#runOnAllWindows(null, (aWindow) => {
|
||||
let previousWindowPromise = Promise.resolve();
|
||||
this.#runOnAllWindows(null, async (aWindow) => {
|
||||
await previousWindowPromise;
|
||||
previousWindowPromise = new Promise((resolve) => {
|
||||
const { gZenWorkspaces } = aWindow;
|
||||
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) {
|
||||
this.setPinnedTabState(tab);
|
||||
allPromises.push(this.setPinnedTabState(tab));
|
||||
}
|
||||
}
|
||||
Promise.all(allPromises).then(resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -793,6 +799,7 @@ class nsZenWindowSync {
|
||||
*/
|
||||
setPinnedTabState(aTab) {
|
||||
return lazy.TabStateFlusher.flush(aTab.linkedBrowser).finally(() => {
|
||||
this.log(`Setting pinned initial state for tab ${aTab.id}`);
|
||||
const state = this.#getTabState(aTab);
|
||||
const initialState = {
|
||||
entry: state.entries[state.index - 1],
|
||||
|
||||
@@ -37,6 +37,10 @@ class nsZenCollapsiblePins extends nsZenFolder {
|
||||
super.collapsed = value;
|
||||
gBrowser.tabContainer._invalidateCachedVisibleTabs();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.collapsed = !this.collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
export class nsZenWorkspace extends MozXULElement {
|
||||
|
||||
@@ -920,7 +920,7 @@ class nsZenWorkspaces {
|
||||
window.addEventListener('TabSelect', this.onLocationChange.bind(this));
|
||||
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
|
||||
|
||||
this.#updateWorkspacesChangeContextMenu();
|
||||
this.updateWorkspacesChangeContextMenu();
|
||||
}
|
||||
|
||||
async selectStartPage() {
|
||||
@@ -1173,12 +1173,15 @@ class nsZenWorkspaces {
|
||||
);
|
||||
}
|
||||
|
||||
generateMenuItemForWorkspace(workspace) {
|
||||
generateMenuItemForWorkspace(workspace, disableCurrent = false) {
|
||||
const item = document.createXULElement('menuitem');
|
||||
item.className = 'zen-workspace-context-menu-item';
|
||||
item.setAttribute('zen-workspace-id', workspace.uuid);
|
||||
if (!disableCurrent) {
|
||||
item.setAttribute('type', 'radio');
|
||||
}
|
||||
if (workspace.uuid === this.activeWorkspace) {
|
||||
item.setAttribute('disabled', true);
|
||||
item.setAttribute(disableCurrent ? 'disabled' : 'checked', true);
|
||||
}
|
||||
let name = workspace.name;
|
||||
const iconIsSvg = workspace.icon && workspace.icon.endsWith('.svg');
|
||||
@@ -1381,7 +1384,7 @@ class nsZenWorkspaces {
|
||||
this._organizeWorkspaceStripLocations(this.getActiveWorkspaceFromCache()).finally(() => {
|
||||
this.updateTabsContainers();
|
||||
});
|
||||
this.#updateWorkspacesChangeContextMenu();
|
||||
this.updateWorkspacesChangeContextMenu();
|
||||
}
|
||||
|
||||
async reorderWorkspace(id, newPosition) {
|
||||
@@ -2335,24 +2338,49 @@ class nsZenWorkspaces {
|
||||
}
|
||||
}
|
||||
|
||||
#updateWorkspacesChangeContextMenu() {
|
||||
updateWorkspacesChangeContextMenu() {
|
||||
if (gZenWorkspaces.privateWindowOrDisabled) return;
|
||||
const workspaces = this.getWorkspaces();
|
||||
|
||||
const menuPopup = document.getElementById('moveTabOptionsMenu');
|
||||
if (!menuPopup) {
|
||||
let menuPopupID = 'moveTabOptionsMenu';
|
||||
const menuPopup = document.getElementById(menuPopupID);
|
||||
let menubar = document.getElementById('zen-spaces-menubar');
|
||||
if (!menuPopup || !menubar) {
|
||||
return;
|
||||
}
|
||||
for (const item of menuPopup.querySelectorAll('.zen-workspace-context-menu-item')) {
|
||||
let itemsToFill = [menubar.querySelector('menupopup'), menuPopup];
|
||||
for (const popup of itemsToFill) {
|
||||
let isMoveTabPopup = popup.id === menuPopupID;
|
||||
for (const item of popup.querySelectorAll('.zen-workspace-context-menu-item')) {
|
||||
item.remove();
|
||||
}
|
||||
const separator = document.createXULElement('menuseparator');
|
||||
separator.classList.add('zen-workspace-context-menu-item');
|
||||
menuPopup.prepend(separator);
|
||||
for (let workspace of workspaces.reverse()) {
|
||||
const menuItem = this.generateMenuItemForWorkspace(workspace);
|
||||
if (isMoveTabPopup) {
|
||||
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');
|
||||
menuPopup.prepend(menuItem);
|
||||
} else {
|
||||
if (i < 10) {
|
||||
menuItem.setAttribute('key', `zen-workspace-switch-${i + 1}`);
|
||||
}
|
||||
menuItem.addEventListener('command', () => {
|
||||
this.changeWorkspace(workspace);
|
||||
});
|
||||
popup.appendChild(menuItem);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ zen-workspace {
|
||||
.zen-current-workspace-indicator-stack {
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -399,7 +399,7 @@ zen-workspace {
|
||||
transform: rotate(90deg);
|
||||
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;
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -407,19 +407,19 @@ zen-workspace {
|
||||
|
||||
& zen-workspace[haspinnedtabs] .zen-current-workspace-indicator:hover,
|
||||
& zen-workspace[collapsedpinnedtabs] .zen-current-workspace-indicator {
|
||||
.zen-current-workspace-indicator-stack:not([zen-emoji-open='true']) {
|
||||
margin-inline-end: 0;
|
||||
|
||||
.zen-current-workspace-indicator-chevron {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.zen-current-workspace-indicator-stack {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.zen-current-workspace-indicator-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zen-workspace[collapsedpinnedtabs] .zen-current-workspace-indicator-chevron {
|
||||
transform: rotate(0deg);
|
||||
|
||||
Reference in New Issue
Block a user