Schnittebenen 2D↔3D Phase 1+2, native Fenster, Hell/Dunkel-Theme, SWISSIMAGE-Import
- Schnittebenen: 3D-Live-Schnitt folgt der gewählten Grundriss-Schnittlinie (section3dCutId/sectionPlaneFromLevel), Schalter "Im 3D schneiden" im Objekt-Info, Doppelklick auf Schnittlinie springt in 2D-Schnittansicht, unsichtbare Ebenen blenden ihre Führungslinie aus - Eigene native Tauri-Fenster für Kontext-Import/Zeichnungsebenen/ Ebenen-Einstellungen/Ressourcen/Einstellungen + klassische Menüleiste (AppMenuBar) neben der Wortmarke - Hell/Dunkel-Umschalter (Einstellungen → Darstellung), persistiert, flackerfrei vor erstem Render gesetzt - SWISSIMAGE-Luftbild-Import (swisstopo WMS) als Kontext-Hintergrundebene - UI-Politur: Werkzeug-Panel Symbole/Liste umschaltbar, Topbar-Quick-Access- Icons entfernt, Zahnrad→Einstellungen in Panel-Köpfen, Footerbar/ Snap-Marker/Maß-HUD auf helle Pillen-Sprache umgestellt - Neues Dachziegel-Material (RoofingTiles013A)
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// Einstellungen als EIGENES natives Tauri-Fenster (statt Overlay-Dialog im
|
||||
// Hauptfenster) — Hauptfenster-Seite der Brücke. Gleiches Prinzip wie
|
||||
// resourceWindow.ts, aber schmaler: SettingsDialog braucht nur eine Handvoll
|
||||
// einzelner Felder (nicht das ganze Project), daher ein explizites
|
||||
// Sync-/Command-Paar statt eines generischen Proxys über 27 Handler.
|
||||
//
|
||||
// Hauptfenster → Einstellungs-Fenster: „settings-sync-state" mit
|
||||
// { marqueeColor, snapColor, referenceElevationMasl, rendererMode,
|
||||
// webGpuAvailable, layout } bei jeder Änderung + auf „settings-window-ready".
|
||||
// Einstellungs-Fenster → Hauptfenster: „settings-command" mit
|
||||
// { name, args } — dieselbe {name,args}-RPC-Form wie resource-command.
|
||||
//
|
||||
// Bezeichner englisch, Kommentare deutsch (CONVENTIONS.md).
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { isTauriRuntime } from "../io/projectFile";
|
||||
import type { LayoutState } from "../panels/types";
|
||||
import type { RendererMode } from "../ui/StatusBar";
|
||||
import type { ThemeMode } from "../theme/themeMode";
|
||||
import { BORDERLESS_WINDOW_CHROME } from "./windowChrome";
|
||||
|
||||
const SETTINGS_WINDOW_LABEL = "settings";
|
||||
|
||||
/** Alles, was SettingsDialog zum Rendern braucht — wandert 1:1 rüber. */
|
||||
export interface SettingsSyncState {
|
||||
themeMode: ThemeMode;
|
||||
marqueeColor: string;
|
||||
snapColor: string;
|
||||
referenceElevationMasl: number | undefined;
|
||||
rendererMode: RendererMode;
|
||||
webGpuAvailable: boolean;
|
||||
layout: LayoutState;
|
||||
}
|
||||
|
||||
/** Alle Aktionen, die das Einstellungs-Fenster auslösen kann. */
|
||||
export interface SettingsCommandHandlers {
|
||||
onThemeModeChange: (mode: ThemeMode) => void;
|
||||
onMarqueeColorChange: (hex: string) => void;
|
||||
onSnapColorChange: (hex: string) => void;
|
||||
onReferenceElevationChange: (n: number | undefined) => void;
|
||||
onRendererModeChange: (mode: RendererMode) => void;
|
||||
onApplyLayout: (layout: LayoutState) => void;
|
||||
}
|
||||
|
||||
/** Öffnet das native Einstellungs-Fenster (bzw. holt es nach vorne). */
|
||||
export async function openSettingsWindowNative(): Promise<void> {
|
||||
const { WebviewWindow } = await import("@tauri-apps/api/webviewWindow");
|
||||
const existing = await WebviewWindow.getByLabel(SETTINGS_WINDOW_LABEL);
|
||||
if (existing) {
|
||||
await existing.setFocus();
|
||||
return;
|
||||
}
|
||||
new WebviewWindow(SETTINGS_WINDOW_LABEL, {
|
||||
url: "index.html?window=settings",
|
||||
title: "Einstellungen — Dossier",
|
||||
width: 520,
|
||||
height: 640,
|
||||
minWidth: 420,
|
||||
minHeight: 420,
|
||||
...BORDERLESS_WINDOW_CHROME,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hauptfenster-Seite der Sync-Brücke: hält das Einstellungs-Fenster (falls
|
||||
* offen) synchron zu `state` und wendet dessen Befehle auf `handlers` an.
|
||||
* No-op ausserhalb von Tauri.
|
||||
*/
|
||||
export function useSettingsWindowHost(
|
||||
state: SettingsSyncState,
|
||||
handlers: SettingsCommandHandlers,
|
||||
): void {
|
||||
const handlersRef = useRef(handlers);
|
||||
handlersRef.current = handlers;
|
||||
const stateRef = useRef(state);
|
||||
stateRef.current = state;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTauriRuntime()) return;
|
||||
let disposed = false;
|
||||
const unlisten: Array<() => void> = [];
|
||||
void (async () => {
|
||||
const { listen, emitTo } = await import("@tauri-apps/api/event");
|
||||
if (disposed) return;
|
||||
unlisten.push(
|
||||
await listen<{ name: string; args: unknown[] }>("settings-command", (e) => {
|
||||
const fn = (handlersRef.current as unknown as Record<string, unknown>)[e.payload.name];
|
||||
if (typeof fn === "function") (fn as (...a: unknown[]) => void)(...e.payload.args);
|
||||
}),
|
||||
);
|
||||
unlisten.push(
|
||||
await listen("settings-window-ready", () => {
|
||||
void emitTo(SETTINGS_WINDOW_LABEL, "settings-sync-state", stateRef.current).catch(
|
||||
() => {},
|
||||
);
|
||||
}),
|
||||
);
|
||||
})();
|
||||
return () => {
|
||||
disposed = true;
|
||||
unlisten.forEach((u) => u());
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTauriRuntime()) return;
|
||||
void (async () => {
|
||||
const { emitTo } = await import("@tauri-apps/api/event");
|
||||
await emitTo(SETTINGS_WINDOW_LABEL, "settings-sync-state", state).catch(() => {});
|
||||
})();
|
||||
}, [state]);
|
||||
}
|
||||
Reference in New Issue
Block a user