deLyrium/src/app.rs

178 lines
4.2 KiB
Rust

use crate::controls::ControlsEvent;
use crate::load_song::load_song;
use crate::editor::Editor;
use crate::file_select::FileSelector;
use rfd::AsyncFileDialog;
use std::path::PathBuf;
use core::time::Duration;
use iced::Subscription;
use iced::Clipboard;
use iced::Command;
use iced::Application;
use iced::Element;
use iced::executor;
use iced_futures::time;
use iced_native::subscription;
use iced_native::keyboard;
use iced_native::window;
use iced_native::event::Event;
pub struct DelyriumApp {
lyrics_component: Option<Editor>,
file_selector: FileSelector,
size: (u32, u32),
}
#[derive(Clone, Debug)]
pub enum Message {
LyricChanged {
line_no: usize,
new_value: String,
},
LineAdvanced(usize),
PasteSent,
Tick,
PromptForFile,
FileOpened(PathBuf),
Resized(u32, u32),
ControlsEvent(ControlsEvent),
Null,
}
impl Application for DelyriumApp {
type Message = Message;
type Executor = executor::Default;
type Flags = ();
fn new(_: Self::Flags) -> (Self, Command<Message>) {
(
DelyriumApp {
lyrics_component: None,
file_selector: FileSelector::default(),
size: (0, 0),
},
Command::none(),
)
}
fn title(&self) -> String {
String::from("Delyrium")
}
fn update(&mut self, message: Message, clipboard: &mut Clipboard) -> Command<Message>{
let mut command = None;
match message {
Message::LyricChanged { line_no, new_value } => {
if let Some(lyrics) = self.lyrics_component.as_mut() {
lyrics.update_line(new_value, line_no);
}
},
Message::LineAdvanced(current_line) => {
if let Some(lyrics) = self.lyrics_component.as_mut() {
lyrics.advance_line(current_line);
}
},
Message::PasteSent => {
if let Some(lyrics) = self.lyrics_component.as_mut() {
let clip_text = clipboard.read().unwrap_or(String::new());
let clip_pasted_len = clip_text.chars()
.filter(|c| *c != '\r' && *c != '\n')
.count();
let line = lyrics.current_line_mut().1;
line.value.truncate(line.value.len() - clip_pasted_len);
lyrics.insert_text(clip_text);
}
},
Message::Tick => {
if self.lyrics_component.is_none() {
self.file_selector.tick();
}
},
Message::FileOpened(path) => {
println!("File opened! {}", path.display());
let song = load_song(&path).unwrap().format;
self.lyrics_component = Some(Editor::new(song, self.size));
},
Message::PromptForFile => {
let task = async {
let handle = AsyncFileDialog::new()
.add_filter("Song Files", &["mp3", "flac", "ogg", "opus", "wav", "mkv"])
.set_title("Select a song")
.pick_file()
.await;
if let Some(h) = handle {
Message::FileOpened(h.path().to_owned())
} else {
Message::Null
}
};
command = Some(task.into());
},
Message::Resized(w, h) => {
self.size = (w, h);
if let Some(lyrics) = self.lyrics_component.as_mut() {
lyrics.notify_resized(w, h);
}
},
Message::ControlsEvent(e) => {
if let Some(lyrics) = self.lyrics_component.as_mut() {
lyrics.handle_controls_event(e);
}
},
Message::Null => { },
}
command.unwrap_or_else(Command::none)
}
fn view(&mut self) -> Element<Message> {
if let Some(lyrics) = self.lyrics_component.as_mut() {
lyrics.view()
} else {
self.file_selector.view()
}
}
fn subscription(&self) -> Subscription<Message> {
let runtime_events = subscription::events_with(|event, _| {
match event {
Event::Keyboard(keyboard::Event::KeyPressed {key_code, modifiers}) => {
match (key_code, modifiers) {
(
keyboard::KeyCode::V,
keyboard::Modifiers { control, .. }
) if control == true => {
Some(Message::PasteSent)
}
_ => { None }
}
},
Event::Window(window::Event::FileDropped(path)) => {
Some(Message::FileOpened(path))
},
Event::Window(window::Event::Resized{width,height}) => {
Some(Message::Resized(width,height))
},
_ => { None }
}
});
let is_animating = if let Some(editor) = &self.lyrics_component {
editor.is_animating()
} else {
true
};
let fps30 = if is_animating {
time::every(Duration::from_millis(1000 / 30)).map(|_| Message::Tick)
} else {
Subscription::none()
};
Subscription::batch([
runtime_events,
fps30
])
}
}