From 0cd6dce47cfddac2d917c1e3ef2e52cead209a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kav=C3=ADk?= Date: Mon, 17 Jun 2024 18:06:36 +0200 Subject: [PATCH] clear_selected_vars, select_vars, selected_vars --- frontend/src/controls_panel.rs | 13 +++--- frontend/src/header_panel.rs | 44 ++++++++++-------- frontend/src/main.rs | 5 +- frontend/src/script_bridge.rs | 46 ++++++++++++++++++- frontend/src/waveform_panel.rs | 8 ++-- frontend/typescript/bundles/pixi_canvas.js | 6 ++- .../typescript/pixi_canvas/pixi_canvas.ts | 10 ++-- 7 files changed, 94 insertions(+), 38 deletions(-) diff --git a/frontend/src/controls_panel.rs b/frontend/src/controls_panel.rs index d55e722..90f9cce 100644 --- a/frontend/src/controls_panel.rs +++ b/frontend/src/controls_panel.rs @@ -3,6 +3,7 @@ use std::cell::Cell; use std::mem; use std::ops::Not; use std::rc::Rc; +use std::sync::Arc; use wellen::GetItem; use zoon::*; @@ -32,7 +33,7 @@ struct ScopeForUI { #[derive(Clone)] pub struct ControlsPanel { selected_scope_ref: Mutable>, - hierarchy: Mutable>>, + hierarchy: Mutable>>, selected_var_refs: MutableVec, layout: Mutable, loaded_filename: Mutable>, @@ -40,7 +41,7 @@ pub struct ControlsPanel { impl ControlsPanel { pub fn new( - hierarchy: Mutable>>, + hierarchy: Mutable>>, selected_var_refs: MutableVec, layout: Mutable, loaded_filename: Mutable>, @@ -114,7 +115,7 @@ impl ControlsPanel { )) } - fn scopes_panel(&self, hierarchy: Rc) -> impl Element { + fn scopes_panel(&self, hierarchy: Arc) -> impl Element { Column::new() .s(Height::fill()) .s(Scrollbars::y_and_clip_x()) @@ -129,7 +130,7 @@ impl ControlsPanel { .item(self.scopes_list(hierarchy)) } - fn scopes_list(&self, hierarchy: Rc) -> impl Element { + fn scopes_list(&self, hierarchy: Arc) -> impl Element { let layout = self.layout.clone(); let mut scopes_for_ui = Vec::new(); let mut max_level_index: usize = 0; @@ -379,7 +380,7 @@ impl ControlsPanel { .label(scope_for_ui.name) } - fn vars_panel(&self, hierarchy: Rc) -> impl Element { + fn vars_panel(&self, hierarchy: Arc) -> impl Element { let selected_scope_ref = self.selected_scope_ref.clone(); Column::new() .s(Align::new().top()) @@ -401,7 +402,7 @@ impl ControlsPanel { fn vars_list( &self, selected_scope_ref: wellen::ScopeRef, - hierarchy: Rc, + hierarchy: Arc, ) -> impl Element { let vars_for_ui = hierarchy .get(selected_scope_ref) diff --git a/frontend/src/header_panel.rs b/frontend/src/header_panel.rs index d12c60e..7f97895 100644 --- a/frontend/src/header_panel.rs +++ b/frontend/src/header_panel.rs @@ -1,16 +1,16 @@ use crate::{platform, script_bridge, Filename, Layout}; -use std::rc::Rc; +use std::sync::Arc; use zoon::*; pub struct HeaderPanel { - hierarchy: Mutable>>, + hierarchy: Mutable>>, layout: Mutable, loaded_filename: Mutable>, } impl HeaderPanel { pub fn new( - hierarchy: Mutable>>, + hierarchy: Mutable>>, layout: Mutable, loaded_filename: Mutable>, ) -> impl Element { @@ -68,7 +68,7 @@ impl HeaderPanel { Task::start(async move { if let Some(filename) = platform::pick_and_load_waveform(None).await { loaded_filename.set_neq(Some(filename)); - hierarchy.set(Some(Rc::new(platform::get_hierarchy().await))) + hierarchy.set(Some(Arc::new(platform::get_hierarchy().await))) } }) }) @@ -267,22 +267,30 @@ impl HeaderPanel { .family([FontFamily::new("Courier New"), FontFamily::Monospace])) .s(Scrollbars::both()) .s(Height::default().max(100)) - .child_signal(command_result.signal_ref(|result| match result { - Some(Ok(js_value)) => { - if let Some(string_value) = js_value.as_string() { - string_value - } else if let Some(number_value) = js_value.as_f64() { - number_value.to_string() - } else if let Some(bool_value) = js_value.as_bool() { - bool_value.to_string() - } else { - format!("{js_value:?}") + .child_signal(command_result.signal_ref(|result| { + fn format_complex_js_value(js_value: &JsValue) -> String { + let value = format!("{js_value:?}"); + let value = value.strip_prefix("JsValue(").unwrap_throw(); + let value = value.strip_suffix(')').unwrap_throw(); + value.to_owned() + } + match result { + Some(Ok(js_value)) => { + if let Some(string_value) = js_value.as_string() { + string_value + } else if let Some(number_value) = js_value.as_f64() { + number_value.to_string() + } else if let Some(bool_value) = js_value.as_bool() { + bool_value.to_string() + } else { + format_complex_js_value(js_value) + } } + Some(Err(js_value)) => { + format!("ERROR: {}", format_complex_js_value(js_value)) + } + None => "-".to_owned(), } - Some(Err(js_value)) => { - format!("Error: {js_value:?}") - } - None => "-".to_owned(), })) } } diff --git a/frontend/src/main.rs b/frontend/src/main.rs index a783db7..a54a5e9 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -1,4 +1,4 @@ -use std::rc::Rc; +use std::sync::Arc; use zoon::*; mod platform; @@ -25,6 +25,7 @@ type Filename = String; #[derive(Default)] struct Store { selected_var_refs: MutableVec, + hierarchy: Mutable>>, } static STORE: Lazy = lazy::default(); @@ -39,7 +40,7 @@ fn main() { } fn root() -> impl Element { - let hierarchy: Mutable>> = <_>::default(); + let hierarchy = STORE.hierarchy.clone(); let selected_var_refs = STORE.selected_var_refs.clone(); let layout: Mutable = <_>::default(); let loaded_filename: Mutable> = <_>::default(); diff --git a/frontend/src/script_bridge.rs b/frontend/src/script_bridge.rs index 31d66bc..139e2f2 100644 --- a/frontend/src/script_bridge.rs +++ b/frontend/src/script_bridge.rs @@ -1,6 +1,9 @@ use crate::STORE; +use wellen::GetItem; use zoon::*; +type FullVarName = String; + #[wasm_bindgen( inline_js = r#"export function strict_eval(code) { "use strict"; return eval?.(`${code}`) }"# )] @@ -14,14 +17,53 @@ pub struct FW; #[wasm_bindgen] impl FW { + /// JS: `FW.say_hello()` -> `Hello!` pub fn say_hello() -> String { "Hello!".to_owned() } - pub fn clear_variables() -> String { + /// JS: `FW.clear_selected_vars()` -> `4` + pub fn clear_selected_vars() -> usize { let mut vars = STORE.selected_var_refs.lock_mut(); let var_count = vars.len(); vars.clear(); - format!("{var_count} variables cleared") + var_count + } + + /// JS: `FW.select_vars(["simple_tb.s.A", "simple_tb.s.B"])` -> `2` + pub fn select_vars(full_var_names: Vec) -> usize { + if let Some(hierarchy) = STORE.hierarchy.get_cloned() { + let mut new_var_refs = Vec::new(); + for full_var_name in full_var_names { + let path_with_name = full_var_name.split_terminator('.').collect::>(); + if let Some((name, path)) = path_with_name.split_last() { + if let Some(var_ref) = hierarchy.lookup_var(path, name) { + new_var_refs.push(var_ref); + } + } + } + let var_ref_count = new_var_refs.len(); + STORE.selected_var_refs.lock_mut().replace(new_var_refs); + return var_ref_count; + } + 0 + } + + pub fn loaded_filename() -> String { + format!("todo loaded filename") + } + + /// JS: `FW.selected_vars()` -> `["simple_tb.s.A", "simple_tb.s.B"]` + pub fn selected_vars() -> Vec { + if let Some(hierarchy) = STORE.hierarchy.get_cloned() { + let mut full_var_names = Vec::new(); + for var_ref in STORE.selected_var_refs.lock_ref().as_slice() { + let var = hierarchy.get(*var_ref); + let var_name = var.full_name(&hierarchy); + full_var_names.push(var_name); + } + return full_var_names; + } + Vec::new() } } diff --git a/frontend/src/waveform_panel.rs b/frontend/src/waveform_panel.rs index 2493ea9..09d5f84 100644 --- a/frontend/src/waveform_panel.rs +++ b/frontend/src/waveform_panel.rs @@ -1,5 +1,5 @@ use crate::platform; -use std::rc::Rc; +use std::sync::Arc; use wellen::GetItem; use zoon::*; @@ -12,13 +12,13 @@ const ROW_GAP: u32 = 4; #[derive(Clone)] pub struct WaveformPanel { selected_var_refs: MutableVec, - hierarchy: Mutable>>, + hierarchy: Mutable>>, canvas_controller: Mutable>>, } impl WaveformPanel { pub fn new( - hierarchy: Mutable>>, + hierarchy: Mutable>>, selected_var_refs: MutableVec, ) -> impl Element { Self { @@ -111,7 +111,7 @@ impl WaveformPanel { async fn push_var( controller: &PixiController, - hierarchy: &Mutable>>, + hierarchy: &Mutable>>, var_ref: wellen::VarRef, ) { let hierarchy = hierarchy.get_cloned().unwrap(); diff --git a/frontend/typescript/bundles/pixi_canvas.js b/frontend/typescript/bundles/pixi_canvas.js index d542b5e..674bb70 100644 --- a/frontend/typescript/bundles/pixi_canvas.js +++ b/frontend/typescript/bundles/pixi_canvas.js @@ -35316,10 +35316,12 @@ var VarSignalRow = class { this.draw(); } draw() { - if (this.app === null || this.app.screen === null) { + if (this?.app?.screen?.width === void 0) { return; } - this.row_container_background.width = this.app.screen.width; + if (this?.row_container_background?._texture?.orig?.width !== void 0) { + this.row_container_background.width = this.app.screen.width; + } this.signal_blocks_container.removeChildren(); this.timeline.blocks.forEach((timeline_block) => { const signal_block = new Container(); diff --git a/frontend/typescript/pixi_canvas/pixi_canvas.ts b/frontend/typescript/pixi_canvas/pixi_canvas.ts index 04e0935..39b896d 100644 --- a/frontend/typescript/pixi_canvas/pixi_canvas.ts +++ b/frontend/typescript/pixi_canvas/pixi_canvas.ts @@ -275,12 +275,14 @@ class VarSignalRow { draw() { // Screen can be null when we are, for instance, switching between miller columns and tree layout - // and then the canvas has to be recreated - if (this.app === null || this.app.screen === null) { + // and then the canvas has to be recreated. + if (this?.app?.screen?.width === undefined) { return; } - - this.row_container_background.width = this.app.screen.width; + // Workaround for "TypeError: Cannot read properties of null (reading 'orig')" + if (this?.row_container_background?._texture?.orig?.width !== undefined) { + this.row_container_background.width = this.app.screen.width; + } // @TODO optimize by reusing a pool of blocks instead or removing all children on every redraw? this.signal_blocks_container.removeChildren();