/** * Deutschsprachiges Anwendungsmenü. * * Das Menü ist auch ein Barrierefreiheits-Merkmal: Zoom, Vollbild und * Neu-Laden sind darüber per Tastatur und für Screenreader erreichbar * beschriftet. Deshalb wird die Menüleiste nicht ausgeblendet. */ import { app, BrowserWindow, Menu, type MenuItemConstructorOptions } from 'electron'; import { KANAL_MENUE_BEFEHL, type Menuebefehl } from '../shared/ipc'; import { anzeigeVergroessern, anzeigeVerkleinern, anzeigeZuruecksetzen } from './anzeige'; /** * Schickt einen Befehl an das Fenster, in dem gerade gearbeitet wird. * * Erst das fokussierte, sonst das erste vorhandene: Ein Menübefehl ohne * Fenster ist kein Fehlerfall, der jemanden erreichen müsste – er ist * einfach wirkungslos. */ function befehlSenden(befehl: Menuebefehl): void { const fenster = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; fenster?.webContents.send(KANAL_MENUE_BEFEHL, befehl); } export function menueEinrichten(): void { const istMac = process.platform === 'darwin'; const macAnwendungsMenue: MenuItemConstructorOptions[] = istMac ? [ { label: app.name, submenu: [ { role: 'about', label: `Über ${app.name}` }, { type: 'separator' }, { role: 'hide', label: `${app.name} ausblenden` }, { role: 'hideOthers', label: 'Andere ausblenden' }, { role: 'unhide', label: 'Alle einblenden' }, { type: 'separator' }, { role: 'quit', label: `${app.name} beenden` }, ], }, ] : []; const vorlage: MenuItemConstructorOptions[] = [ ...macAnwendungsMenue, { label: '&Datei', submenu: istMac ? [{ role: 'close', label: 'Fenster schließen' }] : [{ role: 'quit', label: 'Beenden' }], }, { label: '&Bearbeiten', submenu: [ { role: 'undo', label: 'Rückgängig' }, { role: 'redo', label: 'Wiederholen' }, { type: 'separator' }, { role: 'cut', label: 'Ausschneiden' }, { role: 'copy', label: 'Kopieren' }, { role: 'paste', label: 'Einfügen' }, { role: 'selectAll', label: 'Alles auswählen' }, ], }, { label: '&Ansicht', submenu: [ { role: 'reload', label: 'Neu laden' }, { type: 'separator' }, /* Eigene Aktionen statt der Electron-Rollen: Nur so läuft der Zoom durch dieselbe Stelle wie die Schaltflächen in den Einstellungen – wird begrenzt, gespeichert und angezeigt. Die Rollen änderten die Größe unbegrenzt und vergaben sie beim nächsten Start. */ /* `registerAccelerator: false` außerhalb von macOS: Die Kombination steht im Menü, wird aber NICHT beim Betriebssystem angemeldet — abgefangen wird sie im Renderer, siehe `hooks/useZoomtasten.ts`. Der Grund ist gemessen, nicht vermutet: Electron liest den Namen `Plus` als die umgeschaltete Form von `=` und registriert damit faktisch `Strg+Umschalt+=`. Auf einer deutschen Tastatur ist `+` eine eigene Taste ohne Umschalt — die Kombination traf dort nie zu, und `Strg` und `+` taten schlicht nichts. Unter macOS bleibt der Beschleuniger angemeldet: Dort ist die Option wirkungslos, und beide Wege zusammen zoomten doppelt. */ { label: 'Größer', accelerator: 'CommandOrControl+Plus', registerAccelerator: istMac, click: () => { anzeigeVergroessern(); }, }, { label: 'Kleiner', accelerator: 'CommandOrControl+-', registerAccelerator: istMac, click: () => { anzeigeVerkleinern(); }, }, { label: 'Normalgröße', accelerator: 'CommandOrControl+0', registerAccelerator: istMac, click: () => { anzeigeZuruecksetzen(); }, }, { type: 'separator' }, { role: 'togglefullscreen', label: 'Vollbild' }, { role: 'toggleDevTools', label: 'Entwicklerwerkzeuge' }, ], }, { /* Das Menue ist die einzige Flaeche des Fensters, die nicht wegrollt. Bis Fassung 0.25.0 fuehrte es zu keiner einzigen der (heute elf) Ansichten. Der Startbildschirm ist bei 1265 Bildpunkten Breite 8530 hoch (gemessen an `docs/bildschirmfotos/01-startbildschirm-hell.png`), und das Sprungmenue der Seite steht bei rund 1000 Bildpunkten: Wer daran vorbeigerollt war, hatte fuer die restlichen rund 7500 Bildpunkte keinen Weg mehr in eine andere Ansicht. Warum hier und nicht als klebende Leiste im Fenster: Eine solche Leiste naehme bei 400 Prozent Anzeigegroesse gemessen 155 bis 192 von 256 Bildpunkten Sichtfeld, und sie laege ueber den Sprungzielen, die alle drei Sprungmechanismen mit `scrollIntoView({ block: 'start' })` auf y = 0 setzen (WCAG 2.4.11). Ein Menue kostet keine Bildschirmhoehe und steht im Zugaenglichkeitsbaum des Betriebssystems. Keine Beschleuniger: Strg und ein Buchstabe kollidierte mit den Kuerzeln der Lernsitzung, und die Alt-Folge (Alt, G, ...) ist unter Windows ohnehin da. Die Reihenfolge folgt dem Startbildschirm. */ label: '&Gehe zu', submenu: [ { label: 'Startbildschirm', click: () => { befehlSenden('gehe-zu-start'); }, }, { type: 'separator' }, { label: 'Kapitel wählen', click: () => { befehlSenden('gehe-zu-kapitelwahl'); }, }, { label: 'Prüfungssimulation', click: () => { befehlSenden('gehe-zu-pruefungswahl'); }, }, { type: 'separator' }, { label: 'Fragen durchsuchen', click: () => { befehlSenden('gehe-zu-suche'); }, }, { label: 'Glossar', click: () => { befehlSenden('gehe-zu-glossar'); }, }, { label: 'Gesetze lesen', click: () => { befehlSenden('gehe-zu-gesetze'); }, }, { type: 'separator' }, { label: 'Über diese Software', click: () => { befehlSenden('gehe-zu-ueber'); }, }, ], }, { label: '&Hilfe', role: 'help', /* Vorher stand hier unter macOS ein leeres Menü und unter Windows nur Electrons eigenes Info-Fenster – ein Hilfemenü ohne Hilfe. F1 ist bewusst ein Menü-Accelerator und kein Tastenfang im Renderer: So wirkt die Taste auch dann, wenn der Fokus in einem Textfeld steht (etwa bei einer offenen Frage), und sie steht sichtbar im Menü – WCAG 3.2.6 verlangt eine auffindbare Hilfe, nicht eine geheime. */ submenu: [ { label: 'Handbuch', accelerator: 'F1', click: () => { befehlSenden('hilfe'); }, }, ...(istMac ? [] : [ { type: 'separator' } as MenuItemConstructorOptions, { role: 'about', label: `Über ${app.name}` } as MenuItemConstructorOptions, ]), ], }, ]; Menu.setApplicationMenu(Menu.buildFromTemplate(vorlage)); }