mirror of
https://github.com/zen-browser/components.git
synced 2025-07-08 19:29:58 +02:00
Merge remote-tracking branch 'origin/main' into only-flash-sidebar-when-mouse-leaves-the-browser
# Conflicts: # src/ZenCompactMode.mjs
This commit is contained in:
commit
b2361ea3cc
6 changed files with 217 additions and 60 deletions
|
@ -1,5 +1,6 @@
|
||||||
var gZenCompactModeManager = {
|
var gZenCompactModeManager = {
|
||||||
_flashTimeouts: {},
|
_flashTimeouts: {},
|
||||||
|
_evenListeners: [],
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
Services.prefs.addObserver('zen.view.compact', this._updateEvent.bind(this));
|
Services.prefs.addObserver('zen.view.compact', this._updateEvent.bind(this));
|
||||||
|
@ -34,7 +35,12 @@ var gZenCompactModeManager = {
|
||||||
return this._sidebar;
|
return this._sidebar;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addEventListener(callback) {
|
||||||
|
this._evenListeners.push(callback);
|
||||||
|
},
|
||||||
|
|
||||||
_updateEvent() {
|
_updateEvent() {
|
||||||
|
this._evenListeners.forEach((callback) => callback());
|
||||||
Services.prefs.setBoolPref('zen.view.sidebar-expanded.on-hover', false);
|
Services.prefs.setBoolPref('zen.view.sidebar-expanded.on-hover', false);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -112,9 +118,9 @@ var gZenCompactModeManager = {
|
||||||
|
|
||||||
addMouseActions() {
|
addMouseActions() {
|
||||||
for (let i = 0; i < this.hoverableElements.length; i++) {
|
for (let i = 0; i < this.hoverableElements.length; i++) {
|
||||||
const target = this.hoverableElements[i].element;
|
this.hoverableElements[i].addEventListener('mouseenter', (event) => {
|
||||||
target.addEventListener('mouseenter', (event) => {
|
let target = this.hoverableElements[i];
|
||||||
target.setAttribute('zen-user-hover', 'true');
|
target.setAttribute('zen-has-hover', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.hoverableElements[i].keepHoverDuration) {
|
if (this.hoverableElements[i].keepHoverDuration) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ const kZKSActions = {
|
||||||
|
|
||||||
// Workspace actions
|
// Workspace actions
|
||||||
zenChangeWorkspace: ['ZenWorkspaces.changeWorkspaceShortcut()', 'zen-change-workspace', 'workspace-action'],
|
zenChangeWorkspace: ['ZenWorkspaces.changeWorkspaceShortcut()', 'zen-change-workspace', 'workspace-action'],
|
||||||
|
zenChangeWorkspaceBack: ['ZenWorkspaces.changeWorkspaceShortcut(-1)', 'zen-change-workspace-back', 'workspace-action'],
|
||||||
|
|
||||||
// manage actions
|
// manage actions
|
||||||
openNewTab: ['command:cmd_newNavigatorTabNoEvent', 'open-new-tab', 'tab-action'],
|
openNewTab: ['command:cmd_newNavigatorTabNoEvent', 'open-new-tab', 'tab-action'],
|
||||||
|
@ -129,7 +130,8 @@ const kZenDefaultShortcuts = {
|
||||||
zenSplitViewClose: 'Ctrl+Alt+U',
|
zenSplitViewClose: 'Ctrl+Alt+U',
|
||||||
|
|
||||||
// Workspace actions
|
// Workspace actions
|
||||||
zenChangeWorkspace: 'Ctrl+Shift+E',
|
zenChangeWorkspace: 'Ctrl+E',
|
||||||
|
zenChangeWorkspaceBack: 'Ctrl+Shift+E',
|
||||||
|
|
||||||
// Compact mode actions
|
// Compact mode actions
|
||||||
zenToggleCompactMode: 'Ctrl+Alt+C',
|
zenToggleCompactMode: 'Ctrl+Alt+C',
|
||||||
|
|
|
@ -133,6 +133,7 @@ var gZenBrowserManagerSidebar = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const button = document.getElementById('zen-sidepanel-button');
|
const button = document.getElementById('zen-sidepanel-button');
|
||||||
|
if (!button) return;
|
||||||
if (Services.prefs.getBoolPref('zen.sidebar.enabled')) {
|
if (Services.prefs.getBoolPref('zen.sidebar.enabled')) {
|
||||||
button.removeAttribute('hidden');
|
button.removeAttribute('hidden');
|
||||||
} else {
|
} else {
|
||||||
|
@ -187,6 +188,7 @@ var gZenBrowserManagerSidebar = {
|
||||||
|
|
||||||
_updateSidebarButton() {
|
_updateSidebarButton() {
|
||||||
let button = document.getElementById('zen-sidepanel-button');
|
let button = document.getElementById('zen-sidepanel-button');
|
||||||
|
if (!button) return;
|
||||||
if (!document.getElementById('zen-sidebar-web-panel').hasAttribute('hidden')) {
|
if (!document.getElementById('zen-sidebar-web-panel').hasAttribute('hidden')) {
|
||||||
button.setAttribute('open', 'true');
|
button.setAttribute('open', 'true');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -106,6 +106,11 @@ var gZenThemeImporter = new (class {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateStylesheet() {
|
async updateStylesheet() {
|
||||||
|
if (Services.focus.activeWindow !== window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('ZenThemeImporter: Updating Zen themes');
|
||||||
await this.removeStylesheet();
|
await this.removeStylesheet();
|
||||||
|
|
||||||
const themes = Object.values(await ZenThemesCommon.getThemes());
|
const themes = Object.values(await ZenThemesCommon.getThemes());
|
||||||
|
@ -172,8 +177,7 @@ var gZenThemeImporter = new (class {
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToDom(themesWithPreferences) {
|
writeToDom(themesWithPreferences) {
|
||||||
const browser = ZenThemesCommon.currentBrowser;
|
for (const browser of ZenThemesCommon.browsers) {
|
||||||
|
|
||||||
for (const { enabled, preferences, name } of themesWithPreferences) {
|
for (const { enabled, preferences, name } of themesWithPreferences) {
|
||||||
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`;
|
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`;
|
||||||
|
|
||||||
|
@ -231,6 +235,7 @@ var gZenThemeImporter = new (class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async writeStylesheet(themeList) {
|
async writeStylesheet(themeList) {
|
||||||
const themes = [];
|
const themes = [];
|
||||||
|
|
|
@ -97,10 +97,13 @@ var gZenViewSplitter = new (class {
|
||||||
for (const tab of this._data[this.currentView].tabs) {
|
for (const tab of this._data[this.currentView].tabs) {
|
||||||
this.resetTabState(tab, true);
|
this.resetTabState(tab, true);
|
||||||
}
|
}
|
||||||
|
this.removeSplitters();
|
||||||
|
|
||||||
this.currentView = -1;
|
this.currentView = -1;
|
||||||
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
||||||
this.tabBrowserPanel.style.gridTemplateAreas = '';
|
this.tabBrowserPanel.style.gridTemplateAreas = '';
|
||||||
|
this.tabBrowserPanel.style.gridTemplateColumns = '';
|
||||||
|
this.tabBrowserPanel.style.gridTemplateRows = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,6 +171,10 @@ var gZenViewSplitter = new (class {
|
||||||
return this._tabBrowserPanel;
|
return this._tabBrowserPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get minResizeWidth() {
|
||||||
|
return Services.prefs.getIntPref('zen.splitView.min-resize-width');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits a link in a new tab.
|
* Splits a link in a new tab.
|
||||||
*/
|
*/
|
||||||
|
@ -283,6 +290,8 @@ var gZenViewSplitter = new (class {
|
||||||
}
|
}
|
||||||
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
||||||
this.tabBrowserPanel.style.gridTemplateAreas = '';
|
this.tabBrowserPanel.style.gridTemplateAreas = '';
|
||||||
|
this.tabBrowserPanel.style.gridTemplateColumns = '';
|
||||||
|
this.tabBrowserPanel.style.gridTemplateRows = '';
|
||||||
this.setTabsDocShellState(this._data[this.currentView].tabs, false);
|
this.setTabsDocShellState(this._data[this.currentView].tabs, false);
|
||||||
this.currentView = -1;
|
this.currentView = -1;
|
||||||
}
|
}
|
||||||
|
@ -302,6 +311,9 @@ var gZenViewSplitter = new (class {
|
||||||
|
|
||||||
this.setTabsDocShellState(splitData.tabs, true);
|
this.setTabsDocShellState(splitData.tabs, true);
|
||||||
this.updateSplitViewButton(false);
|
this.updateSplitViewButton(false);
|
||||||
|
this.updateGridSizes(splitData);
|
||||||
|
this.applySplitters(splitData.widths.length , splitData.heights.length);
|
||||||
|
this.applyGridSizes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,6 +334,65 @@ var gZenViewSplitter = new (class {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds splitters to tabBrowserPanel
|
||||||
|
*
|
||||||
|
* @param nrOfColumns number of columns in the grid
|
||||||
|
* @param nrOfRows number of rows in the grid
|
||||||
|
*/
|
||||||
|
applySplitters(nrOfColumns, nrOfRows) {
|
||||||
|
this.removeSplitters();
|
||||||
|
const vSplittersNeeded = (nrOfColumns - 1) * nrOfRows;
|
||||||
|
const hSplittersNeeded = nrOfRows - 1;
|
||||||
|
|
||||||
|
const insertSplitter = (i, orient, gridIdx) => {
|
||||||
|
const splitter = document.createElement('div');
|
||||||
|
splitter.className = 'zen-split-view-splitter';
|
||||||
|
splitter.setAttribute('orient', orient);
|
||||||
|
splitter.setAttribute('gridIdx', gridIdx);
|
||||||
|
splitter.style.gridArea = `${orient === 'vertical' ? 'v' : 'h'}Splitter${i}`;
|
||||||
|
splitter.addEventListener('mousedown', this.handleSplitterMouseDown);
|
||||||
|
this.tabBrowserPanel.insertAdjacentElement("afterbegin", splitter);
|
||||||
|
}
|
||||||
|
for (let i = 1; i <= vSplittersNeeded; i++) {
|
||||||
|
insertSplitter(i, 'vertical', Math.floor((i - 1) /nrOfRows) + 1);
|
||||||
|
}
|
||||||
|
for (let i = 1; i <= hSplittersNeeded; i++) {
|
||||||
|
insertSplitter(i, 'horizontal', i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize splitData with default widths and heights if dimensions of grid don't match
|
||||||
|
*
|
||||||
|
* @param {object} splitData - The split data.
|
||||||
|
*/
|
||||||
|
updateGridSizes(splitData) {
|
||||||
|
const tabs = splitData.tabs;
|
||||||
|
const gridType = splitData.gridType;
|
||||||
|
|
||||||
|
let nrOfWidths = 1;
|
||||||
|
let nrOfHeights = 1;
|
||||||
|
if (gridType === 'vsep') {
|
||||||
|
nrOfWidths = tabs.length;
|
||||||
|
} else if (gridType === 'hsep') {
|
||||||
|
nrOfHeights = tabs.length;
|
||||||
|
} else if (gridType === 'grid') {
|
||||||
|
nrOfWidths = tabs.length > 2 ? Math.ceil(tabs.length / 2) : 2;
|
||||||
|
nrOfHeights = tabs.length > 2 ? 2 : 1;
|
||||||
|
}
|
||||||
|
if (splitData.widths?.length !== nrOfWidths || splitData.heights?.length !== nrOfHeights) {
|
||||||
|
splitData.widths = Array(nrOfWidths).fill(100 / nrOfWidths);
|
||||||
|
splitData.heights = Array(nrOfHeights).fill(100 / nrOfHeights);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSplitters() {
|
||||||
|
[...gZenViewSplitter.tabBrowserPanel.children]
|
||||||
|
.filter(e => e.classList.contains('zen-split-view-splitter'))
|
||||||
|
.forEach(s => s.remove());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the grid areas for the tabs.
|
* Calculates the grid areas for the tabs.
|
||||||
*
|
*
|
||||||
|
@ -334,10 +405,10 @@ var gZenViewSplitter = new (class {
|
||||||
return this.calculateGridAreasForGrid(tabs);
|
return this.calculateGridAreasForGrid(tabs);
|
||||||
}
|
}
|
||||||
if (gridType === 'vsep') {
|
if (gridType === 'vsep') {
|
||||||
return `'${tabs.map((_, j) => `tab${j + 1}`).join(' ')}'`;
|
return `'${tabs.slice(0, -1).map((_, j) => `tab${j + 1} vSplitter${j + 1}`).join(' ')} tab${tabs.length}'`;
|
||||||
}
|
}
|
||||||
if (gridType === 'hsep') {
|
if (gridType === 'hsep') {
|
||||||
return tabs.map((_, j) => `'tab${j + 1}'`).join(' ');
|
return tabs.slice(0, -1).map((_, j) => `'tab${j + 1}' 'hSplitter${j + 1}'`).join(' ') + `'tab${tabs.length}`;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -349,24 +420,32 @@ var gZenViewSplitter = new (class {
|
||||||
* @returns {string} The calculated grid areas.
|
* @returns {string} The calculated grid areas.
|
||||||
*/
|
*/
|
||||||
calculateGridAreasForGrid(tabs) {
|
calculateGridAreasForGrid(tabs) {
|
||||||
|
if (tabs.length === 2) {
|
||||||
|
return "'tab1 vSplitter1 tab2'";
|
||||||
|
}
|
||||||
|
|
||||||
const rows = ['', ''];
|
const rows = ['', ''];
|
||||||
tabs.forEach((_, i) => {
|
for (let i = 0; i < tabs.length - 2; i++) {
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
rows[0] += ` tab${i + 1} vSplitter${i + 1}`;
|
||||||
|
} else {
|
||||||
|
rows[1] += ` tab${i + 1} vSplitter${i + 1}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = tabs.length - 2; i < tabs.length; i++) {
|
||||||
if (i % 2 === 0) {
|
if (i % 2 === 0) {
|
||||||
rows[0] += ` tab${i + 1}`;
|
rows[0] += ` tab${i + 1}`;
|
||||||
} else {
|
} else {
|
||||||
rows[1] += ` tab${i + 1}`;
|
rows[1] += ` tab${i + 1}`;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (tabs.length === 2) {
|
|
||||||
return "'tab1 tab2'";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let middleColumn = 'hSplitter1 '.repeat(tabs.length - 1);
|
||||||
if (tabs.length % 2 !== 0) {
|
if (tabs.length % 2 !== 0) {
|
||||||
rows[1] += ` tab${tabs.length}`;
|
rows[1] += ` vSplitter${tabs.length - 1} tab${tabs.length}`;
|
||||||
|
middleColumn += ` tab${tabs.length}`;
|
||||||
}
|
}
|
||||||
|
return `'${rows[0].trim()}' '${middleColumn}' '${rows[1].trim()}'`;
|
||||||
return `'${rows[0].trim()}' '${rows[1].trim()}'`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,6 +480,66 @@ var gZenViewSplitter = new (class {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleSplitterMouseDown = (event) => {
|
||||||
|
const splitData = this._data[this.currentView];
|
||||||
|
|
||||||
|
const isVertical = event.target.getAttribute('orient') === 'vertical';
|
||||||
|
const dimension = isVertical ? 'widths' : 'heights';
|
||||||
|
const clientAxis = isVertical ? 'screenX' : 'screenY';
|
||||||
|
|
||||||
|
const gridIdx = event.target.getAttribute('gridIdx');
|
||||||
|
let prevPosition = event[clientAxis];
|
||||||
|
const dragFunc = (dEvent) => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const movementX = dEvent[clientAxis] - prevPosition;
|
||||||
|
let percentageChange = (movementX / this.tabBrowserPanel.getBoundingClientRect()[isVertical ? 'width' : 'height']) * 100;
|
||||||
|
|
||||||
|
const currentSize = splitData[dimension][gridIdx - 1];
|
||||||
|
const neighborSize = splitData[dimension][gridIdx];
|
||||||
|
if (currentSize < this.minResizeWidth && neighborSize < this.minResizeWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let max = false;
|
||||||
|
if (currentSize + percentageChange < this.minResizeWidth) {
|
||||||
|
percentageChange = this.minResizeWidth - currentSize;
|
||||||
|
max = true;
|
||||||
|
} else if (neighborSize - percentageChange < this.minResizeWidth) {
|
||||||
|
percentageChange = neighborSize - this.minResizeWidth;
|
||||||
|
max = true;
|
||||||
|
}
|
||||||
|
splitData[dimension][gridIdx - 1] += percentageChange;
|
||||||
|
splitData[dimension][gridIdx] -= percentageChange;
|
||||||
|
this.applyGridSizes();
|
||||||
|
if (!max) prevPosition = dEvent[clientAxis];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const stopListeners = () => {
|
||||||
|
removeEventListener('mousemove', dragFunc);
|
||||||
|
removeEventListener('mouseup', stopListeners);
|
||||||
|
setCursor('auto');
|
||||||
|
}
|
||||||
|
addEventListener('mousemove', dragFunc);
|
||||||
|
addEventListener('mouseup', stopListeners);
|
||||||
|
setCursor(isVertical ? 'ew-resize' : 'n-resize');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the grid column and row sizes
|
||||||
|
*/
|
||||||
|
applyGridSizes() {
|
||||||
|
const splitData = this._data[this.currentView];
|
||||||
|
const columnGap = 'var(--zen-split-column-gap)';
|
||||||
|
const rowGap = 'var(--zen-split-row-gap)';
|
||||||
|
|
||||||
|
this.tabBrowserPanel.style.gridTemplateColumns = splitData.widths.slice(0, -1).map(
|
||||||
|
(w) => `calc(${w}% - ${columnGap} * ${splitData.widths.length - 1}/${splitData.widths.length}) ${columnGap}`
|
||||||
|
).join(' ');
|
||||||
|
|
||||||
|
this.tabBrowserPanel.style.gridTemplateRows = splitData.heights.slice(0, -1).map(
|
||||||
|
(h) => `calc(${h}% - ${rowGap} * ${splitData.heights.length - 1}/${splitData.heights.length}) ${rowGap}`
|
||||||
|
).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the docshell state for the tabs.
|
* Sets the docshell state for the tabs.
|
||||||
*
|
*
|
||||||
|
|
|
@ -515,6 +515,8 @@ var ZenWorkspaces = {
|
||||||
icon?.removeAttribute('selected');
|
icon?.removeAttribute('selected');
|
||||||
await this.createAndSaveWorkspace(workspaceName, false, icon?.label);
|
await this.createAndSaveWorkspace(workspaceName, false, icon?.label);
|
||||||
document.getElementById('PanelUI-zen-workspaces').hidePopup(true);
|
document.getElementById('PanelUI-zen-workspaces').hidePopup(true);
|
||||||
|
await this._updateWorkspacesButton();
|
||||||
|
await this._propagateWorkspaceData();
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveWorkspaceFromEdit() {
|
async saveWorkspaceFromEdit() {
|
||||||
|
@ -773,12 +775,13 @@ var ZenWorkspaces = {
|
||||||
await this.openEditDialog(this._contextMenuId);
|
await this.openEditDialog(this._contextMenuId);
|
||||||
},
|
},
|
||||||
|
|
||||||
async changeWorkspaceShortcut() {
|
async changeWorkspaceShortcut(offset = 1) {
|
||||||
// Cycle through workspaces
|
// Cycle through workspaces
|
||||||
let workspaces = await this._workspaces();
|
let workspaces = await this._workspaces();
|
||||||
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];
|
// note: offset can be negative
|
||||||
|
let nextWorkspace = workspaces.workspaces[(workspaceIndex + offset + workspaces.workspaces.length) % workspaces.workspaces.length];
|
||||||
this.changeWorkspace(nextWorkspace);
|
this.changeWorkspace(nextWorkspace);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue