goosemusic 1.2.0

A music player with YouTube search, local playback, and OS media controls
//! Messages produced by the UI and results produced by background work.

use iced::{Point, Rectangle};

use super::ViewData;
use crate::{
    app::{
        interaction::{self, ContextMenuFocus, DefaultCtxAction, TrackListKind, TrackPos},
        update::operation::CaptureBounds,
        CsvPreset, ImportCsvField, ImportMethod, ViewKind,
    },
    data::library,
    deps::DepKind,
    lyrics::Lyrics,
    providers::ProviderId,
    types::{QueueTab, Track},
};

#[derive(Debug, Clone)]
pub enum BackendResult {
    SearchResults(u64, Vec<Track>, crate::providers::SearchTab),
    SearchResultsAppend(u64, Vec<Track>),
    RadioResults(u64, String, Vec<Track>),
    BrowseResults(u64, Vec<Track>, Option<crate::providers::AlbumMeta>),
    DownloadComplete(Track, String),
    DownloadError(String),
    SearchError(String),
    ThumbnailDownloaded(String),
    LyricsFetched(Result<Lyrics, String>, String),
    NormalizationComputed(String, f32),
    CardPlaylistReady(usize, String, Vec<Track>),
    /// An artist id was resolved on `provider` (by name) for the page that
    /// issued request `rid`; cached into the page's known provider ids.
    ArtistIdResolved {
        rid: u64,
        provider: ProviderId,
        resolved_id: String,
    },
    /// One artist-page section (`kind`) finished fetching for `provider`;
    /// the payload is exactly that kind's data. The error fails just its
    /// section.
    ArtistSectionLoaded {
        rid: u64,
        provider: ProviderId,
        kind: crate::providers::ArtistDataKind,
        data: Box<Result<crate::providers::ArtistKindData, String>>,
    },
    LocalFilesPicked(Vec<std::path::PathBuf>),
    /// Playlist import: the user picked a source (single file for Native/CSV,
    /// a folder for File-list) and the dialog's current settings should be
    /// applied. `method` is captured so the result stays correct even if the
    /// dialog was closed before the picker thread replied.
    ImportPathsPicked {
        method: ImportMethod,
        paths: Vec<std::path::PathBuf>,
    },
    /// Background install of a dependency finished (success or error).
    DependencyInstalled(DepKind, Result<(), String>),
    DependencyProgress(DepKind, u64, u64),
    DependencyDeleted(DepKind, Result<(), String>),
    ProviderResolved {
        original: Track,
        provider: ProviderId,
        resolved: Option<Track>,
        /// Where the track was selected from, so the resolved provider id can
        /// be written back into the source list (search/playlist/queue). `None`
        /// when the resolve was triggered automatically (no source row).
        pos: Option<TrackPos>,
        /// Whether the resolved track should be played (true) or downloaded
        /// (false) once its id is known.
        play: bool,
    },
    ProviderResolveError {
        /// Track title
        title: String,
        provider: ProviderId,
        message: String,
    },
    EditTrackProviderResolved(ProviderId, Option<Track>),
    /// The Edit Track "Find" action failed to resolve `provider`.
    EditTrackProviderError(ProviderId, String),
    /// A background version-check completed.
    VersionChecked {
        current: String,
        latest: Option<String>,
        release_url: String,
        asset_url: Option<String>,
        sha256: Option<String>,
        package_managed: bool,
        error: Option<String>,
    },
    /// Download progress for an in-flight self-update.
    UpdateProgress(u64, u64),
    /// The update download/extract/staged-apply finished.
    UpdateComplete(Result<String, String>),
}

#[derive(Debug, Clone)]
pub enum Message {
    Tick,
    WindowClose,
    WindowResized(iced::Size),
    /// A window was opened; carries its id so the Windows HWND can be resolved.
    WindowOpened(iced::window::Id),
    /// The OS window handle (HWND on Windows) resolved for media controls.
    MediaHwnd(Option<u64>),
    CursorMoved(Point),
    LeftButtonReleased,
    ListBoundsCaptured(Box<CaptureBounds>),
    SearchHistoryBoundsCaptured(crate::app::update::operation::ListGeometry),
    ContextMenuBoundsCaptured {
        panel: Rectangle,
        row_offsets: Vec<f32>,
    },
    ListScrolled {
        list: TrackListKind,
        translation_y: f32,
    },
    KeyPressed {
        key: iced::keyboard::key::Physical,
        modifiers: iced::keyboard::Modifiers,
    },

    SearchInputChanged(String),
    SearchExecute,
    SearchScopeChanged(crate::providers::SearchScope),
    SearchProviderChanged(ProviderId),
    SearchLoadMore,
    SearchHistorySelected(usize),
    DeleteSearchHistory(usize),
    Browse(ViewKind, ProviderId),
    OpenArtist {
        id: String,
        name: String,
        source: ProviderId,
    },
    ArtistSectionProviderChanged(crate::providers::ArtistSectionKind, ProviderId),
    ArtistHeaderProviderChanged(ProviderId),
    DragPress(interaction::Pressed),
    HoverStart(interaction::HoverTarget),
    HoverEnd(interaction::HoverTarget),
    ToggleLibrarySave(library::LibraryItem),
    ToggleLibraryExpanded,

    TrackRightClicked(TrackPos),
    PlayTrackAt(TrackPos),
    TogglePlayPause,
    NextTrack,
    PreviousTrack,
    SetVolume(f32),
    Seek(f32),

    CreatePlaylist,
    NewPlaylistNameChanged(String),
    RenamePlaylist(String),
    AddLocalMusic,
    AddToPlaylist(usize),
    TogglePicker(Vec<usize>),
    ClosePicker,
    ShowDeleteConfirm(usize),
    ConfirmDeletePlaylist,
    HideDeleteConfirm,
    OpenAndPlayPlaylist(usize),

    OpenImportPlaylist,
    CloseImportPlaylist,
    ImportMethodChanged(ImportMethod),
    ImportCsvColChanged(ImportCsvField, String),
    ImportCsvPresetChanged(CsvPreset),
    ImportPlaylistNameChanged(String),
    ImportPatternChanged(usize, String),
    ImportAddPattern,
    ImportRemovePattern(usize),
    ImportSelectFiles,

    TrackListSearchInput(String),
    TrackListSearchNext,
    TrackListSearchPrev,
    TrackListSearchClose,

    ToggleQueue,
    SwitchQueueTab(QueueTab),
    RevealNowPlaying,
    ToggleRepeat,
    ShowLyrics,
    SetLyricsViewMode(crate::app::LyricsViewMode),
    LyricsLineClicked(f32),
    SelectLyricsProvider(crate::lyrics::LyricsProvider),
    LyricsEditorAction(iced::widget::text_editor::Action),
    CopyLyrics,

    NavigateTo(ViewData),
    SidebarSearch,
    NavigateBack,
    NavigateForward,

    SettingsDownloadDirChanged(String),
    SettingsMaxHistoryVisibleChanged(String),
    SettingsMaxHistoryStoredChanged(String),
    SettingsCacheMaxSizeChanged(String),
    SettingsMaxRecentlyPlayedChanged(String),
    SettingsVolumeNormalizationToggled(bool),
    SettingsDefaultProviderChanged(ProviderId),
    SettingsLanguageChanged(crate::i18n::Language),
    SettingsThemeChanged(crate::theme::ThemeKind),
    SettingsResetDefaults,

    ContextMenuPlayTrack(TrackPos),
    ContextMenuHover(Option<ContextMenuFocus>),
    ContextMenuGoToArtist,
    ContextMenuGoToArtistProvider(ProviderId),
    ContextMenuDefault(DefaultCtxAction),
    ContextMenuPlayViaProvider(ProviderId, TrackPos),
    ContextMenuDownloadViaProvider(ProviderId),
    ContextMenuSongRadioProvider(ProviderId),
    ContextMenuArtistRadioProvider(ProviderId),
    ContextMenuRemoveFromPlaylist(Vec<usize>),
    ContextMenuRemoveFromQueue(Vec<usize>),
    ContextMenuEditTrack,
    EditTrackField(EditTrackField, String),
    EditTrackSelectProvider(ProviderId),
    EditTrackFindProvider(ProviderId),
    SaveEditTrack,
    CloseEditTrack,
    CloseContextMenu,

    DepToggle(DepKind),
    DepInstall,
    DepDismiss,
    DepSettingsInstall(DepKind),
    DepSettingsDelete(DepKind),

    /// Trigger a background version check (GitHub releases API).
    CheckForUpdates,
    /// Download and stage the available update, then restart.
    UpdateApp,
}

/// Editable text fields of a [`Track`](crate::types::Track) in the track
/// editing popup. `source` is excluded: it is changed only via the provider
/// "select" buttons, never a text input.
#[derive(Debug, Clone, Copy)]
pub enum EditTrackField {
    Title,
    Artist,
}