Skip to content

Commit c4aec64

Browse files
committed
refactor(P1-1): consolidate canvas event listeners in GUIAdapter
- Remove initEventListeners() from CircuitRenderer (was duplicate) - GUIAdapter is now single owner of all canvas listeners - Wire wheel→zoom, MMB pan, hover, dblclick, mouseleave through GUIAdapter - Clean up dispose() to use stable _boundPerformRender reference - Remove dead panStartX/panStartY code from GUIAdapter 448 tests passing, build OK.
1 parent 6a4fd75 commit c4aec64

2 files changed

Lines changed: 23 additions & 34 deletions

File tree

src/gui/adapters/GUIAdapter.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,8 @@ export class GUIAdapter {
344344
* Leaves normal scrolling alone if Ctrl is not pressed.
345345
*/
346346
bindWheelZoom() {
347-
// Let CircuitRenderer handle wheel events directly - it has its own zoom method
348-
// No need to bind wheel events here as CircuitRenderer.initEventListeners() handles them
347+
this._onWheel = (event) => this.circuitRenderer.zoom(event);
348+
this.canvas.addEventListener("wheel", this._onWheel);
349349
}
350350

351351
/**
@@ -545,8 +545,7 @@ export class GUIAdapter {
545545
this.canvas.addEventListener("mousedown", (event) => {
546546
if (event.button === 1) {
547547
this.canvas.style.cursor = "grabbing";
548-
this.panStartX = event.clientX - this.circuitRenderer.offsetX;
549-
this.panStartY = event.clientY - this.circuitRenderer.offsetY;
548+
this.circuitRenderer.startPan(event);
550549
return;
551550
}
552551

@@ -633,6 +632,10 @@ export class GUIAdapter {
633632

634633
// Move / live placement preview / command move
635634
this.canvas.addEventListener("mousemove", (event) => {
635+
// Delegate panning and hover detection to the renderer first
636+
this.circuitRenderer.pan(event);
637+
this.circuitRenderer.handleMouseMove(event);
638+
636639
const { offsetX, offsetY } = this.getTransformedMousePosition(event);
637640

638641
// Always track current mouse position for immediate element placement
@@ -684,6 +687,7 @@ export class GUIAdapter {
684687
this.canvas.addEventListener("mouseup", (event) => {
685688
if (event.button === 1) {
686689
this.canvas.style.cursor = "default";
690+
this.circuitRenderer.stopPan();
687691
return;
688692
}
689693

@@ -762,6 +766,17 @@ export class GUIAdapter {
762766
this.resetCursor();
763767
}
764768
});
769+
770+
// Mouse leave → stop panning and clear hover highlights
771+
this.canvas.addEventListener("mouseleave", () => {
772+
this.circuitRenderer.stopPan();
773+
this.circuitRenderer.clearAllHovers();
774+
});
775+
776+
// Double-click → open property panel (delegates element detection to renderer)
777+
this.canvas.addEventListener("dblclick", (event) => {
778+
this.circuitRenderer.handleDoubleClick(event);
779+
});
765780
}
766781

767782
/**

src/gui/renderers/CircuitRenderer.js

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -108,28 +108,11 @@ export class CircuitRenderer {
108108
this.circuitService.on('elementMoved', () => this.invalidateSpatialIndex());
109109
this.circuitService.on('circuitCleared', () => this.invalidateSpatialIndex());
110110

111-
// Attach Event Listeners
112-
this.initEventListeners();
111+
// NOTE: Canvas event listeners (wheel, mouse*, dblclick) are NOT registered here.
112+
// GUIAdapter is the single owner of all canvas event listeners and calls
113+
// our methods (zoom, startPan, pan, stopPan, handleMouseMove, etc.) directly.
113114
}
114115

115-
/**
116-
* Initializes event listeners for zooming, panning, and double-click property editing.
117-
*/
118-
initEventListeners() {
119-
this.canvas.addEventListener("wheel", (event) => this.zoom(event));
120-
this.canvas.addEventListener("mousedown", (event) => this.startPan(event));
121-
this.canvas.addEventListener("mousemove", (event) => {
122-
this.pan(event);
123-
this.handleMouseMove(event);
124-
});
125-
this.canvas.addEventListener("mouseup", () => this.stopPan());
126-
this.canvas.addEventListener("mouseleave", () => {
127-
this.stopPan();
128-
this.clearAllHovers();
129-
});
130-
this.canvas.addEventListener("dblclick", (event) => this.handleDoubleClick(event));
131-
}
132-
133116
/**
134117
* Clears the canvas by resetting its drawing context.
135118
*/
@@ -609,23 +592,14 @@ export class CircuitRenderer {
609592
* Cleanup method to remove event listeners and prevent memory leaks
610593
*/
611594
dispose() {
612-
// Remove all canvas event listeners
613-
this.canvas.removeEventListener("wheel", this.zoom);
614-
this.canvas.removeEventListener("mousedown", this.startPan);
615-
this.canvas.removeEventListener("mousemove", this.handleMouseMove);
616-
this.canvas.removeEventListener("mouseup", this.stopPan);
617-
this.canvas.removeEventListener("mouseleave", this.clearAllHovers);
618-
this.canvas.removeEventListener("dblclick", this.handleDoubleClick);
619-
620595
// Clear any scheduled renders
621-
globalRenderScheduler.cancelRender(this.performRender);
596+
globalRenderScheduler.cancelRender(this._boundPerformRender);
622597

623598
// Clear references
624599
this.renderers.clear();
625600
this.selectedElements.clear();
626601
this.hoveredElement = null;
627602
this.selectedElement = null;
628-
629603
}
630604

631605
/**

0 commit comments

Comments
 (0)