diff --git a/neothesia/src/scene/playing_scene/top_bar/mod.rs b/neothesia/src/scene/playing_scene/top_bar/mod.rs index c96260f..03ced86 100644 --- a/neothesia/src/scene/playing_scene/top_bar/mod.rs +++ b/neothesia/src/scene/playing_scene/top_bar/mod.rs @@ -1,15 +1,13 @@ use std::time::{Duration, Instant}; -use neothesia_core::render::QuadInstance; use ui::{LooperMsg, ProgressBarMsg}; -use wgpu_jumpstart::Color; use crate::{context::Context, NeothesiaEvent}; use super::{ animation::{Animated, Easing}, rewind_controller::RewindController, - PlayingScene, LAYER_FG, + PlayingScene, }; mod renderer; @@ -19,7 +17,7 @@ mod widget; use renderer::NuonRenderer; pub struct TopBar { - animation: Animated, + topbar_expand_animation: Animated, is_expanded: bool, settings_animation: Animated, @@ -34,7 +32,7 @@ pub struct TopBar { impl TopBar { pub fn new() -> Self { Self { - animation: Animated::new(false) + topbar_expand_animation: Animated::new(false) .duration(1000.) .easing(Easing::EaseOutExpo) .delay(30.0), @@ -138,19 +136,23 @@ impl TopBar { } #[profiling::function] - fn update_nuon(scene: &mut PlayingScene, ctx: &mut Context, _delta: Duration, y: f32) { + fn update_nuon(scene: &mut PlayingScene, ctx: &mut Context, _delta: Duration) { let globals = nuon::GlobalStore::with(|store| { store.insert(&scene.player); }); let mut root = ui::top_bar(ui::UiData { - y, + window_size: ctx.window_state.logical_size, is_settings_open: scene.top_bar.settings_active, is_looper_on: scene.top_bar.is_looper_active(), speed: ctx.config.speed_multiplier(), player: &scene.player, loop_start: scene.top_bar.loop_start_timestamp(), loop_end: scene.top_bar.loop_end_timestamp(), + + frame_timestamp: ctx.frame_timestamp, + topbar_expand_animation: &scene.top_bar.topbar_expand_animation, + settings_animation: &scene.top_bar.settings_animation, }) .into(); @@ -213,64 +215,13 @@ impl TopBar { top_bar.is_expanded |= top_bar.settings_active; top_bar.is_expanded |= scene.nuon_event_queue.is_mouse_grabbed(); - let now = ctx.frame_timestamp; - - top_bar.animation.transition(top_bar.is_expanded, now); + top_bar + .topbar_expand_animation + .transition(top_bar.is_expanded, ctx.frame_timestamp); top_bar .settings_animation - .transition(top_bar.settings_active, now); - - let y = top_bar.animation.animate_bool(-h + 5.0, 0.0, now); - - update_settings_card(scene, ctx, y); - Self::update_nuon(scene, ctx, delta, y); - } -} - -fn update_settings_card(scene: &mut PlayingScene, ctx: &mut Context, y: f32) { - let PlayingScene { - top_bar, - quad_pipeline, - .. - } = scene; - - let h = 75.0; - let w = ctx.window_state.logical_size.width; - let now = ctx.frame_timestamp; - - if top_bar.settings_animation.in_progress(now) || top_bar.settings_animation.value { - let card_w = 300.0; - let card_x = top_bar.settings_animation.animate_bool(card_w, 0.0, now); - - let bar_bg: Color = Color::from_rgba8(37, 35, 42, 1.0); - - let x = card_x + w - card_w; - let y = y + h + 1.0; - - let w = card_w; - let h = 100.0; - - quad_pipeline.push( - LAYER_FG, - QuadInstance { - position: [x, y], - size: [w, h], - color: bar_bg.into_linear_rgba(), - border_radius: [10.0, 0.0, 10.0, 0.0], - }, - ); - - fn cone_icon() -> &'static str { - "\u{F2D2}" - } - - let size = 50.0; - let half_size = size / 2.0; - ctx.text_renderer - .queue_icon(x + w / 2.0 - half_size, y + 10.0, size, cone_icon()); + .transition(top_bar.settings_active, ctx.frame_timestamp); - let buffer = ctx.text_renderer.gen_buffer_bold(25.0, "WIP"); - ctx.text_renderer - .queue_buffer_centered(x, y + size + 15.0, w, 25.0, buffer); + Self::update_nuon(scene, ctx, delta); } } diff --git a/neothesia/src/scene/playing_scene/top_bar/ui.rs b/neothesia/src/scene/playing_scene/top_bar/ui.rs index 815f25f..e7b421c 100644 --- a/neothesia/src/scene/playing_scene/top_bar/ui.rs +++ b/neothesia/src/scene/playing_scene/top_bar/ui.rs @@ -1,9 +1,11 @@ -use std::time::Duration; +use std::time::{Duration, Instant}; +use lilt::Animated; use nuon::{ - button::Button, column::Column, container::Container, row::Row, stack::Stack, + button::Button, canvas::Canvas, column::Column, container::Container, row::Row, stack::Stack, trilayout::TriLayout, Color, Element, }; +use winit::dpi::LogicalSize; use crate::scene::playing_scene::midi_player::MidiPlayer; @@ -35,6 +37,10 @@ pub enum Msg { Looper(LooperMsg), } +fn cone_icon() -> &'static str { + "\u{F2D2}" +} + fn gear_icon() -> &'static str { "\u{F3E5}" } @@ -65,8 +71,13 @@ pub struct UiData<'a> { pub loop_start: Duration, pub loop_end: Duration, pub speed: f32, - pub y: f32, + pub player: &'a MidiPlayer, + pub window_size: LogicalSize, + + pub frame_timestamp: Instant, + pub topbar_expand_animation: &'a Animated, + pub settings_animation: &'a Animated, } fn header(data: &UiData) -> impl Into> { @@ -145,8 +156,40 @@ pub fn top_bar(data: UiData) -> impl Into> { let body = Column::new().push(header).push(timeline); - Container::new() - .y(data.y) - .background(Color::new_u8(37, 35, 42, 1.0)) - .child(body) + let y = data + .topbar_expand_animation + .animate_bool(-75.0 + 5.0, 0.0, data.frame_timestamp); + + Stack::new() + .push( + Container::new() + .y(y) + .background(Color::new_u8(37, 35, 42, 1.0)) + .child(body), + ) + .push({ + let card_w = 300.0; + let card_x = data + .settings_animation + .animate_bool(card_w, 0.0, data.frame_timestamp); + let card_x = card_x + data.window_size.width - card_w; + + Container::new() + .y(y + 30.0 + 45.0) + .x(card_x) + .height(100.0) + .width(card_w) + .background(Color::new_u8(37, 35, 42, 1.0)) + .border_radius([10.0, 0.0, 10.0, 0.0]) + .child(Canvas::new(|renderer, layout| { + let x = layout.x; + let y = layout.y; + let w = layout.w; + let size = 50.0; + let half_size = size / 2.0; + + renderer.icon(x + w / 2.0 - half_size, y + 10.0, size, cone_icon()); + renderer.centered_text(x, y + size + 15.0, w, 25.0, 25.0, "WIP"); + })) + }) } diff --git a/nuon/src/widget/canvas.rs b/nuon/src/widget/canvas.rs new file mode 100644 index 0000000..0b7e954 --- /dev/null +++ b/nuon/src/widget/canvas.rs @@ -0,0 +1,48 @@ +use crate::{Element, LayoutCtx, Node, ParentLayout, RenderCtx, Renderer, Tree, Widget}; + +pub struct Canvas { + draw: F, +} + +impl Canvas { + pub fn new(draw: F) -> Self { + Self { draw } + } +} + +impl Widget for Canvas { + type State = (); + + fn layout( + &self, + _tree: &mut Tree, + parent: &ParentLayout, + _ctx: &LayoutCtx, + ) -> Node { + Node { + x: parent.x, + y: parent.y, + w: parent.w, + h: parent.h, + children: vec![], + } + } + + fn render( + &self, + renderer: &mut dyn Renderer, + layout: &Node, + _tree: &Tree, + _ctx: &RenderCtx, + ) { + (self.draw)(renderer, layout) + } +} + +impl From> + for Element +{ + fn from(value: Canvas) -> Self { + Element::new(value) + } +} diff --git a/nuon/src/widget/container.rs b/nuon/src/widget/container.rs index 3ce267f..2c893ed 100644 --- a/nuon/src/widget/container.rs +++ b/nuon/src/widget/container.rs @@ -3,6 +3,7 @@ use crate::{Color, Element, LayoutCtx, Node, ParentLayout, RenderCtx, Renderer, pub struct Container { child: [Element; 1], background: Option, + border_radius: [f32; 4], x: f32, y: f32, width: Option, @@ -20,6 +21,7 @@ impl Container { Self { child: [Element::null()], background: None, + border_radius: [0.0; 4], x: 0.0, y: 0.0, width: None, @@ -37,6 +39,11 @@ impl Container { self } + pub fn border_radius(mut self, radius: [f32; 4]) -> Self { + self.border_radius = radius; + self + } + pub fn x(mut self, x: f32) -> Self { self.x = x; self @@ -104,7 +111,14 @@ impl Widget for Container { ctx: &RenderCtx, ) { if let Some(bg) = self.background { - renderer.quad(layout.x, layout.y, layout.w, layout.h, bg); + renderer.rounded_quad( + layout.x, + layout.y, + layout.w, + layout.h, + bg, + self.border_radius, + ); } crate::default_render(self, renderer, layout, tree, ctx); } diff --git a/nuon/src/widget/mod.rs b/nuon/src/widget/mod.rs index 63ab817..bc1667d 100644 --- a/nuon/src/widget/mod.rs +++ b/nuon/src/widget/mod.rs @@ -1,4 +1,5 @@ pub mod button; +pub mod canvas; pub mod column; pub mod container; pub mod null;