diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs
index 728547f..53f458f 100644
--- a/src/ZenWorkspaces.mjs
+++ b/src/ZenWorkspaces.mjs
@@ -4,6 +4,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
*/
_lastSelectedWorkspaceTabs = {};
_inChangingWorkspace = false;
+ draggedElement = null;
async init() {
if (!this.shouldHaveWorkspaces) {
@@ -324,6 +325,66 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
element.classList.add('identity-color-' + containerGroup.color);
element.setAttribute('data-usercontextid', containerGroup.userContextId);
}
+ if (this.isReorderModeOn(browser)) {
+ element.setAttribute('draggable', 'true');
+ }
+
+ element.addEventListener('dragstart', function (event) {
+ if (this.isReorderModeOn(browser)) {
+ this.draggedElement = element;
+ event.dataTransfer.effectAllowed = 'move';
+ event.dataTransfer.setData('text/plain', element.getAttribute('zen-workspace-id'));
+ element.classList.add('dragging');
+ } else {
+ event.preventDefault();
+ }
+ }.bind(browser.ZenWorkspaces));
+
+ element.addEventListener('dragover', function (event) {
+ if (this.isReorderModeOn(browser) && this.draggedElement) {
+ event.preventDefault();
+ event.dataTransfer.dropEffect = 'move';
+ }
+ }.bind(browser.ZenWorkspaces));
+
+ element.addEventListener('dragenter', function (event) {
+ if (this.isReorderModeOn(browser) && this.draggedElement) {
+ element.classList.add('dragover');
+ }
+ }.bind(browser.ZenWorkspaces));
+
+ element.addEventListener('dragleave', function (event) {
+ element.classList.remove('dragover');
+ }.bind(browser.ZenWorkspaces));
+
+ element.addEventListener('drop', async function (event) {
+ event.preventDefault();
+ element.classList.remove('dragover');
+ if (this.isReorderModeOn(browser)) {
+ const draggedWorkspaceId = event.dataTransfer.getData('text/plain');
+ const targetWorkspaceId = element.getAttribute('zen-workspace-id');
+ if (draggedWorkspaceId !== targetWorkspaceId) {
+ await this.moveWorkspace(draggedWorkspaceId, targetWorkspaceId);
+ await this._propagateWorkspaceData();
+ }
+ if (this.draggedElement) {
+ this.draggedElement.classList.remove('dragging');
+ this.draggedElement = null;
+ }
+ }
+ }.bind(browser.ZenWorkspaces));
+
+ element.addEventListener('dragend', function (event) {
+ if (this.draggedElement) {
+ this.draggedElement.classList.remove('dragging');
+ this.draggedElement = null;
+ }
+ const workspaceElements = browser.document.querySelectorAll('.zen-workspace-button');
+ for (const elem of workspaceElements) {
+ elem.classList.remove('dragover');
+ }
+ }.bind(browser.ZenWorkspaces));
+
let childs = browser.MozXULElement.parseXULToFragment(`
@@ -333,15 +394,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
-
-
-
-
-
-
-
-
-
+
@@ -349,16 +402,6 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
// use text content instead of innerHTML to avoid XSS
childs.querySelector('.zen-workspace-icon').textContent = browser.ZenWorkspaces.getWorkspaceIcon(workspace);
- childs.querySelector('.zen-workspace-order-up').addEventListener('click', (async (event) => {
- event.stopPropagation();
- event.preventDefault();
- await browser.ZenWorkspaces.moveWorkspaceUp(event, workspace.uuid);
- }).bind(browser.ZenWorkspaces));
- childs.querySelector('.zen-workspace-order-down').addEventListener('click', (async (event) => {
- event.stopPropagation();
- event.preventDefault();
- await browser.ZenWorkspaces.moveWorkspaceDown(event, workspace.uuid);
- }).bind(browser.ZenWorkspaces));
childs.querySelector('.zen-workspace-name').textContent = workspace.name;
if (containerGroup) {
childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel(
@@ -376,6 +419,9 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
}).bind(browser.ZenWorkspaces));
element.appendChild(childs);
element.onclick = (async () => {
+ if (this.isReorderModeOn(browser)) {
+ return; // Return early if reorder mode is on
+ }
if (event.target.closest('.zen-workspace-actions')) {
return; // Ignore clicks on the actions button
}
@@ -410,38 +456,46 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
});
}
- isLastWorkspace(workspace) {
- let workspaces = this._workspaceCache;
- return workspaces.workspaces.indexOf(workspace) === workspaces.workspaces.length - 1;
+ isReorderModeOn(browser) {
+ return browser.document.getElementById('PanelUI-zen-workspaces-list').getAttribute('reorder-mode') === 'true';
}
- isFirstWorkspace(workspace) {
- let workspaces = this._workspaceCache;
- return workspaces.workspaces.indexOf(workspace) === 0;
- }
-
- async moveWorkspaceUp(event, uuid) {
- await ZenWorkspacesStorage.moveWorkspaceUp(uuid);
- await this._propagateWorkspaceData();
- }
-
- async moveWorkspaceDown(event, uuid) {
- await ZenWorkspacesStorage.moveWorkspaceDown(uuid);
- await this._propagateWorkspaceData();
- }
-
- toggleReorderMode () {
+ toggleReorderMode() {
const workspacesList = document.getElementById("PanelUI-zen-workspaces-list");
const reorderModeButton = document.getElementById("PanelUI-zen-workspaces-reorder-mode");
- if(workspacesList.getAttribute('reorder-mode') === 'true' && reorderModeButton.getAttribute('active') === 'true') {
+ const isActive = workspacesList.getAttribute('reorder-mode') === 'true';
+ if (isActive) {
workspacesList.removeAttribute('reorder-mode');
reorderModeButton.removeAttribute('active');
} else {
workspacesList.setAttribute('reorder-mode', 'true');
reorderModeButton.setAttribute('active', 'true');
}
+
+ // Update draggable attribute
+ const workspaceElements = document.querySelectorAll('.zen-workspace-button');
+ workspaceElements.forEach(elem => {
+ if (isActive) {
+ elem.removeAttribute('draggable');
+ } else {
+ elem.setAttribute('draggable', 'true');
+ }
+ });
}
+
+ async moveWorkspace(draggedWorkspaceId, targetWorkspaceId) {
+ const workspaces = (await this._workspaces()).workspaces;
+ const draggedIndex = workspaces.findIndex(w => w.uuid === draggedWorkspaceId);
+ const draggedWorkspace = workspaces.splice(draggedIndex, 1)[0];
+ const targetIndex = workspaces.findIndex(w => w.uuid === targetWorkspaceId);
+ workspaces.splice(targetIndex, 0, draggedWorkspace);
+
+ await ZenWorkspacesStorage.updateWorkspacePositions(workspaces);
+ }
+
+
+
async openWorkspacesDialog(event) {
if (!this.workspaceEnabled) {
return;
diff --git a/src/ZenWorkspacesStorage.mjs b/src/ZenWorkspacesStorage.mjs
index 477d5a9..27b6416 100644
--- a/src/ZenWorkspacesStorage.mjs
+++ b/src/ZenWorkspacesStorage.mjs
@@ -296,163 +296,39 @@ var ZenWorkspacesStorage = {
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
},
- async moveWorkspaceUp(uuid, notifyObservers = true) {
+ async updateWorkspacePositions(workspaces) {
const changedUUIDs = new Set();
- await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.moveWorkspaceUp', async (db) => {
+ await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.updateWorkspacePositions', async (db) => {
await db.executeTransaction(async () => {
const now = Date.now();
- // Get the current workspace
- const currentWorkspaceRows = await db.execute(`
- SELECT uuid, "position" FROM zen_workspaces WHERE uuid = :uuid
- `, { uuid });
+ for (let i = 0; i < workspaces.length; i++) {
+ const workspace = workspaces[i];
+ const newPosition = (i + 1) * 1000;
- if (currentWorkspaceRows.length === 0) {
- // Workspace not found
- return;
- }
-
- const currentWorkspace = {
- uuid: currentWorkspaceRows[0].getResultByName('uuid'),
- position: currentWorkspaceRows[0].getResultByName('position')
- };
-
- // Find the workspace before this one
- const previousWorkspaceRows = await db.execute(`
- SELECT uuid, "position" FROM zen_workspaces
- WHERE "position" < :currentPosition
- ORDER BY "position" DESC
- LIMIT 1
- `, { currentPosition: currentWorkspace.position });
-
- if (previousWorkspaceRows.length === 0) {
- // No previous workspace; already at the top
- return;
- }
-
- const previousWorkspace = {
- uuid: previousWorkspaceRows[0].getResultByName('uuid'),
- position: previousWorkspaceRows[0].getResultByName('position')
- };
-
- // Swap positions
- await db.execute(`
+ await db.execute(`
UPDATE zen_workspaces
SET "position" = :newPosition
WHERE uuid = :uuid
- `, { uuid: currentWorkspace.uuid, newPosition: previousWorkspace.position });
+ `, { newPosition, uuid: workspace.uuid });
- await db.execute(`
- UPDATE zen_workspaces
- SET "position" = :newPosition
- WHERE uuid = :uuid
- `, { uuid: previousWorkspace.uuid, newPosition: currentWorkspace.position });
+ changedUUIDs.add(workspace.uuid);
- // Record the changes
- await db.execute(`
+ // Record the change
+ await db.execute(`
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
- VALUES (:uuid1, :timestamp), (:uuid2, :timestamp)
+ VALUES (:uuid, :timestamp)
`, {
- uuid1: currentWorkspace.uuid,
- uuid2: previousWorkspace.uuid,
- timestamp: Math.floor(now / 1000)
- });
-
- changedUUIDs.add(currentWorkspace.uuid);
- changedUUIDs.add(previousWorkspace.uuid);
+ uuid: workspace.uuid,
+ timestamp: Math.floor(now / 1000)
+ });
+ }
await this.updateLastChangeTimestamp(db);
-
- // Check if reordering is necessary
- if (this.shouldReorderWorkspaces(null, previousWorkspace.position, currentWorkspace.position)) {
- await this.reorderAllWorkspaces(db, changedUUIDs);
- }
});
});
- if (notifyObservers) {
- this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
- }
- },
-
- async moveWorkspaceDown(uuid, notifyObservers = true) {
- const changedUUIDs = new Set();
-
- await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.moveWorkspaceDown', async (db) => {
- await db.executeTransaction(async () => {
- const now = Date.now();
-
- // Get the current workspace
- const currentWorkspaceRows = await db.execute(`
- SELECT uuid, "position" FROM zen_workspaces WHERE uuid = :uuid
- `, { uuid });
-
- if (currentWorkspaceRows.length === 0) {
- // Workspace not found
- return;
- }
-
- const currentWorkspace = {
- uuid: currentWorkspaceRows[0].getResultByName('uuid'),
- position: currentWorkspaceRows[0].getResultByName('position')
- };
-
- // Find the workspace after this one
- const nextWorkspaceRows = await db.execute(`
- SELECT uuid, "position" FROM zen_workspaces
- WHERE "position" > :currentPosition
- ORDER BY "position" ASC
- LIMIT 1
- `, { currentPosition: currentWorkspace.position });
-
- if (nextWorkspaceRows.length === 0) {
- // No next workspace; already at the bottom
- return;
- }
-
- const nextWorkspace = {
- uuid: nextWorkspaceRows[0].getResultByName('uuid'),
- position: nextWorkspaceRows[0].getResultByName('position')
- };
-
- // Swap positions
- await db.execute(`
- UPDATE zen_workspaces
- SET "position" = :newPosition
- WHERE uuid = :uuid
- `, { uuid: currentWorkspace.uuid, newPosition: nextWorkspace.position });
-
- await db.execute(`
- UPDATE zen_workspaces
- SET "position" = :newPosition
- WHERE uuid = :uuid
- `, { uuid: nextWorkspace.uuid, newPosition: currentWorkspace.position });
-
- // Record the changes
- await db.execute(`
- INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
- VALUES (:uuid1, :timestamp), (:uuid2, :timestamp)
- `, {
- uuid1: currentWorkspace.uuid,
- uuid2: nextWorkspace.uuid,
- timestamp: Math.floor(now / 1000)
- });
-
- changedUUIDs.add(currentWorkspace.uuid);
- changedUUIDs.add(nextWorkspace.uuid);
-
- await this.updateLastChangeTimestamp(db);
-
- // Check if reordering is necessary
- if (this.shouldReorderWorkspaces(currentWorkspace.position, nextWorkspace.position, null)) {
- await this.reorderAllWorkspaces(db, changedUUIDs);
- }
- });
- });
-
- if (notifyObservers) {
- this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
- }
+ this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
},
};