// "Create playlist" modal — 1:1 with Tauri's PlaylistModal (create // mode): Name, optional multiline Description, a Folder dropdown // ("No folder" + the user's folders), a "Make playlist public" // checkbox, and Cancel / Create buttons. // // Opened from the sidebar "+" button (CreatePlaylistState.open). import { Theme } from "../foundation/semantic-colors.slint"; import { Typography } from "../foundation/typography.slint"; import { Radius } from "../foundation/radius.slint"; import { CreatePlaylistState, CreatePlaylistActions } from "../state.slint"; import { QbzIcon } from "QbzIcon.slint"; import { QbzSelect } from "QbzSelect.slint"; import { LineEdit, TextEdit } from "std-widgets.slint"; export component CreatePlaylistModal inherits Rectangle { visible: CreatePlaylistState.open; if CreatePlaylistState.open: Rectangle { background: #000000bf; TouchArea { mouse-cursor: default; clicked => { CreatePlaylistActions.close(); } } Rectangle { width: Math.min(root.width - 80px, 420px); height: panel.preferred-height; x: Math.round((parent.width - self.width) / 2 / 1px) * 1px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; border-radius: Radius.md; background: Theme.surface-card; border-width: 1px; border-color: Theme.border-subtle; drop-shadow-blur: 32px; drop-shadow-color: #00000080; TouchArea { } panel := VerticalLayout { padding: 20px; spacing: 16px; HorizontalLayout { Text { text: @tr("New playlist"); color: Theme.text-primary; font-size: Typography.heading; font-weight: Typography.semibold; horizontal-stretch: 1; vertical-alignment: center; } close-x := TouchArea { width: 28px; height: 28px; mouse-cursor: pointer; clicked => { CreatePlaylistActions.close(); } QbzIcon { source: @image-url("../assets/icons/x.svg"); width: 17px; height: 17px; x: Math.round((parent.width - self.width) / 2 / 1px) * 1px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; tint: close-x.has-hover ? Theme.text-primary : Theme.text-muted; } } } // Name. VerticalLayout { spacing: 8px; Text { text: @tr("Name"); color: Theme.text-secondary; font-size: Typography.legal; font-weight: Typography.medium; } name-input := LineEdit { placeholder-text: @tr("My Playlist"); text <=> CreatePlaylistState.name; accepted => { CreatePlaylistActions.submit(); } } } // Description (optional, multiline). VerticalLayout { spacing: 8px; Text { text: @tr("Description (optional)"); color: Theme.text-secondary; font-size: Typography.legal; font-weight: Typography.medium; } desc-input := TextEdit { height: 80px; wrap: word-wrap; text <=> CreatePlaylistState.description; } } // Folder dropdown — only shown when the user has folders // (mirrors Tauri, which hides the row when folders.length // === 0). if CreatePlaylistState.folder-options.length > 1: VerticalLayout { spacing: 8px; Text { text: @tr("Folder"); color: Theme.text-secondary; font-size: Typography.legal; font-weight: Typography.medium; } QbzSelect { options: CreatePlaylistState.folder-options; current-index: CreatePlaylistState.folder-index; menu-width: 380px; selected(i) => { CreatePlaylistState.folder-index = i; } } } // Public checkbox row — meaningless for a local playlist, // so it dims + locks while offline-only is ON. HorizontalLayout { spacing: 10px; opacity: CreatePlaylistState.offline-only ? 0.4 : 1.0; toggle := TouchArea { width: 20px; height: 20px; mouse-cursor: CreatePlaylistState.offline-only ? default : pointer; enabled: !CreatePlaylistState.offline-only; clicked => { CreatePlaylistState.is-public = !CreatePlaylistState.is-public; } Rectangle { width: 18px; height: 18px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; border-radius: 4px; background: CreatePlaylistState.is-public ? Theme.accent : transparent; border-width: CreatePlaylistState.is-public ? 0px : 1.5px; border-color: Theme.text-muted; QbzIcon { source: @image-url("../assets/icons/check.svg"); width: 12px; height: 12px; x: Math.round((parent.width - self.width) / 2 / 1px) * 1px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; tint: #ffffff; visible: CreatePlaylistState.is-public; } } } Text { text: @tr("Make playlist public"); color: Theme.text-secondary; font-size: Typography.body; vertical-alignment: center; horizontal-stretch: 1; } } // Offline-only checkbox row (D8) — creates a LOCAL playlist // (library.db) that never reaches Qobuz. While the app is // offline the toggle is forced ON and locked, with a hint. VerticalLayout { spacing: 6px; HorizontalLayout { spacing: 10px; opacity: CreatePlaylistState.offline-locked ? 0.7 : 1.0; offline-toggle := TouchArea { width: 20px; height: 20px; mouse-cursor: CreatePlaylistState.offline-locked ? default : pointer; enabled: !CreatePlaylistState.offline-locked; clicked => { CreatePlaylistState.offline-only = !CreatePlaylistState.offline-only; } Rectangle { width: 18px; height: 18px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; border-radius: 4px; background: CreatePlaylistState.offline-only ? Theme.accent : transparent; border-width: CreatePlaylistState.offline-only ? 0px : 1.5px; border-color: Theme.text-muted; QbzIcon { source: @image-url("../assets/icons/check.svg"); width: 12px; height: 12px; x: Math.round((parent.width - self.width) / 2 / 1px) * 1px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; tint: #ffffff; visible: CreatePlaylistState.offline-only; } } } Text { text: @tr("Offline-only playlist"); color: Theme.text-secondary; font-size: Typography.body; vertical-alignment: center; horizontal-stretch: 1; } } // Hint indented to the label's left edge (20px checkbox // + 10px spacing), so it reads as the row's description. if CreatePlaylistState.offline-locked: HorizontalLayout { padding-left: 30px; Text { text: @tr("Created locally — you're offline"); color: Theme.text-muted; font-size: Typography.legal; } } } // Cancel + Create. HorizontalLayout { alignment: end; spacing: 10px; // Cancel. Rectangle { width: cancel-label.preferred-width + 28px; height: 36px; border-radius: Radius.sm; background: cancel-ta.has-hover ? Theme.surface-hover : Theme.surface-elevated; cancel-label := Text { text: @tr("Cancel"); color: Theme.text-primary; font-size: Typography.body; x: Math.round((parent.width - self.width) / 2 / 1px) * 1px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; } cancel-ta := TouchArea { mouse-cursor: pointer; clicked => { CreatePlaylistActions.close(); } } } // Create. Rectangle { width: create-label.preferred-width + 36px; height: 36px; border-radius: Radius.sm; opacity: (CreatePlaylistState.name != "" && !CreatePlaylistState.creating) ? 1.0 : 0.5; background: create-ta.has-hover ? #ffffffd6 : Theme.accent; create-label := Text { text: CreatePlaylistState.creating ? @tr("Creating...") : @tr("Create"); color: #ffffff; font-size: Typography.body; font-weight: Typography.semibold; x: Math.round((parent.width - self.width) / 2 / 1px) * 1px; y: Math.round((parent.height - self.height) / 2 / 1px) * 1px; } create-ta := TouchArea { mouse-cursor: (CreatePlaylistState.name != "" && !CreatePlaylistState.creating) ? pointer : default; clicked => { if (CreatePlaylistState.name != "" && !CreatePlaylistState.creating) { CreatePlaylistActions.submit(); } } } } } } } } }