mirror of
https://github.com/zen-browser/components.git
synced 2025-07-08 04:59:58 +02:00
feat(workspaces): add quick switch functionality
Adds a new quick switch feature for workspaces that allows users to quickly switch between workspaces using the workspace icons or mouse wheel. The quick switch feature displays a list of all workspaces in a container next to the browser tabs, making it easier to switch between them. Users can also use SHIFT + mouse wheel while hovering the container to quickly navigate between workspaces. This feature improves the user experience by providing a more efficient way to switch between workspaces.
This commit is contained in:
parent
0f775df5e6
commit
9e1d0d0080
1 changed files with 193 additions and 3 deletions
|
@ -32,6 +32,10 @@ var ZenWorkspaces = {
|
||||||
return this.workspaceEnabled;
|
return this.workspaceEnabled;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get workspaceQuickSwitchEnabled() {
|
||||||
|
return Services.prefs.getBoolPref('zen.workspaces.quick-switch', false);
|
||||||
|
},
|
||||||
|
|
||||||
getActiveWorkspaceFromCache() {
|
getActiveWorkspaceFromCache() {
|
||||||
return this._workspaceCache.workspaces.find((workspace) => workspace.used);
|
return this._workspaceCache.workspaces.find((workspace) => workspace.used);
|
||||||
},
|
},
|
||||||
|
@ -63,9 +67,25 @@ var ZenWorkspaces = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async onWorkspacesQuickSwitchEnabledChanged() {
|
||||||
|
if (this.workspaceQuickSwitchEnabled) {
|
||||||
|
await this.initializeWorkspacesQuickSwitch();
|
||||||
|
} else {
|
||||||
|
document.getElementById('zen-workspace-quick-switch-container')?.remove();
|
||||||
|
|
||||||
|
let workspacesButton = document.getElementById('zen-workspaces-button');
|
||||||
|
if (workspacesButton) {
|
||||||
|
workspacesButton.classList.remove('zen-workspaces-button-minimalist');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async initializeWorkspaces() {
|
async initializeWorkspaces() {
|
||||||
Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this));
|
Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this));
|
||||||
|
Services.prefs.addObserver('zen.workspaces.quick-switch', this.onWorkspacesQuickSwitchEnabledChanged.bind(this));
|
||||||
|
|
||||||
this.initializeWorkspacesButton();
|
this.initializeWorkspacesButton();
|
||||||
|
await this.initializeWorkspacesQuickSwitch();
|
||||||
let file = new FileUtils.File(this._storeFile);
|
let file = new FileUtils.File(this._storeFile);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
await IOUtils.writeJSON(this._storeFile, {});
|
await IOUtils.writeJSON(this._storeFile, {});
|
||||||
|
@ -182,6 +202,7 @@ var ZenWorkspaces = {
|
||||||
await this.unsafeSaveWorkspaces(json);
|
await this.unsafeSaveWorkspaces(json);
|
||||||
await this._propagateWorkspaceData();
|
await this._propagateWorkspaceData();
|
||||||
await this._updateWorkspacesChangeContextMenu();
|
await this._updateWorkspacesChangeContextMenu();
|
||||||
|
await this._updateQuickSwitchState();
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveWorkspaces() {
|
async saveWorkspaces() {
|
||||||
|
@ -378,6 +399,154 @@ var ZenWorkspaces = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async initializeWorkspacesQuickSwitch() {
|
||||||
|
if (!this.workspaceEnabled || !this.workspaceQuickSwitchEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or update the workspace quick switch container to show a list of all workspaces
|
||||||
|
let workspaceListContainer = document.getElementById('zen-workspace-quick-switch-container');
|
||||||
|
|
||||||
|
// If the container doesn't exist, create one
|
||||||
|
if (!workspaceListContainer) {
|
||||||
|
workspaceListContainer = document.createElement('vbox');
|
||||||
|
workspaceListContainer.id = 'zen-workspace-quick-switch-container';
|
||||||
|
let browserTabs = document.getElementById('tabbrowser-tabs');
|
||||||
|
browserTabs.insertAdjacentElement('afterend', workspaceListContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add alternate styling to workspaces button
|
||||||
|
let workspacesButton = document.getElementById('zen-workspaces-button');
|
||||||
|
|
||||||
|
if (workspacesButton) {
|
||||||
|
workspacesButton.classList.add('zen-workspaces-button-minimalist');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear any existing content in the container
|
||||||
|
workspaceListContainer.innerHTML = '';
|
||||||
|
|
||||||
|
// Get all the workspaces and create buttons for them
|
||||||
|
let workspaces = await this._workspaces();
|
||||||
|
|
||||||
|
for (let workspace of workspaces.workspaces) {
|
||||||
|
this._createOrUpdateWorkspaceButton(workspace, workspaceListContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add event listener for quicker workspace change
|
||||||
|
workspaceListContainer.addEventListener('wheel', this._handleShiftScroll.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
async _updateQuickSwitchState() {
|
||||||
|
let workspaceListContainer = document.getElementById('zen-workspace-quick-switch-container');
|
||||||
|
if (!workspaceListContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let existingButtons = workspaceListContainer.querySelectorAll('.zen-workspace-button');
|
||||||
|
|
||||||
|
let workspaces = await this._workspaces();
|
||||||
|
let currentWorkspaceIds = workspaces.workspaces.map(workspace => workspace.uuid);
|
||||||
|
|
||||||
|
// Remove buttons that no longer exist
|
||||||
|
for (let button of existingButtons) {
|
||||||
|
let workspaceId = button.getAttribute('zen-workspace-id');
|
||||||
|
if (!currentWorkspaceIds.includes(workspaceId)) {
|
||||||
|
button.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add or update existing buttons
|
||||||
|
for (let workspace of workspaces.workspaces) {
|
||||||
|
this._createOrUpdateWorkspaceButton(workspace, workspaceListContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_createOrUpdateWorkspaceButton(workspace, container) {
|
||||||
|
let button = container.querySelector(`.zen-workspace-button[zen-workspace-id="${workspace.uuid}"]`);
|
||||||
|
|
||||||
|
// If the button doesn't exist, create it
|
||||||
|
if (!button) {
|
||||||
|
button = document.createElement('div');
|
||||||
|
button.className = 'zen-workspace-button';
|
||||||
|
button.setAttribute('zen-workspace-id', workspace.uuid);
|
||||||
|
|
||||||
|
// Add click event to open the workspace on click
|
||||||
|
button.onclick = async (event) => {
|
||||||
|
await this.changeWorkspace(workspace);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add context menu event
|
||||||
|
button.oncontextmenu = async (e) => {
|
||||||
|
e.preventDefault(); // Prevent default context menu
|
||||||
|
this._contextMenuId = workspace.uuid;
|
||||||
|
const popup = document.getElementById('zenWorkspaceActionsMenu');
|
||||||
|
|
||||||
|
// add attribute "hide-edit-workspace" to popup
|
||||||
|
popup.setAttribute('hide-edit-workspace', 'true');
|
||||||
|
|
||||||
|
popup.openPopup(button, 'after_end');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Append the workspace button to the container
|
||||||
|
container.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the icon has changed or if it's missing, and update it if necessary
|
||||||
|
let newIcon = this.workspaceHasIcon(workspace) ? this.getWorkspaceIcon(workspace) : '⬤';
|
||||||
|
if (button.innerHTML !== newIcon) {
|
||||||
|
button.innerHTML = newIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update active state
|
||||||
|
if (workspace.used) {
|
||||||
|
button.classList.add('zen-workspace-button-active');
|
||||||
|
} else {
|
||||||
|
button.classList.remove('zen-workspace-button-active');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async _handleShiftScroll(event) {
|
||||||
|
// if hold shift key, go to the next or previous workspace
|
||||||
|
if (event.shiftKey) {
|
||||||
|
// If scrolling down (deltaY > 0), go to the next workspace
|
||||||
|
if (event.deltaY > 0) {
|
||||||
|
await this._goToNextWorkspace();
|
||||||
|
}
|
||||||
|
// If scrolling up (deltaY < 0), go to the previous workspace
|
||||||
|
else if (event.deltaY < 0) {
|
||||||
|
await this._goToPreviousWorkspace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle horizontal scrolling
|
||||||
|
if (event.deltaX > 0) {
|
||||||
|
await this._goToNextWorkspace();
|
||||||
|
} else if (event.deltaX < 0) {
|
||||||
|
await this._goToPreviousWorkspace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async _goToPreviousWorkspace() {
|
||||||
|
let workspaces = await this._workspaces();
|
||||||
|
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||||
|
let workspaceIndex = workspaces.workspaces.indexOf(activeWorkspace);
|
||||||
|
|
||||||
|
// Go to the previous workspace, or wrap around to the last one if at the beginning
|
||||||
|
let previousWorkspace = workspaces.workspaces[workspaceIndex - 1] || workspaces.workspaces[workspaces.workspaces.length - 1];
|
||||||
|
await this.changeWorkspace(previousWorkspace);
|
||||||
|
},
|
||||||
|
|
||||||
|
async _goToNextWorkspace() {
|
||||||
|
let workspaces = await this._workspaces();
|
||||||
|
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||||
|
let workspaceIndex = workspaces.workspaces.indexOf(activeWorkspace);
|
||||||
|
|
||||||
|
// Go to the next workspace, or wrap around to the first one if at the end
|
||||||
|
let nextWorkspace = workspaces.workspaces[workspaceIndex + 1] || workspaces.workspaces[0];
|
||||||
|
await this.changeWorkspace(nextWorkspace);
|
||||||
|
},
|
||||||
|
|
||||||
// Workspaces management
|
// Workspaces management
|
||||||
|
|
||||||
get _workspaceCreateInput() {
|
get _workspaceCreateInput() {
|
||||||
|
@ -454,6 +623,7 @@ var ZenWorkspaces = {
|
||||||
workspaceData.icon = icon?.label;
|
workspaceData.icon = icon?.label;
|
||||||
await this.saveWorkspace(workspaceData);
|
await this.saveWorkspace(workspaceData);
|
||||||
await this._updateWorkspacesButton();
|
await this._updateWorkspacesButton();
|
||||||
|
await this._updateQuickSwitchState();
|
||||||
await this._propagateWorkspaceData();
|
await this._propagateWorkspaceData();
|
||||||
this.closeWorkspacesSubView();
|
this.closeWorkspacesSubView();
|
||||||
},
|
},
|
||||||
|
@ -538,6 +708,7 @@ var ZenWorkspaces = {
|
||||||
await this._updateWorkspacesButton();
|
await this._updateWorkspacesButton();
|
||||||
await this._propagateWorkspaceData();
|
await this._propagateWorkspaceData();
|
||||||
await this._updateWorkspacesChangeContextMenu();
|
await this._updateWorkspacesChangeContextMenu();
|
||||||
|
await this._updateQuickSwitchState();
|
||||||
|
|
||||||
document.getElementById('tabbrowser-tabs')._positionPinnedTabs();
|
document.getElementById('tabbrowser-tabs')._positionPinnedTabs();
|
||||||
},
|
},
|
||||||
|
@ -616,7 +787,15 @@ var ZenWorkspaces = {
|
||||||
// Context menu management
|
// Context menu management
|
||||||
|
|
||||||
_contextMenuId: null,
|
_contextMenuId: null,
|
||||||
async updateContextMenu(_) {
|
async updateContextMenu(e = null) {
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
// check if context menu has "hide-edit-workspace" attribute, if so, hide edit workspace menu item
|
||||||
|
if (e.hasAttribute('hide-edit-workspace') && e.querySelector('#context_zenEditWorkspace')) {
|
||||||
|
e.querySelector('#context_zenEditWorkspace').hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.assert(this._contextMenuId, 'No context menu ID set');
|
console.assert(this._contextMenuId, 'No context menu ID set');
|
||||||
document
|
document
|
||||||
.querySelector(`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`)
|
.querySelector(`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`)
|
||||||
|
@ -654,7 +833,18 @@ var ZenWorkspaces = {
|
||||||
await this._propagateWorkspaceData();
|
await this._propagateWorkspaceData();
|
||||||
},
|
},
|
||||||
|
|
||||||
onContextMenuClose() {
|
onContextMenuClose(e = null) {
|
||||||
|
|
||||||
|
// remove "hide-edit-workspace" attribute from context menu
|
||||||
|
if (e && e.hasAttribute('hide-edit-workspace')) {
|
||||||
|
e.removeAttribute('hide-edit-workspace');
|
||||||
|
}
|
||||||
|
|
||||||
|
// un-hide edit workspace menu
|
||||||
|
if (e.querySelector('#context_zenEditWorkspace')) {
|
||||||
|
e.querySelector('#context_zenEditWorkspace').hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
let target = document.querySelector(
|
let target = document.querySelector(
|
||||||
`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`
|
`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`
|
||||||
);
|
);
|
||||||
|
@ -697,7 +887,7 @@ var ZenWorkspaces = {
|
||||||
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||||
let workspaceIndex = workspaces.workspaces.indexOf(activeWorkspace);
|
let workspaceIndex = workspaces.workspaces.indexOf(activeWorkspace);
|
||||||
let nextWorkspace = workspaces.workspaces[workspaceIndex + 1] || workspaces.workspaces[0];
|
let nextWorkspace = workspaces.workspaces[workspaceIndex + 1] || workspaces.workspaces[0];
|
||||||
this.changeWorkspace(nextWorkspace);
|
await this.changeWorkspace(nextWorkspace);
|
||||||
},
|
},
|
||||||
|
|
||||||
_initializeWorkspaceTabContextMenus() {
|
_initializeWorkspaceTabContextMenus() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue