waffensachkunde

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

/ app src main menue.ts

7,4 KB Rohdatei
app/src/main/menue.ts — 221 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 (heute elf)
123 Ansichten.
124 Der Startbildschirm ist bei 1265 Bildpunkten Breite 8530 hoch
125 (gemessen an `docs/bildschirmfotos/01-startbildschirm-hell.png`), und
126 das Sprungmenue der Seite steht bei rund 1000 Bildpunkten: Wer daran
127 vorbeigerollt war, hatte fuer die restlichen rund 7500 Bildpunkte
128 keinen Weg mehr in eine andere Ansicht.
129
130 Warum hier und nicht als klebende Leiste im Fenster: Eine solche
131 Leiste naehme bei 400 Prozent Anzeigegroesse gemessen 155 bis 192 von
132 256 Bildpunkten Sichtfeld, und sie laege ueber den Sprungzielen, die
133 alle drei Sprungmechanismen mit `scrollIntoView({ block: 'start' })`
134 auf y = 0 setzen (WCAG 2.4.11). Ein Menue kostet keine Bildschirmhoehe
135 und steht im Zugaenglichkeitsbaum des Betriebssystems.
136
137 Keine Beschleuniger: Strg und ein Buchstabe kollidierte mit den
138 Kuerzeln der Lernsitzung, und die Alt-Folge (Alt, G, ...) ist unter
139 Windows ohnehin da. Die Reihenfolge folgt dem Startbildschirm.
140 */
141 label: '&Gehe zu',
142 submenu: [
143 {
144 label: 'Startbildschirm',
145 click: () => {
146 befehlSenden('gehe-zu-start');
147 },
148 },
149 { type: 'separator' },
150 {
151 label: 'Kapitel wählen',
152 click: () => {
153 befehlSenden('gehe-zu-kapitelwahl');
154 },
155 },
156 {
157 label: 'Prüfungssimulation',
158 click: () => {
159 befehlSenden('gehe-zu-pruefungswahl');
160 },
161 },
162 { type: 'separator' },
163 {
164 label: 'Fragen durchsuchen',
165 click: () => {
166 befehlSenden('gehe-zu-suche');
167 },
168 },
169 {
170 label: 'Glossar',
171 click: () => {
172 befehlSenden('gehe-zu-glossar');
173 },
174 },
175 {
176 label: 'Gesetze lesen',
177 click: () => {
178 befehlSenden('gehe-zu-gesetze');
179 },
180 },
181 { type: 'separator' },
182 {
183 label: 'Über diese Software',
184 click: () => {
185 befehlSenden('gehe-zu-ueber');
186 },
187 },
188 ],
189 },
190 {
191 label: '&Hilfe',
192 role: 'help',
193 /*
194 Vorher stand hier unter macOS ein leeres Menü und unter Windows nur
195 Electrons eigenes Info-Fenster – ein Hilfemenü ohne Hilfe.
196
197 F1 ist bewusst ein Menü-Accelerator und kein Tastenfang im Renderer:
198 So wirkt die Taste auch dann, wenn der Fokus in einem Textfeld steht
199 (etwa bei einer offenen Frage), und sie steht sichtbar im Menü –
200 WCAG 3.2.6 verlangt eine auffindbare Hilfe, nicht eine geheime.
201 */
202 submenu: [
203 {
204 label: 'Handbuch',
205 accelerator: 'F1',
206 click: () => {
207 befehlSenden('hilfe');
208 },
209 },
210 ...(istMac
211 ? []
212 : [
213 { type: 'separator' } as MenuItemConstructorOptions,
214 { role: 'about', label: `Über ${app.name}` } as MenuItemConstructorOptions,
215 ]),
216 ],
217 },
218 ];
219
220 Menu.setApplicationMenu(Menu.buildFromTemplate(vorlage));
221 }