Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions src/gui/components/CircuitAppManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/**
* CircuitAppManager - Manages the complete circuit application setup
* Encapsulates common initialization logic
*/

import { Circuit } from "../../domain/aggregates/Circuit.js";
import { CircuitService } from "../../application/CircuitService.js";
import { GUIAdapter } from "../adapters/GUIAdapter.js";
import {
ElementRegistry,
rendererFactory,
GUICommandRegistry,
setupCommands
} from "../../config/settings.js";

export class CircuitAppManager {
constructor(options = {}) {
this.options = {
model: null, // For widget usage
...options
};

this.circuit = null;
this.circuitService = null;
this.guiAdapter = null;
this.elements = {
controls: null,
canvas: null,
buttons: {}
};
}

/**
* Initialize the circuit application
* @param {HTMLElement} controls - Controls container
* @param {HTMLCanvasElement} canvas - Canvas element
* @returns {Promise<void>}
*/
async initialize(controls, canvas) {
// Store references
this.elements.controls = controls;
this.elements.canvas = canvas;

// Get button references
this.elements.buttons = {
addResistor: controls.querySelector("#addResistor"),
addWire: controls.querySelector("#addWire"),
undo: controls.querySelector("#undoButton"),
redo: controls.querySelector("#redoButton"),
export: controls.querySelector("#export")
};

// Set up the circuit and services
this.circuit = new Circuit();
this.circuitService = new CircuitService(this.circuit, ElementRegistry);

// Create GUI adapter
this.guiAdapter = new GUIAdapter(
controls,
canvas,
this.circuitService,
ElementRegistry,
rendererFactory,
GUICommandRegistry
);

// Wait for commands to be set up
await setupCommands(this.circuitService, this.guiAdapter.circuitRenderer);

// Initialize the GUI adapter
this.guiAdapter.initialize();

// Set up additional event handlers
this.setupEventHandlers();
}

/**
* Set up additional event handlers (like export)
*/
setupEventHandlers() {
if (this.elements.buttons.export) {
this.setupExportHandler();
}
}

/**
* Set up export functionality for widget usage
*/
setupExportHandler() {
const { model } = this.options;
const exportButton = this.elements.buttons.export;

if (!exportButton) return;

exportButton.addEventListener('click', () => {
const elements = this.circuitService.getElements();

if (model) {
// Widget usage - save to model
model.set("circuitElements", elements);
model.set("exportTrigger", (model.get("exportTrigger") || 0) + 1);
model.save_changes();
} else {
// Standalone usage - download as JSON
const dataStr = JSON.stringify(elements, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);

const link = document.createElement('a');
link.href = url;
link.download = 'circuit-export.json';
link.click();

URL.revokeObjectURL(url);
}
});
}

/**
* Get the circuit service instance
*/
getCircuitService() {
return this.circuitService;
}

/**
* Get the GUI adapter instance
*/
getGUIAdapter() {
return this.guiAdapter;
}

/**
* Get the circuit instance
*/
getCircuit() {
return this.circuit;
}

/**
* Clean up resources
*/
destroy() {
// Clean up any event listeners or resources if needed
this.circuit = null;
this.circuitService = null;
this.guiAdapter = null;
this.elements = {
controls: null,
canvas: null,
buttons: {}
};
}
}
154 changes: 154 additions & 0 deletions src/gui/components/CircuitUIFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/**
* CircuitUIFactory - Factory for creating circuit UI components
* Centralizes common UI creation logic to reduce code duplication
*/

export class CircuitUIFactory {
/**
* Creates a standardized button element
* @param {string} id - The button ID
* @param {string} text - The button text
* @param {Object} options - Additional options
* @returns {HTMLButtonElement}
*/
static createButton(id, text, options = {}) {
const btn = document.createElement("button");
btn.id = id;
btn.textContent = text;

if (options.className) {
btn.className = options.className;
}

if (options.style) {
Object.assign(btn.style, options.style);
}

return btn;
}

/**
* Creates a standardized canvas element
* @param {Object} options - Canvas configuration
* @returns {HTMLCanvasElement}
*/
static createCanvas(options = {}) {
const canvas = document.createElement("canvas");
canvas.id = options.id || "circuitCanvas";
canvas.width = options.width || 800;
canvas.height = options.height || 600;

// Default styling
canvas.style.border = options.border || "1px solid #ccc";
canvas.style.marginTop = options.marginTop || "20px";

if (options.style) {
Object.assign(canvas.style, options.style);
}

return canvas;
}

/**
* Creates a controls container with standard buttons
* @param {Object} options - Configuration options
* @returns {HTMLDivElement}
*/
static createControlsContainer(options = {}) {
const controls = document.createElement("div");
controls.className = options.className || "controls";

// Default styling
controls.style.marginTop = options.marginTop || "10px";

if (options.style) {
Object.assign(controls.style, options.style);
}

// Create standard buttons
const buttons = this.createStandardButtons(options.buttons || {});
buttons.forEach(button => controls.appendChild(button));

return controls;
}

/**
* Creates the standard set of circuit control buttons
* @param {Object} buttonConfig - Configuration for which buttons to include
* @returns {HTMLButtonElement[]}
*/
static createStandardButtons(buttonConfig = {}) {
const buttons = [];

const defaultButtons = [
{ id: "addResistor", text: "Add Resistor", include: buttonConfig.addResistor !== false },
{ id: "addWire", text: "Add Wire", include: buttonConfig.addWire !== false },
{ id: "undoButton", text: "<", include: buttonConfig.undo !== false },
{ id: "redoButton", text: ">", include: buttonConfig.redo !== false },
{ id: "export", text: "Export circuit", include: true } // Always include export button
];

defaultButtons.forEach(({ id, text, include }) => {
if (include) {
buttons.push(this.createButton(id, text));
}
});

return buttons;
}

/**
* Creates a complete circuit interface
* @param {HTMLElement} container - Container element to append to
* @param {Object} options - Configuration options
* @returns {Object} - Object containing references to created elements
*/
static createCircuitInterface(container, options = {}) {
const controls = this.createControlsContainer(options.controls);
const canvas = this.createCanvas(options.canvas);

// Add title if specified
if (options.title) {
const title = document.createElement("h1");
title.textContent = options.title;
container.appendChild(title);
}

container.appendChild(controls);
container.appendChild(canvas);

return {
controls,
canvas,
buttons: {
addResistor: controls.querySelector("#addResistor"),
addWire: controls.querySelector("#addWire"),
undo: controls.querySelector("#undoButton"),
redo: controls.querySelector("#redoButton"),
export: controls.querySelector("#export")
}
};
}

/**
* Applies standard CSS styles to the document
*/
static applyStandardStyles() {
const style = document.createElement("style");
style.textContent = `
canvas {
border: 1px solid #ccc;
margin-top: 20px;
}
.controls {
margin-top: 10px;
}
.controls button {
margin-right: 5px;
padding: 5px 10px;
cursor: pointer;
}
`;
document.head.appendChild(style);
}
}
40 changes: 12 additions & 28 deletions src/gui/gui.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Circuit Designer</title>
<style>
canvas {
border: 1px solid #ccc;
margin-top: 20px;
}
.controls {
margin-top: 10px;
}
</style>
</head>
<body>
<h1>Circuit Designer</h1>
<div class="controls">
<button id="addResistor">Add Resistor</button>
<button id="addWire">Add Wire</button>
<button id="undoButton">&lt;</button>
<button id="redoButton">&gt;</button>
</div>
<canvas id="circuitCanvas" width="800" height="600"></canvas>
<script type="module" src="./static/main.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Circuit Designer</title>
</head>
<body>
<!-- All UI will be created dynamically by JavaScript -->
<script type="module" src="./static/main.js"></script>
</body>
</html>
44 changes: 20 additions & 24 deletions src/gui/main.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { Circuit } from "../domain/aggregates/Circuit.js";
import { CircuitService } from "../application/CircuitService.js";
import { GUIAdapter } from "./adapters/GUIAdapter.js";

import {
ElementRegistry,
rendererFactory,
GUICommandRegistry,
setupCommands
} from "../config/settings.js";

// Init services
const circuit = new Circuit();
const circuitService = new CircuitService(circuit, ElementRegistry);

// Controls and canvas
const controls = document.querySelector(".controls");
const canvas = document.getElementById("circuitCanvas");
const guiAdapter = new GUIAdapter(controls, canvas, circuitService, ElementRegistry, rendererFactory, GUICommandRegistry);

// Wait for commands to be set up
setupCommands(circuitService, guiAdapter.circuitRenderer).then(() => {
guiAdapter.initialize();
});
import { CircuitUIFactory } from './components/CircuitUIFactory.js';
import { CircuitAppManager } from './components/CircuitAppManager.js';

// Wait for DOM to be ready
document.addEventListener('DOMContentLoaded', () => {
// Apply standard styles
CircuitUIFactory.applyStandardStyles();

// Set document title
document.title = "Circuit Designer";

// Create the complete circuit interface in the body
const ui = CircuitUIFactory.createCircuitInterface(document.body, {
title: "Circuit Designer"
});

// Initialize the circuit application
const appManager = new CircuitAppManager();
appManager.initialize(ui.controls, ui.canvas);
});