Add support for selecting a file

This commit is contained in:
Emi Simpson 2022-01-01 01:02:54 -05:00
parent febc0737bf
commit 5724b25a88
Signed by: Emi
GPG Key ID: A12F2C2FFDC3D847
2 changed files with 218 additions and 3 deletions

185
src/app/file_select.rs Normal file
View File

@ -0,0 +1,185 @@
use iced::canvas::event::Status;
use iced::Font;
use iced::canvas::Text;
use iced::canvas::Stroke;
use iced::Size;
use iced::Point;
use iced::canvas::Path;
use iced::canvas::Frame;
use iced::canvas::Geometry;
use iced::canvas::Cursor;
use iced::Element;
use crate::app::Message;
use iced::canvas::Program;
use iced::mouse;
use iced::Color;
use iced::Rectangle;
use iced::Length;
use iced::HorizontalAlignment;
use iced::VerticalAlignment;
use iced::widget::canvas::{self, Canvas};
/* RYGCBM
const COLORS: [Color; 6] = [
Color { r: 1., g: 0., b: 0., a: 1. },
Color { r: 1., g: 1., b: 0., a: 1. },
Color { r: 0., g: 1., b: 0., a: 1. },
Color { r: 0., g: 1., b: 1., a: 1. },
Color { r: 0., g: 0., b: 1., a: 1. },
Color { r: 1., g: 0., b: 1., a: 1. },
];*/
const COLORS: [Color; 6] = [
Color { r: 255./255., g: 129./255., b: 126./255., a: 1. },
Color { r: 239./255., g: 190./255., b: 125./255., a: 1. },
Color { r: 233./255., g: 236./255., b: 107./255., a: 1. },
Color { r: 119./255., g: 221./255., b: 119./255., a: 1. },
Color { r: 139./255., g: 211./255., b: 230./255., a: 1. },
Color { r: 177./255., g: 162./255., b: 202./255., a: 1. },
];
const MAX_TICKS: usize = 900;
const FONT_MR_PIXEL: Font = Font::External {
name: "Mister Pixel",
bytes: include_bytes!("../../fonts/mister-pixel/mister-pixel.otf"),
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FileSelector {
tick: usize,
}
impl FileSelector {
pub fn tick(&mut self) {
self.tick = (self.tick + 1) % MAX_TICKS;
}
pub fn view(&mut self) -> Element<Message> {
Canvas::new(self)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
impl Default for FileSelector {
fn default() -> Self {
FileSelector {
tick: 0,
}
}
}
impl Program<Message> for FileSelector {
fn draw(&self, bounds: Rectangle, _cursor: Cursor) -> Vec<Geometry> {
let offset_per_index = MAX_TICKS / &COLORS.len();
const TEXT_RECT_W: f32 = 350.;
const TEXT_RECT_H: f32 = 50.;
const RECT_RATIO: f32 = TEXT_RECT_W / TEXT_RECT_H;
let text_rect_scale = TEXT_RECT_W / bounds.width;
let text_rect: (Point, Size) = centered_rectangle(&bounds, RECT_RATIO, text_rect_scale);
let text_rect_far_point = Point {
x: text_rect.0.x + text_rect.1.width,
y: text_rect.0.y + text_rect.1.height,
};
let bound_far_point: Point = Point {
x: bounds.x + bounds.width,
y: bounds.y + bounds.height,
};
let screen_top_left = Point { x: bounds.x, y: bounds.y };
let mut frame = Frame::new(bounds.size());
frame.fill_rectangle(
Point::new(bounds.x, bounds.y),
Size::new(bounds.width, bounds.height),
Color::WHITE,
);
COLORS.iter()
.enumerate()
.map(|(index, color)| {
let size =
((self.tick + offset_per_index * index) % MAX_TICKS) as f32 /
MAX_TICKS as f32;
let top_left = interpolate(text_rect.0, screen_top_left, size);
let far_corner = interpolate(text_rect_far_point, bound_far_point, size);
let rect_size = Size::new(far_corner.x - top_left.x, far_corner.y - top_left.y);
(Path::rectangle(top_left, rect_size), color, size)
})
.for_each(|(rect, color, size)|
frame.stroke(
&rect,
Stroke::default()
.with_color(*color)
.with_width(5.)
)
);
frame.fill_text(
Text {
content: String::from("select a song to start"),
horizontal_alignment: HorizontalAlignment::Center,
vertical_alignment: VerticalAlignment::Center,
size: 32.,
color: Color::WHITE,
font: FONT_MR_PIXEL,
position: Point::new(
bounds.x + bounds.width * 0.5,
bounds.y + bounds.height * 0.5,
),
..Default::default()
}
);
frame.fill(
&Path::rectangle(text_rect.0, text_rect.1),
Color::BLACK,
);
frame.stroke(
&Path::rectangle(text_rect.0, text_rect.1),
Stroke::default()
.with_color(Color::BLACK)
.with_width(2.)
);
vec![frame.into_geometry()]
}
fn update(
&mut self,
event: canvas::Event,
_: Rectangle<f32>,
_: Cursor
) -> (Status, Option<Message>) {
match event {
canvas::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => {
(Status::Captured, Some(Message::PromptForFile))
},
_ => (Status::Ignored, None),
}
}
}
fn centered_rectangle(bounds: &Rectangle, ratio: f32, scale: f32) -> (Point, Size) {
let center_x = bounds.x + 0.5 * bounds.width;
let center_y = bounds.y + 0.5 * bounds.height;
let width = bounds.width * scale;
let height = width / ratio;
let top_x = center_x - width * 0.5;
let top_y = center_y - height * 0.5;
(
Point::new(top_x, top_y),
Size::new(width, height),
)
}
fn interpolate(from: Point, to: Point, amount: f32) -> Point {
Point {
x: from.x + amount * (to.x - from.x),
y: from.y + amount * (to.y - from.y),
}
}

View File

@ -1,3 +1,5 @@
use rfd::{AsyncFileDialog, FileHandle};
use std::path::PathBuf;
use core::time::Duration;
use iced::Subscription;
use iced::Clipboard;
@ -12,6 +14,7 @@ use iced::executor;
use iced_futures::time;
use iced_native::subscription;
use iced_native::keyboard;
use iced_native::window;
use iced_native::event::Event;
mod lyrics;
@ -43,6 +46,9 @@ pub enum Message {
LineAdvanced(usize),
PasteSent,
Tick,
PromptForFile,
FileOpened(PathBuf),
Null,
}
impl Application for DelyriumApp {
@ -67,6 +73,7 @@ impl Application for DelyriumApp {
}
fn update(&mut self, message: Message, clipboard: &mut Clipboard) -> Command<Message>{
let mut command = None;
match message {
Message::LyricChanged { line_no, new_value } => {
self.lyrics_component.update_line(new_value, line_no);
@ -90,9 +97,29 @@ impl Application for DelyriumApp {
},
_ => { },
}
}
},
Message::FileOpened(path) => {
println!("File opened! {}", path.display());
},
Message::PromptForFile => {
let task = async {
let handle = AsyncFileDialog::new()
.add_filter("Song Files", &["mp3", "flac", "ogg", "opus", "wav"])
.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::Null => { },
}
Command::none()
command.unwrap_or_else(Command::none)
}
fn view(&mut self) -> Element<Message> {
@ -126,7 +153,10 @@ impl Application for DelyriumApp {
}
_ => { None }
}
}
},
Event::Window(window::Event::FileDropped(path)) => {
Some(Message::FileOpened(path))
},
_ => { None }
}
});