From f5c35e48c480355036778d26aacde498e5c15e68 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sun, 22 Sep 2024 23:53:02 +0200 Subject: feat: restructure project, start drag and drop --- .gitignore | 2 +- Cargo.lock | 328 ++++++++++--------- Cargo.toml | 22 +- fonts/icons.ttf | Bin 6352 -> 0 bytes iced_builder/Cargo.toml | 21 ++ iced_builder/fonts/icons.ttf | Bin 0 -> 6352 bytes iced_builder/src/codegen/mod.rs | 158 +++++++++ iced_builder/src/lib.rs | 0 iced_builder/src/main.rs | 326 +++++++++++++++++++ iced_builder/src/types/mod.rs | 54 ++++ iced_builder/src/types/rendered_element.rs | 47 +++ iced_drop/.gitignore | 3 + iced_drop/Cargo.toml | 8 + iced_drop/README.md | 73 +++++ iced_drop/src/lib.rs | 51 +++ iced_drop/src/widget.rs | 2 + iced_drop/src/widget/droppable.rs | 497 +++++++++++++++++++++++++++++ iced_drop/src/widget/operation.rs | 1 + iced_drop/src/widget/operation/drop.rs | 89 ++++++ src/codegen/mod.rs | 160 ---------- src/lib.rs | 0 src/main.rs | 301 ----------------- src/types/mod.rs | 25 -- src/types/rendered_element.rs | 47 --- 24 files changed, 1511 insertions(+), 704 deletions(-) delete mode 100644 fonts/icons.ttf create mode 100644 iced_builder/Cargo.toml create mode 100644 iced_builder/fonts/icons.ttf create mode 100644 iced_builder/src/codegen/mod.rs create mode 100644 iced_builder/src/lib.rs create mode 100644 iced_builder/src/main.rs create mode 100644 iced_builder/src/types/mod.rs create mode 100644 iced_builder/src/types/rendered_element.rs create mode 100644 iced_drop/.gitignore create mode 100644 iced_drop/Cargo.toml create mode 100644 iced_drop/README.md create mode 100644 iced_drop/src/lib.rs create mode 100644 iced_drop/src/widget.rs create mode 100644 iced_drop/src/widget/droppable.rs create mode 100644 iced_drop/src/widget/operation.rs create mode 100644 iced_drop/src/widget/operation/drop.rs delete mode 100644 src/codegen/mod.rs delete mode 100644 src/lib.rs delete mode 100644 src/main.rs delete mode 100644 src/types/mod.rs delete mode 100644 src/types/rendered_element.rs diff --git a/.gitignore b/.gitignore index 2f7896d..f4ceea7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -target/ +**/target/ diff --git a/Cargo.lock b/Cargo.lock index b519b92..d12997d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -144,17 +144,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -275,9 +275,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" dependencies = [ "bytemuck_derive", ] @@ -290,7 +290,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -301,9 +301,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "calloop" @@ -359,9 +359,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.14" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", @@ -403,13 +403,13 @@ dependencies = [ [[package]] name = "clipboard_macos" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145a7f9e9b89453bc0a5e32d166456405d389cea5b578f57f1274b1397588a95" +checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" dependencies = [ - "objc", - "objc-foundation", - "objc_id", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", ] [[package]] @@ -507,6 +507,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -520,8 +530,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", "foreign-types", "libc", ] @@ -533,7 +556,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", "libc", ] @@ -720,9 +754,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" [[package]] name = "etagere" @@ -736,9 +770,9 @@ dependencies = [ [[package]] name = "euclid" -version = "0.22.10" +version = "0.22.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f0eb73b934648cd7a4a61f1b15391cd95dab0b4da6e2e66c2a072c144b4a20" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" dependencies = [ "num-traits", ] @@ -767,9 +801,9 @@ checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fdeflate" @@ -782,9 +816,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -867,7 +901,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -933,7 +967,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1009,9 +1043,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "gl_generator" @@ -1194,17 +1228,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "iced-builder" -version = "0.1.0" -dependencies = [ - "iced", - "iced_aw", - "rust-format", - "tokio", - "unique_id", -] - [[package]] name = "iced_aw" version = "0.9.3" @@ -1215,6 +1238,18 @@ dependencies = [ "iced", ] +[[package]] +name = "iced_builder" +version = "0.1.0" +dependencies = [ + "iced", + "iced_aw", + "iced_drop", + "rust-format", + "tokio", + "unique_id", +] + [[package]] name = "iced_core" version = "0.12.3" @@ -1233,6 +1268,13 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "iced_drop" +version = "0.1.0" +dependencies = [ + "iced", +] + [[package]] name = "iced_futures" version = "0.12.0" @@ -1426,9 +1468,9 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -1719,9 +1761,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] @@ -1734,7 +1776,7 @@ checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ "bitflags 2.6.0", "block", - "core-graphics-types", + "core-graphics-types 0.1.3", "foreign-types", "log", "objc", @@ -1860,7 +1902,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -1873,17 +1915,6 @@ 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-sys" version = "0.3.5" @@ -1970,7 +2001,6 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", "block2 0.5.1", - "dispatch", "libc", "objc2 0.5.2", ] @@ -2009,20 +2039,11 @@ dependencies = [ "cc", ] -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - [[package]] name = "object" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -2094,7 +2115,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -2140,7 +2161,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall 0.5.4", "smallvec", "windows-targets 0.52.6", ] @@ -2187,7 +2208,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -2278,9 +2299,9 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] @@ -2326,9 +2347,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.34.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", ] @@ -2440,9 +2461,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] @@ -2478,9 +2499,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" dependencies = [ "bytemuck", ] @@ -2520,9 +2541,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -2599,7 +2620,7 @@ checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.4", + "memmap2 0.9.5", "smithay-client-toolkit 0.18.1", "tiny-skia", ] @@ -2612,29 +2633,29 @@ checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -2715,7 +2736,7 @@ dependencies = [ "cursor-icon", "libc", "log", - "memmap2 0.9.4", + "memmap2 0.9.5", "rustix", "thiserror", "wayland-backend", @@ -2740,15 +2761,15 @@ dependencies = [ "cursor-icon", "libc", "log", - "memmap2 0.9.4", + "memmap2 0.9.5", "rustix", "thiserror", "wayland-backend", "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.3", - "wayland-protocols-wlr 0.3.3", + "wayland-protocols 0.32.4", + "wayland-protocols-wlr 0.3.4", "wayland-scanner", "xkeysym", ] @@ -2775,26 +2796,25 @@ dependencies = [ [[package]] name = "softbuffer" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d623bff5d06f60d738990980d782c8c866997d9194cfe79ecad00aa2f76826dd" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ "as-raw-xcb-connection", "bytemuck", "cfg_aliases 0.2.1", - "core-graphics", + "core-graphics 0.24.0", "drm", "fastrand", "foreign-types", "js-sys", "log", - "memmap2 0.9.4", + "memmap2 0.9.5", "objc2 0.5.2", - "objc2-app-kit", "objc2-foundation", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.3", + "redox_syscall 0.5.4", "rustix", "tiny-xlib", "wasm-bindgen", @@ -2802,7 +2822,7 @@ dependencies = [ "wayland-client", "wayland-sys", "web-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "x11rb", ] @@ -2879,9 +2899,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -2930,22 +2950,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -3062,9 +3082,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "toml_datetime", @@ -3090,7 +3110,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] @@ -3140,9 +3160,9 @@ checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-linebreak" @@ -3158,15 +3178,15 @@ checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] name = "unicode-script" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-vo" @@ -3176,15 +3196,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unique_id" @@ -3311,7 +3331,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -3345,7 +3365,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3373,9 +3393,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" dependencies = [ "cc", "downcast-rs", @@ -3387,9 +3407,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943" +checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" dependencies = [ "bitflags 2.6.0", "rustix", @@ -3410,9 +3430,9 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef9489a8df197ebf3a8ce8a7a7f0a2320035c3743f3c1bd0bdbccf07ce64f95" +checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" dependencies = [ "rustix", "wayland-client", @@ -3433,9 +3453,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.3" +version = "0.32.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62989625a776e827cc0f15d41444a3cea5205b963c3a25be48ae1b52d6b4daaa" +checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -3471,33 +3491,33 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953" +checksum = "dad87b5fd1b1d3ca2f792df8f686a2a11e3fe1077b71096f7a175ab699f89109" dependencies = [ "bitflags 2.6.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.3", + "wayland-protocols 0.32.4", "wayland-scanner", ] [[package]] name = "wayland-scanner" -version = "0.31.4" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" dependencies = [ "proc-macro2", - "quick-xml 0.34.0", + "quick-xml 0.36.2", "quote", ] [[package]] name = "wayland-sys" -version = "0.31.4" +version = "0.31.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" dependencies = [ "dlib", "log", @@ -3595,7 +3615,7 @@ dependencies = [ "bitflags 2.6.0", "block", "cfg_aliases 0.1.1", - "core-graphics-types", + "core-graphics-types 0.1.3", "d3d12", "glow", "glutin_wgl_sys", @@ -3935,14 +3955,14 @@ dependencies = [ "bytemuck", "calloop 0.12.4", "cfg_aliases 0.1.1", - "core-foundation", - "core-graphics", + "core-foundation 0.9.4", + "core-graphics 0.23.2", "cursor-icon", "icrate", "js-sys", "libc", "log", - "memmap2 0.9.4", + "memmap2 0.9.5", "ndk", "ndk-sys", "objc2 0.4.1", @@ -3972,9 +3992,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -4038,9 +4058,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" [[package]] name = "xmlparser" @@ -4098,7 +4118,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.77", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 301ecfe..64f9037 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,6 @@ -[package] -name = "iced-builder" -description = "GUI builder for iced, built with iced." -version = "0.1.0" -edition = "2021" -authors = ["pml68 "] -repository = "https://github.com/pml68/iced-builder" -license = "GPL-3.0-or-later" -keywords = ["gui", "iced"] - -[dependencies] -iced = { version = "0.12.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } -iced_aw = { version = "0.9.3", default-features = false, features = ["menu","color_picker"] } -tokio = { version = "1.40.0", features = ["fs"] } -rust-format = "0.3.4" -unique_id = "0.1.5" +[workspace] +resolver = "2" +members = [ + "iced_drop", + "iced_builder" +] diff --git a/fonts/icons.ttf b/fonts/icons.ttf deleted file mode 100644 index 393c692..0000000 Binary files a/fonts/icons.ttf and /dev/null differ diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml new file mode 100644 index 0000000..febff28 --- /dev/null +++ b/iced_builder/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "iced_builder" +description = "GUI builder for iced, built with iced." +version = "0.1.0" +edition = "2021" +authors = ["pml68 "] +repository = "https://github.com/pml68/iced-builder" +license = "GPL-3.0-or-later" +keywords = ["gui", "iced"] + +[dependencies] +iced = { version = "0.12.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } +iced_aw = { version = "0.9.3", default-features = false, features = ["menu","color_picker"] } +iced_drop = { path = "../iced_drop" } +tokio = { version = "1.40.0", features = ["fs"] } +rust-format = "0.3.4" +unique_id = "0.1.5" + +[[bin]] +name = "iced-builder" +path = "src/main.rs" diff --git a/iced_builder/fonts/icons.ttf b/iced_builder/fonts/icons.ttf new file mode 100644 index 0000000..393c692 Binary files /dev/null and b/iced_builder/fonts/icons.ttf differ diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs new file mode 100644 index 0000000..88c0c61 --- /dev/null +++ b/iced_builder/src/codegen/mod.rs @@ -0,0 +1,158 @@ +use rust_format::{Config, Edition, Formatter, RustFmt}; + +use crate::types::{rendered_element::RenderedElement, ElementName}; + +impl RenderedElement { + fn props_codegen(&self) -> String { + let mut props_string = String::new(); + + for (k, v) in self.props.clone() { + props_string = format!("{props_string}.{k}({v})"); + } + + props_string + } + + fn codegen(&self) -> (String, String) { + let mut imports = String::new(); + let mut view = String::new(); + let props = self.props_codegen(); + + let mut elements = String::new(); + + match self.name { + ElementName::Column | ElementName::Row | ElementName::Container => { + for element in &self.child_elements { + let (c_imports, children) = element.codegen(); + imports = format!("{imports}{c_imports}"); + elements = format!("{elements}{},", children); + } + } + _ => {} + } + + match &self.name { + ElementName::Container => { + imports = format!("{imports}container,"); + view = if self.child_elements.len() < 2 { + format!("{view}\ncontainer({elements}){props}") + } else { + format!("{view}\ncontainer(){props}") + }; + } + ElementName::Row => { + imports = format!("{imports}row,"); + view = format!("{view}\nrow![{elements}]{props}"); + } + ElementName::Column => { + imports = format!("{imports}column,"); + view = format!("{view}\ncolumn![{elements}]{props}"); + } + ElementName::Text(string) => { + imports = format!("{imports}text,"); + view = format!( + "{view}\ntext(\"{}\"){props}", + if *string == String::new() { + "New Text" + } else { + string + } + ); + } + ElementName::Button(string) => { + imports = format!("{imports}button,"); + view = format!( + "{view}\nbutton(\"{}\"){props}", + if *string == String::new() { + "New Button" + } else { + string + } + ); + } + ElementName::Image(path) => { + imports = format!("{imports}image,"); + view = format!("{view}\nimage(\"{path}\"){props}"); + } + ElementName::SVG(path) => { + imports = format!("{imports}svg,"); + view = format!("{view}\nsvg(\"{path}\"){props}"); + } + } + + (imports, view) + } + + pub fn app_code( + &self, + title: &str, + theme: Option, + ) -> Result> { + let (imports, view) = self.codegen(); + let mut app_code = format!("use iced::{{widget::{{{imports}}},Sandbox,Settings,Element}};"); + + app_code = format!( + r#"{app_code} + + fn main() -> iced::Result {{ + App::run(Settings::default()) + }} + + struct App; + + impl Sandbox for App {{ + type Message = (); + + fn new() -> Self {{ + Self {{}} + }} + + fn title(&self) -> String {{ + "{title}".into() + }} + + fn theme(&self) -> iced::Theme {{ + iced::Theme::{} + }} + + fn update(&mut self, message: Self::Message) {{ + + }} + + fn view(&self) -> Element {{ + {view}.into() + }} + }}"#, + if let Some(c) = theme { + c.to_string().replace(' ', "") + } else { + "default()".to_owned() + } + ); + let config = Config::new_str() + .edition(Edition::Rust2021) + .option("trailing_comma", "Never") + .option("imports_granularity", "Crate"); + let rustfmt = RustFmt::from_config(config); + Ok(rustfmt.format_str(app_code)?) + } + + pub fn test() -> String { + let mut text1 = RenderedElement::new(ElementName::Text("wow")); + text1.set_property("height", "120.5"); + text1.set_property("width", "230"); + + let element = RenderedElement::new(ElementName::Container).push(RenderedElement::from_vec( + ElementName::Row, + vec![ + text1, + RenderedElement::new(ElementName::Text("heh")), + RenderedElement::new(ElementName::SVG( + "/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg", + )), + ], + )); + + element.app_code("new app", None).unwrap() + } +} diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs new file mode 100644 index 0000000..e966614 --- /dev/null +++ b/iced_builder/src/main.rs @@ -0,0 +1,326 @@ +mod codegen; +mod types; + +use iced::{ + clipboard, executor, + highlighter::{self, Highlighter}, + theme, + widget::{ + button, column, container, + pane_grid::{self, Pane, PaneGrid}, + row, text, text_editor, tooltip, Column, Space, + }, + Alignment, Application, Color, Command, Element, Font, Length, Settings, +}; +use iced_drop::droppable; +use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; + +fn main() -> iced::Result { + App::run(Settings { + fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], + ..Settings::default() + }) +} + +struct App { + is_saved: bool, + current_project: Option, + dark_theme: bool, + pane_state: pane_grid::State, + focus: Option, + designer_state: DesignerState, + element_list: Vec, + editor_content: text_editor::Content, +} + +#[derive(Debug, Clone)] +enum Message { + ToggleTheme, + CopyCode, + Drop(types::ElementName, iced::Point, iced::Rectangle), + HandleZones( + types::ElementName, + Vec<(iced::advanced::widget::Id, iced::Rectangle)>, + ), + Resized(pane_grid::ResizeEvent), + Clicked(pane_grid::Pane), + PaneDragged(pane_grid::DragEvent), +} + +#[derive(Clone, Debug)] +enum Panes { + Designer, + ElementList, +} + +impl Application for App { + type Message = Message; + type Theme = theme::Theme; + type Executor = executor::Default; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command) { + let state = pane_grid::State::with_configuration(pane_grid::Configuration::Split { + axis: pane_grid::Axis::Vertical, + ratio: 0.8, + a: Box::new(pane_grid::Configuration::Pane(Panes::Designer)), + b: Box::new(pane_grid::Configuration::Pane(Panes::ElementList)), + }); + ( + Self { + is_saved: true, + current_project: None, + dark_theme: true, + pane_state: state, + focus: None, + designer_state: DesignerState { + designer_content: vec![], + designer_page: DesignerPage::Designer, + }, + element_list: types::ElementName::ALL.to_vec(), + editor_content: text_editor::Content::with_text(&RenderedElement::test()), + }, + Command::none(), + ) + } + + fn title(&self) -> String { + let saved_state = if self.is_saved { "" } else { " *" }; + + let project_name = match &self.current_project { + Some(n) => format!(" - {n}"), + None => "".to_owned(), + }; + + format!("iced Builder{project_name}{saved_state}") + } + + fn theme(&self) -> iced::Theme { + if self.dark_theme { + theme::Theme::CatppuccinMocha + } else { + theme::Theme::CatppuccinLatte + } + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::ToggleTheme => self.dark_theme = !self.dark_theme, + Message::CopyCode => return clipboard::write(self.editor_content.text()), + Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { + self.pane_state.resize(split, ratio); + } + Message::Clicked(pane) => { + self.focus = Some(pane); + } + Message::Drop(name, point, _) => { + return iced_drop::zones_on_point( + move |zones| Message::HandleZones(name, zones), + point, + None, + None, + ) + .into() + } + Message::HandleZones(name, zones) => { + println!("{:?}\n{name}", zones); + } + Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { + self.pane_state.drop(pane, target); + } + Message::PaneDragged(_) => {} + } + + Command::none() + } + + fn view(&self) -> Element { + let header = row![button("Toggle Theme") + .on_press(Message::ToggleTheme) + .padding(5)] + .width(200); + let pane_grid = PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { + let is_focused = Some(id) == self.focus; + match pane { + Panes::Designer => match self.designer_state.designer_page { + DesignerPage::Designer => { + let content = container("") + .id(iced::widget::container::Id::new("drop_zone")) + .height(Length::Fill) + .width(Length::Fill); + let title = text("Designer").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new(content) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + DesignerPage::CodeView => { + let title = row![ + text("Generated Code").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }), + Space::with_width(Length::Fill), + tooltip( + button( + container( + text('\u{0e801}').font(Font::with_name("editor-icons")) + ) + .width(30) + .center_x() + ) + .on_press(Message::CopyCode), + "Copy code to clipboard", + tooltip::Position::Left + ) + ]; + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new( + text_editor(&self.editor_content) + .highlight::( + highlighter::Settings { + theme: highlighter::Theme::Base16Mocha, + extension: "rs".to_string(), + }, + |highlight, _theme| highlight.to_format(), + ) + .height(Length::Fill) + .padding(20), + ) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + }, + Panes::ElementList => { + let items_list = items_list_view(self.element_list.clone()); + let content = column![items_list] + .align_items(Alignment::Center) + .height(Length::Fill) + .width(Length::Fill); + let title = text("Element List").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }); + let title_bar = pane_grid::TitleBar::new(title) + .padding(10) + .style(style::title_bar); + pane_grid::Content::new(content) + .title_bar(title_bar) + .style(if is_focused { + style::pane_focused + } else { + style::pane_active + }) + } + } + }) + .width(Length::Fill) + .height(Length::Fill) + .spacing(10) + .on_resize(10, Message::Resized) + .on_click(Message::Clicked) + .on_drag(Message::PaneDragged); + + let content = Column::new() + .push(header) + .push(pane_grid) + .spacing(5) + .align_items(Alignment::Center) + .width(Length::Fill); + + container(content).height(Length::Fill).into() + } +} + +const fn from_grayscale(grayscale: f32) -> Color { + Color { + r: grayscale, + g: grayscale, + b: grayscale, + a: 1.0, + } +} + +// #ffffff +const PANE_ID_COLOR_FOCUSED: Color = from_grayscale(1.0); + +// #e8e8e8 +const PANE_ID_COLOR_UNFOCUSED: Color = from_grayscale(0xE8 as f32 / 255.0); + +fn items_list_view(items: Vec) -> Element<'static, Message> { + let mut column = Column::new() + .spacing(20) + .align_items(Alignment::Center) + .width(Length::Fill); + + for item in items { + let value = item.clone(); + column = column.push( + droppable(text(value.to_string())) + .on_drop(move |point, rect| Message::Drop(value, point, rect)), + ); + } + + container(column).height(250.0).width(300).into() +} + +mod style { + use iced::widget::container; + use iced::{Border, Theme}; + + pub fn title_bar(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + text_color: Some(palette.background.strong.text), + background: Some(palette.background.strong.color.into()), + ..Default::default() + } + } + + pub fn pane_active(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + background: Some(palette.background.weak.color.into()), + border: Border { + width: 1.0, + color: palette.background.strong.color, + ..Border::default() + }, + ..Default::default() + } + } + + pub fn pane_focused(theme: &Theme) -> container::Appearance { + let palette = theme.extended_palette(); + + container::Appearance { + background: Some(palette.background.weak.color.into()), + border: Border { + width: 4.0, + color: palette.background.strong.color, + ..Border::default() + }, + ..Default::default() + } + } +} diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs new file mode 100644 index 0000000..db06ffa --- /dev/null +++ b/iced_builder/src/types/mod.rs @@ -0,0 +1,54 @@ +pub mod rendered_element; + +use rendered_element::RenderedElement; + +pub struct DesignerState { + pub designer_content: Vec, + pub designer_page: DesignerPage, +} + +#[derive(Debug, Clone, Copy)] +pub enum ElementName { + Text(&'static str), + Button(&'static str), + SVG(&'static str), + Image(&'static str), + Container, + Row, + Column, +} + +impl ElementName { + pub const ALL: [Self; 7] = [ + Self::Text(""), + Self::Button(""), + Self::SVG(""), + Self::Image(""), + Self::Container, + Self::Row, + Self::Column, + ]; +} + +impl std::fmt::Display for ElementName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Text(_) => "Text", + Self::Button(_) => "Button", + Self::SVG(_) => "SVG", + Self::Image(_) => "Image", + Self::Container => "Container", + Self::Row => "Row", + Self::Column => "Column", + } + ) + } +} + +pub enum DesignerPage { + Designer, + CodeView, +} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs new file mode 100644 index 0000000..f05594d --- /dev/null +++ b/iced_builder/src/types/rendered_element.rs @@ -0,0 +1,47 @@ +use std::collections::HashMap; + +use unique_id::{string::StringGenerator, Generator}; + +use iced::advanced::widget::Id; + +use super::ElementName; + +#[derive(Debug)] +pub struct RenderedElement { + pub id: Id, + pub child_elements: Vec, + pub name: ElementName, + pub props: HashMap<&'static str, &'static str>, +} + +impl RenderedElement { + pub fn new(name: ElementName) -> Self { + let gen = StringGenerator::default(); + Self { + id: Id::new(gen.next_id()), + child_elements: vec![], + name, + props: HashMap::new(), + } + } + + pub fn from_vec(name: ElementName, child_elements: Vec) -> Self { + let gen = StringGenerator::default(); + Self { + id: Id::new(gen.next_id()), + child_elements, + name, + props: HashMap::new(), + } + } + + pub fn push(mut self, element: RenderedElement) -> Self { + self.child_elements.push(element); + self + } + + pub fn set_property(&mut self, prop: &'static str, value: &'static str) { + let prop_ref = self.props.entry(prop).or_insert(value); + *prop_ref = value; + } +} diff --git a/iced_drop/.gitignore b/iced_drop/.gitignore new file mode 100644 index 0000000..ff0d847 --- /dev/null +++ b/iced_drop/.gitignore @@ -0,0 +1,3 @@ +/target +/.vscode +Cargo.lock diff --git a/iced_drop/Cargo.toml b/iced_drop/Cargo.toml new file mode 100644 index 0000000..8b9d88c --- /dev/null +++ b/iced_drop/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "iced_drop" +version = "0.1.0" +edition = "2021" + +[dependencies.iced] +version = "0.12.1" +features = ["advanced"] diff --git a/iced_drop/README.md b/iced_drop/README.md new file mode 100644 index 0000000..41b637b --- /dev/null +++ b/iced_drop/README.md @@ -0,0 +1,73 @@ +# iced_drop + +A small library which provides a custom widget and operation to make drag and drop easier to implement in [iced](https://github.com/iced-rs/iced/tree/master) + +## Usage + +To add drag and drog functionality, first define two messages with the following format + +```rust +enum Message { + Drop(iced::Point, iced::Rectangle) + HandleZones(Vec<(iced::advanced::widget::Id, iced::Rectangle)>) +} +``` + +The `Drop` message will be sent when the droppable is being dragged, and the left mouse button is released. This message provides the mouse position and layout boundaries of the droppable at the release point. + +The `HandleZones` message will be sent after an operation that finds the drop zones under the mouse position. It provides the Id and bounds for each drop zone. + +Next, create create a droppable in the view method and assign the on_drop message. The dropopable function takes an `impl Into` object, so it's easy to make a droppable from any iced widget. + +```rust +iced_drop::droppable("Drop me!").on_drop(Message::Drop); +``` + +Next, create a "drop zone." A drop zone is any widget that operates like a container andhas some assigned Id. It's important that the widget is assigned some Id or it won't be recognized as a drop zone. + +```rust +iced::widget::container("Drop zone") + .id(iced::widget::container::Id::new("drop_zone")); +``` + +Finally, handle the updates of the drop messages + +```rust +match message { + Message::Drop(cursor_pos, _) => { + return iced_drop::zones_on_point( + Message::HandleZonesFound, + point, + None, + None, + ); + } + Message::HandleZones(zones) => { + println!("{:?}", zones) + } +} +``` + +On Drop, we return a widget operation that looks for drop zones under the cursor_pos. When this operation finishes, it returns the zones found and sends the `HandleZones` message. In this example, we only defined one zone, so the zones vector will either be empty if the droppable was not dropped on the zone, or it will contain the `drop_zone` + +## Examples + +There are two examples: color, todo. + +The color example is a very basic drag/drop showcase where the user can drag colors into zones and change the zone's color. I would start here. + +[Link to video](https://drive.google.com/file/d/1K1CCi2Lc90IUyDufsvoUBZmUCbeg6_Fi/view?usp=sharing) + +To run this examples: `cargo run -p color` + +The todo example is a basic todo board application similar to Trello. This is a much much more complex example as it handles custom highlighting and nested droppables, but it just shows you can make some pretty cool things with iced. + +[Link to video](https://drive.google.com/file/d/1MLOCk4Imd_oUnrTj_psbpYbwua976HmR/view?usp=sharing) + +To run this example try: `cargo run -p todo` + +Note: the todo example might also be a good example on how one can use operations. Check examples/todo/src/operation.rs. I didn't find any other examples of this in the iced repo except for the built in focus operations. + +## Future Development + +Right now it's a little annoying having to work with iced's Id type. At some point, I will work on a drop_zone widget that can take some generic clonable type as an id, and I will create a seperate find_zones operation that will return a list of this custom Id. This should make it easier to determine which drop zones were found. diff --git a/iced_drop/src/lib.rs b/iced_drop/src/lib.rs new file mode 100644 index 0000000..fc559dc --- /dev/null +++ b/iced_drop/src/lib.rs @@ -0,0 +1,51 @@ +pub mod widget; + +use iced::{ + advanced::{graphics::futures::MaybeSend, renderer, widget::Id}, + Command, Element, Point, Rectangle, +}; + +use widget::droppable::*; +use widget::operation::drop; + +pub fn droppable<'a, Message, Theme, Renderer>( + content: impl Into>, +) -> Droppable<'a, Message, Theme, Renderer> +where + Message: Clone, + Renderer: renderer::Renderer, +{ + Droppable::new(content) +} + +pub fn zones_on_point( + msg: MF, + point: Point, + options: Option>, + depth: Option, +) -> Command +where + Message: 'static, + MF: Fn(Vec<(Id, Rectangle)>) -> Message + MaybeSend + Sync + Clone + 'static, +{ + Command::widget(drop::find_zones( + move |bounds| bounds.contains(point), + options, + depth, + )) + .map(move |id| msg(id)) +} + +pub fn find_zones( + msg: MF, + filter: F, + options: Option>, + depth: Option, +) -> Command +where + Message: 'static, + MF: Fn(Vec<(Id, Rectangle)>) -> Message + MaybeSend + Sync + Clone + 'static, + F: Fn(&Rectangle) -> bool + 'static, +{ + Command::widget(drop::find_zones(filter, options, depth)).map(move |id| msg(id)) +} diff --git a/iced_drop/src/widget.rs b/iced_drop/src/widget.rs new file mode 100644 index 0000000..6b3fed2 --- /dev/null +++ b/iced_drop/src/widget.rs @@ -0,0 +1,2 @@ +pub mod droppable; +pub mod operation; diff --git a/iced_drop/src/widget/droppable.rs b/iced_drop/src/widget/droppable.rs new file mode 100644 index 0000000..ed7dcbd --- /dev/null +++ b/iced_drop/src/widget/droppable.rs @@ -0,0 +1,497 @@ +//! Encapsulates a widget that can be dragged and dropped. +use std::fmt::Debug; +use std::vec; + +use iced::advanced::widget::{Operation, Tree, Widget}; +use iced::advanced::{self, layout, mouse, overlay, renderer, Layout}; +use iced::event::Status; +use iced::{Element, Point, Rectangle, Size, Vector}; + +/// An element that can be dragged and dropped on a [`DropZone`] +pub struct Droppable<'a, Message, Theme = iced::Theme, Renderer = iced::Renderer> +where + Message: Clone, + Renderer: renderer::Renderer, +{ + content: Element<'a, Message, Theme, Renderer>, + id: Option, + on_click: Option, + on_drop: Option Message + 'a>>, + on_drag: Option Message + 'a>>, + on_cancel: Option, + drag_mode: Option<(bool, bool)>, + drag_overlay: bool, + drag_hide: bool, + drag_center: bool, + drag_size: Option, + reset_delay: usize, +} + +impl<'a, Message, Theme, Renderer> Droppable<'a, Message, Theme, Renderer> +where + Message: Clone, + Renderer: renderer::Renderer, +{ + /// Creates a new [`Droppable`]. + pub fn new(content: impl Into>) -> Self { + Self { + content: content.into(), + id: None, + on_click: None, + on_drop: None, + on_drag: None, + on_cancel: None, + drag_mode: Some((true, true)), + drag_overlay: true, + drag_hide: false, + drag_center: false, + drag_size: None, + reset_delay: 0, + } + } + + /// Sets the unique identifier of the [`Droppable`]. + pub fn id(mut self, id: iced::advanced::widget::Id) -> Self { + self.id = Some(id); + self + } + + /// Sets the message that will be produced when the [`Droppable`] is clicked. + pub fn on_click(mut self, message: Message) -> Self { + self.on_click = Some(message); + self + } + + /// Sets the message that will be produced when the [`Droppable`] is dropped on a [`DropZone`]. + /// + /// Unless this is set, the [`Droppable`] will be disabled. + pub fn on_drop(mut self, message: F) -> Self + where + F: Fn(Point, Rectangle) -> Message + 'a, + { + self.on_drop = Some(Box::new(message)); + self + } + + /// Sets the message that will be produced when the [`Droppable`] is dragged. + pub fn on_drag(mut self, message: F) -> Self + where + F: Fn(Point, Rectangle) -> Message + 'a, + { + self.on_drag = Some(Box::new(message)); + self + } + + /// Sets the message that will be produced when the user right clicks while dragging the [`Droppable`]. + pub fn on_cancel(mut self, message: Message) -> Self { + self.on_cancel = Some(message); + self + } + + /// Sets whether the [`Droppable`] should be drawn under the cursor while dragging. + pub fn drag_overlay(mut self, drag_overlay: bool) -> Self { + self.drag_overlay = drag_overlay; + self + } + + /// Sets whether the [`Droppable`] should be hidden while dragging. + pub fn drag_hide(mut self, drag_hide: bool) -> Self { + self.drag_hide = drag_hide; + self + } + + /// Sets whether the [`Droppable`] should be centered on the cursor while dragging. + pub fn drag_center(mut self, drag_center: bool) -> Self { + self.drag_center = drag_center; + self + } + + // Sets whether the [`Droppable`] can be dragged along individual axes. + pub fn drag_mode(mut self, drag_x: bool, drag_y: bool) -> Self { + self.drag_mode = Some((drag_x, drag_y)); + self + } + + /// Sets whether the [`Droppable`] should be be resized to a given size while dragging. + pub fn drag_size(mut self, hide_size: Size) -> Self { + self.drag_size = Some(hide_size); + self + } + + /// Sets the number of frames/layout calls to wait before resetting the size of the [`Droppable`] after dropping. + /// + /// This is useful for cases where the [`Droppable`] is being moved to a new location after some widget operation. + /// In this case, the [`Droppable`] will mainting the 'drag_size' for the given number of frames before resetting to its original size. + /// This prevents the [`Droppable`] from 'jumping' back to its original size before the new location is rendered which + /// prevents flickering. + /// + /// Warning: this should only be set if there's is some noticeble flickering when the [`Droppable`] is dropped. That is, if the + /// [`Droppable`] returns to its original size before it's moved to it's new location. + pub fn reset_delay(mut self, reset_delay: usize) -> Self { + self.reset_delay = reset_delay; + self + } +} + +impl<'a, Message, Theme, Renderer> Widget + for Droppable<'a, Message, Theme, Renderer> +where + Message: Clone, + Renderer: renderer::Renderer, +{ + fn state(&self) -> iced::advanced::widget::tree::State { + advanced::widget::tree::State::new(State::default()) + } + + fn tag(&self) -> iced::advanced::widget::tree::Tag { + advanced::widget::tree::Tag::of::() + } + + fn children(&self) -> Vec { + vec![advanced::widget::Tree::new(&self.content)] + } + + fn diff(&self, tree: &mut iced::advanced::widget::Tree) { + tree.diff_children(std::slice::from_ref(&self.content)) + } + + fn size(&self) -> iced::Size { + self.content.as_widget().size() + } + + fn on_event( + &mut self, + tree: &mut iced::advanced::widget::Tree, + event: iced::Event, + layout: iced::advanced::Layout<'_>, + cursor: iced::advanced::mouse::Cursor, + _renderer: &Renderer, + _clipboard: &mut dyn iced::advanced::Clipboard, + shell: &mut iced::advanced::Shell<'_, Message>, + _viewport: &iced::Rectangle, + ) -> iced::advanced::graphics::core::event::Status { + // handle the on event of the content first, in case that the droppable is nested + let status = self.content.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + layout, + cursor, + _renderer, + _clipboard, + shell, + _viewport, + ); + // this should really only be captured if the droppable is nested or it contains some other + // widget that captures the event + if status == Status::Captured { + return status; + }; + + if let Some(on_drop) = self.on_drop.as_deref() { + let state = tree.state.downcast_mut::(); + if let iced::Event::Mouse(mouse) = event { + match mouse { + mouse::Event::ButtonPressed(btn) => { + if btn == mouse::Button::Left && cursor.is_over(layout.bounds()) { + // select the droppable and store the position of the widget before dragging + state.action = Action::Select(cursor.position().unwrap()); + let bounds = layout.bounds(); + state.widget_pos = bounds.position(); + state.overlay_bounds.width = bounds.width; + state.overlay_bounds.height = bounds.height; + + if let Some(on_click) = self.on_click.clone() { + shell.publish(on_click); + } + return Status::Captured; + } else if btn == mouse::Button::Right { + if let Action::Drag(_, _) = state.action { + shell.invalidate_layout(); + state.action = Action::None; + if let Some(on_cancel) = self.on_cancel.clone() { + shell.publish(on_cancel); + } + } + } + } + mouse::Event::CursorMoved { mut position } => match state.action { + Action::Select(start) | Action::Drag(start, _) => { + // calculate the new position of the widget after dragging + + if let Some((drag_x, drag_y)) = self.drag_mode { + position = Point { + x: if drag_x { position.x } else { start.x }, + y: if drag_y { position.y } else { start.y }, + }; + } + + state.action = Action::Drag(start, position); + // update the position of the overlay since the cursor was moved + if self.drag_center { + state.overlay_bounds.x = position.x - state.overlay_bounds.width / 2.0; + state.overlay_bounds.y = position.y - state.overlay_bounds.height / 2.0; + } else { + state.overlay_bounds.x = state.widget_pos.x + position.x - start.x; + state.overlay_bounds.y = state.widget_pos.y + position.y - start.y; + } + // send on drag msg + if let Some(on_drag) = self.on_drag.as_deref() { + let message = (on_drag)(position, state.overlay_bounds); + shell.publish(message); + } + } + _ => (), + }, + mouse::Event::ButtonReleased(btn) => { + if btn == mouse::Button::Left { + match state.action { + Action::Select(_) => { + state.action = Action::None; + } + Action::Drag(_, current) => { + // send on drop msg + let message = (on_drop)(current, state.overlay_bounds); + shell.publish(message); + + if self.reset_delay == 0 { + state.action = Action::None; + } else { + state.action = Action::Wait(self.reset_delay); + } + } + _ => (), + } + } + } + _ => {} + } + } + } + Status::Ignored + } + + fn layout( + &self, + tree: &mut iced::advanced::widget::Tree, + renderer: &Renderer, + limits: &iced::advanced::layout::Limits, + ) -> iced::advanced::layout::Node { + let state: &mut State = tree.state.downcast_mut::(); + let content_node = self + .content + .as_widget() + .layout(&mut tree.children[0], renderer, limits); + + // Adjust the size of the original widget if it's being dragged or we're wating to reset the size + if let Some(new_size) = self.drag_size { + match state.action { + Action::Drag(_, _) => { + return iced::advanced::layout::Node::with_children( + new_size, + content_node.children().to_vec(), + ); + } + Action::Wait(reveal_index) => { + if reveal_index <= 1 { + state.action = Action::None; + } else { + state.action = Action::Wait(reveal_index - 1); + } + + return iced::advanced::layout::Node::with_children( + new_size, + content_node.children().to_vec(), + ); + } + _ => (), + } + } + + content_node + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + let state = tree.state.downcast_mut::(); + operation.custom(state, self.id.as_ref()); + operation.container(self.id.as_ref(), layout.bounds(), &mut |operation| { + self.content + .as_widget() + .operate(&mut tree.children[0], layout, renderer, operation); + }); + } + + fn draw( + &self, + tree: &iced::advanced::widget::Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: iced::advanced::Layout<'_>, + cursor: iced::advanced::mouse::Cursor, + viewport: &iced::Rectangle, + ) { + let state: &State = tree.state.downcast_ref::(); + if let Action::Drag(_, _) = state.action { + if self.drag_hide { + return; + } + } + + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + style, + layout, + cursor, + &viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + _translation: Vector, + ) -> Option> { + let state: &mut State = tree.state.downcast_mut::(); + let mut children = tree.children.iter_mut(); + if self.drag_overlay { + if let Action::Drag(_, _) = state.action { + return Some(overlay::Element::new(Box::new(Overlay { + content: &self.content, + tree: children.next().unwrap(), + overlay_bounds: state.overlay_bounds, + }))); + } + } + self.content.as_widget_mut().overlay( + children.next().unwrap(), + layout, + renderer, + _translation, + ) + } + + fn mouse_interaction( + &self, + tree: &iced::advanced::widget::Tree, + layout: iced::advanced::Layout<'_>, + cursor: iced::advanced::mouse::Cursor, + _viewport: &iced::Rectangle, + _renderer: &Renderer, + ) -> iced::advanced::mouse::Interaction { + let child_interact = self.content.as_widget().mouse_interaction( + &tree.children[0], + layout, + cursor, + _viewport, + _renderer, + ); + if child_interact != mouse::Interaction::default() { + return child_interact; + } + + let state = tree.state.downcast_ref::(); + + if self.on_drop.is_none() { + return mouse::Interaction::NotAllowed; + } + if let Action::Drag(_, _) = state.action { + return mouse::Interaction::Grabbing; + } + if cursor.is_over(layout.bounds()) { + return mouse::Interaction::Pointer; + } + mouse::Interaction::default() + } +} + +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a + Clone, + Theme: 'a, + Renderer: 'a + renderer::Renderer, +{ + fn from( + droppable: Droppable<'a, Message, Theme, Renderer>, + ) -> Element<'a, Message, Theme, Renderer> { + Element::new(droppable) + } +} + +#[derive(Default, Clone, Copy, PartialEq, Debug)] +pub struct State { + widget_pos: Point, + overlay_bounds: Rectangle, + action: Action, +} + +#[derive(Default, Clone, Copy, PartialEq, Debug)] +pub enum Action { + #[default] + None, + /// (point clicked) + Select(Point), + /// (start pos, current pos) + Drag(Point, Point), + /// (frames to wait) + Wait(usize), +} + +struct Overlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + content: &'b Element<'a, Message, Theme, Renderer>, + tree: &'b mut advanced::widget::Tree, + overlay_bounds: Rectangle, +} + +impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay + for Overlay<'a, 'b, Message, Theme, Renderer> +where + Renderer: renderer::Renderer, +{ + fn layout(&mut self, renderer: &Renderer, _bounds: Size) -> layout::Node { + Widget::::layout( + self.content.as_widget(), + self.tree, + renderer, + &layout::Limits::new(Size::ZERO, self.overlay_bounds.size()), + ) + .move_to(self.overlay_bounds.position()) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Theme, + inherited_style: &renderer::Style, + layout: Layout<'_>, + cursor_position: mouse::Cursor, + ) { + Widget::::draw( + self.content.as_widget(), + self.tree, + renderer, + theme, + inherited_style, + layout, + cursor_position, + &Rectangle::with_size(Size::INFINITY), + ); + } + + fn is_over(&self, _layout: Layout<'_>, _renderer: &Renderer, _cursor_position: Point) -> bool { + false + } +} diff --git a/iced_drop/src/widget/operation.rs b/iced_drop/src/widget/operation.rs new file mode 100644 index 0000000..3d7dcff --- /dev/null +++ b/iced_drop/src/widget/operation.rs @@ -0,0 +1 @@ +pub mod drop; diff --git a/iced_drop/src/widget/operation/drop.rs b/iced_drop/src/widget/operation/drop.rs new file mode 100644 index 0000000..12a2e30 --- /dev/null +++ b/iced_drop/src/widget/operation/drop.rs @@ -0,0 +1,89 @@ +use iced::{ + advanced::widget::{ + operation::{Outcome, Scrollable}, + Id, Operation, + }, + Rectangle, Vector, +}; + +/// Produces an [`Operation`] that will find the drop zones that pass a filter on the zone's bounds. +/// For any drop zone to be considered, the Element must have some Id. +/// If `options` is `None`, all drop zones will be considered. +/// Depth determines how how deep into nested drop zones to go. +/// If 'depth' is `None`, nested dropzones will be fully explored +pub fn find_zones( + filter: F, + options: Option>, + depth: Option, +) -> impl Operation> +where + F: Fn(&Rectangle) -> bool + 'static, +{ + struct FindDropZone { + filter: F, + options: Option>, + zones: Vec<(Id, Rectangle)>, + max_depth: Option, + c_depth: usize, + offset: Vector, + } + + impl Operation> for FindDropZone + where + F: Fn(&Rectangle) -> bool + 'static, + { + fn container( + &mut self, + id: Option<&Id>, + bounds: iced::Rectangle, + operate_on_children: &mut dyn FnMut(&mut dyn Operation>), + ) { + match id { + Some(id) => { + let is_option = match &self.options { + Some(options) => options.contains(id), + None => true, + }; + let bounds = bounds - self.offset; + if is_option && (self.filter)(&bounds) { + self.c_depth += 1; + self.zones.push((id.clone(), bounds)); + } + } + None => (), + } + let goto_next = match &self.max_depth { + Some(m_depth) => self.c_depth < *m_depth, + None => true, + }; + if goto_next { + operate_on_children(self); + } + } + + fn finish(&self) -> Outcome> { + Outcome::Some(self.zones.clone()) + } + + fn scrollable( + &mut self, + _state: &mut dyn Scrollable, + _id: Option<&Id>, + bounds: Rectangle, + translation: Vector, + ) { + if (self.filter)(&bounds) { + self.offset = self.offset + translation; + } + } + } + + FindDropZone { + filter, + options, + zones: vec![], + max_depth: depth, + c_depth: 0, + offset: Vector { x: 0.0, y: 0.0 }, + } +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs deleted file mode 100644 index 2dd9cff..0000000 --- a/src/codegen/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::path::PathBuf; - -use rust_format::{Config, Edition, Formatter, RustFmt}; - -use crate::types::{rendered_element::RenderedElement, ElementName}; - -impl RenderedElement { - fn props_codegen(&self) -> String { - let mut props_string = String::new(); - - for (k, v) in self.props.clone() { - props_string = format!("{props_string}.{k}({v})"); - } - - props_string - } - - fn codegen(&self) -> (String, String) { - let mut imports = String::new(); - let mut view = String::new(); - let props = self.props_codegen(); - - let mut elements = String::new(); - - match self.name { - ElementName::Column | ElementName::Row | ElementName::Container => { - for element in &self.child_elements { - let (c_imports, children) = element.codegen(); - imports = format!("{imports}{c_imports}"); - elements = format!("{elements}{},", children); - } - } - _ => {} - } - - match &self.name { - ElementName::Container => { - imports = format!("{imports}container,"); - view = if self.child_elements.len() < 2 { - format!("{view}\ncontainer({elements}){props}") - } else { - format!("{view}\ncontainer(){props}") - }; - } - ElementName::Row => { - imports = format!("{imports}row,"); - view = format!("{view}\nrow![{elements}]{props}"); - } - ElementName::Column => { - imports = format!("{imports}column,"); - view = format!("{view}\ncolumn![{elements}]{props}"); - } - ElementName::Text(string) => { - imports = format!("{imports}text,"); - view = format!( - "{view}\ntext(\"{}\"){props}", - if *string == String::new() { - "New Text" - } else { - string - } - ); - } - ElementName::Button(string) => { - imports = format!("{imports}button,"); - view = format!( - "{view}\nbutton(\"{}\"){props}", - if *string == String::new() { - "New Button" - } else { - string - } - ); - } - ElementName::Image(path) => { - imports = format!("{imports}image,"); - view = format!("{view}\nimage(\"{}\"){props}", path.display().to_string()); - } - ElementName::SVG(path) => { - imports = format!("{imports}svg,"); - view = format!("{view}\nsvg(\"{}\"){props}", path.display().to_string()); - } - } - - (imports, view) - } - - pub fn app_code( - &self, - title: &str, - theme: Option, - ) -> Result> { - let (imports, view) = self.codegen(); - let mut app_code = format!("use iced::{{widget::{{{imports}}},Sandbox,Settings,Element}};"); - - app_code = format!( - r#"{app_code} - - fn main() -> iced::Result {{ - App::run(Settings::default()) - }} - - struct App; - - impl Sandbox for App {{ - type Message = (); - - fn new() -> Self {{ - Self {{}} - }} - - fn title(&self) -> String {{ - "{title}".into() - }} - - fn theme(&self) -> iced::Theme {{ - iced::Theme::{} - }} - - fn update(&mut self, message: Self::Message) {{ - - }} - - fn view(&self) -> Element {{ - {view}.into() - }} - }}"#, - if let Some(c) = theme { - c.to_string().replace(' ', "") - } else { - "default()".to_owned() - } - ); - let config = Config::new_str() - .edition(Edition::Rust2021) - .option("trailing_comma", "Never") - .option("imports_granularity", "Crate"); - let rustfmt = RustFmt::from_config(config); - Ok(rustfmt.format_str(app_code)?) - } - - pub fn test() -> String { - let mut text1 = RenderedElement::new(ElementName::Text("wow")); - text1.set_property("height", "120.5"); - text1.set_property("width", "230"); - - let element = RenderedElement::new(ElementName::Container).push(RenderedElement::from_vec( - ElementName::Row, - vec![ - text1, - RenderedElement::new(ElementName::Text("heh")), - RenderedElement::new(ElementName::SVG(PathBuf::from( - "/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg", - ))), - ], - )); - - element.app_code("new app", None).unwrap() - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index d3fafc6..0000000 --- a/src/main.rs +++ /dev/null @@ -1,301 +0,0 @@ -mod codegen; -mod types; - -use iced::{ - clipboard, executor, - highlighter::{self, Highlighter}, - theme, - widget::{ - button, column, container, - pane_grid::{self, Pane, PaneGrid}, - row, text, text_editor, tooltip, Column, Space, - }, - Alignment, Application, Color, Command, Element, Font, Length, Settings, -}; -use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; - -fn main() -> iced::Result { - App::run(Settings { - fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], - ..Settings::default() - }) -} - -struct App { - is_saved: bool, - current_project: Option, - dark_theme: bool, - pane_state: pane_grid::State, - focus: Option, - designer_state: DesignerState, - element_list: Vec, - editor_content: text_editor::Content, -} - -#[derive(Debug, Clone)] -enum Message { - ToggleTheme, - CopyCode, - Resized(pane_grid::ResizeEvent), - Clicked(pane_grid::Pane), -} - -#[derive(Clone, Debug)] -enum Panes { - Designer, - ElementList, -} - -impl Application for App { - type Message = Message; - type Theme = theme::Theme; - type Executor = executor::Default; - type Flags = (); - - fn new(_flags: ()) -> (Self, Command) { - let state = pane_grid::State::with_configuration(pane_grid::Configuration::Split { - axis: pane_grid::Axis::Vertical, - ratio: 0.8, - a: Box::new(pane_grid::Configuration::Pane(Panes::Designer)), - b: Box::new(pane_grid::Configuration::Pane(Panes::ElementList)), - }); - ( - Self { - is_saved: true, - current_project: None, - dark_theme: true, - pane_state: state, - focus: None, - designer_state: DesignerState { - designer_content: vec![], - designer_page: DesignerPage::CodeView, - }, - element_list: vec!["Column", "Row", "PickList", "PaneGrid", "Button", "Text"] - .into_iter() - .map(|c| c.to_owned()) - .collect(), - editor_content: text_editor::Content::with_text(&RenderedElement::test()), - }, - Command::none(), - ) - } - - fn title(&self) -> String { - let saved_state = if self.is_saved { "" } else { " *" }; - - let project_name = match &self.current_project { - Some(n) => format!(" - {n}"), - None => "".to_owned(), - }; - - format!("iced Builder{project_name}{saved_state}") - } - - fn theme(&self) -> iced::Theme { - if self.dark_theme { - theme::Theme::CatppuccinMocha - } else { - theme::Theme::CatppuccinLatte - } - } - - fn update(&mut self, message: Message) -> Command { - match message { - Message::ToggleTheme => self.dark_theme = !self.dark_theme, - Message::CopyCode => return clipboard::write(self.editor_content.text()), - Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { - self.pane_state.resize(split, ratio); - } - Message::Clicked(pane) => { - self.focus = Some(pane); - } - } - - Command::none() - } - - fn view(&self) -> Element { - let header = row![button("Toggle Theme") - .on_press(Message::ToggleTheme) - .padding(5)] - .width(200); - let pane_grid = PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { - let is_focused = Some(id) == self.focus; - match pane { - Panes::Designer => match self.designer_state.designer_page { - DesignerPage::Designer => { - let content = column![text("Designer"),] - .align_items(Alignment::Center) - .height(Length::Fill) - .width(Length::Fill); - let title = text("Designer").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); - let title_bar = pane_grid::TitleBar::new(title) - .padding(10) - .style(style::title_bar); - pane_grid::Content::new(content) - .title_bar(title_bar) - .style(if is_focused { - style::pane_focused - } else { - style::pane_active - }) - } - DesignerPage::CodeView => { - let title = row![ - text("Generated Code").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }), - Space::with_width(Length::Fill), - tooltip( - button( - container( - text('\u{0e801}').font(Font::with_name("editor-icons")) - ) - .width(30) - .center_x() - ) - .on_press(Message::CopyCode), - "Copy code to clipboard", - tooltip::Position::Left - ) - ]; - let title_bar = pane_grid::TitleBar::new(title) - .padding(10) - .style(style::title_bar); - pane_grid::Content::new( - text_editor(&self.editor_content) - .highlight::( - highlighter::Settings { - theme: highlighter::Theme::Base16Mocha, - extension: "rs".to_string(), - }, - |highlight, _theme| highlight.to_format(), - ) - .height(Length::Fill) - .padding(20), - ) - .title_bar(title_bar) - .style(if is_focused { - style::pane_focused - } else { - style::pane_active - }) - } - }, - Panes::ElementList => { - let items_list = items_list_view(&self.element_list); - let content = column![items_list] - .align_items(Alignment::Center) - .height(Length::Fill) - .width(Length::Fill); - let title = text("Element List").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); - let title_bar = pane_grid::TitleBar::new(title) - .padding(10) - .style(style::title_bar); - pane_grid::Content::new(content) - .title_bar(title_bar) - .style(if is_focused { - style::pane_focused - } else { - style::pane_active - }) - } - } - }) - .width(Length::Fill) - .height(Length::Fill) - .spacing(10) - .on_resize(10, Message::Resized) - .on_click(Message::Clicked); - - let content = Column::new() - .push(header) - .push(pane_grid) - .spacing(5) - .align_items(Alignment::Center) - .width(Length::Fill); - - container(content).height(Length::Fill).into() - } -} - -const fn from_grayscale(grayscale: f32) -> Color { - Color { - r: grayscale, - g: grayscale, - b: grayscale, - a: 1.0, - } -} - -// #ffffff -const PANE_ID_COLOR_FOCUSED: Color = from_grayscale(1.0); - -// #e8e8e8 -const PANE_ID_COLOR_UNFOCUSED: Color = from_grayscale(0xE8 as f32 / 255.0); - -fn items_list_view(items: &Vec) -> Element<'static, Message> { - let mut column = Column::new() - .spacing(20) - .align_items(Alignment::Center) - .width(Length::Fill); - - for value in items { - column = column.push(text(value)); - } - - container(column).height(250.0).width(300).into() -} - -mod style { - use iced::widget::container; - use iced::{Border, Theme}; - - pub fn title_bar(theme: &Theme) -> container::Appearance { - let palette = theme.extended_palette(); - - container::Appearance { - text_color: Some(palette.background.strong.text), - background: Some(palette.background.strong.color.into()), - ..Default::default() - } - } - - pub fn pane_active(theme: &Theme) -> container::Appearance { - let palette = theme.extended_palette(); - - container::Appearance { - background: Some(palette.background.weak.color.into()), - border: Border { - width: 1.0, - color: palette.background.strong.color, - ..Border::default() - }, - ..Default::default() - } - } - - pub fn pane_focused(theme: &Theme) -> container::Appearance { - let palette = theme.extended_palette(); - - container::Appearance { - background: Some(palette.background.weak.color.into()), - border: Border { - width: 4.0, - color: palette.background.strong.color, - ..Border::default() - }, - ..Default::default() - } - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs deleted file mode 100644 index 7a04d79..0000000 --- a/src/types/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -pub mod rendered_element; - -use rendered_element::RenderedElement; -use std::path::PathBuf; - -pub struct DesignerState { - pub designer_content: Vec, - pub designer_page: DesignerPage, -} - -#[derive(Debug)] -pub enum ElementName { - Text(&'static str), - Button(&'static str), - SVG(PathBuf), - Image(PathBuf), - Container, - Row, - Column, -} - -pub enum DesignerPage { - Designer, - CodeView, -} diff --git a/src/types/rendered_element.rs b/src/types/rendered_element.rs deleted file mode 100644 index f05594d..0000000 --- a/src/types/rendered_element.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::collections::HashMap; - -use unique_id::{string::StringGenerator, Generator}; - -use iced::advanced::widget::Id; - -use super::ElementName; - -#[derive(Debug)] -pub struct RenderedElement { - pub id: Id, - pub child_elements: Vec, - pub name: ElementName, - pub props: HashMap<&'static str, &'static str>, -} - -impl RenderedElement { - pub fn new(name: ElementName) -> Self { - let gen = StringGenerator::default(); - Self { - id: Id::new(gen.next_id()), - child_elements: vec![], - name, - props: HashMap::new(), - } - } - - pub fn from_vec(name: ElementName, child_elements: Vec) -> Self { - let gen = StringGenerator::default(); - Self { - id: Id::new(gen.next_id()), - child_elements, - name, - props: HashMap::new(), - } - } - - pub fn push(mut self, element: RenderedElement) -> Self { - self.child_elements.push(element); - self - } - - pub fn set_property(&mut self, prop: &'static str, value: &'static str) { - let prop_ref = self.props.entry(prop).or_insert(value); - *prop_ref = value; - } -} -- cgit v1.2.3 From bc2066220f656765cc820baeb687d8594fbc8826 Mon Sep 17 00:00:00 2001 From: pml68 Date: Mon, 23 Sep 2024 23:57:45 +0200 Subject: feat: make code view scrollable, add autogen comment to code --- iced_builder/src/codegen/mod.rs | 3 ++- iced_builder/src/main.rs | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 88c0c61..20a2e65 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -92,7 +92,8 @@ impl RenderedElement { let mut app_code = format!("use iced::{{widget::{{{imports}}},Sandbox,Settings,Element}};"); app_code = format!( - r#"{app_code} + r#"// Automatically generated by iced Builder + {app_code} fn main() -> iced::Result {{ App::run(Settings::default()) diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index e966614..2633e6c 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -37,6 +37,7 @@ struct App { enum Message { ToggleTheme, CopyCode, + EditorAction(text_editor::Action), Drop(types::ElementName, iced::Point, iced::Rectangle), HandleZones( types::ElementName, @@ -107,6 +108,11 @@ impl Application for App { match message { Message::ToggleTheme => self.dark_theme = !self.dark_theme, Message::CopyCode => return clipboard::write(self.editor_content.text()), + Message::EditorAction(action) => { + if let text_editor::Action::Scroll { lines: _ } = action { + self.editor_content.perform(action); + } + } Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); } @@ -190,6 +196,7 @@ impl Application for App { .style(style::title_bar); pane_grid::Content::new( text_editor(&self.editor_content) + .on_action(Message::EditorAction) .highlight::( highlighter::Settings { theme: highlighter::Theme::Base16Mocha, -- cgit v1.2.3 From b7e1527ab07acc45038f71c0a2bbbaf4180e5db3 Mon Sep 17 00:00:00 2001 From: pml68 Date: Sat, 28 Sep 2024 12:17:53 +0200 Subject: feat: add button to switch between designer and code view f --- iced_builder/src/codegen/mod.rs | 4 ++-- iced_builder/src/main.rs | 50 +++++++++++++++++++++++++++++++++-------- iced_builder/src/types/mod.rs | 3 ++- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 20a2e65..923ef99 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -138,7 +138,7 @@ impl RenderedElement { Ok(rustfmt.format_str(app_code)?) } - pub fn test() -> String { + pub fn test() -> RenderedElement { let mut text1 = RenderedElement::new(ElementName::Text("wow")); text1.set_property("height", "120.5"); text1.set_property("width", "230"); @@ -154,6 +154,6 @@ impl RenderedElement { ], )); - element.app_code("new app", None).unwrap() + element } } diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 2633e6c..42ea627 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -37,6 +37,7 @@ struct App { enum Message { ToggleTheme, CopyCode, + SwitchPage(DesignerPage), EditorAction(text_editor::Action), Drop(types::ElementName, iced::Point, iced::Rectangle), HandleZones( @@ -75,11 +76,11 @@ impl Application for App { pane_state: state, focus: None, designer_state: DesignerState { - designer_content: vec![], + designer_content: Some(RenderedElement::test()), designer_page: DesignerPage::Designer, }, element_list: types::ElementName::ALL.to_vec(), - editor_content: text_editor::Content::with_text(&RenderedElement::test()), + editor_content: text_editor::Content::new(), }, Command::none(), ) @@ -89,7 +90,16 @@ impl Application for App { let saved_state = if self.is_saved { "" } else { " *" }; let project_name = match &self.current_project { - Some(n) => format!(" - {n}"), + Some(n) => { + format!( + " - {}", + if n.len() > 60 { + format!("...{}", &n[n.len() - 40..]) + } else { + n.to_owned() + } + ) + } None => "".to_owned(), }; @@ -108,6 +118,7 @@ impl Application for App { match message { Message::ToggleTheme => self.dark_theme = !self.dark_theme, Message::CopyCode => return clipboard::write(self.editor_content.text()), + Message::SwitchPage(page) => self.designer_state.designer_page = page, Message::EditorAction(action) => { if let text_editor::Action::Scroll { lines: _ } = action { self.editor_content.perform(action); @@ -130,6 +141,19 @@ impl Application for App { } Message::HandleZones(name, zones) => { println!("{:?}\n{name}", zones); + println!("{:?}\n{name}\n{:?}", zones, self.title()); + if let Some(el) = &self.designer_state.designer_content { + self.editor_content = text_editor::Content::with_text( + &el.app_code( + match &self.current_project { + Some(title) => &title, + None => "New App", + }, + None, + ) + .unwrap(), + ); + } } Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { self.pane_state.drop(pane, target); @@ -154,11 +178,16 @@ impl Application for App { .id(iced::widget::container::Id::new("drop_zone")) .height(Length::Fill) .width(Length::Fill); - let title = text("Designer").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); + let title = row![ + text("Designer").style(if is_focused { + PANE_ID_COLOR_FOCUSED + } else { + PANE_ID_COLOR_UNFOCUSED + }), + Space::with_width(Length::Fill), + button("Switch to Code view") + .on_press(Message::SwitchPage(DesignerPage::CodeView)), + ]; let title_bar = pane_grid::TitleBar::new(title) .padding(10) .style(style::title_bar); @@ -189,7 +218,10 @@ impl Application for App { .on_press(Message::CopyCode), "Copy code to clipboard", tooltip::Position::Left - ) + ), + Space::with_width(20), + button("Switch to Designer view") + .on_press(Message::SwitchPage(DesignerPage::Designer)) ]; let title_bar = pane_grid::TitleBar::new(title) .padding(10) diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index db06ffa..eaf1686 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -3,7 +3,7 @@ pub mod rendered_element; use rendered_element::RenderedElement; pub struct DesignerState { - pub designer_content: Vec, + pub designer_content: Option, pub designer_page: DesignerPage, } @@ -48,6 +48,7 @@ impl std::fmt::Display for ElementName { } } +#[derive(Debug, Clone)] pub enum DesignerPage { Designer, CodeView, -- cgit v1.2.3 From 99e141d9fe9c8f494bb15a9ca31c07d27722f9dd Mon Sep 17 00:00:00 2001 From: pml68 Date: Sat, 28 Sep 2024 23:10:17 +0200 Subject: feat: rework RenderedElement, prepare for JSON project format --- Cargo.lock | 2 + iced_builder/Cargo.toml | 2 + iced_builder/src/codegen/mod.rs | 49 ++++++++++------------- iced_builder/src/main.rs | 10 +++-- iced_builder/src/types/mod.rs | 19 ++++----- iced_builder/src/types/rendered_element.rs | 63 ++++++++++++++++++++++-------- 6 files changed, 88 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d12997d..baeab67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1246,6 +1246,8 @@ dependencies = [ "iced_aw", "iced_drop", "rust-format", + "serde", + "serde_json", "tokio", "unique_id", ] diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml index febff28..8106b09 100644 --- a/iced_builder/Cargo.toml +++ b/iced_builder/Cargo.toml @@ -12,6 +12,8 @@ keywords = ["gui", "iced"] iced = { version = "0.12.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } iced_aw = { version = "0.9.3", default-features = false, features = ["menu","color_picker"] } iced_drop = { path = "../iced_drop" } +serde = { version = "1.0.210", features = ["derive"] } +serde_json = "1.0.128" tokio = { version = "1.40.0", features = ["fs"] } rust-format = "0.3.4" unique_id = "0.1.5" diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 923ef99..927b6e4 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -1,13 +1,18 @@ use rust_format::{Config, Edition, Formatter, RustFmt}; -use crate::types::{rendered_element::RenderedElement, ElementName}; +use crate::types::{ + rendered_element::{container, row, svg, text, RenderedElement}, + ElementName, +}; impl RenderedElement { fn props_codegen(&self) -> String { let mut props_string = String::new(); for (k, v) in self.props.clone() { - props_string = format!("{props_string}.{k}({v})"); + if let Some(value) = v { + props_string = format!("{props_string}.{k}({value})"); + } } props_string @@ -20,25 +25,18 @@ impl RenderedElement { let mut elements = String::new(); - match self.name { - ElementName::Column | ElementName::Row | ElementName::Container => { - for element in &self.child_elements { - let (c_imports, children) = element.codegen(); - imports = format!("{imports}{c_imports}"); - elements = format!("{elements}{},", children); - } + if let Some(els) = &self.child_elements { + for element in els { + let (c_imports, children) = element.codegen(); + imports = format!("{imports}{c_imports}"); + elements = format!("{elements}{},", children); } - _ => {} } match &self.name { ElementName::Container => { imports = format!("{imports}container,"); - view = if self.child_elements.len() < 2 { - format!("{view}\ncontainer({elements}){props}") - } else { - format!("{view}\ncontainer(){props}") - }; + view = format!("{view}\ncontainer({elements}){props}"); } ElementName::Row => { imports = format!("{imports}row,"); @@ -139,20 +137,13 @@ impl RenderedElement { } pub fn test() -> RenderedElement { - let mut text1 = RenderedElement::new(ElementName::Text("wow")); - text1.set_property("height", "120.5"); - text1.set_property("width", "230"); - - let element = RenderedElement::new(ElementName::Container).push(RenderedElement::from_vec( - ElementName::Row, - vec![ - text1, - RenderedElement::new(ElementName::Text("heh")), - RenderedElement::new(ElementName::SVG( - "/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg", - )), - ], - )); + let text1 = text("wow").option("height", "120.5").option("width", "230"); + + let element = container(row(vec![ + text1, + text("heh"), + svg("/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg"), + ])); element } diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 42ea627..971adce 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -132,7 +132,7 @@ impl Application for App { } Message::Drop(name, point, _) => { return iced_drop::zones_on_point( - move |zones| Message::HandleZones(name, zones), + move |zones| Message::HandleZones(name.clone(), zones), point, None, None, @@ -231,7 +231,11 @@ impl Application for App { .on_action(Message::EditorAction) .highlight::( highlighter::Settings { - theme: highlighter::Theme::Base16Mocha, + theme: if self.dark_theme { + highlighter::Theme::Base16Mocha + } else { + highlighter::Theme::InspiredGitHub + }, extension: "rs".to_string(), }, |highlight, _theme| highlight.to_format(), @@ -314,7 +318,7 @@ fn items_list_view(items: Vec) -> Element<'static, Message> let value = item.clone(); column = column.push( droppable(text(value.to_string())) - .on_drop(move |point, rect| Message::Drop(value, point, rect)), + .on_drop(move |point, rect| Message::Drop(value.clone(), point, rect)), ); } diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index eaf1686..def04cf 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -1,18 +1,19 @@ pub mod rendered_element; use rendered_element::RenderedElement; +use serde::{Deserialize, Serialize}; pub struct DesignerState { pub designer_content: Option, pub designer_page: DesignerPage, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ElementName { - Text(&'static str), - Button(&'static str), - SVG(&'static str), - Image(&'static str), + Text(String), + Button(String), + SVG(String), + Image(String), Container, Row, Column, @@ -20,10 +21,10 @@ pub enum ElementName { impl ElementName { pub const ALL: [Self; 7] = [ - Self::Text(""), - Self::Button(""), - Self::SVG(""), - Self::Image(""), + Self::Text(String::new()), + Self::Button(String::new()), + Self::SVG(String::new()), + Self::Image(String::new()), Self::Container, Self::Row, Self::Column, diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index f05594d..1938aa0 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -1,47 +1,78 @@ use std::collections::HashMap; +use serde::{Deserialize, Serialize}; use unique_id::{string::StringGenerator, Generator}; -use iced::advanced::widget::Id; - use super::ElementName; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct RenderedElement { - pub id: Id, - pub child_elements: Vec, + pub id: String, + pub child_elements: Option>, pub name: ElementName, - pub props: HashMap<&'static str, &'static str>, + pub props: HashMap>, } impl RenderedElement { - pub fn new(name: ElementName) -> Self { + fn new(name: ElementName) -> Self { let gen = StringGenerator::default(); Self { - id: Id::new(gen.next_id()), - child_elements: vec![], + id: gen.next_id(), + child_elements: None, name, props: HashMap::new(), } } - pub fn from_vec(name: ElementName, child_elements: Vec) -> Self { + fn from_vec(name: ElementName, child_elements: Vec) -> Self { let gen = StringGenerator::default(); Self { - id: Id::new(gen.next_id()), - child_elements, + id: gen.next_id(), + child_elements: Some(child_elements), name, props: HashMap::new(), } } pub fn push(mut self, element: RenderedElement) -> Self { - self.child_elements.push(element); + if let Some(els) = self.child_elements.as_mut() { + els.push(element); + } else { + self.child_elements = Some(vec![element]); + } self } - pub fn set_property(&mut self, prop: &'static str, value: &'static str) { - let prop_ref = self.props.entry(prop).or_insert(value); - *prop_ref = value; + pub fn option(mut self, prop: &'static str, value: &'static str) -> Self { + let prop_ref = self + .props + .entry(prop.to_owned()) + .or_insert(Some(value.to_owned())); + *prop_ref = Some(value.to_owned()); + self } } + +pub fn text(text: &str) -> RenderedElement { + RenderedElement::new(ElementName::Text(text.to_owned())) +} + +pub fn button(text: &str) -> RenderedElement { + RenderedElement::new(ElementName::Button(text.to_owned())) +} + +pub fn svg(path: &str) -> RenderedElement { + RenderedElement::new(ElementName::SVG(path.to_owned())) +} + +pub fn image(path: &str) -> RenderedElement { + RenderedElement::new(ElementName::Image(path.to_owned())) +} + +pub fn container(content: RenderedElement) -> RenderedElement { + RenderedElement::from_vec(ElementName::Container, vec![content]) +} + +pub fn row(child_elements: Vec) -> RenderedElement { + RenderedElement::from_vec(ElementName::Row, child_elements) +} -- cgit v1.2.3 From f950f6357aea66962ea3a778501430205bf4d6c6 Mon Sep 17 00:00:00 2001 From: pml68 Date: Mon, 30 Sep 2024 01:02:46 +0200 Subject: feat: implement fmt::Display for RenderedElement, work on props --- iced_builder/src/main.rs | 16 ++++--- iced_builder/src/types/mod.rs | 1 + iced_builder/src/types/rendered_element.rs | 68 ++++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 971adce..f0f58cc 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -172,12 +172,18 @@ impl Application for App { let pane_grid = PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { let is_focused = Some(id) == self.focus; match pane { - Panes::Designer => match self.designer_state.designer_page { + Panes::Designer => match &self.designer_state.designer_page { DesignerPage::Designer => { - let content = container("") - .id(iced::widget::container::Id::new("drop_zone")) - .height(Length::Fill) - .width(Length::Fill); + let content = container(text(format!( + "{:0.4}", + self.designer_state + .designer_content + .clone() + .expect("Designer content hasn't been set yet."), + ))) + .id(iced::widget::container::Id::new("drop_zone")) + .height(Length::Fill) + .width(Length::Fill); let title = row![ text("Designer").style(if is_focused { PANE_ID_COLOR_FOCUSED diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index def04cf..344f543 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -3,6 +3,7 @@ pub mod rendered_element; use rendered_element::RenderedElement; use serde::{Deserialize, Serialize}; +#[derive(Clone)] pub struct DesignerState { pub designer_content: Option, pub designer_page: DesignerPage, diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 1938aa0..2d5c81a 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -5,7 +5,7 @@ use unique_id::{string::StringGenerator, Generator}; use super::ElementName; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct RenderedElement { pub id: String, pub child_elements: Option>, @@ -34,6 +34,13 @@ impl RenderedElement { } } + fn preset_options(mut self, options: Vec<&str>) -> Self { + for opt in options { + self.props.insert(opt.to_owned(), None); + } + self + } + pub fn push(mut self, element: RenderedElement) -> Self { if let Some(els) = self.child_elements.as_mut() { els.push(element); @@ -43,18 +50,63 @@ impl RenderedElement { self } - pub fn option(mut self, prop: &'static str, value: &'static str) -> Self { - let prop_ref = self - .props - .entry(prop.to_owned()) - .or_insert(Some(value.to_owned())); - *prop_ref = Some(value.to_owned()); + pub fn option(mut self, option: &'static str, value: &'static str) -> Self { + self.props + .entry(option.to_owned()) + .and_modify(|opt| *opt = Some(value.to_owned())); self } } +impl std::fmt::Display for RenderedElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut has_props = false; + f.pad("")?; + f.write_fmt(format_args!("{:?}\n", self.name))?; + f.pad("")?; + f.write_str("Options: (")?; + for (k, v) in &self.props { + if let Some(value) = v { + has_props = true; + f.write_fmt(format_args!( + "\n{:width$.precision$}{}: {}", + "", + k, + value, + width = f.width().unwrap_or(0) + f.precision().unwrap_or(0), + precision = f.precision().unwrap_or(0) + ))?; + } + } + if has_props { + f.write_str("\n")?; + f.pad("")?; + } + f.write_str(")")?; + if let Some(els) = &self.child_elements { + f.write_str(" {\n")?; + for el in els { + f.write_fmt(format_args!( + "\n{:width$.precision$}\n", + el, + width = f.width().unwrap_or(0) + f.precision().unwrap_or(0), + precision = f.precision().unwrap_or(0) + ))?; + } + f.pad("")?; + f.write_str("}")?; + } + Ok(()) + } +} + pub fn text(text: &str) -> RenderedElement { - RenderedElement::new(ElementName::Text(text.to_owned())) + RenderedElement::new(ElementName::Text(text.to_owned())).preset_options(vec![ + "size", + "line_height", + "width", + "height", + ]) } pub fn button(text: &str) -> RenderedElement { -- cgit v1.2.3 From 510d68b92972b99868e187dd5340f04780b4c354 Mon Sep 17 00:00:00 2001 From: pml68 Date: Fri, 4 Oct 2024 00:44:02 +0200 Subject: feat: update to iced 0.13.1, basic project state file, prepare for drag&drop --- .gitignore | 1 + Cargo.lock | 1564 ++++++++++++++++++++-------- iced_builder/Cargo.toml | 5 +- iced_builder/src/codegen/mod.rs | 122 ++- iced_builder/src/lib.rs | 85 ++ iced_builder/src/main.rs | 292 +++--- iced_builder/src/types/mod.rs | 10 +- iced_builder/src/types/project/mod.rs | 59 ++ iced_builder/src/types/rendered_element.rs | 124 ++- iced_drop/Cargo.toml | 2 +- iced_drop/LICENSE | 21 + iced_drop/README.md | 2 + iced_drop/src/lib.rs | 24 +- iced_drop/src/widget/droppable.rs | 8 +- iced_drop/src/widget/operation/drop.rs | 5 +- 15 files changed, 1652 insertions(+), 672 deletions(-) create mode 100644 iced_builder/src/types/project/mod.rs create mode 100644 iced_drop/LICENSE diff --git a/.gitignore b/.gitignore index f4ceea7..9545d1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/target/ +**/*.json diff --git a/Cargo.lock b/Cargo.lock index baeab67..a341d35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.11" @@ -60,9 +71,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-activity" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", "bitflags 2.6.0", @@ -74,7 +85,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "thiserror", ] @@ -130,6 +141,182 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "ashpd" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe7e0dd0ac5a401dc116ed9f9119cf9decc625600474cb41f0fc0a0050abc9a" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "raw-window-handle", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + +[[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-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.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-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[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.79", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.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.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -138,9 +325,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" @@ -163,12 +350,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -234,31 +415,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] -name = "block-sys" -version = "0.2.1" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "objc-sys", + "generic-array", ] [[package]] name = "block2" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "block-sys", - "objc2 0.4.1", + "objc2", ] [[package]] -name = "block2" -version = "0.5.1" +name = "blocking" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "objc2 0.5.2", + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", ] [[package]] @@ -290,7 +474,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -305,20 +489,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" -[[package]] -name = "calloop" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" -dependencies = [ - "bitflags 2.6.0", - "log", - "polling", - "rustix", - "slab", - "thiserror", -] - [[package]] name = "calloop" version = "0.13.0" @@ -333,25 +503,13 @@ dependencies = [ "thiserror", ] -[[package]] -name = "calloop-wayland-source" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" -dependencies = [ - "calloop 0.12.4", - "rustix", - "wayland-backend", - "wayland-client", -] - [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop 0.13.0", + "calloop", "rustix", "wayland-backend", "wayland-client", @@ -359,9 +517,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", @@ -407,7 +565,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" dependencies = [ - "objc2 0.5.2", + "objc2", "objc2-app-kit", "objc2-foundation", ] @@ -573,25 +731,36 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.10.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75acbfb314aeb4f5210d379af45ed1ec2c98c7f1790bf57b8a4c562ac0c51b71" +checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" dependencies = [ - "fontdb", - "libm", + "bitflags 2.6.0", + "fontdb 0.16.2", "log", "rangemap", - "rustc-hash", - "rustybuzz 0.11.0", + "rayon", + "rustc-hash 1.1.0", + "rustybuzz", "self_cell", "swash", "sys-locale", + "ttf-parser 0.21.1", "unicode-bidi", "unicode-linebreak", "unicode-script", "unicode-segmentation", ] +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -632,6 +801,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctor-lite" version = "0.1.0" @@ -655,12 +834,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "dark-light" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a76fa97167fa740dcdbfe18e8895601e1bc36525f09b044e00916e717c03a3c" +dependencies = [ + "dconf_rs", + "detect-desktop-environment", + "dirs", + "objc", + "rust-ini", + "web-sys", + "winreg", + "zbus", +] + [[package]] name = "data-url" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +[[package]] +name = "dconf_rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" + [[package]] name = "deranged" version = "0.3.11" @@ -670,6 +871,42 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "detect-desktop-environment" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -685,12 +922,24 @@ dependencies = [ "libloading 0.8.5", ] +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + [[package]] name = "downcast-rs" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + [[package]] name = "drm" version = "0.12.0" @@ -736,6 +985,33 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -777,6 +1053,27 @@ dependencies = [ "num-traits", ] +[[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 = "exr" version = "1.72.0" @@ -807,18 +1104,18 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" dependencies = [ "simd-adler32", ] [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -866,21 +1163,35 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" dependencies = [ - "roxmltree 0.20.0", + "roxmltree", ] [[package]] name = "fontdb" -version = "0.15.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.8.0", + "memmap2", "slotmap", "tinyvec", - "ttf-parser 0.19.2", + "ttf-parser 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.21.1", ] [[package]] @@ -901,7 +1212,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -910,6 +1221,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.30" @@ -959,6 +1279,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" @@ -967,7 +1300,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1000,6 +1333,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -1021,16 +1364,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gif" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gif" version = "0.13.1" @@ -1085,18 +1418,6 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "glyphon" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a62d0338e4056db6a73221c2fb2e30619452f6ea9651bac4110f51b0f7a7581" -dependencies = [ - "cosmic-text", - "etagere", - "lru", - "wgpu", -] - [[package]] name = "gpu-alloc" version = "0.6.0" @@ -1137,7 +1458,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ "bitflags 2.6.0", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1169,16 +1490,31 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.11", "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hassle-rs" version = "0.11.0" @@ -1206,6 +1542,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -1214,9 +1556,9 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "iced" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d4eb0fbbefb8c428b70680e77ed9013887b17c1d6be366b40f264f956d1a096" +checksum = "88acfabc84ec077eaf9ede3457ffa3a104626d79022a9bf7f296093b1d60c73f" dependencies = [ "iced_core", "iced_futures", @@ -1230,12 +1572,13 @@ dependencies = [ [[package]] name = "iced_aw" -version = "0.9.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e68c330918a95bd73176206d65b84efe9aee6581da0e6dea0390cd146d7214c" +checksum = "42e05df3019f20c6decea93d035b32a2afc7b329d89cc5a68cca097d0e0a1889" dependencies = [ "cfg-if", "iced", + "iced_fonts", ] [[package]] @@ -1245,6 +1588,7 @@ dependencies = [ "iced", "iced_aw", "iced_drop", + "rfd", "rust-format", "serde", "serde_json", @@ -1254,20 +1598,22 @@ dependencies = [ [[package]] name = "iced_core" -version = "0.12.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7e6bbd197f311ed3d8b71651876b0ce01318fde52cda862a9a7a4373c9b930" +checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c" dependencies = [ "bitflags 2.6.0", + "bytes", + "dark-light", "glam", "log", "num-traits", + "once_cell", "palette", - "raw-window-handle", + "rustc-hash 2.0.0", "smol_str", "thiserror", "web-time", - "xxhash-rust", ] [[package]] @@ -1277,25 +1623,48 @@ dependencies = [ "iced", ] +[[package]] +name = "iced_fonts" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7deb0800a850ee25c8a42559f72c0f249e577feb3aad37b9b65dc1e517e52a" +dependencies = [ + "iced_core", +] + [[package]] name = "iced_futures" -version = "0.12.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370bad88fb3832cbeeb3fa6c486b4701fb7e8da32a753b3101d4ce81fc1d9497" +checksum = "0c04a6745ba2e80f32cf01e034fd00d853aa4f4cd8b91888099cb7aaee0d5d7c" dependencies = [ "futures", "iced_core", "log", + "rustc-hash 2.0.0", "tokio", "wasm-bindgen-futures", "wasm-timer", ] +[[package]] +name = "iced_glyphon" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c3bb56f1820ca252bc1d0994ece33d233a55657c0c263ea7cb16895adbde82" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.0.0", + "wgpu", +] + [[package]] name = "iced_graphics" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a044c193ef0840eacabfa05424717331d1fc5b3ecb9a89316200c75da2ba9a4" +checksum = "ba25a18cfa6d5cc160aca7e1b34f73ccdff21680fa8702168c09739767b6c66f" dependencies = [ "bitflags 2.6.0", "bytemuck", @@ -1309,17 +1678,16 @@ dependencies = [ "lyon_path", "once_cell", "raw-window-handle", - "rustc-hash", + "rustc-hash 2.0.0", "thiserror", "unicode-segmentation", - "xxhash-rust", ] [[package]] name = "iced_highlighter" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d27d1e5f526255ad4902907509f9ff9e98a40653dd31df36aba38d7c3905ad" +checksum = "bad88b25a1328cd4bb0b72d8e20f8207c0433649dc788f67e911423b9406f45c" dependencies = [ "iced_core", "once_cell", @@ -1328,9 +1696,9 @@ dependencies = [ [[package]] name = "iced_renderer" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c281e03001d566058f53dec9325bbe61c62da715341206d2627f57a3ecc7f69" +checksum = "73558208059f9e622df2bf434e044ee2f838ce75201a023cf0ca3e1244f46c2a" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -1341,32 +1709,22 @@ dependencies = [ [[package]] name = "iced_runtime" -version = "0.12.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a79f852c01cc6d61663c94379cb3974ac3ad315a28c504e847d573e094f46822" +checksum = "348b5b2c61c934d88ca3b0ed1ed913291e923d086a66fa288ce9669da9ef62b5" dependencies = [ + "bytes", "iced_core", "iced_futures", "raw-window-handle", "thiserror", ] -[[package]] -name = "iced_style" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea42a740915d2a5a9ff9c3aa0bca28b16e9fb660bc8f675eed71d186cadb579" -dependencies = [ - "iced_core", - "once_cell", - "palette", -] - [[package]] name = "iced_tiny_skia" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2228781f4d381a1cbbd7905a9f077351aa8d37269094021d5d9e779f130aff" +checksum = "c625d368284fcc43b0b36b176f76eff1abebe7959dd58bd8ce6897d641962a50" dependencies = [ "bytemuck", "cosmic-text", @@ -1374,59 +1732,64 @@ dependencies = [ "kurbo 0.10.4", "log", "resvg", - "rustc-hash", + "rustc-hash 2.0.0", "softbuffer", "tiny-skia", - "xxhash-rust", ] [[package]] name = "iced_wgpu" -version = "0.12.1" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c243b6700452886aac1ee1987e84d9fb43b56b53fea9a1eb67713fd0fde244" +checksum = "15708887133671d2bcc6c1d01d1f176f43a64d6cdc3b2bf893396c3ee498295f" dependencies = [ "bitflags 2.6.0", "bytemuck", "futures", "glam", - "glyphon", "guillotiere", + "iced_glyphon", "iced_graphics", "log", "lyon", "once_cell", "resvg", + "rustc-hash 2.0.0", + "thiserror", "wgpu", ] [[package]] name = "iced_widget" -version = "0.12.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01b2212adecf1cb80e2267f302c0e0c263e55f97812056949199ccf9f0b908" +checksum = "81429e1b950b0e4bca65be4c4278fea6678ea782030a411778f26fa9f8983e1d" dependencies = [ + "iced_highlighter", "iced_renderer", "iced_runtime", - "iced_style", "num-traits", + "once_cell", "qrcode", + "rustc-hash 2.0.0", "thiserror", "unicode-segmentation", ] [[package]] name = "iced_winit" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f66831d0e399b93f631739121a6171780d344b275d56808b9504d8ca75c7d2" +checksum = "f44cd4e1c594b6334f409282937bf972ba14d31fedf03c23aa595d982a2fda28" dependencies = [ + "iced_futures", "iced_graphics", "iced_runtime", - "iced_style", "log", + "rustc-hash 2.0.0", "thiserror", "tracing", + "wasm-bindgen-futures", "web-sys", "winapi", "window_clipboard", @@ -1434,14 +1797,13 @@ dependencies = [ ] [[package]] -name = "icrate" -version = "0.0.4" +name = "idna" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "block2 0.3.0", - "dispatch", - "objc2 0.4.1", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1454,7 +1816,7 @@ dependencies = [ "byteorder", "color_quant", "exr", - "gif 0.13.1", + "gif", "jpeg-decoder", "num-traits", "png", @@ -1470,12 +1832,12 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -1570,18 +1932,19 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kurbo" -version = "0.9.5" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" dependencies = [ "arrayvec", + "smallvec", ] [[package]] name = "kurbo" -version = "0.10.4" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" +checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" dependencies = [ "arrayvec", "smallvec", @@ -1601,9 +1964,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" @@ -1642,6 +2005,16 @@ dependencies = [ "redox_syscall 0.4.1", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1681,9 +2054,6 @@ name = "lru" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" -dependencies = [ - "hashbrown", -] [[package]] name = "lyon" @@ -1754,20 +2124,20 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] [[package]] -name = "memmap2" -version = "0.9.5" +name = "memoffset" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ - "libc", + "autocfg", ] [[package]] @@ -1792,7 +2162,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -1802,6 +2171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1823,7 +2193,7 @@ dependencies = [ "indexmap", "log", "num-traits", - "rustc-hash", + "rustc-hash 1.1.0", "spirv", "termcolor", "thiserror", @@ -1832,14 +2202,14 @@ dependencies = [ [[package]] name = "ndk" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ "bitflags 2.6.0", "jni-sys", "log", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "raw-window-handle", "thiserror", @@ -1860,6 +2230,28 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", + "memoffset", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1904,7 +2296,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1923,16 +2315,6 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" -[[package]] -name = "objc2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" -dependencies = [ - "objc-sys", - "objc2-encode 3.0.0", -] - [[package]] name = "objc2" version = "0.5.2" @@ -1940,7 +2322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ "objc-sys", - "objc2-encode 4.0.3", + "objc2-encode", ] [[package]] @@ -1950,15 +2332,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", + "block2", "libc", - "objc2 0.5.2", + "objc2", "objc2-core-data", "objc2-core-image", "objc2-foundation", "objc2-quartz-core", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -1966,8 +2372,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -1977,17 +2383,23 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] [[package]] -name = "objc2-encode" -version = "3.0.0" +name = "objc2-core-location" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] [[package]] name = "objc2-encode" @@ -2002,9 +2414,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", + "block2", + "dispatch", "libc", - "objc2 0.5.2", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", ] [[package]] @@ -2014,8 +2439,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -2026,12 +2451,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -2052,9 +2532,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "onig" @@ -2084,7 +2567,27 @@ version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "libredox", + "libredox 0.0.2", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[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]] @@ -2117,9 +2620,15 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.11.2" @@ -2163,7 +2672,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.4", + "redox_syscall 0.5.7", "smallvec", "windows-targets 0.52.6", ] @@ -2210,7 +2719,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2219,7 +2728,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] @@ -2228,6 +2737,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2240,11 +2769,22 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plist" @@ -2261,15 +2801,15 @@ dependencies = [ [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", ] [[package]] @@ -2287,12 +2827,33 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "presser" version = "0.3.1" @@ -2371,6 +2932,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", "rand_core", ] @@ -2379,6 +2952,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "range-alloc" @@ -2418,12 +2994,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "rctree" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" - [[package]] name = "read-fonts" version = "0.20.0" @@ -2445,36 +3015,38 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] -name = "redox_syscall" -version = "0.5.4" +name = "redox_users" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "bitflags 2.6.0", + "getrandom", + "libredox 0.1.3", + "thiserror", ] [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "renderdoc-sys" @@ -2484,21 +3056,42 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "resvg" -version = "0.36.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7980f653f9a7db31acff916a262c3b78c562919263edea29bf41a056e20497" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" dependencies = [ - "gif 0.12.0", + "gif", "jpeg-decoder", "log", "pico-args", - "png", "rgb", "svgtypes", "tiny-skia", "usvg", ] +[[package]] +name = "rfd" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" +dependencies = [ + "ashpd", + "block2", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rgb" version = "0.8.50" @@ -2508,15 +3101,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "roxmltree" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" -dependencies = [ - "xmlparser", -] - [[package]] name = "roxmltree" version = "0.20.0" @@ -2529,6 +3113,16 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60e7c00b6c3bf5e38a880eec01d7e829d12ca682079f8238a464def3c4b31627" +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2541,6 +3135,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustix" version = "0.38.37" @@ -2556,31 +3156,15 @@ dependencies = [ [[package]] name = "rustybuzz" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71cd15fef9112a1f94ac64b58d1e4628192631ad6af4dc69997f995459c874e7" -dependencies = [ - "bitflags 1.3.2", - "bytemuck", - "smallvec", - "ttf-parser 0.19.2", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "rustybuzz" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee8fe2a8461a0854a37101fe7a1b13998d0cfa987e43248e81d2a5f4570f6fa" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "bytemuck", "libm", "smallvec", - "ttf-parser 0.20.0", + "ttf-parser 0.21.1", "unicode-bidi-mirroring", "unicode-ccc", "unicode-properties", @@ -2616,14 +3200,14 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctk-adwaita" -version = "0.8.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.5", - "smithay-client-toolkit 0.18.1", + "memmap2", + "smithay-client-toolkit", "tiny-skia", ] @@ -2650,7 +3234,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2665,12 +3249,43 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -2692,6 +3307,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "skrifa" version = "0.20.0" @@ -2726,31 +3347,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smithay-client-toolkit" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" -dependencies = [ - "bitflags 2.6.0", - "calloop 0.12.4", - "calloop-wayland-source 0.2.0", - "cursor-icon", - "libc", - "log", - "memmap2 0.9.5", - "rustix", - "thiserror", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols 0.31.2", - "wayland-protocols-wlr 0.2.0", - "wayland-scanner", - "xkeysym", -] - [[package]] name = "smithay-client-toolkit" version = "0.19.2" @@ -2758,20 +3354,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.6.0", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", - "memmap2 0.9.5", + "memmap2", "rustix", "thiserror", "wayland-backend", "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.4", - "wayland-protocols-wlr 0.3.4", + "wayland-protocols", + "wayland-protocols-wlr", "wayland-scanner", "xkeysym", ] @@ -2783,7 +3379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" dependencies = [ "libc", - "smithay-client-toolkit 0.19.2", + "smithay-client-toolkit", "wayland-backend", ] @@ -2811,12 +3407,12 @@ dependencies = [ "foreign-types", "js-sys", "log", - "memmap2 0.9.5", - "objc2 0.5.2", + "memmap2", + "objc2", "objc2-foundation", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.4", + "redox_syscall 0.5.7", "rustix", "tiny-xlib", "wasm-bindgen", @@ -2869,12 +3465,12 @@ checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca" [[package]] name = "svgtypes" -version = "0.12.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71499ff2d42f59d26edb21369a308ede691421f79ebc0f001e2b1fd3a7c9e52" +checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" dependencies = [ - "kurbo 0.9.5", - "siphasher", + "kurbo 0.11.1", + "siphasher 1.0.1", ] [[package]] @@ -2901,9 +3497,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -2941,6 +3537,19 @@ dependencies = [ "libc", ] +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -2967,7 +3576,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3084,9 +3693,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", @@ -3112,7 +3721,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3126,15 +3735,15 @@ dependencies = [ [[package]] name = "ttf-parser" -version = "0.19.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "ttf-parser" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" [[package]] name = "ttf-parser" @@ -3142,23 +3751,40 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" +[[package]] +name = "typenum" +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 = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-bidi-mirroring" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" [[package]] name = "unicode-ccc" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" [[package]] name = "unicode-ident" @@ -3172,11 +3798,20 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-script" @@ -3220,64 +3855,48 @@ dependencies = [ ] [[package]] -name = "usvg" -version = "0.36.0" +name = "url" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51daa774fe9ee5efcf7b4fec13019b8119cda764d9a8b5b06df02bb1445c656" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ - "base64 0.21.7", - "log", - "pico-args", - "usvg-parser", - "usvg-text-layout", - "usvg-tree", - "xmlwriter", + "form_urlencoded", + "idna", + "percent-encoding", + "serde", ] [[package]] -name = "usvg-parser" -version = "0.36.0" +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "usvg" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c88a5ffaa338f0e978ecf3d4e00d8f9f493e29bed0752e1a808a1db16afc40" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" dependencies = [ + "base64 0.22.1", "data-url", "flate2", + "fontdb 0.18.0", "imagesize", - "kurbo 0.9.5", + "kurbo 0.11.1", "log", - "roxmltree 0.18.1", + "pico-args", + "roxmltree", + "rustybuzz", "simplecss", - "siphasher", + "siphasher 1.0.1", + "strict-num", "svgtypes", - "usvg-tree", -] - -[[package]] -name = "usvg-text-layout" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2374378cb7a3fb8f33894e0fdb8625e1bbc4f25312db8d91f862130b541593" -dependencies = [ - "fontdb", - "kurbo 0.9.5", - "log", - "rustybuzz 0.10.0", + "tiny-skia-path", "unicode-bidi", "unicode-script", "unicode-vo", - "usvg-tree", -] - -[[package]] -name = "usvg-tree" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cacb0c5edeaf3e80e5afcf5b0d4004cc1d36318befc9a7c6606507e5d0f4062" -dependencies = [ - "rctree", - "strict-num", - "svgtypes", - "tiny-skia-path", + "xmlwriter", ] [[package]] @@ -3333,7 +3952,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -3367,7 +3986,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3441,18 +4060,6 @@ dependencies = [ "xcursor", ] -[[package]] -name = "wayland-protocols" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", -] - [[package]] name = "wayland-protocols" version = "0.32.4" @@ -3467,27 +4074,14 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-protocols 0.31.2", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" dependencies = [ "bitflags 2.6.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", + "wayland-protocols", "wayland-scanner", ] @@ -3500,7 +4094,7 @@ dependencies = [ "bitflags 2.6.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.4", + "wayland-protocols", "wayland-scanner", ] @@ -3529,9 +4123,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -3539,9 +4133,9 @@ dependencies = [ [[package]] name = "web-time" -version = "0.2.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -3596,7 +4190,7 @@ dependencies = [ "parking_lot 0.12.3", "profiling", "raw-window-handle", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror", "web-sys", @@ -3632,7 +4226,7 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", "parking_lot 0.12.3", @@ -3640,7 +4234,7 @@ dependencies = [ "range-alloc", "raw-window-handle", "renderdoc-sys", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror", "wasm-bindgen", @@ -3946,47 +4540,51 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.29.15" +version = "0.30.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" dependencies = [ - "ahash", + "ahash 0.8.11", "android-activity", "atomic-waker", "bitflags 2.6.0", + "block2", "bytemuck", - "calloop 0.12.4", - "cfg_aliases 0.1.1", + "calloop", + "cfg_aliases 0.2.1", + "concurrent-queue", "core-foundation 0.9.4", "core-graphics 0.23.2", "cursor-icon", - "icrate", + "dpi", "js-sys", "libc", - "log", - "memmap2 0.9.5", + "memmap2", "ndk", - "ndk-sys", - "objc2 0.4.1", - "once_cell", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "orbclient", "percent-encoding", + "pin-project", "raw-window-handle", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "sctk-adwaita", - "smithay-client-toolkit 0.18.1", + "smithay-client-toolkit", "smol_str", + "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", + "wayland-protocols", "wayland-protocols-plasma", "web-sys", "web-time", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", "x11rb", "xkbcommon-dl", @@ -3994,13 +4592,22 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -4039,6 +4646,16 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -4064,24 +4681,12 @@ version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" -[[package]] -name = "xmlparser" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" - [[package]] name = "xmlwriter" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" -[[package]] -name = "xxhash-rust" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" - [[package]] name = "yaml-rust" version = "0.4.5" @@ -4097,6 +4702,68 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.79", + "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 = "zeno" version = "0.2.3" @@ -4109,6 +4776,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] @@ -4120,7 +4788,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4131,3 +4799,41 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.79", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml index 8106b09..a7b1e7b 100644 --- a/iced_builder/Cargo.toml +++ b/iced_builder/Cargo.toml @@ -9,12 +9,13 @@ license = "GPL-3.0-or-later" keywords = ["gui", "iced"] [dependencies] -iced = { version = "0.12.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } -iced_aw = { version = "0.9.3", default-features = false, features = ["menu","color_picker"] } +iced = { version = "0.13.1", features = [ "image","svg","canvas","qr_code","advanced","tokio","highlighter"] } +iced_aw = { version = "0.11.0", default-features = false, features = ["menu","color_picker"] } iced_drop = { path = "../iced_drop" } serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" tokio = { version = "1.40.0", features = ["fs"] } +rfd = "0.15.0" rust-format = "0.3.4" unique_id = "0.1.5" diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs index 927b6e4..38e8ec3 100644 --- a/iced_builder/src/codegen/mod.rs +++ b/iced_builder/src/codegen/mod.rs @@ -1,8 +1,12 @@ use rust_format::{Config, Edition, Formatter, RustFmt}; -use crate::types::{ - rendered_element::{container, row, svg, text, RenderedElement}, - ElementName, +use crate::{ + types::{ + project::Project, + rendered_element::{container, row, svg, text, RenderedElement}, + ElementName, + }, + Error, }; impl RenderedElement { @@ -10,8 +14,8 @@ impl RenderedElement { let mut props_string = String::new(); for (k, v) in self.props.clone() { - if let Some(value) = v { - props_string = format!("{props_string}.{k}({value})"); + if let Some(v) = v { + props_string = format!("{props_string}.{k}({v})"); } } @@ -81,70 +85,62 @@ impl RenderedElement { (imports, view) } - pub fn app_code( - &self, - title: &str, - theme: Option, - ) -> Result> { - let (imports, view) = self.codegen(); - let mut app_code = format!("use iced::{{widget::{{{imports}}},Sandbox,Settings,Element}};"); - - app_code = format!( - r#"// Automatically generated by iced Builder - {app_code} - - fn main() -> iced::Result {{ - App::run(Settings::default()) - }} - - struct App; - - impl Sandbox for App {{ - type Message = (); - - fn new() -> Self {{ - Self {{}} - }} - - fn title(&self) -> String {{ - "{title}".into() - }} - - fn theme(&self) -> iced::Theme {{ - iced::Theme::{} - }} - - fn update(&mut self, message: Self::Message) {{ - - }} - - fn view(&self) -> Element {{ - {view}.into() - }} - }}"#, - if let Some(c) = theme { - c.to_string().replace(' ', "") - } else { - "default()".to_owned() - } - ); - let config = Config::new_str() - .edition(Edition::Rust2021) - .option("trailing_comma", "Never") - .option("imports_granularity", "Crate"); - let rustfmt = RustFmt::from_config(config); - Ok(rustfmt.format_str(app_code)?) - } - pub fn test() -> RenderedElement { - let text1 = text("wow").option("height", "120.5").option("width", "230"); + let mut text1 = text("wow"); + text1.option("height", "120.5"); + text1.option("width", "230"); - let element = container(row(vec![ + let element = container(Some(row(Some(vec![ text1, text("heh"), svg("/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg"), - ])); + ])))); element } } + +impl Project { + pub fn app_code(self) -> Result { + match &self.content { + Some(el) => { + let (imports, view) = el.codegen(); + let mut app_code = format!("use iced::{{widget::{{{imports}}},Element}};"); + + app_code = format!( + r#"// Automatically generated by iced Builder + {app_code} + + fn main() -> iced::Result {{ + iced::run("{}", State::update, State::view) + }} + + #[derive(Default)] + struct State; + + #[derive(Debug, Clone)] + enum Message {{}} + + impl State {{ + fn update(&mut self, _message: Message) {{}} + + fn view(&self) -> Element {{ + {view}.into() + }} + }}"#, + match &self.title { + Some(t) => t, + None => "New app", + } + ); + let config = Config::new_str() + .edition(Edition::Rust2021) + .option("trailing_comma", "Never") + .option("imports_granularity", "Crate"); + let rustfmt = RustFmt::from_config(config); + Ok(rustfmt.format_str(app_code)?) + } + None => Err("No element tree present".into()), + } + } +} diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs index e69de29..971e0e3 100644 --- a/iced_builder/src/lib.rs +++ b/iced_builder/src/lib.rs @@ -0,0 +1,85 @@ +pub mod codegen; +pub mod types; + +use std::path::PathBuf; + +use iced::widget::{pane_grid, text_editor}; +use types::{project::Project, rendered_element::RenderedElement, DesignerPage}; + +#[derive(Debug, Clone)] +pub enum Error { + IOError(std::io::ErrorKind), + SerdeError(String), + FormatError(String), + DialogClosed, + String(String), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SerdeError(string) | Self::FormatError(string) | Self::String(string) => { + write!(f, "{}", string) + } + Self::IOError(kind) => { + write!(f, "{}", kind) + } + Self::DialogClosed => { + write!( + f, + "The file dialog has been closed without selecting a valid option." + ) + } + } + } +} + +impl From for Error { + fn from(value: std::io::Error) -> Self { + Self::IOError(value.kind()) + } +} + +impl From for Error { + fn from(value: serde_json::Error) -> Self { + Self::SerdeError(value.to_string()) + } +} + +impl From for Error { + fn from(value: rust_format::Error) -> Self { + Self::FormatError(value.to_string()) + } +} + +impl From<&'static str> for Error { + fn from(value: &'static str) -> Self { + Self::String(value.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub enum Message { + ToggleTheme, + CopyCode, + SwitchPage(DesignerPage), + EditorAction(text_editor::Action), + DropNewElement(types::ElementName, iced::Point, iced::Rectangle), + HandleNew( + types::ElementName, + Vec<(iced::advanced::widget::Id, iced::Rectangle)>, + ), + MoveElement(RenderedElement, iced::Point, iced::Rectangle), + HandleMove( + RenderedElement, + Vec<(iced::advanced::widget::Id, iced::Rectangle)>, + ), + Resized(pane_grid::ResizeEvent), + Clicked(pane_grid::Pane), + PaneDragged(pane_grid::DragEvent), + NewFile, + OpenFile, + FileOpened(Result<(PathBuf, Project), Error>), + SaveFile, + FileSaved(Result), +} diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index f0f58cc..aeb5ea6 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -1,67 +1,50 @@ -mod codegen; -mod types; +use std::path::PathBuf; use iced::{ - clipboard, executor, - highlighter::{self, Highlighter}, - theme, + clipboard, highlighter, keyboard, widget::{ button, column, container, pane_grid::{self, Pane, PaneGrid}, row, text, text_editor, tooltip, Column, Space, }, - Alignment, Application, Color, Command, Element, Font, Length, Settings, + Alignment, Element, Font, Length, Settings, Task, Theme, }; +use iced_builder::types::{project::Project, DesignerPage, ElementName}; +use iced_builder::Message; use iced_drop::droppable; -use types::{rendered_element::RenderedElement, DesignerPage, DesignerState}; fn main() -> iced::Result { - App::run(Settings { - fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], - ..Settings::default() - }) + iced::application(App::title, App::update, App::view) + .settings(Settings { + fonts: vec![include_bytes!("../fonts/icons.ttf").as_slice().into()], + ..Settings::default() + }) + .theme(App::theme) + .subscription(App::subscription) + .run_with(App::new) } struct App { - is_saved: bool, - current_project: Option, + is_dirty: bool, + is_loading: bool, + project_path: Option, + project: Project, dark_theme: bool, pane_state: pane_grid::State, focus: Option, - designer_state: DesignerState, - element_list: Vec, + designer_page: DesignerPage, + element_list: Vec, editor_content: text_editor::Content, } -#[derive(Debug, Clone)] -enum Message { - ToggleTheme, - CopyCode, - SwitchPage(DesignerPage), - EditorAction(text_editor::Action), - Drop(types::ElementName, iced::Point, iced::Rectangle), - HandleZones( - types::ElementName, - Vec<(iced::advanced::widget::Id, iced::Rectangle)>, - ), - Resized(pane_grid::ResizeEvent), - Clicked(pane_grid::Pane), - PaneDragged(pane_grid::DragEvent), -} - #[derive(Clone, Debug)] enum Panes { Designer, ElementList, } -impl Application for App { - type Message = Message; - type Theme = theme::Theme; - type Executor = executor::Default; - type Flags = (); - - fn new(_flags: ()) -> (Self, Command) { +impl App { + fn new() -> (Self, Task) { let state = pane_grid::State::with_configuration(pane_grid::Configuration::Split { axis: pane_grid::Axis::Vertical, ratio: 0.8, @@ -70,26 +53,25 @@ impl Application for App { }); ( Self { - is_saved: true, - current_project: None, + is_dirty: false, + is_loading: false, + project_path: None, + project: Project::new(), dark_theme: true, pane_state: state, focus: None, - designer_state: DesignerState { - designer_content: Some(RenderedElement::test()), - designer_page: DesignerPage::Designer, - }, - element_list: types::ElementName::ALL.to_vec(), + designer_page: DesignerPage::Designer, + element_list: ElementName::ALL.to_vec(), editor_content: text_editor::Content::new(), }, - Command::none(), + Task::none(), ) } fn title(&self) -> String { - let saved_state = if self.is_saved { "" } else { " *" }; + let saved_state = if !self.is_dirty { "" } else { " *" }; - let project_name = match &self.current_project { + let project_name = match &self.project.title { Some(n) => { format!( " - {}", @@ -108,17 +90,17 @@ impl Application for App { fn theme(&self) -> iced::Theme { if self.dark_theme { - theme::Theme::CatppuccinMocha + Theme::SolarizedDark } else { - theme::Theme::CatppuccinLatte + Theme::SolarizedLight } } - fn update(&mut self, message: Message) -> Command { + fn update(&mut self, message: Message) -> Task { match message { Message::ToggleTheme => self.dark_theme = !self.dark_theme, Message::CopyCode => return clipboard::write(self.editor_content.text()), - Message::SwitchPage(page) => self.designer_state.designer_page = page, + Message::SwitchPage(page) => self.designer_page = page, Message::EditorAction(action) => { if let text_editor::Action::Scroll { lines: _ } = action { self.editor_content.perform(action); @@ -130,38 +112,103 @@ impl Application for App { Message::Clicked(pane) => { self.focus = Some(pane); } - Message::Drop(name, point, _) => { + Message::DropNewElement(name, point, _) => { return iced_drop::zones_on_point( - move |zones| Message::HandleZones(name.clone(), zones), + move |zones| Message::HandleNew(name.clone(), zones), point, None, None, ) .into() } - Message::HandleZones(name, zones) => { - println!("{:?}\n{name}", zones); - println!("{:?}\n{name}\n{:?}", zones, self.title()); - if let Some(el) = &self.designer_state.designer_content { - self.editor_content = text_editor::Content::with_text( - &el.app_code( - match &self.current_project { - Some(title) => &title, - None => "New App", - }, - None, - ) - .unwrap(), - ); - } + Message::HandleNew(name, zones) => { + println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); + let code = self + .project + .clone() + .app_code() + .unwrap_or_else(|err| err.to_string()); + self.editor_content = text_editor::Content::with_text(&code); + } + Message::MoveElement(element, point, _) => { + return iced_drop::zones_on_point( + move |zones| Message::HandleMove(element.clone(), zones), + point, + None, + None, + ) + .into() + } + Message::HandleMove(element, zones) => { + println!( + "\n\n{:?}\n{element:0.4}", + zones + .into_iter() + .map(|c| c.0) + .collect::>() + ); } Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { self.pane_state.drop(pane, target); } Message::PaneDragged(_) => {} + Message::NewFile => { + if !self.is_loading { + self.project = Project::new(); + self.project_path = None; + self.editor_content = text_editor::Content::new(); + } + } + Message::OpenFile => { + if !self.is_loading { + self.is_loading = true; + + return Task::perform(Project::from_file(), Message::FileOpened); + } + } + Message::FileOpened(result) => { + self.is_loading = false; + self.is_dirty = false; + + if let Ok((path, project)) = result { + self.project = project.clone(); + self.project_path = Some(path); + self.editor_content = text_editor::Content::with_text( + &project.app_code().unwrap_or_else(|err| err.to_string()), + ); + } + } + Message::SaveFile => { + if !self.is_loading { + self.is_loading = true; + + return Task::perform( + self.project + .clone() + .write_to_file(self.project_path.clone()), + Message::FileSaved, + ); + } + } + Message::FileSaved(result) => { + self.is_loading = false; + + if let Ok(path) = result { + self.project_path = Some(path); + self.is_dirty = false; + } + } } - Command::none() + Task::none() + } + + fn subscription(&self) -> iced::Subscription { + keyboard::on_key_press(|key, modifiers| match key.as_ref() { + keyboard::Key::Character("o") if modifiers.command() => Some(Message::OpenFile), + keyboard::Key::Character("s") if modifiers.command() => Some(Message::SaveFile), + _ => None, + }) } fn view(&self) -> Element { @@ -172,28 +219,23 @@ impl Application for App { let pane_grid = PaneGrid::new(&self.pane_state, |id, pane, _is_maximized| { let is_focused = Some(id) == self.focus; match pane { - Panes::Designer => match &self.designer_state.designer_page { + Panes::Designer => match &self.designer_page { DesignerPage::Designer => { - let content = container(text(format!( - "{:0.4}", - self.designer_state - .designer_content - .clone() - .expect("Designer content hasn't been set yet."), - ))) - .id(iced::widget::container::Id::new("drop_zone")) - .height(Length::Fill) - .width(Length::Fill); + let el_tree = match self.project.content.clone() { + Some(tree) => tree.as_element(), + None => text("Open a project or begin creating one").into(), + }; + let content = container(el_tree) + .id(iced::widget::container::Id::new("drop_zone")) + .height(Length::Fill) + .width(Length::Fill); let title = row![ - text("Designer").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }), + text("Designer"), Space::with_width(Length::Fill), button("Switch to Code view") .on_press(Message::SwitchPage(DesignerPage::CodeView)), - ]; + ] + .align_y(Alignment::Center); let title_bar = pane_grid::TitleBar::new(title) .padding(10) .style(style::title_bar); @@ -207,44 +249,37 @@ impl Application for App { } DesignerPage::CodeView => { let title = row![ - text("Generated Code").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }), + text("Generated Code"), Space::with_width(Length::Fill), tooltip( button( container( text('\u{0e801}').font(Font::with_name("editor-icons")) ) - .width(30) - .center_x() + .center_x(30) ) .on_press(Message::CopyCode), "Copy code to clipboard", - tooltip::Position::Left + tooltip::Position::FollowCursor ), Space::with_width(20), button("Switch to Designer view") .on_press(Message::SwitchPage(DesignerPage::Designer)) - ]; + ] + .align_y(Alignment::Center); let title_bar = pane_grid::TitleBar::new(title) .padding(10) .style(style::title_bar); pane_grid::Content::new( text_editor(&self.editor_content) .on_action(Message::EditorAction) - .highlight::( - highlighter::Settings { - theme: if self.dark_theme { - highlighter::Theme::Base16Mocha - } else { - highlighter::Theme::InspiredGitHub - }, - extension: "rs".to_string(), + .highlight( + "rs", + if self.dark_theme { + highlighter::Theme::SolarizedDark + } else { + highlighter::Theme::InspiredGitHub }, - |highlight, _theme| highlight.to_format(), ) .height(Length::Fill) .padding(20), @@ -260,14 +295,10 @@ impl Application for App { Panes::ElementList => { let items_list = items_list_view(self.element_list.clone()); let content = column![items_list] - .align_items(Alignment::Center) + .align_x(Alignment::Center) .height(Length::Fill) .width(Length::Fill); - let title = text("Element List").style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }); + let title = text("Element List"); let title_bar = pane_grid::TitleBar::new(title) .padding(10) .style(style::title_bar); @@ -292,39 +323,24 @@ impl Application for App { .push(header) .push(pane_grid) .spacing(5) - .align_items(Alignment::Center) + .align_x(Alignment::Center) .width(Length::Fill); container(content).height(Length::Fill).into() } } -const fn from_grayscale(grayscale: f32) -> Color { - Color { - r: grayscale, - g: grayscale, - b: grayscale, - a: 1.0, - } -} - -// #ffffff -const PANE_ID_COLOR_FOCUSED: Color = from_grayscale(1.0); - -// #e8e8e8 -const PANE_ID_COLOR_UNFOCUSED: Color = from_grayscale(0xE8 as f32 / 255.0); - -fn items_list_view(items: Vec) -> Element<'static, Message> { +fn items_list_view<'a>(items: Vec) -> Element<'a, Message> { let mut column = Column::new() .spacing(20) - .align_items(Alignment::Center) + .align_x(Alignment::Center) .width(Length::Fill); for item in items { let value = item.clone(); column = column.push( droppable(text(value.to_string())) - .on_drop(move |point, rect| Message::Drop(value.clone(), point, rect)), + .on_drop(move |point, rect| Message::DropNewElement(value.clone(), point, rect)), ); } @@ -332,23 +348,23 @@ fn items_list_view(items: Vec) -> Element<'static, Message> } mod style { - use iced::widget::container; - use iced::{Border, Theme}; + use iced::widget::{container::Style as CStyle, text::Style as TStyle}; + use iced::{color, Border, Theme}; - pub fn title_bar(theme: &Theme) -> container::Appearance { + pub fn title_bar(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); - container::Appearance { + CStyle { text_color: Some(palette.background.strong.text), background: Some(palette.background.strong.color.into()), ..Default::default() } } - pub fn pane_active(theme: &Theme) -> container::Appearance { + pub fn pane_active(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); - container::Appearance { + CStyle { background: Some(palette.background.weak.color.into()), border: Border { width: 1.0, @@ -359,10 +375,10 @@ mod style { } } - pub fn pane_focused(theme: &Theme) -> container::Appearance { + pub fn pane_focused(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); - container::Appearance { + CStyle { background: Some(palette.background.weak.color.into()), border: Border { width: 4.0, diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index 344f543..2d6cd8f 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -1,15 +1,9 @@ +pub mod project; pub mod rendered_element; -use rendered_element::RenderedElement; use serde::{Deserialize, Serialize}; -#[derive(Clone)] -pub struct DesignerState { - pub designer_content: Option, - pub designer_page: DesignerPage, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ElementName { Text(String), Button(String), diff --git a/iced_builder/src/types/project/mod.rs b/iced_builder/src/types/project/mod.rs new file mode 100644 index 0000000..557fa92 --- /dev/null +++ b/iced_builder/src/types/project/mod.rs @@ -0,0 +1,59 @@ +use std::path::{Path, PathBuf}; + +use serde::{Deserialize, Serialize}; + +use crate::Error; + +use super::rendered_element::RenderedElement; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Project { + pub title: Option, + pub content: Option, +} + +impl Project { + pub fn new() -> Self { + Self { + title: None, + content: None, + } + } + + pub async fn from_file() -> Result<(PathBuf, Self), Error> { + let picked_file = rfd::AsyncFileDialog::new() + .set_title("Open a JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .pick_file() + .await + .ok_or(Error::DialogClosed)?; + + let path = picked_file.path().to_owned(); + + let contents = tokio::fs::read_to_string(&path).await?; + let element: Self = serde_json::from_str(&contents)?; + + Ok((path, element)) + } + + pub async fn write_to_file(self, path: Option) -> Result { + let path = if let Some(p) = path { + p + } else { + rfd::AsyncFileDialog::new() + .set_title("Save to JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .save_file() + .await + .as_ref() + .map(rfd::FileHandle::path) + .map(Path::to_owned) + .ok_or(Error::DialogClosed)? + }; + + let contents = serde_json::to_string(&self.clone())?; + tokio::fs::write(&path, contents).await?; + + Ok(path) + } +} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 2d5c81a..967352b 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -1,11 +1,15 @@ use std::collections::HashMap; +use iced::advanced::widget::Id; +use iced::{widget, Element, Length}; use serde::{Deserialize, Serialize}; use unique_id::{string::StringGenerator, Generator}; +use crate::Message; + use super::ElementName; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderedElement { pub id: String, pub child_elements: Option>, @@ -34,27 +38,104 @@ impl RenderedElement { } } - fn preset_options(mut self, options: Vec<&str>) -> Self { - for opt in options { - self.props.insert(opt.to_owned(), None); + pub fn find_by_id(&mut self, id: Id) -> Option<&mut Self> { + if Id::new(self.id.clone()) == id.clone() { + println!(""); + return Some(self); + } else if let Some(child_elements) = self.child_elements.as_mut() { + for element in child_elements { + let element = element.find_by_id(id.clone()); + if element.is_some() { + return element; + } + } + return None; + } else { + return None; } - self } - pub fn push(mut self, element: RenderedElement) -> Self { - if let Some(els) = self.child_elements.as_mut() { - els.push(element); + pub fn find_parent(&mut self, child_element: &RenderedElement) -> Option<&mut Self> { + if child_element == self { + return Some(self); + } else if self.child_elements.is_some() { + if self.child_elements.clone()?.contains(child_element) { + return Some(self); + } else { + if let Some(child_elements) = self.child_elements.as_mut() { + for element in child_elements { + let element: Option<&mut Self> = element.find_parent(child_element); + if element.is_some() { + return element; + } + } + } + return None; + } } else { - self.child_elements = Some(vec![element]); + return None; + } + } + + pub fn remove(&mut self, element: &RenderedElement) { + let parent = self.find_parent(element); + if let Some(child_elements) = parent.unwrap().child_elements.as_mut() { + if let Some(index) = child_elements.iter().position(|x| x == element) { + child_elements.remove(index); + } + } + } + + pub fn push(&mut self, element: RenderedElement) { + if let Some(child_elements) = self.child_elements.as_mut() { + child_elements.push(element); + } + } + + pub fn insert_after(&mut self, id: Id, element: RenderedElement) { + if let Some(child_elements) = self.child_elements.as_mut() { + if let Some(index) = child_elements + .iter() + .position(|x| Id::new(x.id.clone()) == id) + { + child_elements.insert(index, element); + } else { + child_elements.push(element); + } + } + } + + fn preset_options(mut self, options: Vec<&str>) -> Self { + for opt in options { + self.props.insert(opt.to_owned(), None); } self } - pub fn option(mut self, option: &'static str, value: &'static str) -> Self { + pub fn option(&mut self, option: &'static str, value: &'static str) { self.props .entry(option.to_owned()) .and_modify(|opt| *opt = Some(value.to_owned())); - self + } + + pub fn as_element(self) -> Element<'static, Message> { + let mut children = widget::column![]; + + if let Some(els) = self.child_elements.clone() { + for el in els { + children = children.push(el.clone().as_element()); + } + } + iced_drop::droppable( + widget::container( + widget::column![widget::text(self.name.clone().to_string()), children] + .width(Length::Fill), + ) + .style(widget::container::bordered_box), + ) + .id(Id::new(self.id.clone())) + .on_drop(move |point, rect| Message::MoveElement(self.clone(), point, rect)) + .into() } } @@ -121,10 +202,23 @@ pub fn image(path: &str) -> RenderedElement { RenderedElement::new(ElementName::Image(path.to_owned())) } -pub fn container(content: RenderedElement) -> RenderedElement { - RenderedElement::from_vec(ElementName::Container, vec![content]) +pub fn container(content: Option) -> RenderedElement { + match content { + Some(el) => RenderedElement::from_vec(ElementName::Container, vec![el]), + None => RenderedElement::from_vec(ElementName::Container, vec![]), + } } -pub fn row(child_elements: Vec) -> RenderedElement { - RenderedElement::from_vec(ElementName::Row, child_elements) +pub fn row(child_elements: Option>) -> RenderedElement { + match child_elements { + Some(els) => RenderedElement::from_vec(ElementName::Row, els), + None => RenderedElement::from_vec(ElementName::Row, vec![]), + } +} + +pub fn column(child_elements: Option>) -> RenderedElement { + match child_elements { + Some(els) => RenderedElement::from_vec(ElementName::Column, els), + None => RenderedElement::from_vec(ElementName::Column, vec![]), + } } diff --git a/iced_drop/Cargo.toml b/iced_drop/Cargo.toml index 8b9d88c..40beeb0 100644 --- a/iced_drop/Cargo.toml +++ b/iced_drop/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies.iced] -version = "0.12.1" +version = "0.13.1" features = ["advanced"] diff --git a/iced_drop/LICENSE b/iced_drop/LICENSE new file mode 100644 index 0000000..89d9fee --- /dev/null +++ b/iced_drop/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 jhannyj + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/iced_drop/README.md b/iced_drop/README.md index 41b637b..3768d54 100644 --- a/iced_drop/README.md +++ b/iced_drop/README.md @@ -1,5 +1,7 @@ # iced_drop +# Modified to use iced 0.13.1 for compatibility + A small library which provides a custom widget and operation to make drag and drop easier to implement in [iced](https://github.com/iced-rs/iced/tree/master) ## Usage diff --git a/iced_drop/src/lib.rs b/iced_drop/src/lib.rs index fc559dc..9906cbe 100644 --- a/iced_drop/src/lib.rs +++ b/iced_drop/src/lib.rs @@ -1,8 +1,10 @@ pub mod widget; use iced::{ - advanced::{graphics::futures::MaybeSend, renderer, widget::Id}, - Command, Element, Point, Rectangle, + advanced::widget::{operate, Id}, + advanced::{graphics::futures::MaybeSend, renderer}, + task::Task, + Element, Point, Rectangle, }; use widget::droppable::*; @@ -18,17 +20,17 @@ where Droppable::new(content) } -pub fn zones_on_point( +pub fn zones_on_point( msg: MF, point: Point, options: Option>, depth: Option, -) -> Command +) -> Task where - Message: 'static, - MF: Fn(Vec<(Id, Rectangle)>) -> Message + MaybeSend + Sync + Clone + 'static, + T: Send + 'static, + MF: Fn(Vec<(Id, Rectangle)>) -> T + MaybeSend + Sync + Clone + 'static, { - Command::widget(drop::find_zones( + operate(drop::find_zones( move |bounds| bounds.contains(point), options, depth, @@ -41,11 +43,11 @@ pub fn find_zones( filter: F, options: Option>, depth: Option, -) -> Command +) -> Task where - Message: 'static, + Message: Send + 'static, MF: Fn(Vec<(Id, Rectangle)>) -> Message + MaybeSend + Sync + Clone + 'static, - F: Fn(&Rectangle) -> bool + 'static, + F: Fn(&Rectangle) -> bool + Send + 'static, { - Command::widget(drop::find_zones(filter, options, depth)).map(move |id| msg(id)) + operate(drop::find_zones(filter, options, depth)).map(move |id| msg(id)) } diff --git a/iced_drop/src/widget/droppable.rs b/iced_drop/src/widget/droppable.rs index ed7dcbd..80d8600 100644 --- a/iced_drop/src/widget/droppable.rs +++ b/iced_drop/src/widget/droppable.rs @@ -228,8 +228,10 @@ where state.action = Action::Drag(start, position); // update the position of the overlay since the cursor was moved if self.drag_center { - state.overlay_bounds.x = position.x - state.overlay_bounds.width / 2.0; - state.overlay_bounds.y = position.y - state.overlay_bounds.height / 2.0; + state.overlay_bounds.x = + position.x - state.overlay_bounds.width / 2.0; + state.overlay_bounds.y = + position.y - state.overlay_bounds.height / 2.0; } else { state.overlay_bounds.x = state.widget_pos.x + position.x - start.x; state.overlay_bounds.y = state.widget_pos.y + position.y - start.y; @@ -315,7 +317,7 @@ where tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, - operation: &mut dyn Operation, + operation: &mut dyn Operation, ) { let state = tree.state.downcast_mut::(); operation.custom(state, self.id.as_ref()); diff --git a/iced_drop/src/widget/operation/drop.rs b/iced_drop/src/widget/operation/drop.rs index 12a2e30..a76181c 100644 --- a/iced_drop/src/widget/operation/drop.rs +++ b/iced_drop/src/widget/operation/drop.rs @@ -17,7 +17,7 @@ pub fn find_zones( depth: Option, ) -> impl Operation> where - F: Fn(&Rectangle) -> bool + 'static, + F: Fn(&Rectangle) -> bool + Send + 'static, { struct FindDropZone { filter: F, @@ -30,7 +30,7 @@ where impl Operation> for FindDropZone where - F: Fn(&Rectangle) -> bool + 'static, + F: Fn(&Rectangle) -> bool + Send + 'static, { fn container( &mut self, @@ -70,6 +70,7 @@ where _state: &mut dyn Scrollable, _id: Option<&Id>, bounds: Rectangle, + _content_bounds: Rectangle, translation: Vector, ) { if (self.filter)(&bounds) { -- cgit v1.2.3 From 7d241eb768a3c56d98f5d41183866fffb9ff167a Mon Sep 17 00:00:00 2001 From: pml68 Date: Sun, 20 Oct 2024 11:51:54 +0200 Subject: feat: implement d&d for new elements `ActionKind` enum to help generalize d&d actions removed `codegen` module small refactors --- Cargo.lock | 2 + iced_builder/Cargo.toml | 1 + iced_builder/src/codegen/mod.rs | 146 -------------------- iced_builder/src/lib.rs | 17 ++- iced_builder/src/main.rs | 68 +++++++--- iced_builder/src/types/element_name.rs | 90 +++++++++++++ iced_builder/src/types/mod.rs | 44 +----- iced_builder/src/types/project.rs | 137 +++++++++++++++++++ iced_builder/src/types/project/mod.rs | 59 --------- iced_builder/src/types/rendered_element.rs | 206 +++++++++++++++++++++++++---- 10 files changed, 467 insertions(+), 303 deletions(-) delete mode 100644 iced_builder/src/codegen/mod.rs create mode 100644 iced_builder/src/types/element_name.rs create mode 100644 iced_builder/src/types/project.rs delete mode 100644 iced_builder/src/types/project/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a341d35..d65a742 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1588,6 +1588,7 @@ dependencies = [ "iced", "iced_aw", "iced_drop", + "indexmap", "rfd", "rust-format", "serde", @@ -1838,6 +1839,7 @@ checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown 0.15.0", + "serde", ] [[package]] diff --git a/iced_builder/Cargo.toml b/iced_builder/Cargo.toml index a7b1e7b..d788bc2 100644 --- a/iced_builder/Cargo.toml +++ b/iced_builder/Cargo.toml @@ -18,6 +18,7 @@ tokio = { version = "1.40.0", features = ["fs"] } rfd = "0.15.0" rust-format = "0.3.4" unique_id = "0.1.5" +indexmap = { version = "2.6.0", features = ["serde"] } [[bin]] name = "iced-builder" diff --git a/iced_builder/src/codegen/mod.rs b/iced_builder/src/codegen/mod.rs deleted file mode 100644 index 38e8ec3..0000000 --- a/iced_builder/src/codegen/mod.rs +++ /dev/null @@ -1,146 +0,0 @@ -use rust_format::{Config, Edition, Formatter, RustFmt}; - -use crate::{ - types::{ - project::Project, - rendered_element::{container, row, svg, text, RenderedElement}, - ElementName, - }, - Error, -}; - -impl RenderedElement { - fn props_codegen(&self) -> String { - let mut props_string = String::new(); - - for (k, v) in self.props.clone() { - if let Some(v) = v { - props_string = format!("{props_string}.{k}({v})"); - } - } - - props_string - } - - fn codegen(&self) -> (String, String) { - let mut imports = String::new(); - let mut view = String::new(); - let props = self.props_codegen(); - - let mut elements = String::new(); - - if let Some(els) = &self.child_elements { - for element in els { - let (c_imports, children) = element.codegen(); - imports = format!("{imports}{c_imports}"); - elements = format!("{elements}{},", children); - } - } - - match &self.name { - ElementName::Container => { - imports = format!("{imports}container,"); - view = format!("{view}\ncontainer({elements}){props}"); - } - ElementName::Row => { - imports = format!("{imports}row,"); - view = format!("{view}\nrow![{elements}]{props}"); - } - ElementName::Column => { - imports = format!("{imports}column,"); - view = format!("{view}\ncolumn![{elements}]{props}"); - } - ElementName::Text(string) => { - imports = format!("{imports}text,"); - view = format!( - "{view}\ntext(\"{}\"){props}", - if *string == String::new() { - "New Text" - } else { - string - } - ); - } - ElementName::Button(string) => { - imports = format!("{imports}button,"); - view = format!( - "{view}\nbutton(\"{}\"){props}", - if *string == String::new() { - "New Button" - } else { - string - } - ); - } - ElementName::Image(path) => { - imports = format!("{imports}image,"); - view = format!("{view}\nimage(\"{path}\"){props}"); - } - ElementName::SVG(path) => { - imports = format!("{imports}svg,"); - view = format!("{view}\nsvg(\"{path}\"){props}"); - } - } - - (imports, view) - } - - pub fn test() -> RenderedElement { - let mut text1 = text("wow"); - text1.option("height", "120.5"); - text1.option("width", "230"); - - let element = container(Some(row(Some(vec![ - text1, - text("heh"), - svg("/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg"), - ])))); - - element - } -} - -impl Project { - pub fn app_code(self) -> Result { - match &self.content { - Some(el) => { - let (imports, view) = el.codegen(); - let mut app_code = format!("use iced::{{widget::{{{imports}}},Element}};"); - - app_code = format!( - r#"// Automatically generated by iced Builder - {app_code} - - fn main() -> iced::Result {{ - iced::run("{}", State::update, State::view) - }} - - #[derive(Default)] - struct State; - - #[derive(Debug, Clone)] - enum Message {{}} - - impl State {{ - fn update(&mut self, _message: Message) {{}} - - fn view(&self) -> Element {{ - {view}.into() - }} - }}"#, - match &self.title { - Some(t) => t, - None => "New app", - } - ); - let config = Config::new_str() - .edition(Edition::Rust2021) - .option("trailing_comma", "Never") - .option("imports_granularity", "Crate"); - let rustfmt = RustFmt::from_config(config); - Ok(rustfmt.format_str(app_code)?) - } - None => Err("No element tree present".into()), - } - } -} diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs index 971e0e3..6de9ba8 100644 --- a/iced_builder/src/lib.rs +++ b/iced_builder/src/lib.rs @@ -1,16 +1,18 @@ -pub mod codegen; pub mod types; use std::path::PathBuf; use iced::widget::{pane_grid, text_editor}; -use types::{project::Project, rendered_element::RenderedElement, DesignerPage}; +use types::{ + element_name::ElementName, project::Project, rendered_element::RenderedElement, DesignerPage, +}; #[derive(Debug, Clone)] pub enum Error { IOError(std::io::ErrorKind), SerdeError(String), FormatError(String), + NonExistentElement, DialogClosed, String(String), } @@ -24,6 +26,9 @@ impl std::fmt::Display for Error { Self::IOError(kind) => { write!(f, "{}", kind) } + Self::NonExistentElement => { + write!(f, "The element tree contains no matching element.") + } Self::DialogClosed => { write!( f, @@ -64,9 +69,9 @@ pub enum Message { CopyCode, SwitchPage(DesignerPage), EditorAction(text_editor::Action), - DropNewElement(types::ElementName, iced::Point, iced::Rectangle), + DropNewElement(ElementName, iced::Point, iced::Rectangle), HandleNew( - types::ElementName, + ElementName, Vec<(iced::advanced::widget::Id, iced::Rectangle)>, ), MoveElement(RenderedElement, iced::Point, iced::Rectangle), @@ -74,8 +79,8 @@ pub enum Message { RenderedElement, Vec<(iced::advanced::widget::Id, iced::Rectangle)>, ), - Resized(pane_grid::ResizeEvent), - Clicked(pane_grid::Pane), + PaneResized(pane_grid::ResizeEvent), + PaneClicked(pane_grid::Pane), PaneDragged(pane_grid::DragEvent), NewFile, OpenFile, diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index aeb5ea6..8efad3e 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -1,15 +1,18 @@ use std::path::PathBuf; use iced::{ + advanced::widget::Id, clipboard, highlighter, keyboard, widget::{ button, column, container, pane_grid::{self, Pane, PaneGrid}, - row, text, text_editor, tooltip, Column, Space, + row, text, text_editor, themer, tooltip, Column, Space, }, Alignment, Element, Font, Length, Settings, Task, Theme, }; -use iced_builder::types::{project::Project, DesignerPage, ElementName}; +use iced_builder::types::{ + element_name::ElementName, project::Project, rendered_element::ActionKind, DesignerPage, +}; use iced_builder::Message; use iced_drop::droppable; @@ -37,7 +40,7 @@ struct App { editor_content: text_editor::Content, } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] enum Panes { Designer, ElementList, @@ -106,12 +109,6 @@ impl App { self.editor_content.perform(action); } } - Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { - self.pane_state.resize(split, ratio); - } - Message::Clicked(pane) => { - self.focus = Some(pane); - } Message::DropNewElement(name, point, _) => { return iced_drop::zones_on_point( move |zones| Message::HandleNew(name.clone(), zones), @@ -122,7 +119,16 @@ impl App { .into() } Message::HandleNew(name, zones) => { - println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); + //println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); + let ids: Vec = zones.into_iter().map(|z| z.0).collect(); + if ids.len() > 0 { + let action = ActionKind::new(ids, &mut self.project.content.clone(), None); + let result = name.handle_action(self.project.content.as_mut(), action); + if let Ok(Some(ref element)) = result { + self.project.content = Some(element.clone()); + } + println!("{:?}", result); + } let code = self .project .clone() @@ -140,13 +146,30 @@ impl App { .into() } Message::HandleMove(element, zones) => { - println!( - "\n\n{:?}\n{element:0.4}", - zones - .into_iter() - .map(|c| c.0) - .collect::>() - ); + let ids: Vec = zones.into_iter().map(|z| z.0).collect(); + if ids.len() > 0 { + println!( + "{:?}", + ActionKind::new( + ids, + &mut self.project.content.clone(), + Some(element.get_id()) + ) + ); + } + //println!( + // "\n\n{:?}\n{element:0.4}", + // zones + // .into_iter() + // .map(|c| c.0) + // .collect::>() + //); + } + Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { + self.pane_state.resize(split, ratio); + } + Message::PaneClicked(pane) => { + self.focus = Some(pane); } Message::PaneDragged(pane_grid::DragEvent::Dropped { pane, target }) => { self.pane_state.drop(pane, target); @@ -207,6 +230,7 @@ impl App { keyboard::on_key_press(|key, modifiers| match key.as_ref() { keyboard::Key::Character("o") if modifiers.command() => Some(Message::OpenFile), keyboard::Key::Character("s") if modifiers.command() => Some(Message::SaveFile), + keyboard::Key::Character("n") if modifiers.command() => Some(Message::NewFile), _ => None, }) } @@ -225,7 +249,7 @@ impl App { Some(tree) => tree.as_element(), None => text("Open a project or begin creating one").into(), }; - let content = container(el_tree) + let content = container(themer(self.project.get_theme(), el_tree)) .id(iced::widget::container::Id::new("drop_zone")) .height(Length::Fill) .width(Length::Fill); @@ -315,8 +339,8 @@ impl App { .width(Length::Fill) .height(Length::Fill) .spacing(10) - .on_resize(10, Message::Resized) - .on_click(Message::Clicked) + .on_resize(10, Message::PaneResized) + .on_click(Message::PaneClicked) .on_drag(Message::PaneDragged); let content = Column::new() @@ -348,8 +372,8 @@ fn items_list_view<'a>(items: Vec) -> Element<'a, Message> { } mod style { - use iced::widget::{container::Style as CStyle, text::Style as TStyle}; - use iced::{color, Border, Theme}; + use iced::widget::container::Style as CStyle; + use iced::{Border, Theme}; pub fn title_bar(theme: &Theme) -> CStyle { let palette = theme.extended_palette(); diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs new file mode 100644 index 0000000..ca0668c --- /dev/null +++ b/iced_builder/src/types/element_name.rs @@ -0,0 +1,90 @@ +use serde::{Deserialize, Serialize}; + +use crate::Error; + +use super::rendered_element::{ + self, button, column, container, image, row, svg, text, ActionKind, RenderedElement, +}; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum ElementName { + Text(String), + Button(String), + SVG(String), + Image(String), + Container, + Row, + Column, +} + +impl ElementName { + pub const ALL: [Self; 7] = [ + Self::Text(String::new()), + Self::Button(String::new()), + Self::SVG(String::new()), + Self::Image(String::new()), + Self::Container, + Self::Row, + Self::Column, + ]; + + pub fn handle_action( + &self, + element_tree: Option<&mut RenderedElement>, + action: ActionKind, + ) -> Result, Error> { + let element = match self { + Self::Text(_) => text(""), + Self::Button(_) => button(""), + Self::SVG(_) => svg(""), + Self::Image(_) => image(""), + Self::Container => container(None), + Self::Row => row(None), + Self::Column => column(None), + }; + match action { + ActionKind::Stop => Ok(None), + ActionKind::AddNew => Ok(Some(element)), + ActionKind::PushFront(id) => { + element_tree + .ok_or(Error::String( + "The action was of kind `PushFront`, but no element tree was provided." + .to_owned(), + ))? + .find_by_id(id) + .ok_or(Error::NonExistentElement)? + .push_front(&element); + Ok(None) + } + ActionKind::InsertAfter(parent_id, child_id) => { + element_tree + .ok_or(Error::String( + "The action was of kind `InsertAfter`, but no element tree was provided." + .to_owned(), + ))? + .find_by_id(parent_id) + .ok_or(Error::NonExistentElement)? + .insert_after(child_id, &element); + Ok(None) + } + } + } +} + +impl std::fmt::Display for ElementName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Text(_) => "Text", + Self::Button(_) => "Button", + Self::SVG(_) => "SVG", + Self::Image(_) => "Image", + Self::Container => "Container", + Self::Row => "Row", + Self::Column => "Column", + } + ) + } +} diff --git a/iced_builder/src/types/mod.rs b/iced_builder/src/types/mod.rs index 2d6cd8f..a48a2d8 100644 --- a/iced_builder/src/types/mod.rs +++ b/iced_builder/src/types/mod.rs @@ -1,49 +1,7 @@ +pub mod element_name; pub mod project; pub mod rendered_element; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum ElementName { - Text(String), - Button(String), - SVG(String), - Image(String), - Container, - Row, - Column, -} - -impl ElementName { - pub const ALL: [Self; 7] = [ - Self::Text(String::new()), - Self::Button(String::new()), - Self::SVG(String::new()), - Self::Image(String::new()), - Self::Container, - Self::Row, - Self::Column, - ]; -} - -impl std::fmt::Display for ElementName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Text(_) => "Text", - Self::Button(_) => "Button", - Self::SVG(_) => "SVG", - Self::Image(_) => "Image", - Self::Container => "Container", - Self::Row => "Row", - Self::Column => "Column", - } - ) - } -} - #[derive(Debug, Clone)] pub enum DesignerPage { Designer, diff --git a/iced_builder/src/types/project.rs b/iced_builder/src/types/project.rs new file mode 100644 index 0000000..0e0442a --- /dev/null +++ b/iced_builder/src/types/project.rs @@ -0,0 +1,137 @@ +use rust_format::{Config, Edition, Formatter, RustFmt}; +use std::path::{Path, PathBuf}; + +use iced::Theme; +use serde::{Deserialize, Serialize}; + +use crate::Error; + +use super::rendered_element::RenderedElement; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Project { + pub title: Option, + pub theme: Option, + pub content: Option, +} + +impl Project { + pub fn new() -> Self { + Self { + title: None, + theme: None, + content: None, + } + } + + pub fn get_theme(&self) -> Theme { + match &self.theme { + Some(theme) => match theme.as_str() { + "Light" => Theme::Light, + "Dark" => Theme::Dark, + "Dracula" => Theme::Dracula, + "Nord" => Theme::Nord, + "Solarized Light" => Theme::SolarizedLight, + "Solarized Dark" => Theme::SolarizedDark, + "Gruvbox Light" => Theme::GruvboxLight, + "Gruvbox Dark" => Theme::GruvboxDark, + "Catppuccin Latte" => Theme::CatppuccinLatte, + "Catppuccin Frappé" => Theme::CatppuccinFrappe, + "Catppuccin Macchiato" => Theme::CatppuccinMacchiato, + "Catppuccin Mocha" => Theme::CatppuccinMocha, + "Tokyo Night" => Theme::TokyoNight, + "Tokyo Night Storm" => Theme::TokyoNightStorm, + "Tokyo Night Light" => Theme::TokyoNightLight, + "Kanagawa Wave" => Theme::KanagawaWave, + "Kanagawa Dragon" => Theme::KanagawaDragon, + "Kanagawa Lotus" => Theme::KanagawaLotus, + "Moonfly" => Theme::Moonfly, + "Nightfly" => Theme::Nightfly, + "Oxocarbon" => Theme::Oxocarbon, + "Ferra" => Theme::Ferra, + _ => Theme::Dark, + }, + None => Theme::Dark, + } + } + + pub async fn from_file() -> Result<(PathBuf, Self), Error> { + let picked_file = rfd::AsyncFileDialog::new() + .set_title("Open a JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .pick_file() + .await + .ok_or(Error::DialogClosed)?; + + let path = picked_file.path().to_owned(); + + let contents = tokio::fs::read_to_string(&path).await?; + let element: Self = serde_json::from_str(&contents)?; + + Ok((path, element)) + } + + pub async fn write_to_file(self, path: Option) -> Result { + let path = if let Some(p) = path { + p + } else { + rfd::AsyncFileDialog::new() + .set_title("Save to JSON file...") + .add_filter("*.JSON, *.json", &["JSON", "json"]) + .save_file() + .await + .as_ref() + .map(rfd::FileHandle::path) + .map(Path::to_owned) + .ok_or(Error::DialogClosed)? + }; + + let contents = serde_json::to_string(&self.clone())?; + tokio::fs::write(&path, contents).await?; + + Ok(path) + } + + pub fn app_code(self) -> Result { + match &self.content { + Some(el) => { + let (imports, view) = el.codegen(); + let mut app_code = format!("use iced::{{widget::{{{imports}}},Element}};"); + + app_code = format!( + r#"// Automatically generated by iced Builder + {app_code} + + fn main() -> iced::Result {{ + iced::run("{}", State::update, State::view) + }} + + #[derive(Default)] + struct State; + + #[derive(Debug, Clone)] + enum Message {{}} + + impl State {{ + fn update(&mut self, _message: Message) {{}} + + fn view(&self) -> Element {{ + {view}.into() + }} + }}"#, + match &self.title { + Some(t) => t, + None => "New app", + } + ); + let config = Config::new_str() + .edition(Edition::Rust2021) + .option("trailing_comma", "Never") + .option("imports_granularity", "Crate"); + let rustfmt = RustFmt::from_config(config); + Ok(rustfmt.format_str(app_code)?) + } + None => Err("No element tree present".into()), + } + } +} diff --git a/iced_builder/src/types/project/mod.rs b/iced_builder/src/types/project/mod.rs deleted file mode 100644 index 557fa92..0000000 --- a/iced_builder/src/types/project/mod.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::path::{Path, PathBuf}; - -use serde::{Deserialize, Serialize}; - -use crate::Error; - -use super::rendered_element::RenderedElement; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Project { - pub title: Option, - pub content: Option, -} - -impl Project { - pub fn new() -> Self { - Self { - title: None, - content: None, - } - } - - pub async fn from_file() -> Result<(PathBuf, Self), Error> { - let picked_file = rfd::AsyncFileDialog::new() - .set_title("Open a JSON file...") - .add_filter("*.JSON, *.json", &["JSON", "json"]) - .pick_file() - .await - .ok_or(Error::DialogClosed)?; - - let path = picked_file.path().to_owned(); - - let contents = tokio::fs::read_to_string(&path).await?; - let element: Self = serde_json::from_str(&contents)?; - - Ok((path, element)) - } - - pub async fn write_to_file(self, path: Option) -> Result { - let path = if let Some(p) = path { - p - } else { - rfd::AsyncFileDialog::new() - .set_title("Save to JSON file...") - .add_filter("*.JSON, *.json", &["JSON", "json"]) - .save_file() - .await - .as_ref() - .map(rfd::FileHandle::path) - .map(Path::to_owned) - .ok_or(Error::DialogClosed)? - }; - - let contents = serde_json::to_string(&self.clone())?; - tokio::fs::write(&path, contents).await?; - - Ok(path) - } -} diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 967352b..827e8c2 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use indexmap::IndexMap; use iced::advanced::widget::Id; use iced::{widget, Element, Length}; @@ -7,14 +7,14 @@ use unique_id::{string::StringGenerator, Generator}; use crate::Message; -use super::ElementName; +use super::element_name::ElementName; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RenderedElement { - pub id: String, + id: String, pub child_elements: Option>, pub name: ElementName, - pub props: HashMap>, + pub options: IndexMap>, } impl RenderedElement { @@ -24,22 +24,26 @@ impl RenderedElement { id: gen.next_id(), child_elements: None, name, - props: HashMap::new(), + options: IndexMap::new(), } } - fn from_vec(name: ElementName, child_elements: Vec) -> Self { + fn with(name: ElementName, child_elements: Vec) -> Self { let gen = StringGenerator::default(); Self { id: gen.next_id(), child_elements: Some(child_elements), name, - props: HashMap::new(), + options: IndexMap::new(), } } + pub fn get_id(&self) -> Id { + Id::new(self.id.clone()) + } + pub fn find_by_id(&mut self, id: Id) -> Option<&mut Self> { - if Id::new(self.id.clone()) == id.clone() { + if self.get_id() == id.clone() { println!(""); return Some(self); } else if let Some(child_elements) = self.child_elements.as_mut() { @@ -59,12 +63,17 @@ impl RenderedElement { if child_element == self { return Some(self); } else if self.child_elements.is_some() { - if self.child_elements.clone()?.contains(child_element) { + if self + .child_elements + .clone() + .unwrap_or(vec![]) + .contains(child_element) + { return Some(self); } else { if let Some(child_elements) = self.child_elements.as_mut() { for element in child_elements { - let element: Option<&mut Self> = element.find_parent(child_element); + let element = element.find_parent(child_element); if element.is_some() { return element; } @@ -77,6 +86,10 @@ impl RenderedElement { } } + pub fn is_parent(&self) -> bool { + self.child_elements.is_some() + } + pub fn remove(&mut self, element: &RenderedElement) { let parent = self.find_parent(element); if let Some(child_elements) = parent.unwrap().child_elements.as_mut() { @@ -86,39 +99,39 @@ impl RenderedElement { } } - pub fn push(&mut self, element: RenderedElement) { + pub fn push_front(&mut self, element: &RenderedElement) { if let Some(child_elements) = self.child_elements.as_mut() { - child_elements.push(element); + child_elements.insert(0, element.clone()); } } - pub fn insert_after(&mut self, id: Id, element: RenderedElement) { + pub fn insert_after(&mut self, id: Id, element: &RenderedElement) { if let Some(child_elements) = self.child_elements.as_mut() { if let Some(index) = child_elements .iter() .position(|x| Id::new(x.id.clone()) == id) { - child_elements.insert(index, element); + child_elements.insert(index + 1, element.clone()); } else { - child_elements.push(element); + child_elements.push(element.clone()); } } } fn preset_options(mut self, options: Vec<&str>) -> Self { for opt in options { - self.props.insert(opt.to_owned(), None); + self.options.insert(opt.to_owned(), None); } self } - pub fn option(&mut self, option: &'static str, value: &'static str) { - self.props + pub fn option<'a>(&mut self, option: &'a str, value: &'a str) { + self.options .entry(option.to_owned()) .and_modify(|opt| *opt = Some(value.to_owned())); } - pub fn as_element(self) -> Element<'static, Message> { + pub fn as_element<'a>(self) -> Element<'a, Message> { let mut children = widget::column![]; if let Some(els) = self.child_elements.clone() { @@ -131,12 +144,103 @@ impl RenderedElement { widget::column![widget::text(self.name.clone().to_string()), children] .width(Length::Fill), ) + .padding(10) .style(widget::container::bordered_box), ) - .id(Id::new(self.id.clone())) + .id(self.get_id()) + .drag_hide(true) .on_drop(move |point, rect| Message::MoveElement(self.clone(), point, rect)) .into() } + + fn props_codegen(&self) -> String { + let mut props_string = String::new(); + + for (k, v) in self.options.clone() { + if let Some(v) = v { + props_string = format!("{props_string}.{k}({v})"); + } + } + + props_string + } + + pub fn codegen(&self) -> (String, String) { + let mut imports = String::new(); + let mut view = String::new(); + let props = self.props_codegen(); + + let mut elements = String::new(); + + if let Some(els) = &self.child_elements { + for element in els { + let (c_imports, children) = element.codegen(); + imports = format!("{imports}{c_imports}"); + elements = format!("{elements}{},", children); + } + } + + match &self.name { + ElementName::Container => { + imports = format!("{imports}container,"); + view = format!("{view}\ncontainer({elements}){props}"); + } + ElementName::Row => { + imports = format!("{imports}row,"); + view = format!("{view}\nrow![{elements}]{props}"); + } + ElementName::Column => { + imports = format!("{imports}column,"); + view = format!("{view}\ncolumn![{elements}]{props}"); + } + ElementName::Text(string) => { + imports = format!("{imports}text,"); + view = format!( + "{view}\ntext(\"{}\"){props}", + if *string == String::new() { + "New Text" + } else { + string + } + ); + } + ElementName::Button(string) => { + imports = format!("{imports}button,"); + view = format!( + "{view}\nbutton(\"{}\"){props}", + if *string == String::new() { + "New Button" + } else { + string + } + ); + } + ElementName::Image(path) => { + imports = format!("{imports}image,"); + view = format!("{view}\nimage(\"{path}\"){props}"); + } + ElementName::SVG(path) => { + imports = format!("{imports}svg,"); + view = format!("{view}\nsvg(\"{path}\"){props}"); + } + } + + (imports, view) + } + + pub fn test() -> RenderedElement { + let mut text1 = text("wow"); + text1.option("height", "120.5"); + text1.option("width", "230"); + + let element = container(Some(row(Some(vec![ + text1, + text("heh"), + svg("/mnt/drive_d/git/obs-website/src/lib/assets/bars-solid.svg"), + ])))); + + element + } } impl std::fmt::Display for RenderedElement { @@ -146,7 +250,7 @@ impl std::fmt::Display for RenderedElement { f.write_fmt(format_args!("{:?}\n", self.name))?; f.pad("")?; f.write_str("Options: (")?; - for (k, v) in &self.props { + for (k, v) in &self.options { if let Some(value) = v { has_props = true; f.write_fmt(format_args!( @@ -181,6 +285,54 @@ impl std::fmt::Display for RenderedElement { } } +#[derive(Debug, Clone)] +pub enum ActionKind { + AddNew, + PushFront(Id), + InsertAfter(Id, Id), + Stop, +} + +impl ActionKind { + pub fn new( + ids: Vec, + element_tree: &mut Option, + source_id: Option, + ) -> Self { + let mut action = Self::Stop; + if ids.len() == 1 { + if element_tree.is_none() { + action = Self::AddNew; + } + } else { + let id: Id = match source_id { + Some(id) if ids.contains(&id) => { + let element_id = ids[ids.iter().position(|x| *x == id).unwrap()].clone(); + if ids.len() > 2 && ids[ids.clone().len() - 1] == element_id { + return Self::Stop; + } + element_id + } + _ => ids.last().cloned().unwrap(), + }; + let element = element_tree.as_mut().unwrap().find_by_id(id.clone()); + + match element.unwrap().is_parent() { + true => action = Self::PushFront(id), + false => { + if ids.len() > 2 { + action = Self::InsertAfter( + ids[ids.clone().len() - 2].clone(), + ids[ids.clone().len() - 1].clone(), + ); + } + } + } + } + action + } +} + pub fn text(text: &str) -> RenderedElement { RenderedElement::new(ElementName::Text(text.to_owned())).preset_options(vec![ "size", @@ -204,21 +356,21 @@ pub fn image(path: &str) -> RenderedElement { pub fn container(content: Option) -> RenderedElement { match content { - Some(el) => RenderedElement::from_vec(ElementName::Container, vec![el]), - None => RenderedElement::from_vec(ElementName::Container, vec![]), + Some(el) => RenderedElement::with(ElementName::Container, vec![el]), + None => RenderedElement::with(ElementName::Container, vec![]), } } pub fn row(child_elements: Option>) -> RenderedElement { match child_elements { - Some(els) => RenderedElement::from_vec(ElementName::Row, els), - None => RenderedElement::from_vec(ElementName::Row, vec![]), + Some(els) => RenderedElement::with(ElementName::Row, els), + None => RenderedElement::with(ElementName::Row, vec![]), } } pub fn column(child_elements: Option>) -> RenderedElement { match child_elements { - Some(els) => RenderedElement::from_vec(ElementName::Column, els), - None => RenderedElement::from_vec(ElementName::Column, vec![]), + Some(els) => RenderedElement::with(ElementName::Column, els), + None => RenderedElement::with(ElementName::Column, vec![]), } } -- cgit v1.2.3 From b66f5d57013243211c37fa52dbe26fe0b241bd07 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 22 Oct 2024 12:57:35 +0200 Subject: feat: disable appending to container if it already has children fix: Column element now showing in element list --- iced_builder/src/main.rs | 24 +++++++++++++----------- iced_builder/src/types/element_name.rs | 2 +- iced_builder/src/types/rendered_element.rs | 13 +++++++++++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 8efad3e..0c76374 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -361,34 +361,36 @@ fn items_list_view<'a>(items: Vec) -> Element<'a, Message> { .width(Length::Fill); for item in items { - let value = item.clone(); column = column.push( - droppable(text(value.to_string())) - .on_drop(move |point, rect| Message::DropNewElement(value.clone(), point, rect)), + droppable(text(item.clone().to_string())) + .on_drop(move |point, rect| Message::DropNewElement(item.clone(), point, rect)), ); } - container(column).height(250.0).width(300).into() + container(column) + .width(Length::Fill) + .height(Length::Fill) + .into() } mod style { - use iced::widget::container::Style as CStyle; + use iced::widget::container::Style; use iced::{Border, Theme}; - pub fn title_bar(theme: &Theme) -> CStyle { + pub fn title_bar(theme: &Theme) -> Style { let palette = theme.extended_palette(); - CStyle { + Style { text_color: Some(palette.background.strong.text), background: Some(palette.background.strong.color.into()), ..Default::default() } } - pub fn pane_active(theme: &Theme) -> CStyle { + pub fn pane_active(theme: &Theme) -> Style { let palette = theme.extended_palette(); - CStyle { + Style { background: Some(palette.background.weak.color.into()), border: Border { width: 1.0, @@ -399,10 +401,10 @@ mod style { } } - pub fn pane_focused(theme: &Theme) -> CStyle { + pub fn pane_focused(theme: &Theme) -> Style { let palette = theme.extended_palette(); - CStyle { + Style { background: Some(palette.background.weak.color.into()), border: Border { width: 4.0, diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs index ca0668c..c30d6e3 100644 --- a/iced_builder/src/types/element_name.rs +++ b/iced_builder/src/types/element_name.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::Error; use super::rendered_element::{ - self, button, column, container, image, row, svg, text, ActionKind, RenderedElement, + button, column, container, image, row, svg, text, ActionKind, RenderedElement, }; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 827e8c2..6b01bc3 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -317,8 +317,17 @@ impl ActionKind { }; let element = element_tree.as_mut().unwrap().find_by_id(id.clone()); - match element.unwrap().is_parent() { - true => action = Self::PushFront(id), + match element.as_ref().unwrap().is_parent() { + true => { + let element = &element.unwrap(); + if element.name == ElementName::Container + && element.child_elements != Some(vec![]) + { + action = Self::Stop; + } else { + action = Self::PushFront(id); + } + } false => { if ids.len() > 2 { action = Self::InsertAfter( -- cgit v1.2.3 From 75b8ef8d9801aec40654c955ff941a3205859603 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 24 Oct 2024 20:45:26 +0200 Subject: test: remove some unneeded printlns --- iced_builder/src/main.rs | 8 -------- iced_builder/src/types/rendered_element.rs | 1 - 2 files changed, 9 deletions(-) diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 0c76374..424e0e0 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -119,7 +119,6 @@ impl App { .into() } Message::HandleNew(name, zones) => { - //println!("\n\n{:?}\n{name}\n{:?}", zones, self.title()); let ids: Vec = zones.into_iter().map(|z| z.0).collect(); if ids.len() > 0 { let action = ActionKind::new(ids, &mut self.project.content.clone(), None); @@ -157,13 +156,6 @@ impl App { ) ); } - //println!( - // "\n\n{:?}\n{element:0.4}", - // zones - // .into_iter() - // .map(|c| c.0) - // .collect::>() - //); } Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 6b01bc3..336f78f 100644 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -44,7 +44,6 @@ impl RenderedElement { pub fn find_by_id(&mut self, id: Id) -> Option<&mut Self> { if self.get_id() == id.clone() { - println!(""); return Some(self); } else if let Some(child_elements) = self.child_elements.as_mut() { for element in child_elements { -- cgit v1.2.3 From bddbb502c2fed62c6d05f7ef2c9d0b76da493ab1 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 24 Oct 2024 20:48:21 +0200 Subject: feat(actions): refactor match statement, add special case for containers When a new element is dragged on top of a container that isn't the root element (i.e. has a parent) and said parent isn't a container either, the element is inserted into the container's parent element. s --- iced_builder/src/types/rendered_element.rs | 46 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 16 deletions(-) mode change 100644 => 100755 iced_builder/src/types/rendered_element.rs diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs old mode 100644 new mode 100755 index 336f78f..8b9b222 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -89,9 +89,12 @@ impl RenderedElement { self.child_elements.is_some() } + pub fn is_empty(&self) -> bool { + self.child_elements == Some(vec![]) + } + pub fn remove(&mut self, element: &RenderedElement) { - let parent = self.find_parent(element); - if let Some(child_elements) = parent.unwrap().child_elements.as_mut() { + if let Some(child_elements) = self.child_elements.as_mut() { if let Some(index) = child_elements.iter().position(|x| x == element) { child_elements.remove(index); } @@ -314,27 +317,38 @@ impl ActionKind { } _ => ids.last().cloned().unwrap(), }; - let element = element_tree.as_mut().unwrap().find_by_id(id.clone()); - - match element.as_ref().unwrap().is_parent() { - true => { - let element = &element.unwrap(); - if element.name == ElementName::Container - && element.child_elements != Some(vec![]) + let element = element_tree + .as_mut() + .unwrap() + .find_by_id(id.clone()) + .unwrap(); + + match ( + element.is_parent(), + element.name == ElementName::Container && !element.is_empty(), + ) { + (true, false) => { + action = Self::PushFront(id); + } + _ if ids.len() > 2 => { + let parent = element_tree + .as_mut() + .unwrap() + .find_by_id(ids[&ids.len() - 2].clone()) + .unwrap(); + + if parent.name == ElementName::Container + && parent.child_elements != Some(vec![]) { action = Self::Stop; } else { - action = Self::PushFront(id); - } - } - false => { - if ids.len() > 2 { action = Self::InsertAfter( - ids[ids.clone().len() - 2].clone(), - ids[ids.clone().len() - 1].clone(), + ids[&ids.len() - 2].clone(), + ids[&ids.len() - 1].clone(), ); } } + _ => {} } } action -- cgit v1.2.3 From f04dc01bf5400faa6876b402100037501e374807 Mon Sep 17 00:00:00 2001 From: pml68 Date: Thu, 24 Oct 2024 23:17:42 +0200 Subject: feat: implement d&d for existing elements --- iced_builder/src/lib.rs | 1 + iced_builder/src/main.rs | 32 ++++++++++++++--------- iced_builder/src/types/element_name.rs | 12 +++------ iced_builder/src/types/rendered_element.rs | 42 ++++++++++++++++++++++++++---- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/iced_builder/src/lib.rs b/iced_builder/src/lib.rs index 6de9ba8..420b14c 100644 --- a/iced_builder/src/lib.rs +++ b/iced_builder/src/lib.rs @@ -69,6 +69,7 @@ pub enum Message { CopyCode, SwitchPage(DesignerPage), EditorAction(text_editor::Action), + RefreshEditorContent, DropNewElement(ElementName, iced::Point, iced::Rectangle), HandleNew( ElementName, diff --git a/iced_builder/src/main.rs b/iced_builder/src/main.rs index 424e0e0..fc7f18c 100644 --- a/iced_builder/src/main.rs +++ b/iced_builder/src/main.rs @@ -109,6 +109,14 @@ impl App { self.editor_content.perform(action); } } + Message::RefreshEditorContent => { + let code = self + .project + .clone() + .app_code() + .unwrap_or_else(|err| err.to_string()); + self.editor_content = text_editor::Content::with_text(&code); + } Message::DropNewElement(name, point, _) => { return iced_drop::zones_on_point( move |zones| Message::HandleNew(name.clone(), zones), @@ -128,12 +136,8 @@ impl App { } println!("{:?}", result); } - let code = self - .project - .clone() - .app_code() - .unwrap_or_else(|err| err.to_string()); - self.editor_content = text_editor::Content::with_text(&code); + + return Task::done(Message::RefreshEditorContent); } Message::MoveElement(element, point, _) => { return iced_drop::zones_on_point( @@ -147,15 +151,17 @@ impl App { Message::HandleMove(element, zones) => { let ids: Vec = zones.into_iter().map(|z| z.0).collect(); if ids.len() > 0 { - println!( - "{:?}", - ActionKind::new( - ids, - &mut self.project.content.clone(), - Some(element.get_id()) - ) + let action = ActionKind::new( + ids, + &mut self.project.content.clone(), + Some(element.get_id()), ); + let result = element.handle_action(self.project.content.as_mut(), action); + + println!("{result:?}"); } + + return Task::done(Message::RefreshEditorContent); } Message::PaneResized(pane_grid::ResizeEvent { split, ratio }) => { self.pane_state.resize(split, ratio); diff --git a/iced_builder/src/types/element_name.rs b/iced_builder/src/types/element_name.rs index c30d6e3..8d00814 100644 --- a/iced_builder/src/types/element_name.rs +++ b/iced_builder/src/types/element_name.rs @@ -47,10 +47,7 @@ impl ElementName { ActionKind::AddNew => Ok(Some(element)), ActionKind::PushFront(id) => { element_tree - .ok_or(Error::String( - "The action was of kind `PushFront`, but no element tree was provided." - .to_owned(), - ))? + .ok_or("The action was of kind `PushFront`, but no element tree was provided.")? .find_by_id(id) .ok_or(Error::NonExistentElement)? .push_front(&element); @@ -58,10 +55,9 @@ impl ElementName { } ActionKind::InsertAfter(parent_id, child_id) => { element_tree - .ok_or(Error::String( - "The action was of kind `InsertAfter`, but no element tree was provided." - .to_owned(), - ))? + .ok_or( + "The action was of kind `InsertAfter`, but no element tree was provided.", + )? .find_by_id(parent_id) .ok_or(Error::NonExistentElement)? .insert_after(child_id, &element); diff --git a/iced_builder/src/types/rendered_element.rs b/iced_builder/src/types/rendered_element.rs index 8b9b222..e2bebfa 100755 --- a/iced_builder/src/types/rendered_element.rs +++ b/iced_builder/src/types/rendered_element.rs @@ -5,7 +5,7 @@ use iced::{widget, Element, Length}; use serde::{Deserialize, Serialize}; use unique_id::{string::StringGenerator, Generator}; -use crate::Message; +use crate::{Error, Message}; use super::element_name::ElementName; @@ -120,6 +120,39 @@ impl RenderedElement { } } + pub fn handle_action( + &self, + element_tree: Option<&mut RenderedElement>, + action: ActionKind, + ) -> Result<(), Error> { + let element_tree = element_tree.unwrap(); + + match action { + ActionKind::Stop => Ok(()), + ActionKind::AddNew => Err( + "The action was of kind `AddNew`, but invoking it on an existing element tree is not possible.".into(), + ), + ActionKind::PushFront(id) => { + let old_parent = element_tree.find_parent(self).unwrap(); + old_parent.remove(self); + + let new_parent = element_tree.find_by_id(id).unwrap(); + new_parent.push_front(self); + + Ok(()) + } + ActionKind::InsertAfter(parent_id, target_id) => { + let old_parent = element_tree.find_parent(self).unwrap(); + old_parent.remove(self); + + let new_parent = element_tree.find_by_id(parent_id).unwrap(); + new_parent.insert_after(target_id, self); + + Ok(()) + } + } + } + fn preset_options(mut self, options: Vec<&str>) -> Self { for opt in options { self.options.insert(opt.to_owned(), None); @@ -127,10 +160,11 @@ impl RenderedElement { self } - pub fn option<'a>(&mut self, option: &'a str, value: &'a str) { + pub fn option<'a>(&mut self, option: &'a str, value: &'a str) -> Self { self.options .entry(option.to_owned()) .and_modify(|opt| *opt = Some(value.to_owned())); + self.clone() } pub fn as_element<'a>(self) -> Element<'a, Message> { @@ -231,9 +265,7 @@ impl RenderedElement { } pub fn test() -> RenderedElement { - let mut text1 = text("wow"); - text1.option("height", "120.5"); - text1.option("width", "230"); + let text1 = text("wow").option("height", "120.5").option("width", "230"); let element = container(Some(row(Some(vec![ text1, -- cgit v1.2.3