Add support for selecting a file
This commit is contained in:
parent
febc0737bf
commit
5724b25a88
|
@ -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),
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue