Multi-Window Architecture Design
Window Manager Pattern
// WindowManager.js - Window Manager Implementation
const { BrowserWindow, ipcMain } = require('electron');
const path = require('path');
class WindowManager {
constructor() {
this.windows = new Map(); // Store all window instances
this.setupIPC(); // Initialize IPC communication
}
// Create new window
createWindow(windowId, options = {}) {
if (this.windows.has(windowId)) {
console.warn(`Window ${windowId} already exists`);
return this.windows.get(windowId);
}
const defaultOptions = {
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
};
const win = new BrowserWindow({
...defaultOptions,
...options
});
// Window lifecycle management
win.on('closed', () => {
this.windows.delete(windowId);
win = null; // Release reference
});
this.windows.set(windowId, win);
return win;
}
// Get window instance
getWindow(windowId) {
return this.windows.get(windowId);
}
// Close window
closeWindow(windowId) {
const win = this.getWindow(windowId);
if (win) {
win.close();
}
}
// Setup IPC communication
setupIPC() {
// Create window
ipcMain.handle('window:create', (event, windowId, options) => {
return this.createWindow(windowId, options);
});
// Close window
ipcMain.handle('window:close', (event, windowId) => {
this.closeWindow(windowId);
});
// Window communication
ipcMain.handle('window:send', (event, windowId, channel, ...args) => {
const win = this.getWindow(windowId);
if (win) {
win.webContents.send(channel, ...args);
}
});
}
// Get all windows
getAllWindows() {
return Array.from(this.windows.values());
}
}
// Export singleton
module.exports = new WindowManager();
Window Creation and Communication
Window Creation Example
// main.js - Main Process
const { app } = require('electron');
const WindowManager = require('./WindowManager');
app.whenReady().then(() => {
// Create main window
const mainWindow = WindowManager.createWindow('main', {
width: 1024,
height: 768,
title: 'Main Window'
});
mainWindow.loadFile('main.html');
// Example: Delayed creation of settings window
setTimeout(() => {
WindowManager.createWindow('settings', {
parent: mainWindow, // Set parent window
modal: false,
width: 600,
height: 400,
title: 'Settings'
}).loadFile('settings.html');
}, 2000);
});
Inter-Window Communication
Event-Based Bus Pattern
// eventBus.js - Cross-window event bus
const { ipcRenderer } = require('electron');
class EventBus {
constructor() {
this.channels = new Map();
}
// Subscribe to event
on(channel, callback) {
if (!this.channels.has(channel)) {
this.channels.set(channel, new Set());
}
this.channels.get(channel).add(callback);
// Listen for messages from main process
ipcRenderer.on(`event:${channel}`, (_, ...args) => {
callback(...args);
});
}
// Emit event
emit(channel, ...args) {
// Send to main process for broadcasting
ipcRenderer.send('event:broadcast', channel, ...args);
}
}
// Export singleton
module.exports = new EventBus();
Main Process Event Forwarding
// main.js - Event forwarding
const { ipcMain } = require('electron');
const WindowManager = require('./WindowManager');
// Event forwarding system
ipcMain.on('event:broadcast', (event, channel, ...args) => {
// Get all windows
const windows = WindowManager.getAllWindows();
// Broadcast to all windows
windows.forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send(`event:${channel}`, ...args);
}
});
});
Renderer Process Communication Example
// renderer.js - Renderer process usage example
const eventBus = require('./eventBus');
// Subscribe to event
eventBus.on('data-updated', (data) => {
console.log('Received data update:', data);
document.getElementById('data-display').textContent = JSON.stringify(data);
});
// Emit event
document.getElementById('update-btn').addEventListener('click', () => {
eventBus.emit('request-data', { requestId: Date.now() });
});
Window Lifecycle Management
Window State Persistence
// windowState.js - Window state management
const { ipcRenderer } = require('electron');
const Store = require('electron-store');
const store = new Store();
class WindowState {
constructor(windowId) {
this.windowId = windowId;
this.stateKey = `window-state-${windowId}`;
}
// Save window state
saveState(bounds) {
store.set(this.stateKey, bounds);
}
// Load window state
loadState() {
return store.get(this.stateKey, {
x: undefined,
y: undefined,
width: 800,
height: 600
});
}
}
// Usage example
ipcRenderer.on('window:will-move', (_, bounds) => {
windowState.saveState(bounds);
});
// Apply state when creating window
const initialState = windowState.loadState();
const win = new BrowserWindow({
...initialState,
// Other configurations...
});
Window Restoration Strategy
// Window restoration manager
class WindowRestorer {
constructor() {
this.pendingWindows = new Map();
}
// Register window to restore
registerWindow(windowId, createFn) {
this.pendingWindows.set(windowId, createFn);
}
// Restore window
restoreWindow(windowId) {
const createFn = this.pendingWindows.get(windowId);
if (createFn) {
createFn();
this.pendingWindows.delete(windowId);
}
}
}
// Main process usage example
const restorer = new WindowRestorer();
// Restore windows on app startup
app.whenReady().then(() => {
const shouldRestore = store.get('should-restore-windows', true);
if (shouldRestore) {
const openWindows = store.get('open-windows', []);
openWindows.forEach(windowId => {
restorer.restoreWindow(windowId);
});
}
});
// Save state on window close
ipcMain.on('window:close', (event, windowId) => {
const win = WindowManager.getWindow(windowId);
if (win) {
const bounds = win.getBounds();
store.set(`window-state-${windowId}`, bounds);
// Record open windows
const openWindows = store.get('open-windows', []);
if (!openWindows.includes(windowId)) {
store.set('open-windows', [...openWindows, windowId]);
}
}
});
Advanced Window Modes
Modal Window Implementation
// modalWindow.js - Modal window management
class ModalWindow {
constructor(parentWindow, options = {}) {
this.parentWindow = parentWindow;
this.options = {
width: 400,
height: 300,
parent: parentWindow,
modal: true,
...options
};
}
create(contentPath) {
this.win = new BrowserWindow(this.options);
this.win.loadFile(contentPath);
// Focus parent window when modal closes
this.win.on('closed', () => {
if (!this.win.isDestroyed()) {
this.win = null;
}
this.parentWindow.focus();
});
return this.win;
}
}
// Usage example
const parentWin = WindowManager.getWindow('main');
const modal = new ModalWindow(parentWin);
modal.create('modal.html');
Document-Associated Windows
// documentWindows.js - Document-associated window management
class DocumentWindows {
constructor() {
this.documentWindows = new Map(); // fileId -> Set<windowId>
}
// Create window for document
createWindowForDocument(fileId, windowId, options) {
if (!this.documentWindows.has(fileId)) {
this.documentWindows.set(fileId, new Set());
}
const winSet = this.documentWindows.get(fileId);
winSet.add(windowId);
// Create window
const win = WindowManager.createWindow(windowId, options);
// Update mapping on window close
win.on('closed', () => {
winSet.delete(windowId);
if (winSet.size === 0) {
this.documentWindows.delete(fileId);
}
});
return win;
}
// Get all windows associated with a document
getWindowsForDocument(fileId) {
const winSet = this.documentWindows.get(fileId);
return winSet ? Array.from(winSet).map(WindowManager.getWindow) : [];
}
}
// Usage example
const docWindows = new DocumentWindows();
docWindows.createWindowForDocument('doc-123', 'editor-1', {
title: 'Document Editor - Document 123'
});
Window Lazy Loading
// Lazy loading window manager
class LazyWindowManager {
constructor() {
this.windowPromises = new Map(); // windowId -> Promise<BrowserWindow>
}
// Get window (lazy loading)
async getWindow(windowId, createFn) {
if (this.windowPromises.has(windowId)) {
return this.windowPromises.get(windowId);
}
const promise = (async () => {
if (WindowManager.hasWindow(windowId)) {
return WindowManager.getWindow(windowId);
}
const win = await createFn();
return win;
})();
this.windowPromises.set(windowId, promise);
return promise;
}
// Clear cache
clearCache(windowId) {
this.windowPromises.delete(windowId);
}
}
// Usage example
const lazyManager = new LazyWindowManager();
// Create window on first access
async function showSettings() {
const win = await lazyManager.getWindow('settings', () => {
return WindowManager.createWindow('settings', {
/* Configuration */
});
});
win.show();
}
Resource Isolation Strategy
// Resource-isolated window configuration
const isolatedWindow = new BrowserWindow({
// ...
webPreferences: {
sandbox: true, // Enable sandbox
contextIsolation: true,
preload: path.join(__dirname, 'isolatedPreload.js'), // Dedicated preload script
partition: 'persist:isolated' // Isolated session partition
}
});
// Isolated preload script (isolatedPreload.js)
const { contextBridge } = require('electron');
// Expose only necessary APIs
contextBridge.exposeInMainWorld('isolatedAPI', {
fetchData: () => ipcRenderer.invoke('isolated-fetch')
});