mirror of
https://github.com/zen-browser/desktop.git
synced 2026-01-06 21:37:50 +00:00
Merge pull request #5435 from neurokitti/improved-color-picker
Improved color picker
This commit is contained in:
2
l10n
2
l10n
Submodule l10n updated: 19e2af33c4...83464a5d35
@@ -97,8 +97,8 @@
|
||||
|
||||
#PanelUI-zen-gradient-generator-color-custom-add {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -239,13 +239,13 @@
|
||||
& .zen-theme-picker-dot {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background: var(--zen-theme-picker-dot-color);
|
||||
box-shadow: 0 0 0 2px var(--zen-themed-toolbar-bg);
|
||||
cursor: pointer;
|
||||
border: 2px solid #fff;
|
||||
border: 3px solid #fff;
|
||||
animation: zen-theme-picker-dot-animation 0.5s;
|
||||
transition: transform 0.2s;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
currentOpacity = 0.5;
|
||||
currentRotation = 45;
|
||||
|
||||
numberOfDots = 0;
|
||||
dots = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -171,10 +170,13 @@
|
||||
}
|
||||
|
||||
onCustomColorKeydown(event) {
|
||||
//checks for enter key for custom colors
|
||||
// Check for Enter key to add custom colors
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
this.addCustomColor();
|
||||
|
||||
let colorPositions = this.calculateCompliments(this.dots);
|
||||
this.handleColorPositions(colorPositions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +185,7 @@
|
||||
themePicker.style.setProperty('--zen-theme-picker-gradient-image', `url(${ZenThemePicker.GRADIENT_DISPLAY_URL})`);
|
||||
themePicker.addEventListener('mousemove', this.onDotMouseMove.bind(this));
|
||||
themePicker.addEventListener('mouseup', this.onDotMouseUp.bind(this));
|
||||
themePicker.addEventListener('mousedown', this.onDotMouseDown.bind(this));
|
||||
themePicker.addEventListener('click', this.onThemePickerClick.bind(this));
|
||||
}
|
||||
|
||||
@@ -233,67 +236,31 @@
|
||||
dot.style.opacity = 0;
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', color.c);
|
||||
} else {
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${r}, ${g}, ${b})`);
|
||||
const { x, y } = this.calculateInitialPosition(color);
|
||||
const dotPad = this.panel.querySelector('.zen-theme-picker-gradient');
|
||||
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('zen-theme-picker-dot');
|
||||
|
||||
dot.style.left = `${x * 100}%`;
|
||||
dot.style.top = `${y * 100}%`;
|
||||
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
|
||||
|
||||
dotPad.appendChild(dot);
|
||||
let id = this.dots.length;
|
||||
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${r}, ${g}, ${b})`);
|
||||
|
||||
this.dots.push({
|
||||
ID: id,
|
||||
Element: dot,
|
||||
Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) },
|
||||
});
|
||||
}
|
||||
this.panel.querySelector('.zen-theme-picker-gradient').appendChild(dot);
|
||||
if (!fromWorkspace) {
|
||||
this.updateCurrentWorkspace(true);
|
||||
}
|
||||
}
|
||||
|
||||
onDotMouseDown(event) {
|
||||
event.preventDefault();
|
||||
if (event.button === 2) {
|
||||
return;
|
||||
}
|
||||
this.dragging = true;
|
||||
this.draggedDot = event.target;
|
||||
this.draggedDot.style.zIndex = 1;
|
||||
this.draggedDot.classList.add('dragging');
|
||||
|
||||
// Store the starting position of the drag
|
||||
this.dragStartPosition = {
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
};
|
||||
}
|
||||
|
||||
onDotMouseMove(event) {
|
||||
if (this.dragging) {
|
||||
event.preventDefault();
|
||||
const rect = this.panel.querySelector('.zen-theme-picker-gradient').getBoundingClientRect();
|
||||
const padding = 90; // each side
|
||||
// do NOT let the ball be draged outside of an imaginary circle. You can drag it anywhere inside the circle
|
||||
// if the distance between the center of the circle and the dragged ball is bigger than the radius, then the ball
|
||||
// should be placed on the edge of the circle. If it's inside the circle, then the ball just follows the mouse
|
||||
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const radius = (rect.width - padding) / 2;
|
||||
let pixelX = event.clientX;
|
||||
let pixelY = event.clientY;
|
||||
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
|
||||
if (distance > radius) {
|
||||
const angle = Math.atan2(pixelY - centerY, pixelX - centerX);
|
||||
pixelX = centerX + Math.cos(angle) * radius;
|
||||
pixelY = centerY + Math.sin(angle) * radius;
|
||||
}
|
||||
|
||||
// set the location of the dot in pixels
|
||||
const relativeX = pixelX - rect.left;
|
||||
const relativeY = pixelY - rect.top;
|
||||
this.draggedDot.style.left = `${relativeX}px`;
|
||||
this.draggedDot.style.top = `${relativeY}px`;
|
||||
const color = this.getColorFromPosition(relativeX, relativeY);
|
||||
this.draggedDot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
}
|
||||
|
||||
addColorToCustomList(color) {
|
||||
const listItems = window.MozXULElement.parseXULToFragment(`
|
||||
<hbox class="zen-theme-picker-custom-list-item">
|
||||
@@ -331,14 +298,162 @@
|
||||
await this.updateCurrentWorkspace();
|
||||
}
|
||||
|
||||
spawnDot(relativePosition, primary = false) {
|
||||
const dotPad = this.panel.querySelector('.zen-theme-picker-gradient');
|
||||
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('zen-theme-picker-dot');
|
||||
|
||||
dot.style.left = `${relativePosition.x}px`;
|
||||
dot.style.top = `${relativePosition.y}px`;
|
||||
|
||||
dotPad.appendChild(dot);
|
||||
|
||||
let id = this.dots.length;
|
||||
|
||||
if (primary === true) {
|
||||
id = 0;
|
||||
const existingPrimaryDot = this.dots.find((d) => d.ID === 0);
|
||||
if (existingPrimaryDot) {
|
||||
existingPrimaryDot.ID = this.dots.length;
|
||||
}
|
||||
}
|
||||
|
||||
const colorFromPos = this.getColorFromPosition(relativePosition.x, relativePosition.y);
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`);
|
||||
|
||||
this.dots.push({
|
||||
ID: id,
|
||||
Element: dot,
|
||||
Position: { x: parseFloat(dot.style.left), y: parseFloat(dot.style.top) },
|
||||
});
|
||||
}
|
||||
|
||||
calculateCompliments(dots, dotRemoved = false) {
|
||||
const colorHarmonies = [
|
||||
{ type: 'complementary', angles: [180] },
|
||||
{ type: 'splitComplementary', angles: [150, 210] },
|
||||
{ type: 'analogous', angles: [30, 330] },
|
||||
{ type: 'triadic', angles: [120, 240] },
|
||||
];
|
||||
|
||||
function getColorHarmonyType(numDots) {
|
||||
if (dotRemoved) {
|
||||
return colorHarmonies.find((harmony) => harmony.angles.length === numDots - 1);
|
||||
}
|
||||
return colorHarmonies.find((harmony) => harmony.angles.length === numDots);
|
||||
}
|
||||
|
||||
// rule: if the data will be inputed into an argument it should be stored as an object else not
|
||||
function getAngleFromPosition(position, centerPosition) {
|
||||
let deltaX = position.x - centerPosition.x;
|
||||
let deltaY = position.y - centerPosition.y;
|
||||
let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
|
||||
return (angle + 360) % 360;
|
||||
}
|
||||
|
||||
function getDistanceFromCenter(position, centerPosition) {
|
||||
const deltaX = position.x - centerPosition.x;
|
||||
const deltaY = position.y - centerPosition.y;
|
||||
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
}
|
||||
|
||||
const dotPad = this.panel.querySelector('.zen-theme-picker-gradient');
|
||||
const rect = dotPad.getBoundingClientRect();
|
||||
const padding = 90;
|
||||
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const radius = (rect.width - padding) / 2;
|
||||
const centerPosition = { x: rect.width / 2, y: rect.height / 2 };
|
||||
|
||||
const harmonyAngles = getColorHarmonyType(dots.length);
|
||||
if (!harmonyAngles || harmonyAngles.angles.length === 0) return [];
|
||||
|
||||
let primaryDot = dots.find((dot) => dot.ID === 0);
|
||||
|
||||
if (!primaryDot) return [];
|
||||
|
||||
const baseAngle = getAngleFromPosition(primaryDot.Position, centerPosition);
|
||||
|
||||
let distance = getDistanceFromCenter(primaryDot.Position, centerPosition);
|
||||
|
||||
if (distance > radius) {
|
||||
distance = radius;
|
||||
}
|
||||
|
||||
dots.sort((a, b) => a.ID - b.ID);
|
||||
|
||||
let updatedDots = [];
|
||||
harmonyAngles.angles.forEach((angleOffset, index) => {
|
||||
let newAngle = (baseAngle + angleOffset) % 360;
|
||||
let radian = (newAngle * Math.PI) / 180;
|
||||
|
||||
let newPosition = {
|
||||
x: centerPosition.x + distance * Math.cos(radian),
|
||||
y: centerPosition.y + distance * Math.sin(radian),
|
||||
};
|
||||
|
||||
updatedDots.push({ ID: index + 1, Position: newPosition });
|
||||
});
|
||||
|
||||
return updatedDots;
|
||||
}
|
||||
|
||||
handleColorPositions(colorPositions) {
|
||||
colorPositions.sort((a, b) => a.ID - b.ID);
|
||||
|
||||
const existingPrimaryDot = this.dots.find((d) => d.ID === 0);
|
||||
|
||||
if (existingPrimaryDot) {
|
||||
existingPrimaryDot.Element.style.zIndex = 999;
|
||||
const colorFromPos = this.getColorFromPosition(existingPrimaryDot.Position.x, existingPrimaryDot.Position.y);
|
||||
existingPrimaryDot.Element.style.setProperty(
|
||||
'--zen-theme-picker-dot-color',
|
||||
`rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`
|
||||
);
|
||||
}
|
||||
colorPositions.forEach((dotPosition) => {
|
||||
const existingDot = this.dots.find((dot) => dot.ID === dotPosition.ID);
|
||||
|
||||
if (existingDot) {
|
||||
existingDot.Position = dotPosition.Position;
|
||||
existingDot.Element.style.left = `${dotPosition.Position.x}px`;
|
||||
existingDot.Element.style.top = `${dotPosition.Position.y}px`;
|
||||
const colorFromPos = this.getColorFromPosition(dotPosition.Position.x, dotPosition.Position.y);
|
||||
existingDot.Element.style.setProperty(
|
||||
'--zen-theme-picker-dot-color',
|
||||
`rgb(${colorFromPos[0]}, ${colorFromPos[1]}, ${colorFromPos[2]})`
|
||||
);
|
||||
|
||||
if (!this.dragging) {
|
||||
gZenUIManager.motion.animate(
|
||||
existingDot.Element,
|
||||
{
|
||||
left: `${dotPosition.Position.x}px`,
|
||||
top: `${dotPosition.Position.y}px`,
|
||||
},
|
||||
{
|
||||
duration: 0.4,
|
||||
type: 'spring',
|
||||
bounce: 0.3,
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.spawnDot(dotPosition.Position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onThemePickerClick(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.button !== 0 || this.dragging) return;
|
||||
if (event.button !== 0 || this.dragging || this.recentlyDragged) return;
|
||||
|
||||
const gradient = this.panel.querySelector('.zen-theme-picker-gradient');
|
||||
const rect = gradient.getBoundingClientRect();
|
||||
const padding = 90; // each side
|
||||
const padding = 90;
|
||||
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
@@ -346,32 +461,60 @@
|
||||
let pixelX = event.clientX;
|
||||
let pixelY = event.clientY;
|
||||
|
||||
// Check if the click is within the circle
|
||||
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
|
||||
if (distance > radius) {
|
||||
const clickedElement = event.target;
|
||||
let clickedDot = null;
|
||||
const existingPrimaryDot = this.dots.find((d) => d.ID === 0);
|
||||
clickedDot = this.dots.find((dot) => dot.Element === clickedElement);
|
||||
if (clickedDot) {
|
||||
existingPrimaryDot.ID = clickedDot.ID;
|
||||
clickedDot.ID = 0;
|
||||
clickedDot.Element.style.zIndex = 999;
|
||||
let colorPositions = this.calculateCompliments(this.dots, true);
|
||||
this.handleColorPositions(colorPositions);
|
||||
return;
|
||||
}
|
||||
|
||||
const clickedElement = event.target;
|
||||
const isExistingDot = clickedElement.classList.contains('zen-theme-picker-dot');
|
||||
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
|
||||
if (distance > radius) {
|
||||
const angle = Math.atan2(pixelY - centerY, pixelX - centerX);
|
||||
pixelX = centerX + Math.cos(angle) * radius;
|
||||
pixelY = centerY + Math.sin(angle) * radius;
|
||||
}
|
||||
|
||||
if (!isExistingDot && this.numberOfDots < ZenThemePicker.MAX_DOTS) {
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
const relativeX = pixelX - rect.left;
|
||||
const relativeY = pixelY - rect.top;
|
||||
|
||||
const color = this.getColorFromPosition(relativeX, relativeY);
|
||||
|
||||
const dot = document.createElement('div');
|
||||
dot.classList.add('zen-theme-picker-dot');
|
||||
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
|
||||
|
||||
dot.style.left = `${relativeX}px`;
|
||||
dot.style.top = `${relativeY}px`;
|
||||
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
|
||||
|
||||
gradient.appendChild(dot);
|
||||
if (!clickedDot && this.dots.length < 1) {
|
||||
if (this.dots.length === 0) {
|
||||
this.spawnDot({ x: relativeX, y: relativeY }, true);
|
||||
} else {
|
||||
this.spawnDot({ x: relativeX, y: relativeY });
|
||||
}
|
||||
|
||||
this.updateCurrentWorkspace(true);
|
||||
} else if (!clickedDot && existingPrimaryDot) {
|
||||
existingPrimaryDot.Element.style.left = `${relativeX}px`;
|
||||
existingPrimaryDot.Element.style.top = `${relativeY}px`;
|
||||
existingPrimaryDot.Position = {
|
||||
x: relativeX,
|
||||
y: relativeY,
|
||||
};
|
||||
|
||||
let colorPositions = this.calculateCompliments(this.dots, true);
|
||||
this.handleColorPositions(colorPositions);
|
||||
|
||||
gZenUIManager.motion.animate(
|
||||
existingPrimaryDot.Element,
|
||||
{
|
||||
left: `${relativeX}px`,
|
||||
top: `${relativeY}px`,
|
||||
},
|
||||
{
|
||||
duration: 0.4,
|
||||
type: 'spring',
|
||||
bounce: 0.3,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,10 +523,12 @@
|
||||
if (event.button === 2) {
|
||||
return;
|
||||
}
|
||||
this.dragging = true;
|
||||
this.draggedDot = event.target;
|
||||
this.draggedDot.style.zIndex = 1;
|
||||
this.draggedDot.classList.add('dragging');
|
||||
const draggedDot = this.dots.find((dot) => dot.Element === event.target);
|
||||
if (draggedDot) {
|
||||
this.dragging = true;
|
||||
this.draggedDot = event.target;
|
||||
this.draggedDot.classList.add('dragging');
|
||||
}
|
||||
|
||||
// Store the starting position of the drag
|
||||
this.dragStartPosition = {
|
||||
@@ -397,9 +542,21 @@
|
||||
if (!event.target.classList.contains('zen-theme-picker-dot')) {
|
||||
return;
|
||||
}
|
||||
this.dots = this.dots.filter((dot) => dot.Element !== event.target);
|
||||
event.target.remove();
|
||||
|
||||
this.dots.sort((a, b) => a.ID - b.ID);
|
||||
|
||||
// Reassign the IDs after sorting
|
||||
this.dots.forEach((dot, index) => {
|
||||
dot.ID = index;
|
||||
});
|
||||
|
||||
console.log(this.dots);
|
||||
let colorPositions = this.calculateCompliments(this.dots, true);
|
||||
this.handleColorPositions(colorPositions);
|
||||
|
||||
this.updateCurrentWorkspace();
|
||||
this.numberOfDots--;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -407,14 +564,55 @@
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.dragging = false;
|
||||
this.draggedDot.style.zIndex = 1;
|
||||
this.draggedDot.classList.remove('dragging');
|
||||
this.draggedDot = null;
|
||||
this.dragStartPosition = null; // Reset the drag start position
|
||||
|
||||
this.recentlyDragged = true;
|
||||
setTimeout(() => {
|
||||
this.recentlyDragged = false;
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.numberOfDots = this.panel.querySelectorAll('.zen-theme-picker-dot').length;
|
||||
onDotMouseMove(event) {
|
||||
if (this.dragging) {
|
||||
event.preventDefault();
|
||||
const rect = this.panel.querySelector('.zen-theme-picker-gradient').getBoundingClientRect();
|
||||
const padding = 90; // each side
|
||||
// do NOT let the ball be draged outside of an imaginary circle. You can drag it anywhere inside the circle
|
||||
// if the distance between the center of the circle and the dragged ball is bigger than the radius, then the ball
|
||||
// should be placed on the edge of the circle. If it's inside the circle, then the ball just follows the mouse
|
||||
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const radius = (rect.width - padding) / 2;
|
||||
let pixelX = event.clientX;
|
||||
let pixelY = event.clientY;
|
||||
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
|
||||
if (distance > radius) {
|
||||
const angle = Math.atan2(pixelY - centerY, pixelX - centerX);
|
||||
pixelX = centerX + Math.cos(angle) * radius;
|
||||
pixelY = centerY + Math.sin(angle) * radius;
|
||||
}
|
||||
|
||||
// set the location of the dot in pixels
|
||||
const relativeX = pixelX - rect.left;
|
||||
const relativeY = pixelY - rect.top;
|
||||
|
||||
const draggedDot = this.dots.find((dot) => dot.Element === this.draggedDot);
|
||||
draggedDot.Element.style.left = `${relativeX}px`;
|
||||
draggedDot.Element.style.top = `${relativeY}px`;
|
||||
draggedDot.Position = {
|
||||
x: relativeX,
|
||||
y: relativeY,
|
||||
};
|
||||
let colorPositions = this.calculateCompliments(this.dots, true);
|
||||
this.handleColorPositions(colorPositions);
|
||||
|
||||
this.updateCurrentWorkspace();
|
||||
}
|
||||
}
|
||||
|
||||
themedColors(colors) {
|
||||
@@ -632,8 +830,6 @@
|
||||
browser.gZenThemePicker.currentRotation = workspaceTheme.rotation ?? 45;
|
||||
browser.gZenThemePicker.currentTexture = workspaceTheme.texture ?? 0;
|
||||
|
||||
browser.gZenThemePicker.numberOfDots = workspaceTheme.gradientColors.length;
|
||||
|
||||
browser.document.getElementById('PanelUI-zen-gradient-generator-opacity').value =
|
||||
browser.gZenThemePicker.currentOpacity;
|
||||
browser.document.getElementById('PanelUI-zen-gradient-generator-texture').value =
|
||||
@@ -662,6 +858,7 @@
|
||||
}
|
||||
|
||||
if (!skipUpdate) {
|
||||
this.dots = [];
|
||||
browser.gZenThemePicker.recalculateDots(workspaceTheme.gradientColors);
|
||||
}
|
||||
});
|
||||
@@ -702,7 +899,6 @@
|
||||
}
|
||||
|
||||
recalculateDots(colors) {
|
||||
//THIS IS PART OF THE ISSUE
|
||||
for (const color of colors) {
|
||||
this.createDot(color, true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user