Skip to content

Commit

Permalink
nuon: Nuon persistent state tree (PolyMeilex#223)
Browse files Browse the repository at this point in the history
* nuon: WidgetAny

* nuon: State associated type

* nuon: Primitive state tree

* nuon: Use associated type for auto downcasting

* nuon: Ability to override state initializer

* nuon: Cleanup trilayout

* nuon: Use null tree instead of Option<Tree>

* nuon: Global ref store

* nuon: Drop all lifetimes from widgets

* nuon: Remove multiple tree diffing methods

* Drop `Ui` struct that used to hold UI state

* nuon: Base Layout impl reuse

* nuon: Default implementations for Widget methods
  • Loading branch information
PolyMeilex authored Nov 25, 2024
1 parent 501234e commit d95236f
Show file tree
Hide file tree
Showing 20 changed files with 1,043 additions and 691 deletions.
2 changes: 2 additions & 0 deletions neothesia/src/scene/playing_scene/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct PlayingScene {
toast_manager: ToastManager,

nuon_event_queue: nuon::input::EventQueue,
tree: nuon::Tree,

top_bar: TopBar,
}
Expand Down Expand Up @@ -113,6 +114,7 @@ impl PlayingScene {
toast_manager: ToastManager::default(),

nuon_event_queue: nuon::input::EventQueue::new(),
tree: nuon::Tree::null(),

top_bar: TopBar::new(),
}
Expand Down
65 changes: 36 additions & 29 deletions neothesia/src/scene/playing_scene/top_bar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ pub struct TopBar {
looper_active: bool,
loop_start: Duration,
loop_end: Duration,

ui: ui::Ui,
}

impl TopBar {
Expand All @@ -51,8 +49,6 @@ impl TopBar {
looper_active: false,
loop_start: Duration::ZERO,
loop_end: Duration::ZERO,

ui: ui::Ui::new(),
}
}

Expand Down Expand Up @@ -143,35 +139,46 @@ impl TopBar {

#[profiling::function]
fn update_nuon(scene: &mut PlayingScene, ctx: &mut Context, _delta: Duration, y: f32) {
let mut root = scene
.top_bar
.ui
.view(ui::UiData {
y,
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(),
})
.into();
let globals = nuon::GlobalStore::with(|store| {
store.insert(&scene.player);
});

let mut root = ui::top_bar(ui::UiData {
y,
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(),
})
.into();

scene.tree.diff(root.as_widget());

let layout = {
profiling::scope!("nuon_layout");
root.as_widget_mut().layout(&nuon::LayoutCtx {
x: 0.0,
y: 0.0,
w: ctx.window_state.logical_size.width,
h: ctx.window_state.logical_size.height,
})
root.as_widget_mut().layout(
&mut scene.tree,
&nuon::ParentLayout {
x: 0.0,
y: 0.0,
w: ctx.window_state.logical_size.width,
h: ctx.window_state.logical_size.height,
},
&nuon::LayoutCtx { globals: &globals },
)
};

let mut messages = vec![];

scene
.nuon_event_queue
.dispatch_events(&mut messages, root.as_widget_mut(), &layout);
scene.nuon_event_queue.dispatch_events(
&mut messages,
&mut scene.tree,
root.as_widget_mut(),
&layout,
&globals,
);

{
profiling::scope!("nuon_render");
Expand All @@ -181,7 +188,8 @@ impl TopBar {
text: &mut ctx.text_renderer,
},
&layout,
&nuon::RenderCtx {},
&scene.tree,
&nuon::RenderCtx { globals: &globals },
);
}

Expand All @@ -203,8 +211,7 @@ impl TopBar {

top_bar.is_expanded = is_hovered;
top_bar.is_expanded |= top_bar.settings_active;
top_bar.is_expanded |= top_bar.ui.looper.is_grabbed();
top_bar.is_expanded |= top_bar.ui.proggress_bar.is_grabbed();
top_bar.is_expanded |= scene.nuon_event_queue.is_mouse_grabbed();

let now = ctx.frame_timestamp;

Expand Down
187 changes: 76 additions & 111 deletions neothesia/src/scene/playing_scene/top_bar/ui.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
use std::time::Duration;

use nuon::{
button::{Button, ButtonState},
column::Column,
container::Container,
row::Row,
speed_pill::{SpeedPill, SpeedPillState},
stack::Stack,
translate::Translate,
trilayout::TriLayout,
Color, Element,
button::Button, column::Column, container::Container, row::Row, stack::Stack,
trilayout::TriLayout, Color, Element,
};

use crate::scene::playing_scene::midi_player::MidiPlayer;

use super::widget::{
looper::{Looper, LooperState},
progress_bar::{ProgressBar, ProgressBarState},
};
use super::widget::{looper::Looper, progress_bar::ProgressBar, speed_pill::SpeedPill};

#[derive(Debug, Clone)]
pub enum ProgressBarMsg {
Expand Down Expand Up @@ -79,109 +69,84 @@ pub struct UiData<'a> {
pub player: &'a MidiPlayer,
}

#[derive(Default)]
pub struct Header {
back: ButtonState,
play: ButtonState,
repeat: ButtonState,
settings: ButtonState,
speed_pill: SpeedPillState,
}

impl Header {
fn view(&mut self, data: &UiData) -> impl Into<Element<'_, Msg>> {
Container::new().height(30.0).child(
TriLayout::new()
.start(
Row::new().push(
Button::new(&mut self.back)
.icon(left_arrow_icon())
fn header(data: &UiData) -> impl Into<Element<Msg>> {
Container::new().height(30.0).child(
TriLayout::new()
.start(
Row::new().push(
Button::new()
.icon(left_arrow_icon())
.width(30.0)
.on_click(Msg::GoBack),
),
)
.center(
SpeedPill::new()
.speed(data.speed)
.on_minus(Msg::SpeedDown)
.on_plus(Msg::SpeedUp),
)
.end(
Row::new()
.push(
Button::new()
.icon(if data.player.is_paused() {
play_icon()
} else {
pause_icon()
})
.width(30.0)
.on_click(Msg::PauseResume),
)
.push(
Button::new()
.icon(repeat_icon())
.width(30.0)
.on_click(Msg::Looper(LooperMsg::Toggle)),
)
.push(
Button::new()
.icon(if data.is_settings_open {
gear_fill_icon()
} else {
gear_icon()
})
.width(30.0)
.on_click(Msg::GoBack),
.on_click(Msg::SettingsToggle),
),
)
.center(
SpeedPill::new(&mut self.speed_pill)
.speed(data.speed)
.on_minus(Msg::SpeedDown)
.on_plus(Msg::SpeedUp),
)
.end(
Row::new()
.push(
Button::new(&mut self.play)
.icon(if data.player.is_paused() {
play_icon()
} else {
pause_icon()
})
.width(30.0)
.on_click(Msg::PauseResume),
)
.push(
Button::new(&mut self.repeat)
.icon(repeat_icon())
.width(30.0)
.on_click(Msg::Looper(LooperMsg::Toggle)),
)
.push(
Button::new(&mut self.settings)
.icon(if data.is_settings_open {
gear_fill_icon()
} else {
gear_icon()
})
.width(30.0)
.on_click(Msg::SettingsToggle),
),
),
)
}
}

#[derive(Default)]
pub struct Ui {
header: Header,
pub proggress_bar: ProgressBarState,
pub looper: LooperState,
),
)
}

impl Ui {
pub fn new() -> Self {
Self::default()
}

#[profiling::function]
pub fn view<'a>(&'a mut self, data: UiData<'a>) -> impl Into<Element<'a, Msg>> {
let header = self.header.view(&data);

let timeline = Container::new().height(45.0).child(
Row::new().push(
Stack::new()
.push(
ProgressBar::new(&mut self.proggress_bar, data.player)
.color(Color::new_u8(56, 145, 255, 1.0))
.on_press(Msg::ProggresBar(ProgressBarMsg::Pressed))
.on_release(Msg::ProggresBar(ProgressBarMsg::Released)),
#[profiling::function]
pub fn top_bar(data: UiData) -> impl Into<Element<Msg>> {
let header = header(&data);

let timeline = Container::new().height(45.0).child(
Row::new().push(
Stack::new()
.push(
ProgressBar::new()
.color(Color::new_u8(56, 145, 255, 1.0))
.on_press(Msg::ProggresBar(ProgressBarMsg::Pressed))
.on_release(Msg::ProggresBar(ProgressBarMsg::Released)),
)
.when(data.is_looper_on, |stack| {
stack.push(
Looper::new()
.start(data.loop_start)
.end(data.loop_end)
.on_start_move(|p| Msg::Looper(LooperMsg::MoveStart(p)))
.on_end_move(|p| Msg::Looper(LooperMsg::MoveEnd(p))),
)
.when(data.is_looper_on, |stack| {
stack.push(
Looper::new(&mut self.looper, data.player)
.start(data.loop_start)
.end(data.loop_end)
.on_start_move(|p| Msg::Looper(LooperMsg::MoveStart(p)))
.on_end_move(|p| Msg::Looper(LooperMsg::MoveEnd(p))),
)
}),
),
);
}),
),
);

let body = Column::new().push(header).push(timeline);
let body = Column::new().push(header).push(timeline);

Translate::new().y(data.y).child(
Container::new()
.background(Color::new_u8(37, 35, 42, 1.0))
.child(body),
)
}
Container::new()
.y(data.y)
.background(Color::new_u8(37, 35, 42, 1.0))
.child(body)
}
Loading

0 comments on commit d95236f

Please sign in to comment.