From ce36cd914f4c3f19ef75e0d4b0bb8d9266e0e152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kav=C3=ADk?= Date: Mon, 3 Jun 2024 19:11:22 +0200 Subject: [PATCH] file picker + show_open_file_picker --- .cargo/config.toml | 5 + Cargo.lock | 579 ++++++++++++++++++- MoonZoon.toml | 6 +- frontend/Cargo.toml | 2 + frontend/src/controls_panel.rs | 61 +- frontend/src/platform.rs | 7 +- frontend/src/platform/browser.rs | 41 +- frontend/src/platform/tauri.rs | 43 +- frontend/typescript/bundles/tauri_glue.js | 6 +- frontend/typescript/tauri_glue/tauri_glue.ts | 5 +- src-tauri/Cargo.toml | 7 +- src-tauri/src/lib.rs | 48 +- src-tauri/tauri.conf.json | 2 +- 13 files changed, 702 insertions(+), 110 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..1cb68ba --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +# 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/Cargo.lock b/Cargo.lock index 89dd31c..78eb858 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,18 +315,144 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f47b57fc4521e3cae26a4d45b5227f8fadee4c345be0fefd8d5d1711afb8aeb9" +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + [[package]] name = "askama_escape" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-io" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-once-cell" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9338790e78aa95a416786ec8389546c4b6a1dfc3dc36071ed9518a9413a542eb" +[[package]] +name = "async-process" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + +[[package]] +name = "async-signal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.80" @@ -361,6 +487,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -431,6 +563,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bool_ext" version = "0.5.3" @@ -695,6 +840,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -917,6 +1071,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -940,6 +1105,15 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -950,6 +1124,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1015,7 +1201,7 @@ dependencies = [ "futures-channel", "futures-signals", "futures-util", - "gloo-events", + "gloo-events 0.1.2", "js-sys", "once_cell", "pin-project", @@ -1113,6 +1299,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + [[package]] name = "enum-ordinalize" version = "4.3.0" @@ -1133,6 +1325,27 @@ dependencies = [ "syn 2.0.61", ] +[[package]] +name = "enumflags2" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -1170,6 +1383,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -1185,6 +1419,7 @@ dependencies = [ "shared", "tauri", "tauri-build", + "tauri-plugin-dialog", "tauri-plugin-window-state", "wellen", ] @@ -1264,8 +1499,10 @@ dependencies = [ name = "frontend" version = "0.1.0" dependencies = [ + "gloo-file", "shared", "wasm-bindgen-test", + "web-sys", "wellen", "zoon", ] @@ -1346,6 +1583,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -1689,6 +1939,29 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-events" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f" +dependencies = [ + "futures-channel", + "gloo-events 0.2.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gobject-sys" version = "0.18.0" @@ -2507,9 +2780,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.13.1" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f428b4e9db3d17e2f809dfb1ff9ddfbbf16c71790d1656d10aee320877e1392f" +checksum = "86b959f97c97044e4c96e32e1db292a7d594449546a3c6b77ae613dc3a5b5145" dependencies = [ "cocoa", "crossbeam-channel", @@ -2584,6 +2857,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -2677,6 +2962,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -2710,6 +3006,22 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "overload" version = "0.1.1" @@ -2750,6 +3062,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.4.8" @@ -2973,6 +3291,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -3006,6 +3335,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -3361,6 +3705,30 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfd" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a73a7337fc24366edfca76ec521f51877b114e42dab584008209cca6719251" +dependencies = [ + "ashpd", + "block", + "dispatch", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle 0.6.1", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ring" version = "0.17.8" @@ -4063,9 +4431,9 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tauri" -version = "2.0.0-beta.19" +version = "2.0.0-beta.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8e5bc2e4f5eb7496d1a3e5f4d272f69f1333db5f8efed28d79d7f93334fe95" +checksum = "5a258ecc5ac7ddade525f512c4962fd01cd0f5265e917b4572579c32c027bb31" dependencies = [ "anyhow", "bytes", @@ -4112,9 +4480,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.0.0-beta.15" +version = "2.0.0-beta.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aa28eebafcda490fa7097a6e3a4d07f65967614d35dd88b2aaa19dbb49241cd" +checksum = "82b964bb6d03d97e24e12f896aab463b02a3c2ff76a60f728cc37b5548eb470e" dependencies = [ "anyhow", "cargo_toml", @@ -4134,9 +4502,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.0.0-beta.15" +version = "2.0.0-beta.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727d13a28e9ec895f537d90a09acb0aa3593f703a715fe8a77f87269d3245b52" +checksum = "3529cfa977ed7c097f2a5e8da19ecffbe61982450a6c819e6165b6d0cfd3dd3a" dependencies = [ "base64 0.22.1", "brotli", @@ -4161,9 +4529,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.0-beta.15" +version = "2.0.0-beta.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258667612ad901d256e04ace71ac54d4b3dd8fb1e5baa24403b50991cade4365" +checksum = "36f97dd80334f29314aa5f40b5fad10cb9feffd08e5a5324fd728613841e5d33" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4175,9 +4543,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.0.0-beta.14" +version = "2.0.0-beta.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade77c77db5206b493655d44c395ef67c84fec6bd9d4c19237efdced6b3840f8" +checksum = "7c8385fd0a4f661f5652b0d9e2d7256187d553bb174f88564d10ebcfa6a3af53" dependencies = [ "anyhow", "glob", @@ -4191,10 +4559,47 @@ dependencies = [ ] [[package]] -name = "tauri-plugin-window-state" -version = "2.0.0-beta.7" +name = "tauri-plugin-dialog" +version = "2.0.0-beta.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f723f70fef894434dfb265c9b040ea849acc6e49df0d3c66e0b9f9908214b08a" +checksum = "fed4b22c59f7b04ae2a0bed8241aa715b41973c3f042c84aa67a1f4dc0174a8d" +dependencies = [ + "dunce", + "log", + "raw-window-handle 0.6.1", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.0.0-beta.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa91955751f329e0aa431b87c199b7378b6f91ec0765d2ad9d4c64e017c3cda" +dependencies = [ + "anyhow", + "glob", + "schemars", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror", + "url", + "uuid", +] + +[[package]] +name = "tauri-plugin-window-state" +version = "2.0.0-beta.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b5596f41b90668ea37562d512e1dead687d9ae5eadb49f9faa8b875a2740a3" dependencies = [ "bitflags 2.5.0", "log", @@ -4207,9 +4612,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.0.0-beta.16" +version = "2.0.0-beta.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "574f3d59cbe6c76b6d849bc35aa3a9e8061ff8f75f557dc33f38c0e43cf55a41" +checksum = "d7dc96172a43536236ab55b7da7b8461bf75810985e668589e2395cb476937cb" dependencies = [ "dpi", "gtk", @@ -4226,9 +4631,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.0.0-beta.16" +version = "2.0.0-beta.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d1f223de1d674aaa561c900ac650b3160f11520e9b191a3574f6c493fc77fa" +checksum = "5d4fd913b1f14a9b618c7f3ae35656d3aa759767fcb95b72006357c12b9d0b09" dependencies = [ "cocoa", "gtk", @@ -4250,16 +4655,15 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.0.0-beta.15" +version = "2.0.0-beta.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4251529d92b5c611ccaa611f8a31cb41b1aa00db8bcc0a49efe5d966bfa911" +checksum = "4f24a9c20d676a3f025331cc1c3841256ba88c9f25fb7fae709d2b3089c50d90" dependencies = [ "brotli", "cargo_metadata", "ctor", "dunce", "glob", - "heck 0.5.0", "html5ever", "infer", "json-patch", @@ -4423,6 +4827,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.48.0", ] @@ -4633,14 +5038,14 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.13.5" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39240037d755a1832e752d64f99078c3b0b21c09a71c12405070c75ef4e7cd3c" +checksum = "0b27516dfcfa22a9faaf192283a122bfbede38c1e59ef194e3c4db6549b419c0" dependencies = [ "cocoa", "core-graphics", "crossbeam-channel", - "dirs-next", + "dirs", "libappindicator", "muda", "objc", @@ -4673,6 +5078,17 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -5450,9 +5866,9 @@ dependencies = [ [[package]] name = "wry" -version = "0.39.3" +version = "0.40.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e180ac2740d6cb4d5cec0abf63eacbea90f1b7e5e3803043b13c1c84c4b7884" +checksum = "1fa597526af53f310a8e6218630c5024fdde8271f229e70d7d2fc70b52b8fb1e" dependencies = [ "base64 0.22.1", "block", @@ -5511,6 +5927,75 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-process", + "async-recursion", + "async-trait", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.7.33" @@ -5570,3 +6055,41 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", ] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/MoonZoon.toml b/MoonZoon.toml index 3e0c013..2b62ab0 100644 --- a/MoonZoon.toml +++ b/MoonZoon.toml @@ -1,6 +1,6 @@ -port = 8080 -# port = 8443 -https = false +# port = 8080 +port = 8443 +https = true cache_busting = true backend_log_level = "warn" # "error" / "warn" / "info" / "debug" / "trace" diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 4bb8e99..9d27365 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -14,3 +14,5 @@ wasm-bindgen-test = "0.3.19" shared.workspace = true zoon.workspace = true wellen.workspace = true +web-sys = { version = "*", features = ["FileSystemFileHandle"] } +gloo-file = { version = "0.3.0", features = ["futures"] } diff --git a/frontend/src/controls_panel.rs b/frontend/src/controls_panel.rs index cf6e6c4..cafdb53 100644 --- a/frontend/src/controls_panel.rs +++ b/frontend/src/controls_panel.rs @@ -1,5 +1,6 @@ use crate::{platform, HierarchyAndTimeTable, Layout}; use futures_util::join; +use std::cell::Cell; use std::mem; use std::ops::Not; use std::rc::Rc; @@ -9,6 +10,8 @@ use zoon::*; const SCOPE_VAR_ROW_MAX_WIDTH: u32 = 480; const MILLER_COLUMN_MAX_HEIGHT: u32 = 500; +type Filename = String; + #[derive(Clone)] struct VarForUI { name: Rc, @@ -35,6 +38,7 @@ pub struct ControlsPanel { hierarchy_and_time_table: Mutable>, selected_var_refs: MutableVec, layout: Mutable, + loaded_filename: Mutable>, } impl ControlsPanel { @@ -48,19 +52,28 @@ impl ControlsPanel { hierarchy_and_time_table, selected_var_refs, layout, + loaded_filename: <_>::default(), } .root() } fn triggers(&self) -> Vec { - vec![Task::start_droppable( - self.hierarchy_and_time_table - .signal_ref(Option::is_none) - .for_each_sync(clone!((self => s) move |_| { - s.selected_scope_ref.set(None); - s.selected_var_refs.lock_mut().clear(); - })), - )] + vec![Task::start_droppable(clone!((self => s) async move { + let was_some = Cell::new(false); + s.hierarchy_and_time_table + .signal_ref(Option::is_some) + .dedupe() + .for_each_sync(clone!((s) move |is_some| { + if is_some { + return was_some.set(true); + } + if was_some.get() { + s.selected_scope_ref.set(None); + s.selected_var_refs.lock_mut().clear(); + s.loaded_filename.set(None); + } + })).await + }))] } fn root(&self) -> impl Element { @@ -94,8 +107,7 @@ impl ControlsPanel { Row::new() .s(Gap::both(15)) .s(Align::new().left()) - .item(self.load_button("simple.vcd")) - .item(self.load_button("wave_27.fst")) + .item(self.load_button()) .item(self.layout_switcher()), ) .item_signal( @@ -112,9 +124,10 @@ impl ControlsPanel { )) } - fn load_button(&self, test_file_name: &'static str) -> impl Element { + 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(); Button::new() .s(Padding::new().x(20).y(10)) .s(Background::new().color_signal( @@ -122,16 +135,12 @@ impl ControlsPanel { )) .s(Align::new().left()) .s(RoundedCorners::all(15)) - .label( - El::new().s(Font::new().no_wrap()).child_signal( - hierarchy_and_time_table - .signal_ref(Option::is_some) - .map_bool( - || format!("Unload test file"), - move || format!("Load {test_file_name}"), - ), + .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)) .on_press(move || { let mut hierarchy_and_time_table_lock = hierarchy_and_time_table.lock_mut(); @@ -141,11 +150,15 @@ impl ControlsPanel { } drop(hierarchy_and_time_table_lock); let hierarchy_and_time_table = hierarchy_and_time_table.clone(); + let loaded_filename = loaded_filename.clone(); Task::start(async move { - platform::load_waveform(test_file_name).await; - 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)))) + if let Some(filename) = platform::pick_and_load_waveform().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)))) + } }) }) } diff --git a/frontend/src/platform.rs b/frontend/src/platform.rs index ddee1c1..cac13aa 100644 --- a/frontend/src/platform.rs +++ b/frontend/src/platform.rs @@ -13,12 +13,15 @@ mod browser; #[cfg(FASTWAVE_PLATFORM = "BROWSER")] use browser as platform; +type Filename = String; + pub async fn show_window() { platform::show_window().await } -pub async fn load_waveform(test_file_name: &'static str) { - platform::load_waveform(test_file_name).await +// @TODO allow only support file types +pub async fn pick_and_load_waveform() -> Option { + platform::pick_and_load_waveform().await } pub async fn get_hierarchy() -> wellen::Hierarchy { diff --git a/frontend/src/platform/browser.rs b/frontend/src/platform/browser.rs index a48d078..d24b392 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::*; +use zoon::{println, *}; #[derive(Default)] struct Store { @@ -12,19 +12,40 @@ static STORE: Lazy = lazy::default(); pub(super) async fn show_window() {} -pub(super) async fn load_waveform(test_file_name: &'static str) { - static SIMPLE_VCD: &'static [u8; 311] = include_bytes!("../../../test_files/simple.vcd"); - // static WAVE_27_FST: &'static [u8; 28860652] = include_bytes!("../../../test_files/wave_27.fst"); - let chosen_file = match test_file_name { - "simple.vcd" => SIMPLE_VCD.to_vec(), - // "wave_27.fst" => WAVE_27_FST.to_vec(), - test_file_name => todo!("add {test_file_name} to the `test_files` folder"), +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 waveform = wellen_helpers::read_from_bytes(chosen_file); + 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!("VCD file reading failed") + panic!("Waveform file reading failed") }; *STORE.waveform.lock().unwrap_throw() = Some(waveform); + Some(file.name()) } pub(super) async fn get_hierarchy() -> wellen::Hierarchy { diff --git a/frontend/src/platform/tauri.rs b/frontend/src/platform/tauri.rs index 0e803e8..b72472c 100644 --- a/frontend/src/platform/tauri.rs +++ b/frontend/src/platform/tauri.rs @@ -1,28 +1,37 @@ use zoon::*; pub(super) async fn show_window() { - tauri_glue::show_window().await + tauri_glue::show_window().await.unwrap_throw() } -pub(super) async fn load_waveform(test_file_name: &'static str) { - tauri_glue::load_waveform(test_file_name).await +pub(super) async fn pick_and_load_waveform() -> Option { + tauri_glue::pick_and_load_waveform() + .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() + serde_wasm_bindgen::from_value(tauri_glue::get_hierarchy().await.unwrap_throw()).unwrap_throw() } pub(super) async fn get_time_table() -> wellen::TimeTable { - serde_wasm_bindgen::from_value(tauri_glue::get_time_table().await).unwrap_throw() + serde_wasm_bindgen::from_value(tauri_glue::get_time_table().await.unwrap_throw()).unwrap_throw() } pub(super) async fn load_and_get_signal(signal_ref: wellen::SignalRef) -> wellen::Signal { - serde_wasm_bindgen::from_value(tauri_glue::load_and_get_signal(signal_ref.index()).await) - .unwrap_throw() + serde_wasm_bindgen::from_value( + tauri_glue::load_and_get_signal(signal_ref.index()) + .await + .unwrap_throw(), + ) + .unwrap_throw() } pub(super) async fn unload_signal(signal_ref: wellen::SignalRef) { - tauri_glue::unload_signal(signal_ref.index()).await + tauri_glue::unload_signal(signal_ref.index()) + .await + .unwrap_throw() } mod tauri_glue { @@ -31,16 +40,22 @@ mod tauri_glue { // Note: Add all corresponding methods to `frontend/typescript/tauri_glue/tauri_glue.ts` #[wasm_bindgen(module = "/typescript/bundles/tauri_glue.js")] extern "C" { - pub async fn show_window(); + #[wasm_bindgen(catch)] + pub async fn show_window() -> Result<(), JsValue>; - pub async fn load_waveform(test_file_name: &str); + #[wasm_bindgen(catch)] + pub async fn pick_and_load_waveform() -> Result; - pub async fn get_hierarchy() -> JsValue; + #[wasm_bindgen(catch)] + pub async fn get_hierarchy() -> Result; - pub async fn get_time_table() -> JsValue; + #[wasm_bindgen(catch)] + pub async fn get_time_table() -> Result; - pub async fn load_and_get_signal(signal_ref_index: usize) -> JsValue; + #[wasm_bindgen(catch)] + pub async fn load_and_get_signal(signal_ref_index: usize) -> Result; - pub async fn unload_signal(signal_ref_index: usize); + #[wasm_bindgen(catch)] + pub async fn unload_signal(signal_ref_index: usize) -> Result<(), JsValue>; } } diff --git a/frontend/typescript/bundles/tauri_glue.js b/frontend/typescript/bundles/tauri_glue.js index a8b60a0..3a14939 100644 --- a/frontend/typescript/bundles/tauri_glue.js +++ b/frontend/typescript/bundles/tauri_glue.js @@ -2514,8 +2514,8 @@ var invoke2 = core_exports.invoke; async function show_window() { return await invoke2("show_window"); } -async function load_waveform(test_file_name) { - return await invoke2("load_waveform", { test_file_name }); +async function pick_and_load_waveform() { + return await invoke2("pick_and_load_waveform"); } async function get_hierarchy() { return await invoke2("get_hierarchy"); @@ -2533,7 +2533,7 @@ export { get_hierarchy, get_time_table, load_and_get_signal, - load_waveform, + pick_and_load_waveform, show_window, unload_signal }; diff --git a/frontend/typescript/tauri_glue/tauri_glue.ts b/frontend/typescript/tauri_glue/tauri_glue.ts index 8bb13f8..d075e4f 100644 --- a/frontend/typescript/tauri_glue/tauri_glue.ts +++ b/frontend/typescript/tauri_glue/tauri_glue.ts @@ -4,6 +4,7 @@ import { core } from '@tauri-apps/api' const invoke = core.invoke; +type Filename = string; type WellenHierarchy = unknown; type WellenTimeTable = unknown; type WellenSignal = unknown; @@ -12,8 +13,8 @@ export async function show_window(): Promise { return await invoke("show_window"); } -export async function load_waveform(test_file_name: string): Promise { - return await invoke("load_waveform", { test_file_name }); +export async function pick_and_load_waveform(): Promise { + return await invoke("pick_and_load_waveform"); } export async function get_hierarchy(): Promise { diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6a7573d..6bec432 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,12 +13,13 @@ name = "app_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "=2.0.0-beta.15", features = [] } +tauri-build = { version = "=2.0.0-beta.17", features = [] } [dependencies] shared.workspace = true wellen.workspace = true serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "=2.0.0-beta.19", features = ["macos-private-api", "linux-ipc-protocol"] } -tauri-plugin-window-state = "=2.0.0-beta.7" +tauri = { version = "=2.0.0-beta.22", features = ["macos-private-api", "linux-ipc-protocol"] } +tauri-plugin-window-state = "=2.0.0-beta.9" +tauri-plugin-dialog = "=2.0.0-beta.9" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e21a8e3..daf6974 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,50 +1,56 @@ -use shared::wellen_helpers; -use std::rc::Rc; use std::sync::Mutex; +use tauri_plugin_dialog::DialogExt; use wellen::simple::Waveform; +type Filename = String; + #[derive(Default)] struct Store { waveform: Mutex>, } #[tauri::command(rename_all = "snake_case")] -fn show_window(window: tauri::Window) { +async fn show_window(window: tauri::Window) { window.show().unwrap(); } #[tauri::command(rename_all = "snake_case")] -fn load_waveform(test_file_name: Rc, store: tauri::State) { - static SIMPLE_VCD: &'static [u8; 311] = include_bytes!("../../test_files/simple.vcd"); - static WAVE_27_FST: &'static [u8; 28860652] = include_bytes!("../../test_files/wave_27.fst"); - let chosen_file = match test_file_name.as_str() { - "simple.vcd" => SIMPLE_VCD.to_vec(), - "wave_27.fst" => WAVE_27_FST.to_vec(), - test_file_name => todo!("add {test_file_name} to the `test_files` folder"), +async fn pick_and_load_waveform( + store: tauri::State<'_, Store>, + app: tauri::AppHandle, +) -> Result, ()> { + let Some(file_response) = app.dialog().file().blocking_pick_file() else { + return Ok(None); }; - let waveform = wellen_helpers::read_from_bytes(chosen_file); + let file_path = file_response.path.as_os_str().to_str().unwrap(); + // @TODO `read` should accept `Path` instead of `&str` + let waveform = wellen::simple::read(file_path); let Ok(waveform) = waveform else { - panic!("VCD file reading failed") + panic!("Waveform file reading failed") }; *store.waveform.lock().unwrap() = Some(waveform); + Ok(Some(file_response.name.unwrap())) } #[tauri::command(rename_all = "snake_case")] -fn get_hierarchy(store: tauri::State) -> serde_json::Value { +async fn get_hierarchy(store: tauri::State<'_, Store>) -> Result { let waveform = store.waveform.lock().unwrap(); let hierarchy = waveform.as_ref().unwrap().hierarchy(); - serde_json::to_value(hierarchy).unwrap() + Ok(serde_json::to_value(hierarchy).unwrap()) } #[tauri::command(rename_all = "snake_case")] -fn get_time_table(store: tauri::State) -> serde_json::Value { +async fn get_time_table(store: tauri::State<'_, Store>) -> Result { let waveform = store.waveform.lock().unwrap(); let time_table = waveform.as_ref().unwrap().time_table(); - serde_json::to_value(time_table).unwrap() + Ok(serde_json::to_value(time_table).unwrap()) } #[tauri::command(rename_all = "snake_case")] -fn load_and_get_signal(signal_ref_index: usize, store: tauri::State) -> serde_json::Value { +async fn load_and_get_signal( + signal_ref_index: usize, + store: tauri::State<'_, Store>, +) -> Result { let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap(); let mut waveform_lock = store.waveform.lock().unwrap(); let waveform = waveform_lock.as_mut().unwrap(); @@ -52,15 +58,16 @@ fn load_and_get_signal(signal_ref_index: usize, store: tauri::State) -> s // make the command async or return the result through a Tauri channel waveform.load_signals_multi_threaded(&[signal_ref]); let signal = waveform.get_signal(signal_ref).unwrap(); - serde_json::to_value(signal).unwrap() + Ok(serde_json::to_value(signal).unwrap()) } #[tauri::command(rename_all = "snake_case")] -fn unload_signal(signal_ref_index: usize, store: tauri::State) { +async fn unload_signal(signal_ref_index: usize, store: tauri::State<'_, Store>) -> Result<(), ()> { let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap(); let mut waveform_lock = store.waveform.lock().unwrap(); let waveform = waveform_lock.as_mut().unwrap(); waveform.unload_signals(&[signal_ref]); + Ok(()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -72,10 +79,11 @@ pub fn run() { tauri::Builder::default() .manage(Store::default()) .plugin(tauri_plugin_window_state::Builder::default().build()) + .plugin(tauri_plugin_dialog::init()) // Npte: Add all handlers to `frontend/src/tauri_bridge.rs` .invoke_handler(tauri::generate_handler![ show_window, - load_waveform, + pick_and_load_waveform, get_hierarchy, get_time_table, load_and_get_signal, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index bd2346e..2c18dfc 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": "http://localhost:8080", + "devUrl": "https://localhost:8443", "beforeDevCommand": "makers mzoon_for_tauri start", "beforeBuildCommand": "makers mzoon_for_tauri build -r -f" },