fix: allow dragging all gates + stop waveform recording on edits
- INPUT/CLOCK gates can now be dragged (click-without-move = toggle, click-and-drag = move). Uses 4px movement threshold. - Waveform only records samples on intentional actions (INPUT toggle, manual step, simulation tick), not on gate placement/movement/connections. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
43
js/events.js
43
js/events.js
@@ -30,9 +30,19 @@ export function initEvents() {
|
|||||||
state.hoveredGate = state.hoveredPort ? state.hoveredPort.gate : findGateAt(world.x, world.y);
|
state.hoveredGate = state.hoveredPort ? state.hoveredPort.gate : findGateAt(world.x, world.y);
|
||||||
|
|
||||||
if (state.dragging) {
|
if (state.dragging) {
|
||||||
state.dragging.x = world.x - state.dragOffset.x;
|
// Detect if mouse actually moved (threshold of 4px to distinguish click vs drag)
|
||||||
state.dragging.y = world.y - state.dragOffset.y;
|
if (dragStartPos && !dragMoved) {
|
||||||
evaluateAll();
|
const dx = e.offsetX - dragStartPos.x;
|
||||||
|
const dy = e.offsetY - dragStartPos.y;
|
||||||
|
if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
|
||||||
|
dragMoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dragMoved) {
|
||||||
|
state.dragging.x = world.x - state.dragOffset.x;
|
||||||
|
state.dragging.y = world.y - state.dragOffset.y;
|
||||||
|
evaluateAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.style.cursor = state.placingGate ? 'crosshair'
|
canvas.style.cursor = state.placingGate ? 'crosshair'
|
||||||
@@ -41,9 +51,14 @@ export function initEvents() {
|
|||||||
: 'default';
|
: 'default';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let dragStartPos = null;
|
||||||
|
let dragMoved = false;
|
||||||
|
|
||||||
canvas.addEventListener('mousedown', e => {
|
canvas.addEventListener('mousedown', e => {
|
||||||
if (e.button !== 0) return;
|
if (e.button !== 0) return;
|
||||||
const world = screenToWorld(e.offsetX, e.offsetY);
|
const world = screenToWorld(e.offsetX, e.offsetY);
|
||||||
|
dragStartPos = { x: e.offsetX, y: e.offsetY };
|
||||||
|
dragMoved = false;
|
||||||
|
|
||||||
// Placing a new gate
|
// Placing a new gate
|
||||||
if (state.placingGate) {
|
if (state.placingGate) {
|
||||||
@@ -81,15 +96,8 @@ export function initEvents() {
|
|||||||
|
|
||||||
if (state.connecting) { state.connecting = null; return; }
|
if (state.connecting) { state.connecting = null; return; }
|
||||||
|
|
||||||
// Toggle INPUT/CLOCK
|
// Drag any gate (including INPUT/CLOCK)
|
||||||
const gate = findGateAt(world.x, world.y);
|
const gate = findGateAt(world.x, world.y);
|
||||||
if (gate && (gate.type === 'INPUT' || gate.type === 'CLOCK')) {
|
|
||||||
gate.value = gate.value ? 0 : 1;
|
|
||||||
evaluateAll();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drag gate
|
|
||||||
if (gate) {
|
if (gate) {
|
||||||
state.dragging = gate;
|
state.dragging = gate;
|
||||||
state.dragOffset = { x: world.x - gate.x, y: world.y - gate.y };
|
state.dragOffset = { x: world.x - gate.x, y: world.y - gate.y };
|
||||||
@@ -97,7 +105,18 @@ export function initEvents() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
canvas.addEventListener('mouseup', () => { state.dragging = null; });
|
canvas.addEventListener('mouseup', e => {
|
||||||
|
// Toggle INPUT/CLOCK only on click (no drag movement)
|
||||||
|
if (state.dragging && !dragMoved) {
|
||||||
|
const gate = state.dragging;
|
||||||
|
if (gate.type === 'INPUT' || gate.type === 'CLOCK') {
|
||||||
|
gate.value = gate.value ? 0 : 1;
|
||||||
|
evaluateAll(true); // record waveform on intentional toggle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.dragging = null;
|
||||||
|
dragStartPos = null;
|
||||||
|
});
|
||||||
|
|
||||||
canvas.addEventListener('contextmenu', e => {
|
canvas.addEventListener('contextmenu', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -53,12 +53,12 @@ export function evaluate(gate, visited = new Set()) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function evaluateAll() {
|
export function evaluateAll(recordWave = false) {
|
||||||
state.gates.forEach(g => {
|
state.gates.forEach(g => {
|
||||||
if (g.type !== 'INPUT' && g.type !== 'CLOCK') g.value = 0;
|
if (g.type !== 'INPUT' && g.type !== 'CLOCK') g.value = 0;
|
||||||
});
|
});
|
||||||
state.gates.forEach(g => evaluate(g));
|
state.gates.forEach(g => evaluate(g));
|
||||||
if (state.recording && state.waveformVisible) recordSample();
|
if (recordWave && state.recording && state.waveformVisible) recordSample();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register evaluateAll in waveform to break circular dependency
|
// Register evaluateAll in waveform to break circular dependency
|
||||||
|
|||||||
Reference in New Issue
Block a user