WIP SAO UI music player
This commit is contained in:
parent
87d70ee6d1
commit
1e43b0a2c4
|
@ -10,6 +10,7 @@ crate-type = ["cdylib"]
|
|||
[dependencies]
|
||||
glam = "^0.21"
|
||||
keyframe = "1"
|
||||
canary-music-player = { path = "../../apps/music-player" }
|
||||
canary-script = { path = "../../crates/script" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
|
|
@ -4,6 +4,7 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
|||
pub mod anim;
|
||||
pub mod draw;
|
||||
pub mod main_menu;
|
||||
pub mod music_player;
|
||||
pub mod panel;
|
||||
pub mod widgets;
|
||||
|
||||
|
@ -11,8 +12,9 @@ use canary_script::*;
|
|||
use api::*;
|
||||
use widgets::Widget;
|
||||
use main_menu::MainMenuPanel;
|
||||
use music_player::MusicPlayerPanel;
|
||||
|
||||
export_abi!(MainMenuPanel);
|
||||
export_abi!(MusicPlayerPanel);
|
||||
|
||||
pub const ICON_FONT: &str = "Iosevka Nerd Font";
|
||||
pub const DISPLAY_FONT: &str = "Homenaje";
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
use api::*;
|
||||
use canary_script::*;
|
||||
|
||||
use canary_music_player::{AlbumInfo, TrackInfo};
|
||||
|
||||
use crate::widgets::prelude::*;
|
||||
use dialog::{DialogBodyStyle, DialogFooterStyle};
|
||||
use shell::Offset;
|
||||
use text::{HorizontalAlignment, Label, LabelText};
|
||||
|
||||
pub struct MusicPlayerPanel {
|
||||
panel: Panel,
|
||||
widget: Option<MusicPlayerWidget>,
|
||||
disconnected: Offset<Label>,
|
||||
}
|
||||
|
||||
impl BindPanel for MusicPlayerPanel {
|
||||
fn bind(panel: Panel, _msg: Message) -> Box<dyn PanelImpl> {
|
||||
let dc_text = LabelText {
|
||||
font: Font::new(crate::DISPLAY_FONT),
|
||||
text: "Disconnected".to_string(),
|
||||
};
|
||||
|
||||
let disconnected = Label::new_centered(dc_text, 10.0, Color::WHITE);
|
||||
let disconnected = Offset::new(disconnected, Vec2::ZERO);
|
||||
|
||||
Box::new(Self {
|
||||
panel,
|
||||
widget: None,
|
||||
disconnected,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PanelImpl for MusicPlayerPanel {
|
||||
fn update(&mut self, dt: f32) {
|
||||
if let Some(widget) = self.widget.as_mut() {
|
||||
Widget::update(widget, dt);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self) {
|
||||
let ctx = DrawContext::new(self.panel);
|
||||
|
||||
if let Some(widget) = self.widget.as_mut() {
|
||||
Widget::draw(widget, &ctx);
|
||||
} else {
|
||||
self.disconnected.draw(&ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_resize(&mut self, new_size: Vec2) {
|
||||
self.disconnected.set_offset(new_size / 2.0);
|
||||
|
||||
if let Some(widget) = self.widget.as_mut() {
|
||||
widget.resize(new_size);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
if let Some(widget) = self.widget.as_mut() {
|
||||
Widget::on_cursor_event(widget, kind, at);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_message(&mut self, msg: Message) {
|
||||
use canary_music_player::{serde_json::from_slice, InMsg};
|
||||
let msg = msg.to_vec();
|
||||
let msg: InMsg = match from_slice(&msg) {
|
||||
Ok(msg) => msg,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
use InMsg::*;
|
||||
match (self.widget.as_mut(), msg) {
|
||||
(Some(_), Disconnected) => self.widget = None,
|
||||
(None, Connected) => self.widget = Some(MusicPlayerWidget::new()),
|
||||
(Some(widget), AlbumChanged(info)) => widget.update_album(info),
|
||||
(Some(widget), TrackChanged(info)) => widget.update_track(info),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MusicPlayerStyle {
|
||||
pub body: DialogBodyStyle,
|
||||
pub footer: DialogFooterStyle,
|
||||
pub rounding: f32,
|
||||
pub art_margin: f32,
|
||||
}
|
||||
|
||||
impl Default for MusicPlayerStyle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
body: Default::default(),
|
||||
footer: Default::default(),
|
||||
rounding: 5.0,
|
||||
art_margin: 5.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MusicPlayerWidget {
|
||||
artist: Offset<Label>,
|
||||
album: Offset<Label>,
|
||||
track: Offset<Label>,
|
||||
style: MusicPlayerStyle,
|
||||
art_rect: Rect,
|
||||
body_rect: Rect,
|
||||
footer_rect: Rect,
|
||||
}
|
||||
|
||||
impl Container for MusicPlayerWidget {
|
||||
fn with_children(&mut self, mut f: impl FnMut(&mut dyn Widget)) {
|
||||
f(&mut self.artist);
|
||||
f(&mut self.album);
|
||||
f(&mut self.track);
|
||||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
ctx.draw_partially_rounded_rect(
|
||||
CornerFlags::TOP,
|
||||
self.body_rect,
|
||||
self.style.rounding,
|
||||
self.style.body.color,
|
||||
);
|
||||
|
||||
ctx.draw_rounded_rect(self.art_rect, self.style.rounding, Color::MAGENTA);
|
||||
|
||||
ctx.draw_partially_rounded_rect(
|
||||
CornerFlags::BOTTOM,
|
||||
self.footer_rect,
|
||||
self.style.rounding,
|
||||
self.style.footer.color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl MusicPlayerWidget {
|
||||
pub fn new() -> Self {
|
||||
let font = Font::new(crate::DISPLAY_FONT);
|
||||
|
||||
let make_label = |content: &str| {
|
||||
let text = LabelText {
|
||||
font,
|
||||
text: content.to_string(),
|
||||
};
|
||||
|
||||
let label = Label::new_centered(text, 10.0, Color::BLACK);
|
||||
Offset::new(label, Vec2::ZERO)
|
||||
};
|
||||
|
||||
Self {
|
||||
artist: make_label("Artist"),
|
||||
album: make_label("Album"),
|
||||
track: make_label("Track"),
|
||||
style: Default::default(),
|
||||
art_rect: Rect::from_xy_size(Vec2::ZERO, Vec2::ZERO),
|
||||
body_rect: Rect::from_xy_size(Vec2::ZERO, Vec2::ZERO),
|
||||
footer_rect: Rect::from_xy_size(Vec2::ZERO, Vec2::ZERO),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: Vec2) {
|
||||
let style = &self.style;
|
||||
let width = new_size.x;
|
||||
let body_height = new_size.y - style.footer.height;
|
||||
let body_height = body_height.max(0.0);
|
||||
let body_size = Vec2::new(width, body_height);
|
||||
let footer_size = Vec2::new(width, style.footer.height);
|
||||
|
||||
let art_size = body_height - style.art_margin * 2.0;
|
||||
self.art_rect = Rect::from_xy_size(Vec2::splat(style.art_margin), Vec2::splat(art_size));
|
||||
|
||||
let label_x = (width + self.art_rect.br.x) / 2.0;
|
||||
let artist_baseline = body_height * 0.25;
|
||||
let album_baseline = body_height * 0.55;
|
||||
let track_baseline = body_height * 0.85;
|
||||
|
||||
self.artist.set_offset(Vec2::new(label_x, artist_baseline));
|
||||
self.album.set_offset(Vec2::new(label_x, album_baseline));
|
||||
self.track.set_offset(Vec2::new(label_x, track_baseline));
|
||||
|
||||
self.body_rect = Rect::from_xy_size(Vec2::ZERO, body_size);
|
||||
self.footer_rect = Rect::from_xy_size(self.body_rect.bl(), footer_size);
|
||||
}
|
||||
|
||||
pub fn update_album(&mut self, info: AlbumInfo) {
|
||||
self.album.set_text(
|
||||
info.title
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("<album here>"),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update_track(&mut self, info: TrackInfo) {
|
||||
self.artist.set_text(
|
||||
info.artists
|
||||
.first()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("<artist here>"),
|
||||
);
|
||||
|
||||
self.track.set_text(
|
||||
info.title
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("<album here>"),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -53,6 +53,26 @@ impl Label {
|
|||
offset: Vec2::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_centered(text: LabelText, scale: f32, color: Color) -> Self {
|
||||
Self::new(
|
||||
text,
|
||||
HorizontalAlignment::Center,
|
||||
scale,
|
||||
color,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_text(&mut self, text: &str) {
|
||||
if self.text.text != text {
|
||||
self.text.text = text.to_string();
|
||||
self.layout = None;
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Label {
|
||||
|
|
Loading…
Reference in New Issue