Merge pull request #7 from JezzWTF/feat/custom-titlebar
feat: custom TitleBar with window controls (no native decorations)
This commit was merged in pull request #7.
This commit is contained in:
@@ -12,6 +12,10 @@
|
|||||||
"fs:allow-read-file",
|
"fs:allow-read-file",
|
||||||
"fs:allow-read-dir",
|
"fs:allow-read-dir",
|
||||||
"fs:read-files",
|
"fs:read-files",
|
||||||
"fs:read-dirs"
|
"fs:read-dirs",
|
||||||
|
"core:window:allow-minimize",
|
||||||
|
"core:window:allow-close",
|
||||||
|
"core:window:allow-toggle-maximize",
|
||||||
|
"core:window:allow-is-maximized"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
"width": 1280,
|
"width": 1280,
|
||||||
"height": 800,
|
"height": 800,
|
||||||
"minWidth": 800,
|
"minWidth": 800,
|
||||||
"minHeight": 600
|
"minHeight": 600,
|
||||||
|
"decorations": false,
|
||||||
|
"shadow": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
+24
-16
@@ -6,6 +6,7 @@ import { Toolbar } from "./components/Toolbar";
|
|||||||
import { Gallery } from "./components/Gallery";
|
import { Gallery } from "./components/Gallery";
|
||||||
import { Lightbox } from "./components/Lightbox";
|
import { Lightbox } from "./components/Lightbox";
|
||||||
import { TagCloud } from "./components/TagCloud";
|
import { TagCloud } from "./components/TagCloud";
|
||||||
|
import { TitleBar } from "./components/TitleBar";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const loadFolders = useGalleryStore((state) => state.loadFolders);
|
const loadFolders = useGalleryStore((state) => state.loadFolders);
|
||||||
@@ -29,22 +30,29 @@ export default function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen bg-gray-950 text-white overflow-hidden select-none">
|
<div className="flex h-screen flex-col bg-gray-950 text-white overflow-hidden select-none">
|
||||||
<Sidebar />
|
{/* Custom title bar — sits at the very top */}
|
||||||
<main className="flex-1 flex flex-col min-w-0">
|
<TitleBar />
|
||||||
{activeView === "explore" ? (
|
|
||||||
<>
|
{/* Main app content below the title bar */}
|
||||||
<BackgroundTasks />
|
<div className="flex flex-1 min-h-0">
|
||||||
<TagCloud />
|
<Sidebar />
|
||||||
</>
|
<main className="flex-1 flex flex-col min-w-0">
|
||||||
) : (
|
{activeView === "explore" ? (
|
||||||
<>
|
<>
|
||||||
<Toolbar />
|
<BackgroundTasks />
|
||||||
<BackgroundTasks />
|
<TagCloud />
|
||||||
<Gallery />
|
</>
|
||||||
</>
|
) : (
|
||||||
)}
|
<>
|
||||||
</main>
|
<Toolbar />
|
||||||
|
<BackgroundTasks />
|
||||||
|
<Gallery />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Lightbox />
|
<Lightbox />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||||
|
|
||||||
|
// SVG icons for window controls
|
||||||
|
function MinimizeIcon() {
|
||||||
|
return (
|
||||||
|
<svg width="10" height="2" viewBox="0 0 10 2" fill="none">
|
||||||
|
<rect y="0.5" width="10" height="1" rx="0.5" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MaximizeIcon() {
|
||||||
|
return (
|
||||||
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none">
|
||||||
|
<rect x="0.5" y="0.5" width="9" height="9" rx="1.5" stroke="currentColor" strokeWidth="1" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RestoreIcon() {
|
||||||
|
return (
|
||||||
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none">
|
||||||
|
<rect x="2.5" y="0.5" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1" />
|
||||||
|
<rect x="0.5" y="2.5" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1" fill="#030712" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CloseIcon() {
|
||||||
|
return (
|
||||||
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none">
|
||||||
|
<path d="M1 1L9 9M9 1L1 9" stroke="currentColor" strokeWidth="1.25" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TitleBar() {
|
||||||
|
const [isMaximized, setIsMaximized] = useState(false);
|
||||||
|
const appWindow = getCurrentWindow();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Get initial maximized state
|
||||||
|
appWindow.isMaximized().then(setIsMaximized);
|
||||||
|
|
||||||
|
// Listen for resize events to update maximized state
|
||||||
|
const unlisten = appWindow.onResized(() => {
|
||||||
|
appWindow.isMaximized().then(setIsMaximized);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unlisten.then((fn) => fn());
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMinimize = () => appWindow.minimize();
|
||||||
|
const handleMaximize = () => appWindow.toggleMaximize();
|
||||||
|
const handleClose = () => appWindow.close();
|
||||||
|
|
||||||
|
return (
|
||||||
|
// data-tauri-drag-region is the recommended Tauri approach for drag regions.
|
||||||
|
// WebkitAppRegion is kept as a CSS fallback for compatibility.
|
||||||
|
<div
|
||||||
|
data-tauri-drag-region
|
||||||
|
className="titlebar relative z-50 flex h-9 shrink-0 items-center bg-gray-950 select-none"
|
||||||
|
style={{ WebkitAppRegion: "drag" } as React.CSSProperties}
|
||||||
|
>
|
||||||
|
{/* App icon + name — left side */}
|
||||||
|
<div className="flex items-center gap-2 pl-3 pr-4">
|
||||||
|
<div className="flex h-5 w-5 items-center justify-center rounded-md bg-white/8 overflow-hidden">
|
||||||
|
{/* Phokus logo placeholder — replace with <img src={logo} /> if you have an SVG */}
|
||||||
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||||
|
<circle cx="6" cy="6" r="4" stroke="#a78bfa" strokeWidth="1.5" />
|
||||||
|
<circle cx="6" cy="6" r="1.5" fill="#a78bfa" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<span className="text-[11px] font-semibold text-gray-400 tracking-wide">Phokus</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Spacer — draggable region fills here */}
|
||||||
|
<div data-tauri-drag-region className="flex-1" />
|
||||||
|
|
||||||
|
{/* Window control buttons — right side, non-draggable */}
|
||||||
|
<div
|
||||||
|
className="flex items-stretch h-full"
|
||||||
|
style={{ WebkitAppRegion: "no-drag" } as React.CSSProperties}
|
||||||
|
>
|
||||||
|
{/* Minimize */}
|
||||||
|
<button
|
||||||
|
onClick={handleMinimize}
|
||||||
|
title="Minimize"
|
||||||
|
className="group flex h-full w-10 items-center justify-center text-gray-600 transition-colors duration-100 hover:bg-white/6 hover:text-gray-300"
|
||||||
|
>
|
||||||
|
<MinimizeIcon />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Maximize / Restore */}
|
||||||
|
<button
|
||||||
|
onClick={handleMaximize}
|
||||||
|
title={isMaximized ? "Restore" : "Maximize"}
|
||||||
|
className="group flex h-full w-10 items-center justify-center text-gray-600 transition-colors duration-100 hover:bg-white/6 hover:text-gray-300"
|
||||||
|
>
|
||||||
|
{isMaximized ? <RestoreIcon /> : <MaximizeIcon />}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Close */}
|
||||||
|
<button
|
||||||
|
onClick={handleClose}
|
||||||
|
title="Close"
|
||||||
|
className="group flex h-full w-10 items-center justify-center text-gray-600 transition-colors duration-100 hover:bg-red-500/80 hover:text-white"
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user