Shell & navigation: - Three-state sidebar (open/mini/closed); collapse cycles via header button. - Section nav as right-flyouts (sidebar) + compact header nav (closed) + full header tabs (nav-in-sidebar off), via a shared HeaderMenuOverlay. - Collapsed-folder right-flyout popup (max 4 + scrollbar), Rust-populated. - Thin auto-hiding sidebar scrollbar pinned to the edge (Flickable, drag-safe). - Slack-style bezel: rounded inset content panel over surface-card chrome. - Header restructure: nav buttons, window-centered search, app menu. Themes & components: - Live theme switching: Dark / OLED Black / Tokyo Night / System (std-widgets). - SegmentedTabBar (Discovery-style) with count badges for Library + Local Library. - MP3 quality-badge tier (format-first classification) across badges. - QbzSelect (Group/Sort) trimmed 20px. Behavior fixes: - Artist + Local Library Folder cover art now reach the rendered (visible/ grouped) models, not just the source set. - Favorites Playlists/Labels random buttons. - Playlist track click plays from the clicked track in visible order. - Dropdown idle-close pauses while the cursor rests on the menu. cc
147 lines
5.3 KiB
Plaintext
147 lines
5.3 KiB
Plaintext
// Collapsed-sidebar folder flyout. When the sidebar is in the Colapsado
|
|
// (mini) state, clicking a folder opens this popup to its right, listing the
|
|
// folder's playlists (max 4 rows visible; a scrollbar appears beyond four).
|
|
// Mirrors Tauri's collapsed folder popover. Rendered at the AppShell level
|
|
// because the sidebar clips its own overflow.
|
|
|
|
import { Theme } from "../foundation/semantic-colors.slint";
|
|
import { Radius } from "../foundation/radius.slint";
|
|
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
|
import { ListScrollbar } from "../primitives/ListScrollbar.slint";
|
|
import { SidebarFolderPopupState, SidebarActions, SidebarEntry } from "../state.slint";
|
|
|
|
// One playlist row inside the popup.
|
|
component FpRow inherits Rectangle {
|
|
in property <SidebarEntry> entry;
|
|
callback clicked();
|
|
height: 32px;
|
|
border-radius: 5px;
|
|
background: ta.has-hover ? Theme.surface-hover : transparent;
|
|
HorizontalLayout {
|
|
padding-left: 12px;
|
|
padding-right: 12px;
|
|
spacing: 8px;
|
|
VerticalLayout {
|
|
alignment: center;
|
|
QbzIcon {
|
|
source: @image-url("../assets/icons/list-music.svg");
|
|
width: 14px;
|
|
height: 14px;
|
|
tint: Theme.text-muted;
|
|
}
|
|
}
|
|
Text {
|
|
text: root.entry.name;
|
|
color: Theme.text-secondary;
|
|
font-size: 13px;
|
|
vertical-alignment: center;
|
|
horizontal-stretch: 1;
|
|
overflow: elide;
|
|
}
|
|
}
|
|
ta := TouchArea {
|
|
mouse-cursor: pointer;
|
|
clicked => { root.clicked(); }
|
|
}
|
|
}
|
|
|
|
export component SidebarFolderPopup inherits Rectangle {
|
|
visible: SidebarFolderPopupState.open;
|
|
|
|
property <length> row-h: 32px;
|
|
property <int> visible-rows: Math.min(SidebarFolderPopupState.count, 4);
|
|
property <length> list-h: root.visible-rows * root.row-h;
|
|
|
|
// Scrim — a click anywhere outside the panel closes the popup.
|
|
if SidebarFolderPopupState.open: TouchArea {
|
|
clicked => {
|
|
SidebarFolderPopupState.open = false;
|
|
}
|
|
}
|
|
|
|
if SidebarFolderPopupState.open: Rectangle {
|
|
x: SidebarFolderPopupState.anchor-x;
|
|
y: SidebarFolderPopupState.anchor-y;
|
|
width: 230px;
|
|
height: 34px + 1px + root.list-h + 8px;
|
|
|
|
Rectangle {
|
|
background: Theme.surface-main;
|
|
border-radius: Radius.sm;
|
|
border-width: 1px;
|
|
border-color: Theme.border-muted;
|
|
drop-shadow-blur: 24px;
|
|
drop-shadow-color: Theme.card-shadow;
|
|
drop-shadow-offset-y: 6px;
|
|
|
|
// Swallow clicks so the scrim doesn't close while interacting.
|
|
TouchArea { }
|
|
|
|
VerticalLayout {
|
|
padding-top: 4px;
|
|
padding-bottom: 4px;
|
|
|
|
// Header — folder name.
|
|
HorizontalLayout {
|
|
height: 30px;
|
|
padding-left: 12px;
|
|
padding-right: 12px;
|
|
spacing: 8px;
|
|
VerticalLayout {
|
|
alignment: center;
|
|
QbzIcon {
|
|
source: @image-url("../assets/icons/folder-open.svg");
|
|
width: 14px;
|
|
height: 14px;
|
|
tint: Theme.accent;
|
|
}
|
|
}
|
|
Text {
|
|
text: SidebarFolderPopupState.folder-name;
|
|
color: Theme.text-primary;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
vertical-alignment: center;
|
|
horizontal-stretch: 1;
|
|
overflow: elide;
|
|
}
|
|
}
|
|
Rectangle {
|
|
height: 1px;
|
|
background: Theme.border-subtle;
|
|
}
|
|
|
|
// Playlist list — capped at four rows; scrolls beyond that.
|
|
Rectangle {
|
|
height: root.list-h;
|
|
fp-flick := Flickable {
|
|
width: 100%;
|
|
height: 100%;
|
|
viewport-width: self.width;
|
|
viewport-height: SidebarFolderPopupState.count * root.row-h;
|
|
VerticalLayout {
|
|
width: parent.width;
|
|
alignment: start;
|
|
for entry in SidebarFolderPopupState.playlists: FpRow {
|
|
entry: entry;
|
|
clicked => {
|
|
SidebarActions.open-playlist(entry.id);
|
|
SidebarFolderPopupState.open = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if SidebarFolderPopupState.count > 4: ListScrollbar {
|
|
x: parent.width - self.width;
|
|
width: 10px;
|
|
height: 100%;
|
|
viewport-height: fp-flick.viewport-height;
|
|
visible-height: fp-flick.height;
|
|
viewport-y <=> fp-flick.viewport-y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|