Implement Client-Side Decorations (CSD) with custom title bar
- Disable native window decorations in tauri.conf.json - Create TitleBar component with minimize/maximize/close controls - Add drag region for window movement - Integrate TitleBar into main layout and LoginView - Adjust content heights to account for 36px title bar - Prepare for future mini player implementation
This commit is contained in:
1 parent
57bfc0eb48
commit
96dc524c92
5 files changed
+155
-11
No files matched your search
@@ -17,7 +17,7 @@
|
||||
"height": 800,
|
||||
"minWidth": 800,
|
||||
"minHeight": 600,
|
||||
"decorations": true,
|
||||
"decorations": false,
|
||||
"transparent": false,
|
||||
"resizable": true
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
background-color: var(--bg-secondary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 104px);
|
||||
height: calc(100vh - 140px); /* 104px NowPlayingBar + 36px TitleBar */
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
<script lang="ts">
|
||||
import { Minus, Square, X, Copy } from 'lucide-svelte';
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||
|
||||
let isMaximized = $state(false);
|
||||
|
||||
const appWindow = getCurrentWindow();
|
||||
|
||||
// Check initial maximized state
|
||||
appWindow.isMaximized().then((maximized) => {
|
||||
isMaximized = maximized;
|
||||
});
|
||||
|
||||
// Listen for window state changes
|
||||
appWindow.onResized(async () => {
|
||||
isMaximized = await appWindow.isMaximized();
|
||||
});
|
||||
|
||||
function handleMinimize() {
|
||||
appWindow.minimize();
|
||||
}
|
||||
|
||||
function handleMaximize() {
|
||||
appWindow.toggleMaximize();
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
appWindow.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="titlebar" data-tauri-drag-region>
|
||||
<div class="titlebar-content" data-tauri-drag-region>
|
||||
<img src="/icons/AppIcons/android/48x48.png" alt="QBZ" class="titlebar-icon" />
|
||||
<span class="titlebar-title">QBZ</span>
|
||||
</div>
|
||||
|
||||
<div class="window-controls">
|
||||
<button class="control-btn" onclick={handleMinimize} title="Minimize">
|
||||
<Minus size={14} />
|
||||
</button>
|
||||
<button class="control-btn" onclick={handleMaximize} title={isMaximized ? "Restore" : "Maximize"}>
|
||||
{#if isMaximized}
|
||||
<Copy size={12} />
|
||||
{:else}
|
||||
<Square size={12} />
|
||||
{/if}
|
||||
</button>
|
||||
<button class="control-btn close-btn" onclick={handleClose} title="Close">
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.titlebar {
|
||||
height: 36px;
|
||||
min-height: 36px;
|
||||
background-color: var(--bg-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 8px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
border-bottom: 1px solid var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.titlebar-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.titlebar-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.titlebar-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.window-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
width: 36px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: all 100ms ease;
|
||||
}
|
||||
|
||||
.control-btn:hover {
|
||||
background-color: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.control-btn.close-btn:hover {
|
||||
background-color: #e81123;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.control-btn :global(svg) {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import TitleBar from '../TitleBar.svelte';
|
||||
|
||||
interface UserInfo {
|
||||
userName: string;
|
||||
@@ -129,8 +130,10 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="login-view">
|
||||
<div class="login-card">
|
||||
<div class="login-wrapper">
|
||||
<TitleBar />
|
||||
<div class="login-view">
|
||||
<div class="login-card">
|
||||
<!-- Logo -->
|
||||
<div class="logo">
|
||||
<img src="/logo.png" alt="QBZ Logo" class="logo-img" />
|
||||
@@ -201,16 +204,20 @@
|
||||
Your credentials are sent directly to Qobuz and stored securely in your system's keyring.
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.login-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: var(--bg-primary);
|
||||
}
|
||||
|
||||
.login-view {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
+16
-2
@@ -183,6 +183,7 @@
|
||||
} from '$lib/stores/lyricsStore';
|
||||
|
||||
// Components
|
||||
import TitleBar from '$lib/components/TitleBar.svelte';
|
||||
import Sidebar from '$lib/components/Sidebar.svelte';
|
||||
import AboutModal from '$lib/components/AboutModal.svelte';
|
||||
import NowPlayingBar from '$lib/components/NowPlayingBar.svelte';
|
||||
@@ -1025,6 +1026,10 @@
|
||||
<LoginView onLoginSuccess={handleLoginSuccess} />
|
||||
{:else}
|
||||
<div class="app">
|
||||
<!-- Custom Title Bar (CSD) -->
|
||||
<TitleBar />
|
||||
|
||||
<div class="app-body">
|
||||
<!-- Sidebar -->
|
||||
<Sidebar
|
||||
bind:this={sidebarRef}
|
||||
@@ -1229,6 +1234,7 @@
|
||||
onCast={openCastPicker}
|
||||
/>
|
||||
{/if}
|
||||
</div><!-- end app-body -->
|
||||
|
||||
<!-- Queue Panel -->
|
||||
<QueuePanel
|
||||
@@ -1350,23 +1356,31 @@
|
||||
<style>
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: var(--bg-primary);
|
||||
}
|
||||
|
||||
.app-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: calc(100vh - 104px);
|
||||
height: calc(100vh - 140px); /* 104px NowPlayingBar + 36px TitleBar */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: calc(100vh - 104px);
|
||||
height: calc(100vh - 140px); /* 104px NowPlayingBar + 36px TitleBar */
|
||||
overflow-y: auto;
|
||||
padding: 24px 32px;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user