feat: drag selection box to select, move, and delete multiple gates
Click and drag on empty space to draw a selection rectangle. Gates inside the box get selected (cyan dashed outline). Drag any selected gate to move all of them together. Delete/Backspace removes all selected gates and their connections. Escape clears the selection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,10 +32,25 @@ export function screenToWorld(sx, sy) {
|
||||
};
|
||||
}
|
||||
|
||||
function drawSelectionHighlight(gate) {
|
||||
if (!state.selectedGates.includes(gate.id)) return;
|
||||
const isDynamic = gate.type.startsWith('COMPONENT:') || gate.type.startsWith('BUS:');
|
||||
const w = isDynamic ? getComponentWidth(gate) : GATE_W;
|
||||
const h = isDynamic ? getComponentHeight(gate) : GATE_H;
|
||||
const pad = 4;
|
||||
ctx.strokeStyle = '#44ddff';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.setLineDash([5, 3]);
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(gate.x - pad, gate.y - pad, w + pad * 2, h + pad * 2, 10);
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
|
||||
function drawGate(gate) {
|
||||
// Special gate types have different rendering
|
||||
if (gate.type.startsWith('BUS:')) return drawBusGate(gate);
|
||||
if (gate.type.startsWith('COMPONENT:')) return drawComponentGate(gate);
|
||||
if (gate.type.startsWith('BUS:')) { drawBusGate(gate); drawSelectionHighlight(gate); return; }
|
||||
if (gate.type.startsWith('COMPONENT:')) { drawComponentGate(gate); drawSelectionHighlight(gate); return; }
|
||||
|
||||
const color = GATE_COLORS[gate.type];
|
||||
const isHovered = state.hoveredGate === gate;
|
||||
@@ -125,6 +140,8 @@ function drawGate(gate) {
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.stroke();
|
||||
});
|
||||
|
||||
drawSelectionHighlight(gate);
|
||||
}
|
||||
|
||||
function drawBusGate(gate) {
|
||||
@@ -519,6 +536,23 @@ function segsHit(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) {
|
||||
return t >= 0 && t <= 1 && u >= 0 && u <= 1;
|
||||
}
|
||||
|
||||
function drawSelectionBox() {
|
||||
if (!state.selectionBox) return;
|
||||
const box = state.selectionBox;
|
||||
const x = Math.min(box.startX, box.endX);
|
||||
const y = Math.min(box.startY, box.endY);
|
||||
const w = Math.abs(box.endX - box.startX);
|
||||
const h = Math.abs(box.endY - box.startY);
|
||||
|
||||
ctx.fillStyle = '#44ddff0a';
|
||||
ctx.fillRect(x, y, w, h);
|
||||
ctx.strokeStyle = '#44ddff88';
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.setLineDash([6, 3]);
|
||||
ctx.strokeRect(x, y, w, h);
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
|
||||
function drawPlacingGhost() {
|
||||
if (!state.placingGate) return;
|
||||
ctx.globalAlpha = 0.5;
|
||||
@@ -572,6 +606,7 @@ function draw() {
|
||||
state.gates.forEach(drawGate);
|
||||
drawConnectingWire();
|
||||
drawBusCutLine();
|
||||
drawSelectionBox();
|
||||
drawPlacingGhost();
|
||||
|
||||
ctx.restore();
|
||||
|
||||
Reference in New Issue
Block a user