From 1160efe0e353bfdf396d62cfeb23adf4b86c1830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kav=C3=ADk?= Date: Tue, 18 Jun 2024 20:43:00 +0200 Subject: [PATCH] platform::load_file_with_selected_vars, simple_vcd.fw.js --- frontend/src/header_panel.rs | 2 +- frontend/src/platform.rs | 7 ++ frontend/src/platform/browser.rs | 16 ++++ frontend/src/platform/tauri.rs | 12 +++ frontend/src/waveform_panel.rs | 84 +++++++++++++++++++- frontend/typescript/bundles/tauri_glue.js | 4 + frontend/typescript/tauri_glue/tauri_glue.ts | 5 ++ src-tauri/src/lib.rs | 15 ++++ test_files/simple_vcd.fw.js | 7 ++ 9 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 test_files/simple_vcd.fw.js diff --git a/frontend/src/header_panel.rs b/frontend/src/header_panel.rs index 52b2900..72e5c25 100644 --- a/frontend/src/header_panel.rs +++ b/frontend/src/header_panel.rs @@ -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() diff --git a/frontend/src/platform.rs b/frontend/src/platform.rs index 73c7f73..e152433 100644 --- a/frontend/src/platform.rs +++ b/frontend/src/platform.rs @@ -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) -> Option) -> Option { + platform::load_file_with_selected_vars(file).await +} + pub async fn get_hierarchy() -> wellen::Hierarchy { platform::get_hierarchy().await } diff --git a/frontend/src/platform/browser.rs b/frontend/src/platform/browser.rs index 8fcb001..363a5c2 100644 --- a/frontend/src/platform/browser.rs +++ b/frontend/src/platform/browser.rs @@ -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, +) -> Option { + 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(); diff --git a/frontend/src/platform/tauri.rs b/frontend/src/platform/tauri.rs index 30300be..aea34b6 100644 --- a/frontend/src/platform/tauri.rs +++ b/frontend/src/platform/tauri.rs @@ -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, +) -> Option { + 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; + #[wasm_bindgen(catch)] + pub async fn load_file_with_selected_vars() -> Result; + #[wasm_bindgen(catch)] pub async fn get_hierarchy() -> Result; diff --git a/frontend/src/waveform_panel.rs b/frontend/src/waveform_panel.rs index fbfd835..c0e6920 100644 --- a/frontend/src/waveform_panel.rs +++ b/frontend/src/waveform_panel.rs @@ -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 { diff --git a/frontend/typescript/bundles/tauri_glue.js b/frontend/typescript/bundles/tauri_glue.js index a81bef0..026793f 100644 --- a/frontend/typescript/bundles/tauri_glue.js +++ b/frontend/typescript/bundles/tauri_glue.js @@ -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, diff --git a/frontend/typescript/tauri_glue/tauri_glue.ts b/frontend/typescript/tauri_glue/tauri_glue.ts index 2945a41..a4be66c 100644 --- a/frontend/typescript/tauri_glue/tauri_glue.ts +++ b/frontend/typescript/tauri_glue/tauri_glue.ts @@ -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 { return await invoke("pick_and_load_waveform"); } +export async function load_file_with_selected_vars(): Promise { + return await invoke("load_file_with_selected_vars"); +} + export async function get_hierarchy(): Promise { return await invoke("get_hierarchy"); } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index d985c1c..c8752db 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -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, ()> { + 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 { 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, diff --git a/test_files/simple_vcd.fw.js b/test_files/simple_vcd.fw.js new file mode 100644 index 0000000..999095c --- /dev/null +++ b/test_files/simple_vcd.fw.js @@ -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" + ]) +}