feat(slint): hover tooltip on collapsed-sidebar folder/playlist rows
In the mini (icon-only) sidebar the rows show no name. Add a hover bubble to the right of each folder/top-level-playlist row surfacing the name (folders also append their playlist count), mirroring the native title Tauri puts on its collapsed buttons. Rendered at the AppShell level (SidebarTooltip + SidebarTooltipState) because the sidebar clips its overflow; anchored with the same Flickable absolute-position correction as the folder flyout. Closes on click so it does not linger behind the flyout. cc
This commit is contained in:
4 files changed
+94
-1
No files matched your search
@@ -12,6 +12,7 @@ import { Sidebar } from "Sidebar.slint";
|
||||
import { HeaderBar } from "HeaderBar.slint";
|
||||
import { HeaderMenuOverlay } from "HeaderMenuOverlay.slint";
|
||||
import { SidebarFolderPopup } from "SidebarFolderPopup.slint";
|
||||
import { SidebarTooltip } from "SidebarTooltip.slint";
|
||||
import { PlaylistPickerModal } from "../primitives/PlaylistPickerModal.slint";
|
||||
import { CreatePlaylistModal } from "../primitives/CreatePlaylistModal.slint";
|
||||
import { EditPlaylistModal } from "../primitives/EditPlaylistModal.slint";
|
||||
@@ -299,6 +300,9 @@ export component AppShell inherits Rectangle {
|
||||
// Collapsed-sidebar folder flyout (mini state). Drawn above the content.
|
||||
SidebarFolderPopup { }
|
||||
|
||||
// Collapsed-sidebar hover tooltip (mini state) — name + folder count.
|
||||
SidebarTooltip { }
|
||||
|
||||
// Global "Add to playlist" picker overlay, opened from any track
|
||||
// context menu.
|
||||
PlaylistPickerModal { }
|
||||
|
||||
@@ -15,7 +15,7 @@ import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||
import { SidebarPlaylistCollage } from "../primitives/SidebarPlaylistCollage.slint";
|
||||
import { ContextMenu } from "../primitives/ContextMenu.slint";
|
||||
import { ContextMenuItem } from "../primitives/ContextMenuItem.slint";
|
||||
import { ShellState, SidebarState, SidebarActions, SidebarEntry, DragState, HeaderMenuState, NavMenuEntry, SidebarFolderPopupState } from "../state.slint";
|
||||
import { ShellState, SidebarState, SidebarActions, SidebarEntry, DragState, HeaderMenuState, NavMenuEntry, SidebarFolderPopupState, SidebarTooltipState } from "../state.slint";
|
||||
import { ScrollView, LineEdit } from "std-widgets.slint";
|
||||
|
||||
// A single context-menu item row.
|
||||
@@ -163,6 +163,29 @@ component SidebarRow inherits Rectangle {
|
||||
changed drag-py => { root.recompute-drop(); }
|
||||
changed drag-on => { root.recompute-drop(); }
|
||||
|
||||
// Collapsed (mini) hover tooltip: the row shows only an icon, so reveal the
|
||||
// name (folders also show their playlist count) in a bubble to the right.
|
||||
// Same Flickable absolute-position correction as the folder flyout anchor.
|
||||
function show-tooltip() {
|
||||
if (!ShellState.sidebar-mini) {
|
||||
return;
|
||||
}
|
||||
SidebarTooltipState.id = root.entry.id;
|
||||
SidebarTooltipState.text = root.is-folder
|
||||
? root.entry.name + " (" + root.entry.count + ")"
|
||||
: root.entry.name;
|
||||
SidebarTooltipState.anchor-x = root.absolute-position.x - root.x + root.width + 6px;
|
||||
SidebarTooltipState.anchor-y = root.absolute-position.y - root.y;
|
||||
SidebarTooltipState.open = true;
|
||||
}
|
||||
// Close only if this row still owns the tooltip (race-safe when the pointer
|
||||
// slides directly from one row to the next).
|
||||
function hide-tooltip() {
|
||||
if (SidebarTooltipState.id == root.entry.id) {
|
||||
SidebarTooltipState.open = false;
|
||||
}
|
||||
}
|
||||
|
||||
// True when this playlist row should show its micro-collage instead
|
||||
// of the generic glyph (opt-out + at least one cover available).
|
||||
property <bool> use-collage: !root.is-folder
|
||||
@@ -276,8 +299,16 @@ component SidebarRow inherits Rectangle {
|
||||
mouse-cursor: pointer;
|
||||
changed has-hover => {
|
||||
root.hover-changed(self.has-hover);
|
||||
if (self.has-hover) {
|
||||
root.show-tooltip();
|
||||
} else {
|
||||
root.hide-tooltip();
|
||||
}
|
||||
}
|
||||
clicked => {
|
||||
// The flyout / navigation takes over from here — drop the tooltip
|
||||
// so it doesn't linger behind the opening flyout.
|
||||
SidebarTooltipState.open = false;
|
||||
if (root.is-folder) {
|
||||
// Collapsed (mini) sidebar: a folder opens a flyout to the
|
||||
// right instead of expanding downward (Tauri parity).
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Collapsed-sidebar hover tooltip. When the sidebar is in the mini (icon-only)
|
||||
// state, hovering a folder or top-level playlist row surfaces its name in a
|
||||
// small bubble to the right (folders append their playlist count). Mirrors the
|
||||
// native `title` Tauri puts on the collapsed buttons. Rendered at the AppShell
|
||||
// level because the sidebar clips its own overflow. Visual only (no TouchArea),
|
||||
// so it never blocks the pointer.
|
||||
|
||||
import { Theme } from "../foundation/semantic-colors.slint";
|
||||
import { Radius } from "../foundation/radius.slint";
|
||||
import { SidebarTooltipState, ShellState } from "../state.slint";
|
||||
|
||||
export component SidebarTooltip inherits Rectangle {
|
||||
// Auto-hides if the sidebar leaves the mini state while a tooltip lingers.
|
||||
visible: SidebarTooltipState.open && ShellState.sidebar-mini;
|
||||
|
||||
if SidebarTooltipState.open && ShellState.sidebar-mini: bubble := Rectangle {
|
||||
x: SidebarTooltipState.anchor-x;
|
||||
// Center the bubble against the 32px row.
|
||||
y: SidebarTooltipState.anchor-y + (32px - self.height) / 2;
|
||||
width: row.preferred-width;
|
||||
height: row.preferred-height;
|
||||
background: Theme.surface-elevated;
|
||||
border-radius: Radius.sm;
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-muted;
|
||||
drop-shadow-blur: 16px;
|
||||
drop-shadow-color: Theme.card-shadow;
|
||||
drop-shadow-offset-y: 4px;
|
||||
|
||||
row := HorizontalLayout {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
Text {
|
||||
text: SidebarTooltipState.text;
|
||||
color: Theme.text-primary;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -818,6 +818,20 @@ export global SidebarFolderPopupState {
|
||||
in property <[SidebarEntry]> playlists: [];
|
||||
}
|
||||
|
||||
// Collapsed (mini) sidebar hover tooltip — the row shows only an icon, so a
|
||||
// hover bubble surfaces the name (folders also show their playlist count).
|
||||
// Mirrors Tauri's native `title` on the collapsed folder/playlist buttons.
|
||||
// Rendered at the AppShell level because the sidebar clips its overflow; the
|
||||
// anchor is the row's true window position (the collapsed rows live inside a
|
||||
// Flickable, so SidebarRow corrects the absolute-position double-count).
|
||||
export global SidebarTooltipState {
|
||||
in-out property <bool> open: false;
|
||||
in-out property <string> id; // owning row id (race-safe leave)
|
||||
in-out property <string> text; // "Folder (N)" or "Playlist name"
|
||||
in-out property <length> anchor-x; // window x (sidebar right edge)
|
||||
in-out property <length> anchor-y; // window y (row top)
|
||||
}
|
||||
|
||||
// The sidebar's playlists + folders. Populated by crate::sidebar on
|
||||
// shell entry and after create/delete/move.
|
||||
export global SidebarState {
|
||||
|
||||
Reference in new issue
Block a user