waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app src main menue.ts

7,2 KB Rohdatei
app/src/main/menue.ts — 214 Zeilen
1 /**
2 * Deutschsprachiges Anwendungsmenü.
3 *
4 * Das Menü ist auch ein Barrierefreiheits-Merkmal: Zoom, Vollbild und
5 * Neu-Laden sind darüber per Tastatur und für Screenreader erreichbar
6 * beschriftet. Deshalb wird die Menüleiste nicht ausgeblendet.
7 */
8
9 import { app, BrowserWindow, Menu, type MenuItemConstructorOptions } from 'electron';
10
11 import { KANAL_MENUE_BEFEHL, type Menuebefehl } from '../shared/ipc';
12 import { anzeigeVergroessern, anzeigeVerkleinern, anzeigeZuruecksetzen } from './anzeige';
13
14 /**
15 * Schickt einen Befehl an das Fenster, in dem gerade gearbeitet wird.
16 *
17 * Erst das fokussierte, sonst das erste vorhandene: Ein Menübefehl ohne
18 * Fenster ist kein Fehlerfall, der jemanden erreichen müsste – er ist
19 * einfach wirkungslos.
20 */
21 function befehlSenden(befehl: Menuebefehl): void {
22 const fenster = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0];
23 fenster?.webContents.send(KANAL_MENUE_BEFEHL, befehl);
24 }
25
26 export function menueEinrichten(): void {
27 const istMac = process.platform === 'darwin';
28
29 const macAnwendungsMenue: MenuItemConstructorOptions[] = istMac
30 ? [
31 {
32 label: app.name,
33 submenu: [
34 { role: 'about', label: `Über ${app.name}` },
35 { type: 'separator' },
36 { role: 'hide', label: `${app.name} ausblenden` },
37 { role: 'hideOthers', label: 'Andere ausblenden' },
38 { role: 'unhide', label: 'Alle einblenden' },
39 { type: 'separator' },
40 { role: 'quit', label: `${app.name} beenden` },
41 ],
42 },
43 ]
44 : [];
45
46 const vorlage: MenuItemConstructorOptions[] = [
47 ...macAnwendungsMenue,
48 {
49 label: '&Datei',
50 submenu: istMac
51 ? [{ role: 'close', label: 'Fenster schließen' }]
52 : [{ role: 'quit', label: 'Beenden' }],
53 },
54 {
55 label: '&Bearbeiten',
56 submenu: [
57 { role: 'undo', label: 'Rückgängig' },
58 { role: 'redo', label: 'Wiederholen' },
59 { type: 'separator' },
60 { role: 'cut', label: 'Ausschneiden' },
61 { role: 'copy', label: 'Kopieren' },
62 { role: 'paste', label: 'Einfügen' },
63 { role: 'selectAll', label: 'Alles auswählen' },
64 ],
65 },
66 {
67 label: '&Ansicht',
68 submenu: [
69 { role: 'reload', label: 'Neu laden' },
70 { type: 'separator' },
71 /* Eigene Aktionen statt der Electron-Rollen: Nur so läuft der Zoom
72 durch dieselbe Stelle wie die Schaltflächen in den Einstellungen –
73 wird begrenzt, gespeichert und angezeigt. Die Rollen änderten die
74 Größe unbegrenzt und vergaben sie beim nächsten Start. */
75 /*
76 `registerAccelerator: false` außerhalb von macOS: Die Kombination
77 steht im Menü, wird aber NICHT beim Betriebssystem angemeldet —
78 abgefangen wird sie im Renderer, siehe `hooks/useZoomtasten.ts`.
79
80 Der Grund ist gemessen, nicht vermutet: Electron liest den Namen
81 `Plus` als die umgeschaltete Form von `=` und registriert damit
82 faktisch `Strg+Umschalt+=`. Auf einer deutschen Tastatur ist `+`
83 eine eigene Taste ohne Umschalt — die Kombination traf dort nie zu,
84 und `Strg` und `+` taten schlicht nichts.
85
86 Unter macOS bleibt der Beschleuniger angemeldet: Dort ist die Option
87 wirkungslos, und beide Wege zusammen zoomten doppelt.
88 */
89 {
90 label: 'Größer',
91 accelerator: 'CommandOrControl+Plus',
92 registerAccelerator: istMac,
93 click: () => {
94 anzeigeVergroessern();
95 },
96 },
97 {
98 label: 'Kleiner',
99 accelerator: 'CommandOrControl+-',
100 registerAccelerator: istMac,
101 click: () => {
102 anzeigeVerkleinern();
103 },
104 },
105 {
106 label: 'Normalgröße',
107 accelerator: 'CommandOrControl+0',
108 registerAccelerator: istMac,
109 click: () => {
110 anzeigeZuruecksetzen();
111 },
112 },
113 { type: 'separator' },
114 { role: 'togglefullscreen', label: 'Vollbild' },
115 { role: 'toggleDevTools', label: 'Entwicklerwerkzeuge' },
116 ],
117 },
118 {
119 /*
120 Das Menue ist die einzige Flaeche des Fensters, die nicht wegrollt.
121
122 Bis Fassung 0.25.0 fuehrte es zu keiner einzigen der zehn Ansichten.
123 Der Startbildschirm ist bei 1265 Bildpunkten Breite 8530 hoch
124 (gemessen an `docs/bildschirmfotos/01-startbildschirm-hell.png`), und
125 das Sprungmenue der Seite steht bei rund 1000 Bildpunkten: Wer daran
126 vorbeigerollt war, hatte fuer die restlichen rund 7500 Bildpunkte
127 keinen Weg mehr in eine andere Ansicht.
128
129 Warum hier und nicht als klebende Leiste im Fenster: Eine solche
130 Leiste naehme bei 400 Prozent Anzeigegroesse gemessen 155 bis 192 von
131 256 Bildpunkten Sichtfeld, und sie laege ueber den Sprungzielen, die
132 alle drei Sprungmechanismen mit `scrollIntoView({ block: 'start' })`
133 auf y = 0 setzen (WCAG 2.4.11). Ein Menue kostet keine Bildschirmhoehe
134 und steht im Zugaenglichkeitsbaum des Betriebssystems.
135
136 Keine Beschleuniger: Strg und ein Buchstabe kollidierte mit den
137 Kuerzeln der Lernsitzung, und die Alt-Folge (Alt, G, ...) ist unter
138 Windows ohnehin da. Die Reihenfolge folgt dem Startbildschirm.
139 */
140 label: '&Gehe zu',
141 submenu: [
142 {
143 label: 'Startbildschirm',
144 click: () => {
145 befehlSenden('gehe-zu-start');
146 },
147 },
148 { type: 'separator' },
149 {
150 label: 'Kapitel wählen',
151 click: () => {
152 befehlSenden('gehe-zu-kapitelwahl');
153 },
154 },
155 {
156 label: 'Prüfungssimulation',
157 click: () => {
158 befehlSenden('gehe-zu-pruefungswahl');
159 },
160 },
161 { type: 'separator' },
162 {
163 label: 'Fragen durchsuchen',
164 click: () => {
165 befehlSenden('gehe-zu-suche');
166 },
167 },
168 {
169 label: 'Glossar',
170 click: () => {
171 befehlSenden('gehe-zu-glossar');
172 },
173 },
174 { type: 'separator' },
175 {
176 label: 'Über diese Software',
177 click: () => {
178 befehlSenden('gehe-zu-ueber');
179 },
180 },
181 ],
182 },
183 {
184 label: '&Hilfe',
185 role: 'help',
186 /*
187 Vorher stand hier unter macOS ein leeres Menü und unter Windows nur
188 Electrons eigenes Info-Fenster – ein Hilfemenü ohne Hilfe.
189
190 F1 ist bewusst ein Menü-Accelerator und kein Tastenfang im Renderer:
191 So wirkt die Taste auch dann, wenn der Fokus in einem Textfeld steht
192 (etwa bei einer offenen Frage), und sie steht sichtbar im Menü –
193 WCAG 3.2.6 verlangt eine auffindbare Hilfe, nicht eine geheime.
194 */
195 submenu: [
196 {
197 label: 'Handbuch',
198 accelerator: 'F1',
199 click: () => {
200 befehlSenden('hilfe');
201 },
202 },
203 ...(istMac
204 ? []
205 : [
206 { type: 'separator' } as MenuItemConstructorOptions,
207 { role: 'about', label: `Über ${app.name}` } as MenuItemConstructorOptions,
208 ]),
209 ],
210 },
211 ];
212
213 Menu.setApplicationMenu(Menu.buildFromTemplate(vorlage));
214 }