From 9ea952a2f570b1731ac117a2fcda0e6d2ff200bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kav=C3=ADk?= Date: Fri, 31 May 2024 01:35:05 +0200 Subject: [PATCH] fix canvas position, dynamic layout --- frontend/src/controls_panel.rs | 42 +++++++++++------ frontend/src/main.rs | 34 ++++++++++---- frontend/src/waveform_panel.rs | 46 ++++++++++++------- frontend/src/waveform_panel/pixi_canvas.rs | 2 + frontend/typescript/bundles/pixi_canvas.js | 6 ++- .../typescript/pixi_canvas/pixi_canvas.ts | 6 ++- 6 files changed, 93 insertions(+), 43 deletions(-) diff --git a/frontend/src/controls_panel.rs b/frontend/src/controls_panel.rs index 08b3458..1eabc14 100644 --- a/frontend/src/controls_panel.rs +++ b/frontend/src/controls_panel.rs @@ -1,5 +1,5 @@ use crate::tauri_bridge; -use crate::HierarchyAndTimeTable; +use crate::{HierarchyAndTimeTable, Layout}; use futures_util::join; use std::mem; use std::ops::Not; @@ -8,13 +8,7 @@ use wellen::GetItem; use zoon::*; const SCOPE_VAR_ROW_MAX_WIDTH: u32 = 480; - -#[derive(Clone, Copy, Default)] -enum Layout { - Tree, - #[default] - Columns, -} +const MILLER_COLUMN_MAX_HEIGHT: u32 = 500; #[derive(Clone)] struct VarForUI { @@ -48,12 +42,13 @@ impl ControlsPanel { pub fn new( hierarchy_and_time_table: Mutable>, selected_var_refs: MutableVec, + layout: Mutable, ) -> impl Element { Self { selected_scope_ref: <_>::default(), hierarchy_and_time_table, selected_var_refs, - layout: <_>::default(), + layout, } .root() } @@ -71,16 +66,25 @@ impl ControlsPanel { fn root(&self) -> impl Element { let triggers = self.triggers(); + let layout = self.layout.clone(); let layout_and_hierarchy_signal = map_ref! { - let layout = self.layout.signal(), + let layout = layout.signal(), let hierarchy_and_time_table = self.hierarchy_and_time_table.signal_cloned() => { (*layout, hierarchy_and_time_table.clone().map(|(hierarchy, _)| hierarchy)) } }; Column::new() .after_remove(move |_| drop(triggers)) - .s(Height::fill()) - .s(Width::fill()) + .s(Width::with_signal_self( + self.layout + .signal() + .map(|layout| matches!(layout, Layout::Columns)) + .map_true(|| Width::fill()), + )) + .s(Height::with_signal_self(layout.signal().map(move |layout| match layout { + Layout::Tree => Height::fill(), + Layout::Columns => Height::fill().max(MILLER_COLUMN_MAX_HEIGHT), + }))) .s(Scrollbars::both()) .s(Padding::all(20)) .s(Gap::new().y(40)) @@ -254,7 +258,7 @@ impl ControlsPanel { .s(Height::fill()) // @TODO add `width: max-content` to MoonZoon's `Width`? .update_raw_el(|raw_el| raw_el.style("width", "max-content")) - .on_viewport_size_change(move |width, _| viewport_x.set(i32::MAX)) + .on_viewport_size_change(move |_, _| viewport_x.set(i32::MAX)) .items(scopes_for_ui_in_levels.into_iter().map(|scopes_in_level| { Column::new() .s(Height::fill()) @@ -396,7 +400,16 @@ impl ControlsPanel { ) .on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered)) .on_press(move || match layout.get() { - Layout::Tree => scope_for_ui.expanded.update(not), + Layout::Tree => { + if scope_for_ui.expanded.get() { + scope_for_ui.selected_scope_in_level.set(None); + } else { + scope_for_ui + .selected_scope_in_level + .set(Some(scope_for_ui.scope_ref)); + } + scope_for_ui.expanded.update(not) + } Layout::Columns => { selected_scope_ref.set_neq(None); if scope_for_ui.expanded.get() { @@ -415,7 +428,6 @@ impl ControlsPanel { scope_for_ui: ScopeForUI, button_hovered: Mutable, ) -> impl Element { - let layout = self.layout.clone(); Button::new() .s(Padding::new().x(15).y(5)) .s(Font::new().wrap_anywhere()) diff --git a/frontend/src/main.rs b/frontend/src/main.rs index 480ac71..94629ac 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -11,6 +11,13 @@ use waveform_panel::WaveformPanel; type HierarchyAndTimeTable = (Rc, Rc); +#[derive(Clone, Copy, Default)] +enum Layout { + Tree, + #[default] + Columns, +} + fn main() { start_app("app", root); Task::start(async { @@ -23,16 +30,27 @@ fn main() { fn root() -> impl Element { let hierarchy_and_time_table: Mutable> = <_>::default(); let selected_var_refs: MutableVec = <_>::default(); - Row::new() + let layout: Mutable = <_>::default(); + Column::new() .s(Height::fill()) + .s(Scrollbars::y_and_clip_x()) .s(Font::new().color(color!("Lavender"))) - .s(Gap::new().x(15)) - .item(ControlsPanel::new( + .item( + Row::new() + .s(Height::fill()) + .s(Gap::new().x(15)) + .item(ControlsPanel::new( + hierarchy_and_time_table.clone(), + selected_var_refs.clone(), + layout.clone(), + )) + .item_signal(layout.signal().map(|layout| matches!(layout, Layout::Tree)).map_true(clone!((hierarchy_and_time_table, selected_var_refs) move || WaveformPanel::new( + hierarchy_and_time_table.clone(), + selected_var_refs.clone(), + )))) + ) + .item_signal(layout.signal().map(|layout| matches!(layout, Layout::Columns)).map_true(move || WaveformPanel::new( hierarchy_and_time_table.clone(), selected_var_refs.clone(), - )) - .item(WaveformPanel::new( - hierarchy_and_time_table, - selected_var_refs, - )) + ))) } diff --git a/frontend/src/waveform_panel.rs b/frontend/src/waveform_panel.rs index 61aa76c..08b6873 100644 --- a/frontend/src/waveform_panel.rs +++ b/frontend/src/waveform_panel.rs @@ -3,7 +3,7 @@ use wellen::GetItem; use zoon::*; mod pixi_canvas; -use pixi_canvas::PixiCanvas; +use pixi_canvas::{PixiCanvas, PixiController}; const ROW_HEIGHT: u32 = 40; const ROW_GAP: u32 = 4; @@ -67,7 +67,13 @@ impl WaveformPanel { })).for_each(clone!((controller, hierarchy_and_time_table) move |vec_diff| { clone!((controller, hierarchy_and_time_table) async move { match vec_diff { - VecDiff::Replace { values: _ } => { todo!("`task_with_controller` + `Replace`") }, + VecDiff::Replace { values } => { + let controller = controller.wait_for_some_cloned().await; + controller.clear_vars(); + for var_ref in values { + Self::push_var(&controller, &hierarchy_and_time_table, var_ref).await; + } + }, VecDiff::InsertAt { index: _, value: _ } => { todo!("`task_with_controller` + `InsertAt`") } VecDiff::UpdateAt { index: _, value: _ } => { todo!("`task_with_controller` + `UpdateAt`") } VecDiff::RemoveAt { index } => { @@ -78,20 +84,7 @@ impl WaveformPanel { VecDiff::Move { old_index: _, new_index: _ } => { todo!("`task_with_controller` + `Move`") } VecDiff::Push { value: var_ref } => { if let Some(controller) = controller.lock_ref().as_ref() { - let (hierarchy, time_table) = hierarchy_and_time_table.get_cloned().unwrap(); - let var = hierarchy.get(var_ref); - let signal_ref = var.signal_ref(); - let signal = tauri_bridge::load_and_get_signal(signal_ref).await; - - let timescale = hierarchy.timescale(); - zoon::println!("{timescale:?}"); - - // Note: Sync `timeline`'s type with the `Timeline` in `frontend/typescript/pixi_canvas/pixi_canvas.ts' - let mut timeline: Vec<(wellen::Time, Option)> = time_table.iter().map(|time| (*time, None)).collect(); - for (time_index, signal_value) in signal.iter_changes() { - timeline[time_index as usize].1 = Some(signal_value.to_string()); - } - controller.push_var(serde_wasm_bindgen::to_value(&timeline).unwrap_throw()); + Self::push_var(controller, &hierarchy_and_time_table, var_ref).await; } } VecDiff::Pop {} => { @@ -110,6 +103,27 @@ impl WaveformPanel { }) } + async fn push_var( + controller: &PixiController, + hierarchy_and_time_table: &Mutable>, + var_ref: wellen::VarRef, + ) { + let (hierarchy, time_table) = hierarchy_and_time_table.get_cloned().unwrap(); + let var = hierarchy.get(var_ref); + let signal_ref = var.signal_ref(); + let signal = tauri_bridge::load_and_get_signal(signal_ref).await; + + let timescale = hierarchy.timescale(); + zoon::println!("{timescale:?}"); + + // Note: Sync `timeline`'s type with the `Timeline` in `frontend/typescript/pixi_canvas/pixi_canvas.ts' + let mut timeline: Vec<(wellen::Time, Option)> = time_table.iter().map(|time| (*time, None)).collect(); + for (time_index, signal_value) in signal.iter_changes() { + timeline[time_index as usize].1 = Some(signal_value.to_string()); + } + controller.push_var(serde_wasm_bindgen::to_value(&timeline).unwrap_throw()); + } + fn selected_var_panel( &self, index: ReadOnlyMutable>, diff --git a/frontend/src/waveform_panel/pixi_canvas.rs b/frontend/src/waveform_panel/pixi_canvas.rs index b935056..6ffcd2a 100644 --- a/frontend/src/waveform_panel/pixi_canvas.rs +++ b/frontend/src/waveform_panel/pixi_canvas.rs @@ -1,4 +1,5 @@ use zoon::*; +pub use js_bridge::PixiController; pub struct PixiCanvas { raw_el: RawHtmlEl, @@ -89,6 +90,7 @@ mod js_bridge { // Note: Add all corresponding methods to `frontend/typescript/pixi_canvas/pixi_canvas.ts` #[wasm_bindgen(module = "/typescript/bundles/pixi_canvas.js")] extern "C" { + #[derive(Clone)] pub type PixiController; // @TODO `row_height` and `row_gap` is FastWave-specific diff --git a/frontend/typescript/bundles/pixi_canvas.js b/frontend/typescript/bundles/pixi_canvas.js index b8136f4..114a541 100644 --- a/frontend/typescript/bundles/pixi_canvas.js +++ b/frontend/typescript/bundles/pixi_canvas.js @@ -35161,7 +35161,9 @@ var PixiController = class { } // -- FastWave-specific -- remove_var(index) { - this.var_signal_rows[index].destroy(); + if (typeof this.var_signal_rows[index] !== "undefined") { + this.var_signal_rows[index].destroy(); + } } push_var(timeline) { new VarSignalRow( @@ -35174,7 +35176,7 @@ var PixiController = class { ); } pop_var() { - this.var_signal_rows[this.var_signal_rows.length - 1].destroy(); + this.remove_var(this.var_signal_rows.length - 1); } clear_vars() { this.var_signal_rows.slice().reverse().forEach((row) => row.destroy()); diff --git a/frontend/typescript/pixi_canvas/pixi_canvas.ts b/frontend/typescript/pixi_canvas/pixi_canvas.ts index 8249c3a..e23feda 100644 --- a/frontend/typescript/pixi_canvas/pixi_canvas.ts +++ b/frontend/typescript/pixi_canvas/pixi_canvas.ts @@ -46,7 +46,9 @@ export class PixiController { // -- FastWave-specific -- remove_var(index: number) { - this.var_signal_rows[index].destroy(); + if (typeof this.var_signal_rows[index] !== 'undefined') { + this.var_signal_rows[index].destroy(); + } } push_var(timeline: Timeline) { @@ -61,7 +63,7 @@ export class PixiController { } pop_var() { - this.var_signal_rows[this.var_signal_rows.length - 1].destroy(); + this.remove_var(this.var_signal_rows.length - 1); } clear_vars() {