@@ -9,152 +9,168 @@ import { CommandHistory } from "../commands/CommandHistory.js";
9
9
*
10
10
* **Core Concepts**:
11
11
* - **Event-Driven Execution**: UI interactions trigger commands rather than modifying state directly.
12
- * - **Dynamic Command Injection**: Commands are managed by `CommandFactory ` and executed via `CommandHistory`.
12
+ * - **Dynamic Command Injection**: Commands are managed by `GUICommandRegistry ` and executed via `CommandHistory`.
13
13
* - **Separation of Concerns**: The GUI delegates all logic to `CircuitService` via the command system.
14
14
*
15
15
* **Responsibilities**:
16
16
* 1. **Initialization**:
17
17
* - Renders the circuit and **binds UI controls dynamically**.
18
18
* 2. **Command Execution**:
19
- * - Forwards user actions to `CommandFactory`, which then emits **commandExecuted** events .
19
+ * - Retrieves commands from `GUICommandRegistry` and executes them .
20
20
* 3. **Undo/Redo Support**:
21
21
* - Ensures that every executed command is trackable via `CommandHistory`.
22
22
*
23
- * **Example Workflow**:
24
- * 1. A user clicks the "Add Resistor" button.
25
- * 2. The `click` event triggers `executeCommand("addElement", "Resistor")`.
26
- * 3. `GUIAdapter` retrieves the command from `CommandFactory`.
27
- * 4. The command executes via `CommandHistory`.
28
- * 5. `CircuitService` adds the element and emits an `"update"` event.
29
- * 6. `GUIAdapter` listens for `"update"` and **re-renders the UI**.
30
- *
31
- * **Benefits**:
32
- * - **Decoupled UI & Business Logic**: The GUI does not directly modify the circuit.
33
- * - **Fully Extensible**: New commands can be added without modifying this class.
34
- * - **Undo/Redo Support**: Actions can be undone/redone via `CommandHistory`.
35
- *
36
23
* @example
37
24
* const guiAdapter = new GUIAdapter(canvas, circuitService, elementRegistry);
38
25
* guiAdapter.initialize();
39
26
*/
40
27
export class GUIAdapter {
41
- /**
42
- * @param {HTMLCanvasElement } canvas - The canvas element for rendering the circuit.
43
- * @param {CircuitService } circuitService - The service managing circuit logic.
44
- * @param {Object } elementRegistry - The registry of circuit elements.
45
- * @param {RendererFactory } rendererFactory - The factory for creating element renderers.
46
- * @param {GUICommandRegistry } commandFactory - The factory for creating commands.
47
- */
48
- constructor ( canvas , circuitService , elementRegistry , rendererFactory , guiCommandRegistry ) {
49
- this . canvas = canvas ;
50
- this . circuitService = circuitService ;
51
- this . elementRegistry = elementRegistry ;
52
- this . circuitRenderer = new CircuitRenderer ( canvas , circuitService , rendererFactory ) ;
53
- this . guiCommandRegistry = guiCommandRegistry ;
54
-
55
- // this.commandFactory = new GUICommandFactory(circuitService, this.circuitRenderer, elementRegistry);
56
- this . commandHistory = new CommandHistory ( ) ;
57
- }
28
+ /**
29
+ * @param {HTMLCanvasElement } canvas - The canvas element for rendering the circuit.
30
+ * @param {CircuitService } circuitService - The service managing circuit logic.
31
+ * @param {Object } elementRegistry - The registry of circuit elements.
32
+ * @param {RendererFactory } rendererFactory - The factory for creating element renderers.
33
+ * @param {GUICommandRegistry } guiCommandRegistry - The factory for creating commands.
34
+ */
35
+ constructor ( canvas , circuitService , elementRegistry , rendererFactory , guiCommandRegistry ) {
36
+ this . canvas = canvas ;
37
+ this . circuitService = circuitService ;
38
+ this . elementRegistry = elementRegistry ;
39
+ this . circuitRenderer = new CircuitRenderer ( canvas , circuitService , rendererFactory ) ;
40
+ this . guiCommandRegistry = guiCommandRegistry ;
41
+ this . commandHistory = new CommandHistory ( ) ;
42
+ this . dragCommand = null ;
43
+ }
58
44
59
- /**
60
- * Dynamically binds UI controls to their corresponding commands.
61
- * Instead of directly modifying circuit state, this now delegates execution to CommandFactory.
62
- */
63
- bindUIControls ( ) {
64
- console . log ( "🔍 Binding UI controls in GUIAdapter..." ) ;
65
- console . log ( "Commands available:" , this . guiCommandRegistry . getTypes ( ) ) ;
66
-
67
- this . elementRegistry . getTypes ( ) . forEach ( ( elementType ) => {
68
- const buttonName = `add${ elementType } ` ; // Format should match HTML IDs
69
- console . log ( `🔍 Searching for button: ${ buttonName } ` ) ;
70
-
71
- const button = document . getElementById ( buttonName ) ;
72
- if ( button ) {
73
- console . log ( `✅ Found button: ${ button . id } , binding addElement command for ${ elementType } ` ) ;
74
-
75
- button . addEventListener ( "click" , ( ) => {
76
- console . log ( `🛠 Executing addElement command for: ${ elementType } ` ) ;
77
-
78
- // Retrieve the correct command with the element type
79
- const command = this . guiCommandRegistry . get (
80
- "addElement" ,
81
- // this, // Pass GUIAdapter
82
- this . circuitService ,
83
- this . circuitRenderer ,
84
- this . elementRegistry ,
85
- elementType
86
- ) ;
87
-
88
- if ( command ) {
89
- command . execute ( ) ;
90
- console . log ( `✅ Command 'addElement' executed for ${ elementType } ` ) ;
91
- } else {
92
- console . warn ( `⚠️ Command 'addElement' not found for ${ elementType } ` ) ;
93
- }
94
- } ) ;
95
- } else {
96
- console . warn ( `⚠️ Button for adding ${ elementType } not found` ) ;
97
- }
98
- } ) ;
99
- }
100
- /**
101
- * Executes a command by retrieving it from the CommandFactory and executing via CommandHistory.
102
- * @param {string } commandName - The name of the command to execute.
103
- * @param {...any } args - Arguments to pass to the command.
104
- */
105
- executeCommand ( commandName , ...args ) {
106
- console . log ( `Executing command: ${ commandName } with args:` , args ) ;
107
-
108
- const command = this . commandFactory . get ( commandName , ...args ) ;
109
- if ( command ) {
110
- this . commandHistory . executeCommand ( command , ...args ) ;
111
- } else {
112
- console . warn ( `Command "${ commandName } " not found.` ) ;
113
- }
114
- }
45
+ /**
46
+ * Dynamically binds UI controls to their corresponding commands.
47
+ */
48
+ bindUIControls ( ) {
49
+ console . log ( "Binding UI controls in GUIAdapter..." ) ;
50
+ console . log ( "Commands available:" , this . guiCommandRegistry . getTypes ( ) ) ;
115
51
116
- /**
117
- * Initializes the GUI by rendering the circuit and binding UI controls.
118
- */
119
- initialize ( ) {
120
- this . circuitRenderer . render ( ) ;
121
- this . bindUIControls ( ) ;
122
- this . setupCanvasInteractions ( ) ;
52
+ this . elementRegistry . getTypes ( ) . forEach ( ( elementType ) => {
53
+ const buttonName = `add${ elementType } ` ;
54
+ console . log ( `Searching for button: ${ buttonName } ` ) ;
123
55
124
- // Listen for UI updates from CircuitService
125
- this . circuitService . on ( "update" , ( ) => this . circuitRenderer . render ( ) ) ;
126
- }
56
+ const button = document . getElementById ( buttonName ) ;
57
+ if ( button ) {
58
+ console . log ( `Found button: ${ button . id } , binding addElement command for ${ elementType } ` ) ;
127
59
128
- /**
129
- * Sets up canvas interactions for dragging elements.
130
- */
131
- setupCanvasInteractions ( ) {
132
- let dragCommand = null ; // Cache the command to prevent infinite looping
133
-
134
- this . canvas . addEventListener ( "mousedown" , ( event ) => {
135
- const { offsetX, offsetY } = event ;
136
- // Retrieve the drag command only ONCE when dragging starts
137
- dragCommand = this . guiCommandRegistry . get ( "dragElement" , this . circuitService ) ;
138
-
139
- if ( dragCommand ) {
140
- dragCommand . start ( offsetX , offsetY ) ;
141
- } else {
142
- console . warn ( "⚠️ Drag command not found in registry" ) ;
143
- }
144
- } ) ;
60
+ button . addEventListener ( "click" , ( ) => {
61
+ console . log ( `Executing addElement command for: ${ elementType } ` ) ;
145
62
146
- this . canvas . addEventListener ( "mousemove" , ( event ) => {
147
- if ( dragCommand ) { // Only execute if a drag command exists
148
- const { offsetX, offsetY } = event ;
149
- dragCommand . move ( offsetX , offsetY ) ;
150
- }
151
- } ) ;
63
+ const command = this . guiCommandRegistry . get (
64
+ "addElement" ,
65
+ this . circuitService ,
66
+ this . circuitRenderer ,
67
+ this . elementRegistry ,
68
+ elementType
69
+ ) ;
152
70
153
- this . canvas . addEventListener ( "mouseup" , ( ) => {
154
- if ( dragCommand ) {
155
- dragCommand . stop ( ) ;
156
- dragCommand = null ; // Reset command after stopping to prevent looping
157
- }
71
+ if ( command ) {
72
+ command . execute ( ) ;
73
+ console . log ( `Command 'addElement' executed for ${ elementType } ` ) ;
74
+ } else {
75
+ console . warn ( `Command 'addElement' not found for ${ elementType } ` ) ;
76
+ }
158
77
} ) ;
78
+ } else {
79
+ console . warn ( `Button for adding ${ elementType } not found` ) ;
80
+ }
81
+ } ) ;
82
+ }
83
+
84
+ /**
85
+ * Executes a command by retrieving it from `GUICommandRegistry` and executing via `CommandHistory`.
86
+ * @param {string } commandName - The name of the command to execute.
87
+ * @param {...any } args - Arguments to pass to the command.
88
+ */
89
+ executeCommand ( commandName , ...args ) {
90
+ console . log ( `Executing command: ${ commandName } with args:` , args ) ;
91
+
92
+ const command = this . guiCommandRegistry . get ( commandName , ...args ) ;
93
+ if ( command ) {
94
+ this . commandHistory . executeCommand ( command , ...args ) ;
95
+ } else {
96
+ console . warn ( `Command "${ commandName } " not found.` ) ;
159
97
}
98
+ }
99
+
100
+ /**
101
+ * Initializes the GUI by rendering the circuit and binding UI controls.
102
+ */
103
+ initialize ( ) {
104
+ this . circuitRenderer . render ( ) ;
105
+ this . bindUIControls ( ) ;
106
+ this . setupCanvasInteractions ( ) ;
107
+
108
+ // Listen for UI updates from CircuitService
109
+ this . circuitService . on ( "update" , ( ) => this . circuitRenderer . render ( ) ) ;
110
+ }
111
+
112
+ /**
113
+ * Sets up canvas interactions for dragging elements.
114
+ */
115
+ setupCanvasInteractions ( ) {
116
+ this . canvas . addEventListener ( "wheel" , ( event ) => {
117
+ event . preventDefault ( ) ;
118
+ this . circuitRenderer . zoom ( event ) ;
119
+ } ) ;
120
+
121
+ this . canvas . addEventListener ( "mousedown" , ( event ) => {
122
+ const { offsetX, offsetY } = this . getTransformedMousePosition ( event ) ;
123
+ this . dragCommand = this . guiCommandRegistry . get ( "dragElement" , this . circuitService ) ;
124
+
125
+ if ( this . dragCommand ) {
126
+ this . dragCommand . start ( offsetX , offsetY ) ;
127
+ }
128
+ } ) ;
129
+
130
+ this . canvas . addEventListener ( "mousemove" , ( event ) => {
131
+ if ( this . dragCommand ) {
132
+ const { offsetX, offsetY } = this . getTransformedMousePosition ( event ) ;
133
+ this . dragCommand . move ( offsetX , offsetY ) ;
134
+ }
135
+ } ) ;
136
+
137
+ this . canvas . addEventListener ( "mouseup" , ( ) => {
138
+ if ( this . dragCommand ) {
139
+ this . dragCommand . stop ( ) ;
140
+ this . dragCommand = null ;
141
+ }
142
+ } ) ;
143
+
144
+ // Enable panning with middle mouse button
145
+ this . canvas . addEventListener ( "mousedown" , ( event ) => {
146
+ if ( event . button === 1 ) {
147
+ this . canvas . style . cursor = "grabbing" ;
148
+ this . panStartX = event . clientX - this . circuitRenderer . offsetX ;
149
+ this . panStartY = event . clientY - this . circuitRenderer . offsetY ;
150
+ }
151
+ } ) ;
152
+
153
+ this . canvas . addEventListener ( "mousemove" , ( event ) => {
154
+ if ( event . buttons === 4 ) {
155
+ const newX = event . clientX - this . panStartX ;
156
+ const newY = event . clientY - this . panStartY ;
157
+ this . circuitRenderer . setPan ( newX , newY ) ;
158
+ }
159
+ } ) ;
160
+
161
+ this . canvas . addEventListener ( "mouseup" , ( ) => {
162
+ this . canvas . style . cursor = "default" ;
163
+ } ) ;
164
+ }
165
+
166
+ /**
167
+ * Adjusts mouse position based on zoom and pan.
168
+ */
169
+ getTransformedMousePosition ( event ) {
170
+ const rect = this . canvas . getBoundingClientRect ( ) ;
171
+ return {
172
+ offsetX : ( event . clientX - rect . left - this . circuitRenderer . offsetX ) / this . circuitRenderer . scale ,
173
+ offsetY : ( event . clientY - rect . top - this . circuitRenderer . offsetY ) / this . circuitRenderer . scale ,
174
+ } ;
175
+ }
160
176
}
0 commit comments