Add draggable blend mode menu and improve right-click UX

Introduces a draggable title bar to the blend mode menu for better usability and restricts its movement within the viewport. Right-clicking on selected layers now opens the blend mode menu, and the default context menu is suppressed on the canvas. Also refines tooltip table cell sizing for improved display.
This commit is contained in:
Dariusz L
2025-07-01 16:37:18 +02:00
parent 5a473cc14a
commit b2ff5666f9
3 changed files with 98 additions and 16 deletions

View File

@@ -51,6 +51,9 @@ export class CanvasInteractions {
this.canvas.canvas.addEventListener('dragenter', this.handleDragEnter.bind(this));
this.canvas.canvas.addEventListener('dragleave', this.handleDragLeave.bind(this));
this.canvas.canvas.addEventListener('drop', this.handleDrop.bind(this));
// Prevent default context menu on canvas
this.canvas.canvas.addEventListener('contextmenu', this.handleContextMenu.bind(this));
}
resetInteractionState() {
@@ -95,6 +98,23 @@ export class CanvasInteractions {
}
this.interaction.lastClickTime = currentTime;
// Check for right click to show blend mode menu on selected layers
if (e.button === 2) {
const clickedLayerResult = this.canvas.canvasLayers.getLayerAtPosition(worldCoords.x, worldCoords.y);
if (clickedLayerResult && this.canvas.selectedLayers.includes(clickedLayerResult.layer)) {
e.preventDefault(); // Prevent context menu
this.canvas.canvasLayers.showBlendModeMenu(viewCoords.x ,viewCoords.y);
return;
}
}
// Check for Shift key first to start output area drawing, ignoring layers
if (e.shiftKey) {
this.startCanvasResize(worldCoords);
this.canvas.render();
return;
}
const transformTarget = this.canvas.canvasLayers.getHandleAtPosition(worldCoords.x, worldCoords.y);
if (transformTarget) {
this.startLayerTransform(transformTarget.layer, transformTarget.handle, worldCoords);
@@ -103,18 +123,11 @@ export class CanvasInteractions {
const clickedLayerResult = this.canvas.canvasLayers.getLayerAtPosition(worldCoords.x, worldCoords.y);
if (clickedLayerResult) {
if (e.shiftKey && this.canvas.selectedLayers.includes(clickedLayerResult.layer)) {
this.canvas.canvasLayers.showBlendModeMenu(e.clientX, e.clientY);
return;
}
this.startLayerDrag(clickedLayerResult.layer, worldCoords);
return;
}
if (e.shiftKey) {
this.startCanvasResize(worldCoords);
} else {
this.startPanning(e);
}
this.startPanning(e);
this.canvas.render();
}
@@ -216,6 +229,11 @@ export class CanvasInteractions {
}
}
handleContextMenu(e) {
// Prevent default context menu on canvas
e.preventDefault();
}
handleWheel(e) {
e.preventDefault();
if (this.canvas.maskTool.isActive) {

View File

@@ -473,11 +473,74 @@ export class CanvasLayers {
background: #2a2a2a;
border: 1px solid #3a3a3a;
border-radius: 4px;
padding: 5px;
z-index: 10000;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 200px;
`;
// Create draggable title bar
const titleBar = document.createElement('div');
titleBar.style.cssText = `
background: #3a3a3a;
color: white;
padding: 8px 10px;
cursor: move;
user-select: none;
border-radius: 3px 3px 0 0;
font-size: 12px;
font-weight: bold;
border-bottom: 1px solid #4a4a4a;
`;
titleBar.textContent = 'Blend Mode';
// Create content area
const content = document.createElement('div');
content.style.cssText = `
padding: 5px;
`;
menu.appendChild(titleBar);
menu.appendChild(content);
// Add drag functionality
let isDragging = false;
let dragOffset = { x: 0, y: 0 };
const handleMouseMove = (e) => {
if (isDragging) {
const newX = e.clientX - dragOffset.x;
const newY = e.clientY - dragOffset.y;
// Keep menu within viewport bounds
const maxX = window.innerWidth - menu.offsetWidth;
const maxY = window.innerHeight - menu.offsetHeight;
menu.style.left = Math.max(0, Math.min(newX, maxX)) + 'px';
menu.style.top = Math.max(0, Math.min(newY, maxY)) + 'px';
}
};
const handleMouseUp = () => {
if (isDragging) {
isDragging = false;
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
}
};
titleBar.addEventListener('mousedown', (e) => {
isDragging = true;
// Calculate offset from mouse position to menu's top-left corner
dragOffset.x = e.clientX - parseInt(menu.style.left);
dragOffset.y = e.clientY - parseInt(menu.style.top);
e.preventDefault();
e.stopPropagation();
// Add global event listeners for dragging
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
});
this.blendModes.forEach(mode => {
const container = document.createElement('div');
container.className = 'blend-mode-container';
@@ -512,10 +575,10 @@ export class CanvasLayers {
}
option.onclick = () => {
menu.querySelectorAll('input[type="range"]').forEach(s => {
content.querySelectorAll('input[type="range"]').forEach(s => {
s.style.display = 'none';
});
menu.querySelectorAll('.blend-mode-container div').forEach(d => {
content.querySelectorAll('.blend-mode-container div').forEach(d => {
d.style.backgroundColor = '';
});
@@ -558,14 +621,14 @@ export class CanvasLayers {
container.appendChild(option);
container.appendChild(slider);
menu.appendChild(container);
content.appendChild(container);
});
const container = this.canvas.canvas.parentElement || document.body;
container.appendChild(menu);
const closeMenu = (e) => {
if (!menu.contains(e.target)) {
if (!menu.contains(e.target) && !isDragging) {
this.closeBlendModeMenu();
document.removeEventListener('mousedown', closeMenu);
}

View File

@@ -241,12 +241,13 @@ async function createCanvasWidget(node, widget, app) {
}
.painter-tooltip table td:first-child {
width: 45%;
width: auto;
white-space: nowrap;
min-width: fit-content;
}
.painter-tooltip table td:last-child {
width: 55%;
width: auto;
}
.painter-tooltip table tr:nth-child(odd) td {