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
+153
-9
No files matched your search
@@ -17,7 +17,7 @@
|
|||||||
"height": 800,
|
"height": 800,
|
||||||
"minWidth": 800,
|
"minWidth": 800,
|
||||||
"minHeight": 600,
|
"minHeight": 600,
|
||||||
"decorations": true,
|
"decorations": false,
|
||||||
"transparent": false,
|
"transparent": false,
|
||||||
"resizable": true
|
"resizable": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,7 @@
|
|||||||
background-color: var(--bg-secondary);
|
background-color: var(--bg-secondary);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: calc(100vh - 104px);
|
height: calc(100vh - 140px); /* 104px NowPlayingBar + 36px TitleBar */
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.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">
|
<script lang="ts">
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import TitleBar from '../TitleBar.svelte';
|
||||||
|
|
||||||
interface UserInfo {
|
interface UserInfo {
|
||||||
userName: string;
|
userName: string;
|
||||||
@@ -129,6 +130,8 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="login-wrapper">
|
||||||
|
<TitleBar />
|
||||||
<div class="login-view">
|
<div class="login-view">
|
||||||
<div class="login-card">
|
<div class="login-card">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
@@ -203,14 +206,18 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.login-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.login-view {
|
.login-view {
|
||||||
position: fixed;
|
flex: 1;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
+16
-2
@@ -183,6 +183,7 @@
|
|||||||
} from '$lib/stores/lyricsStore';
|
} from '$lib/stores/lyricsStore';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
import TitleBar from '$lib/components/TitleBar.svelte';
|
||||||
import Sidebar from '$lib/components/Sidebar.svelte';
|
import Sidebar from '$lib/components/Sidebar.svelte';
|
||||||
import AboutModal from '$lib/components/AboutModal.svelte';
|
import AboutModal from '$lib/components/AboutModal.svelte';
|
||||||
import NowPlayingBar from '$lib/components/NowPlayingBar.svelte';
|
import NowPlayingBar from '$lib/components/NowPlayingBar.svelte';
|
||||||
@@ -1025,6 +1026,10 @@
|
|||||||
<LoginView onLoginSuccess={handleLoginSuccess} />
|
<LoginView onLoginSuccess={handleLoginSuccess} />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="app">
|
<div class="app">
|
||||||
|
<!-- Custom Title Bar (CSD) -->
|
||||||
|
<TitleBar />
|
||||||
|
|
||||||
|
<div class="app-body">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
<Sidebar
|
<Sidebar
|
||||||
bind:this={sidebarRef}
|
bind:this={sidebarRef}
|
||||||
@@ -1229,6 +1234,7 @@
|
|||||||
onCast={openCastPicker}
|
onCast={openCastPicker}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div><!-- end app-body -->
|
||||||
|
|
||||||
<!-- Queue Panel -->
|
<!-- Queue Panel -->
|
||||||
<QueuePanel
|
<QueuePanel
|
||||||
@@ -1350,23 +1356,31 @@
|
|||||||
<style>
|
<style>
|
||||||
.app {
|
.app {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: var(--bg-primary);
|
background-color: var(--bg-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-body {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.content-area {
|
.content-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: calc(100vh - 104px);
|
height: calc(100vh - 140px); /* 104px NowPlayingBar + 36px TitleBar */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: calc(100vh - 104px);
|
height: calc(100vh - 140px); /* 104px NowPlayingBar + 36px TitleBar */
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 24px 32px;
|
padding: 24px 32px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user