186 lines
4.6 KiB
Rust
186 lines
4.6 KiB
Rust
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),
|
|
}
|
|
}
|