mirror of
https://github.com/zen-browser/components.git
synced 2025-07-08 09:00:00 +02:00
Re-implemented workspace reordering to be based on dragging.
This commit is contained in:
parent
17cace3097
commit
c8deb36100
2 changed files with 109 additions and 179 deletions
|
@ -4,6 +4,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
*/
|
*/
|
||||||
_lastSelectedWorkspaceTabs = {};
|
_lastSelectedWorkspaceTabs = {};
|
||||||
_inChangingWorkspace = false;
|
_inChangingWorkspace = false;
|
||||||
|
draggedElement = null;
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
if (!this.shouldHaveWorkspaces) {
|
if (!this.shouldHaveWorkspaces) {
|
||||||
|
@ -324,6 +325,66 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
element.classList.add('identity-color-' + containerGroup.color);
|
element.classList.add('identity-color-' + containerGroup.color);
|
||||||
element.setAttribute('data-usercontextid', containerGroup.userContextId);
|
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(`
|
let childs = browser.MozXULElement.parseXULToFragment(`
|
||||||
<div class="zen-workspace-icon">
|
<div class="zen-workspace-icon">
|
||||||
</div>
|
</div>
|
||||||
|
@ -333,15 +394,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
<div class="zen-workspace-container" ${containerGroup ? '' : 'hidden="true"'}>
|
<div class="zen-workspace-container" ${containerGroup ? '' : 'hidden="true"'}>
|
||||||
</div>
|
</div>
|
||||||
</vbox>
|
</vbox>
|
||||||
<div style="margin-left: auto;display: flex; align-items: center; gap: 5px; flex-shrink: 0;">
|
<image class="toolbarbutton-icon zen-workspace-actions-reorder-icon" ></image>
|
||||||
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-order-up zen-workspace-reorder-button">
|
|
||||||
<image class="toolbarbutton-icon" id="zen-workspace-actions-order-up-icon"></image>
|
|
||||||
</toolbarbutton>
|
|
||||||
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-order-down zen-workspace-reorder-button">
|
|
||||||
<image class="toolbarbutton-icon" id="zen-workspace-actions-order-down-icon"></image>
|
|
||||||
</toolbarbutton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-actions">
|
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-actions">
|
||||||
<image class="toolbarbutton-icon" id="zen-workspace-actions-menu-icon"></image>
|
<image class="toolbarbutton-icon" id="zen-workspace-actions-menu-icon"></image>
|
||||||
</toolbarbutton>
|
</toolbarbutton>
|
||||||
|
@ -349,16 +402,6 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
|
|
||||||
// use text content instead of innerHTML to avoid XSS
|
// use text content instead of innerHTML to avoid XSS
|
||||||
childs.querySelector('.zen-workspace-icon').textContent = browser.ZenWorkspaces.getWorkspaceIcon(workspace);
|
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;
|
childs.querySelector('.zen-workspace-name').textContent = workspace.name;
|
||||||
if (containerGroup) {
|
if (containerGroup) {
|
||||||
childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel(
|
childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel(
|
||||||
|
@ -376,6 +419,9 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
}).bind(browser.ZenWorkspaces));
|
}).bind(browser.ZenWorkspaces));
|
||||||
element.appendChild(childs);
|
element.appendChild(childs);
|
||||||
element.onclick = (async () => {
|
element.onclick = (async () => {
|
||||||
|
if (this.isReorderModeOn(browser)) {
|
||||||
|
return; // Return early if reorder mode is on
|
||||||
|
}
|
||||||
if (event.target.closest('.zen-workspace-actions')) {
|
if (event.target.closest('.zen-workspace-actions')) {
|
||||||
return; // Ignore clicks on the actions button
|
return; // Ignore clicks on the actions button
|
||||||
}
|
}
|
||||||
|
@ -410,37 +456,45 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isLastWorkspace(workspace) {
|
isReorderModeOn(browser) {
|
||||||
let workspaces = this._workspaceCache;
|
return browser.document.getElementById('PanelUI-zen-workspaces-list').getAttribute('reorder-mode') === 'true';
|
||||||
return workspaces.workspaces.indexOf(workspace) === workspaces.workspaces.length - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isFirstWorkspace(workspace) {
|
toggleReorderMode() {
|
||||||
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 () {
|
|
||||||
const workspacesList = document.getElementById("PanelUI-zen-workspaces-list");
|
const workspacesList = document.getElementById("PanelUI-zen-workspaces-list");
|
||||||
const reorderModeButton = document.getElementById("PanelUI-zen-workspaces-reorder-mode");
|
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');
|
workspacesList.removeAttribute('reorder-mode');
|
||||||
reorderModeButton.removeAttribute('active');
|
reorderModeButton.removeAttribute('active');
|
||||||
} else {
|
} else {
|
||||||
workspacesList.setAttribute('reorder-mode', 'true');
|
workspacesList.setAttribute('reorder-mode', 'true');
|
||||||
reorderModeButton.setAttribute('active', '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) {
|
async openWorkspacesDialog(event) {
|
||||||
if (!this.workspaceEnabled) {
|
if (!this.workspaceEnabled) {
|
||||||
|
|
|
@ -296,163 +296,39 @@ var ZenWorkspacesStorage = {
|
||||||
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
|
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
async moveWorkspaceUp(uuid, notifyObservers = true) {
|
async updateWorkspacePositions(workspaces) {
|
||||||
const changedUUIDs = new Set();
|
const changedUUIDs = new Set();
|
||||||
|
|
||||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.moveWorkspaceUp', async (db) => {
|
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.updateWorkspacePositions', async (db) => {
|
||||||
await db.executeTransaction(async () => {
|
await db.executeTransaction(async () => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
// Get the current workspace
|
for (let i = 0; i < workspaces.length; i++) {
|
||||||
const currentWorkspaceRows = await db.execute(`
|
const workspace = workspaces[i];
|
||||||
SELECT uuid, "position" FROM zen_workspaces WHERE uuid = :uuid
|
const newPosition = (i + 1) * 1000;
|
||||||
`, { uuid });
|
|
||||||
|
|
||||||
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(`
|
|
||||||
UPDATE zen_workspaces
|
|
||||||
SET "position" = :newPosition
|
|
||||||
WHERE uuid = :uuid
|
|
||||||
`, { uuid: currentWorkspace.uuid, newPosition: previousWorkspace.position });
|
|
||||||
|
|
||||||
await db.execute(`
|
await db.execute(`
|
||||||
UPDATE zen_workspaces
|
UPDATE zen_workspaces
|
||||||
SET "position" = :newPosition
|
SET "position" = :newPosition
|
||||||
WHERE uuid = :uuid
|
WHERE uuid = :uuid
|
||||||
`, { uuid: previousWorkspace.uuid, newPosition: currentWorkspace.position });
|
`, { newPosition, uuid: workspace.uuid });
|
||||||
|
|
||||||
// Record the changes
|
changedUUIDs.add(workspace.uuid);
|
||||||
|
|
||||||
|
// Record the change
|
||||||
await db.execute(`
|
await db.execute(`
|
||||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||||
VALUES (:uuid1, :timestamp), (:uuid2, :timestamp)
|
VALUES (:uuid, :timestamp)
|
||||||
`, {
|
`, {
|
||||||
uuid1: currentWorkspace.uuid,
|
uuid: workspace.uuid,
|
||||||
uuid2: previousWorkspace.uuid,
|
|
||||||
timestamp: Math.floor(now / 1000)
|
timestamp: Math.floor(now / 1000)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
changedUUIDs.add(currentWorkspace.uuid);
|
|
||||||
changedUUIDs.add(previousWorkspace.uuid);
|
|
||||||
|
|
||||||
await this.updateLastChangeTimestamp(db);
|
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));
|
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));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue