diff --git a/.cargo/config.toml b/.cargo/config.toml index 1cb68ba..2ad2672 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,6 @@ [build] +# `target` to to prevent rebuilding from scratch on each change +[target.wasm32-unknown-unknown] # https://docs.rs/web-sys/latest/web_sys/struct.Window.html#method.show_open_file_picker # https://github.com/rustwasm/wasm-bindgen/issues/2339#issuecomment-1000651386 -[target.wasm32-unknown-unknown] rustflags = ["--cfg=web_sys_unstable_apis"] diff --git a/MoonZoon.toml b/MoonZoon.toml index 2b62ab0..3e0c013 100644 --- a/MoonZoon.toml +++ b/MoonZoon.toml @@ -1,6 +1,6 @@ -# port = 8080 -port = 8443 -https = true +port = 8080 +# port = 8443 +https = false cache_busting = true backend_log_level = "warn" # "error" / "warn" / "info" / "debug" / "trace" diff --git a/frontend/src/controls_panel.rs b/frontend/src/controls_panel.rs index cafdb53..0f77de6 100644 --- a/frontend/src/controls_panel.rs +++ b/frontend/src/controls_panel.rs @@ -5,7 +5,7 @@ use std::mem; use std::ops::Not; use std::rc::Rc; use wellen::GetItem; -use zoon::*; +use zoon::{println, *}; const SCOPE_VAR_ROW_MAX_WIDTH: u32 = 480; const MILLER_COLUMN_MAX_HEIGHT: u32 = 500; @@ -124,6 +124,7 @@ impl ControlsPanel { )) } + #[cfg(FASTWAVE_PLATFORM = "TAURI")] fn load_button(&self) -> impl Element { let (hovered, hovered_signal) = Mutable::new_and_signal(false); let hierarchy_and_time_table = self.hierarchy_and_time_table.clone(); @@ -152,7 +153,7 @@ impl ControlsPanel { let hierarchy_and_time_table = hierarchy_and_time_table.clone(); let loaded_filename = loaded_filename.clone(); Task::start(async move { - if let Some(filename) = platform::pick_and_load_waveform().await { + if let Some(filename) = platform::pick_and_load_waveform(None).await { loaded_filename.set_neq(Some(filename)); let (hierarchy, time_table) = join!(platform::get_hierarchy(), platform::get_time_table()); @@ -163,6 +164,77 @@ impl ControlsPanel { }) } + #[cfg(FASTWAVE_PLATFORM = "BROWSER")] + fn load_button(&self) -> impl Element { + let (hovered, hovered_signal) = Mutable::new_and_signal(false); + let hierarchy_and_time_table = self.hierarchy_and_time_table.clone(); + let loaded_filename = self.loaded_filename.clone(); + let file_input_id = "file_input"; + 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(El::new().s(Font::new().no_wrap()).child_signal( + loaded_filename.signal_cloned().map_option( + |filename| format!("Unload {filename}"), + || format!("Load file.."), + ), + )) + .on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered)) + .for_input(file_input_id) + .on_click_event_with_options(EventOptions::new().preventable(), clone!((hierarchy_and_time_table) move |event| { + let mut hierarchy_and_time_table_lock = hierarchy_and_time_table.lock_mut(); + if hierarchy_and_time_table_lock.is_some() { + *hierarchy_and_time_table_lock = None; + if let RawMouseEvent::Click(raw_event) = event.raw_event { + // @TODO Move to MoonZoon as a new API + raw_event.prevent_default(); + } + return; + } + })) + ) + .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 { + println!("file list is `None`"); + return; + }; + let Some(file) = file_list.first().cloned() else { + println!("file list is empty"); + return; + }; + let hierarchy_and_time_table = hierarchy_and_time_table.clone(); + let loaded_filename = loaded_filename.clone(); + Task::start(async move { + if let Some(filename) = platform::pick_and_load_waveform(Some(file)).await { + loaded_filename.set_neq(Some(filename)); + let (hierarchy, time_table) = + join!(platform::get_hierarchy(), platform::get_time_table()); + hierarchy_and_time_table + .set(Some((Rc::new(hierarchy), Rc::new(time_table)))) + } + }) + }) + }) + ) + } + fn layout_switcher(&self) -> impl Element { let layout = self.layout.clone(); let (hovered, hovered_signal) = Mutable::new_and_signal(false); diff --git a/frontend/src/platform.rs b/frontend/src/platform.rs index cac13aa..297bee4 100644 --- a/frontend/src/platform.rs +++ b/frontend/src/platform.rs @@ -19,9 +19,10 @@ pub async fn show_window() { platform::show_window().await } -// @TODO allow only support file types -pub async fn pick_and_load_waveform() -> Option { - platform::pick_and_load_waveform().await +// @TODO allow only supported file types by Wellen +// @TODO remove the `file` parameter once we don't have to use FileInput element +pub async fn pick_and_load_waveform(file: Option) -> Option { + platform::pick_and_load_waveform(file).await } pub async fn get_hierarchy() -> wellen::Hierarchy { diff --git a/frontend/src/platform/browser.rs b/frontend/src/platform/browser.rs index d24b392..dc4d2eb 100644 --- a/frontend/src/platform/browser.rs +++ b/frontend/src/platform/browser.rs @@ -1,7 +1,7 @@ use shared::wellen_helpers; use std::sync::Mutex; use wellen::simple::Waveform; -use zoon::{println, *}; +use zoon::*; #[derive(Default)] struct Store { @@ -12,30 +12,11 @@ static STORE: Lazy = lazy::default(); pub(super) async fn show_window() {} -pub(super) async fn pick_and_load_waveform() -> Option { - let file_handles_promise = window().show_open_file_picker().expect_throw( - "failed to open file picker (browser has to support `showOpenFilePicker` and use HTTPS", - ); - let file_handles = JsFuture::from(file_handles_promise).await; - let file_handles = match file_handles { - Ok(file_handles) => file_handles.dyn_into::().unwrap_throw(), - Err(error) => { - println!("file picker error: {error:?}"); - return None; - } - }; - let file_handle = file_handles - .at(0) - .dyn_into::() - .unwrap_throw(); +pub(super) async fn pick_and_load_waveform( + file: Option, +) -> Option { + let file = file.unwrap_throw(); - let file = JsFuture::from(file_handle.get_file()) - .await - .unwrap_throw() - .dyn_into::() - .unwrap_throw(); - - let file = gloo_file::File::from(file); let content = gloo_file::futures::read_as_bytes(&file) .await .unwrap_throw(); @@ -48,6 +29,44 @@ pub(super) async fn pick_and_load_waveform() -> Option { Some(file.name()) } +// @TODO Use this `pick_and_load_waveform` version once `showOpenFilePicker` is supported by Safari and Firefox +// https://caniuse.com/mdn-api_window_showopenfilepicker +// pub(super) async fn pick_and_load_waveform() -> Option { +// let file_handles_promise = window().show_open_file_picker().expect_throw( +// "failed to open file picker (browser has to support `showOpenFilePicker` and use HTTPS", +// ); +// let file_handles = JsFuture::from(file_handles_promise).await; +// let file_handles = match file_handles { +// Ok(file_handles) => file_handles.dyn_into::().unwrap_throw(), +// Err(error) => { +// println!("file picker error: {error:?}"); +// return None; +// } +// }; +// let file_handle = file_handles +// .at(0) +// .dyn_into::() +// .unwrap_throw(); + +// let file = JsFuture::from(file_handle.get_file()) +// .await +// .unwrap_throw() +// .dyn_into::() +// .unwrap_throw(); + +// let file = gloo_file::File::from(file); +// let content = gloo_file::futures::read_as_bytes(&file) +// .await +// .unwrap_throw(); + +// let waveform = wellen_helpers::read_from_bytes(content); +// let Ok(waveform) = waveform else { +// panic!("Waveform file reading failed") +// }; +// *STORE.waveform.lock().unwrap_throw() = Some(waveform); +// Some(file.name()) +// } + pub(super) async fn get_hierarchy() -> wellen::Hierarchy { let waveform = 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 b72472c..cd97554 100644 --- a/frontend/src/platform/tauri.rs +++ b/frontend/src/platform/tauri.rs @@ -4,7 +4,9 @@ pub(super) async fn show_window() { tauri_glue::show_window().await.unwrap_throw() } -pub(super) async fn pick_and_load_waveform() -> Option { +pub(super) async fn pick_and_load_waveform( + _file: Option, +) -> Option { tauri_glue::pick_and_load_waveform() .await .unwrap_throw() diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2c18dfc..bd2346e 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -4,7 +4,7 @@ "identifier": "com.fastwave", "build": { "frontendDist": "../frontend_dist", - "devUrl": "https://localhost:8443", + "devUrl": "http://localhost:8080", "beforeDevCommand": "makers mzoon_for_tauri start", "beforeBuildCommand": "makers mzoon_for_tauri build -r -f" },