use iced::{Point, Rectangle};
use super::{pane::PaneId, 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),
PlayerClientEvent(crate::providers::ClientEvent),
SearchError(u64, String),
ThumbnailDownloaded(ProviderId, String),
LyricsFetched(
Result<Lyrics, String>,
String,
crate::lyrics::LyricsProvider,
),
LyricsTranslationFetched(
Result<crate::lyrics::TranslatedLyrics, String>,
String,
crate::lyrics::LyricsProvider,
),
NormalizationComputed(String, f32),
CardPlaylistReady(usize, String, Vec<Track>),
ArtistIdResolved {
rid: u64,
provider: ProviderId,
resolved_id: String,
},
ArtistSectionLoaded {
rid: u64,
provider: ProviderId,
kind: crate::providers::ArtistDataKind,
data: Box<Result<crate::providers::ArtistKindData, String>>,
},
LocalFilesPicked(Vec<std::path::PathBuf>),
ImportPathsPicked {
method: ImportMethod,
paths: Vec<std::path::PathBuf>,
},
DependencyInstalled(DepKind, Result<(), String>),
DependencyProgress(DepKind, u64, u64),
DependencyDeleted(DepKind, Result<(), String>),
ProviderResolved {
original: Track,
provider: ProviderId,
resolved: Option<Track>,
rid: u64,
pos: TrackPos,
play: bool,
},
ProviderResolveError {
title: String,
provider: ProviderId,
message: String,
},
EditTrackProviderResolved(ProviderId, Option<Track>),
EditTrackProviderError(ProviderId, String),
VersionChecked {
current: String,
latest: Option<String>,
release_url: String,
asset_url: Option<String>,
sha256: Option<String>,
package_managed: bool,
error: Option<String>,
},
UpdateProgress(u64, u64),
UpdateComplete(Result<String, String>),
TranslationDone {
pane: PaneId,
track_id: String,
language: String,
text: String,
},
TranslationError {
pane: PaneId,
message: String,
},
TranslationModels(Vec<String>),
TranslationModelsError(String),
}
#[derive(Debug, Clone)]
pub enum Message {
Tick,
WindowClose,
WindowResized(iced::Size),
WindowOpened(iced::window::Id),
MediaHwnd(Option<u64>),
CursorMoved(Point),
LeftButtonReleased,
ListBoundsCaptured(Box<CaptureBounds>),
SearchHistoryBoundsCaptured(PaneId, crate::app::update::operation::ListGeometry),
ContextMenuBoundsCaptured {
panel: Rectangle,
row_offsets: Vec<f32>,
},
ListScrolled {
pane: PaneId,
list: TrackListKind,
translation_y: f32,
},
LyricsScrolled {
pane: PaneId,
translation_y: f32,
viewport_h: f32,
content_h: f32,
},
KeyPressed {
key: iced::keyboard::key::Physical,
modifiers: iced::keyboard::Modifiers,
},
ModifiersChanged(iced::keyboard::Modifiers),
SearchInputChanged(PaneId, String),
SearchExecute(PaneId),
SearchScopeChanged(PaneId, crate::providers::SearchScope),
SearchProviderChanged(PaneId, ProviderId),
SearchLoadMore(PaneId),
SearchHistorySelected(PaneId, usize),
DeleteSearchHistory(PaneId, usize),
Browse(PaneId, ViewKind, ProviderId),
OpenArtist {
pane: PaneId,
id: String,
name: String,
source: ProviderId,
},
ArtistSectionProviderChanged(PaneId, crate::providers::ArtistSectionKind, ProviderId),
ArtistHeaderProviderChanged(PaneId, 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,
OpenPlaylistJump,
PlaylistJumpInput(String),
PlaylistJumpConfirm,
PlaylistJumpOpen(usize),
PlaylistJumpOpenPlay(usize),
NewPlaylistNameChanged(String),
RenamePlaylist(String),
AddLocalMusic,
AddToPlaylist(usize),
TogglePicker(Vec<usize>),
ShowDeleteConfirm(usize),
ConfirmDeletePlaylist,
OpenAndPlayPlaylist(usize),
OpenImportPlaylist,
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(PaneId),
SetLyricsViewMode(PaneId, crate::app::LyricsViewMode),
LyricsLineClicked(f32),
SelectLyricsProvider(PaneId, crate::lyrics::LyricsProvider),
SelectLyricsTranslation(PaneId, Option<String>),
LyricsEditorAction(PaneId, iced::widget::text_editor::Action),
CopyLyrics(PaneId),
SelectLyricLine(PaneId, usize),
LyricNoteAction(PaneId, iced::widget::text_editor::Action),
StartCustomLyricsEdit(PaneId),
EditCustomLyrics(PaneId, String),
SelectCustomLyrics(PaneId, String),
CustomLyricsNameChanged(PaneId, String),
CustomLyricsEditorAction(PaneId, iced::widget::text_editor::Action),
SaveCustomLyrics(PaneId),
CancelCustomLyricsEdit(PaneId),
DeleteCustomLyrics(PaneId),
OpenTranslateDialog(PaneId),
TranslateLanguageChanged(String),
TranslateLanguageSubmit,
TranslatePromptAction(iced::widget::text_editor::Action),
TranslateResetPrompt,
TranslateSubmit,
RequestTranslationModels,
TranslationModelsFilterChanged(String),
NavigateTo(PaneId, ViewData),
SidebarSearch,
NavigateBack(PaneId),
NavigateForward(PaneId),
SplitHorizontal(PaneId),
SplitVertical(PaneId),
ClosePane(PaneId),
FocusPane(PaneId),
SettingsChanged(crate::app::update::SettingsChange),
SettingsResetDefaults,
ContextMenuPlayTrack(TrackPos),
ContextMenuHover(Option<ContextMenuFocus>),
ContextMenuGoToArtist,
ContextMenuGoToArtistProvider(ProviderId),
ContextMenuDefault(DefaultCtxAction),
ContextMenuPlayViaProvider(ProviderId, TrackPos),
ContextMenuDownloadViaProvider(ProviderId),
ContextMenuSongRadioProvider(ProviderId),
ContextMenuArtistRadioProvider(ProviderId),
ContextMenuClearCache,
ContextMenuClearCacheProvider(ProviderId),
ContextMenuAddToQueue(TrackListKind, Vec<usize>),
ContextMenuRemoveFromList(TrackListKind, Vec<usize>),
ContextMenuEditTrack,
EditTrackField(EditTrackField, String),
EditTrackSelectProvider(ProviderId),
EditTrackFindProvider(ProviderId),
SaveEditTrack,
CloseContextMenu,
CloseDialog,
DepToggle(DepKind),
DepInstall,
DepDismiss,
DepSettingsInstall(DepKind),
DepSettingsDelete(DepKind),
CheckForUpdates,
UpdateApp,
}
impl Message {
pub fn pane(&self) -> Option<PaneId> {
let active_pane = |pos: &TrackPos| pos.list.is_main().then_some(pos.pane);
match self {
Message::SearchInputChanged(pane, _)
| Message::SearchExecute(pane)
| Message::SearchScopeChanged(pane, _)
| Message::SearchProviderChanged(pane, _)
| Message::SearchLoadMore(pane)
| Message::SearchHistorySelected(pane, _)
| Message::DeleteSearchHistory(pane, _)
| Message::Browse(pane, _, _)
| Message::ArtistSectionProviderChanged(pane, _, _)
| Message::ArtistHeaderProviderChanged(pane, _)
| Message::ShowLyrics(pane)
| Message::SetLyricsViewMode(pane, _)
| Message::SelectLyricsProvider(pane, _)
| Message::SelectLyricsTranslation(pane, _)
| Message::LyricsEditorAction(pane, _)
| Message::CopyLyrics(pane)
| Message::SelectLyricLine(pane, _)
| Message::LyricNoteAction(pane, _)
| Message::StartCustomLyricsEdit(pane)
| Message::EditCustomLyrics(pane, _)
| Message::SelectCustomLyrics(pane, _)
| Message::CustomLyricsNameChanged(pane, _)
| Message::CustomLyricsEditorAction(pane, _)
| Message::SaveCustomLyrics(pane)
| Message::CancelCustomLyricsEdit(pane)
| Message::DeleteCustomLyrics(pane)
| Message::OpenTranslateDialog(pane)
| Message::NavigateTo(pane, _)
| Message::NavigateBack(pane)
| Message::NavigateForward(pane)
| Message::SplitHorizontal(pane)
| Message::SplitVertical(pane)
| Message::ClosePane(pane)
| Message::FocusPane(pane)
| Message::OpenArtist { pane, .. }
| Message::ListScrolled { pane, .. }
| Message::SearchHistoryBoundsCaptured(pane, _)
| Message::LyricsScrolled { pane, .. } => Some(*pane),
Message::DragPress(interaction::Pressed::Track(pos))
| Message::TrackRightClicked(pos)
| Message::PlayTrackAt(pos)
| Message::ContextMenuPlayTrack(pos)
| Message::HoverStart(interaction::HoverTarget::Track(pos))
| Message::HoverEnd(interaction::HoverTarget::Track(pos))
| Message::ContextMenuPlayViaProvider(_, pos) => active_pane(pos),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum EditTrackField {
Title,
Artist,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
app::interaction::{HoverTarget, Pressed},
providers::{ArtistSectionKind, SearchScope},
};
#[test]
fn pane_scoped_messages_report_their_pane() {
let pane = 7;
let pos = TrackPos::new(0, TrackListKind::Active, pane);
let editor = || iced::widget::text_editor::Action::SelectAll;
let messages = vec![
Message::SearchInputChanged(pane, String::new()),
Message::SearchExecute(pane),
Message::SearchScopeChanged(pane, SearchScope::Songs),
Message::SearchProviderChanged(pane, ProviderId::YouTube),
Message::SearchLoadMore(pane),
Message::SearchHistorySelected(pane, 0),
Message::DeleteSearchHistory(pane, 0),
Message::Browse(pane, ViewKind::Downloads, ProviderId::YouTube),
Message::ArtistSectionProviderChanged(
pane,
ArtistSectionKind::Popular,
ProviderId::YouTube,
),
Message::ArtistHeaderProviderChanged(pane, ProviderId::YouTube),
Message::ShowLyrics(pane),
Message::SetLyricsViewMode(pane, crate::app::LyricsViewMode::Synced),
Message::SelectLyricsProvider(pane, crate::lyrics::LyricsProvider::LrcLib),
Message::LyricsEditorAction(pane, editor()),
Message::CopyLyrics(pane),
Message::SelectLyricLine(pane, 0),
Message::LyricNoteAction(pane, editor()),
Message::StartCustomLyricsEdit(pane),
Message::EditCustomLyrics(pane, String::new()),
Message::SelectCustomLyrics(pane, String::new()),
Message::CustomLyricsNameChanged(pane, String::new()),
Message::CustomLyricsEditorAction(pane, editor()),
Message::SaveCustomLyrics(pane),
Message::CancelCustomLyricsEdit(pane),
Message::DeleteCustomLyrics(pane),
Message::OpenTranslateDialog(pane),
Message::NavigateTo(pane, ViewData::default()),
Message::NavigateBack(pane),
Message::NavigateForward(pane),
Message::SplitHorizontal(pane),
Message::SplitVertical(pane),
Message::ClosePane(pane),
Message::FocusPane(pane),
Message::OpenArtist {
pane,
id: String::new(),
name: String::new(),
source: ProviderId::YouTube,
},
Message::ListScrolled {
pane,
list: TrackListKind::Active,
translation_y: 0.0,
},
Message::LyricsScrolled {
pane,
translation_y: 0.0,
viewport_h: 0.0,
content_h: 0.0,
},
Message::SearchHistoryBoundsCaptured(
pane,
crate::app::update::operation::ListGeometry::default(),
),
Message::DragPress(Pressed::Track(pos)),
Message::TrackRightClicked(pos),
Message::PlayTrackAt(pos),
Message::ContextMenuPlayTrack(pos),
Message::HoverStart(HoverTarget::Track(pos)),
Message::HoverEnd(HoverTarget::Track(pos)),
Message::ContextMenuPlayViaProvider(ProviderId::YouTube, pos),
];
assert!(!messages.is_empty());
for msg in messages {
assert_eq!(msg.pane(), Some(pane), "{msg:?}");
}
assert_eq!(
Message::PlayTrackAt(TrackPos::new(0, TrackListKind::Queue, pane)).pane(),
None
);
assert_eq!(
Message::PlayTrackAt(TrackPos::new(0, TrackListKind::Recent, pane)).pane(),
None
);
assert_eq!(Message::Tick.pane(), None);
}
}