Scripting, save & reload vars, layout improvements #3

Merged
MartinKavik merged 17 commits from scripting into main 2024-06-19 17:09:00 +00:00
7 changed files with 94 additions and 38 deletions
Showing only changes of commit b55d876fd2 - Show all commits

View file

@ -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<Option<wellen::ScopeRef>>,
hierarchy: Mutable<Option<Rc<wellen::Hierarchy>>>,
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
selected_var_refs: MutableVec<wellen::VarRef>,
layout: Mutable<Layout>,
loaded_filename: Mutable<Option<Filename>>,
@ -40,7 +41,7 @@ pub struct ControlsPanel {
impl ControlsPanel {
pub fn new(
hierarchy: Mutable<Option<Rc<wellen::Hierarchy>>>,
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
selected_var_refs: MutableVec<wellen::VarRef>,
layout: Mutable<Layout>,
loaded_filename: Mutable<Option<Filename>>,
@ -114,7 +115,7 @@ impl ControlsPanel {
))
}
fn scopes_panel(&self, hierarchy: Rc<wellen::Hierarchy>) -> impl Element {
fn scopes_panel(&self, hierarchy: Arc<wellen::Hierarchy>) -> 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<wellen::Hierarchy>) -> impl Element {
fn scopes_list(&self, hierarchy: Arc<wellen::Hierarchy>) -> 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<wellen::Hierarchy>) -> impl Element {
fn vars_panel(&self, hierarchy: Arc<wellen::Hierarchy>) -> 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<wellen::Hierarchy>,
hierarchy: Arc<wellen::Hierarchy>,
) -> impl Element {
let vars_for_ui = hierarchy
.get(selected_scope_ref)

View file

@ -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<Option<Rc<wellen::Hierarchy>>>,
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
layout: Mutable<Layout>,
loaded_filename: Mutable<Option<Filename>>,
}
impl HeaderPanel {
pub fn new(
hierarchy: Mutable<Option<Rc<wellen::Hierarchy>>>,
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
layout: Mutable<Layout>,
loaded_filename: Mutable<Option<Filename>>,
) -> 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(),
}))
}
}

View file

@ -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<wellen::VarRef>,
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
}
static STORE: Lazy<Store> = lazy::default();
@ -39,7 +40,7 @@ fn main() {
}
fn root() -> impl Element {
let hierarchy: Mutable<Option<Rc<wellen::Hierarchy>>> = <_>::default();
let hierarchy = STORE.hierarchy.clone();
let selected_var_refs = STORE.selected_var_refs.clone();
let layout: Mutable<Layout> = <_>::default();
let loaded_filename: Mutable<Option<Filename>> = <_>::default();

View file

@ -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<FullVarName>) -> 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::<Vec<_>>();
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<FullVarName> {
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()
}
}

View file

@ -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<wellen::VarRef>,
hierarchy: Mutable<Option<Rc<wellen::Hierarchy>>>,
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
canvas_controller: Mutable<ReadOnlyMutable<Option<PixiController>>>,
}
impl WaveformPanel {
pub fn new(
hierarchy: Mutable<Option<Rc<wellen::Hierarchy>>>,
hierarchy: Mutable<Option<Arc<wellen::Hierarchy>>>,
selected_var_refs: MutableVec<wellen::VarRef>,
) -> impl Element {
Self {
@ -111,7 +111,7 @@ impl WaveformPanel {
async fn push_var(
controller: &PixiController,
hierarchy: &Mutable<Option<Rc<wellen::Hierarchy>>>,
hierarchy: &Mutable<Option<Arc<wellen::Hierarchy>>>,
var_ref: wellen::VarRef,
) {
let hierarchy = hierarchy.get_cloned().unwrap();

View file

@ -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();

View file

@ -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();