diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8780524..36d1c99 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -8,6 +8,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: true - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -22,6 +24,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: true - name: Install nightly uses: actions-rs/toolchain@v1 with: @@ -40,6 +44,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: true - name: Install nightly uses: actions-rs/toolchain@v1 with: @@ -61,6 +67,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: true - uses: actions-rs/toolchain@v1 with: profile: minimal diff --git a/.gitmodules b/.gitmodules index e69de29..3eb6e5e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "flax"] + path = flax + url = https://github.com/ten3roberts/flax diff --git a/Cargo.lock b/Cargo.lock index d9f3264..b9c8b0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,188 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "ashpd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" +dependencies = [ + "event-listener 5.2.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +dependencies = [ + "concurrent-queue", + "event-listener 5.2.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +dependencies = [ + "async-lock 3.3.0", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1" +dependencies = [ + "async-lock 3.3.0", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8" +dependencies = [ + "async-channel", + "async-io", + "async-lock 3.3.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 5.2.0", + "futures-lite", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-recursion" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "async-trait" +version = "0.1.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -226,6 +408,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block-sys" version = "0.2.1" @@ -245,6 +436,22 @@ dependencies = [ "objc2", ] +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel", + "async-lock 3.3.0", + "async-task", + "fastrand", + "futures-io", + "futures-lite", + "piper", + "tracing", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -353,12 +560,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecdffb913a326b6c642290a0d0ec8e8d6597291acdc07cc4c9cb4b3635d44cf9" -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "com" version = "0.6.0" @@ -482,6 +683,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -525,6 +735,16 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[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 = "cursor-icon" version = "1.1.0" @@ -544,6 +764,27 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "discard" version = "1.0.4" @@ -577,6 +818,33 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -602,12 +870,66 @@ dependencies = [ "num-traits", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.2.0", + "pin-project-lite", +] + [[package]] name = "fast-srgb8" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fdeflate" version = "0.3.4" @@ -630,7 +952,6 @@ dependencies = [ [[package]] name = "flax" version = "0.6.2" -source = "git+https://github.com/ten3roberts/flax#b75a7275d9a119309d3d7f41d824347dc2f9806b" dependencies = [ "anyhow", "atomic_refcell", @@ -648,10 +969,9 @@ dependencies = [ [[package]] name = "flax-derive" version = "0.6.0" -source = "git+https://github.com/ten3roberts/flax#b75a7275d9a119309d3d7f41d824347dc2f9806b" dependencies = [ "itertools 0.11.0", - "proc-macro-crate", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", "syn 2.0.48", @@ -725,6 +1045,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 = "funty" version = "2.0.0" @@ -792,6 +1121,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" @@ -849,6 +1191,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 = "gensym" version = "0.1.1" @@ -903,9 +1255,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.24.2" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" dependencies = [ "bytemuck", ] @@ -1028,6 +1380,12 @@ dependencies = [ "winapi", ] +[[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" @@ -1045,18 +1403,28 @@ dependencies = [ "objc2", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" -version = "0.24.8" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" +checksum = "a9b4f005360d32e9325029b38ba47ebd7a56f3316df09249368939562d518645" dependencies = [ "bytemuck", "byteorder", - "color_quant", - "jpeg-decoder", "num-traits", "png", + "zune-core", + "zune-jpeg", ] [[package]] @@ -1067,6 +1435,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", + "serde", ] [[package]] @@ -1124,12 +1493,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - [[package]] name = "js-sys" version = "0.3.67" @@ -1275,6 +1638,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "metal" version = "0.27.0" @@ -1371,6 +1743,18 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "7.1.3" @@ -1424,7 +1808,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", "syn 2.0.48", @@ -1440,6 +1824,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc-sys" version = "0.3.2" @@ -1471,6 +1866,15 @@ 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.32.2" @@ -1495,6 +1899,16 @@ dependencies = [ "libredox", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "overload" version = "0.1.1" @@ -1532,6 +1946,12 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1641,6 +2061,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.29" @@ -1674,12 +2105,34 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "presser" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + [[package]] name = "proc-macro-crate" version = "2.0.1" @@ -1687,7 +2140,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" dependencies = [ "toml_datetime", - "toml_edit", + "toml_edit 0.20.2", ] [[package]] @@ -1764,6 +2217,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", ] @@ -1772,6 +2237,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "rangemap" @@ -1882,6 +2350,29 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" +[[package]] +name = "rfd" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373d2fc6310e2d14943d4e66ebed5b774a2b6b3b1610e7377edf124fb2760d6b" +dependencies = [ + "ashpd", + "block", + "dispatch", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + [[package]] name = "roxmltree" version = "0.19.0" @@ -2007,6 +2498,28 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[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 = "sharded-slab" version = "0.1.7" @@ -2016,6 +2529,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -2179,6 +2701,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -2286,6 +2820,17 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "toml_edit" version = "0.20.2" @@ -2398,6 +2943,23 @@ dependencies = [ "nom", ] +[[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" @@ -2428,6 +2990,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-properties" version = "0.1.1" @@ -2458,6 +3029,24 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "uuid" version = "1.7.0" @@ -2547,11 +3136,17 @@ dependencies = [ name = "violet-demo" version = "0.0.1" dependencies = [ + "anyhow", "console_error_panic_hook", + "flume", "futures", "glam", + "indexmap", "itertools 0.12.1", "puffin", + "rfd", + "serde", + "serde_json", "tracing-subscriber", "tracing-tree", "tracing-web", @@ -3270,6 +3865,16 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -3301,6 +3906,70 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock 3.3.0", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener 5.2.0", + "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.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zeno" version = "0.2.3" @@ -3326,3 +3995,56 @@ dependencies = [ "quote", "syn 2.0.48", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/Cargo.toml b/Cargo.toml index 8f85f89..e428727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "violet-wgpu", "violet-core", "violet-demo" ] +exclude = [ "flax" ] [workspace.package] version = "0.0.1" @@ -23,7 +24,7 @@ documentation = "https://docs.rs/violet" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace.dependencies] -flax = { git = "https://github.com/ten3roberts/flax", version = "0.6.0", features = [ +flax = { path = "./flax", version = "0.6.0", features = [ "derive", "puffin", ] } @@ -31,7 +32,7 @@ flax = { git = "https://github.com/ten3roberts/flax", version = "0.6.0", feature atomic_refcell = "0.1" futures-signals = "0.3" itertools = "0.12" -glam = { version = "0.24", features = ["bytemuck"] } +glam = { version = "0.25", features = ["bytemuck"] } futures = "0.3" futures-concurrency = "7.0" flume = "0.11" @@ -51,9 +52,12 @@ winit = "0.29" wgpu = { version = "0.19", default-features = false, features = ["fragile-send-sync-non-atomic-wasm", "webgl", "wgsl"] } palette = { version = "0.7", features = ["serializing"] } dashmap = "5.4" -image = { version = "0.24", default_features = false, features = ["png", "jpeg"] } +image = { version = "0.25", default_features = false, features = ["png", "jpeg"] } color-hex = "0.2" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +indexmap = { version = "2.0", features = ["serde"] } +rfd = "0.14" tracing = "0.1" pin-project = "1.1" diff --git a/examples/basic.rs b/examples/basic.rs index 36059a5..fdfd9f9 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -267,5 +267,5 @@ pub fn main() -> anyhow::Result<()> { .with(EnvFilter::from_default_env()) .init(); - violet_wgpu::App::new().run(MainApp) + violet_wgpu::AppBuilder::new().run(MainApp) } diff --git a/examples/color.rs b/examples/color.rs index 79eaf18..095f9b0 100644 --- a/examples/color.rs +++ b/examples/color.rs @@ -29,7 +29,7 @@ pub fn main() -> anyhow::Result<()> { .with(EnvFilter::from_default_env()) .init(); - violet_wgpu::App::new() + violet_wgpu::AppBuilder::new() .with_renderer_config(RendererConfig { debug_mode: false }) .run(MainApp) } diff --git a/examples/counter.rs b/examples/counter.rs index dbd8192..1b49052 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -77,5 +77,5 @@ pub fn main() -> anyhow::Result<()> { .with(EnvFilter::from_default_env()) .init(); - violet_wgpu::App::new().run(MainApp) + violet_wgpu::AppBuilder::new().run(MainApp) } diff --git a/examples/flow.rs b/examples/flow.rs index b3555b8..dd2b419 100644 --- a/examples/flow.rs +++ b/examples/flow.rs @@ -37,7 +37,7 @@ pub fn main() -> anyhow::Result<()> { .with(EnvFilter::from_default_env()) .init(); - violet_wgpu::App::new() + violet_wgpu::AppBuilder::new() .with_renderer_config(RendererConfig { debug_mode: false }) .run(MainApp) } diff --git a/examples/row.rs b/examples/row.rs index 5509c44..974050d 100644 --- a/examples/row.rs +++ b/examples/row.rs @@ -39,7 +39,7 @@ pub fn main() -> anyhow::Result<()> { .with(EnvFilter::from_default_env()) .init(); - violet_wgpu::App::new() + violet_wgpu::AppBuilder::new() .with_renderer_config(RendererConfig { debug_mode: true }) .run(MainApp) } diff --git a/examples/sizing.rs b/examples/sizing.rs index 9029756..2a59923 100644 --- a/examples/sizing.rs +++ b/examples/sizing.rs @@ -41,7 +41,7 @@ pub fn main() -> anyhow::Result<()> { .with(EnvFilter::from_default_env()) .init(); - violet_wgpu::App::new() + violet_wgpu::AppBuilder::new() .with_renderer_config(RendererConfig { debug_mode: true }) .run(MainApp) } diff --git a/flax b/flax new file mode 160000 index 0000000..ef3e4b3 --- /dev/null +++ b/flax @@ -0,0 +1 @@ +Subproject commit ef3e4b3860e9f3934bf6df818d4e373b96e5cc9f diff --git a/violet-core/src/atom.rs b/violet-core/src/atom.rs index 1aa2904..d347936 100644 --- a/violet-core/src/atom.rs +++ b/violet-core/src/atom.rs @@ -1,7 +1,16 @@ +#[doc(hidden)] +pub use flax; use flax::Component; pub struct Atom(pub(crate) Component); +impl Atom { + #[doc(hidden)] + pub fn from_component(component: Component) -> Self { + Self(component) + } +} + impl Clone for Atom { fn clone(&self) -> Self { *self @@ -23,14 +32,14 @@ macro_rules! declare_atom { ($(#[$outer:meta])* $vis: vis $name: ident: $ty: ty $(=> [$($metadata: ty),*])?, $($rest:tt)*) => { $(#[$outer])* $vis fn $name() -> $crate::atom::Atom<$ty> { - use flax::entity::EntityKind; + use $crate::atom::flax::entity::EntityKind; - static COMPONENT_ID: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(flax::entity::EntityIndex::MAX); - static VTABLE: &flax::vtable::ComponentVTable<$ty> = flax::component_vtable!($name: $ty $(=> [$($metadata),*])?); - $crate::atom::Atom(flax::Component::static_init(&COMPONENT_ID, EntityKind::COMPONENT, VTABLE)) + static COMPONENT_ID: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new($crate::atom::flax::entity::EntityIndex::MAX); + static VTABLE: &$crate::atom::flax::vtable::ComponentVTable<$ty> = $crate::atom::flax::component_vtable!($name: $ty $(=> [$($metadata),*])?); + $crate::atom::Atom::from_component($crate::atom::flax::Component::static_init(&COMPONENT_ID, EntityKind::COMPONENT, VTABLE)) } - flax::component!{ $($rest)* } + $crate::atom::flax::component!{ $($rest)* } }; } diff --git a/violet-core/src/components.rs b/violet-core/src/components.rs index 1a6c5a0..8c34937 100644 --- a/violet-core/src/components.rs +++ b/violet-core/src/components.rs @@ -28,6 +28,8 @@ component! { /// Specifies in screen space where the widget rect upper left corner is pub screen_position: Vec2 => [ Debuggable ], + pub rotation: f32 => [ Debuggable ], + /// Offset the widget from its original position pub offset: Unit => [ Debuggable ], @@ -48,7 +50,9 @@ component! { /// Constrain the aspect ratio of a widget pub aspect_ratio: f32 => [ Debuggable ], - /// Sets the anchor point withing the bounds of the widget where position is applied + /// Set the origin or anchor point of a widget. + /// + /// This determines the center of positioning and rotation pub anchor: Unit => [ Debuggable ], diff --git a/violet-core/src/state/constant.rs b/violet-core/src/state/constant.rs new file mode 100644 index 0000000..402c0e4 --- /dev/null +++ b/violet-core/src/state/constant.rs @@ -0,0 +1,28 @@ +use futures::{prelude::stream::BoxStream, StreamExt}; + +use super::{State, StateRef, StateStream}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Constant(pub T); + +impl State for Constant { + type Item = T; +} + +impl StateRef for Constant { + type Item = T; + + fn read_ref V, V>(&self, f: F) -> V { + (f)(&self.0) + } +} + +impl StateStream for Constant +where + T: 'static + Send + Sync + Clone, +{ + fn stream(&self) -> BoxStream<'static, Self::Item> { + let value = self.0.clone(); + futures::stream::once(async move { value }).boxed() + } +} diff --git a/violet-core/src/state/mod.rs b/violet-core/src/state/mod.rs index accce60..61d9f18 100644 --- a/violet-core/src/state/mod.rs +++ b/violet-core/src/state/mod.rs @@ -7,6 +7,7 @@ use std::{marker::PhantomData, rc::Rc, sync::Arc}; use futures::{stream::BoxStream, FutureExt, Stream, StreamExt}; use futures_signals::signal::{Mutable, SignalExt}; +pub mod constant; mod dedup; mod feedback; mod filter; diff --git a/violet-core/src/widget/interactive/input.rs b/violet-core/src/widget/interactive/input.rs index 69144ca..bde662a 100644 --- a/violet-core/src/widget/interactive/input.rs +++ b/violet-core/src/widget/interactive/input.rs @@ -16,7 +16,7 @@ use crate::{ components::{self, screen_rect}, editor::{CursorMove, EditAction, EditorAction, TextEditor}, input::{focus_sticky, focusable, on_focus, on_keyboard_input, on_mouse_input, KeyboardInput}, - state::{StateDuplex, StateSink, StateStream}, + state::{State, StateDuplex, StateSink, StateStream}, style::{ interactive_active, interactive_inactive, spacing_small, Background, SizeExt, StyleExt, ValueOrRef, WidgetSize, @@ -104,7 +104,7 @@ impl Widget for TextInput { editor.set_cursor_at_end(); let (editor_props_tx, editor_props_rx) = signal::channel(Box::new(NoOp) as Box); - let content = self.content; + let content = self.content.prevent_feedback(); scope.spawn({ let mut layout_glyphs = layout_glyphs.signal_cloned().to_stream(); diff --git a/violet-demo/Cargo.toml b/violet-demo/Cargo.toml index faa1d55..9c85582 100644 --- a/violet-demo/Cargo.toml +++ b/violet-demo/Cargo.toml @@ -29,6 +29,12 @@ wasm-bindgen-futures = "0.4" itertools.workspace = true tracing-tree.workspace = true puffin.workspace = true +serde.workspace = true +serde_json.workspace = true +indexmap.workspace = true +rfd.workspace = true +anyhow.workspace = true +flume.workspace = true [package.metadata.wasm-pack.profile.profiling] wasm-opt = false diff --git a/violet-demo/src/lib.rs b/violet-demo/src/lib.rs index dd30371..7422540 100644 --- a/violet-demo/src/lib.rs +++ b/violet-demo/src/lib.rs @@ -1,37 +1,45 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; -use futures::StreamExt; -use glam::{Vec2, Vec3}; +use anyhow::Context; +use flume::Sender; +use futures::{Stream, StreamExt}; +use glam::Vec2; +use indexmap::IndexMap; use itertools::Itertools; -use tracing_subscriber::{ - filter::LevelFilter, fmt::format::Pretty, layer::SubscriberExt, util::SubscriberInitExt, Layer, -}; -use tracing_web::{performance_layer, MakeWebConsoleWriter}; +use rfd::AsyncFileDialog; +use serde::{Deserialize, Serialize}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use violet::{ core::{ + declare_atom, layout::Alignment, state::{DynStateDuplex, State, StateMut, StateStream, StateStreamRef}, - style::{primary_background, Background, SizeExt, ValueOrRef}, - time::sleep, + style::{ + danger_item, primary_background, success_item, warning_item, Background, SizeExt, + ValueOrRef, + }, + time::interval, to_owned, unit::Unit, - utils::{throttle, zip_latest_ref}, + utils::zip_latest_ref, widget::{ - card, column, label, pill, row, Button, Radio, Rectangle, SliderWithLabel, Stack, + card, column, label, row, Button, Radio, Rectangle, SliderWithLabel, Stack, StreamWidget, Text, TextInput, WidgetExt, }, - Edges, Scope, Widget, + Edges, Frame, FutureEffect, Scope, Widget, }, futures_signals::signal::Mutable, - glam::vec2, palette::{FromColor, IntoColor, OklabHue, Oklch, Srgb}, web_time::Duration, - wgpu::renderer::RendererConfig, + wgpu::{app::App, renderer::RendererConfig}, }; use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] fn setup() { + use tracing_subscriber::{filter::LevelFilter, fmt::format::Pretty, Layer}; + use tracing_web::{performance_layer, MakeWebConsoleWriter}; + let fmt_layer = tracing_subscriber::fmt::layer() .with_ansi(false) .without_time() @@ -66,7 +74,7 @@ fn setup() { pub fn run() { setup(); - violet::wgpu::App::new() + App::builder() .with_renderer_config(RendererConfig { debug_mode: false }) .run(MainApp) .unwrap(); @@ -84,55 +92,47 @@ impl Widget for MainApp { Mutable::new(PaletteColor { color: Oklch::new(0.5, 0.27, (i as f32 * 60.0) % 360.0), falloff: DEFAULT_FALLOFF, + name: format!("color_{i}"), }) }) .collect(), ); - column((Palettes::new(palette_item),)) - .with_size(Unit::rel2(1.0, 1.0)) - .with_background(Background::new(primary_background())) - .contain_margins(true) - .mount(scope); - } -} + let (notify_tx, notify_rx) = flume::unbounded(); -struct Tints { - base: Oklch, - falloff: f32, -} + scope.frame_mut().set_atom(crate::notify_tx(), notify_tx); -impl Tints { - fn new(base: Oklch, falloff: f32) -> Self { - Self { base, falloff } + Stack::new(( + Palettes::new(palette_item), + Stack::new(Notifications { + items: notify_rx.into_stream(), + }) + .with_maximize(Vec2::ONE) + .with_horizontal_alignment(Alignment::End), + )) + .with_size(Unit::rel2(1.0, 1.0)) + .with_background(Background::new(primary_background())) + .mount(scope); } } -impl Widget for Tints { - fn mount(self, scope: &mut Scope<'_>) { - puffin::profile_function!(); - row((1..=9) - .map(|i| { +fn tints(color: impl StateStream) -> impl Widget { + puffin::profile_function!(); + row((1..=9) + .map(move |i| { + let color = color.stream().map(move |v| { let f = (i as f32) / 10.0; - let chroma = self.base.chroma * (1.0 / (1.0 + self.falloff * (f - 0.5).powi(2))); + let color = v.tint(f); - // let color = self.base.lighten(f); - let color = Oklch { - chroma, - l: f, - ..self.base - }; + Rectangle::new(ValueOrRef::value(color.into_color())) + .with_size(Unit::px2(80.0, 60.0)) + }); - Stack::new(column((Rectangle::new(ValueOrRef::value( - color.into_color(), - )) - .with_size(Unit::px2(80.0, 60.0)),))) + Stack::new(column(StreamWidget(color))) .with_margin(Edges::even(4.0)) .with_name("Tint") - }) - .collect_vec()) - .mount(scope) - } + }) + .collect_vec()) } pub fn color_hex(color: impl IntoColor) -> String { @@ -150,8 +150,13 @@ impl Palettes { } } +declare_atom! { + notify_tx: flume::Sender, +} + impl Widget for Palettes { fn mount(self, scope: &mut Scope<'_>) { + let notify_tx = scope.frame().get_atom(notify_tx()).unwrap().clone(); let items = self.items.clone(); let discard = move |i| { @@ -196,9 +201,7 @@ impl Widget for Palettes { card(row(( checkbox, discard(i), - StreamWidget(throttle(item.stream(), || { - sleep(Duration::from_millis(100)) - })), + palette_color_view(item.clone()), ))) } }) @@ -211,6 +214,7 @@ impl Widget for Palettes { let items = self.items.clone(); column(( + menu_bar(self.items.clone(), notify_tx), StreamWidget(editor), palettes, Button::label("+").on_press(move |_, _| { @@ -218,6 +222,7 @@ impl Widget for Palettes { v.push(Mutable::new(PaletteColor { color: Oklch::new(0.5, 0.27, (v.len() as f32 * 60.0) % 360.0), falloff: DEFAULT_FALLOFF, + name: format!("color_{}", v.len() + 1), })); current_choice.set(Some(v.len() - 1)); }) @@ -227,23 +232,243 @@ impl Widget for Palettes { } } -#[derive(Debug, Clone, Copy)] +struct Notification { + message: String, + kind: NotificationKind, +} + +enum NotificationKind { + Info, + Warning, + Error, +} + +pub struct Notifications { + items: S, +} + +impl Notifications { + pub fn new(items: S) -> Self { + Self { items } + } +} + +impl Widget for Notifications +where + S: 'static + Stream, +{ + fn mount(self, scope: &mut Scope<'_>) { + let notifications = Mutable::new(Vec::new()); + + let notifications_stream = notifications.stream_ref(|v| { + let items = v + .iter() + .map(|(_, v): &(f32, Notification)| { + let color = match v.kind { + NotificationKind::Info => success_item(), + NotificationKind::Warning => warning_item(), + NotificationKind::Error => danger_item(), + }; + card(label(v.message.clone())).with_background(Background::new(color)) + }) + .collect_vec(); + + column(items) + }); + + scope.spawn(async move { + let stream = self.items; + + let mut interval = interval(Duration::from_secs(1)).fuse(); + + let stream = stream.fuse(); + futures::pin_mut!(stream); + + loop { + futures::select! { + _ = interval.next() => { + let notifications = &mut *notifications.lock_mut(); + notifications.retain(|(time, _)| *time > 0.0); + for (time, _) in notifications { + *time -= 1.0; + } + }, + notification = stream.select_next_some() => { + notifications.lock_mut().push((5.0, notification)); + } + complete => break, + } + } + }); + + StreamWidget(notifications_stream).mount(scope); + } +} + +fn menu_bar( + items: Mutable>>, + notify_tx: Sender, +) -> impl Widget { + fn notify_result( + notify_tx: &Sender, + on_success: &str, + ) -> impl Fn(&mut Frame, anyhow::Result<()>) { + let notify_tx = notify_tx.clone(); + move |_, result| match result { + Ok(()) => { + notify_tx + .send(Notification { + message: "Saved".to_string(), + kind: NotificationKind::Info, + }) + .unwrap(); + } + Err(e) => { + notify_tx + .send(Notification { + message: format!("Failed to save: {e}"), + kind: NotificationKind::Error, + }) + .unwrap(); + } + } + } + + let export = Button::label("Export Tints").on_press({ + to_owned![items, notify_tx]; + move |frame, _| { + let data = items + .lock_ref() + .iter() + .map(|item| { + let item = item.lock_ref(); + let tints = (1..=9) + .map(|i| { + let color = item.tint(i as f32 / 10.0); + ( + format!("{}", i * 100), + HexColor(Srgb::from_color(color).into_format()), + ) + }) + .collect::>(); + + (item.name.clone(), tints) + }) + .collect::>(); + + let json = serde_json::to_string_pretty(&data).unwrap(); + + let fut = async move { + let Some(file) = AsyncFileDialog::new().set_directory(".").save_file().await else { + return anyhow::Ok(()); + }; + + file.write(json.as_bytes()) + .await + .context("Failed to write to save file")?; + + Ok(()) + }; + + frame.spawn(FutureEffect::new(fut, notify_result(¬ify_tx, "Saves"))); + } + }); + + let save = Button::label("Save").on_press({ + to_owned![items, notify_tx]; + move |frame, _| { + to_owned![items, notify_tx]; + let fut = async move { + let Some(file) = AsyncFileDialog::new().set_directory(".").save_file().await else { + return anyhow::Ok(()); + }; + + let items = items.lock_ref(); + let data = + serde_json::to_string_pretty(&*items).context("Failed to serialize state")?; + + file.write(data.as_bytes()) + .await + .context("Failed to write to save file")?; + + Ok(()) + }; + + frame.spawn(FutureEffect::new(fut, notify_result(¬ify_tx, "Saves"))); + } + }); + + let load = Button::label("Load").on_press({ + to_owned![items, notify_tx]; + move |frame, _| { + to_owned![items, notify_tx]; + let fut = async move { + let Some(file) = AsyncFileDialog::new().set_directory(".").pick_file().await else { + return anyhow::Ok(()); + }; + + let data = file.read().await; + + let data = serde_json::from_slice(&data).context("Failed to deserialize state")?; + + items.set(data); + + Ok(()) + }; + + frame.spawn(FutureEffect::new(fut, notify_result(¬ify_tx, "Loaded"))); + } + }); + + let test_notification = Button::label("Test Notification").on_press({ + to_owned![notify_tx]; + move |_, _| { + notify_tx + .send(Notification { + message: "Test notification".to_string(), + kind: NotificationKind::Info, + }) + .unwrap(); + } + }); + + row(( + label("Palette editor"), + save, + load, + export, + test_notification, + )) +} + +#[derive(Clone, Serialize, Deserialize)] pub struct PaletteColor { color: Oklch, falloff: f32, + name: String, } -impl Widget for PaletteColor { - fn mount(self, scope: &mut Scope<'_>) { - puffin::profile_function!(); - Stack::new(( - row((Tints::new(self.color, self.falloff),)), - pill(label(color_hex(self.color))), - )) +impl PaletteColor { + pub fn tint(&self, tint: f32) -> Oklch { + let chroma = self.color.chroma * (1.0 / (1.0 + self.falloff * (tint - 0.5).powi(2))); + // let color = self.base.lighten(f); + Oklch { + chroma, + l: tint, + ..self.color + } + } +} + +fn palette_color_view(color: Mutable) -> impl Widget { + puffin::profile_function!(); + // let label = color.stream().map(|v| label(color_hex(v.color))); + let label = color.clone().map_ref(|v| &v.name, |v| &mut v.name); + + let label = TextInput::new(label); + Stack::new((row((tints(color),)), label)) .with_vertical_alignment(Alignment::End) .with_horizontal_alignment(Alignment::Center) - .mount(scope) - } } pub struct PaletteEditor { @@ -330,3 +555,55 @@ impl Widget for ColorHexEditor { TextInput::new(value).mount(scope) } } + +pub struct HexColor(Srgb); + +impl Serialize for HexColor { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let s = format!( + "#{:0>2x}{:0>2x}{:0>2x}", + self.0.red, self.0.green, self.0.blue + ); + + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for HexColor { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + let color: Srgb = s.trim().parse().map_err(serde::de::Error::custom)?; + Ok(HexColor(color)) + } +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct TintsData { + #[serde(rename = "100")] + _100: HexColor, + #[serde(rename = "200")] + _200: HexColor, + #[serde(rename = "300")] + _300: HexColor, + #[serde(rename = "400")] + _400: HexColor, + #[serde(rename = "500")] + _500: HexColor, + #[serde(rename = "600")] + _600: HexColor, + #[serde(rename = "700")] + _700: HexColor, + #[serde(rename = "800")] + _800: HexColor, + #[serde(rename = "900")] + _900: HexColor, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ColorPalettes(HashMap); diff --git a/violet-wgpu/src/app.rs b/violet-wgpu/src/app.rs index 92eecf4..c2391f5 100644 --- a/violet-wgpu/src/app.rs +++ b/violet-wgpu/src/app.rs @@ -8,7 +8,7 @@ use parking_lot::Mutex; use winit::{ dpi::{LogicalSize, PhysicalSize}, event::{Event, WindowEvent}, - event_loop::{EventLoopBuilder, EventLoopWindowTarget}, + event_loop::{ControlFlow, EventLoopBuilder, EventLoopWindowTarget}, window::WindowBuilder, }; @@ -58,11 +58,11 @@ impl Widget for Canvas { } } -pub struct App { +pub struct AppBuilder { renderer_config: RendererConfig, } -impl App { +impl AppBuilder { pub fn new() -> Self { Self { renderer_config: Default::default(), @@ -85,7 +85,7 @@ impl App { let event_loop = EventLoopBuilder::new().build()?; #[allow(unused_mut)] - let mut builder = WindowBuilder::new().with_inner_size(PhysicalSize::new(800, 600)); + let mut builder = WindowBuilder::new().with_inner_size(PhysicalSize::new(1920, 1080)); #[cfg(target_arch = "wasm32")] { @@ -174,7 +174,7 @@ impl App { #[cfg(not(target_arch = "wasm32"))] let _puffin_server = setup_puffin(); - let mut instance = AppInstance { + let mut instance = App { frame, renderer: None, root, @@ -198,10 +198,6 @@ impl App { instance.update(); - if let Err(err) = instance.draw() { - tracing::error!("Failed to draw to window: {err:?}"); - } - if !instance.is_minimized() { let report = instance.stats.report(); window.set_title(&format!( @@ -209,6 +205,9 @@ impl App { report.min_frame_time, report.average_frame_time, report.max_frame_time, )); } + + ctl.set_control_flow(ControlFlow::Poll); + window.request_redraw(); puffin::GlobalProfiler::lock().new_frame(); } Event::WindowEvent { window_id, event } => match event { @@ -261,10 +260,10 @@ impl App { } }; - // #[cfg(not(target_arch = "wasm32"))] - // { - // event_loop.run(on_event)?; - // } + #[cfg(not(target_arch = "wasm32"))] + { + event_loop.run(on_event)?; + } #[cfg(target_arch = "wasm32")] { use winit::platform::web::EventLoopExtWebSys; @@ -275,7 +274,8 @@ impl App { } } -pub struct AppInstance { +/// A running application instance of violet +pub struct App { frame: Frame, renderer: Option, root: Entity, @@ -288,7 +288,11 @@ pub struct AppInstance { window_size: PhysicalSize, } -impl AppInstance { +impl App { + pub fn builder() -> AppBuilder { + AppBuilder::new() + } + pub fn on_resize(&mut self, size: PhysicalSize) { self.window_size = size; @@ -361,7 +365,7 @@ fn setup_puffin() -> Option { Some(server) } -impl Default for App { +impl Default for AppBuilder { fn default() -> Self { Self::new() } diff --git a/violet-wgpu/src/lib.rs b/violet-wgpu/src/lib.rs index a94225a..2290faa 100644 --- a/violet-wgpu/src/lib.rs +++ b/violet-wgpu/src/lib.rs @@ -10,4 +10,4 @@ mod texture; pub use graphics::Gpu; -pub use app::App; +pub use app::AppBuilder; diff --git a/violet-wgpu/src/renderer/rect_renderer.rs b/violet-wgpu/src/renderer/rect_renderer.rs index 8b237a1..915eafa 100644 --- a/violet-wgpu/src/renderer/rect_renderer.rs +++ b/violet-wgpu/src/renderer/rect_renderer.rs @@ -5,16 +5,17 @@ use flax::{ filter::{All, With}, CommandBuffer, Component, EntityIds, Fetch, FetchExt, Mutable, Opt, OptOr, Query, }; -use glam::{vec2, vec3, Mat4, Quat, Vec4}; +use glam::{vec2, vec3, Mat4, Quat, Vec2, Vec4}; use image::{DynamicImage, ImageBuffer}; use palette::Srgba; use wgpu::{BindGroup, BindGroupLayout, SamplerDescriptor, ShaderStages, TextureFormat}; use violet_core::{ assets::{map::HandleMap, Asset, AssetCache, AssetKey}, - components::{color, draw_shape, image, screen_rect}, + components::{anchor, color, draw_shape, image, rotation, screen_rect}, shape::{self, shape_rectangle}, stored::{self, WeakHandle}, + unit::Unit, Frame, Rect, }; @@ -63,6 +64,8 @@ impl AssetKey for ImageFromColor { #[derive(Fetch)] struct RectObjectQuery { screen_rect: Component, + rotation: OptOr, f32>, + anchor: OptOr>, Unit>, // pos: Component, // local_pos: Component, color: OptOr, Srgba>, @@ -73,6 +76,8 @@ impl RectObjectQuery { fn new() -> Self { Self { screen_rect: screen_rect(), + rotation: rotation().opt_or(0.0), + anchor: anchor().opt_or_default(), object_data: object_data().as_mut(), color: color().opt_or(Srgba::new(1.0, 1.0, 1.0, 1.0)), } @@ -231,11 +236,12 @@ impl RectRenderer { // return; // } - let model_matrix = Mat4::from_scale_rotation_translation( - rect.size().extend(1.0), - Quat::IDENTITY, - rect.pos().extend(0.1), - ); + let anchor = item.anchor.resolve(rect.size()).extend(0.0); + + let model_matrix = Mat4::from_translation(rect.pos().extend(0.1) + anchor) + * Mat4::from_rotation_z(*item.rotation) + * Mat4::from_translation(-anchor) + * Mat4::from_scale(rect.size().extend(1.0)); *item.object_data = ObjectData { model_matrix,