wasm + native support for choosing files

This commit is contained in:
kirjavascript 2023-03-13 01:04:20 +00:00
parent 7b934633ee
commit 56e0e82e60
3 changed files with 200 additions and 77 deletions

View file

@ -115,11 +115,9 @@ impl eframe::App for App {
self.file.open_file();
}
use web_sys::{window, console, Element, HtmlInputElement, FileReader};
if let Some(file) = self.file.opened() {
console::log_1(&format!("File data buffer: {:?}", file).into());
println!("{:#?}", file);
}
});

View file

@ -0,0 +1,156 @@
// use std::future::Future;
// use eframe::{egui, epi};
// use rfd;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{window, console, Element, HtmlInputElement, FileReader};
use js_sys::{Uint8Array, ArrayBuffer, Object};
type File = Vec<u8>;
pub struct FileDialog {
tx: std::sync::mpsc::Sender<File>,
rx: std::sync::mpsc::Receiver<File>,
input: HtmlInputElement,
closure: Option<Closure<dyn FnMut()>>,
}
// #[cfg(not(target_arch = "wasm32"))]
impl Default for FileDialog {
#[cfg(target_arch = "wasm32")]
fn default() -> Self {
let (tx, rx) = std::sync::mpsc::channel();
let document = window().unwrap().document().unwrap();
let body = document.body().unwrap();
let input = document.create_element("input").unwrap().dyn_into::<HtmlInputElement>().unwrap();
input.set_attribute("type", "file").unwrap();
input.style().set_property("display", "none").unwrap();
body.append_child(&input).unwrap();
Self {
rx,
tx,
input,
closure: None,
}
}
}
impl Drop for FileDialog {
fn drop(&mut self) {
self.input.remove();
}
}
impl FileDialog {
pub fn open_file(&mut self) {
if let Some(closure) = &self.closure {
self.input.remove_event_listener_with_callback("change", closure.as_ref().unchecked_ref()).unwrap();
std::mem::replace(&mut self.closure, None).unwrap().forget();
}
let tx = self.tx.clone();
let input_clone = self.input.clone();
let closure = Closure::once(move || {
if let Some(file) = input_clone.files().and_then(|files| files.get(0)) {
let reader = FileReader::new().unwrap();
let reader_clone = reader.clone();
let onload_closure = Closure::once(Box::new(move || {
let array_buffer = reader_clone.result().unwrap().dyn_into::<ArrayBuffer>().unwrap();
let buffer = Uint8Array::new(&array_buffer).to_vec();
tx.send(buffer);
}));
reader.set_onload(Some(onload_closure.as_ref().unchecked_ref()));
reader.read_as_array_buffer(&file).unwrap();
onload_closure.forget();
}
});
self.input.add_event_listener_with_callback("change", closure.as_ref().unchecked_ref()).unwrap();
self.closure = Some(closure);
self.input.click();
// if ui.button("Open file…").clicked() {
// if let Some(path) = rfd::AsyncFileDialog::new().pick_file() {
// let _ = Some(path.display().to_string());
// }
// }
}
pub fn opened(&self) -> Option<Vec<u8>> {
if let Ok(file) = self.rx.try_recv() {
Some(file)
} else {
None
}
}
}
// pub fn filedialog(state: FileState) ->
// impl egui::Widget
// {
// move |ui: &mut egui::Ui| {
// }
// impl epi::App for FileApp {
// fn name(&self) -> &str {
// "file dialog app"
// }
// fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) {
// // This is important, otherwise file dialog can hang
// // and messages are not processed
// ctx.request_repaint();
// loop {
// match self.message_channel.1.try_recv() {
// Ok(_message) => {
// // Process FileOpen and other messages
// }
// Err(_) => {
// break;
// }
// }
// }
// egui::CentralPanel::default().show(ctx, |ui| {
// let open_button = ui.add(egui::widgets::Button::new("Open..."));
// if open_button.clicked() {
// let task = rfd::AsyncFileDialog::new()
// .add_filter("Text files", &["txt"])
// .set_directory("/")
// .pick_file();
// let message_sender = self.message_channel.0.clone();
// execute(async move {
// let file = task.await;
// if let Some(file) = file {
// message_sender.send(file.read().await).ok();
// }
// });
// }
// });
// }
// }
// #[cfg(not(target_arch = "wasm32"))]
// fn execute<F: Future<Output = ()> + Send + 'static>(f: F) {
// std::thread::spawn(move || futures::executor::block_on(f));
// }
// #[cfg(target_arch = "wasm32")]
// fn execute<F: Future<Output = ()> + 'static>(f: F) {
// wasm_bindgen_futures::spawn_local(f);
// }

View file

@ -1,14 +1,18 @@
// use std::future::Future;
// use eframe::{egui, epi};
// use rfd;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{window, console, Element, HtmlInputElement, FileReader};
use js_sys::{Uint8Array, ArrayBuffer, Object};
type File = Vec<u8>;
// wasm
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsCast;
#[cfg(target_arch = "wasm32")]
use web_sys::{window, console, Element, HtmlInputElement, FileReader};
#[cfg(target_arch = "wasm32")]
use js_sys::{Uint8Array, ArrayBuffer, Object};
#[cfg(target_arch = "wasm32")]
pub struct FileDialog {
tx: std::sync::mpsc::Sender<File>,
rx: std::sync::mpsc::Receiver<File>,
@ -16,10 +20,8 @@ pub struct FileDialog {
closure: Option<Closure<dyn FnMut()>>,
}
// #[cfg(not(target_arch = "wasm32"))]
#[cfg(target_arch = "wasm32")]
impl Default for FileDialog {
#[cfg(target_arch = "wasm32")]
fn default() -> Self {
let (tx, rx) = std::sync::mpsc::channel();
@ -39,12 +41,14 @@ impl Default for FileDialog {
}
}
#[cfg(target_arch = "wasm32")]
impl Drop for FileDialog {
fn drop(&mut self) {
self.input.remove();
}
}
#[cfg(target_arch = "wasm32")]
impl FileDialog {
pub fn open_file(&mut self) {
if let Some(closure) = &self.closure {
@ -74,14 +78,6 @@ impl FileDialog {
self.input.add_event_listener_with_callback("change", closure.as_ref().unchecked_ref()).unwrap();
self.closure = Some(closure);
self.input.click();
// if ui.button("Open file…").clicked() {
// if let Some(path) = rfd::AsyncFileDialog::new().pick_file() {
// let _ = Some(path.display().to_string());
// }
// }
}
pub fn opened(&self) -> Option<Vec<u8>> {
@ -94,63 +90,36 @@ impl FileDialog {
}
// pub fn filedialog(state: FileState) ->
// impl egui::Widget
// {
// move |ui: &mut egui::Ui| {
// native
// }
#[cfg(not(target_arch = "wasm32"))]
use rfd;
#[cfg(not(target_arch = "wasm32"))]
pub struct FileDialog {
file: Option<File>,
}
// impl epi::App for FileApp {
// fn name(&self) -> &str {
// "file dialog app"
// }
// fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) {
// // This is important, otherwise file dialog can hang
// // and messages are not processed
// ctx.request_repaint();
#[cfg(not(target_arch = "wasm32"))]
impl Default for FileDialog {
fn default() -> Self {
Self {
file: None,
}
}
}
// loop {
// match self.message_channel.1.try_recv() {
// Ok(_message) => {
// // Process FileOpen and other messages
// }
// Err(_) => {
// break;
// }
// }
// }
#[cfg(not(target_arch = "wasm32"))]
impl FileDialog {
pub fn open_file(&mut self) {
let path = rfd::FileDialog::new().pick_file();
if let Some(path) = path {
self.file = std::fs::read(path).ok();
}
}
// egui::CentralPanel::default().show(ctx, |ui| {
// let open_button = ui.add(egui::widgets::Button::new("Open..."));
pub fn opened(&mut self) -> Option<File> {
std::mem::replace(&mut self.file, None)
}
// if open_button.clicked() {
// let task = rfd::AsyncFileDialog::new()
// .add_filter("Text files", &["txt"])
// .set_directory("/")
// .pick_file();
// let message_sender = self.message_channel.0.clone();
// execute(async move {
// let file = task.await;
// if let Some(file) = file {
// message_sender.send(file.read().await).ok();
// }
// });
// }
// });
// }
// }
// #[cfg(not(target_arch = "wasm32"))]
// fn execute<F: Future<Output = ()> + Send + 'static>(f: F) {
// std::thread::spawn(move || futures::executor::block_on(f));
// }
// #[cfg(target_arch = "wasm32")]
// fn execute<F: Future<Output = ()> + 'static>(f: F) {
// wasm_bindgen_futures::spawn_local(f);
// }
}