file picker - file input
This commit is contained in:
parent
ce36cd914f
commit
e3e3820cc5
|
@ -1,5 +1,6 @@
|
||||||
[build]
|
[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://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
|
# https://github.com/rustwasm/wasm-bindgen/issues/2339#issuecomment-1000651386
|
||||||
[target.wasm32-unknown-unknown]
|
|
||||||
rustflags = ["--cfg=web_sys_unstable_apis"]
|
rustflags = ["--cfg=web_sys_unstable_apis"]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# port = 8080
|
port = 8080
|
||||||
port = 8443
|
# port = 8443
|
||||||
https = true
|
https = false
|
||||||
cache_busting = true
|
cache_busting = true
|
||||||
backend_log_level = "warn" # "error" / "warn" / "info" / "debug" / "trace"
|
backend_log_level = "warn" # "error" / "warn" / "info" / "debug" / "trace"
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::mem;
|
||||||
use std::ops::Not;
|
use std::ops::Not;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wellen::GetItem;
|
use wellen::GetItem;
|
||||||
use zoon::*;
|
use zoon::{println, *};
|
||||||
|
|
||||||
const SCOPE_VAR_ROW_MAX_WIDTH: u32 = 480;
|
const SCOPE_VAR_ROW_MAX_WIDTH: u32 = 480;
|
||||||
const MILLER_COLUMN_MAX_HEIGHT: u32 = 500;
|
const MILLER_COLUMN_MAX_HEIGHT: u32 = 500;
|
||||||
|
@ -124,6 +124,7 @@ impl ControlsPanel {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(FASTWAVE_PLATFORM = "TAURI")]
|
||||||
fn load_button(&self) -> impl Element {
|
fn load_button(&self) -> impl Element {
|
||||||
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
|
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
|
||||||
let hierarchy_and_time_table = self.hierarchy_and_time_table.clone();
|
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 hierarchy_and_time_table = hierarchy_and_time_table.clone();
|
||||||
let loaded_filename = loaded_filename.clone();
|
let loaded_filename = loaded_filename.clone();
|
||||||
Task::start(async move {
|
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));
|
loaded_filename.set_neq(Some(filename));
|
||||||
let (hierarchy, time_table) =
|
let (hierarchy, time_table) =
|
||||||
join!(platform::get_hierarchy(), platform::get_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 {
|
fn layout_switcher(&self) -> impl Element {
|
||||||
let layout = self.layout.clone();
|
let layout = self.layout.clone();
|
||||||
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
|
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
|
||||||
|
|
|
@ -19,9 +19,10 @@ pub async fn show_window() {
|
||||||
platform::show_window().await
|
platform::show_window().await
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO allow only support file types
|
// @TODO allow only supported file types by Wellen
|
||||||
pub async fn pick_and_load_waveform() -> Option<Filename> {
|
// @TODO remove the `file` parameter once we don't have to use FileInput element
|
||||||
platform::pick_and_load_waveform().await
|
pub async fn pick_and_load_waveform(file: Option<gloo_file::File>) -> Option<Filename> {
|
||||||
|
platform::pick_and_load_waveform(file).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_hierarchy() -> wellen::Hierarchy {
|
pub async fn get_hierarchy() -> wellen::Hierarchy {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use shared::wellen_helpers;
|
use shared::wellen_helpers;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use wellen::simple::Waveform;
|
use wellen::simple::Waveform;
|
||||||
use zoon::{println, *};
|
use zoon::*;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Store {
|
struct Store {
|
||||||
|
@ -12,30 +12,11 @@ static STORE: Lazy<Store> = lazy::default();
|
||||||
|
|
||||||
pub(super) async fn show_window() {}
|
pub(super) async fn show_window() {}
|
||||||
|
|
||||||
pub(super) async fn pick_and_load_waveform() -> Option<super::Filename> {
|
pub(super) async fn pick_and_load_waveform(
|
||||||
let file_handles_promise = window().show_open_file_picker().expect_throw(
|
file: Option<gloo_file::File>,
|
||||||
"failed to open file picker (browser has to support `showOpenFilePicker` and use HTTPS",
|
) -> Option<super::Filename> {
|
||||||
);
|
let file = file.unwrap_throw();
|
||||||
let file_handles = JsFuture::from(file_handles_promise).await;
|
|
||||||
let file_handles = match file_handles {
|
|
||||||
Ok(file_handles) => file_handles.dyn_into::<js_sys::Array>().unwrap_throw(),
|
|
||||||
Err(error) => {
|
|
||||||
println!("file picker error: {error:?}");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let file_handle = file_handles
|
|
||||||
.at(0)
|
|
||||||
.dyn_into::<web_sys::FileSystemFileHandle>()
|
|
||||||
.unwrap_throw();
|
|
||||||
|
|
||||||
let file = JsFuture::from(file_handle.get_file())
|
|
||||||
.await
|
|
||||||
.unwrap_throw()
|
|
||||||
.dyn_into::<web_sys::File>()
|
|
||||||
.unwrap_throw();
|
|
||||||
|
|
||||||
let file = gloo_file::File::from(file);
|
|
||||||
let content = gloo_file::futures::read_as_bytes(&file)
|
let content = gloo_file::futures::read_as_bytes(&file)
|
||||||
.await
|
.await
|
||||||
.unwrap_throw();
|
.unwrap_throw();
|
||||||
|
@ -48,6 +29,44 @@ pub(super) async fn pick_and_load_waveform() -> Option<super::Filename> {
|
||||||
Some(file.name())
|
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<super::Filename> {
|
||||||
|
// 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::<js_sys::Array>().unwrap_throw(),
|
||||||
|
// Err(error) => {
|
||||||
|
// println!("file picker error: {error:?}");
|
||||||
|
// return None;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// let file_handle = file_handles
|
||||||
|
// .at(0)
|
||||||
|
// .dyn_into::<web_sys::FileSystemFileHandle>()
|
||||||
|
// .unwrap_throw();
|
||||||
|
|
||||||
|
// let file = JsFuture::from(file_handle.get_file())
|
||||||
|
// .await
|
||||||
|
// .unwrap_throw()
|
||||||
|
// .dyn_into::<web_sys::File>()
|
||||||
|
// .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 {
|
pub(super) async fn get_hierarchy() -> wellen::Hierarchy {
|
||||||
let waveform = STORE.waveform.lock().unwrap_throw();
|
let waveform = STORE.waveform.lock().unwrap_throw();
|
||||||
let hierarchy = waveform.as_ref().unwrap_throw().hierarchy();
|
let hierarchy = waveform.as_ref().unwrap_throw().hierarchy();
|
||||||
|
|
|
@ -4,7 +4,9 @@ pub(super) async fn show_window() {
|
||||||
tauri_glue::show_window().await.unwrap_throw()
|
tauri_glue::show_window().await.unwrap_throw()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn pick_and_load_waveform() -> Option<super::Filename> {
|
pub(super) async fn pick_and_load_waveform(
|
||||||
|
_file: Option<gloo_file::File>,
|
||||||
|
) -> Option<super::Filename> {
|
||||||
tauri_glue::pick_and_load_waveform()
|
tauri_glue::pick_and_load_waveform()
|
||||||
.await
|
.await
|
||||||
.unwrap_throw()
|
.unwrap_throw()
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"identifier": "com.fastwave",
|
"identifier": "com.fastwave",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../frontend_dist",
|
"frontendDist": "../frontend_dist",
|
||||||
"devUrl": "https://localhost:8443",
|
"devUrl": "http://localhost:8080",
|
||||||
"beforeDevCommand": "makers mzoon_for_tauri start",
|
"beforeDevCommand": "makers mzoon_for_tauri start",
|
||||||
"beforeBuildCommand": "makers mzoon_for_tauri build -r -f"
|
"beforeBuildCommand": "makers mzoon_for_tauri build -r -f"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue