diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d8f25b --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Generated by Cargo +.cargo + +# will have compiled files and executables +/target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# IDE specific +.vscode +.idea diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4b903b1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,5925 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e53b0a3d5760cd2ba9b787ae0c6440ad18ee294ff71b05e3381c900a7d16cfd" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "accesskit" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4700bdc115b306d6c43381c344dc307f03b7f0460c304e4892c309930322bd7" + +[[package]] +name = "accesskit_atspi_common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1de72dc7093910a1284cef784b6b143bab0a34d67f6178e4fc3aaaf29a09f8b" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "thiserror", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe3a07a32ab5837ad83db3230ac490c8504c2cd5b90ac8c00db6535f6ed65d0b" +dependencies = [ + "accesskit", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_macos" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a189d159c153ae0fce5f9eefdcfec4a27885f453ce5ef0ccf078f72a73c39d34" +dependencies = [ + "accesskit", + "accesskit_consumer", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", +] + +[[package]] +name = "accesskit_unix" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b76c448cfd96d16131a9ad3ab786d06951eb341cdac1db908978ab010245a19d" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "async-channel 2.3.1", + "async-executor", + "async-task", + "atspi", + "futures-lite 1.13.0", + "futures-util", + "serde", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "682d8c4fb425606f97408e7577793f32e96310b646fa77662eb4216293eddc7f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "paste", + "static_assertions", + "windows 0.54.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afbd6d598b7c035639ad2b664aa0edc94c93dc1fc3ebb4b40d8a95fcd43ffac" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-activity" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +dependencies = [ + "android-properties", + "bitflags 2.6.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk 0.8.0", + "ndk-context", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.6.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk 0.9.0", + "ndk-context", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_log-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" + +[[package]] +name = "android_logger" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[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 0.5.2", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.1.0", + "futures-lite 2.3.0", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io 2.3.2", + "async-lock 3.3.0", + "blocking", + "futures-lite 2.3.0", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[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 2.3.0", + "parking", + "polling 3.7.0", + "rustix 0.38.34", + "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-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.34", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "async-signal" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" +dependencies = [ + "async-io 2.3.2", + "async-lock 3.3.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.34", + "signal-hook-registry", + "slab", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atspi" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" +dependencies = [ + "atspi-common", + "atspi-connection", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-connection" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite 1.13.0", + "zbus", +] + +[[package]] +name = "atspi-proxies" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" +dependencies = [ + "atspi-common", + "serde", + "zbus", +] + +[[package]] +name = "auto_enums" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1899bfcfd9340ceea3533ea157360ba8fa864354eccbceab58e1006ecab35393" +dependencies = [ + "derive_utils", + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.65", + "which", +] + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block" +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 = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +dependencies = [ + "async-channel 2.3.1", + "async-lock 3.3.0", + "async-task", + "futures-io", + "futures-lite 2.3.0", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + +[[package]] +name = "bytemuck" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[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 3.7.0", + "rustix 0.38.34", + "slab", + "thiserror", +] + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.6.0", + "log", + "polling 3.7.0", + "rustix 0.38.34", + "slab", + "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 0.38.34", + "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", + "rustix 0.38.34", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.5", +] + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading 0.8.3", +] + +[[package]] +name = "clipboard-win" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +dependencies = [ + "lazy-bytes-cast", + "winapi", +] + +[[package]] +name = "clru" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "codemap" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" + +[[package]] +name = "codemap-diagnostic" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc20770be05b566a963bf91505e60412c4a2d016d1ef95c5512823bb085a8122" +dependencies = [ + "codemap", + "termcolor", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "const-field-offset" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fcde4ca1211b5a94b573083c472ee19e86b19a441913f66e1cc5c41daf0255" +dependencies = [ + "const-field-offset-macro", + "field-offset", +] + +[[package]] +name = "const-field-offset-macro" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5387f5bbc9e9e6c96436ea125afa12614cebf8ac67f49abc08c1e7a891466c90" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "copypasta" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb85422867ca93da58b7f95fb5c0c10f6183ed6e1ef8841568968a896d3a858" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "countme" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" + +[[package]] +name = "cpp" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa65869ef853e45c60e9828aa08cdd1398cb6e13f3911d9cb2a079b144fcd64" +dependencies = [ + "cpp_macros", +] + +[[package]] +name = "cpp_build" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e361fae2caf9758164b24da3eedd7f7d7451be30d90d8e7b5d2be29a2f0cf5b" +dependencies = [ + "cc", + "cpp_common", + "lazy_static", + "proc-macro2", + "regex", + "syn 2.0.65", + "unicode-xid", +] + +[[package]] +name = "cpp_common" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1a2532e4ed4ea13031c13bc7bc0dbca4aae32df48e9d77f0d1e743179f2ea1" +dependencies = [ + "lazy_static", + "proc-macro2", + "syn 2.0.65", +] + +[[package]] +name = "cpp_macros" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ec9cc90633446f779ef481a9ce5a0077107dd5b87016440448d908625a83fd" +dependencies = [ + "aho-corasick", + "byteorder", + "cpp_common", + "lazy_static", + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +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 = "css-color-parser2" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8ed1639f4b56ec6f31d007ff66ce4a13099dce5a9995d48368a30d62bf04bd" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "ctor-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "derive_utils" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61bb5a1014ce6dfc2a378578509abe775a5aa06bff584a547555d9efdb81b926" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[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 = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.3", +] + +[[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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf9159ef4bcecd0c5e4cbeb573b8d0037493403d542780dba5d840bbf9df56f" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "nix 0.26.4", +] + +[[package]] +name = "drm-ffi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1352481b7b90e27a8a1bf8ef6b33cf18b98dba7c410e75c24bb3eef2f0d8d525" +dependencies = [ + "drm-sys", + "nix 0.26.4", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1369f1679d6b706d234c4c1e0613c415c2c74b598a09ad28080ba2474b72e42d" +dependencies = [ + "libc", +] + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "serde", + "serde_derive", + "winapi", + "wio", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[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.65", +] + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "euclid" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" +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 = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[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.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.0", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "femtovg" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47921d14afc4daad9bedc926099bc6edcaa23e37a957448f86cdefcbafe2f632" +dependencies = [ + "bitflags 2.6.0", + "fnv", + "glow", + "image 0.25.2", + "imgref", + "log", + "lru", + "rgb", + "rustybuzz", + "slotmap", + "unicode-bidi", + "unicode-segmentation", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset 0.9.1", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fontconfig-parser" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" +dependencies = [ + "roxmltree 0.19.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 0.9.4", + "slotmap", + "tinyvec", + "ttf-parser", +] + +[[package]] +name = "fontdue" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efe23d02309319171d00d794c9ff48d4f903c0e481375b1b04b017470838af04" +dependencies = [ + "hashbrown", + "ttf-parser", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +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-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.1.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gbm" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ec389cda876966cf824111bf6e533fb934c711d473498279964a990853b3c6" +dependencies = [ + "bitflags 1.3.2", + "drm", + "drm-fourcc", + "gbm-sys", + "libc", +] + +[[package]] +name = "gbm-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63eba9b9b7a231514482deb08759301c9f9f049ac6869403f381834ebfeaf67" +dependencies = [ + "libc", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2491aa3090f682ddd920b184491844440fdd14379c7eef8f5bc10ef7fb3242fd" +dependencies = [ + "bitflags 2.6.0", + "cfg_aliases", + "cgl", + "core-foundation", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "libloading 0.8.3", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "raw-window-handle", + "wayland-sys", + "windows-sys 0.52.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827" +dependencies = [ + "gl_generator", + "windows-sys 0.52.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "i-slint-backend-android-activity" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da124c519f5defb8f25a3bb000396e11b0c32c799237e45ac1f1514be36472f" +dependencies = [ + "android-activity 0.5.2", + "android-activity 0.6.0", + "i-slint-core", + "i-slint-renderer-skia", + "jni", + "ndk 0.9.0", + "raw-window-handle", +] + +[[package]] +name = "i-slint-backend-linuxkms" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17924cec94f3411fb3b2ef25259ceca0eae837db5387e13c4052c3e1fcccc190" +dependencies = [ + "bytemuck", + "calloop 0.12.4", + "drm", + "gbm", + "gbm-sys", + "glutin", + "i-slint-common", + "i-slint-core", + "i-slint-renderer-femtovg", + "input", + "memmap2 0.9.4", + "nix 0.27.1", + "raw-window-handle", + "xkbcommon", +] + +[[package]] +name = "i-slint-backend-qt" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bc990b005abc9dd10b133e01398a76d3894af8763740fe683304056006f2d88" +dependencies = [ + "const-field-offset", + "cpp", + "cpp_build", + "i-slint-common", + "i-slint-core", + "i-slint-core-macros", + "lyon_path", + "once_cell", + "pin-project", + "pin-weak", + "qttypes", + "vtable", +] + +[[package]] +name = "i-slint-backend-selector" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1398edffc061119b3fc547e5cde49683d47de806f1d8e431faa913a995c66d12" +dependencies = [ + "cfg-if", + "i-slint-backend-linuxkms", + "i-slint-backend-qt", + "i-slint-backend-winit", + "i-slint-common", + "i-slint-core", +] + +[[package]] +name = "i-slint-backend-winit" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc34a993838bd2d3625f1f64ae4324b490dbb0e1e410a0be71fffaae1df825" +dependencies = [ + "accesskit", + "accesskit_winit", + "bytemuck", + "cfg-if", + "cfg_aliases", + "cocoa", + "const-field-offset", + "copypasta", + "derive_more", + "glutin", + "glutin-winit", + "i-slint-common", + "i-slint-core", + "i-slint-core-macros", + "i-slint-renderer-femtovg", + "i-slint-renderer-skia", + "imgref", + "lyon_path", + "once_cell", + "pin-weak", + "raw-window-handle", + "rgb", + "scoped-tls-hkt", + "scopeguard", + "softbuffer", + "wasm-bindgen", + "web-sys", + "winit", +] + +[[package]] +name = "i-slint-common" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79721ae5b531dc2c5e1ecc2160e042c2563e0bf7cde688e0f27fc3feb90ccd60" +dependencies = [ + "cfg-if", + "derive_more", + "fontdb", + "libloading 0.8.3", +] + +[[package]] +name = "i-slint-compiler" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc4854b2842ef0858d6c5a483e0125b8db70a84d270497743d104f322314b97" +dependencies = [ + "by_address", + "codemap", + "codemap-diagnostic", + "css-color-parser2", + "derive_more", + "fontdue", + "i-slint-common", + "image 0.24.9", + "itertools 0.13.0", + "linked_hash_set", + "lyon_extra", + "lyon_path", + "num_enum", + "once_cell", + "proc-macro2", + "quote", + "resvg", + "rowan", + "smol_str", + "strum", + "thiserror", + "url", +] + +[[package]] +name = "i-slint-core" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d980dabb573ca810538a47647c9ab98c58daa97a06d51d912300ba5abc719bb0" +dependencies = [ + "auto_enums", + "bitflags 2.6.0", + "bytemuck", + "cfg-if", + "chrono", + "clru", + "const-field-offset", + "derive_more", + "euclid", + "fontdue", + "i-slint-common", + "i-slint-core-macros", + "image 0.24.9", + "integer-sqrt", + "lyon_algorithms", + "lyon_extra", + "lyon_geom", + "lyon_path", + "num-traits", + "once_cell", + "pin-project", + "pin-weak", + "portable-atomic", + "raw-window-handle", + "resvg", + "rgb", + "rustybuzz", + "scoped-tls-hkt", + "scopeguard", + "slab", + "static_assertions", + "strum", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", + "vtable", + "wasm-bindgen", + "web-sys", + "web-time", +] + +[[package]] +name = "i-slint-core-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f38c93191fc4264887ac9fdcd8537f81353df9be20254039d880f36bad97ac1" +dependencies = [ + "quote", + "syn 2.0.65", +] + +[[package]] +name = "i-slint-renderer-femtovg" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85a2de6699a2e2d4faaa6780621366c5363de8580af53dc5edeafa408b92686" +dependencies = [ + "cfg-if", + "const-field-offset", + "core-foundation", + "core-text", + "derive_more", + "dwrote", + "femtovg", + "glow", + "i-slint-common", + "i-slint-core", + "i-slint-core-macros", + "imgref", + "lyon_path", + "once_cell", + "pin-weak", + "rgb", + "scoped-tls-hkt", + "ttf-parser", + "unicode-script", + "unicode-segmentation", + "wasm-bindgen", + "web-sys", + "winapi", +] + +[[package]] +name = "i-slint-renderer-skia" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535141cb4349f3677d097a46170b07cc11672dbb1dbdc7eade4a3b6f4ed2a110" +dependencies = [ + "bytemuck", + "cfg-if", + "cfg_aliases", + "cocoa", + "const-field-offset", + "core-foundation", + "core-graphics-types", + "derive_more", + "foreign-types 0.5.0", + "glow", + "glutin", + "i-slint-common", + "i-slint-core", + "i-slint-core-macros", + "lyon_path", + "metal", + "objc", + "once_cell", + "pin-weak", + "raw-window-handle", + "scoped-tls-hkt", + "skia-safe", + "softbuffer", + "unicode-segmentation", + "vtable", + "windows 0.56.0", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "image" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", +] + +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + +[[package]] +name = "immutable-chunkmap" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4419f022e55cc63d5bbd6b44b71e1d226b9c9480a47824c706e9d54e5c40c5eb" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "input" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059" +dependencies = [ + "bitflags 1.3.2", + "input-sys", + "io-lifetimes", + "libc", + "log", + "udev", +] + +[[package]] +name = "input-sys" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0" + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "java-locator" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90003f2fd9c52f212c21d8520f1128da0080bad6fff16b68fe6e7f2f0c3780c2" +dependencies = [ + "glob", + "lazy_static", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "java-locator", + "jni-sys", + "libloading 0.7.4", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kurbo" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy-bytes-cast" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.6.0", + "libc", + "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 = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" + +[[package]] +name = "lyon_algorithms" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3bca95f9a4955b3e4a821fbbcd5edfbd9be2a9a50bb5758173e5358bfb4c623" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_extra" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4a243ce61e7e5f3ae6c72a88d8fb081b6c69f13500c15e99cfd1159a833b20" +dependencies = [ + "lyon_path", + "thiserror", +] + +[[package]] +name = "lyon_geom" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edecfb8d234a2b0be031ab02ebcdd9f3b9ee418fb35e265f7a540a48d197bff9" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c08a606c7a59638d6c6aa18ac91a06aa9fb5f765a7efb27e6a4da58700740d7" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +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.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "dispatch", + "libc", + "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]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.3.1+3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "openweather_sdk" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee22240238746180b55cdf7278c1ac5fb5c59f6dbff7f7d25d8d072c0dc74a0" +dependencies = [ + "reqwest", + "serde", + "tokio", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox 0.0.2", +] + +[[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 = "owned_ttf_parser" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b41438d2fc63c46c74a2203bf5ccd82c41ba04347b2fcf5754f230b167067d5" +dependencies = [ + "ttf-parser", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pico-args" +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.65", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pin-weak" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b330c9d1b92dfe68442ca20b009c717d5f0b1e3cf4965e62f704c3c6e95a1305" + +[[package]] +name = "piper" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464db0c665917b13ebb5d453ccdec4add5658ee1adc7affc7677615356a8afaf" +dependencies = [ + "atomic-waker", + "fastrand 2.1.0", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 0.38.34", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +dependencies = [ + "critical-section", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.65", +] + +[[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 = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro2" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "qttypes" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d405b0934e4176fb264d6ca597e5c8c2fba562fa155cba681ca160c1eb7edc6e" +dependencies = [ + "cpp", + "cpp_build", + "semver", +] + +[[package]] +name = "quick-xml" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox 0.1.3", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "resvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rowan" +version = "0.15.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49" +dependencies = [ + "countme", + "hashbrown", + "memoffset 0.9.1", + "rustc-hash", + "text-size", +] + +[[package]] +name = "roxmltree" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "rusty-weather" +version = "1.0.0" +dependencies = [ + "android_logger", + "async-std", + "chrono", + "console_error_panic_hook", + "console_log", + "directories", + "env_logger", + "log", + "openssl", + "openweather_sdk", + "serde", + "serde_json", + "slint", + "slint-build", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scoped-tls-hkt" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ddc765d3410d9f6c6ca071bf0b67f6b01e3ec4595dc3892f02677e75819dddc" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.4", + "smithay-client-toolkit 0.19.2", + "tiny-skia", +] + +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.202" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "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.65", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "skia-bindings" +version = "0.75.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06e19e97660b09a381c6eb566849b63556b1a90b8e2c6ba2d146b3f5066847b" +dependencies = [ + "bindgen", + "cc", + "flate2", + "heck 0.5.0", + "lazy_static", + "regex", + "serde_json", + "tar", + "toml", +] + +[[package]] +name = "skia-safe" +version = "0.75.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad6e6f369522471b585c99427720b53aad04016fa4314e0a8cf23f17083a4e4c" +dependencies = [ + "bitflags 2.6.0", + "lazy_static", + "skia-bindings", + "windows 0.56.0", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slint" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37776c51062d2c7ab7b9d00612a0d425fd792216fd777c473aee41c531490e4" +dependencies = [ + "const-field-offset", + "i-slint-backend-android-activity", + "i-slint-backend-qt", + "i-slint-backend-selector", + "i-slint-core", + "i-slint-renderer-femtovg", + "num-traits", + "once_cell", + "pin-weak", + "slint-macros", + "vtable", +] + +[[package]] +name = "slint-build" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719fe028aee2f051b9eea782e4d7068fb9b0a1de18426f00d3b2097c79e0e659" +dependencies = [ + "i-slint-compiler", + "spin_on", + "thiserror", + "toml_edit 0.22.13", +] + +[[package]] +name = "slint-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d30b8166c46cf34a91d17bc63281f01fe78fd9017b405ae1624e9740f15fe89" +dependencies = [ + "i-slint-compiler", + "proc-macro2", + "quote", + "spin_on", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +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.4", + "rustix 0.38.34", + "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" +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", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.4", + "rustix 0.38.34", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols 0.32.3", + "wayland-protocols-wlr 0.3.3", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c091e7354ea8059d6ad99eace06dd13ddeedbb0ac72d40a9a6e7ff790525882d" +dependencies = [ + "libc", + "smithay-client-toolkit 0.18.1", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d623bff5d06f60d738990980d782c8c866997d9194cfe79ecad00aa2f76826dd" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "cfg_aliases", + "core-graphics", + "fastrand 2.1.0", + "foreign-types 0.5.0", + "js-sys", + "log", + "memmap2 0.9.4", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall 0.5.1", + "rustix 0.38.34", + "tiny-xlib", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.52.0", + "x11rb", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spin_on" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076e103ed41b9864aa838287efe5f4e3a7a0362dd00671ae62a212e5e4612da2" +dependencies = [ + "pin-utils", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.65", +] + +[[package]] +name = "svgtypes" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand 2.1.0", + "rustix 0.38.34", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "text-size" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d52f22673960ad13af14ff4025997312def1223bfa7c8e4949d099e6b3d5d1c" +dependencies = [ + "as-raw-xcb-connection", + "ctor-lite", + "libloading 0.8.3", + "pkg-config", + "tracing", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.7", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.13", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[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 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.8", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "udev" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a" +dependencies = [ + "libc", + "libudev-sys", + "pkg-config", +] + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.1", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-linebreak" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "unicode-script" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] +name = "unicode-xid" +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", +] + +[[package]] +name = "usvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vtable" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379cd4a9d99f35bcf1687282268b94b59f14b098cba210632605f39709230963" +dependencies = [ + "const-field-offset", + "portable-atomic", + "stable_deref_trait", + "vtable-macro", +] + +[[package]] +name = "vtable-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c1b85ec843d3bc60e9d65fa7e00ce6549416a25c267b5ea93e6c81e3aa66e5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.65", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wayland-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993" +dependencies = [ + "cc", + "downcast-rs", + "rustix 0.38.34", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943" +dependencies = [ + "bitflags 2.6.0", + "rustix 0.38.34", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.6.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" +dependencies = [ + "rustix 0.38.34", + "wayland-client", + "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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62989625a776e827cc0f15d41444a3cea5205b963c3a25be48ae1b52d6b4daaa" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79f2d57c7fcc6ab4d602adba364bf59a5c24de57bd194486bf9b8360e06bfc4" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.3", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.31.2", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.3", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.34", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-implement 0.53.0", + "windows-interface 0.53.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +dependencies = [ + "windows-core 0.56.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement 0.56.0", + "windows-interface 0.56.0", + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-implement" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "windows-interface" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winit" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4225ddd8ab67b8b59a2fee4b34889ebf13c0460c1c3fa297c58e21eb87801b33" +dependencies = [ + "ahash", + "android-activity 0.6.0", + "atomic-waker", + "bitflags 2.6.0", + "block2", + "bytemuck", + "calloop 0.13.0", + "cfg_aliases", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2 0.9.4", + "ndk 0.9.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.34", + "sctk-adwaita", + "smithay-client-toolkit 0.19.2", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.3", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "x11-clipboard" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98785a09322d7446e28a13203d2cae1059a0dd3dfb32cb06d0a225f023d8286" +dependencies = [ + "libc", + "x11rb", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.8.3", + "once_cell", + "rustix 0.38.34", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys 0.4.14", + "rustix 0.38.34", +] + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xdg-home" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.6.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "zbus" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io 1.13.0", + "async-lock 2.8.0", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.65", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zvariant" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..91f169e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "rusty-weather" +version = "1.0.0" +authors = ["FELGO GmbH "] +edition = "2021" +build = "build.rs" + +[features] +open_weather = ["dep:openweather_sdk", "dep:openssl"] + +[dependencies] +async-std = "1.12.0" +chrono = "0.4.38" +directories = "5.0.1" +log = "0.4.21" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.115" +slint = { version = "1.7", features = [ "backend-android-activity-06" ] } + +[target.'cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))'.dependencies] +env_logger = "0.11.3" + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.14.1" +openssl = { version = "0.10", features = ["vendored"], optional = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +openweather_sdk = { version = "0.1.8", optional = true } +tokio = "1.37.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +console_log = "1.0" +console_error_panic_hook = "0.1.7" +wasm-bindgen = "0.2" +wasm-bindgen-futures = "0.4" + +[build-dependencies] +slint-build = "1.7" + +# Android-activity / wasm support +[lib] +name="rusty_weather_lib" +crate-type = ["cdylib"] +path = "src/lib.rs" + +# Andoroid settings +# See more: https://github.com/rust-mobile/cargo-apk?tab=readme-ov-file#manifest +[package.metadata.android] +package = "com.felgo.demos.rustyweather" +resources = "android-res" +build_targets = [ "aarch64-linux-android" ] + +[package.metadata.android.sdk] +min_sdk_version = 29 +target_sdk_version = 32 + +[package.metadata.android.application] +label = "Rusty Weather" +icon = "@mipmap/ic_launcher" + +[[package.metadata.android.uses_permission]] +name = "android.permission.INTERNET" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..797a2bd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 FELGO GmbH + +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/README.md b/README.md new file mode 100644 index 0000000..02e8614 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Rusty Weather + +Rusty Weather is a weather application made by [Felgo](https://felgo.com/). + +The application retrieves weather data from the [OpenWeather](https://openweathermap.org/) API to provide: +* Real-time weather data, +* 8-day forecasts data, +* Temperatures at particular times of the day, +* Daily rain amount and rain probability, +* UV index level, +* support for various locations around the globe. + + The project demonstrates how to write a cross-platform Rust GUI application using the [Slint](https://slint.dev/) toolkit. + It includes subjects like: + * responsive layouts and adaptions for different screen sizes and orientations, + * providing fully customized look and feel with custom widgets, + * integrating Slint code with the Rust backend code, + * using `async` features with a Slint-based application and combining it with the Tokio runtime. + +### Check the application live [here](https://felgosdk.github.io/RustyWeather/). + +

+ + +

+ +## Weather data +To enable real weather data from the [OpenWeather](https://openweathermap.org/) API, you must enable the `open_weather` feature and provide the `RUSTY_WEATHER_API_KEY` environment variable with your API key at build time. The [OpenCall API](https://openweathermap.org/price#onecall) subscription is required. + +If you do not enable the feature or provide the key, the application loads the dummy data instead. + +**Note:** You cannot use real weather data for the WebAssembly target. + +# Supported platforms + +## Desktop +The application runs on all desktop platforms (Windows, Linux and macOS). + +To start the application, execute: + +``` +cargo run +``` + +## Android +To be able to compile the application for Android, you must follow an initial setup. The instruction is available in [Slint's documentation](https://snapshots.slint.dev/master/docs/rust/slint/android/#building-and-deploying). + +***Note:*** To build `openssl` for Android, you must ensure that the proper development libraries are available in the system and provided in the `PATH` variable. + +To start the application, execute: + +``` +cargo apk run --lib +``` + +## WebAssembly +It is also possible to embed the application in a web page. You need to install the `wasm-pack` crate for this. + +``` +cargo install wasm-pack +``` + +To build the application, execute: + +``` +wasm-pack build --target web --out-dir /pkg +``` + +To run locally: + +``` +cp /wasm/index.html / # you can also provide your HTML file +cd & python3 -m http.server +``` + +Now, you can access the application at http://localhost:8000/. diff --git a/android-res/mipmap-hdpi/ic_launcher.png b/android-res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..6190119 Binary files /dev/null and b/android-res/mipmap-hdpi/ic_launcher.png differ diff --git a/android-res/mipmap-ldpi/ic_launcher.png b/android-res/mipmap-ldpi/ic_launcher.png new file mode 100644 index 0000000..054e396 Binary files /dev/null and b/android-res/mipmap-ldpi/ic_launcher.png differ diff --git a/android-res/mipmap-mdpi/ic_launcher.png b/android-res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..5032804 Binary files /dev/null and b/android-res/mipmap-mdpi/ic_launcher.png differ diff --git a/android-res/mipmap-xhdpi/ic_launcher.png b/android-res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..55f61f9 Binary files /dev/null and b/android-res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android-res/mipmap-xxhdpi/ic_launcher.png b/android-res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..10dedb5 Binary files /dev/null and b/android-res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android-res/mipmap-xxxhdpi/ic_launcher.png b/android-res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..5b2d351 Binary files /dev/null and b/android-res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..de759d5 --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ +use std::env; + +fn main() { + env::set_var("SLINT_ENABLE_EXPERIMENTAL_FEATURES", "true"); + + slint_build::compile("ui/main.slint").unwrap(); +} diff --git a/docs/img/android-preview.png b/docs/img/android-preview.png new file mode 100644 index 0000000..d5f70c5 Binary files /dev/null and b/docs/img/android-preview.png differ diff --git a/docs/img/desktop-preview.png b/docs/img/desktop-preview.png new file mode 100644 index 0000000..33fc1ac Binary files /dev/null and b/docs/img/desktop-preview.png differ diff --git a/src/app_main.rs b/src/app_main.rs new file mode 100644 index 0000000..d7c636a --- /dev/null +++ b/src/app_main.rs @@ -0,0 +1,88 @@ +use std::sync::{Arc, Mutex}; + +use crate::ui::*; + +use crate::weather; +use weather::DummyWeatherController; +use weather::{WeatherControllerPointer, WeatherControllerSharedPointer, WeatherDisplayController}; + +#[cfg(all(not(target_arch = "wasm32"), feature = "open_weather"))] +use weather::OpenWeatherController; + +pub struct AppHandler { + weather_controller: WeatherControllerSharedPointer, + weather_display_controller: WeatherDisplayController, + window: Option, + support_add_city: bool, +} + +impl AppHandler { + pub fn new() -> Self { + #[cfg_attr( + any(target_arch = "wasm32", not(feature = "open_weather")), + allow(unused_mut) + )] + let mut support_add_city = false; + + #[cfg_attr( + any(target_arch = "wasm32", not(feature = "open_weather")), + allow(unused_mut) + )] + let mut data_controller_opt: Option = None; + + #[cfg(all(not(target_arch = "wasm32"), feature = "open_weather"))] + { + if let Some(api_key) = std::option_env!("RUSTY_WEATHER_API_KEY") { + data_controller_opt = Some(Box::new(OpenWeatherController::new(api_key.into()))); + support_add_city = true; + } + } + + let data_controller = match data_controller_opt { + Some(data_contoller_some) => data_contoller_some, + None => { + log::info!("Weather API key not provided. Using dummy data."); + Box::new(DummyWeatherController::new()) + } + }; + let data_controller: WeatherControllerSharedPointer = Arc::new(Mutex::new(data_controller)); + + Self { + weather_controller: data_controller.clone(), + weather_display_controller: WeatherDisplayController::new(&data_controller), + window: None, + support_add_city, + } + } + + pub fn save(&self) { + log::debug!("Saving state"); + if let Err(e) = self.weather_controller.lock().unwrap().save() { + log::warn!("Error while saving state: {}", e) + } + } + + #[cfg_attr(not(target_os = "android"), allow(dead_code))] + pub fn reload(&self) { + log::debug!("Reloading state"); + if let Some(window) = &self.window { + self.weather_display_controller.refresh(window); // load new weather data + } else { + log::warn!("Cannot reload state, window not available."); + } + } + + pub fn initialize_ui(&mut self) { + let window = AppWindow::new().expect("Cannot create main window!"); + self.weather_display_controller + .initialize_ui(&window, self.support_add_city); + + self.window = Some(window); + } + + pub fn run(&self) -> Result<(), slint::PlatformError> { + let window = self.window.as_ref().expect("Cannot access main window!"); + self.weather_display_controller.load(window); + window.run() + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..979cb31 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,91 @@ +#![cfg(any(target_os = "android", target_arch = "wasm32"))] + +pub mod ui { + slint::include_modules!(); +} + +mod app_main; +mod weather; + +use crate::app_main::AppHandler; + +// Android +#[cfg(target_os = "android")] +use { + crate::android_activity::{MainEvent, PollEvent}, + core::cell::RefCell, + slint::android::android_activity, + std::rc::Rc, +}; + +#[cfg(target_os = "android")] +#[no_mangle] +fn android_main(android_app: slint::android::AndroidApp) -> Result<(), slint::PlatformError> { + android_logger::init_once(android_logger::Config::default().with_max_level( + if cfg!(debug_assertions) { + log::LevelFilter::Debug + } else { + log::LevelFilter::Info + }, + )); + + let app_handler = Rc::new(RefCell::new(AppHandler::new())); + + // initialize android before creating main window + slint::android::init_with_event_listener(android_app, { + let app_handler = app_handler.clone(); + move |event| match event { + PollEvent::Main(main_event) => match main_event { + MainEvent::Start => { + app_handler.borrow().reload(); + } + MainEvent::Resume { .. } => { + app_handler.borrow().reload(); + } + MainEvent::SaveState { .. } => { + app_handler.borrow().save(); + } + _ => {} + }, + _ => {} + } + }) + .unwrap(); + + { + // create main window here + let mut app_handler = app_handler.borrow_mut(); + app_handler.initialize_ui(); + } + + let app_handler = app_handler.borrow(); + app_handler.run() +} + +// Wasm +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen::prelude::wasm_bindgen(start)] +pub fn main() { + #[cfg(debug_assertions)] + console_error_panic_hook::set_once(); + + console_log::init_with_level(if cfg!(debug_assertions) { + log::Level::Debug + } else { + log::Level::Info + }) + .ok(); + + let mut app_handler = AppHandler::new(); + app_handler.initialize_ui(); + + let res = app_handler.run(); + app_handler.save(); + + match res { + Ok(()) => {} + Err(e) => { + log::error!("Runtime error: {}", e); + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..446c7e2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,27 @@ +#![cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] + +use crate::app_main::AppHandler; + +pub mod ui { + slint::include_modules!(); +} + +mod app_main; +mod weather; + +fn main() -> Result<(), slint::PlatformError> { + env_logger::Builder::default() + .filter_level(if cfg!(debug_assertions) { + log::LevelFilter::Debug + } else { + log::LevelFilter::Info + }) + .init(); + + let mut app_handler = AppHandler::new(); + app_handler.initialize_ui(); + + let res = app_handler.run(); + app_handler.save(); + res +} diff --git a/src/weather/dummyweather.json b/src/weather/dummyweather.json new file mode 100644 index 0000000..e11d729 --- /dev/null +++ b/src/weather/dummyweather.json @@ -0,0 +1,620 @@ +[ + { + "city_data": { + "lat": 52.51703643798828, + "lon": 13.388859748840332, + "city_name": "Berlin" + }, + "weather_data": { + "current_data": { + "condition": "PartiallyCloudy", + "description": "broken clouds", + "current_temperature": 15.24, + "detailed_temperature": { + "min": 12.39, + "max": 19.27, + "morning": 14.01, + "day": 18.62, + "evening": 15.83, + "night": 13.8 + }, + "precipitation": { + "probability": 1, + "rain_volume": 2.77, + "snow_volume": 0 + }, + "uv_index": 2.3 + }, + "forecast_data": [ + { + "day_name": "d0", + "weather_data": { + "condition": "SunnyRainy", + "description": "moderate rain", + "current_temperature": 16.18, + "detailed_temperature": { + "min": 9.56, + "max": 17.94, + "morning": 10.37, + "day": 16.18, + "evening": 16.71, + "night": 12.45 + }, + "precipitation": { + "probability": 1, + "rain_volume": 2.77, + "snow_volume": 0 + }, + "uv_index": 3.82 + } + }, + { + "day_name": "d1", + "weather_data": { + "condition": "SunnyRainy", + "description": "moderate rain", + "current_temperature": 15.94, + "detailed_temperature": { + "min": 9.34, + "max": 18.96, + "morning": 10.31, + "day": 15.94, + "evening": 16.76, + "night": 12.56 + }, + "precipitation": { + "probability": 1, + "rain_volume": 2.87, + "snow_volume": 0 + }, + "uv_index": 3.82 + } + }, + { + "day_name": "d2", + "weather_data": { + "condition": "Cloudy", + "description": "broken clouds", + "current_temperature": 15.73, + "detailed_temperature": { + "min": 9.17, + "max": 17.9, + "morning": 10.65, + "day": 15.73, + "evening": 17.14, + "night": 12.34 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 5.16 + } + }, + { + "day_name": "d3", + "weather_data": { + "condition": "Cloudy", + "description": "broken clouds", + "current_temperature": 16.99, + "detailed_temperature": { + "min": 9.47, + "max": 17.69, + "morning": 10.72, + "day": 16.99, + "evening": 16.7, + "night": 14.37 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 5.6 + } + }, + { + "day_name": "d4", + "weather_data": { + "condition": "SunnyRainy", + "description": "light rain", + "current_temperature": 22.95, + "detailed_temperature": { + "min": 13.23, + "max": 22.95, + "morning": 15.19, + "day": 22.95, + "evening": 20.97, + "night": 17.48 + }, + "precipitation": { + "probability": 0.2, + "rain_volume": 0.15, + "snow_volume": 0 + }, + "uv_index": 5.97 + } + }, + { + "day_name": "d5", + "weather_data": { + "condition": "SunnyRainy", + "description": "light rain", + "current_temperature": 24.07, + "detailed_temperature": { + "min": 16.06, + "max": 24.85, + "morning": 18.61, + "day": 24.07, + "evening": 21.87, + "night": 17.92 + }, + "precipitation": { + "probability": 0.2, + "rain_volume": 0.19, + "snow_volume": 0 + }, + "uv_index": 6 + } + }, + { + "day_name": "d6", + "weather_data": { + "condition": "Cloudy", + "description": "overcast clouds", + "current_temperature": 23.27, + "detailed_temperature": { + "min": 14.73, + "max": 23.27, + "morning": 16.66, + "day": 23.27, + "evening": 22.33, + "night": 18.94 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 6 + } + }, + { + "day_name": "d7", + "weather_data": { + "condition": "SunnyRainy", + "description": "light rain", + "current_temperature": 25.15, + "detailed_temperature": { + "min": 16.69, + "max": 25.29, + "morning": 19.84, + "day": 25.15, + "evening": 24.47, + "night": 20.54 + }, + "precipitation": { + "probability": 0.87, + "rain_volume": 0.69, + "snow_volume": 0 + }, + "uv_index": 6 + } + } + ] + } + }, + { + "city_data": { + "lat": 48.20835494995117, + "lon": 16.37250328063965, + "city_name": "Vienna" + }, + "weather_data": { + "current_data": { + "condition": "Rainy", + "description": "light intensity shower rain", + "current_temperature": 17.33, + "detailed_temperature": { + "min": 15.84, + "max": 18.96, + "morning": 17.5, + "day": 17.33, + "evening": 16.89, + "night": 17.5 + }, + "precipitation": { + "probability": 1, + "rain_volume": 10.78, + "snow_volume": 0 + }, + "uv_index": 2.63 + }, + "forecast_data": [ + { + "day_name": "d0", + "weather_data": { + "condition": "SunnyRainy", + "description": "moderate rain", + "current_temperature": 17.74, + "detailed_temperature": { + "min": 15.84, + "max": 18.96, + "morning": 17.5, + "day": 17.74, + "evening": 16.89, + "night": 17.5 + }, + "precipitation": { + "probability": 1, + "rain_volume": 10.78, + "snow_volume": 0 + }, + "uv_index": 2.63 + } + }, + { + "day_name": "d1", + "weather_data": { + "condition": "Cloudy", + "description": "overcast clouds", + "current_temperature": 18.14, + "detailed_temperature": { + "min": 15.06, + "max": 20.2, + "morning": 15.76, + "day": 18.14, + "evening": 19.76, + "night": 16.07 + }, + "precipitation": { + "probability": 0.11, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 4.81 + } + }, + { + "day_name": "d2", + "weather_data": { + "condition": "SunnyRainy", + "description": "light rain", + "current_temperature": 12.23, + "detailed_temperature": { + "min": 12.08, + "max": 15.59, + "morning": 13.42, + "day": 12.23, + "evening": 13.86, + "night": 12.08 + }, + "precipitation": { + "probability": 1, + "rain_volume": 1.82, + "snow_volume": 0 + }, + "uv_index": 0.78 + } + }, + { + "day_name": "d3", + "weather_data": { + "condition": "SunnyRainy", + "description": "light rain", + "current_temperature": 18.83, + "detailed_temperature": { + "min": 11.93, + "max": 20.12, + "morning": 12.56, + "day": 18.83, + "evening": 19.76, + "night": 14.12 + }, + "precipitation": { + "probability": 1, + "rain_volume": 1.05, + "snow_volume": 0 + }, + "uv_index": 2.35 + } + }, + { + "day_name": "d4", + "weather_data": { + "condition": "MostlyCloudy", + "description": "scattered clouds", + "current_temperature": 21.13, + "detailed_temperature": { + "min": 11.73, + "max": 23.26, + "morning": 11.73, + "day": 21.13, + "evening": 23.24, + "night": 17.1 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 6.17 + } + }, + { + "day_name": "d5", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 23.55, + "detailed_temperature": { + "min": 14.71, + "max": 26, + "morning": 14.71, + "day": 23.55, + "evening": 25.7, + "night": 20.03 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 7 + } + }, + { + "day_name": "d6", + "weather_data": { + "condition": "SunnyRainy", + "description": "light rain", + "current_temperature": 21.59, + "detailed_temperature": { + "min": 16.28, + "max": 23.22, + "morning": 16.28, + "day": 21.59, + "evening": 22.76, + "night": 17.24 + }, + "precipitation": { + "probability": 0.38, + "rain_volume": 0.27, + "snow_volume": 0 + }, + "uv_index": 7 + } + }, + { + "day_name": "d7", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 24.89, + "detailed_temperature": { + "min": 14.89, + "max": 27.33, + "morning": 14.89, + "day": 24.89, + "evening": 27.33, + "night": 23.1 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 7 + } + } + ] + } + }, + { + "city_data": { + "lat": 25.774173736572266, + "lon": -80.19361877441406, + "city_name": "Miami" + }, + "weather_data": { + "current_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 20.14, + "detailed_temperature": { + "min": 20.14, + "max": 30.88, + "morning": 20.83, + "day": 30.88, + "evening": 26.41, + "night": 23.2 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 11.85 + }, + "forecast_data": [ + { + "day_name": "d0", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 30.88, + "detailed_temperature": { + "min": 20.14, + "max": 30.88, + "morning": 20.83, + "day": 30.88, + "evening": 26.41, + "night": 23.2 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 11.85 + } + }, + { + "day_name": "d1", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 28.94, + "detailed_temperature": { + "min": 21.56, + "max": 29.68, + "morning": 21.61, + "day": 28.94, + "evening": 26.24, + "night": 23.22 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 11.62 + } + }, + { + "day_name": "d2", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 29.32, + "detailed_temperature": { + "min": 21.09, + "max": 31.17, + "morning": 21.09, + "day": 29.32, + "evening": 26.99, + "night": 23.65 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 11.67 + } + }, + { + "day_name": "d3", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 27.11, + "detailed_temperature": { + "min": 21.26, + "max": 30.37, + "morning": 21.26, + "day": 27.11, + "evening": 28.03, + "night": 22.17 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 11.53 + } + }, + { + "day_name": "d4", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 25.7, + "detailed_temperature": { + "min": 19.67, + "max": 28.59, + "morning": 19.67, + "day": 25.7, + "evening": 27.08, + "night": 20.91 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 12 + } + }, + { + "day_name": "d5", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 25.27, + "detailed_temperature": { + "min": 18.6, + "max": 27.95, + "morning": 18.6, + "day": 25.27, + "evening": 25.42, + "night": 20 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 12 + } + }, + { + "day_name": "d6", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 26.84, + "detailed_temperature": { + "min": 19.13, + "max": 28.99, + "morning": 19.13, + "day": 26.84, + "evening": 27.56, + "night": 21.58 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 12 + } + }, + { + "day_name": "d7", + "weather_data": { + "condition": "Sunny", + "description": "clear sky", + "current_temperature": 28.99, + "detailed_temperature": { + "min": 20.87, + "max": 30.64, + "morning": 20.87, + "day": 28.99, + "evening": 29.22, + "night": 22.64 + }, + "precipitation": { + "probability": 0, + "rain_volume": 0, + "snow_volume": 0 + }, + "uv_index": 12 + } + } + ] + } + } +] diff --git a/src/weather/dummyweathercontroller.rs b/src/weather/dummyweathercontroller.rs new file mode 100644 index 0000000..95a5726 --- /dev/null +++ b/src/weather/dummyweathercontroller.rs @@ -0,0 +1,92 @@ +use chrono::{Duration, Utc}; + +use crate::weather::utils::*; +use crate::weather::weathercontroller::{ + CityData, CityWeatherData, GeoLocationData, WeatherController, +}; + +pub struct DummyWeatherController { + city_weather_data: Vec, +} + +impl DummyWeatherController { + pub fn new() -> Self { + Self { + city_weather_data: vec![], + } + } + + fn generate_dummy_data() -> Vec { + let json_data = std::include_str!("./dummyweather.json"); + + match serde_json::from_str::>(json_data) { + Ok(weather_data) => { + // fix day names + let mut weather_data = weather_data.clone(); + for city_data in &mut weather_data { + let forecast_data = &mut (city_data.weather_data.forecast_data); + for (index, data) in forecast_data.iter_mut().enumerate() { + if index == 0 { + data.day_name = "Today".into(); + } else { + data.day_name = + get_day_from_datetime(Utc::now() + Duration::days(index as i64)); + } + } + } + + return weather_data; + } + Err(e) => { + log::warn!("Cannot read dummy weather data! Error: {}", e); + } + } + + vec![] + } +} + +impl WeatherController for DummyWeatherController { + fn load(&mut self) -> Result<(), Box> { + self.city_weather_data = Self::generate_dummy_data(); + Ok(()) + } + + fn save(&self) -> Result<(), Box> { + Ok(()) + } + + fn refresh_cities(&mut self) -> Result, Box> { + Ok(self.city_weather_data.clone()) + } + + fn add_city( + &mut self, + _city: CityData, + ) -> Result, Box> { + // not supported for the dummy data + unimplemented!(); + } + + fn reorder_cities( + &mut self, + index: usize, + new_index: usize, + ) -> Result<(), Box> { + self.city_weather_data.swap(index, new_index); + Ok(()) + } + + fn remove_city(&mut self, index: usize) -> Result<(), Box> { + self.city_weather_data.remove(index); + Ok(()) + } + + fn search_location( + &self, + _query: String, + ) -> Result, Box> { + // not supported for the dummy data + unimplemented!(); + } +} diff --git a/src/weather/mod.rs b/src/weather/mod.rs new file mode 100644 index 0000000..6a9cd8c --- /dev/null +++ b/src/weather/mod.rs @@ -0,0 +1,18 @@ +mod weathercontroller; +mod weatherdisplaycontroller; + +mod dummyweathercontroller; + +pub use weathercontroller::WeatherControllerPointer; +pub use weathercontroller::WeatherControllerSharedPointer; +pub use weatherdisplaycontroller::WeatherDisplayController; + +pub use dummyweathercontroller::DummyWeatherController; + +#[cfg(all(not(target_arch = "wasm32"), feature = "open_weather"))] +mod openweathercontroller; + +#[cfg(all(not(target_arch = "wasm32"), feature = "open_weather"))] +pub use openweathercontroller::OpenWeatherController; + +pub mod utils; diff --git a/src/weather/openweathercontroller.rs b/src/weather/openweathercontroller.rs new file mode 100644 index 0000000..ef8f255 --- /dev/null +++ b/src/weather/openweathercontroller.rs @@ -0,0 +1,453 @@ +#![cfg(all(not(target_arch = "wasm32"), feature = "open_weather"))] + +use chrono::DateTime; +use openweather_sdk::responses::{GeocodingResponse, OneCallResponse}; +use openweather_sdk::{Language, OpenWeather, Units}; +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::io; +use std::io::{BufReader, BufWriter, Write}; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; +use std::vec; +use tokio::sync::Mutex; + +use crate::weather::utils::*; +use crate::weather::weathercontroller::{ + CityData, CityWeatherData, DayWeatherData, ForecastWeatherData, GeoLocationData, + PrecipitationData, TemperatureData, WeatherCondition, WeatherController, WeatherData, +}; + +#[cfg(target_os = "android")] +use std::env; + +const CITIES_STORED_FILE_NAME: &str = "cities_data.json"; +const ORGANIZATION_QUALIFIER: &str = "com"; // have to match android app name in cargo.toml +const ORGANIZATION_NAME: &str = "felgo.demos"; // have to match android app name in cargo.toml +const APPLICATION_NAME: &str = "rustyweather"; // have to match app android name in cargo.toml + +fn project_data_dir() -> Option { + #[cfg(target_os = "android")] + { + match env::var("ANDROID_DATA") { + Ok(data_root) => { + if data_root.is_empty() { + return None; + } else { + let project_name = format!( + "{}.{}.{}", + ORGANIZATION_QUALIFIER, ORGANIZATION_NAME, APPLICATION_NAME + ); + return Some(PathBuf::from(format!( + "{}/data/{}/files", + data_root, project_name + ))); + } + } + Err(_e) => { + log::warn!("Cannot read ANDROID_DATA, persistence not avaialble."); + return None; + } + } + } + + #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] + { + if let Some(project_dir) = directories::ProjectDirs::from( + ORGANIZATION_QUALIFIER, + ORGANIZATION_NAME, + APPLICATION_NAME, + ) { + return Some(project_dir.data_dir().to_path_buf()); + }; + + None + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct WeatherClient { + pub city_data: CityData, + pub weather_data: Option, +} + +pub struct OpenWeatherController { + tokio_runtime: tokio::runtime::Runtime, + weather_api: OpenWeather, + city_clients: Arc>>, + storage_path: Option, +} + +impl OpenWeatherController { + pub fn new(api_key: String) -> Self { + let mut weather_api = OpenWeather::new(api_key, Units::Metric, Language::English); + weather_api.one_call.fields.minutely = false; + weather_api.one_call.fields.hourly = false; + weather_api.one_call.fields.alerts = false; + + let storage_path; + if let Some(project_dir) = project_data_dir() { + storage_path = Some(project_dir.as_path().join(CITIES_STORED_FILE_NAME)); + } else { + storage_path = None; + log::error!("Failed to initialize project dir. Persistent data will not be loaded"); + } + + Self { + tokio_runtime: tokio::runtime::Runtime::new().unwrap(), + weather_api, + city_clients: Arc::new(Mutex::new(vec![])), + storage_path, + } + } + + fn weather_condition_from_icon_icon_type(icon_type: &str) -> WeatherCondition { + match icon_type { + "01d" | "01n" => WeatherCondition::Sunny, + "02d" | "02n" => WeatherCondition::PartiallyCloudy, + "03d" | "03n" => WeatherCondition::MostlyCloudy, + "04d" | "04n" => WeatherCondition::Cloudy, + "10d" | "10n" => WeatherCondition::SunnyRainy, + "09d" | "09n" => WeatherCondition::Rainy, + "11d" | "11n" => WeatherCondition::Stormy, + "13d" | "13n" => WeatherCondition::Snowy, + "50d" | "50n" => WeatherCondition::Foggy, + _ => WeatherCondition::Unknown, + } + } + + fn current_day_weather_data_from_response( + weather_response: &Option, + ) -> DayWeatherData { + if let Some(weather_data) = weather_response { + if let Some(current) = &weather_data.current { + let weather_details = ¤t.weather[0]; + let today_weather_info = + weather_data.daily.as_ref().and_then(|daily| daily.first()); + + let detailed_temp = match today_weather_info { + Some(info) => { + let temp = info.temp; + TemperatureData { + min: temp.min, + max: temp.max, + + morning: temp.morn, + day: temp.day, + evening: temp.eve, + night: temp.night, + } + } + None => TemperatureData { + min: current.temp, + max: current.temp, + + morning: current.temp, + day: current.temp, + evening: current.temp, + night: current.temp, + }, + }; + + return DayWeatherData { + description: weather_details.description.clone(), + condition: Self::weather_condition_from_icon_icon_type(&weather_details.icon), + current_temperature: current.temp, + detailed_temperature: detailed_temp, + precipitation: PrecipitationData::default(), + uv_index: 0.0, + }; + } + } + + DayWeatherData::default() + } + + fn forecast_day_weather_data_from_response( + weather_response: &Option, + ) -> Vec { + let mut forecast_weather_info: Vec = vec![]; + + if let Some(weather_data) = weather_response { + if let Some(daily_weather_data) = &weather_data.daily { + for day_weather_data in daily_weather_data.iter() { + if let Some(datetime) = DateTime::from_timestamp(day_weather_data.datetime, 0) { + let weather_details = &day_weather_data.weather[0]; + + let detailed_temperature = TemperatureData { + min: day_weather_data.temp.min, + max: day_weather_data.temp.max, + + morning: day_weather_data.temp.morn, + day: day_weather_data.temp.day, + evening: day_weather_data.temp.eve, + night: day_weather_data.temp.night, + }; + + let precipitation: PrecipitationData = PrecipitationData { + probability: day_weather_data.pop, + rain_volume: day_weather_data.rain.unwrap_or(0 as f64), + snow_volume: day_weather_data.snow.unwrap_or(0 as f64), + }; + + let day_weather_info = DayWeatherData { + description: weather_details.description.clone(), + condition: Self::weather_condition_from_icon_icon_type( + &weather_details.icon, + ), + current_temperature: day_weather_data.temp.day, + detailed_temperature, + precipitation, + uv_index: day_weather_data.uvi, + }; + + // TODO: localization + forecast_weather_info.push(ForecastWeatherData { + day_name: get_day_from_datetime(datetime), + weather_data: day_weather_info, + }); + } + } + } + } + + forecast_weather_info + } + + fn city_weather_data_from_client(city_client: &WeatherClient) -> CityWeatherData { + let current_data = Self::current_day_weather_data_from_response(&city_client.weather_data); + let forecast_data = + Self::forecast_day_weather_data_from_response(&city_client.weather_data); + + CityWeatherData { + city_data: city_client.city_data.clone(), + weather_data: WeatherData { + current_data, + forecast_data, + }, + } + } + + fn geo_location_data_from_response(response: &GeocodingResponse) -> GeoLocationData { + GeoLocationData { + name: response.name.clone(), + state: response.state.clone(), + country: response.country.clone(), + lat: response.lat, + lon: response.lon, + } + } +} + +impl WeatherController for OpenWeatherController { + fn load(&mut self) -> Result<(), Box> { + if let Some(storage_path) = &self.storage_path { + log::debug!("Loading data from: {:?}", storage_path.to_str()); + + let file = File::open(storage_path.as_path())?; + let reader = BufReader::new(file); + + let city_clients_data: Vec = serde_json::from_reader(reader)?; + log::debug!("Successfully loaded {} cities", city_clients_data.len()); + + let city_clients = self.city_clients.clone(); + self.tokio_runtime.block_on(async move { + let mut city_clients = city_clients.lock().await; + *city_clients = city_clients_data; + Ok(()) + }) + } else { + Err(Box::new(io::Error::new( + io::ErrorKind::NotFound, + "Storage path not initialized", + ))) + } + } + + fn save(&self) -> Result<(), Box> { + if let Some(storage_path) = &self.storage_path { + log::debug!("Saving data to: {:?}", storage_path.to_str()); + + // Ensure the parent directories exist + if let Some(parent_dir) = storage_path.parent() { + std::fs::create_dir_all(parent_dir)?; + } + + let file = File::create(storage_path)?; + let mut writer = BufWriter::new(file); + let city_clients = self.city_clients.clone(); + + self.tokio_runtime.block_on(async move { + let city_clients = city_clients.lock().await; + serde_json::to_writer(&mut writer, city_clients.deref())?; + writer.flush()?; + + Ok(()) + }) + } else { + Err(Box::new(io::Error::new( + io::ErrorKind::NotFound, + "Storage path not initialized", + ))) + } + } + + fn refresh_cities(&mut self) -> Result, Box> { + log::debug!("Refreshing all the clients!"); + + let city_clients_clone = self.city_clients.clone(); + let weather_api = self.weather_api.clone(); + + self.tokio_runtime.block_on(async move { + let mut city_clients = city_clients_clone.lock().await; + + let mut errors = vec![]; + for client in city_clients.iter_mut() { + // TODO: Spawn all tasks at once and join them later. + if let Err(e) = client.refresh_weather(&weather_api).await { + errors.push(e); + } + } + log::debug!("Refreshing weather finished!"); + + if !errors.is_empty() && errors.len() == city_clients.len() { + return Err(errors.pop().unwrap()); + } + Ok(city_clients + .iter() + .map(Self::city_weather_data_from_client) + .collect()) + }) + } + + fn add_city( + &mut self, + city: CityData, + ) -> Result, Box> { + log::debug!("Adding new city: {city:?}"); + let city_clients_clone = self.city_clients.clone(); + let weather_api = self.weather_api.clone(); + + self.tokio_runtime.block_on(async move { + let mut city_clients = city_clients_clone.lock().await; + match city_clients + .iter() + .position(|client| client.city_data == city) + { + Some(_) => { + log::info!("City already present in list!"); + Ok(None) + } + None => { + // Add to list and refresh + let mut client = WeatherClient::new(city.lat, city.lon, &city.city_name); + client.refresh_weather(&weather_api).await?; + let city_weather_data = Self::city_weather_data_from_client(&client); + city_clients.push(client); + Ok(Some(city_weather_data)) + } + } + }) + } + + fn reorder_cities( + &mut self, + index: usize, + new_index: usize, + ) -> Result<(), Box> { + let city_clients = self.city_clients.clone(); + self.tokio_runtime.block_on(async move { + let mut city_clients = city_clients.lock().await; + city_clients.swap(index, new_index); + }); + Ok(()) + } + + fn remove_city(&mut self, index: usize) -> Result<(), Box> { + let city_clients = self.city_clients.clone(); + self.tokio_runtime.block_on(async move { + let mut city_clients = city_clients.lock().await; + city_clients.remove(index); + }); + Ok(()) + } + + fn search_location( + &self, + query: String, + ) -> Result, Box> { + log::debug!("Searching for: {query}"); + let weather_api = self.weather_api.clone(); + + if query.is_empty() { + return Ok(vec![]); + } + + self.tokio_runtime.block_on(async move { + let response_data = weather_api + .geocoding + .get_geocoding(&query, None, None, 0) + .await?; + + log::debug!("Search result: {response_data:?}"); + + let mut unique_response_data: Vec = Vec::new(); + for element in response_data { + if !unique_response_data.iter().any(|existing_element| { + if existing_element.name == element.name + && existing_element.country == element.country + && existing_element.state == element.state + { + return true; + } + false + }) { + unique_response_data.push(element); + } + } + + Ok(unique_response_data + .iter() + .map(Self::geo_location_data_from_response) + .collect()) + }) + } +} + +impl WeatherClient { + pub fn new(lat: f64, lon: f64, cname: &str) -> Self { + Self { + city_data: CityData { + lat, + lon, + city_name: cname.to_string(), + }, + weather_data: None, + } + } + + pub async fn refresh_weather( + &mut self, + weather_api: &OpenWeather, + ) -> Result<(), Box> { + let res = weather_api + .one_call + .call(self.city_data.lat, self.city_data.lon) + .await; + log::debug!("Weather response: {res:?}"); + + match res { + Ok(response_data) => { + self.weather_data = Some(response_data); + log::debug!( + "Response received at: {:?}", + chrono::offset::Local::now().timestamp() + ); + + Ok(()) + } + Err(e) => Err(e), + } + } +} diff --git a/src/weather/utils.rs b/src/weather/utils.rs new file mode 100644 index 0000000..2ab7fb9 --- /dev/null +++ b/src/weather/utils.rs @@ -0,0 +1,9 @@ +use chrono::{DateTime, Datelike}; + +pub fn get_day_from_datetime(date: DateTime) -> String { + if date.day() == chrono::offset::Local::now().day() { + // TODO: translations + return "Today".to_string(); + } + date.weekday().to_string() +} diff --git a/src/weather/weathercontroller.rs b/src/weather/weathercontroller.rs new file mode 100644 index 0000000..b51d4c9 --- /dev/null +++ b/src/weather/weathercontroller.rs @@ -0,0 +1,112 @@ +use serde::{Deserialize, Serialize}; +use std::sync::{Arc, Mutex}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct CityData { + pub lat: f64, + pub lon: f64, + pub city_name: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +pub enum WeatherCondition { + #[default] + Unknown, + Sunny, + PartiallyCloudy, + MostlyCloudy, + Cloudy, + SunnyRainy, + Rainy, + Stormy, + Snowy, + Foggy, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +pub struct TemperatureData { + pub min: f64, + pub max: f64, + pub morning: f64, + pub day: f64, + pub evening: f64, + pub night: f64, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +pub struct PrecipitationData { + pub probability: f64, + pub rain_volume: f64, + pub snow_volume: f64, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +pub struct DayWeatherData { + pub condition: WeatherCondition, + pub description: String, + + pub current_temperature: f64, + pub detailed_temperature: TemperatureData, + + pub precipitation: PrecipitationData, + pub uv_index: f64, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +pub struct ForecastWeatherData { + pub day_name: String, + pub weather_data: DayWeatherData, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct WeatherData { + pub current_data: DayWeatherData, + pub forecast_data: Vec, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct CityWeatherData { + pub city_data: CityData, + pub weather_data: WeatherData, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct GeoLocationData { + pub name: String, + pub lat: f64, + pub lon: f64, + pub country: String, + pub state: Option, +} + +#[cfg(not(target_arch = "wasm32"))] +pub type WeatherControllerPointer = Box; +#[cfg(target_arch = "wasm32")] +pub type WeatherControllerPointer = Box; + +pub type WeatherControllerSharedPointer = Arc>; + +pub trait WeatherController { + fn load(&mut self) -> Result<(), Box>; + fn save(&self) -> Result<(), Box>; + + fn refresh_cities(&mut self) -> Result, Box>; + + fn add_city( + &mut self, + city: CityData, + ) -> Result, Box>; + + fn reorder_cities( + &mut self, + index: usize, + new_index: usize, + ) -> Result<(), Box>; + + fn remove_city(&mut self, index: usize) -> Result<(), Box>; + + fn search_location( + &self, + query: String, + ) -> Result, Box>; +} diff --git a/src/weather/weatherdisplaycontroller.rs b/src/weather/weatherdisplaycontroller.rs new file mode 100644 index 0000000..de7e66e --- /dev/null +++ b/src/weather/weatherdisplaycontroller.rs @@ -0,0 +1,489 @@ +use slint::{ComponentHandle, Model, ModelRc, SharedString, VecModel, Weak}; +use std::rc::Rc; + +use crate::ui; +use ui::{ + AppWindow, BusyLayerController, CityWeather, CityWeatherInfo, GeoLocation, GeoLocationEntry, + IconType, TemperatureInfo, WeatherForecastInfo, WeatherInfo, +}; + +use crate::weather::weathercontroller::{ + CityData, CityWeatherData, DayWeatherData, ForecastWeatherData, GeoLocationData, + WeatherCondition, WeatherControllerSharedPointer, +}; + +#[cfg(not(target_arch = "wasm32"))] +use async_std::task::spawn as spawn_task; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_futures::spawn_local as spawn_task; + +pub struct WeatherDisplayController { + data_controller: WeatherControllerSharedPointer, +} + +fn forecast_graph_command( + model: ModelRc, + days_count: i32, + width: f32, + height: f32, +) -> SharedString { + if days_count == 0 || width == 0.0 || height == 0.0 { + return SharedString::new(); + } + + let temperatures: Vec = model + .clone() + .iter() + .take(days_count as usize) + .map(|info| info.weather_info.detailed_temp.day) + .collect(); + + const MIN_MAX_MARGIN: f32 = 5.0; + let min_temperature = match temperatures.iter().min_by(|a, b| a.total_cmp(b)) { + Some(min) => min - MIN_MAX_MARGIN, + None => 0.0, + }; + let max_temperature = match temperatures.iter().max_by(|a, b| a.total_cmp(b)) { + Some(max) => max + MIN_MAX_MARGIN, + None => 50.0, + }; + + let max_temperature_value = max_temperature - min_temperature; + let temperature_ratio = height / max_temperature_value; + + let day_width = width / days_count as f32; + let max_day_shift = days_count as f32 * day_width; + + let border_command = + format!( + "M 0 0 M {max_width} 0 M {max_width} {max_temperature_value} M 0 {max_temperature_value} ", + max_width=max_day_shift, max_temperature_value=max_temperature_value * temperature_ratio); + + let mut command = border_command; + + let day_shift = |index: f32| -> f32 { index * day_width + 0.5 * day_width }; + let day_temperature = + |temperature: f32| -> f32 { (max_temperature - temperature) * temperature_ratio }; + + for (index, &temperature) in temperatures.iter().enumerate() { + if index == 0 { + command += format!( + "M {x} {y} ", + x = day_shift(index as f32), + y = day_temperature(temperature) + ) + .as_str(); + } + + if let Some(next_temperature) = temperatures.get(index + 1) { + let next_temperature = *next_temperature; + + let day1 = day_shift(index as f32); + let day2 = day_shift(index as f32 + 1.0); + let temp1 = day_temperature(temperature); + let temp2 = day_temperature(next_temperature); + + let day_mid = (day1 + day2) / 2.0; + let temp_mid = (temp1 + temp2) / 2.0; + + let cp_day1 = (day_mid + day1) / 2.0; + let cp_day2 = (day_mid + day2) / 2.0; + + command += format!( + "Q {x1} {y1} {cx1} {cy1} Q {x2} {y2} {cx2} {cy2} ", + x1 = cp_day1, + y1 = temp1, + cx1 = day_mid, + cy1 = temp_mid, + x2 = cp_day2, + y2 = temp2, + cx2 = day2, + cy2 = temp2 + ) + .as_str(); + } + } + + SharedString::from(command) +} + +impl WeatherDisplayController { + pub fn new(data_controller: &WeatherControllerSharedPointer) -> Self { + Self { + data_controller: data_controller.clone(), + } + } + + pub fn initialize_ui(&self, window: &AppWindow, support_add_city: bool) { + let city_weather = window.global::(); + let geo_location = window.global::(); + + // initialized models + city_weather.set_city_weather(ModelRc::from(Rc::new(VecModel::::from( + vec![], + )))); + geo_location.set_result_list(ModelRc::from(Rc::new(VecModel::::from( + vec![], + )))); + + // initialize state + city_weather.set_can_add_city(support_add_city); + + // handle callbacks + city_weather.on_get_forecast_graph_command(forecast_graph_command); + + city_weather.on_refresh_all({ + let window_weak = window.as_weak(); + let data_controller = self.data_controller.clone(); + + move || Self::refresh_cities(&window_weak, &data_controller) + }); + + city_weather.on_reorder({ + let window_weak = window.as_weak(); + let data_controller = self.data_controller.clone(); + + move |index, new_index| { + if let Err(e) = + Self::reorder_cities(&window_weak, &data_controller, index, new_index) + { + log::warn!( + "Failed to reorder city from {} to {}: {}", + index, + new_index, + e + ); + } + } + }); + + city_weather.on_delete({ + let window_weak = window.as_weak(); + let data_controller = self.data_controller.clone(); + + move |index| { + if let Err(e) = Self::remove_city(&window_weak, &data_controller, index) { + log::warn!("Failed to remove city from {}: {}", index, e); + } + } + }); + + geo_location.on_search_location({ + let window_weak = window.as_weak(); + let data_controller = self.data_controller.clone(); + + move |location| Self::search_location(&window_weak, &data_controller, location) + }); + + geo_location.on_add_location({ + let window_weak = window.as_weak(); + let data_controller = self.data_controller.clone(); + + move |location| { + Self::add_city(&window_weak, &data_controller, location); + } + }); + } + + #[cfg_attr(not(target_os = "android"), allow(dead_code))] + pub fn refresh(&self, window: &AppWindow) { + Self::set_busy(window); + + let window_weak = window.as_weak(); + Self::refresh_cities(&window_weak, &self.data_controller); + } + + pub fn load(&self, window: &AppWindow) { + Self::set_busy(window); + + let window_weak = window.as_weak(); + let data_controller = self.data_controller.clone(); + + spawn_task(async move { + let city_data_res = async { + let mut data_controller = data_controller.lock().unwrap(); + data_controller.load()?; + data_controller.refresh_cities() + } + .await; + + let city_data = match city_data_res { + Ok(city_data) => Some(city_data), + Err(e) => { + log::warn!("Failed to load cities: {}.", e); + None + } + }; + + Self::check_update_error(window_weak.upgrade_in_event_loop(move |window| { + if let Some(city_data) = city_data { + WeatherDisplayController::update_displayed_cities(&window, city_data); + } + Self::unset_busy(&window); + })); + }); + } + + fn refresh_cities( + window_weak: &Weak, + data_controller: &WeatherControllerSharedPointer, + ) { + let window_weak = window_weak.clone(); + let data_controller = data_controller.clone(); + + spawn_task(async move { + let city_data_res = async { data_controller.lock().unwrap().refresh_cities() }.await; + + let city_data = match city_data_res { + Ok(city_data) => Some(city_data), + Err(e) => { + log::warn!("Failed to update cities: {}.", e); + None + } + }; + + Self::check_update_error(window_weak.upgrade_in_event_loop(move |window| { + if let Some(city_data) = city_data { + WeatherDisplayController::update_displayed_cities(&window, city_data); + } + Self::unset_busy(&window); + })); + }); + } + + fn add_city( + window_weak: &Weak, + data_controller: &WeatherControllerSharedPointer, + location: GeoLocationEntry, + ) { + let city = CityData { + lat: location.lat as f64, + lon: location.lon as f64, + city_name: String::from(&location.name), + }; + let city_data_res = data_controller.lock().unwrap().add_city(city); + + // update ui + let window = window_weak.upgrade().unwrap(); + match city_data_res { + Ok(city_data) => { + if let Some(city_data) = city_data { + let city_weather = window.global::(); + let city_weather_list = city_weather.get_city_weather(); + + let city_weather = Self::city_weather_info_from_data(&city_data); + city_weather_list + .as_any() + .downcast_ref::>() + .unwrap() + .push(city_weather); + } + } + Err(e) => { + log::warn!("Failed to add city: {}.", e); + } + } + + Self::unset_busy(&window); + } + + fn reorder_cities( + window_weak: &Weak, + data_controller: &WeatherControllerSharedPointer, + index: i32, + new_index: i32, + ) -> Result<(), Box> { + let pos: usize = index.try_into()?; + let new_pos: usize = new_index.try_into()?; + + data_controller + .lock() + .unwrap() + .reorder_cities(pos, new_pos)?; + + // update ui + let window = window_weak.upgrade().unwrap(); + let city_weather = window.global::(); + let city_weather_list = city_weather.get_city_weather(); + + let pos_data = city_weather_list + .row_data(pos) + .ok_or(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Index out of bounds", + )))?; + let new_pos_data = + city_weather_list + .row_data(new_pos) + .ok_or(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Index out of bounds", + )))?; + + city_weather_list.set_row_data(pos, new_pos_data); + city_weather_list.set_row_data(new_pos, pos_data); + Ok(()) + } + + fn remove_city( + window_weak: &Weak, + data_controller: &WeatherControllerSharedPointer, + index: i32, + ) -> Result<(), Box> { + let pos: usize = index.try_into()?; + + data_controller.lock().unwrap().remove_city(pos)?; + + // update ui + let window = window_weak.upgrade().unwrap(); + let city_weather = window.global::(); + let city_weather_list = city_weather.get_city_weather(); + + let model = city_weather_list + .as_any() + .downcast_ref::>() + .expect("CityWeatherInfo model is not provided!"); + + model.remove(pos); + Ok(()) + } + + fn search_location( + window_weak: &Weak, + data_controller: &WeatherControllerSharedPointer, + query: slint::SharedString, + ) { + let window_weak = window_weak.clone(); + let data_controller = data_controller.clone(); + let query = query.to_string(); + + spawn_task(async move { + let locations_res = + async { data_controller.lock().unwrap().search_location(query) }.await; + + let locations = match locations_res { + Ok(locations) => Some(locations), + Err(e) => { + log::warn!("Failed to search for location: {}.", e); + None + } + }; + + Self::check_update_error(window_weak.upgrade_in_event_loop(move |window| { + if let Some(locations) = locations { + WeatherDisplayController::update_location_search_results(&window, locations); + } + })); + }); + } + + fn update_displayed_cities(window: &AppWindow, data: Vec) { + let display_vector: Vec = + data.iter().map(Self::city_weather_info_from_data).collect(); + + let city_weather = window.global::().get_city_weather(); + let model = city_weather + .as_any() + .downcast_ref::>() + .expect("City weather model not set."); + + model.set_vec(display_vector); + } + + fn update_location_search_results(window: &AppWindow, result: Vec) { + let display_vector: Vec = result + .iter() + .map(Self::geo_location_entry_from_data) + .collect(); + + let geo_location = window.global::().get_result_list(); + let model = geo_location + .as_any() + .downcast_ref::>() + .expect("Geo location entry model not set."); + + model.set_vec(display_vector); + } + + fn set_busy(window: &AppWindow) { + window.global::().invoke_set_busy(); + } + + fn unset_busy(window: &AppWindow) { + window.global::().invoke_unset_busy(); + } + + fn check_update_error(result: Result<(), E>) { + if let Err(e) = result { + log::error!("Error while updating UI: {}", e); + } + } + + fn icon_type_from_condition(condition: &WeatherCondition) -> IconType { + match condition { + WeatherCondition::Sunny => IconType::Sunny, + WeatherCondition::PartiallyCloudy => IconType::PartiallyCloudy, + WeatherCondition::MostlyCloudy => IconType::MostlyCloudy, + WeatherCondition::Cloudy => IconType::Cloudy, + WeatherCondition::SunnyRainy => IconType::SunnyRainy, + WeatherCondition::Rainy => IconType::Rainy, + WeatherCondition::Stormy => IconType::Stormy, + WeatherCondition::Snowy => IconType::Snowy, + WeatherCondition::Foggy => IconType::Foggy, + _ => IconType::Unknown, + } + } + + fn weather_info_from_data(data: &DayWeatherData) -> WeatherInfo { + WeatherInfo { + description: SharedString::from(&data.description), + icon_type: Self::icon_type_from_condition(&data.condition), + current_temp: data.current_temperature as f32, + detailed_temp: TemperatureInfo { + min: data.detailed_temperature.min as f32, + max: data.detailed_temperature.max as f32, + + morning: data.detailed_temperature.morning as f32, + day: data.detailed_temperature.day as f32, + evening: data.detailed_temperature.evening as f32, + night: data.detailed_temperature.night as f32, + }, + uv: data.uv_index as i32, + precipitation_prob: data.precipitation.probability as f32, + rain: data.precipitation.rain_volume as f32, + snow: data.precipitation.snow_volume as f32, + } + } + + fn forecast_weather_info_from_data(data: &[ForecastWeatherData]) -> Vec { + data.iter() + .map(|forecast_data| WeatherForecastInfo { + day_name: SharedString::from(&forecast_data.day_name), + weather_info: Self::weather_info_from_data(&forecast_data.weather_data), + }) + .collect() + } + + fn city_weather_info_from_data(data: &CityWeatherData) -> CityWeatherInfo { + let current_weather_info = Self::weather_info_from_data(&data.weather_data.current_data); + let forecast_weather_info = + Self::forecast_weather_info_from_data(&data.weather_data.forecast_data); + + CityWeatherInfo { + city_name: SharedString::from(&data.city_data.city_name), + current_weather: current_weather_info, + forecast_weather: Rc::new(slint::VecModel::from(forecast_weather_info)).into(), + } + } + + fn geo_location_entry_from_data(data: &GeoLocationData) -> GeoLocationEntry { + GeoLocationEntry { + name: SharedString::from(&data.name), + state: SharedString::from(data.state.as_deref().unwrap_or_default()), + country: SharedString::from(&data.country), + lat: data.lat as f32, + lon: data.lon as f32, + } + } +} diff --git a/ui/about-box.slint b/ui/about-box.slint new file mode 100644 index 0000000..64488ee --- /dev/null +++ b/ui/about-box.slint @@ -0,0 +1,87 @@ +import { WindowInfo } from "./ui_utils.slint"; +import { AppText } from "./controls/generic.slint"; +import { AboutSlint } from "std-widgets.slint"; + +component AboutFelgo { + width: 100%; + height: 100%; + + VerticalLayout { + spacing: 5px; + + width: 90% * parent.width; + padding-bottom: 14px; + + Rectangle { + background: white; + border-radius: self.height / 2; + + preferred-height: self.width * 45%; + + logo-layout := VerticalLayout { + alignment: center; + spacing: 2px; + + made-text := AppText { + text: "MADE BY"; + horizontal-alignment: center; + font-size: 1rem; + color: black; + } + + Rectangle { + Image { + image-fit: contain; + width: 70% * parent.width; + source: @image-url("./assets/felgo-logo.svg"); + } + } + } + } + + AppText { + text: "https://felgo.com/"; + horizontal-alignment: center; + } + } +} + +export component AboutBox { + VerticalLayout { + if WindowInfo.is-portrait: VerticalLayout { + alignment: center; + + Rectangle { + AboutFelgo { + width: Math.min(200px, 80% * parent.width); + min-height: self.preferred-height; + } + } + Rectangle { + AboutSlint { + width: Math.min(200px, 80% * parent.width); + min-height: self.preferred-height; + } + } + } + + if !WindowInfo.is-portrait: HorizontalLayout { + alignment: space-around; + + // adjust AboutFelgo size to look the same as AboutSlint + Rectangle { + max-width: 200px; + + AboutFelgo { + height: 84%; + preferred-width: 200px; + padding-bottom: 10px; + } + } + + AboutSlint { + max-width: 200px; + } + } + } +} diff --git a/ui/assets/busy-indicator.png b/ui/assets/busy-indicator.png new file mode 100644 index 0000000..c2228a7 Binary files /dev/null and b/ui/assets/busy-indicator.png differ diff --git a/ui/assets/felgo-logo.svg b/ui/assets/felgo-logo.svg new file mode 100644 index 0000000..d439103 --- /dev/null +++ b/ui/assets/felgo-logo.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/assets/font-awesome.ttf b/ui/assets/font-awesome.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/ui/assets/font-awesome.ttf differ diff --git a/ui/assets/weathericons-font.ttf b/ui/assets/weathericons-font.ttf new file mode 100644 index 0000000..948f0a5 Binary files /dev/null and b/ui/assets/weathericons-font.ttf differ diff --git a/ui/city_weather.slint b/ui/city_weather.slint new file mode 100644 index 0000000..fea7e73 --- /dev/null +++ b/ui/city_weather.slint @@ -0,0 +1,215 @@ +import { WindowInfo } from "./ui_utils.slint"; +import { PageBase } from "page-base.slint"; +import { CityWeatherTile } from "city_weather_tile.slint"; +import { ExpandedCityWeatherTile } from "expanded_city_weather_tile.slint"; +import { CityWeather, CityWeatherInfo } from "weather_datatypes.slint"; +import { AppPalette, Images } from "./style/styles.slint"; +import { SlideButton } from "./controls/generic.slint"; +import { AboutBox } from "about-box.slint"; + +component CitySlideArea inherits Rectangle { + in property can-move-up: true; + in property can-move-down: true; + + callback opened; + callback closed; + + callback up-clicked; + callback down-clicked; + callback delete-clicked; + callback content-clicked; + + public function open() { + flickable.viewport-x = -buttons-layout.width; + root.opened(); + } + + public function close() { + flickable.viewport-x = 0; + root.closed(); + } + + height: content.preferred-height; + + flickable := Flickable { + width: 100%; + + viewport-width: slide-layout.preferred-width; + viewport-x: 0; + + property last-viewport-x: 0px; + flicked => { + if (self.last-viewport-x > self.viewport-x) { + root.open(); + } + else { + root.close(); + } + } + + slide-layout := HorizontalLayout { + content := Rectangle { + width: root.width; + + Rectangle { + @children + } + + TouchArea { + pointer-event(event) => { + if (event.kind == PointerEventKind.down) { + flickable.last-viewport-x = flickable.viewport-x; + } + } + + clicked => { root.content-clicked(); } + } + } + + buttons-layout := VerticalLayout { + width: 50px; + spacing: 1px; + + SlideButton { + text: "\u{f062}"; + enabled: root.can-move-up; + + clicked => { root.up-clicked(); } + } + SlideButton { + text: "\u{f1f8}"; + background-color: AppPalette.error-red; + + clicked => { root.delete-clicked(); } + } + SlideButton { + text: "\u{f063}"; + enabled: root.can-move-down; + + clicked => { root.down-clicked(); } + } + } + } + } + +} + +component CityWeatherList inherits Flickable { + property opened-index: -1; + + callback expand(int, Point, length, length); + + VerticalLayout { + alignment: start; + padding: 0px; + + for city-weather-info[index] in CityWeather.city-weather: + CitySlideArea { + property is-opened: root.opened-index == index; + + can-move-up: index > 0; + can-move-down: index < CityWeather.city-weather.length - 1; + + changed is-opened => { + if (is-opened) { + self.open(); + } + else { + self.close(); + } + } + + opened => { + root.opened-index = index; + } + + content-clicked => { + root.opened-index = -1; + root.expand(index, self.absolute-position, self.width, self.height); + } + + up-clicked => { + root.opened-index = index - 1; + CityWeather.reorder(index, root.opened-index); + } + down-clicked => { + root.opened-index = index + 1; + CityWeather.reorder(index, root.opened-index); + } + delete-clicked => { + root.opened-index = -1; + CityWeather.delete(index); + self.close(); + } + + CityWeatherTile { + city-weather-info: city-weather-info; + alternative-background: Math.mod(index, 2) == 0; + } + } + + Rectangle { + // spacing + min-height: WindowInfo.is-portrait ? 25px : 10px; + } + AboutBox { + min-height: self.preferred-height; + } + } +} + +struct TileInfo { + index: int, + absolute-position: Point, + size: { width: length, height: length } +} + +export component CityListView inherits PageBase { + property selected-tile; + + CityWeatherList { + padding: 16px; + + expand(index, absolute-position, width, height) => { + root.selected-tile = { + index: index, + absolute-position: absolute-position, + size: { width: width, height: height } + }; + expanded-tile.expand(); + } + } + + PageBase { + opacity: 0.0; + + states [ + visible when expanded-tile.expanded: { + opacity: 1; + + out { + animate opacity { delay: expanded-tile.animation-duration; } + } + } + ] + } + + expanded-tile := ExpandedCityWeatherTile { + city-weather-info: CityWeather.city-weather[root.selected-tile.index]; + alternative-background: Math.mod(root.selected-tile.index, 2) == 0; + + block-x: root.selected-tile.absolute-position.x - root.absolute-position.x; + block-y: root.selected-tile.absolute-position.y - root.absolute-position.y; + block-width: root.selected-tile.size.width; + block-height: root.selected-tile.size.height; + + full-x: 0px; + full-y: 0px; + full-width: parent.width; + full-height: parent.height; + + clicked => { + self.collapse(); + } + } +} diff --git a/ui/city_weather_tile.slint b/ui/city_weather_tile.slint new file mode 100644 index 0000000..c073aba --- /dev/null +++ b/ui/city_weather_tile.slint @@ -0,0 +1,105 @@ +import { WeatherInfo, WeatherForecastInfo, CityWeatherInfo, CityWeather } from "weather_datatypes.slint"; +import { WindowInfo } from "./ui_utils.slint"; +import { AppPalette } from "./style/styles.slint"; +import { AppText } from "./controls/generic.slint"; +import { WeatherIcon } from "./controls/weather.slint"; +import { DayForecastGraph } from "./forecast_with_graph.slint"; + +component TileBaseInfo inherits HorizontalLayout { + in property city-name; + in property current-weather; + + spacing: 15px; + + AppText { + font-size: 2.1rem; + text: root.city-name; + } + + VerticalLayout { + horizontal-stretch: 1; + alignment: start; + + AppText { + min-width: self.preferred-width; + font-size: 1.8rem; + horizontal-alignment: right; + + text: Math.round(root.current-weather.current_temp) + "°"; + } + + AppText { + font-size: 1.2rem; + horizontal-alignment: right; + + text: root.current-weather.description; + wrap: word-wrap; + } + } + + WeatherIcon { + icon-type: root.current-weather.icon-type; + + font-size: 3.5rem; + vertical-alignment: top; + } +} + +export component CityWeatherTile inherits TouchArea { + in property city-weather-info; + in property alternative-background: false; + in property show-animations: true; + + out property city-name: city-weather-info.city-name; + out property current-weather: city-weather-info.current-weather; + out property<[WeatherForecastInfo]> forecast-weather: city-weather-info.forecast-weather; + + preferred-height: layout.preferred-height; + + Rectangle { + background: root.alternative-background ? white.with-alpha(2.5%) : white.with-alpha(8.5%); + } + + layout := VerticalLayout { + padding: 15px; + spacing: 10px; + + if WindowInfo.is-portrait: VerticalLayout { + vertical-stretch: 0; + spacing: parent.spacing; + + TileBaseInfo { + city-name: root.city-name; + current-weather: root.current-weather; + } + + DayForecastGraph { + min-height: self.preferred-height; + + forecast-weather: root.forecast-weather; + show-animations: root.show-animations; + } + } + if !WindowInfo.is-portrait: HorizontalLayout { + spacing: 5% * self.width; + + TileBaseInfo { + horizontal-stretch: 1; + max-width: parent.width * 40%; + height: self.preferred-height; + + city-name: root.city-name; + current-weather: root.current-weather; + } + + Rectangle { + min-height: self.preferred-height; + + DayForecastGraph { + forecast-weather: root.forecast-weather; + show-animations: root.show-animations; + } + } + } + } +} diff --git a/ui/controls/busy-layer.slint b/ui/controls/busy-layer.slint new file mode 100644 index 0000000..262b1d5 --- /dev/null +++ b/ui/controls/busy-layer.slint @@ -0,0 +1,47 @@ +import { AppFonts, Images } from "../style/styles.slint"; + +export global BusyLayerController { + out property is-busy: false; + + property busy-counter: 0; + + public function set-busy() { + busy-counter += 1; + + // updating only when real change happen to avoid: + // https://github.com/slint-ui/slint/issues/5209 + if (!root.is-busy) { + root.is-busy = true; + } + } + public function unset-busy() { + busy-counter -= 1; + + // updating only when real change happen to avoid: + // https://github.com/slint-ui/slint/issues/5209 + if (root.is-busy && busy-counter == 0) { + root.is-busy = false; + } + } +} + +export component BusyLayer inherits Rectangle { + Rectangle { + background: black; + opacity: 0.75; + } + + Image { + width: 75px; + source: Images.busy-indicator; + + colorize: white.darker(15%); + rotation-origin-x: self.width / 2; + rotation-origin-y: self.height / 2 + self.height * 7.15%; + + rotation-angle: Math.mod(animation-tick() / 3.25ms, 360) * 1deg; + } + + // touch blocker + TouchArea {} +} diff --git a/ui/controls/generic.slint b/ui/controls/generic.slint new file mode 100644 index 0000000..6dee3f3 --- /dev/null +++ b/ui/controls/generic.slint @@ -0,0 +1,135 @@ +import { AppPalette, AppFonts } from "../style/styles.slint"; + +export component AppText inherits Text { + color: AppPalette.foreground; + + overflow: elide; +} + +export component IconText inherits AppText { + font-family: AppFonts.icons-font-name; + + horizontal-alignment: center; + vertical-alignment: center; +} + +export component FloatingTextButton inherits Rectangle { + in property text <=> text-item.text; + in property text-color <=> text-item.color; + in property font-size <=> text-item.font-size; + in property font-weight <=> text-item.font-weight; + + callback clicked; + + property size: Math.max(text-item.preferred-width + self.padding-left + self.padding-right, + text-item.preferred-height + self.padding-top + self.padding-bottom); + + drop-shadow-color: self.background.darker(50%); + drop-shadow-blur: 5px; + drop-shadow-offset-x: 3px; + drop-shadow-offset-y: 2px; + + background: AppPalette.background; + border-radius: self.size / 2; + + padding: 15px; + padding-left: self.padding; + padding-right: self.padding; + padding-top: self.padding; + padding-bottom: self.padding; + + preferred-width: self.size; + preferred-height: self.size; + + width: self.preferred-width; + height: self.preferred-height; + + text-item := IconText { + horizontal-alignment: center; + vertical-alignment: center; + } + + TouchArea { + clicked => { root.clicked(); } + } +} + +export component SlideButton inherits Rectangle { + in-out property text <=> text.text; + in-out property enabled <=> touch-area.enabled; + in property background-color; + callback clicked <=> touch-area.clicked; + + background: touch-area.pressed ? self.background-color.darker(10%) : self.background-color; + opacity: root.enabled ? 1.0 : 0.5; + + text := IconText { + font-size: 2rem; + + color: touch-area.pressed ? AppPalette.foreground.darker(10%) : AppPalette.foreground; + } + + touch-area := TouchArea {} +} + +export component TextField inherits Rectangle { + in property icon-text; + in property placeholder-text; + in-out property text <=> text-input.text; + callback edited <=> text-input.edited; + + forward-focus: text-input; + + padding: 5px; + padding-top: self.padding; + padding-right: self.padding; + padding-bottom: self.padding; + padding-left: self.padding; + + preferred-height: text-input.preferred-height + self.padding-top + self.padding-bottom; + height: self.preferred-height; + + border-radius: 5px; + background: white.with-alpha(15%); + + HorizontalLayout { + x: root.padding-left; + width: parent.width - root.padding-left - root.padding-right; + + y: root.padding-top; + height: parent.height - root.padding-top - root.padding-bottom; + + spacing: 10px; + + IconText { + text: root.icon-text; + font-size: text-input.font-size; + } + + AppText { + horizontal-stretch: 1; + + horizontal-alignment: left; + font-size: text-input.font-size; + text: text-input.text == "" ? root.placeholder-text : ""; + + text-input := TextInput { + color: AppPalette.foreground; + font-size: 1.2rem; + } + } + + IconText { + text: "\u{f00d}"; + + font-size: text-input.font-size; + visible: text-input.text != ""; + + TouchArea { + clicked => { + text-input.text = ""; + } + } + } + } +} diff --git a/ui/controls/stackview.slint b/ui/controls/stackview.slint new file mode 100644 index 0000000..d0dd4af --- /dev/null +++ b/ui/controls/stackview.slint @@ -0,0 +1,133 @@ +/*! + * \brief The StackView is a component that can be used to simulate a stack view. + * + * \note: Due to the language limitation only the partial implementaion is possible. + * The component is meant to be a helper. + * + * \par Usage: + * The component can be used in two ways: + * - for smaller pages, where all pages are loaded at once, + * - for more complex pages, where pages are loaded dynamically. + * + * Static pages: + * \code{*.slint} + * stack := StackView { + * current-index: 0; + * min-index: 0; + * + * for color in [ Colors.red, Colors.green, Colors.blue ]: + * StackPage { + * is-current: self.check-is-current(stack.current-index); + * init => { self.page-index = stack.insert-page(); } // StackPage.count increased with insert-page function + * + * TestPage { + * background: color; + * push => { stack.push(); } + * pop => { stack.pop(); } + * } + * } + * } + * \endcode + * + * Dynamic pages: + * \code{*.slint} + * stack := StackView { + * count: 2; // StackPage.count provided manually + * current-index: 0; + * min-index: 0; + * + * if (stack.current-index == 0): StackPage { + * page-index: 0; is-current: true; + * + * TestPage { + * background: Colors.red; + * push => { stack.push(); } + * pop => { stack.pop(); } + * } + * } + * if (stack.current-index == 1): StackPage { + * page-index: 1; is-current: true; + * + * TestPage { + * background: Colors.green; + * push => { stack.push(); } + * pop => { stack.pop(); } + * } + * } + * } + * \endcode + * + * \sa StackPage + */ +export component StackView inherits Rectangle { + /// \brief This property states the number of items in the stack + in-out property count: 0; + + /// \brief This property states the index of the currently visible item + in-out property current-index: -1; + + /// \brief This property configures the minimum index the pop function can set (-1 by default) + in property min-index: -1; + + /// \brief This property configures the minimum index the push function can set (#count -1 by default) + in property max-index: self.count - 1; + + /// \brief This function increases the pages #count by one and returns new page index + public function insert-page() -> int { + self.count += 1; + return self.count - 1; + } + + /// \brief This function increases the #current-index if possible + public function push() { + if (self.current-index < Math.min(self.max-index, self.count - 1)) { + self.current-index += 1; + } + } + + /// \brief This function decreased the #current-index if possible + public function pop() { + if (self.current-index > Math.max(self.min-index, -1)) { + self.current-index -= 1; + } + } +} + +/*! + * \brief The StackPage is a component to use in the StackView. + * + * The real content can either derive from the StackPage (which is Rectangle based) or can be contained + * as the page children. + * + * Inherits: + * \code{*.slint} + * export TestPage inherits StackPage { + * background: color; + * } + * \endcode + * + * Contains: + * \code{*.slint} + * StackPage { + * TestPage { + * background: Colors.red; + * } + * } + * \endcode + * + * \sa StackView + */ +export component StackPage inherits TouchArea { + /// \brief This property configures the page index + in property page-index: -1; + + /// \brief This property configures whether the page is a current page (if not, it is hidden) + in property is-current: false; + + /// \brief This function is a helper function to use when setting the #is-current property + public pure function check-is-current(current-index: int) -> bool { + return current-index == self.page-index; + } + + visible: self.is-current; +} diff --git a/ui/controls/weather.slint b/ui/controls/weather.slint new file mode 100644 index 0000000..4f03882 --- /dev/null +++ b/ui/controls/weather.slint @@ -0,0 +1,155 @@ +import { AppPalette, AppFonts } from "../style/styles.slint"; +import { AppText } from "./generic.slint"; +import { WindowInfo } from "../ui_utils.slint"; +import { IconType } from "../weather_datatypes.slint"; + +export component WeatherIconBase inherits Text { + color: AppPalette.foreground; + font-family: AppFonts.weather-icons-font-name; + + horizontal-alignment: center; + vertical-alignment: center; +} + +export component WeatherIcon inherits WeatherIconBase { + in property icon-type; + + pure function get-weather-icon-url(type: IconType) -> string { + if (type == IconType.Sunny) { return "\u{f00d}"; } + if (type == IconType.PartiallyCloudy) { return "\u{f002}"; } + if (type == IconType.MostlyCloudy) { return "\u{f041}"; } + if (type == IconType.Cloudy) { return "\u{f013}"; } + if (type == IconType.SunnyRainy) { return "\u{f008}"; } + if (type == IconType.Rainy) { return "\u{f015}"; } + if (type == IconType.Stormy) { return "\u{f01e}"; } + if (type == IconType.Snowy) { return "\u{f064}"; } + if (type == IconType.Foggy) { return "\u{f063}"; } + + return ""; + } + + pure function get-weather-icon-color(type: IconType) -> color { + if (type == IconType.Sunny) { return AppPalette.sun-yellow; } + + return AppPalette.foreground; + } + + text: root.get-weather-icon-url(root.icon-type); + color: root.get-weather-icon-color(root.icon-type); +} + +component DataText inherits AppText { + in property minimal: false; + + font-size: root.minimal ? (WindowInfo.is-portrait ? 0.85rem : 0.9rem) : 1.1rem; + overflow: elide; + horizontal-alignment: center; + vertical-alignment: center; +} + +export component RainInfo inherits Rectangle { + in property precipitation-probability; + in property rain-volume; + in property snow-volume; + + in property minimal: false; + + property is-snow: root.snow-volume > root.rain-volume; + property volume: Math.max(root.rain-volume, root.snow-volume); + property probability: Math.round(root.precipitation-probability * 100); + + property volume-display: Math.round(volume * 10) / 10; + property type-indicator: self.is-snow ? "\u{f076}" : "\u{f078}"; + property type-color: self.is-snow ? AppPalette.snow-white : AppPalette.rain-blue; + + property max-bar-volume: 10; + + padding-top: 2px; + padding-bottom: 2px; + + opacity: root.minimal ? (root.precipitation-probability * 30% + 70%) : 100%; + + if !root.minimal: Rectangle { + x: parent.width - self.width; + y: parent.height - self.height; + + width: 3px; + height: (parent.height - parent.padding-top - parent.padding-bottom) * + Math.min(root.volume, root.max-bar-volume) / root.max-bar-volume * 100%; + + background: root.type-color; + opacity: root.precipitation-probability * 70% + 30%; + } + + VerticalLayout { + HorizontalLayout { + alignment: center; + spacing: 3px; + + DataText { + minimal: root.minimal; + text: "\{root.probability}%"; + + color: root.type-color; + } + + if !root.minimal || !WindowInfo.is-portrait: WeatherIcon { + font-size: root.minimal ? 0.9rem : 1rem; + text: "\{root.type-indicator}"; + color: root.type-color; + } + + if root.minimal && WindowInfo.is-portrait: DataText { + minimal: true; + text: "/ \{root.volume-display}l"; + + color: root.type-color; + } + } + + if !root.minimal || !WindowInfo.is-portrait: DataText { + minimal: root.minimal; + text: "\{root.volume-display}l"; + + color: root.type-color; + } + } +} + +export component UvInfo inherits Rectangle { + in property uv-index; + + in property minimal: false; + + property uv-index-rate: (root.uv-index / 12.0); + + opacity: root.minimal ? (root.uv-index-rate * 30% + 70%) : 100%; + + HorizontalLayout { + alignment: center; + spacing: 3px; + + WeatherIcon { + text: "\u{f06e}"; + + font-size: root.minimal ? 0.9rem : 1.3rem; + + opacity: root.minimal ? 100% : (root.uv-index-rate * 70% + 30%); + color: AppPalette.sun-yellow; + } + + if root.minimal: DataText { + minimal: true; + text: "UV"; + + color: AppPalette.sun-yellow; + } + + DataText { + minimal: root.minimal; + text: "\{root.uv-index}"; + + color: AppPalette.sun-yellow; + } + } +} diff --git a/ui/expanded_city_weather_tile.slint b/ui/expanded_city_weather_tile.slint new file mode 100644 index 0000000..d641ca2 --- /dev/null +++ b/ui/expanded_city_weather_tile.slint @@ -0,0 +1,282 @@ +import { VerticalBox, HorizontalBox } from "std-widgets.slint"; + +import { WindowInfo } from "./ui_utils.slint"; +import { AppPalette } from "./style/styles.slint"; +import { AppText } from "./controls/generic.slint"; +import { WeatherIcon, RainInfo, UvInfo } from "./controls/weather.slint"; +import { WeatherInfo, WeatherForecastInfo, CityWeatherInfo } from "weather_datatypes.slint"; +import { CityWeather } from "weather_datatypes.slint"; + +import { CityWeatherTile } from "city_weather_tile.slint"; + +component ForecastDayLineBase inherits Rectangle { + out property<{temp: length, rain: length, uv: length}> fields-width: { + temp: 90px, rain: 65px, uv: 65px + }; +} + +component ForecastDataText inherits AppText { + font-size: 1.1rem; + overflow: elide; + horizontal-alignment: center; + vertical-alignment: center; +} + +component ForecastTitleText inherits ForecastDataText { + font-size: 1.25rem; + font-weight: 500; + letter-spacing: 1pt; +} + +component ForecastTitleLine inherits ForecastDayLineBase { + HorizontalLayout { + // spacer + Rectangle { horizontal-stretch: 1; } + + ForecastTitleText { + preferred-width: root.fields-width.temp; + text: @tr("Max/Min"); + } + ForecastTitleText { + preferred-width: root.fields-width.rain; + text: @tr("Rain"); + } + ForecastTitleText { + preferred-width: root.fields-width.uv; + text: @tr("UV"); + } + + if !WindowInfo.is-portrait: HorizontalLayout { + width: WindowInfo.is-portrait ? 0% : 50%; + + for description[index] in [ @tr("Morning"), @tr("Day"), @tr("Evening"), @tr("Night") ]: + ForecastTitleText { + width: 25%; + text: description; + } + } + } +} + +component ForecastDayLine inherits ForecastDayLineBase { + in property day-name; + in property day-weather; + + height: 50px; + + HorizontalLayout { + // spacer + Rectangle { width: 5px; } + + name-text := ForecastDataText { + horizontal-stretch: 1; + min-width: self.preferred-width; + + horizontal-alignment: left; + text: root.day-name; + font-size: 1.2rem; + } + + WeatherIcon { + icon-type: root.day-weather.icon_type; + font-size: 1.8rem; + + visible: WindowInfo.window-width >= 360px; + } + + // spacer + Rectangle { + max-width: WindowInfo.window-width >= 380px ? self.preferred-width : 0; + preferred-width: 15px; + } + + ForecastDataText { + property min-temp: Math.round(root.day-weather.detailed_temp.min); + property max-temp: Math.round(root.day-weather.detailed_temp.max); + + preferred-width: root.fields-width.temp; + text: "\{self.max-temp}° / \{self.min-temp}°"; + } + + RainInfo { + precipitation-probability: root.day-weather.precipitation_prob; + rain-volume: root.day-weather.rain; + snow-volume: root.day-weather.snow; + + preferred-width: root.fields-width.rain; + } + + UvInfo { + uv-index: root.day-weather.uv; + + preferred-width: root.fields-width.uv; + } + } +} + +component ForecastDayDetails inherits HorizontalLayout { + in property day-weather; + + property<[{ time: string, temp: float }]> temp-model: [ + { time: "\u{f051}", temp: Math.round(root.day-weather.detailed_temp.morning) }, + { time: "\u{f00d}", temp: Math.round(root.day-weather.detailed_temp.day) }, + { time: "\u{f052}", temp: Math.round(root.day-weather.detailed_temp.evening) }, + { time: "\u{f02e}", temp: Math.round(root.day-weather.detailed_temp.night) }, + ]; + + padding-top: WindowInfo.is-portrait ? 10px : 0; + padding-bottom: WindowInfo.is-portrait ? 10px : 0; + + for time-temp in root.temp-model: + HorizontalLayout { + width: 25%; + alignment: center; + spacing: 5px; + + WeatherIcon { + text: "\{time-temp.time}"; + font-size: 1.3rem; + } + + ForecastDataText { + font-size: 1.3rem; + text: "\{time-temp.temp}°"; + } + } +} + +component ForecastDayDelegate inherits TouchArea { + in property expanded: false; + in property alternative-background: false; + + in property day-name; + in property day-weather; + + animate height { duration: 250ms; easing: ease-in-out-quad; } + + height: root.expanded ? self.preferred-height : main-info-line.preferred-height; + + Rectangle { + background: root.alternative-background ? Colors.white.transparentize(80%) : transparent; + clip: true; + + VerticalLayout { + main-info-line := HorizontalLayout { + ForecastDayLine { + day-name: root.day-name; + day-weather: root.day-weather; + } + + if !WindowInfo.is-portrait: ForecastDayDetails { + width: 50%; + day-weather: root.day-weather; + } + } + + if WindowInfo.is-portrait: ForecastDayDetails { + day-weather: root.day-weather; + } + } + } +} + +export component ExpandedCityWeatherTile inherits TouchArea { + in property city-weather-info <=> base-tile.city-weather-info; + in property alternative-background <=> base-tile.alternative-background; + + out property expanded: false; + in-out property animation-duration: 300ms; + + in property block-x; + in property block-y; + in property block-width; + in property block-height; + + in property full-x; + in property full-y; + in property full-width; + in property full-height; + + public function expand() { + // Not working properly without the lines below. (A bug?) + // Seems the animation in transition using old values, and + // accessing the properties somehow forces the update. + root.x; root.y; root.width; root.height; + details-rect.height; + + root.expanded = true; + } + + public function collapse() { + root.expanded = false; + } + + x: self.block-x; + y: self.block-y; + width: self.block-width; + height: self.block-height; + + visible: self.opacity > 0; + opacity: 0; + + states [ + full-size when root.expanded: { + opacity: 1; + x: full-x; + y: full-y; + width: full-width; + height: full-height; + + in { + animate x, y, width, height { duration: root.animation-duration; easing: ease-in-out-quad; } + } + out { + animate x, y, width, height { duration: root.animation-duration; easing: ease-in-out-quad; } + animate opacity { delay: root.animation-duration; } + } + } + ] + + VerticalLayout { + base-tile := CityWeatherTile { + show-animations: false; + + clicked => { + root.clicked(); + } + } + + details-rect := Rectangle { + vertical-stretch: 1; + + clip: true; + + Flickable { + padding-top: 15px; + padding-bottom: 15px; + + y: self.padding-top; + height: parent.height - self.padding-top - self.padding-bottom; + + VerticalLayout { + alignment: start; + padding-left: 15px; + padding-right: 15px; + + ForecastTitleLine {} + + for day-forecast-weather[index] in root.city-weather-info.forecast-weather: + ForecastDayDelegate { + alternative-background: Math.mod(index, 2) == 0; + day-name: day-forecast-weather.day-name; + day-weather: day-forecast-weather.weather-info; + + clicked => { + self.expanded = !self.expanded; + } + } + } + } + } + } +} diff --git a/ui/forecast_with_graph.slint b/ui/forecast_with_graph.slint new file mode 100644 index 0000000..fc6c59c --- /dev/null +++ b/ui/forecast_with_graph.slint @@ -0,0 +1,125 @@ +import { VerticalBox } from "std-widgets.slint"; +import { AppPalette } from "./style/styles.slint"; +import { AppText } from "./controls/generic.slint"; +import { WeatherIcon, RainInfo, UvInfo } from "./controls/weather.slint"; +import { WeatherInfo, WeatherForecastInfo, CityWeather } from "./weather_datatypes.slint"; + +component ForecastGraphText inherits AppText { + horizontal-alignment: center; + vertical-alignment: center; + + font-size: 0.85rem; +} + +component DayForecastGraphEntry inherits VerticalLayout { + in property day-name; + in property day-weather; + in property detailed: true; + + spacing: 5px; + + ForecastGraphText { + font-size: 1.2rem; + text: day-name; + } + + WeatherIcon { + icon-type: day-weather.icon-type; + font-size: 1.6rem; + } + + VerticalLayout { + spacing: 5px; + + ForecastGraphText { + text: Math.round(day-weather.detailed_temp.max) + "° / " + Math.round(day-weather.detailed_temp.min) + "°"; + } + + RainInfo { + precipitation-probability: root.day-weather.precipitation_prob; + rain-volume: root.day-weather.rain; + snow-volume: root.day-weather.snow; + + minimal: true; + } + + UvInfo { + uv-index: root.day-weather.uv; + + minimal: true; + } + } +} + +export component DayForecastGraph inherits Rectangle { + in property <[WeatherForecastInfo]> forecast-weather; + in property show-animations: true; + + property preferred-day-width: 85px; + + // max-days-count is not directly as a binding here, only when the value is actually changed. + // This is to avoid reevaluation of the conditional components that rely on it for every window size change. + // see: https://github.com/slint-ui/slint/issues/5209 + property max-days-count: 0; + property days-count: Math.min(root.forecast-weather.length, root.max-days-count); + + property day-width: root.width / root.days-count; + + function update-max-days-count() { + if (Math.floor(root.width / root.preferred-day-width) != root.max-days-count) { + root.max-days-count = Math.floor(root.width / root.preferred-day-width); + } + } + + init => { root.update-max-days-count(); } + changed width => { root.update-max-days-count(); } + + preferred-height: layout.preferred-height; + + Path { + property visible-part: 0%; + + y: 0; + height: 50%; + + stroke-width: 2px; + commands: CityWeather.get_forecast_graph_command( + root.forecast-weather, root.days-count, self.width, self.height); + + stroke: @linear-gradient(90deg, AppPalette.foreground.with-alpha(25%) 0%, + AppPalette.foreground.with-alpha(25%) self.visible-part, + transparent self.visible-part, + transparent 100%); + + opacity: 0.0; + animate opacity { duration: root.show-animations ? 1200ms : 0ms; easing: ease-in; } + animate visible-part { duration: root.show-animations ? 900ms : 0ms; easing: ease-in; } + + init => { + self.opacity = 1.0; + self.visible-part = 100%; + } + } + + layout := HorizontalLayout { + for index in root.days-count: + DayForecastGraphEntry { + property day-forecast-weather: root.forecast-weather[index]; + property animation-duration: 0ms; + + width: root.day-width; + day-name: day-forecast-weather.day-name; + day-weather: day-forecast-weather.weather-info; + + opacity: 0.0; + animate opacity { duration: self.animation-duration; easing: ease-in-out-quad; } + + init => { + if (root.show-animations) { + self.animation-duration = 600ms + (500ms - index * 50ms) * index; + } + self.opacity = 1.0; + } + } + } +} diff --git a/ui/location_datatypes.slint b/ui/location_datatypes.slint new file mode 100644 index 0000000..5d36fab --- /dev/null +++ b/ui/location_datatypes.slint @@ -0,0 +1,14 @@ +export struct GeoLocationEntry { + name: string, + state: string, + country: string, + lat: float, + lon: float, +} + +export global GeoLocation { + in property <[GeoLocationEntry]> result-list; + + callback search-location(string); + callback add-location(GeoLocationEntry); +} \ No newline at end of file diff --git a/ui/location_search.slint b/ui/location_search.slint new file mode 100644 index 0000000..258b020 --- /dev/null +++ b/ui/location_search.slint @@ -0,0 +1,64 @@ +import { PageBase } from "page-base.slint"; +import { AppText, TextField } from "./controls/generic.slint"; +import { BusyLayerController } from "./controls/busy-layer.slint"; +import { GeoLocation } from "./location_datatypes.slint"; + +import { Button } from "std-widgets.slint"; + +export component LocationSearchView inherits PageBase { + callback close-request; + + public function clear() { + GeoLocation.search_location(""); + text-field.text = ""; + } + + forward-focus: text-field; + + VerticalLayout { + padding: 20px; + spacing: 10px; + + text-field := TextField { + icon-text: "\u{f002}"; + placeholder-text: "Search"; + + edited => { + GeoLocation.search_location(self.text); + } + } + + Flickable { + VerticalLayout { + alignment: start; + + for data[index] in GeoLocation.result-list : Rectangle { + preferred-height: layout.preferred-height + 20px; + min-height: self.preferred-height; + + layout := VerticalLayout { + alignment: center; + spacing: 5px; + + AppText { + text: data.name; + font-size: 1.3rem; + } + AppText { + text: data.state == "" ? data.country : data.state + ", " + data.country; + font-size: 0.9rem; + } + } + + TouchArea { + clicked => { + BusyLayerController.set-busy(); + GeoLocation.add-location(data); + root.close-request(); + } + } + } + } + } + } +} diff --git a/ui/main.slint b/ui/main.slint new file mode 100644 index 0000000..fd61831 --- /dev/null +++ b/ui/main.slint @@ -0,0 +1,179 @@ +import { Palette } from "std-widgets.slint"; +import { WindowInfo, WindowInfoHelper } from "./ui_utils.slint"; +import { StackView, StackPage } from "./controls/stackview.slint"; +import { CityListView } from "./city_weather.slint"; +import { CityWeather } from "./weather_datatypes.slint"; +import { LocationSearchView } from "./location_search.slint"; +import { GeoLocation } from "./location_datatypes.slint"; +import { AppPalette, AppFonts } from "./style/styles.slint"; +import { FloatingTextButton } from "./controls/generic.slint"; +import { BusyLayerController, BusyLayer } from "./controls/busy-layer.slint"; + +// Re export for native rust +export { WindowInfo, AppPalette, BusyLayerController, CityWeather, GeoLocation } + +component EdgeFloatingTextButton inherits FloatingTextButton { + out property edge-spacing: 15px; + + padding: 10px; + font-size: 2rem; +} + +component AnimatedStackPage inherits StackPage { + // is-active and is-opened are not set as a binding here, only when the value is actually changed. + // This is to avoid redundant reevaluation of dependent properties and conditional elements. + // see: https://github.com/slint-ui/slint/issues/5209 + out property is-active: false; + out property is-opened: false; + + // using a helper int property to be able to use animate + property is-active-value: 0; + + property animation-duration: 250ms; + + visible: root.is-active; + + init => { root.is-active = (self.is-active_value == 1); } + changed is-active-value => { root.is-active = (self.is-active_value == 1); } + + states [ + active when self.is-current: { + is-active-value: 1; + + out { + animate is-active-value { delay: root.animation-duration; } + } + } + ] + + content := Rectangle { + changed y => { + // First open animation is not working properly without the line below. (A bug?) + // Seems the animation in transition is using old values, + // and accessing the property somehow forces the update. + self.y; + + if (root.is-opened != (self.y == 0)) { + root.is-opened = (self.y == 0); + } + } + + y: root.is-current ? 0px : root.height; + + animate y { duration: root.animation-duration; easing: ease-in-out-quad; } + + @children + } +} + +enum PageType { + Main, + AddLocation, +} + +export component AppWindow inherits Window { + background: AppPalette.background; + default-font-size: AppFonts.default-font-size; + + preferred-width: 900px; + preferred-height: 600px; + + WindowInfoHelper { + init => { + // no support for the different modes currently + // this is to display slint badge in proper colors + Palette.color-scheme = ColorScheme.dark; + } + } + + stack := StackView { + function show-page(pageType : PageType) { + if (pageType == PageType.Main) { + self.current-index = 0; + } + else if (pageType == PageType.AddLocation) { + self.current-index = 1; + } + } + + function back-to-main() { + self.show-page(PageType.Main); + } + + current-index: 0; + min-index: 0; + + StackPage { + is-current: self.check-is-current(stack.current-index); + init => { self.page-index = stack.insert-page(); } + visible: self.page-index <= stack.current-index; + + CityListView {} + + // right (refresh) button + EdgeFloatingTextButton { + x: parent.width - self.width - self.edge-spacing; + y: parent.height - self.height - self.edge-spacing; + + text: "\u{f01e}"; + + clicked => { + BusyLayerController.set-busy(); + CityWeather.refresh-all(); + } + } + + // left (add) button + EdgeFloatingTextButton { + x: self.edge-spacing; + y: parent.height - self.height - self.edge-spacing; + + visible: CityWeather.can-add-city; + + text: "\u{f067}"; + + clicked => { + stack.show-page(PageType.AddLocation); + } + } + } + + AnimatedStackPage { + is-current: self.check-is-current(stack.current-index); + init => { self.page-index = stack.insert-page(); } + + location-search-view := LocationSearchView { + property is-active: parent.is-active; + property is-opened: parent.is-opened; + + changed is-active => { + if (self.is-active) { + self.clear(); + } + } + + changed is-opened => { + if (self.is-opened) { + self.focus(); + } + } + + close-request => { + self.clear-focus(); + stack.back-to-main(); + } + + EdgeFloatingTextButton { + x: parent.width - self.width - self.edge-spacing; + y: parent.height - self.height - self.edge-spacing; + + text: "\u{f00d}"; + + clicked => { location-search-view.close-request(); } + } + } + } + } + + if BusyLayerController.is-busy: BusyLayer {} +} diff --git a/ui/page-base.slint b/ui/page-base.slint new file mode 100644 index 0000000..fc9ee34 --- /dev/null +++ b/ui/page-base.slint @@ -0,0 +1,5 @@ +import { AppPalette } from "style/styles.slint"; + +export component PageBase inherits Rectangle { + background: @linear-gradient(180deg, AppPalette.background 0%, AppPalette.alternate-background 100%); +} diff --git a/ui/style/styles.slint b/ui/style/styles.slint new file mode 100644 index 0000000..75652ab --- /dev/null +++ b/ui/style/styles.slint @@ -0,0 +1,28 @@ +import { Palette } from "std-widgets.slint"; + +import "../assets/weathericons-font.ttf"; +import "../assets/font-awesome.ttf"; + +export global AppPalette { + out property background: #1673b4; + out property alternate-background: #2296bc; + out property foreground: white; + + out property sun-yellow: Colors.yellow; + out property snow-white: Colors.cornsilk; + out property rain-blue: #7DCDFF.brighter(15%); + + out property error-red: Colors.red.darker(20%); +} + +export global AppFonts { + out property default-font-size: 10pt; + + out property icons-font-name: "FontAwesome"; + out property weather-icons-font-name: "Weather Icons"; +} + +export global Images { + out property busy-indicator: @image-url("../assets/busy-indicator.png"); + out property slint-logo: @image-url("../assets/slint-logo.svg"); +} diff --git a/ui/ui_utils.slint b/ui/ui_utils.slint new file mode 100644 index 0000000..e809f6b --- /dev/null +++ b/ui/ui_utils.slint @@ -0,0 +1,27 @@ +export global WindowInfo { + // default values provided for the slint-viewer + in property window-width: 400px; + in property window-height: 700px; + + // is-portrait is not set as a binding here, only when the value is actually changed. + // This is to avoid reevaluation of the conditional components that rely on it for every window size change. + // see: https://github.com/slint-ui/slint/issues/5209 + in property is-portrait: true; +} + +export component WindowInfoHelper inherits Rectangle { + function check-is-portrait() { + if (WindowInfo.is-portrait != (self.width < self.height)) { + WindowInfo.is-portrait = (self.width < self.height); + } + } + + changed width => { + WindowInfo.window-width = self.width; + self.check-is-portrait(); + } + changed height => { + WindowInfo.window-height = self.height; + self.check-is-portrait(); + } +} \ No newline at end of file diff --git a/ui/weather_datatypes.slint b/ui/weather_datatypes.slint new file mode 100644 index 0000000..469be9a --- /dev/null +++ b/ui/weather_datatypes.slint @@ -0,0 +1,54 @@ +export enum IconType { + Unknown, + Sunny, + PartiallyCloudy, + MostlyCloudy, + Cloudy, + SunnyRainy, + Rainy, + Stormy, + Snowy, + Foggy, +} + +export struct TemperatureInfo { + min: float, + max:float, + + morning: float, + day: float, + evening:float, + night:float, +} + +export struct WeatherInfo { + description: string, + icon_type: IconType, + current_temp: float, + detailed_temp: TemperatureInfo, + uv: int, + precipitation_prob: float, + rain: float, + snow: float, +} + +export struct WeatherForecastInfo { + day_name: string, + weather_info: WeatherInfo, +} + +export struct CityWeatherInfo { + city_name: string, + current_weather: WeatherInfo, + forecast_weather: [WeatherForecastInfo], +} + +export global CityWeather { + in property <[CityWeatherInfo]> city-weather; + in property can-add-city: false; + + pure callback refresh-all(); + pure callback delete(int); + pure callback reorder(int, int); + pure callback get_forecast_graph_command([WeatherForecastInfo], int, length, length) -> string; +} diff --git a/wasm/index.html b/wasm/index.html new file mode 100644 index 0000000..517962f --- /dev/null +++ b/wasm/index.html @@ -0,0 +1,59 @@ + + + + + + +
+ + +
+ + +
+
Loading...
+ + + + +
+ +