Lesson 03-Electron User Interface

Window Management and Interface Basics

Window Creation and Configuration

Basic Window Creation

const { BrowserWindow } = require('electron');
const path = require('path');

// Create main window
const mainWindow = new BrowserWindow({
  width: 1200,
  height: 800,
  webPreferences: {
    nodeIntegration: false, // Disable automatic Node integration
    contextIsolation: true, // Enable context isolation
    preload: path.join(__dirname, 'preload.js') // Preload script path
  }
});

// Load interface
mainWindow.loadFile('index.html'); // Load local HTML file
// or mainWindow.loadURL('https://example.com'); // Load remote URL

Window Types and Styles

// Frameless window (suitable for custom title bars)
const framelessWindow = new BrowserWindow({
  frame: false, // No native frame
  transparent: true, // Support transparent background
  webPreferences: { /* ... */ }
});

// Fullscreen window
const fullscreenWindow = new BrowserWindow({
  fullscreen: true, // Start in fullscreen
  kiosk: false // Non-kiosk mode (can exit fullscreen)
});

// Multi-window management example
function createChildWindow() {
  const childWindow = new BrowserWindow({
    parent: mainWindow, // Specify parent window
    modal: true, // Modal window
    width: 600,
    height: 400
  });
  childWindow.loadFile('child.html');
}

Interface Layout Techniques

HTML/CSS Layout Solutions

<!-- Flexbox layout example -->
<div class="app-container">
  <header class="app-header">Title Bar</header>
  <div class="content-area">
    <aside class="sidebar">Sidebar</aside>
    <main class="main-content">Main Content Area</main>
  </div>
</div>

<style>
.app-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
}
.content-area {
  display: flex;
  flex: 1;
}
.sidebar {
  width: 200px;
  background: #f0f0f0;
}
.main-content {
  flex: 1;
  padding: 20px;
}
</style>

Modern Frontend Framework Integration

// React integration example
// 1. Expose IPC methods in preload script
window.electronAPI = {
  sendData: (data) => ipcRenderer.send('data-channel', data)
};

// 2. Use in React component
import { useEffect } from 'react';

function App() {
  useEffect(() => {
    window.electronAPI.sendData({ message: 'Hello from React' });
  }, []);

  return <div>React + Electron Application</div>;
}

Renderer Process UI Development

DOM Operations and Event Handling

Basic DOM Operations

// Safe DOM operation example (avoid direct manipulation)
document.addEventListener('DOMContentLoaded', () => {
  const button = document.getElementById('myButton');
  if (button) {
    button.addEventListener('click', () => {
      // Communicate with main process via IPC
      window.electronAPI.handleButtonClick();
    });
  }
});

Custom Element Development

// Define Web Component
class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>:host { display: block; }</style>
      <div class="container"></div>
    `;
  }

  connectedCallback() {
    this.shadowRoot.querySelector('.container').textContent = 'Custom Element';
  }
}

customElements.define('my-element', MyElement);

Responsive Design Implementation

Viewport and Media Queries

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<style>
/* Desktop styles */
.container { padding: 20px; }

/* Tablet devices */
@media (max-width: 768px) {
  .container { padding: 10px; }
}

/* Mobile devices */
@media (max-width: 480px) {
  .container { padding: 5px; }
}
</style>

Dynamic Layout Adjustment

// Listen for window resize
window.addEventListener('resize', () => {
  const width = window.innerWidth;
  const height = window.innerHeight;

  if (width < 768) {
    // Mobile layout logic
    document.body.classList.add('mobile-layout');
  } else {
    // Desktop layout logic
    document.body.classList.remove('mobile-layout');
  }
});

Native UI Element Integration

System Native Dialogs

// Expose dialog methods in preload script
const { dialog } = require('electron');

window.electronAPI.showDialog = (options) => {
  return dialog.showOpenDialog({
    properties: ['openFile'],
    filters: [{ name: 'Images', extensions: ['png', 'jpg'] }]
  });
};

// Call in renderer process
async function openFile() {
  const { canceled, filePaths } = await window.electronAPI.showDialog();
  if (!canceled) {
    console.log('Selected file:', filePaths[0]);
  }
}

Native Menus and Tray

Application Menu Configuration

// Create menu in main process
const { Menu } = require('electron');

const template = [
  {
    label: 'File',
    submenu: [
      { role: 'quit' } // System-standard quit operation
    ]
  },
  {
    label: 'Edit',
    submenu: [
      { role: 'undo' },
      { role: 'redo' },
      { type: 'separator' },
      { role: 'cut' },
      { role: 'copy' },
      { role: 'paste' }
    ]
  }
];

const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

System Tray Integration

// Create system tray
const { Tray, nativeImage } = require('electron');

const tray = new Tray(nativeImage.createFromPath('icon.png'));
const contextMenu = Menu.buildFromTemplate([
  { label: 'Show', click: () => mainWindow.show() },
  { label: 'Quit', click: () => app.quit() }
]);

tray.setToolTip('My Application');
tray.setContextMenu(contextMenu);

// Tray click event
tray.on('click', () => {
  mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
});

Advanced UI Techniques

Hardware Acceleration and Graphics Rendering

WebGL Integration

<canvas id="webgl-canvas"></canvas>

<script>
const canvas = document.getElementById('webgl-canvas');
const gl = canvas.getContext('webgl');

// WebGL initialization code
if (!gl) {
  console.error('WebGL unavailable');
} else {
  // Rendering logic...
}
</script>

Canvas Drawing Optimization

// High-performance Canvas drawing example
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

// Use offscreen Canvas for animation optimization
const offscreenCanvas = document.createElement('canvas');
const offscreenCtx = offscreenCanvas.getContext('2d');

function animate() {
  // Draw on offscreen Canvas
  offscreenCtx.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
  // ...drawing operations

  // Draw to visible Canvas in one go
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(offscreenCanvas, 0, 0);

  requestAnimationFrame(animate);
}

animate();

Cross-Process UI Synchronization

State Sharing Solution

// Use main process to manage shared state
// main.js
const { ipcMain } = require('electron');
let sharedState = { theme: 'light' };

ipcMain.handle('get-state', () => sharedState);
ipcMain.on('update-state', (event, newState) => {
  sharedState = { ...sharedState, ...newState };
});

// Access state in renderer process
// renderer.js
async function getState() {
  return await window.electronAPI.getState();
}

async function updateState(newState) {
  await window.electronAPI.updateState(newState);
}

Real-Time UI Updates

// Use event-driven UI updates
// Main process
ipcMain.on('data-updated', (event, data) => {
  // Broadcast to all windows
  BrowserWindow.getAllWindows().forEach(win => {
    win.webContents.send('update-ui', data);
  });
});

// Renderer process
window.electronAPI.onUpdateUI((event, data) => {
  // Update DOM elements
  document.getElementById('data-display').textContent = data.value;
});

UI Performance Optimization

Rendering Performance Optimization Strategies

Virtual List Implementation

// Optimize large dataset lists
class VirtualList {
  constructor(container, itemHeight, renderItem) {
    this.container = container;
    this.itemHeight = itemHeight;
    this.renderItem = renderItem;
    this.visibleItems = [];

    this.container.addEventListener('scroll', this.handleScroll.bind(this));
    this.updateVisibleItems();
  }

  handleScroll() {
    requestAnimationFrame(this.updateVisibleItems.bind(this));
  }

  updateVisibleItems() {
    const scrollTop = this.container.scrollTop;
    const visibleStart = Math.floor(scrollTop / this.itemHeight);
    const visibleEnd = visibleStart + Math.ceil(this.container.clientHeight / this.itemHeight);

    // Render only visible items
    this.renderVisibleItems(visibleStart, visibleEnd);
  }
}

CSS Hardware Acceleration

/* Enable GPU acceleration */
.animated-element {
  transform: translateZ(0); /* Trigger hardware acceleration */
  will-change: transform;   /* Hint browser for optimization */
}

/* Avoid repaint properties */
.optimized-element {
  transform: scale(1);      /* Use transform instead of width/height changes */
  opacity: 1;               /* Use opacity instead of visibility changes */
}

Memory Management Optimization

Resource Release Strategies

// Image resource management example
class ImageManager {
  constructor() {
    this.cache = new Map();
  }

  loadImage(url) {
    if (this.cache.has(url)) {
      return this.cache.get(url);
    }

    const img = new Image();
    img.src = url;
    this.cache.set(url, img);

    // Memory management: Limit cache size
    if (this.cache.size > 100) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }

    return img;
  }

  clearCache() {
    this.cache.clear();
  }
}

// Release resources when component unmounts
class MyComponent {
  constructor() {
    this.imageManager = new ImageManager();
  }

  destroy() {
    this.imageManager.clearCache();
    // Other resource cleanup...
  }
}

Loading Performance Optimization

Resource Preloading Techniques

// Preload critical resources
const preloadResources = [
  'styles/main.css',
  'scripts/vendor.js',
  'images/logo.png'
];

function preload() {
  preloadResources.forEach(resource => {
    const link = document.createElement('link');
    link.rel = 'preload';
    link.href = resource;

    if (resource.endsWith('.css')) {
      link.as = 'style';
    } else if (resource.endsWith('.js')) {
      link.as = 'script';
    } else {
      link.as = 'image';
    }

    document.head.appendChild(link);
  });
}

// Call during app initialization
window.addEventListener('DOMContentLoaded', preload);

Lazy Loading Implementation

// Image lazy loading example
const lazyImages = document.querySelectorAll('img[data-src]');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
});

lazyImages.forEach(img => observer.observe(img));

Through the techniques and methods described above, Electron applications can achieve high-performance, visually appealing, and responsive user interfaces while maintaining cross-platform consistency and a native application experience. Developers should select appropriate UI solutions and optimization strategies based on specific application scenarios.

Share your love