Scripting, save & reload vars, layout improvements #3

Merged
MartinKavik merged 17 commits from scripting into main 2024-06-19 17:09:00 +00:00
9 changed files with 147 additions and 5 deletions
Showing only changes of commit 1160efe0e3 - Show all commits

View file

@ -79,7 +79,7 @@ impl HeaderPanel {
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
let hierarchy = self.hierarchy.clone();
let loaded_filename = self.loaded_filename.clone();
let file_input_id = "file_input";
let file_input_id = "file_input_for_load_waveform_button";
Row::new()
.item(
Label::new()

View file

@ -14,6 +14,7 @@ mod browser;
use browser as platform;
type Filename = String;
type JavascriptCode = String;
pub async fn show_window() {
platform::show_window().await
@ -25,6 +26,12 @@ pub async fn pick_and_load_waveform(file: Option<gloo_file::File>) -> Option<Fil
platform::pick_and_load_waveform(file).await
}
// @TODO allow only supported file type (*.fw.js)
// @TODO remove the `file` parameter once we don't have to use FileInput element
pub async fn load_file_with_selected_vars(file: Option<gloo_file::File>) -> Option<JavascriptCode> {
platform::load_file_with_selected_vars(file).await
}
pub async fn get_hierarchy() -> wellen::Hierarchy {
platform::get_hierarchy().await
}

View file

@ -67,6 +67,22 @@ pub(super) async fn pick_and_load_waveform(
// Some(file.name())
// }
// @TODO allow only supported file type (*.fw.js)
// @TODO remove the `file` parameter once we don't have to use FileInput element
pub async fn load_file_with_selected_vars(
file: Option<gloo_file::File>,
) -> Option<super::JavascriptCode> {
let file = file.unwrap_throw();
let javascript_code = gloo_file::futures::read_as_text(&file).await.unwrap_throw();
Some(javascript_code)
}
// @TODO Use alternative `load_file_with_selected_vars` version once `showOpenFilePicker` is supported by Safari and Firefox
// https://caniuse.com/mdn-api_window_showopenfilepicker
// (see the `pick_and_load_waveform` method above)
pub(super) async fn get_hierarchy() -> wellen::Hierarchy {
let waveform = BROWSER_PLATFORM_STORE.waveform.lock().unwrap_throw();
let hierarchy = waveform.as_ref().unwrap_throw().hierarchy();

View file

@ -13,6 +13,15 @@ pub(super) async fn pick_and_load_waveform(
.as_string()
}
pub(super) async fn load_file_with_selected_vars(
_file: Option<gloo_file::File>,
) -> Option<super::JavascriptCode> {
tauri_glue::load_file_with_selected_vars()
.await
.unwrap_throw()
.as_string()
}
pub(super) async fn get_hierarchy() -> wellen::Hierarchy {
serde_wasm_bindgen::from_value(tauri_glue::get_hierarchy().await.unwrap_throw()).unwrap_throw()
}
@ -59,6 +68,9 @@ mod tauri_glue {
#[wasm_bindgen(catch)]
pub async fn pick_and_load_waveform() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn load_file_with_selected_vars() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn get_hierarchy() -> Result<JsValue, JsValue>;

View file

@ -1,4 +1,4 @@
use crate::platform;
use crate::{platform, script_bridge};
use std::sync::Arc;
use wellen::GetItem;
use zoon::*;
@ -46,7 +46,7 @@ impl WaveformPanel {
.s(Gap::new().x(20))
.s(Width::fill())
.item(Spacer::fill())
.item(self.save_load_selected_vars_buttons())
.item(self.load_save_selected_vars_buttons())
.item(self.keys_info())
}
@ -64,7 +64,7 @@ impl WaveformPanel {
)
}
fn save_load_selected_vars_buttons(&self) -> impl Element {
fn load_save_selected_vars_buttons(&self) -> impl Element {
Row::new()
.s(Gap::new().x(20))
.item(self.load_selected_vars_button())
@ -76,6 +76,7 @@ impl WaveformPanel {
.item(self.save_selected_vars_button())
}
#[cfg(FASTWAVE_PLATFORM = "TAURI")]
fn load_selected_vars_button(&self) -> impl Element {
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
Button::new()
@ -83,10 +84,85 @@ impl WaveformPanel {
.s(Background::new().color_signal(
hovered_signal.map_bool(|| color!("MediumSlateBlue"), || color!("SlateBlue")),
))
.s(Align::new().left())
.s(RoundedCorners::all(15))
.label("Load")
.on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered))
.on_press(move || zoon::println!("LOAD!"))
.on_press(|| {
Task::start(async move {
if let Some(javascript_code) =
platform::load_file_with_selected_vars(None).await
{
match script_bridge::strict_eval(&javascript_code) {
Ok(js_value) => {
zoon::println!("File with selected vars loaded: {js_value:?}")
}
Err(js_value) => {
zoon::eprintln!(
"Failed to load file with selected vars: {js_value:?}"
)
}
}
}
})
})
}
#[cfg(FASTWAVE_PLATFORM = "BROWSER")]
fn load_selected_vars_button(&self) -> impl Element {
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
let file_input_id = "file_input_for_load_selected_vars_button";
Row::new()
.item(
Label::new()
.s(Padding::new().x(20).y(10))
.s(Background::new().color_signal(
hovered_signal
.map_bool(|| color!("MediumSlateBlue"), || color!("SlateBlue")),
))
.s(Align::new().left())
.s(RoundedCorners::all(15))
.s(Cursor::new(CursorIcon::Pointer))
.label("Load")
.on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered))
.for_input(file_input_id),
)
.item(
// @TODO https://github.com/MoonZoon/MoonZoon/issues/39
// + https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications#using_hidden_file_input_elements_using_the_click_method
TextInput::new().id(file_input_id).update_raw_el(|raw_el| {
let dom_element = raw_el.dom_element();
raw_el
.style("display", "none")
.attr("type", "file")
.event_handler(move |_: events::Input| {
let Some(file_list) =
dom_element.files().map(gloo_file::FileList::from)
else {
zoon::println!("file list is `None`");
return;
};
let Some(file) = file_list.first().cloned() else {
zoon::println!("file list is empty");
return;
};
Task::start(async move {
if let Some(javascript_code) =
platform::load_file_with_selected_vars(Some(file)).await
{
match script_bridge::strict_eval(&javascript_code) {
Ok(js_value) => zoon::println!(
"File with selected vars loaded: {js_value:?}"
),
Err(js_value) => zoon::eprintln!(
"Failed to load file with selected vars: {js_value:?}"
),
}
}
})
})
}),
)
}
fn save_selected_vars_button(&self) -> impl Element {

View file

@ -2517,6 +2517,9 @@ async function show_window() {
async function pick_and_load_waveform() {
return await invoke2("pick_and_load_waveform");
}
async function load_file_with_selected_vars() {
return await invoke2("load_file_with_selected_vars");
}
async function get_hierarchy() {
return await invoke2("get_hierarchy");
}
@ -2535,6 +2538,7 @@ async function unload_signal(signal_ref_index) {
}
export {
get_hierarchy,
load_file_with_selected_vars,
load_signal_and_get_timeline,
pick_and_load_waveform,
show_window,

View file

@ -5,6 +5,7 @@ import { core } from '@tauri-apps/api'
const invoke = core.invoke;
type Filename = string;
type JavascriptCode = string;
type WellenHierarchy = unknown;
type Timeline = unknown;
type VarFormat = unknown;
@ -17,6 +18,10 @@ export async function pick_and_load_waveform(): Promise<Filename | undefined> {
return await invoke("pick_and_load_waveform");
}
export async function load_file_with_selected_vars(): Promise<JavascriptCode | undefined> {
return await invoke("load_file_with_selected_vars");
}
export async function get_hierarchy(): Promise<WellenHierarchy> {
return await invoke("get_hierarchy");
}

View file

@ -1,8 +1,10 @@
use std::fs;
use std::sync::Mutex;
use tauri_plugin_dialog::DialogExt;
use wellen::simple::Waveform;
type Filename = String;
type JavascriptCode = String;
#[derive(Default)]
struct Store {
@ -32,6 +34,18 @@ async fn pick_and_load_waveform(
Ok(Some(file_response.name.unwrap()))
}
#[tauri::command(rename_all = "snake_case")]
async fn load_file_with_selected_vars(app: tauri::AppHandle) -> Result<Option<JavascriptCode>, ()> {
let Some(file_response) = app.dialog().file().blocking_pick_file() else {
return Ok(None);
};
// @TODO Tokio's `fs` or a Tauri `fs`?
let Ok(javascript_code) = fs::read_to_string(file_response.path) else {
panic!("Selected vars file reading failed")
};
Ok(Some(javascript_code))
}
#[tauri::command(rename_all = "snake_case")]
async fn get_hierarchy(store: tauri::State<'_, Store>) -> Result<serde_json::Value, ()> {
let waveform = store.waveform.lock().unwrap();
@ -91,6 +105,7 @@ pub fn run() {
.invoke_handler(tauri::generate_handler![
show_window,
pick_and_load_waveform,
load_file_with_selected_vars,
get_hierarchy,
load_signal_and_get_timeline,
unload_signal,

View file

@ -0,0 +1,7 @@
if (FW.loaded_filename() === "simple.vcd") {
FW.select_vars([
"simple_tb.s.A",
"simple_tb.s.A",
"simple_tb.s.B"
])
}