From 456bf2998443da31d5e84b919cdc71fb4d73d5c5 Mon Sep 17 00:00:00 2001 From: Karim Date: Sun, 5 Jul 2026 19:42:48 +0200 Subject: [PATCH] =?UTF-8?q?Headless-Titelleiste=20(Tauri=20decorations:fal?= =?UTF-8?q?se):=20Fensterkn=C3=B6pfe=20links=20+=20Drag-Region,=20Tauri-Fe?= =?UTF-8?q?nstersteuerung=20+=20Window-Capabilities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/capabilities/default.json | 16 +++++ src-tauri/tauri.conf.json | 4 +- src/styles.css | 3 +- src/ui/TopBar.tsx | 16 +++-- src/ui/WindowControls.tsx | 97 +++++++++++++++++++++-------- 5 files changed, 103 insertions(+), 33 deletions(-) create mode 100644 src-tauri/capabilities/default.json diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..26b0bbb --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,16 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Basis-Permissions + Fenstersteuerung fuer die randlose (decorations:false) Titelleiste: Minimieren/Maximieren/Schliessen + Ziehen der eigenen Titelleiste (data-tauri-drag-region).", + "windows": ["main"], + "permissions": [ + "core:default", + "core:window:allow-minimize", + "core:window:allow-maximize", + "core:window:allow-unmaximize", + "core:window:allow-toggle-maximize", + "core:window:allow-is-maximized", + "core:window:allow-close", + "core:window:allow-start-dragging" + ] +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 8cb7f7b..8032b5e 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -10,10 +10,12 @@ "app": { "windows": [ { + "label": "main", "title": "cad", "width": 1400, "height": 900, - "dragDropEnabled": false + "dragDropEnabled": false, + "decorations": false } ], "security": { diff --git a/src/styles.css b/src/styles.css index f71953a..fca0bb6 100644 --- a/src/styles.css +++ b/src/styles.css @@ -5317,7 +5317,8 @@ body { display: inline-flex; align-items: center; gap: 2px; - margin-left: 6px; + /* Ganz links in der randlosen Titelleiste (macOS-Konvention). */ + margin-right: 4px; flex: 0 0 auto; } .win-btn { diff --git a/src/ui/TopBar.tsx b/src/ui/TopBar.tsx index d3affba..f532ab2 100644 --- a/src/ui/TopBar.tsx +++ b/src/ui/TopBar.tsx @@ -1299,7 +1299,15 @@ export function TopBar({ }, []); return ( -
+ // `data-tauri-drag-region`: die randlose (decorations:false) Titelleiste ist + // selbst der Fenster-Ziehbereich. Kind-Buttons/Dropdowns bleiben klickbar; + // nur die leeren Zonen ziehen das Fenster. +
+ {/* Fensterknöpfe (Schliessen/Minimieren/Maximieren) GANZ LINKS — die + randlose Titelleiste beginnt mit ihnen (macOS-Konvention). Nur unter + Electron/Tauri sichtbar (sonst null). */} + + {/* Kleine Wortmarke „dossier" (OCS-Stil, klein/kleingeschrieben) + Quick- Access-Leiste: alle Datei-/Export-Aktionen + Ressourcen/Einstellungen als kleine Icons direkt in der Zeile (ersetzt das Burger-Menü). */} @@ -1328,12 +1336,12 @@ export function TopBar({ Bänder: diese schmale Leiste (Chrome + Tabs) und den Inhalt. */} - {/* Rechte Gruppe: Layout-Menü · Projektname · Fensterknöpfe. Datei-/Export- - Aktionen liegen jetzt in der Quick-Access-Leiste links (kein Burger). */} + {/* Rechte Gruppe: Layout-Menü · Projektname. Die Fensterknöpfe sitzen jetzt + ganz links (macOS-Titelleisten-Konvention); Datei-/Export-Aktionen in + der Quick-Access-Leiste (kein Burger). */}
{layoutMenu} {project.name} -
); diff --git a/src/ui/WindowControls.tsx b/src/ui/WindowControls.tsx index 1d4e735..76b31b8 100644 --- a/src/ui/WindowControls.tsx +++ b/src/ui/WindowControls.tsx @@ -1,33 +1,75 @@ -// Eigene Fenstersteuerung (Minimieren/Maximieren/Schliessen) für das randlose -// Electron-Fenster (frame:false, s. scripts/electron-main.cjs) — ohne native -// Titelleiste gibt es sonst keine Möglichkeit, das Fenster zu steuern. +// Eigene Fenstersteuerung (Minimieren/Maximieren/Schliessen) für das RANDLOSE +// Fenster — sowohl Electron (frame:false, s. scripts/electron-main.cjs) ALS AUCH +// Tauri (decorations:false, s. tauri.conf.json). Ohne native Titelleiste gibt es +// sonst keine Möglichkeit, das Fenster zu steuern. // -// Rendert NUR, wenn `window.dossierWindow` existiert (vom Preload-Skript -// injiziert) — also nur unter Electron. In Tauri (native Fensterdekoration) -// und im Browser-Dev-Modus (?vite) bleibt die Komponente unsichtbar (`null`), -// dort steuert das Betriebssystem bzw. der Browser das Fenster. +// Rendert NUR, wenn eine Fenster-API vorhanden ist: unter Electron +// `window.dossierWindow` (Preload), unter Tauri `getCurrentWindow()` +// (@tauri-apps/api/window, dynamisch geladen wie in nativeSync.ts). Im reinen +// Browser-Dev-Modus (?vite) bleibt die Komponente unsichtbar (`null`) — dort +// steuert der Browser das Fenster. Die Tauri-Permissions liegen in +// src-tauri/capabilities/default.json (core:window:allow-minimize/…/close). import { useEffect, useState } from "react"; -/** Von electron-preload.cjs via contextBridge injiziert (nur unter Electron). */ -interface DossierWindowApi { +/** Gemeinsame, minimale Fenster-API (Electron-Preload ODER Tauri-Adapter). */ +interface WindowApi { minimize: () => void; toggleMaximize: () => void; close: () => void; isMaximized: () => Promise; + /** Abo auf Maximiert-Wechsel; liefert eine Abmelde-Funktion. */ onMaximizedChange: (callback: (isMaximized: boolean) => void) => () => void; } declare global { interface Window { - dossierWindow?: DossierWindowApi; + dossierWindow?: WindowApi; } } +/** Tauri-Laufzeit erkannt (analog nativeSync.ts). */ +function isTauri(): boolean { + return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window; +} + export function WindowControls() { - const api = window.dossierWindow; + const [api, setApi] = useState(null); const [maximized, setMaximized] = useState(false); + // Passende Fenster-API einmalig auflösen: Electron synchron, Tauri asynchron + // (dynamischer Import, damit Browser-/Build-Pfad ohne @tauri-apps grün bleibt). + useEffect(() => { + if (window.dossierWindow) { + setApi(window.dossierWindow); + return; + } + if (!isTauri()) return; + let disposed = false; + import(/* @vite-ignore */ "@tauri-apps/api/window") + .then(({ getCurrentWindow }) => { + if (disposed) return; + const w = getCurrentWindow(); + setApi({ + minimize: () => void w.minimize(), + toggleMaximize: () => void w.toggleMaximize(), + close: () => void w.close(), + isMaximized: () => w.isMaximized(), + onMaximizedChange: (cb) => { + // Bei jeder Größenänderung den Maximiert-Zustand neu melden. + const unlisten = w.onResized(() => { + void w.isMaximized().then(cb); + }); + return () => void unlisten.then((fn) => fn()); + }, + }); + }) + .catch(() => {}); + return () => { + disposed = true; + }; + }, []); + useEffect(() => { if (!api) return; let cancelled = false; @@ -43,8 +85,25 @@ export function WindowControls() { if (!api) return null; + // Reihenfolge (macOS-Titelleiste links): Schliessen · Minimieren · Maximieren. return (
+ -
); }