diff --git a/docs/video_desktop.gif b/docs/video_simple_vcd.gif similarity index 100% rename from docs/video_desktop.gif rename to docs/video_simple_vcd.gif diff --git a/frontend/src/controls_panel.rs b/frontend/src/controls_panel.rs index 490526f..ebfd4e5 100644 --- a/frontend/src/controls_panel.rs +++ b/frontend/src/controls_panel.rs @@ -99,7 +99,6 @@ impl ControlsPanel { Layout::Columns => Height::fill().max(MILLER_COLUMN_MAX_HEIGHT), }, ))) - .s(Scrollbars::both()) .s(Padding::all(20)) .s(Gap::new().y(40)) .s(Align::new().top()) diff --git a/frontend/src/waveform_panel.rs b/frontend/src/waveform_panel.rs index b32062e..5ba42a7 100644 --- a/frontend/src/waveform_panel.rs +++ b/frontend/src/waveform_panel.rs @@ -1,6 +1,6 @@ use crate::{platform, HierarchyAndTimeTable}; use wellen::GetItem; -use zoon::{eprintln, *}; +use zoon::*; mod pixi_canvas; use pixi_canvas::{PixiCanvas, PixiController}; @@ -109,10 +109,6 @@ impl WaveformPanel { var_ref: wellen::VarRef, ) { let (hierarchy, time_table) = hierarchy_and_time_table.get_cloned().unwrap(); - if time_table.is_empty() { - eprintln!("timetable is empty"); - return; - } let var = hierarchy.get(var_ref); let signal_ref = var.signal_ref(); @@ -128,7 +124,9 @@ impl WaveformPanel { zoon::println!("{timescale:?}"); // Note: Sync `timeline`'s type with the `Timeline` in `frontend/typescript/pixi_canvas/pixi_canvas.ts' - controller.push_var(serde_wasm_bindgen::to_value(&timeline).unwrap_throw()); + let timeline = serde_wasm_bindgen::to_value(&timeline).unwrap_throw(); + let signal_ref_index = signal_ref.index(); + controller.push_var(signal_ref_index, timeline); } fn selected_var_panel( diff --git a/frontend/src/waveform_panel/pixi_canvas.rs b/frontend/src/waveform_panel/pixi_canvas.rs index 066e8dd..2ac3dcf 100644 --- a/frontend/src/waveform_panel/pixi_canvas.rs +++ b/frontend/src/waveform_panel/pixi_canvas.rs @@ -1,5 +1,7 @@ pub use js_bridge::PixiController; use zoon::*; +use std::rc::Rc; +use crate::platform; pub struct PixiCanvas { raw_el: RawHtmlEl, @@ -45,6 +47,16 @@ impl PixiCanvas { })), ); let task_with_controller = Mutable::new(None); + // -- FastWave-specific -- + let timeline_getter = Rc::new(Closure::new(|signal_ref_index, screen_width, row_height| { + future_to_promise(async move { + let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap_throw(); + let timeline = platform::load_signal_and_get_timeline(signal_ref, screen_width, row_height).await; + let timeline = serde_wasm_bindgen::to_value(&timeline).unwrap_throw(); + Ok(timeline) + }) + })); + // -- // -- Self { controller: controller.read_only(), width: width.read_only(), @@ -56,14 +68,15 @@ impl PixiCanvas { width.set_neq(new_width); height.set_neq(new_height); })) - .after_insert(clone!((controller) move |element| { + .after_insert(clone!((controller, timeline_getter) move |element| { Task::start(async move { - let pixi_controller = js_bridge::PixiController::new(row_height, row_gap); + let pixi_controller = js_bridge::PixiController::new(row_height, row_gap, &timeline_getter); pixi_controller.init(&element).await; controller.set(Some(pixi_controller)); }); })) .after_remove(move |_| { + drop(timeline_getter); drop(resize_task); drop(task_with_controller); if let Some(controller) = controller.take() { @@ -87,6 +100,12 @@ impl PixiCanvas { mod js_bridge { use zoon::*; + type TimelinePromise = js_sys::Promise; + type SignalRefIndex = usize; + type ScreenWidth = u32; + type RowHeight = u32; + type TimelineGetter = Closure TimelinePromise>; + // Note: Add all corresponding methods to `frontend/typescript/pixi_canvas/pixi_canvas.ts` #[wasm_bindgen(module = "/typescript/bundles/pixi_canvas.js")] extern "C" { @@ -95,7 +114,7 @@ mod js_bridge { // @TODO `row_height` and `row_gap` is FastWave-specific #[wasm_bindgen(constructor)] - pub fn new(row_height: u32, row_gap: u32) -> PixiController; + pub fn new(row_height: u32, row_gap: u32, timeline_getter: &TimelineGetter) -> PixiController; #[wasm_bindgen(method)] pub async fn init(this: &PixiController, parent_element: &JsValue); @@ -115,7 +134,7 @@ mod js_bridge { pub fn remove_var(this: &PixiController, index: usize); #[wasm_bindgen(method)] - pub fn push_var(this: &PixiController, timeline: JsValue); + pub fn push_var(this: &PixiController, signal_ref_index: usize, timeline: JsValue); #[wasm_bindgen(method)] pub fn pop_var(this: &PixiController); diff --git a/frontend/typescript/bundles/pixi_canvas.js b/frontend/typescript/bundles/pixi_canvas.js index 3c5a9ad..3c110fa 100644 --- a/frontend/typescript/bundles/pixi_canvas.js +++ b/frontend/typescript/bundles/pixi_canvas.js @@ -35136,12 +35136,14 @@ var PixiController = class { row_height; row_gap; previous_parent_width; - constructor(row_height, row_gap) { + timeline_getter; + constructor(row_height, row_gap, timeline_getter) { this.app = new Application(); this.row_height = row_height; this.row_gap = row_gap; this.app.stage.addChild(this.var_signal_rows_container); this.previous_parent_width = null; + this.timeline_getter = timeline_getter; } async init(parent_element) { await this.app.init({ background: "DarkSlateBlue", antialias: true, resizeTo: parent_element }); @@ -35174,15 +35176,19 @@ var PixiController = class { } // -- FastWave-specific -- redraw_rows() { - this.var_signal_rows.forEach((row) => row.draw()); + this.var_signal_rows.forEach(async (row) => { + const timeline = await this.timeline_getter(row.signal_ref_index, this.app.screen.width, this.row_height); + row.redraw(timeline); + }); } remove_var(index) { if (typeof this.var_signal_rows[index] !== "undefined") { this.var_signal_rows[index].destroy(); } } - push_var(timeline) { + push_var(signal_ref_index, timeline) { new VarSignalRow( + signal_ref_index, timeline, this.app, this.var_signal_rows, @@ -35199,8 +35205,9 @@ var PixiController = class { } }; var VarSignalRow = class { - app; + signal_ref_index; timeline; + app; owner; index_in_owner; rows_container; @@ -35216,9 +35223,10 @@ var VarSignalRow = class { fontSize: 16, fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"' }); - constructor(timeline, app, owner, rows_container, row_height, row_gap) { - this.app = app; + constructor(signal_ref_index, timeline, app, owner, rows_container, row_height, row_gap) { + this.signal_ref_index = signal_ref_index; this.timeline = timeline; + this.app = app; this.row_height = row_height; this.row_gap = row_gap; this.row_height_with_gap = row_height + row_gap; @@ -35241,6 +35249,9 @@ var VarSignalRow = class { this.draw(); } draw() { + if (this.app.screen === null) { + return; + } this.row_container_background.width = this.app.screen.width; this.signal_blocks_container.removeChildren(); this.timeline.blocks.forEach((timeline_block) => { diff --git a/frontend/typescript/pixi_canvas/pixi_canvas.ts b/frontend/typescript/pixi_canvas/pixi_canvas.ts index c8661bf..7800ab5 100644 --- a/frontend/typescript/pixi_canvas/pixi_canvas.ts +++ b/frontend/typescript/pixi_canvas/pixi_canvas.ts @@ -16,6 +16,8 @@ type TimeLineBlockLabel = { y: number, } +type TimelineGetter = (signal_ref_index: number, screen_width: number, row_height: number) => Promise; + export class PixiController { app: Application // -- FastWave-specific -- @@ -24,14 +26,16 @@ export class PixiController { row_height: number; row_gap: number; previous_parent_width: number | null; + timeline_getter: TimelineGetter - constructor(row_height: number, row_gap: number) { + constructor(row_height: number, row_gap: number, timeline_getter: TimelineGetter) { this.app = new Application(); // -- FastWave-specific -- this.row_height = row_height; this.row_gap = row_gap; this.app.stage.addChild(this.var_signal_rows_container); this.previous_parent_width = null; + this.timeline_getter = timeline_getter; } async init(parent_element: HTMLElement) { @@ -71,7 +75,10 @@ export class PixiController { // -- FastWave-specific -- redraw_rows() { - this.var_signal_rows.forEach(row => row.draw()); + this.var_signal_rows.forEach(async row => { + const timeline = await this.timeline_getter(row.signal_ref_index, this.app.screen.width, this.row_height); + row.redraw(timeline); + }); } remove_var(index: number) { @@ -80,8 +87,9 @@ export class PixiController { } } - push_var(timeline: Timeline) { + push_var(signal_ref_index: number, timeline: Timeline) { new VarSignalRow( + signal_ref_index, timeline, this.app, this.var_signal_rows, @@ -101,8 +109,9 @@ export class PixiController { } class VarSignalRow { - app: Application; + signal_ref_index: number; timeline: Timeline; + app: Application; owner: Array; index_in_owner: number; rows_container: Container; @@ -120,6 +129,7 @@ class VarSignalRow { }); constructor( + signal_ref_index: number, timeline: Timeline, app: Application, owner: Array, @@ -127,9 +137,9 @@ class VarSignalRow { row_height: number, row_gap: number, ) { - this.app = app; - + this.signal_ref_index = signal_ref_index; this.timeline = timeline; + this.app = app; this.row_height = row_height; this.row_gap = row_gap; @@ -164,6 +174,12 @@ class VarSignalRow { } draw() { + // Screen can be null when we are, for instance, switching between miller column and tree layout + // and then the canvas has to be recreated + if (this.app.screen === null) { + return; + } + this.row_container_background.width = this.app.screen.width; this.signal_blocks_container.removeChildren();