From 442fc425f723aa7c720e9e5aafda3aca16327089 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 21 Oct 2024 12:03:09 +0300 Subject: [PATCH] ui: update to egui 0.29.1, wallet qr scan content, panels strokes and colors refactoring, check closeable modal at desktop title, fix app socket name --- Cargo.lock | 1398 ++++++----------- Cargo.toml | 22 +- src/gui/app.rs | 227 ++- src/gui/colors.rs | 22 +- src/gui/views/camera.rs | 161 +- src/gui/views/content.rs | 145 +- src/gui/views/file_pick.rs | 4 +- src/gui/views/modal.rs | 185 ++- src/gui/views/network/connections.rs | 48 +- src/gui/views/network/content.rs | 275 ++-- src/gui/views/network/metrics.rs | 132 +- src/gui/views/network/mining.rs | 98 +- src/gui/views/network/node.rs | 155 +- src/gui/views/network/settings.rs | 18 +- src/gui/views/network/setup/dandelion.rs | 13 +- src/gui/views/network/setup/node.rs | 12 +- src/gui/views/network/setup/p2p.rs | 34 +- src/gui/views/network/setup/pool.rs | 18 +- src/gui/views/network/setup/stratum.rs | 10 +- src/gui/views/network/types.rs | 22 +- src/gui/views/pull_to_refresh.rs | 6 +- src/gui/views/qr.rs | 19 +- src/gui/views/scan.rs | 49 +- src/gui/views/title_panel.rs | 50 +- src/gui/views/types.rs | 5 + src/gui/views/views.rs | 127 +- src/gui/views/wallets/content.rs | 176 ++- src/gui/views/wallets/creation/creation.rs | 93 +- src/gui/views/wallets/creation/mnemonic.rs | 2 +- src/gui/views/wallets/modals/conn.rs | 6 +- src/gui/views/wallets/modals/wallets.rs | 4 +- src/gui/views/wallets/wallet/content.rs | 469 +++--- .../views/wallets/wallet/messages/content.rs | 72 +- .../views/wallets/wallet/modals/accounts.rs | 4 +- src/gui/views/wallets/wallet/modals/scan.rs | 133 -- .../views/wallets/wallet/settings/common.rs | 8 +- .../wallets/wallet/settings/connection.rs | 2 +- .../views/wallets/wallet/settings/content.rs | 49 +- .../views/wallets/wallet/settings/recovery.rs | 14 +- .../views/wallets/wallet/transport/content.rs | 45 +- .../views/wallets/wallet/transport/send.rs | 10 +- src/gui/views/wallets/wallet/txs/content.rs | 292 ++-- src/gui/views/wallets/wallet/txs/tx.rs | 256 +-- src/lib.rs | 29 +- src/main.rs | 44 +- src/settings/settings.rs | 14 - src/tor/http.rs | 14 + 47 files changed, 2235 insertions(+), 2756 deletions(-) delete mode 100644 src/gui/views/wallets/wallet/modals/scan.rs diff --git a/Cargo.lock b/Cargo.lock index e600cdb..8638805 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,76 +20,91 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.12.3" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" +checksum = "99b76d84ee70e30a4a7e39ab9018e2b17a6a09e31084176cc7c0b2dec036ba45" + +[[package]] +name = "accesskit_atspi_common" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5393c75d4666f580f4cac0a968bc97c36076bb536a129f28210dac54ee127ed" dependencies = [ - "enumn", + "accesskit", + "accesskit_consumer", + "atspi-common", "serde", + "thiserror", + "zvariant", ] [[package]] name = "accesskit_consumer" -version = "0.16.1" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" +checksum = "7a12dc159d52233c43d9fe5415969433cbdd52c3d6e0df51bda7d447427b9986" dependencies = [ "accesskit", + "immutable-chunkmap", ] [[package]] name = "accesskit_macos" -version = "0.10.1" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" +checksum = "49509c722e8da39e6c569f7d13ec51620208988913e738abbc67e3c65f06e6d5" dependencies = [ "accesskit", "accesskit_consumer", - "objc2 0.3.0-beta.3.patch-leaks.3", + "objc2", + "objc2-app-kit", + "objc2-foundation", "once_cell", ] [[package]] name = "accesskit_unix" -version = "0.6.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f46c18d99ba61ad7123dd13eeb0c104436ab6af1df6a1cd8c11054ed394a08" +checksum = "be7f5cf6165be10a54b2655fa2e0e12b2509f38ed6fc43e11c31fdb7ee6230bb" dependencies = [ "accesskit", - "accesskit_consumer", + "accesskit_atspi_common", "async-channel", - "async-once-cell", + "async-executor", + "async-task", "atspi", - "futures-lite 1.13.0", - "once_cell", + "futures-lite", + "futures-util", "serde", - "zbus 3.15.2", + "zbus", ] [[package]] name = "accesskit_windows" -version = "0.15.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" +checksum = "974e96c347384d9133427167fb8a58c340cb0496988dacceebdc1ed27071023b" dependencies = [ "accesskit", "accesskit_consumer", - "once_cell", "paste", "static_assertions", - "windows 0.48.0", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] name = "accesskit_winit" -version = "0.16.1" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5284218aca17d9e150164428a0ebc7b955f70e3a9a78b4c20894513aabf98a67" +checksum = "9987e852fe7e4e5a493f8c8af0b729b47da2750f0dea10a4c8984fe1117ebaec" dependencies = [ "accesskit", "accesskit_macos", "accesskit_unix", "accesskit_windows", + "raw-window-handle", "winit", ] @@ -179,17 +194,6 @@ dependencies = [ "sha2 0.9.9", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.15", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -199,7 +203,6 @@ dependencies = [ "cfg-if 1.0.0", "getrandom 0.2.15", "once_cell", - "serde", "version_check", "zerocopy", ] @@ -269,27 +272,6 @@ dependencies = [ "syn 1.0.109", ] -[[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" @@ -304,7 +286,7 @@ dependencies = [ "jni-sys", "libc", "log", - "ndk 0.9.0", + "ndk", "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", @@ -429,7 +411,7 @@ dependencies = [ "core-graphics 0.23.2", "image 0.25.2", "log", - "objc2 0.5.2", + "objc2", "objc2-app-kit", "objc2-foundation", "parking_lot 0.12.3", @@ -537,11 +519,11 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "ash" -version = "0.37.3+1.3.251" +version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading 0.7.4", + "libloading", ] [[package]] @@ -550,30 +532,20 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfe7e0dd0ac5a401dc116ed9f9119cf9decc625600474cb41f0fc0a0050abc9a" dependencies = [ - "async-fs 2.1.2", + "async-fs", "async-net", "enumflags2", "futures-channel", "futures-util", "rand 0.8.5", - "raw-window-handle 0.6.2", + "raw-window-handle", "serde", "serde_repr", "url", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.4", - "zbus 4.4.0", -] - -[[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", + "wayland-protocols", + "zbus", ] [[package]] @@ -582,7 +554,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" dependencies = [ - "event-listener 5.3.1", + "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite 0.2.14", @@ -624,52 +596,20 @@ checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.1", - "futures-lite 2.3.0", + "fastrand", + "futures-lite", "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 1.4.0", - "blocking", - "futures-lite 1.13.0", -] - [[package]] name = "async-fs" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ - "async-lock 3.4.0", + "async-lock", "blocking", - "futures-lite 2.3.0", -] - -[[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 1.4.0", - "cfg-if 1.0.0", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", + "futures-lite", ] [[package]] @@ -678,35 +618,26 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" dependencies = [ - "async-lock 3.4.0", + "async-lock", "cfg-if 1.0.0", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "parking", - "polling 3.7.3", - "rustix 0.38.37", + "polling", + "rustix", "slab", "tracing", "windows-sys 0.59.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.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener", "event-listener-strategy", "pin-project-lite 0.2.14", ] @@ -729,32 +660,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ - "async-io 2.3.4", + "async-io", "blocking", - "futures-lite 2.3.0", -] - -[[package]] -name = "async-once-cell" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" - -[[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 1.0.0", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.37", - "windows-sys 0.48.0", + "futures-lite", ] [[package]] @@ -764,15 +672,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" dependencies = [ "async-channel", - "async-io 2.3.4", - "async-lock 3.4.0", + "async-io", + "async-lock", "async-signal", "async-task", "blocking", "cfg-if 1.0.0", - "event-listener 5.3.1", - "futures-lite 2.3.0", - "rustix 0.38.37", + "event-listener", + "futures-lite", + "rustix", "tracing", ] @@ -793,13 +701,13 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.4", - "async-lock 3.4.0", + "async-io", + "async-lock", "atomic-waker", "cfg-if 1.0.0", "futures-core", "futures-io", - "rustix 0.38.37", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -873,9 +781,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atspi" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" +checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf" dependencies = [ "atspi-common", "atspi-connection", @@ -884,39 +792,42 @@ dependencies = [ [[package]] name = "atspi-common" -version = "0.3.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" +checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7" dependencies = [ "enumflags2", "serde", "static_assertions", - "zbus 3.15.2", - "zbus_names 2.6.1", - "zvariant 3.15.2", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", ] [[package]] name = "atspi-connection" -version = "0.3.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" +checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333" dependencies = [ "atspi-common", "atspi-proxies", - "futures-lite 1.13.0", - "zbus 3.15.2", + "futures-lite", + "zbus", ] [[package]] name = "atspi-proxies" -version = "0.3.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" +checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" dependencies = [ "atspi-common", "serde", - "zbus 3.15.2", + "zbus", + "zvariant", ] [[package]] @@ -1090,11 +1001,11 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" dependencies = [ - "bit-vec", + "bit-vec 0.7.0", ] [[package]] @@ -1103,6 +1014,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" + [[package]] name = "bit_field" version = "0.10.2" @@ -1229,51 +1146,13 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "block-sys" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" -dependencies = [ - "objc-sys 0.2.0-beta.2", -] - -[[package]] -name = "block-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" -dependencies = [ - "objc-sys 0.3.5", -] - -[[package]] -name = "block2" -version = "0.2.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" -dependencies = [ - "block-sys 0.1.0-beta.1", - "objc2-encode 2.0.0-pre.2", -] - -[[package]] -name = "block2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" -dependencies = [ - "block-sys 0.2.1", - "objc2 0.4.1", -] - [[package]] name = "block2" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2 0.5.2", + "objc2", ] [[package]] @@ -1285,7 +1164,7 @@ dependencies = [ "async-channel", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "piper", ] @@ -1389,20 +1268,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" -[[package]] -name = "calloop" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" -dependencies = [ - "bitflags 2.6.0", - "log", - "polling 3.7.3", - "rustix 0.38.37", - "slab", - "thiserror", -] - [[package]] name = "calloop" version = "0.13.0" @@ -1411,32 +1276,20 @@ checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ "bitflags 2.6.0", "log", - "polling 3.7.3", - "rustix 0.38.37", + "polling", + "rustix", "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.37", - "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.37", + "calloop", + "rustix", "wayland-backend", "wayland-client", ] @@ -1584,7 +1437,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.5", + "libloading", ] [[package]] @@ -2089,31 +1942,15 @@ dependencies = [ [[package]] name = "d3d12" -version = "0.20.0" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" +checksum = "bdbd1f579714e3c809ebd822c81ef148b1ceaeb3d535352afc73fd0c4c6a0017" dependencies = [ "bitflags 2.6.0", - "libloading 0.8.5", + "libloading", "winapi 0.3.9", ] -[[package]] -name = "dark-light" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a76fa97167fa740dcdbfe18e8895601e1bc36525f09b044e00916e717c03a3c" -dependencies = [ - "dconf_rs", - "detect-desktop-environment", - "dirs 4.0.0", - "objc", - "rust-ini", - "web-sys", - "winreg 0.10.1", - "zbus 4.4.0", -] - [[package]] name = "darling" version = "0.14.4" @@ -2209,12 +2046,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" -[[package]] -name = "dconf_rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" - [[package]] name = "der" version = "0.7.9" @@ -2361,12 +2192,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" -[[package]] -name = "detect-desktop-environment" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" - [[package]] name = "digest" version = "0.8.1" @@ -2416,15 +2241,6 @@ dependencies = [ "dirs-sys 0.3.7", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys 0.3.7", -] - [[package]] name = "dirs" version = "5.0.1" @@ -2501,15 +2317,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.5", + "libloading", ] -[[package]] -name = "dlv-list" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" - [[package]] name = "doctest-file" version = "1.0.0" @@ -2531,6 +2341,12 @@ 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 = "dyn-clone" version = "1.0.17" @@ -2578,13 +2394,11 @@ dependencies = [ [[package]] name = "ecolor" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e6b451ff1143f6de0f33fc7f1b68fecfd2c7de06e104de96c4514de3f5396f8" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ "bytemuck", "emath", - "serde", ] [[package]] @@ -2650,31 +2464,29 @@ dependencies = [ [[package]] name = "eframe" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6490ef800b2e41ee129b1f32f9ac15f713233fe3bc18e241a1afe1e4fb6811e0" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ - "ahash 0.8.11", + "ahash", "bytemuck", "document-features", "egui", "egui-wgpu", "egui-winit", "egui_glow", - "glow", + "glow 0.14.1", "glutin", "glutin-winit", "image 0.25.2", "js-sys", "log", - "objc2 0.5.2", + "objc2", "objc2-app-kit", "objc2-foundation", "parking_lot 0.12.3", "percent-encoding", "pollster", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", + "raw-window-handle", "static_assertions", "wasm-bindgen", "wasm-bindgen-futures", @@ -2682,31 +2494,29 @@ dependencies = [ "web-time", "wgpu", "winapi 0.3.9", + "windows-sys 0.52.0", "winit", ] [[package]] name = "egui" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c97e70a2768de630f161bb5392cbd3874fcf72868f14df0e002e82e06cb798" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ "accesskit", - "ahash 0.8.11", + "ahash", "emath", "epaint", "log", "nohash-hasher", - "serde", ] [[package]] name = "egui-wgpu" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c7a7c707877c3362a321ebb4f32be811c0b91f7aebf345fb162405c0218b4c" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ - "ahash 0.8.11", + "ahash", "bytemuck", "document-features", "egui", @@ -2721,16 +2531,15 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4e066af341bf92559f60dbdf2020b2a03c963415349af5f3f8d79ff7a4926" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ "accesskit_winit", - "ahash 0.8.11", + "ahash", "arboard", "egui", "log", - "raw-window-handle 0.6.2", + "raw-window-handle", "smithay-clipboard", "web-time", "webbrowser", @@ -2739,32 +2548,29 @@ dependencies = [ [[package]] name = "egui_extras" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb783d9fa348f69ed5c340aa25af78b5472043090e8b809040e30960cc2a746" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ - "ahash 0.8.11", + "ahash", "egui", "enum-map", "image 0.25.2", "log", "mime_guess2", "resvg", - "serde", ] [[package]] name = "egui_glow" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2bdc8b38cfa17cc712c4ae079e30c71c00cd4c2763c9e16dc7860a02769103" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ - "ahash 0.8.11", + "ahash", "bytemuck", "egui", - "glow", + "glow 0.14.1", "log", - "memoffset 0.9.1", + "memoffset", "wasm-bindgen", "web-sys", "winit", @@ -2797,12 +2603,10 @@ dependencies = [ [[package]] name = "emath" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6a21708405ea88f63d8309650b4d77431f4bc28fb9d8e6f77d3963b51249e6" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ "bytemuck", - "serde", ] [[package]] @@ -2890,17 +2694,6 @@ dependencies = [ "syn 2.0.79", ] -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2 1.0.87", - "quote 1.0.37", - "syn 2.0.79", -] - [[package]] name = "env_filter" version = "0.1.2" @@ -2952,21 +2745,25 @@ dependencies = [ [[package]] name = "epaint" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f0dcc0a0771e7500e94cd1cb797bd13c9f23b9409bdc3c824e2cbc562b7fa01" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" dependencies = [ "ab_glyph", - "ahash 0.8.11", + "ahash", "bytemuck", "ecolor", "emath", + "epaint_default_fonts", "log", "nohash-hasher", "parking_lot 0.12.3", - "serde", ] +[[package]] +name = "epaint_default_fonts" +version = "0.29.1" +source = "git+https://github.com/emilk/egui?rev=23728e145ec52bd1193f6f0123973763de4dbb3d#23728e145ec52bd1193f6f0123973763de4dbb3d" + [[package]] name = "equivalent" version = "1.0.1" @@ -2989,23 +2786,6 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" -[[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 0.2.14", -] - [[package]] name = "event-listener" version = "5.3.1" @@ -3023,7 +2803,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.1", + "event-listener", "pin-project-lite 0.2.14", ] @@ -3061,15 +2841,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[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.1" @@ -3425,28 +3196,13 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" -[[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 0.2.14", - "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.1", + "fastrand", "futures-core", "futures-io", "parking", @@ -3658,56 +3414,69 @@ dependencies = [ ] [[package]] -name = "glutin" -version = "0.31.3" +name = "glow" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" +checksum = "2f4a888dbe8181a7535853469c21c67ca9a1cea9460b16808fc018ea9e55d248" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec69412a0bf07ea7607e638b415447857a808846c2b685a43c8aa18bc6d5e499" dependencies = [ "bitflags 2.6.0", - "cfg_aliases 0.1.1", + "cfg_aliases 0.2.1", "cgl", "core-foundation 0.9.4", "dispatch", "glutin_egl_sys", "glutin_glx_sys", "glutin_wgl_sys", - "icrate", - "libloading 0.8.5", - "objc2 0.4.1", + "libloading", + "objc2", + "objc2-app-kit", + "objc2-foundation", "once_cell", - "raw-window-handle 0.5.2", + "raw-window-handle", "wayland-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", ] [[package]] name = "glutin-winit" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" dependencies = [ - "cfg_aliases 0.1.1", + "cfg_aliases 0.2.1", "glutin", - "raw-window-handle 0.5.2", + "raw-window-handle", "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +checksum = "cae99fff4d2850dbe6fb8c1fa8e4fead5525bab715beaacfccf3fb994e01c827" dependencies = [ "gl_generator", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "glutin_glx_sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +checksum = "9c2b2d3918e76e18e08796b55eb64e8fe6ec67d5a6b2e2a7e2edce224ad24c63" dependencies = [ "gl_generator", "x11-dl", @@ -3715,9 +3484,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" dependencies = [ "gl_generator", ] @@ -3743,9 +3512,9 @@ dependencies = [ [[package]] name = "gpu-allocator" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7" dependencies = [ "log", "presser", @@ -3778,7 +3547,7 @@ dependencies = [ name = "grim" version = "0.2.1" dependencies = [ - "android-activity 0.6.0", + "android-activity", "android_logger", "anyhow", "arboard", @@ -3786,7 +3555,6 @@ dependencies = [ "backtrace", "chrono", "curve25519-dalek 4.1.3", - "dark-light", "dirs 5.0.1", "ed25519-dalek 2.1.1", "eframe", @@ -3894,7 +3662,7 @@ version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdedcee9e20e0cc8dc1460b37b3951306c774a6aa5788e3f45addc56e60f7fbe" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", "bitflags 1.3.2", "byteorder", "chrono", @@ -4357,9 +4125,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] [[package]] name = "hashbrown" @@ -4367,7 +4132,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", ] @@ -4400,7 +4165,7 @@ dependencies = [ "bitflags 2.6.0", "com", "libc", - "libloading 0.8.5", + "libloading", "thiserror", "widestring", "winapi 0.3.9", @@ -4799,7 +4564,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -4811,17 +4576,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icrate" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" -dependencies = [ - "block2 0.3.0", - "dispatch", - "objc2 0.4.1", -] - [[package]] name = "id-arena" version = "2.2.1" @@ -4917,6 +4671,15 @@ 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 0.7.6", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -4968,15 +4731,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if 1.0.0", -] - [[package]] name = "interpolate_name" version = "0.2.4" @@ -5028,17 +4782,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "iovec" version = "0.1.4" @@ -5205,7 +4948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.5", + "libloading", "pkg-config", ] @@ -5292,16 +5035,6 @@ dependencies = [ "libc", ] -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if 1.0.0", - "winapi 0.3.9", -] - [[package]] name = "libloading" version = "0.8.5" @@ -5357,12 +5090,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[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" @@ -5575,15 +5302,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg 1.4.0", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -5622,9 +5340,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ "bitflags 2.6.0", "block", @@ -5795,18 +5513,18 @@ dependencies = [ [[package]] name = "naga" -version = "0.20.0" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" dependencies = [ "arrayvec 0.7.6", "bit-set", "bitflags 2.6.0", + "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", "indexmap 2.6.0", "log", - "num-traits 0.2.19", "rustc-hash", "spirv", "termcolor", @@ -5840,22 +5558,6 @@ dependencies = [ "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", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "thiserror", -] - [[package]] name = "ndk" version = "0.9.0" @@ -5867,6 +5569,7 @@ dependencies = [ "log", "ndk-sys 0.6.0+11769913", "num_enum", + "raw-window-handle", "thiserror", ] @@ -5936,18 +5639,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[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 1.0.0", - "libc", - "memoffset 0.7.1", -] - [[package]] name = "nix" version = "0.29.0" @@ -5958,7 +5649,7 @@ dependencies = [ "cfg-if 1.0.0", "cfg_aliases 0.2.1", "libc", - "memoffset 0.9.1", + "memoffset", ] [[package]] @@ -6291,47 +5982,20 @@ dependencies = [ "objc_exception", ] -[[package]] -name = "objc-sys" -version = "0.2.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" - [[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.3.0-beta.3.patch-leaks.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" -dependencies = [ - "block2 0.2.0-alpha.6", - "objc-sys 0.2.0-beta.2", - "objc2-encode 2.0.0-pre.2", -] - -[[package]] -name = "objc2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" -dependencies = [ - "objc-sys 0.3.5", - "objc2-encode 3.0.0", -] - [[package]] name = "objc2" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "objc-sys 0.3.5", - "objc2-encode 4.0.3", + "objc-sys", + "objc2-encode", ] [[package]] @@ -6341,15 +6005,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", + "block2", "libc", - "objc2 0.5.2", + "objc2", "objc2-core-data", "objc2-core-image", "objc2-foundation", "objc2-quartz-core", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -6357,8 +6045,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -6368,27 +6056,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] [[package]] -name = "objc2-encode" -version = "2.0.0-pre.2" +name = "objc2-core-location" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "objc-sys 0.2.0-beta.2", + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", ] -[[package]] -name = "objc2-encode" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" - [[package]] name = "objc2-encode" version = "4.0.3" @@ -6402,10 +6087,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", + "block2", "dispatch", "libc", - "objc2 0.5.2", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", ] [[package]] @@ -6415,8 +6112,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", ] @@ -6427,12 +6124,67 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.6.0", - "block2 0.5.1", - "objc2 0.5.2", + "block2", + "objc2", "objc2-foundation", "objc2-metal", ] +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -6565,16 +6317,6 @@ dependencies = [ "num-traits 0.2.19", ] -[[package]] -name = "ordered-multimap" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" -dependencies = [ - "dlv-list", - "hashbrown 0.12.3", -] - [[package]] name = "ordered-stream" version = "0.2.0" @@ -6855,7 +6597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.1", + "fastrand", "futures-io", ] @@ -6899,22 +6641,6 @@ dependencies = [ "miniz_oxide 0.8.0", ] -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg 1.4.0", - "bitflags 1.3.2", - "cfg-if 1.0.0", - "concurrent-queue", - "libc", - "log", - "pin-project-lite 0.2.14", - "windows-sys 0.48.0", -] - [[package]] name = "polling" version = "3.7.3" @@ -6925,7 +6651,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite 0.2.14", - "rustix 0.38.37", + "rustix", "tracing", "windows-sys 0.59.0", ] @@ -7151,6 +6877,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quick-xml" version = "0.36.2" @@ -7444,12 +7180,6 @@ dependencies = [ "rgb", ] -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -7503,15 +7233,6 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -7628,7 +7349,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots", - "winreg 0.7.0", + "winreg", ] [[package]] @@ -7668,14 +7389,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" dependencies = [ "ashpd", - "block2 0.5.1", + "block2", "js-sys", "log", - "objc2 0.5.2", + "objc2", "objc2-app-kit", "objc2-foundation", "pollster", - "raw-window-handle 0.6.2", + "raw-window-handle", "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", @@ -7891,16 +7612,6 @@ dependencies = [ "toml 0.7.8", ] -[[package]] -name = "rust-ini" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" -dependencies = [ - "cfg-if 1.0.0", - "ordered-multimap", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -7922,20 +7633,6 @@ 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.37" @@ -7945,7 +7642,7 @@ dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys", "windows-sys 0.52.0", ] @@ -8091,14 +7788,14 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.8.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70b31447ca297092c5a9916fc3b955203157b37c19ca8edde4f52e9843e602c7" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" dependencies = [ "ab_glyph", "log", "memmap2", - "smithay-client-toolkit 0.18.1", + "smithay-client-toolkit", "tiny-skia", ] @@ -8539,31 +8236,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smithay-client-toolkit" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" -dependencies = [ - "bitflags 2.6.0", - "calloop 0.12.4", - "calloop-wayland-source 0.2.0", - "cursor-icon", - "libc", - "log", - "memmap2", - "rustix 0.38.37", - "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" @@ -8571,20 +8243,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.6.0", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", "memmap2", - "rustix 0.38.37", + "rustix", "thiserror", "wayland-backend", "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.4", - "wayland-protocols-wlr 0.3.4", + "wayland-protocols", + "wayland-protocols-wlr", "wayland-scanner", "xkeysym", ] @@ -8596,7 +8268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" dependencies = [ "libc", - "smithay-client-toolkit 0.19.2", + "smithay-client-toolkit", "wayland-backend", ] @@ -8620,16 +8292,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi 0.3.9", -] - [[package]] name = "socket2" version = "0.5.7" @@ -8921,9 +8583,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if 1.0.0", - "fastrand 2.1.1", + "fastrand", "once_cell", - "rustix 0.38.37", + "rustix", "windows-sys 0.59.0", ] @@ -9690,7 +9352,7 @@ dependencies = [ "derive_more", "digest 0.10.7", "educe", - "event-listener 5.3.1", + "event-listener", "fs-mistrust", "fslock", "futures 0.3.31", @@ -10521,7 +10183,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset 0.9.1", + "memoffset", "tempfile", "winapi 0.3.9", ] @@ -10826,12 +10488,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -[[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" @@ -10949,7 +10605,7 @@ checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.37", + "rustix", "scoped-tls", "smallvec", "wayland-sys", @@ -10962,7 +10618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" dependencies = [ "bitflags 2.6.0", - "rustix 0.38.37", + "rustix", "wayland-backend", "wayland-scanner", ] @@ -10984,23 +10640,11 @@ version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a94697e66e76c85923b0d28a0c251e8f0666f58fc47d316c0f4da6da75d37cb" dependencies = [ - "rustix 0.38.37", + "rustix", "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.4" @@ -11015,27 +10659,14 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +checksum = "8a0a41a6875e585172495f7a96dfa42ca7e0213868f4f15c313f7c33221a7eff" dependencies = [ "bitflags 2.6.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.2.0" -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-protocols", "wayland-scanner", ] @@ -11048,7 +10679,7 @@ dependencies = [ "bitflags 2.6.0", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.4", + "wayland-protocols", "wayland-scanner", ] @@ -11059,7 +10690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" dependencies = [ "proc-macro2 1.0.87", - "quick-xml", + "quick-xml 0.36.2", "quote 1.0.37", ] @@ -11093,9 +10724,9 @@ dependencies = [ [[package]] name = "web-time" -version = "0.2.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -11107,13 +10738,13 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" dependencies = [ - "block2 0.5.1", + "block2", "core-foundation 0.10.0", "home", "jni", "log", "ndk-context", - "objc2 0.5.2", + "objc2", "objc2-foundation", "url", "web-sys", @@ -11156,12 +10787,11 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "0.20.1" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e37c7b9921b75dfd26dd973fdcbce36f13dfa6e2dc82aece584e0ed48c355c" +checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" dependencies = [ "arrayvec 0.7.6", - "cfg-if 1.0.0", "cfg_aliases 0.1.1", "document-features", "js-sys", @@ -11169,7 +10799,7 @@ dependencies = [ "naga", "parking_lot 0.12.3", "profiling", - "raw-window-handle 0.6.2", + "raw-window-handle", "smallvec", "static_assertions", "wasm-bindgen", @@ -11182,15 +10812,14 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.21.1" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" +checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" dependencies = [ "arrayvec 0.7.6", - "bit-vec", + "bit-vec 0.7.0", "bitflags 2.6.0", "cfg_aliases 0.1.1", - "codespan-reporting", "document-features", "indexmap 2.6.0", "log", @@ -11198,20 +10827,19 @@ dependencies = [ "once_cell", "parking_lot 0.12.3", "profiling", - "raw-window-handle 0.6.2", + "raw-window-handle", "rustc-hash", "smallvec", "thiserror", - "web-sys", "wgpu-hal", "wgpu-types", ] [[package]] name = "wgpu-hal" -version = "0.21.1" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" dependencies = [ "android_system_properties", "arrayvec 0.7.6", @@ -11222,7 +10850,7 @@ dependencies = [ "cfg_aliases 0.1.1", "core-graphics-types", "d3d12", - "glow", + "glow 0.13.1", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", @@ -11231,9 +10859,9 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.5", + "libloading", "log", - "metal 0.28.0", + "metal 0.29.0", "naga", "ndk-sys 0.5.0+25.2.9519653", "objc", @@ -11241,7 +10869,7 @@ dependencies = [ "parking_lot 0.12.3", "profiling", "range-alloc", - "raw-window-handle 0.6.2", + "raw-window-handle", "renderdoc-sys", "rustc-hash", "smallvec", @@ -11254,9 +10882,9 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.20.0" +version = "22.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d" dependencies = [ "bitflags 2.6.0", "js-sys", @@ -11336,24 +10964,23 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-targets 0.48.5", -] - [[package]] name = "windows" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -11367,25 +10994,57 @@ dependencies = [ ] [[package]] -name = "windows-implement" -version = "0.48.0" +name = "windows-core" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2 1.0.87", "quote 1.0.37", - "syn 1.0.109", + "syn 2.0.79", ] [[package]] name = "windows-interface" -version = "0.48.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2 1.0.87", "quote 1.0.37", - "syn 1.0.109", + "syn 2.0.79", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -11604,48 +11263,51 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.29.15" +version = "0.30.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" dependencies = [ - "ahash 0.8.11", - "android-activity 0.5.2", + "ahash", + "android-activity", "atomic-waker", "bitflags 2.6.0", + "block2", "bytemuck", - "calloop 0.12.4", - "cfg_aliases 0.1.1", + "calloop", + "cfg_aliases 0.2.1", + "concurrent-queue", "core-foundation 0.9.4", "core-graphics 0.23.2", "cursor-icon", - "icrate", + "dpi", "js-sys", "libc", - "log", "memmap2", - "ndk 0.8.0", - "ndk-sys 0.5.0+25.2.9519653", - "objc2 0.4.1", - "once_cell", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", "orbclient", "percent-encoding", - "raw-window-handle 0.5.2", - "raw-window-handle 0.6.2", - "redox_syscall 0.3.5", - "rustix 0.38.37", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix", "sctk-adwaita", - "smithay-client-toolkit 0.18.1", + "smithay-client-toolkit", "smol_str", + "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", + "wayland-protocols", "wayland-protocols-plasma", "web-sys", "web-time", - "windows-sys 0.48.0", + "windows-sys 0.52.0", "x11-dl", "x11rb", "xkbcommon-dl", @@ -11678,15 +11340,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -11726,9 +11379,9 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.5", + "libloading", "once_cell", - "rustix 0.38.37", + "rustix", "x11rb-protocol", ] @@ -11843,70 +11496,29 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "zbus" -version = "3.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" -dependencies = [ - "async-broadcast 0.5.1", - "async-executor", - "async-fs 1.6.0", - "async-io 1.13.0", - "async-lock 2.8.0", - "async-process 1.8.1", - "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 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "winapi 0.3.9", - "xdg-home", - "zbus_macros 3.15.2", - "zbus_names 2.6.1", - "zvariant 3.15.2", -] - [[package]] name = "zbus" version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" dependencies = [ - "async-broadcast 0.7.1", + "async-broadcast", "async-executor", - "async-fs 2.1.2", - "async-io 2.3.4", - "async-lock 3.4.0", - "async-process 2.3.0", + "async-fs", + "async-io", + "async-lock", + "async-process", "async-recursion", "async-task", "async-trait", "blocking", "enumflags2", - "event-listener 5.3.1", + "event-listener", "futures-core", "futures-sink", "futures-util", "hex", - "nix 0.29.0", + "nix", "ordered-stream", "rand 0.8.5", "serde", @@ -11917,23 +11529,33 @@ dependencies = [ "uds_windows", "windows-sys 0.52.0", "xdg-home", - "zbus_macros 4.4.0", - "zbus_names 3.0.0", - "zvariant 4.2.0", + "zbus_macros", + "zbus_names", + "zvariant", ] [[package]] -name = "zbus_macros" -version = "3.15.2" +name = "zbus-lockstep" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" dependencies = [ - "proc-macro-crate 1.3.1", "proc-macro2 1.0.87", "quote 1.0.37", - "regex", - "syn 1.0.109", - "zvariant_utils 1.0.1", + "syn 2.0.79", + "zbus-lockstep", + "zbus_xml", + "zvariant", ] [[package]] @@ -11946,18 +11568,7 @@ dependencies = [ "proc-macro2 1.0.87", "quote 1.0.37", "syn 2.0.79", - "zvariant_utils 2.1.0", -] - -[[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 3.15.2", + "zvariant_utils", ] [[package]] @@ -11968,7 +11579,20 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant 4.2.0", + "zvariant", +] + +[[package]] +name = "zbus_xml" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" +dependencies = [ + "quick-xml 0.30.0", + "serde", + "static_assertions", + "zbus_names", + "zvariant", ] [[package]] @@ -12075,20 +11699,6 @@ dependencies = [ "zune-core", ] -[[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 3.15.2", -] - [[package]] name = "zvariant" version = "4.2.0" @@ -12100,20 +11710,7 @@ dependencies = [ "serde", "static_assertions", "url", - "zvariant_derive 4.2.0", -] - -[[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 1.0.87", - "quote 1.0.37", - "syn 1.0.109", - "zvariant_utils 1.0.1", + "zvariant_derive", ] [[package]] @@ -12126,18 +11723,7 @@ dependencies = [ "proc-macro2 1.0.87", "quote 1.0.37", "syn 2.0.79", - "zvariant_utils 2.1.0", -] - -[[package]] -name = "zvariant_utils" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" -dependencies = [ - "proc-macro2 1.0.87", - "quote 1.0.37", - "syn 1.0.109", + "zvariant_utils", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e378fd8..57e9ad7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,8 +46,8 @@ grin_wallet_util = "5.3.3" grin_wallet_controller = "5.3.3" ## ui -egui = { version = "0.28.1", default-features = false } -egui_extras = { version = "0.28.1", features = ["image", "svg"] } +egui = { version = "0.29.1", default-features = false } +egui_extras = { version = "0.29.1", features = ["image", "svg"] } rust-i18n = "2.3.1" ## other @@ -112,21 +112,23 @@ tls-api-openssl = "0.9.0" [target.'cfg(not(target_os = "android"))'.dependencies] env_logger = "0.11.3" -winit = { version = "0.29.15" } -eframe = { version = "0.28.1", features = ["wgpu", "glow"] } +winit = { version = "0.30.5" } +eframe = { version = "0.29.1", features = ["wgpu", "glow"] } arboard = "3.2.0" rfd = "0.15.0" -dark-light = "1.1.1" interprocess = { version = "2.2.1", features = ["tokio"] } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.14.1" jni = "0.21.1" +wgpu = "22.1.0" android-activity = { version = "0.6.0", features = ["game-activity"] } -wgpu = "0.20.1" -winit = { version = "0.29.15", features = ["android-game-activity"] } -eframe = { version = "0.28.1", features = ["wgpu", "android-game-activity"] } +winit = { version = "0.30.5", features = ["android-game-activity"] } +eframe = { version = "0.29.1", features = ["wgpu", "android-game-activity"] } -#[patch.crates-io] +[patch.crates-io] +egui_extras = { git = "https://github.com/emilk/egui", rev = "23728e145ec52bd1193f6f0123973763de4dbb3d" } +egui = { git = "https://github.com/emilk/egui", rev = "23728e145ec52bd1193f6f0123973763de4dbb3d" } +eframe = { git = "https://github.com/emilk/egui", rev = "23728e145ec52bd1193f6f0123973763de4dbb3d" } ### patch grin store -#grin_store = { path = "../grin-store" } \ No newline at end of file +#grin_store = { path = "../grin-store" } diff --git a/src/gui/app.rs b/src/gui/app.rs index ea4ade2..8ff042f 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -14,7 +14,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use lazy_static::lazy_static; -use egui::{Align, Context, CursorIcon, Layout, Modifiers, Rect, ResizeDirection, Rounding, Stroke, ViewportCommand}; +use egui::{Align, Context, CursorIcon, Layout, Modifiers, Rect, ResizeDirection, Rounding, Stroke, UiBuilder, ViewportCommand}; use egui::epaint::{RectShape}; use egui::os::OperatingSystem; @@ -22,7 +22,7 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{ARROWS_IN, ARROWS_OUT, CARET_DOWN, MOON, SUN, X}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Content, TitlePanel, View}; +use crate::gui::views::{Content, Modal, TitlePanel, View}; use crate::wallet::ExternalConnection; lazy_static! { @@ -32,17 +32,14 @@ lazy_static! { /// Implements ui entry point and contains platform-specific callbacks. pub struct App { - /// Platform specific callbacks handler. + /// Handles platform-specific functionality. pub platform: Platform, - /// Main content. content: Content, - /// Last window resize direction. resize_direction: Option, - /// Flag to check if it's first draw. - first_draw: bool, + first_draw: bool } impl App { @@ -55,24 +52,29 @@ impl App { } } + /// Called of first content draw. + fn on_first_draw(&mut self, ctx: &Context) { + // Set platform context. + if View::is_desktop() { + self.platform.set_context(ctx); + } + // Check connections availability. + ExternalConnection::check(None, ctx); + // Setup visuals. + crate::setup_visuals(ctx); + } + /// Draw application content. pub fn ui(&mut self, ctx: &Context) { if self.first_draw { - // Set platform context. - if View::is_desktop() { - self.platform.set_context(ctx); - } - - // Check external connections availability. - ExternalConnection::check(None, ctx); - + self.on_first_draw(ctx); self.first_draw = false; } // Handle Esc keyboard key event and platform Back button key event. let back_pressed = BACK_BUTTON_PRESSED.load(Ordering::Relaxed); if back_pressed || ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape)) { - self.content.on_back(); + self.content.on_back(&self.platform); if back_pressed { BACK_BUTTON_PRESSED.store(false, Ordering::Relaxed); } @@ -97,21 +99,26 @@ impl App { } } - // Show main content with custom frame on desktop. + // Show main content. egui::CentralPanel::default() .frame(egui::Frame { ..Default::default() }) .show(ctx, |ui| { - let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; - if View::is_desktop() && !is_mac_os { - self.desktop_window_ui(ui); - } else { - if is_mac_os { - self.window_title_ui(ui); + if View::is_desktop() { + let is_fullscreen = ui.ctx().input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + if OperatingSystem::from_target_os() != OperatingSystem::Mac { + self.desktop_window_ui(ui, is_fullscreen); + } else { + self.window_title_ui(ui, is_fullscreen); ui.add_space(-1.0); + Self::title_panel_bg(ui, is_fullscreen); + self.content.ui(ui, &self.platform); } - self.content.ui(ui, &self.platform); + } else { + self.mobile_window_ui(ui); } // Provide incoming data to wallets. @@ -129,57 +136,29 @@ impl App { } } - /// Draw custom resizeable window content. - fn desktop_window_ui(&mut self, ui: &mut egui::Ui) { - let is_fullscreen = ui.ctx().input(|i| { - i.viewport().fullscreen.unwrap_or(false) - }); + /// Draw mobile platform window content. + fn mobile_window_ui(&mut self, ui: &mut egui::Ui) { + Self::title_panel_bg(ui, false); + self.content.ui(ui, &self.platform); + } - let title_stroke_rect = { + /// Draw desktop platform window content. + fn desktop_window_ui(&mut self, ui: &mut egui::Ui, is_fullscreen: bool) { + Self::title_panel_bg(ui, is_fullscreen); + + let content_bg_rect = { let mut rect = ui.max_rect(); if !is_fullscreen { rect = rect.shrink(Content::WINDOW_FRAME_MARGIN); } - rect.max.y = if !is_fullscreen { - Content::WINDOW_FRAME_MARGIN - } else { - 0.0 - } + Content::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; - rect - }; - let title_stroke = RectShape { - rect: title_stroke_rect, - rounding: Rounding { - nw: 8.0, - ne: 8.0, - sw: 0.0, - se: 0.0, - }, - fill: Colors::yellow(), - stroke: Stroke { - width: 1.0, - color: egui::Color32::from_gray(200) - }, - blur_width: 0.0, - fill_texture_id: Default::default(), - uv: Rect::ZERO - }; - // Draw title stroke. - ui.painter().add(title_stroke); - - let content_stroke_rect = { - let mut rect = ui.max_rect(); - if !is_fullscreen { - rect = rect.shrink(Content::WINDOW_FRAME_MARGIN); - } - let top = Content::WINDOW_TITLE_HEIGHT + TitlePanel::DEFAULT_HEIGHT + 0.5; + let top = Content::WINDOW_TITLE_HEIGHT + TitlePanel::HEIGHT + 0.5; rect.min += egui::vec2(0.0, top); rect }; - let content_stroke = RectShape { - rect: content_stroke_rect, + let content_bg = RectShape { + rect: content_bg_rect, rounding: Rounding::ZERO, - fill: Colors::fill(), + fill: Colors::fill_lite(), stroke: Stroke { width: 1.0, color: Colors::stroke() @@ -188,17 +167,28 @@ impl App { fill_texture_id: Default::default(), uv: Rect::ZERO }; - // Draw content stroke. - ui.painter().add(content_stroke); + // Draw content background. + ui.painter().add(content_bg); - // Draw window content. let mut content_rect = ui.max_rect(); if !is_fullscreen { content_rect = content_rect.shrink(Content::WINDOW_FRAME_MARGIN); } - ui.allocate_ui_at_rect(content_rect, |ui| { - self.window_title_ui(ui); - self.window_content(ui); + // Draw window content. + ui.allocate_new_ui(UiBuilder::new().max_rect(content_rect), |ui| { + // Draw window title. + self.window_title_ui(ui, is_fullscreen); + + let content_rect = { + let mut rect = ui.max_rect(); + rect.min.y += Content::WINDOW_TITLE_HEIGHT; + rect + }; + // Draw main content. + let mut content_ui = ui.new_child(UiBuilder::new() + .max_rect(content_rect) + .layout(*ui.layout())); + self.content.ui(&mut content_ui, &self.platform); }); // Setup resize areas. @@ -214,20 +204,26 @@ impl App { } } - /// Draw window content for desktop. - fn window_content(&mut self, ui: &mut egui::Ui) { - let content_rect = { + /// Draw title panel background. + fn title_panel_bg(ui: &mut egui::Ui, is_fullscreen: bool) { + let title_rect = { let mut rect = ui.max_rect(); - rect.min.y += Content::WINDOW_TITLE_HEIGHT; + if View::is_desktop() { + let is_mac = OperatingSystem::from_target_os() == OperatingSystem::Mac; + if !is_mac && !is_fullscreen { + rect = rect.shrink(Content::WINDOW_FRAME_MARGIN) + } + rect.min.y += Content::WINDOW_TITLE_HEIGHT; + } + rect.max.y = rect.min.y + View::get_top_inset() + TitlePanel::HEIGHT; rect }; - // Draw main content. - let mut content_ui = ui.child_ui(content_rect, *ui.layout(), None); - self.content.ui(&mut content_ui, &self.platform); + let title_bg = RectShape::filled(title_rect, Rounding::ZERO, Colors::yellow()); + ui.painter().add(title_bg); } /// Draw custom window title content. - fn window_title_ui(&self, ui: &mut egui::Ui) { + fn window_title_ui(&self, ui: &mut egui::Ui, is_fullscreen: bool) { let content_rect = ui.max_rect(); let title_rect = { @@ -236,35 +232,26 @@ impl App { rect }; - let is_fullscreen = ui.ctx().input(|i| { - i.viewport().fullscreen.unwrap_or(false) - }); - - let window_title_bg = RectShape { - rect: title_rect, - rounding: if is_fullscreen { - Rounding::ZERO - } else { - Rounding { - nw: 8.0, - ne: 8.0, - sw: 0.0, - se: 0.0, - } - }, - fill: Colors::yellow_dark(), - stroke: Stroke::NONE, - blur_width: 0.0, - fill_texture_id: Default::default(), - uv: Rect::ZERO - }; + let is_mac = OperatingSystem::from_target_os() == OperatingSystem::Mac; + let window_title_bg = RectShape::filled(title_rect, if is_fullscreen || is_mac { + Rounding::ZERO + } else { + Rounding { + nw: 8.0, + ne: 8.0, + sw: 0.0, + se: 0.0, + } + }, Colors::yellow_dark()); // Draw title background. ui.painter().add(window_title_bg); let painter = ui.painter(); let interact_rect = { - let mut rect = title_rect; + let mut rect = title_rect.clone(); + rect.max.x -= 128.0; + rect.min.x += 85.0; if !is_fullscreen { rect.min.y += Content::WINDOW_FRAME_MARGIN; } @@ -273,8 +260,12 @@ impl App { let title_resp = ui.interact( interact_rect, egui::Id::new("window_title"), - egui::Sense::click_and_drag(), + egui::Sense::drag(), ); + // Interact with the window title (drag to move window): + if !is_fullscreen && title_resp.drag_started_by(egui::PointerButton::Primary) { + ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag); + } // Paint the title. let dual_wallets_panel = @@ -283,7 +274,7 @@ impl App { let wallet_panel_opened = self.content.wallets.showing_wallet(); let show_app_name = if dual_wallets_panel { wallet_panel_opened && !AppConfig::show_wallets_at_dual_panel() - } else if Content::is_dual_panel_mode(ui) { + } else if Content::is_dual_panel_mode(ui.ctx()) { wallet_panel_opened } else { Content::is_network_panel_open() || wallet_panel_opened @@ -302,20 +293,13 @@ impl App { Colors::title(true), ); - // Interact with the window title (drag to move window): - if !is_fullscreen && title_resp.double_clicked() { - ui.ctx().send_viewport_cmd(ViewportCommand::Fullscreen(!is_fullscreen)); - } - - if !is_fullscreen && title_resp.drag_started_by(egui::PointerButton::Primary) { - ui.ctx().send_viewport_cmd(ViewportCommand::StartDrag); - } - - ui.allocate_ui_at_rect(title_rect, |ui| { + ui.allocate_new_ui(UiBuilder::new().max_rect(title_rect), |ui| { ui.with_layout(Layout::right_to_left(Align::Center), |ui| { // Draw button to close window. View::title_button_small(ui, X, |_| { - Content::show_exit_modal(); + if Modal::opened().is_none() || Modal::opened_closeable() { + Content::show_exit_modal(); + } }); // Draw fullscreen button. @@ -427,16 +411,7 @@ impl eframe::App for App { } fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] { - if View::is_desktop() { - let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; - if is_mac_os { - Colors::fill().to_normalized_gamma_f32() - } else { - egui::Rgba::TRANSPARENT.to_array() - } - } else { - Colors::fill().to_normalized_gamma_f32() - } + Colors::fill_lite().to_normalized_gamma_f32() } } diff --git a/src/gui/colors.rs b/src/gui/colors.rs index 0799e02..e56d3c9 100644 --- a/src/gui/colors.rs +++ b/src/gui/colors.rs @@ -46,6 +46,9 @@ const FILL_DARK: Color32 = Color32::from_gray(24); const FILL_DEEP: Color32 = Color32::from_gray(238); const FILL_DEEP_DARK: Color32 = Color32::from_gray(18); +const FILL_LITE: Color32 = Color32::from_gray(249); +const FILL_LITE_DARK: Color32 = Color32::from_gray(16); + const TEXT: Color32 = Color32::from_gray(80); const TEXT_DARK: Color32 = Color32::from_gray(185); @@ -58,9 +61,6 @@ const TEXT_BUTTON_DARK: Color32 = Color32::from_gray(195); const TITLE: Color32 = Color32::from_gray(60); const TITLE_DARK: Color32 = Color32::from_gray(205); -const BUTTON: Color32 = Color32::from_gray(249); -const BUTTON_DARK: Color32 = Color32::from_gray(16); - const GRAY: Color32 = Color32::from_gray(120); const GRAY_DARK: Color32 = Color32::from_gray(145); @@ -167,6 +167,14 @@ impl Colors { } } + pub fn fill_lite() -> Color32 { + if use_dark() { + FILL_LITE_DARK + } else { + FILL_LITE + } + } + pub fn checkbox() -> Color32 { if use_dark() { CHECKBOX_DARK @@ -199,14 +207,6 @@ impl Colors { } } - pub fn button() -> Color32 { - if use_dark() { - BUTTON_DARK - } else { - BUTTON - } - } - pub fn gray() -> Color32 { if use_dark() { GRAY_DARK diff --git a/src/gui/views/camera.rs b/src/gui/views/camera.rs index e760975..e4ed35a 100644 --- a/src/gui/views/camera.rs +++ b/src/gui/views/camera.rs @@ -15,11 +15,9 @@ use std::sync::Arc; use parking_lot::RwLock; use std::thread; -use eframe::emath::Align; use egui::load::SizedTexture; -use egui::{Layout, Pos2, Rect, RichText, TextureOptions, Widget}; -use image::{DynamicImage, EncodableLayout, ImageFormat}; - +use egui::{Pos2, Rect, RichText, TextureOptions, UiBuilder, Widget}; +use image::{DynamicImage, EncodableLayout}; use grin_util::ZeroingString; use grin_wallet_libwallet::SlatepackAddress; use grin_keychain::mnemonic::WORDS; @@ -36,16 +34,15 @@ use crate::wallet::WalletUtils; pub struct CameraContent { /// QR code scanning progress and result. qr_scan_state: Arc>, - /// Uniform Resources URIs collected from QR code scanning. - ur_data: Arc, usize)>>>, + ur_data: Arc, usize)>>> } impl Default for CameraContent { fn default() -> Self { Self { qr_scan_state: Arc::new(RwLock::new(QrScanState::default())), - ur_data: Arc::new(RwLock::new(None)), + ur_data: Arc::new(RwLock::new(None)) } } } @@ -53,97 +50,105 @@ impl Default for CameraContent { impl CameraContent { /// Draw camera content. pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw last image from camera or loader. + ui.ctx().request_repaint(); if let Some(img_data) = cb.camera_image() { - // Load image to draw. - if let Ok(mut img) = - image::load_from_memory_with_format(&*img_data.0, ImageFormat::Jpeg) { + if let Ok(img) = + image::load_from_memory(&*img_data.0) { // Process image to find QR code. self.scan_qr(&img); - // Setup image rotation. - img = match img_data.1 { - 90 => img.rotate90(), - 180 => img.rotate180(), - 270 => img.rotate270(), - _ => img - }; - img = img.fliph(); - // Convert to ColorImage to add at content. - let color_img = match &img { - DynamicImage::ImageRgb8(image) => { - egui::ColorImage::from_rgb( - [image.width() as usize, image.height() as usize], - image.as_bytes(), - ) - }, - other => { - let image = other.to_rgba8(); - egui::ColorImage::from_rgba_unmultiplied( - [image.width() as usize, image.height() as usize], - image.as_bytes(), - ) - }, - }; - // Create image texture. - let texture = ui.ctx().load_texture("camera_image", - color_img.clone(), - TextureOptions::default()); - let img_size = egui::emath::vec2(color_img.width() as f32, - color_img.height() as f32); - let sized_img = SizedTexture::new(texture.id(), img_size); - // Add image to content. - ui.vertical_centered(|ui| { - egui::Image::from_texture(sized_img) - // Setup to crop image at square. - .uv(Rect::from([ - Pos2::new(1.0 - (img_size.y / img_size.x), 0.0), - Pos2::new(1.0, 1.0) - ])) - .max_height(ui.available_width()) - .maintain_aspect_ratio(false) - .shrink_to_fit() - .ui(ui); - }); + + // Draw image. + let img_rect = self.image_ui(ui, img, img_data.1); // Show UR scan progress. - let show_ur_progress = { - self.ur_data.clone().read().is_some() - }; - let ur_progress = self.ur_progress(); - if show_ur_progress && ur_progress != 0 { - ui.add_space(-52.0); - ui.vertical_centered(|ui| { - ui.label(RichText::new(format!("{}%", ur_progress)) - .size(16.0) - .color(Colors::yellow())); - }); - } + self.ur_progress_ui(ui); // Show button to switch cameras. if cb.can_switch_camera() { - ui.add_space(-52.0); - let mut size = ui.available_size(); - size.y = 48.0; - ui.allocate_ui_with_layout(size, Layout::right_to_left(Align::Max), |ui| { - ui.add_space(4.0); - View::button(ui, CAMERA_ROTATE.to_string(), Colors::white_or_black(false), || { + let r = { + let mut r = img_rect.clone(); + r.min.y = r.max.y - 52.0; + r.min.x = r.max.x - 52.0; + r + }; + ui.allocate_new_ui(UiBuilder::new().max_rect(r), |ui| { + let rotate_img = CAMERA_ROTATE.to_string(); + View::button(ui, rotate_img, Colors::white_or_black(false), || { cb.switch_camera(); }); }); } } else { - self.loading_content_ui(ui); + self.loading_ui(ui); } } else { - self.loading_content_ui(ui); + self.loading_ui(ui); } + } - // Request redraw. - ui.ctx().request_repaint(); + /// Draw camera image. + fn image_ui(&mut self, ui: &mut egui::Ui, mut img: DynamicImage, rotation: u32) -> Rect { + // Setup image rotation. + img = match rotation { + 90 => img.rotate90(), + 180 => img.rotate180(), + 270 => img.rotate270(), + _ => img + }; + if View::is_desktop() { + img = img.fliph(); + } + // Convert to ColorImage. + let color_img = match &img { + DynamicImage::ImageRgb8(image) => { + egui::ColorImage::from_rgb( + [image.width() as usize, image.height() as usize], + image.as_bytes(), + ) + }, + other => { + let image = other.to_rgba8(); + egui::ColorImage::from_rgba_unmultiplied( + [image.width() as usize, image.height() as usize], + image.as_bytes(), + ) + }, + }; + // Create image texture. + let texture = ui.ctx().load_texture("camera_image", + color_img.clone(), + TextureOptions::default()); + let img_size = egui::emath::vec2(color_img.width() as f32, + color_img.height() as f32); + let sized_img = SizedTexture::new(texture.id(), img_size); + egui::Image::from_texture(sized_img) + // Setup to crop image at square. + .uv(Rect::from([ + Pos2::new(1.0 - (img_size.y / img_size.x), 0.0), + Pos2::new(1.0, 1.0) + ])) + .max_height(ui.available_width()) + .maintain_aspect_ratio(false) + .shrink_to_fit() + .ui(ui).rect + } + + /// Draw animated QR code scanning progress. + fn ur_progress_ui(&self, ui: &mut egui::Ui) { + let show_ur_progress = { + self.ur_data.as_ref().read().is_some() + }; + if show_ur_progress { + ui.centered_and_justified(|ui| { + ui.label(RichText::new(format!("{}%", self.ur_progress())) + .size(17.0) + .color(Colors::green())); + }); + } } /// Draw camera loading progress content. - fn loading_content_ui(&self, ui: &mut egui::Ui) { + fn loading_ui(&self, ui: &mut egui::Ui) { let space = (ui.available_width() - View::BIG_SPINNER_SIZE) / 2.0; ui.vertical_centered(|ui| { ui.add_space(space); diff --git a/src/gui/views/content.rs b/src/gui/views/content.rs index 55ffbba..b20b7f6 100644 --- a/src/gui/views/content.rs +++ b/src/gui/views/content.rs @@ -52,6 +52,11 @@ pub struct Content { allowed_modal_ids: Vec<&'static str> } +/// Identifier for integrated node warning [`Modal`] on Android. +const ANDROID_INTEGRATED_NODE_WARNING_MODAL: &'static str = "android_node_warning_modal"; +/// Identifier for crash report [`Modal`]. +const CRASH_REPORT_MODAL: &'static str = "crash_report_modal"; + impl Default for Content { fn default() -> Self { // Exit from eframe only for non-mobile platforms. @@ -66,8 +71,8 @@ impl Default for Content { allowed_modal_ids: vec![ Self::EXIT_CONFIRMATION_MODAL, Self::SETTINGS_MODAL, - Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL, - Self::CRASH_REPORT_MODAL + ANDROID_INTEGRATED_NODE_WARNING_MODAL, + CRASH_REPORT_MODAL ], } } @@ -85,8 +90,8 @@ impl ModalContainer for Content { match modal.id { Self::EXIT_CONFIRMATION_MODAL => self.exit_modal_content(ui, modal, cb), Self::SETTINGS_MODAL => self.settings_modal_ui(ui, modal), - Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui, modal), - Self::CRASH_REPORT_MODAL => self.crash_report_modal_ui(ui, modal, cb), + ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui, modal), + CRASH_REPORT_MODAL => self.crash_report_modal_ui(ui, modal, cb), _ => {} } } @@ -97,10 +102,6 @@ impl Content { pub const EXIT_CONFIRMATION_MODAL: &'static str = "exit_confirmation_modal"; /// Identifier for wallet opening [`Modal`]. pub const SETTINGS_MODAL: &'static str = "settings_modal"; - /// Identifier for integrated node warning [`Modal`] on Android. - const ANDROID_INTEGRATED_NODE_WARNING_MODAL: &'static str = "android_node_warning_modal"; - /// Identifier for crash report [`Modal`]. - const CRASH_REPORT_MODAL: &'static str = "crash_report_modal"; /// Default width of side panel at application UI. pub const SIDE_PANEL_WIDTH: f32 = 400.0; @@ -110,11 +111,10 @@ impl Content { pub const WINDOW_FRAME_MARGIN: f32 = 6.0; pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. self.current_modal_ui(ui, cb); - let dual_panel = Self::is_dual_panel_mode(ui); - let (is_panel_open, panel_width) = Self::network_panel_state_width(ui, dual_panel); + let dual_panel = Self::is_dual_panel_mode(ui.ctx()); + let (is_panel_open, panel_width) = network_panel_state_width(ui.ctx(), dual_panel); // Show network content. egui::SidePanel::left("network_panel") @@ -137,48 +137,26 @@ impl Content { }); if self.first_draw { - // Show crash report if needed. + // Show crash report or integrated node Android warning. if Settings::crash_report_path().exists() { - Modal::new(Self::CRASH_REPORT_MODAL) + Modal::new(CRASH_REPORT_MODAL) .closeable(false) .position(ModalPosition::Center) .title(t!("crash_report")) .show(); - } else { - // Show integrated node warning on Android if needed. - if OperatingSystem::from_target_os() == OperatingSystem::Android && + } else if OperatingSystem::from_target_os() == OperatingSystem::Android && AppConfig::android_integrated_node_warning_needed() { - Modal::new(Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL) + Modal::new(ANDROID_INTEGRATED_NODE_WARNING_MODAL) .title(t!("network.node")) .show(); - } } self.first_draw = false; } } - /// Get [`NetworkContent`] panel state and width. - fn network_panel_state_width(ui: &mut egui::Ui, dual_panel: bool) -> (bool, f32) { - let is_panel_open = dual_panel || Self::is_network_panel_open(); - let panel_width = if dual_panel { - Self::SIDE_PANEL_WIDTH + View::get_left_inset() - } else { - let is_fullscreen = ui.ctx().input(|i| { - i.viewport().fullscreen.unwrap_or(false) - }); - View::window_size(ui).0 - if View::is_desktop() && !is_fullscreen && - OperatingSystem::from_target_os() != OperatingSystem::Mac { - Self::WINDOW_FRAME_MARGIN * 2.0 - } else { - 0.0 - } - }; - (is_panel_open, panel_width) - } - /// Check if ui can show [`NetworkContent`] and [`WalletsContent`] at same time. - pub fn is_dual_panel_mode(ui: &egui::Ui) -> bool { - let (w, h) = View::window_size(ui); + pub fn is_dual_panel_mode(ctx: &egui::Context) -> bool { + let (w, h) = View::window_size(ctx); // Screen is wide if width is greater than height or just 20% smaller. let is_wide_screen = w > h || w + (w * 0.2) >= h; // Dual panel mode is available when window is wide and its width is at least 2 times @@ -260,9 +238,9 @@ impl Content { } /// Handle Back key event. - pub fn on_back(&mut self) { + pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) { if Modal::on_back() { - if self.wallets.on_back() { + if self.wallets.on_back(cb) { Self::show_exit_modal() } } @@ -341,42 +319,40 @@ impl Content { let item_rounding = View::item_rounding(index, len, false); ui.painter().rect(bg_rect, item_rounding, Colors::fill(), View::item_stroke()); - ui.vertical(|ui| { - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Draw button to select language. - let is_current = if let Some(lang) = AppConfig::locale() { - lang == locale - } else { - rust_i18n::locale() == locale - }; - if !is_current { - View::item_button(ui, View::item_rounding(index, len, true), CHECK, None, || { - rust_i18n::set_locale(locale); - AppConfig::save_locale(locale); - modal.close(); - }); - } else { - ui.add_space(14.0); - ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green())); - ui.add_space(14.0); - } + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to select language. + let is_current = if let Some(lang) = AppConfig::locale() { + lang == locale + } else { + rust_i18n::locale() == locale + }; + if !is_current { + View::item_button(ui, View::item_rounding(index, len, true), CHECK, None, || { + rust_i18n::set_locale(locale); + AppConfig::save_locale(locale); + modal.close(); + }); + } else { + ui.add_space(14.0); + ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green())); + ui.add_space(14.0); + } - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(12.0); + ui.vertical(|ui| { + // Draw language name. ui.add_space(12.0); - ui.vertical(|ui| { - // Draw language name. - ui.add_space(12.0); - let color = if is_current { - Colors::title(false) - } else { - Colors::gray() - }; - ui.label(RichText::new(t!("lang_name", locale = locale)) - .size(17.0) - .color(color)); - ui.add_space(3.0); - }); + let color = if is_current { + Colors::title(false) + } else { + Colors::gray() + }; + ui.label(RichText::new(t!("lang_name", locale = locale)) + .size(17.0) + .color(color)); + ui.add_space(3.0); }); }); }); @@ -433,4 +409,23 @@ impl Content { }); ui.add_space(6.0); } +} + +/// Get [`NetworkContent`] panel state and width. +fn network_panel_state_width(ctx: &egui::Context, dual_panel: bool) -> (bool, f32) { + let is_panel_open = dual_panel || Content::is_network_panel_open(); + let panel_width = if dual_panel { + Content::SIDE_PANEL_WIDTH + View::get_left_inset() + } else { + let is_fullscreen = ctx.input(|i| { + i.viewport().fullscreen.unwrap_or(false) + }); + View::window_size(ctx).0 - if View::is_desktop() && !is_fullscreen && + OperatingSystem::from_target_os() != OperatingSystem::Mac { + Content::WINDOW_FRAME_MARGIN * 2.0 + } else { + 0.0 + } + }; + (is_panel_open, panel_width) } \ No newline at end of file diff --git a/src/gui/views/file_pick.rs b/src/gui/views/file_pick.rs index 4f99203..7917c93 100644 --- a/src/gui/views/file_pick.rs +++ b/src/gui/views/file_pick.rs @@ -78,8 +78,8 @@ impl FilePickButton { } } else { // Draw button to pick file. - let file_text = format!("{} {}", ARCHIVE_BOX, t!("choose_file")); - View::colored_text_button(ui, file_text, Colors::blue(), Colors::button(), || { + let text = format!("{} {}", ARCHIVE_BOX, t!("choose_file")); + View::colored_text_button(ui, text, Colors::blue(), Colors::white_or_black(false), || { if let Some(path) = cb.pick_file() { self.on_file_pick(path); } diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 7b3359a..db96b9c 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -16,7 +16,7 @@ use lazy_static::lazy_static; use std::sync::Arc; use parking_lot::RwLock; use std::sync::atomic::{AtomicBool, Ordering}; -use egui::{Align2, Rect, RichText, Rounding, Stroke, Vec2}; +use egui::{Align2, RichText, Rounding, Stroke, UiBuilder, Vec2}; use egui::epaint::{RectShape, Shadow}; use egui::os::OperatingSystem; @@ -29,17 +29,17 @@ lazy_static! { static ref MODAL_STATE: Arc> = Arc::new(RwLock::new(ModalState::default())); } -/// Stores data to draw modal [`egui::Window`] at ui. +/// Modal [`egui::Window`] container. #[derive(Clone)] pub struct Modal { /// Identifier for modal. pub(crate) id: &'static str, /// Position on the screen. pub position: ModalPosition, - /// To check if it can be closed. + /// Flag to check if modal can be closed by keys. closeable: Arc, - /// Title text - title: Option + /// Title text. + title: Option, } impl Modal { @@ -54,7 +54,7 @@ impl Modal { id, position: ModalPosition::Center, closeable: Arc::new(AtomicBool::new(true)), - title: None + title: None, } } @@ -110,7 +110,7 @@ impl Modal { } /// Remove [`Modal`] from [`ModalState`] if it's showing and can be closed. - /// Return `false` if Modal existed in [`ModalState`] before call. + /// Return `false` if modal existed in state before call. pub fn on_back() -> bool { let mut w_state = MODAL_STATE.write(); @@ -125,7 +125,7 @@ impl Modal { true } - /// Return id of opened [`Modal`]. + /// Return identifier of opened [`Modal`]. pub fn opened() -> Option<&'static str> { // Check if modal is showing. { @@ -140,6 +140,19 @@ impl Modal { Some(modal.id) } + /// Check if [`Modal`] is opened and can be closed. + pub fn opened_closeable() -> bool { + // Check if modal is showing. + { + if MODAL_STATE.read().modal.is_none() { + return false; + } + } + let r_state = MODAL_STATE.read(); + let modal = r_state.modal.as_ref().unwrap(); + modal.closeable.load(Ordering::Relaxed) + } + /// Set title text for current opened [`Modal`]. pub fn set_title(title: String) { // Save state. @@ -170,19 +183,21 @@ impl Modal { let is_fullscreen = ctx.input(|i| { i.viewport().fullscreen.unwrap_or(false) }); - let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; - let mut rect = ctx.screen_rect(); - if View::is_desktop() && !is_mac_os { - let margin = if !is_fullscreen { - Content::WINDOW_FRAME_MARGIN + // Setup content rect. + let rect = if View::is_desktop() { + if !is_fullscreen && OperatingSystem::from_target_os() != OperatingSystem::Mac { + ctx.screen_rect().shrink(Content::WINDOW_FRAME_MARGIN) } else { - 0.0 - }; - rect = rect.shrink(margin - 0.5); - rect.min += egui::vec2(0.0, Content::WINDOW_TITLE_HEIGHT + 0.5); - rect.max.x += 0.5; - } + let mut r = ctx.screen_rect(); + r.min.y += Content::WINDOW_TITLE_HEIGHT; + r + } + } else { + ctx.screen_rect() + }; + + // Draw modal background. egui::Window::new("modal_bg_window") .title_bar(false) .resizable(false) @@ -201,9 +216,9 @@ impl Modal { let available_width = rect.width() - (side_insets + Self::DEFAULT_MARGIN); let width = f32::min(available_width, Self::DEFAULT_WIDTH); - // Show main content Window at given position. - let (content_align, content_offset) = self.modal_position(is_fullscreen); - let layer_id = egui::Window::new(format!("modal_window_{}", self.id)) + // Show main content window at given position. + let (content_align, content_offset) = self.modal_position(); + let layer_id = egui::Window::new("modal_window") .title_bar(false) .resizable(false) .collapsible(false) @@ -218,37 +233,29 @@ impl Modal { color: egui::Color32::from_black_alpha(32), }, rounding: Rounding::same(8.0), - fill: Colors::fill(), ..Default::default() }) .show(ctx, |ui| { - if self.title.is_some() { - self.title_ui(ui); + if let Some(title) = &self.title { + title_ui(title, ui); } self.content_ui(ui, add_content); }).unwrap().response.layer_id; - // Always show main content Window above background Window. + // Always show main content window above background window. ctx.move_to_top(layer_id); - } /// Get [`egui::Window`] position based on [`ModalPosition`]. - fn modal_position(&self, is_fullscreen: bool) -> (Align2, Vec2) { + fn modal_position(&self) -> (Align2, Vec2) { let align = match self.position { ModalPosition::CenterTop => Align2::CENTER_TOP, ModalPosition::Center => Align2::CENTER_CENTER }; let x_align = View::get_left_inset() - View::get_right_inset(); - - let is_mac_os = OperatingSystem::from_target_os() == OperatingSystem::Mac; - let extra_y = if View::is_desktop() && !is_mac_os { - Content::WINDOW_TITLE_HEIGHT + if !is_fullscreen { - Content::WINDOW_FRAME_MARGIN - } else { - 0.0 - } + let extra_y = if View::is_desktop() { + Content::WINDOW_TITLE_HEIGHT - Self::DEFAULT_MARGIN / 2.0 } else { 0.0 }; @@ -264,80 +271,64 @@ impl Modal { /// Draw provided content. fn content_ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) { let mut rect = ui.available_rect_before_wrap(); - rect.min += egui::emath::vec2(6.0, 0.0); - rect.max -= egui::emath::vec2(6.0, 0.0); // Create background shape. - let rounding = if self.title.is_some() { + let mut bg_shape = RectShape::new(rect, if self.title.is_none() { + Rounding::same(8.0) + } else { Rounding { nw: 0.0, ne: 0.0, sw: 8.0, se: 8.0, } - } else { - Rounding::same(8.0) - }; - let mut bg_shape = RectShape { - rect, - rounding, - fill: Colors::fill(), - stroke: Stroke::NONE, - blur_width: 0.0, - fill_texture_id: Default::default(), - uv: Rect::ZERO - }; + }, Colors::fill(), Stroke::NONE); let bg_idx = ui.painter().add(bg_shape); - // Draw main content. - let mut content_rect = ui.allocate_ui_at_rect(rect, |ui| { + rect.min += egui::emath::vec2(6.0, 0.0); + rect.max -= egui::emath::vec2(6.0, 0.0); + let resp = ui.allocate_new_ui(UiBuilder::new().max_rect(rect), |ui| { (add_content)(ui, self); - }).response.rect; - - // Setup background shape to be painted behind main content. - content_rect.min -= egui::emath::vec2(6.0, 0.0); - content_rect.max += egui::emath::vec2(6.0, 0.0); - bg_shape.rect = content_rect; - ui.painter().set(bg_idx, bg_shape); - } - - /// Draw title content. - fn title_ui(&self, ui: &mut egui::Ui) { - let rect = ui.available_rect_before_wrap(); - - // Create background shape. - let mut bg_shape = RectShape { - rect, - rounding: Rounding { - nw: 8.0, - ne: 8.0, - sw: 0.0, - se: 0.0, - }, - fill: Colors::yellow(), - stroke: Stroke::NONE, - blur_width: 0.0, - fill_texture_id: Default::default(), - uv: Rect::ZERO - }; - let bg_idx = ui.painter().add(bg_shape); - - // Draw title content. - let title_resp = ui.allocate_ui_at_rect(rect, |ui| { - ui.vertical_centered_justified(|ui| { - ui.add_space(Self::DEFAULT_MARGIN + 1.0); - ui.label(RichText::new(self.title.as_ref().unwrap()) - .size(19.0) - .color(Colors::title(true)) - ); - ui.add_space(Self::DEFAULT_MARGIN); - // Draw line below title. - View::horizontal_line(ui, Colors::item_stroke()); - }); }).response; - // Setup background shape to be painted behind title content. - bg_shape.rect = title_resp.rect; + // Setup background size. + let bg_rect = { + let mut r = resp.rect.clone(); + r.min -= egui::emath::vec2(6.0, 0.0); + r.max += egui::emath::vec2(6.0, 0.0); + r + }; + bg_shape.rect = bg_rect; ui.painter().set(bg_idx, bg_shape); } +} + +/// Draw title content. +fn title_ui(title: &String, ui: &mut egui::Ui) { + let rect = ui.available_rect_before_wrap(); + + // Create background shape. + let mut bg_shape = RectShape::new(rect, Rounding { + nw: 8.0, + ne: 8.0, + sw: 0.0, + se: 0.0, + }, Colors::yellow(), Stroke::NONE); + let bg_idx = ui.painter().add(bg_shape); + + // Draw title content. + let resp = ui.vertical_centered(|ui| { + ui.add_space(Modal::DEFAULT_MARGIN + 2.0); + ui.label(RichText::new(title) + .size(19.0) + .color(Colors::title(true)) + ); + ui.add_space(Modal::DEFAULT_MARGIN + 1.0); + // Draw line below title. + View::horizontal_line(ui, Colors::item_stroke()); + }).response; + + // Setup background size. + bg_shape.rect = resp.rect; + ui.painter().set(bg_idx, bg_shape); } \ No newline at end of file diff --git a/src/gui/views/network/connections.rs b/src/gui/views/network/connections.rs index 6d928f7..42a9971 100644 --- a/src/gui/views/network/connections.rs +++ b/src/gui/views/network/connections.rs @@ -202,34 +202,32 @@ impl ConnectionsContent { let item_rounding = View::item_rounding(index, len, false); ui.painter().rect(bg_rect, item_rounding, Colors::fill(), View::item_stroke()); - ui.vertical(|ui| { - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Draw provided buttons. - buttons_ui(ui); + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw provided buttons. + buttons_ui(ui); - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(6.0); - ui.vertical(|ui| { - // Draw connections URL. - ui.add_space(4.0); - let conn_text = format!("{} {}", GLOBE_SIMPLE, conn.url); - View::ellipsize_text(ui, conn_text, 15.0, Colors::title(false)); - ui.add_space(1.0); + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(6.0); + ui.vertical(|ui| { + // Draw connections URL. + ui.add_space(4.0); + let conn_text = format!("{} {}", GLOBE_SIMPLE, conn.url); + View::ellipsize_text(ui, conn_text, 15.0, Colors::title(false)); + ui.add_space(1.0); - // Setup connection status text. - let status_text = if let Some(available) = conn.available { - if available { - format!("{} {}", CHECK_CIRCLE, t!("network.available")) - } else { - format!("{} {}", X_CIRCLE, t!("network.not_available")) - } + // Setup connection status text. + let status_text = if let Some(available) = conn.available { + if available { + format!("{} {}", CHECK_CIRCLE, t!("network.available")) } else { - format!("{} {}", DOTS_THREE_CIRCLE, t!("network.availability_check")) - }; - ui.label(RichText::new(status_text).size(15.0).color(Colors::gray())); - ui.add_space(3.0); - }); + format!("{} {}", X_CIRCLE, t!("network.not_available")) + } + } else { + format!("{} {}", DOTS_THREE_CIRCLE, t!("network.availability_check")) + }; + ui.label(RichText::new(status_text).size(15.0).color(Colors::gray())); + ui.add_space(3.0); }); }); }); diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 791d4ee..952767f 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -21,15 +21,15 @@ use crate::gui::icons::{ARROWS_COUNTER_CLOCKWISE, BRIEFCASE, DATABASE, DOTS_THRE use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Content, TitlePanel, View}; use crate::gui::views::network::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings}; -use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; -use crate::gui::views::types::{TitleContentType, TitleType}; +use crate::gui::views::network::types::{NodeTab, NodeTabType}; +use crate::gui::views::types::{LinePosition, TitleContentType, TitleType}; use crate::node::{Node, NodeConfig, NodeError}; use crate::wallet::ExternalConnection; /// Network content. pub struct NetworkContent { /// Current integrated node tab content. - node_tab_content: Box, + node_tab_content: Box, /// Connections content. connections: ConnectionsContent, } @@ -46,14 +46,14 @@ impl Default for NetworkContent { impl NetworkContent { pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { let show_connections = AppConfig::show_connections_network_panel(); - let dual_panel = Content::is_dual_panel_mode(ui); + let dual_panel = Content::is_dual_panel_mode(ui.ctx()); // Show title panel. - self.title_ui(ui, show_connections); + self.title_ui(ui, dual_panel, show_connections); // Show integrated node tabs content. if !show_connections { - egui::TopBottomPanel::bottom("node_tabs_content") + egui::TopBottomPanel::bottom("node_tabs") .min_height(0.5) .resizable(false) .frame(egui::Frame { @@ -67,15 +67,23 @@ impl NetworkContent { ..Default::default() }) .show_inside(ui, |ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - self.tabs_ui(ui); - }); + let rect = ui.available_rect_before_wrap(); + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.tabs_ui(ui); }); + // Draw content divider line. + let r = { + let mut r = rect.clone(); + r.min.x -= View::get_left_inset() + View::TAB_ITEMS_PADDING; + r.min.y -= View::TAB_ITEMS_PADDING; + r.max.x += View::far_right_inset_margin(ui) + View::TAB_ITEMS_PADDING; + r + }; + View::line(ui, LinePosition::TOP, &r, Colors::stroke()); }); } - // Show current node tab content. + // Show integrated node tab content. egui::SidePanel::right("node_tab_content") .resizable(false) .exact_width(ui.available_width()) @@ -85,8 +93,6 @@ impl NetworkContent { .show_animated_inside(ui, !show_connections, |ui| { egui::CentralPanel::default() .frame(egui::Frame { - fill: Colors::white_or_black(false), - stroke: View::item_stroke(), inner_margin: Margin { left: View::get_left_inset() + 4.0, right: View::far_right_inset_margin(ui) + 4.0, @@ -96,14 +102,42 @@ impl NetworkContent { ..Default::default() }) .show_inside(ui, |ui| { - self.node_tab_content.ui(ui, cb); + let rect = ui.available_rect_before_wrap(); + if self.node_tab_content.get_type() != NodeTabType::Settings { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + let node_err = Node::get_error(); + if let Some(err) = node_err { + node_error_ui(ui, err); + } else if !Node::is_running() { + disabled_node_ui(ui); + } else if Node::get_stats().is_none() || Node::is_restarting() || + Node::is_stopping() { + NetworkContent::loading_ui(ui, None); + } else { + self.node_tab_content.ui(ui, cb); + } + }); + } else { + self.node_tab_content.ui(ui, cb); + } + + // Draw content divider line. + let r = { + let mut r = rect.clone(); + r.min.y -= 3.0; + r.max.x += 4.0; + r.max.y += 4.0; + r + }; + if dual_panel { + View::line(ui, LinePosition::RIGHT, &r, Colors::item_stroke()); + } }); }); // Show connections content. egui::CentralPanel::default() .frame(egui::Frame { - stroke: View::item_stroke(), inner_margin: Margin { left: if show_connections { View::get_left_inset() + 4.0 @@ -116,18 +150,14 @@ impl NetworkContent { 0.0 }, top: 3.0, - bottom: if View::is_desktop() && show_connections { - 6.0 - } else { - 4.0 - }, + bottom: 4.0 + View::get_bottom_inset(), }, - fill: Colors::button(), ..Default::default() }) .show_inside(ui, |ui| { + let rect = ui.available_rect_before_wrap(); ScrollArea::vertical() - .id_source("connections_content") + .id_salt("connections_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { @@ -143,15 +173,26 @@ impl NetworkContent { }); }); }); + // Draw content divider line. + let r = { + let mut r = rect.clone(); + r.min.y -= 3.0; + r.max.x += 4.0; + r.max.y += 4.0 + View::get_bottom_inset(); + r + }; + if show_connections && dual_panel { + View::line(ui, LinePosition::RIGHT, &r, Colors::item_stroke()); + } }); - // Redraw after delay. - if Node::is_running() { + // Redraw after delay if node is running at non-dual-panel mode. + if !dual_panel && Content::is_network_panel_open() && Node::is_running() { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); } } - /// Draw tab buttons in the bottom of the screen. + /// Draw tab buttons at bottom of the screen. fn tabs_ui(&mut self, ui: &mut egui::Ui) { ui.vertical_centered(|ui| { // Setup spacing between tabs. @@ -163,22 +204,22 @@ impl NetworkContent { let current_type = self.node_tab_content.get_type(); ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { - View::tab_button(ui, DATABASE, current_type == NetworkTabType::Node, |_| { + View::tab_button(ui, DATABASE, current_type == NodeTabType::Info, |_| { self.node_tab_content = Box::new(NetworkNode::default()); }); }); columns[1].vertical_centered_justified(|ui| { - View::tab_button(ui, GAUGE, current_type == NetworkTabType::Metrics, |_| { + View::tab_button(ui, GAUGE, current_type == NodeTabType::Metrics, |_| { self.node_tab_content = Box::new(NetworkMetrics::default()); }); }); columns[2].vertical_centered_justified(|ui| { - View::tab_button(ui, FACTORY, current_type == NetworkTabType::Mining, |_| { + View::tab_button(ui, FACTORY, current_type == NodeTabType::Mining, |_| { self.node_tab_content = Box::new(NetworkMining::default()); }); }); columns[3].vertical_centered_justified(|ui| { - View::tab_button(ui, FADERS, current_type == NetworkTabType::Settings, |_| { + View::tab_button(ui, FADERS, current_type == NodeTabType::Settings, |_| { self.node_tab_content = Box::new(NetworkSettings::default()); }); }); @@ -187,15 +228,15 @@ impl NetworkContent { } /// Draw title content. - fn title_ui(&mut self, ui: &mut egui::Ui, show_connections: bool) { + fn title_ui(&mut self, ui: &mut egui::Ui, dual_panel: bool, show_connections: bool) { // Setup values for title panel. - let title_text = self.node_tab_content.get_type().title().to_uppercase(); + let title_text = self.node_tab_content.get_type().title(); let subtitle_text = Node::get_sync_status_text(); let not_syncing = Node::not_syncing(); let title_content = if !show_connections { TitleContentType::WithSubTitle(title_text, subtitle_text, !not_syncing) } else { - TitleContentType::Title(t!("network.connections").to_uppercase()) + TitleContentType::Title(t!("network.connections")) }; // Draw title panel. @@ -209,7 +250,7 @@ impl NetworkContent { }); } }, |ui| { - if !Content::is_dual_panel_mode(ui) { + if !dual_panel { View::title_button_big(ui, BRIEFCASE, |_| { Content::toggle_network_panel(); }); @@ -217,23 +258,6 @@ impl NetworkContent { }, ui); } - /// Content to draw when node is disabled. - pub fn disabled_node_ui(ui: &mut egui::Ui) { - View::center_content(ui, 156.0, |ui| { - let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL); - ui.label(RichText::new(text) - .size(16.0) - .color(Colors::inactive_text()) - ); - ui.add_space(8.0); - View::action_button(ui, format!("{} {}", POWER, t!("network.enable_node")), || { - Node::start(); - }); - ui.add_space(2.0); - Self::autorun_node_ui(ui); - }); - } - /// Content to draw on loading. pub fn loading_ui(ui: &mut egui::Ui, text: Option) { match text { @@ -262,81 +286,98 @@ impl NetworkContent { AppConfig::toggle_node_autostart(); }); } +} - /// Draw integrated node error content. - pub fn node_error_ui(ui: &mut egui::Ui, e: NodeError) { - match e { - NodeError::Storage => { - View::center_content(ui, 156.0, |ui| { - ui.label(RichText::new(t!("network_node.error_clean")) - .size(16.0) - .color(Colors::red()) - ); - ui.add_space(8.0); - let btn_txt = format!("{} {}", - ARROWS_COUNTER_CLOCKWISE, - t!("network_node.resync")); - View::action_button(ui, btn_txt, || { - Node::clean_up_data(); - Node::start(); - }); - ui.add_space(2.0); +/// Content to draw when node is disabled. +fn disabled_node_ui(ui: &mut egui::Ui) { + View::center_content(ui, 156.0, |ui| { + let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL); + ui.label(RichText::new(text) + .size(16.0) + .color(Colors::inactive_text()) + ); + ui.add_space(8.0); + View::action_button(ui, format!("{} {}", POWER, t!("network.enable_node")), || { + Node::start(); + }); + ui.add_space(2.0); + NetworkContent::autorun_node_ui(ui); + }); +} + +/// Draw integrated node error content. +pub fn node_error_ui(ui: &mut egui::Ui, e: NodeError) { + match e { + NodeError::Storage => { + View::center_content(ui, 156.0, |ui| { + ui.label(RichText::new(t!("network_node.error_clean")) + .size(16.0) + .color(Colors::red()) + ); + ui.add_space(8.0); + let btn_txt = format!("{} {}", + ARROWS_COUNTER_CLOCKWISE, + t!("network_node.resync")); + View::action_button(ui, btn_txt, || { + Node::clean_up_data(); + Node::start(); }); - return; - } - NodeError::P2P | NodeError::API => { - let msg_type = match e { - NodeError::API => "API", - _ => "P2P" - }; - View::center_content(ui, 106.0, |ui| { - let text = t!( + ui.add_space(2.0); + }); + return; + } + NodeError::P2P | NodeError::API => { + let msg_type = match e { + NodeError::API => "API", + _ => "P2P" + }; + View::center_content(ui, 106.0, |ui| { + let text = t!( "network_node.error_p2p_api", "p2p_api" => msg_type, "settings" => FADERS ); - ui.label(RichText::new(text) - .size(16.0) - .color(Colors::red()) - ); - ui.add_space(2.0); + ui.label(RichText::new(text) + .size(16.0) + .color(Colors::red()) + ); + ui.add_space(2.0); + }); + return; + } + NodeError::Configuration => { + View::center_content(ui, 106.0, |ui| { + ui.label(RichText::new(t!("network_node.error_config", "settings" => FADERS)) + .size(16.0) + .color(Colors::red()) + ); + ui.add_space(8.0); + let btn_txt = format!("{} {}", + ARROWS_COUNTER_CLOCKWISE, + t!("network_settings.reset")); + View::action_button(ui, btn_txt, || { + NodeConfig::reset_to_default(); + Node::start(); }); - return; - } - NodeError::Configuration => { - View::center_content(ui, 106.0, |ui| { - ui.label(RichText::new(t!("network_node.error_config", "settings" => FADERS)) - .size(16.0) - .color(Colors::red()) - ); - ui.add_space(8.0); - let btn_txt = format!("{} {}", - ARROWS_COUNTER_CLOCKWISE, - t!("network_settings.reset")); - View::action_button(ui, btn_txt, || { - NodeConfig::reset_to_default(); - Node::start(); - }); - ui.add_space(2.0); + ui.add_space(2.0); + }); + } + NodeError::Unknown => { + View::center_content(ui, 156.0, |ui| { + ui.label(RichText::new(t!("network_node.error_unknown", "settings" => FADERS)) + .size(16.0) + .color(Colors::red()) + ); + ui.add_space(8.0); + let btn_txt = format!("{} {}", + ARROWS_COUNTER_CLOCKWISE, + t!("network_node.resync")); + View::action_button(ui, btn_txt, || { + Node::clean_up_data(); + Node::start(); }); - } - NodeError::Unknown => { - View::center_content(ui, 156.0, |ui| { - ui.label(RichText::new(t!("network_node.error_unknown", "settings" => FADERS)) - .size(16.0) - .color(Colors::red()) - ); - ui.add_space(8.0); - let btn_txt = format!("{} {}", - ARROWS_COUNTER_CLOCKWISE, - t!("network_node.resync")); - View::action_button(ui, btn_txt, || { - Node::clean_up_data(); - Node::start(); - }); - ui.add_space(2.0); - }); - } + ui.add_space(2.0); + }); } } } \ No newline at end of file diff --git a/src/gui/views/network/metrics.rs b/src/gui/views/network/metrics.rs index 62319c6..534ec06 100644 --- a/src/gui/views/network/metrics.rs +++ b/src/gui/views/network/metrics.rs @@ -14,6 +14,7 @@ use egui::{RichText, Rounding, ScrollArea, vec2}; use egui::scroll_area::ScrollBarVisibility; +use grin_core::consensus::{DAY_HEIGHT, GRIN_BASE, HOUR_SEC, REWARD}; use grin_servers::{DiffBlock, ServerStats}; use crate::gui::Colors; @@ -21,90 +22,64 @@ use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HOURGLASS_LOW, HOURGLASS_ME use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Content, View}; use crate::gui::views::network::NetworkContent; -use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::network::types::{NodeTab, NodeTabType}; use crate::node::Node; /// Chain metrics tab content. #[derive(Default)] pub struct NetworkMetrics; -const BLOCK_REWARD: f64 = 60.0; -// 1 year is calculated as 365 days and 6 hours (31557600). -const YEARLY_SUPPLY: f64 = ((60 * 60 * 24 * 365) + 6 * 60 * 60) as f64; +const BLOCK_REWARD: u64 = REWARD / GRIN_BASE; +// 1 year as 365 days and 6 hours (31557600). +const YEARLY_SUPPLY: u64 = (BLOCK_REWARD * DAY_HEIGHT * 365) + 6 * HOUR_SEC; -impl NetworkTab for NetworkMetrics { - fn get_type(&self) -> NetworkTabType { - NetworkTabType::Metrics +impl NodeTab for NetworkMetrics { + fn get_type(&self) -> NodeTabType { + NodeTabType::Metrics } fn ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { - // Show an error content when available. - let node_err = Node::get_error(); - if node_err.is_some() { - NetworkContent::node_error_ui(ui, node_err.unwrap()); - return; - } - - // Show message to enable node when it's not running. - if !Node::is_running() { - NetworkContent::disabled_node_ui(ui); - return; - } - - // Show loading spinner when node is stopping. - if Node::is_stopping() { - NetworkContent::loading_ui(ui, None); - return; - } - - // Show message when metrics are not available. let server_stats = Node::get_stats(); - if server_stats.is_none() || Node::is_restarting() - || server_stats.as_ref().unwrap().diff_stats.height == 0 { + let stats = server_stats.as_ref().unwrap(); + if stats.diff_stats.height == 0 { NetworkContent::loading_ui(ui, Some(t!("network_metrics.loading"))); return; } - ui.add_space(1.0); - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - let stats = server_stats.as_ref().unwrap(); - // Show emission and difficulty info. - info_ui(ui, stats); - // Show difficulty adjustment window blocks. - blocks_ui(ui, stats); - }); + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Show emission and difficulty info. + info_ui(ui, stats); + // Show difficulty adjustment window blocks. + blocks_ui(ui, stats); }); } } -const BLOCK_ITEM_HEIGHT: f32 = 78.0; - /// Draw emission and difficulty info. fn info_ui(ui: &mut egui::Ui, stats: &ServerStats) { // Show emission info. View::sub_title(ui, format!("{} {}", COINS, t!("network_metrics.emission"))); ui.columns(3, |columns| { - let supply = stats.header_stats.height as f64 * BLOCK_REWARD; - let rate = (YEARLY_SUPPLY * 100.0) / supply; + let supply = stats.header_stats.height * BLOCK_REWARD; + let rate = (YEARLY_SUPPLY * 100) / supply; columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - format!("{}ツ", BLOCK_REWARD), - t!("network_metrics.reward"), - [true, false, true, false]); + View::label_box(ui, + format!("{}ツ", BLOCK_REWARD), + t!("network_metrics.reward"), + [true, false, true, false]); }); columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - format!("{:.2}%", rate), - t!("network_metrics.inflation"), - [false, false, false, false]); + View::label_box(ui, + format!("{:.2}%", rate), + t!("network_metrics.inflation"), + [false, false, false, false]); }); columns[2].vertical_centered(|ui| { - View::rounded_box(ui, - supply.to_string(), - t!("network_metrics.supply"), - [false, true, false, true]); + View::label_box(ui, + supply.to_string(), + t!("network_metrics.supply"), + [false, true, false, true]); }); }); ui.add_space(5.0); @@ -117,32 +92,34 @@ fn info_ui(ui: &mut egui::Ui, stats: &ServerStats) { View::sub_title(ui, format!("{} {}", HOURGLASS_MEDIUM, difficulty_title)); ui.columns(3, |columns| { columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.diff_stats.height.to_string(), - t!("network_node.height"), - [true, false, true, false]); + View::label_box(ui, + stats.diff_stats.height.to_string(), + t!("network_node.height"), + [true, false, true, false]); }); columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - format!("{}s", stats.diff_stats.average_block_time), - t!("network_metrics.block_time"), - [false, false, false, false]); + View::label_box(ui, + format!("{}s", stats.diff_stats.average_block_time), + t!("network_metrics.block_time"), + [false, false, false, false]); }); columns[2].vertical_centered(|ui| { - View::rounded_box(ui, - stats.diff_stats.average_difficulty.to_string(), - t!("network_node.difficulty"), - [false, true, false, true]); + View::label_box(ui, + stats.diff_stats.average_difficulty.to_string(), + t!("network_node.difficulty"), + [false, true, false, true]); }); }); } +const BLOCK_ITEM_HEIGHT: f32 = 77.0; + /// Draw difficulty adjustment window blocks content. fn blocks_ui(ui: &mut egui::Ui, stats: &ServerStats) { let blocks_size = stats.diff_stats.last_blocks.len(); ui.add_space(4.0); ScrollArea::vertical() - .id_source("difficulty_scroll") + .id_salt("mining_difficulty_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .stick_to_bottom(true) @@ -151,11 +128,8 @@ fn blocks_ui(ui: &mut egui::Ui, stats: &ServerStats) { BLOCK_ITEM_HEIGHT, blocks_size, |ui, row_range| { + ui.add_space(4.0); for index in row_range { - // Add space before the first item. - if index == 0 { - ui.add_space(4.0); - } let db = stats.diff_stats.last_blocks.get(index).unwrap(); block_item_ui(ui, db, View::item_rounding(index, blocks_size, false)); } @@ -167,11 +141,11 @@ fn blocks_ui(ui: &mut egui::Ui, stats: &ServerStats) { fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: Rounding) { let mut rect = ui.available_rect_before_wrap(); rect.set_height(BLOCK_ITEM_HEIGHT); - ui.allocate_ui_at_rect(rect, |ui| { + ui.allocate_ui(rect.size(), |ui| { ui.horizontal(|ui| { ui.add_space(6.0); ui.vertical(|ui| { - ui.add_space(3.0); + ui.add_space(4.0); // Draw round background. rect.min += vec2(8.0, 0.0); @@ -180,24 +154,26 @@ fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: Rounding) { // Draw block hash. ui.horizontal(|ui| { - ui.add_space(7.0); + ui.add_space(8.0); ui.label(RichText::new(db.block_hash.to_string()) .color(Colors::white_or_black(true)) .size(17.0)); }); // Draw block difficulty and height. ui.horizontal(|ui| { - ui.add_space(6.0); + ui.add_space(7.0); let diff_text = format!("{} {} {} {}", CUBE_TRANSPARENT, db.difficulty, AT, db.block_height); - ui.label(RichText::new(diff_text).color(Colors::title(false)).size(16.0)); + ui.label(RichText::new(diff_text) + .color(Colors::title(false)) + .size(15.0)); }); // Draw block date. ui.horizontal(|ui| { - ui.add_space(6.0); + ui.add_space(7.0); let block_time = View::format_time(db.time as i64); ui.label(RichText::new(format!("{} {}s {} {}", TIMER, @@ -205,7 +181,7 @@ fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: Rounding) { HOURGLASS_LOW, block_time)) .color(Colors::gray()) - .size(16.0)); + .size(15.0)); }); ui.add_space(3.0); }); diff --git a/src/gui/views/network/mining.rs b/src/gui/views/network/mining.rs index 7266c00..df42ed6 100644 --- a/src/gui/views/network/mining.rs +++ b/src/gui/views/network/mining.rs @@ -23,7 +23,7 @@ use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Content, View}; use crate::gui::views::network::NetworkContent; use crate::gui::views::network::setup::StratumSetup; -use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::network::types::{NodeTab, NodeTabType}; use crate::node::{Node, NodeConfig}; use crate::wallet::WalletConfig; @@ -50,35 +50,13 @@ impl Default for NetworkMining { } } -impl NetworkTab for NetworkMining { - fn get_type(&self) -> NetworkTabType { - NetworkTabType::Mining +impl NodeTab for NetworkMining { + fn get_type(&self) -> NodeTabType { + NodeTabType::Mining } fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Show an error content when available. - let node_err = Node::get_error(); - if node_err.is_some() { - NetworkContent::node_error_ui(ui, node_err.unwrap()); - return; - } - - // Show message to enable node when it's not running. - if !Node::is_running() { - NetworkContent::disabled_node_ui(ui); - return; - } - - // Show loading spinner when node is stopping or stratum server is starting. - if Node::is_stopping() || Node::is_stratum_starting() { - NetworkContent::loading_ui(ui, None); - return; - } - - // Show message when mining is not available. - let server_stats = Node::get_stats(); - if server_stats.is_none() || Node::is_restarting() - || Node::get_sync_status().unwrap() != SyncStatus::NoSync { + if Node::is_stratum_starting() || Node::get_sync_status().unwrap() != SyncStatus::NoSync { NetworkContent::loading_ui(ui, Some(t!("network_mining.loading"))); return; } @@ -87,15 +65,13 @@ impl NetworkTab for NetworkMining { let stratum_stats = Node::get_stratum_stats(); if !stratum_stats.is_running { ScrollArea::vertical() - .id_source("stratum_setup_scroll") + .id_salt("stratum_setup_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(1.0); - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - self.stratum_server_setup.ui(ui, cb); - }); + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.stratum_server_setup.ui(ui, cb); }); }); return; @@ -108,16 +84,16 @@ impl NetworkTab for NetworkMining { ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { let (stratum_addr, stratum_port) = NodeConfig::get_stratum_address(); - View::rounded_box(ui, - format!("{}:{}", stratum_addr, stratum_port), - t!("network_mining.address"), - [true, false, true, false]); + View::label_box(ui, + format!("{}:{}", stratum_addr, stratum_port), + t!("network_mining.address"), + [true, false, true, false]); }); columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - self.wallet_name.clone(), - t!("network_mining.rewards_wallet"), - [false, true, false, true]); + View::label_box(ui, + self.wallet_name.clone(), + t!("network_mining.rewards_wallet"), + [false, true, false, true]); }); }); ui.add_space(4.0); @@ -131,10 +107,10 @@ impl NetworkTab for NetworkMining { } else { "-".into() }; - View::rounded_box(ui, - difficulty, - t!("network_node.difficulty"), - [true, false, true, false]); + View::label_box(ui, + difficulty, + t!("network_node.difficulty"), + [true, false, true, false]); }); columns[1].vertical_centered(|ui| { let block_height = if stratum_stats.block_height > 0 { @@ -142,10 +118,10 @@ impl NetworkTab for NetworkMining { } else { "-".into() }; - View::rounded_box(ui, - block_height, - t!("network_node.header"), - [false, false, false, false]); + View::label_box(ui, + block_height, + t!("network_node.header"), + [false, false, false, false]); }); columns[2].vertical_centered(|ui| { let hashrate = if stratum_stats.network_hashrate > 0.0 { @@ -153,10 +129,10 @@ impl NetworkTab for NetworkMining { } else { "-".into() }; - View::rounded_box(ui, - hashrate, - t!("network_mining.hashrate", "bits" => stratum_stats.edge_bits), - [false, true, false, true]); + View::label_box(ui, + hashrate, + t!("network_mining.hashrate", "bits" => stratum_stats.edge_bits), + [false, true, false, true]); }); }); ui.add_space(4.0); @@ -165,17 +141,17 @@ impl NetworkTab for NetworkMining { View::sub_title(ui, format!("{} {}", CPU, t!("network_mining.miners"))); ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stratum_stats.num_workers.to_string(), - t!("network_mining.devices"), - [true, false, true, false]); + View::label_box(ui, + stratum_stats.num_workers.to_string(), + t!("network_mining.devices"), + [true, false, true, false]); }); columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - stratum_stats.blocks_found.to_string(), - t!("network_mining.blocks_found"), - [false, true, false, true]); + View::label_box(ui, + stratum_stats.blocks_found.to_string(), + t!("network_mining.blocks_found"), + [false, true, false, true]); }); }); ui.add_space(4.0); @@ -187,7 +163,7 @@ impl NetworkTab for NetworkMining { View::horizontal_line(ui, Colors::item_stroke()); ui.add_space(4.0); ScrollArea::vertical() - .id_source("stratum_workers_scroll") + .id_salt("stratum_workers_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show_rows( diff --git a/src/gui/views/network/node.rs b/src/gui/views/network/node.rs index ec0ea4c..fc9080f 100644 --- a/src/gui/views/network/node.rs +++ b/src/gui/views/network/node.rs @@ -20,51 +20,28 @@ use crate::gui::Colors; use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, SHARE_NETWORK}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Content, View}; -use crate::gui::views::network::NetworkContent; -use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::network::types::{NodeTab, NodeTabType}; use crate::node::{Node, NodeConfig}; /// Integrated node tab content. #[derive(Default)] pub struct NetworkNode; -impl NetworkTab for NetworkNode { - fn get_type(&self) -> NetworkTabType { - NetworkTabType::Node +impl NodeTab for NetworkNode { + fn get_type(&self) -> NodeTabType { + NodeTabType::Info } fn ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { - // Show an error content when available. - let node_err = Node::get_error(); - if node_err.is_some() { - NetworkContent::node_error_ui(ui, node_err.unwrap()); - return; - } - - // Show message to enable node when it's not running. - if !Node::is_running() { - NetworkContent::disabled_node_ui(ui); - return; - } - - // Show loading spinner when stats are not available. - let server_stats = Node::get_stats(); - if server_stats.is_none() || Node::is_restarting() || Node::is_stopping() { - NetworkContent::loading_ui(ui, None); - return; - } - ScrollArea::vertical() - .id_source("integrated_node") + .id_salt("integrated_node_info_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(2.0); - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - // Show node stats content. - node_stats_ui(ui); - }); + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Show node stats content. + node_stats_ui(ui); }); }); } @@ -79,32 +56,32 @@ fn node_stats_ui(ui: &mut egui::Ui) { View::sub_title(ui, format!("{} {}", FLOW_ARROW, t!("network_node.header"))); ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.header_stats.last_block_h.to_string(), - t!("network_node.hash"), - [true, false, false, false]); + View::label_box(ui, + stats.header_stats.last_block_h.to_string(), + t!("network_node.hash"), + [true, false, false, false]); }); columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - stats.header_stats.height.to_string(), - t!("network_node.height"), - [false, true, false, false]); + View::label_box(ui, + stats.header_stats.height.to_string(), + t!("network_node.height"), + [false, true, false, false]); }); }); ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.header_stats.total_difficulty.to_string(), - t!("network_node.difficulty"), - [false, false, true, false]); + View::label_box(ui, + stats.header_stats.total_difficulty.to_string(), + t!("network_node.difficulty"), + [false, false, true, false]); }); columns[1].vertical_centered(|ui| { let h_ts = stats.header_stats.latest_timestamp.timestamp(); let h_time = View::format_time(h_ts); - View::rounded_box(ui, - h_time, - t!("network_node.time"), - [false, false, false, true]); + View::label_box(ui, + h_time, + t!("network_node.time"), + [false, false, false, true]); }); }); ui.add_space(5.0); @@ -113,32 +90,32 @@ fn node_stats_ui(ui: &mut egui::Ui) { View::sub_title(ui, format!("{} {}", CUBE, t!("network_node.block"))); ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.chain_stats.last_block_h.to_string(), - t!("network_node.hash"), - [true, false, false, false]); + View::label_box(ui, + stats.chain_stats.last_block_h.to_string(), + t!("network_node.hash"), + [true, false, false, false]); }); columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - stats.chain_stats.height.to_string(), - t!("network_node.height"), - [false, true, false, false]); + View::label_box(ui, + stats.chain_stats.height.to_string(), + t!("network_node.height"), + [false, true, false, false]); }); }); ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.chain_stats.total_difficulty.to_string(), - t!("network_node.difficulty"), - [false, false, true, false]); + View::label_box(ui, + stats.chain_stats.total_difficulty.to_string(), + t!("network_node.difficulty"), + [false, false, true, false]); }); columns[1].vertical_centered(|ui| { let b_ts = stats.chain_stats.latest_timestamp.timestamp(); let b_time = View::format_time(b_ts); - View::rounded_box(ui, - b_time, - t!("network_node.time"), - [false, false, false, true]); + View::label_box(ui, + b_time, + t!("network_node.time"), + [false, false, false, true]); }); }); ui.add_space(5.0); @@ -151,10 +128,10 @@ fn node_stats_ui(ui: &mut egui::Ui) { None => "0 (0)".to_string(), Some(tx) => format!("{} ({})", tx.tx_pool_size, tx.tx_pool_kernels) }; - View::rounded_box(ui, - tx_stat, - t!("network_node.main_pool"), - [true, false, false, false]); + View::label_box(ui, + tx_stat, + t!("network_node.main_pool"), + [true, false, false, false]); }); columns[1].vertical_centered(|ui| { let stem_tx_stat = match &stats.tx_stats { @@ -163,24 +140,24 @@ fn node_stats_ui(ui: &mut egui::Ui) { stx.stem_pool_size, stx.stem_pool_kernels) }; - View::rounded_box(ui, - stem_tx_stat, - t!("network_node.stem_pool"), - [false, true, false, false]); + View::label_box(ui, + stem_tx_stat, + t!("network_node.stem_pool"), + [false, true, false, false]); }); }); ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.disk_usage_gb.to_string(), - t!("network_node.size"), - [false, false, true, false]); + View::label_box(ui, + stats.disk_usage_gb.to_string(), + t!("network_node.size"), + [false, false, true, false]); }); columns[1].vertical_centered(|ui| { let peers_txt = format!("{} ({})", stats.peer_count, NodeConfig::get_max_outbound_peers()); - View::rounded_box(ui, peers_txt, t!("network_node.peers"), [false, false, false, true]); + View::label_box(ui, peers_txt, t!("network_node.peers"), [false, false, false, true]); }); }); ui.add_space(5.0); @@ -196,23 +173,27 @@ fn node_stats_ui(ui: &mut egui::Ui) { } } +const PEER_ITEM_HEIGHT: f32 = 77.0; + /// Draw connected peer info item. fn peer_item_ui(ui: &mut egui::Ui, peer: &PeerStats, rounding: Rounding) { let mut rect = ui.available_rect_before_wrap(); - rect.set_height(79.0); - ui.allocate_ui_at_rect(rect, |ui| { + rect.set_height(PEER_ITEM_HEIGHT); + ui.allocate_ui(rect.size(), |ui| { ui.vertical(|ui| { ui.add_space(4.0); // Draw round background. - ui.painter().rect(rect, rounding, Colors::white_or_black(false), View::item_stroke()); + ui.painter().rect(rect, rounding, Colors::fill_lite(), View::item_stroke()); - // Draw peer address + // Draw IP address. ui.horizontal(|ui| { ui.add_space(7.0); - ui.label(RichText::new(&peer.addr).color(Colors::white_or_black(true)).size(17.0)); + ui.label(RichText::new(&peer.addr) + .color(Colors::white_or_black(true)) + .size(17.0)); }); - // Draw peer difficulty and height + // Draw difficulty and height. ui.horizontal(|ui| { ui.add_space(6.0); let diff_text = format!("{} {} {} {}", @@ -220,13 +201,17 @@ fn peer_item_ui(ui: &mut egui::Ui, peer: &PeerStats, rounding: Rounding) { peer.total_difficulty, AT, peer.height); - ui.label(RichText::new(diff_text).color(Colors::title(false)).size(16.0)); + ui.label(RichText::new(diff_text) + .color(Colors::title(false)) + .size(15.0)); }); - // Draw peer user-agent + // Draw user-agent. ui.horizontal(|ui| { ui.add_space(6.0); let agent_text = format!("{} {}", DEVICES, &peer.user_agent); - ui.label(RichText::new(agent_text).color(Colors::gray()).size(16.0)); + ui.label(RichText::new(agent_text) + .color(Colors::gray()) + .size(15.0)); }); ui.add_space(3.0); diff --git a/src/gui/views/network/settings.rs b/src/gui/views/network/settings.rs index 3bd83af..4ec03ef 100644 --- a/src/gui/views/network/settings.rs +++ b/src/gui/views/network/settings.rs @@ -20,7 +20,7 @@ use crate::gui::icons::ARROW_COUNTER_CLOCKWISE; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, View}; use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; -use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::network::types::{NodeTab, NodeTabType}; use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::node::{Node, NodeConfig}; @@ -42,7 +42,7 @@ pub struct NetworkSettings { } /// Identifier for settings reset confirmation [`Modal`]. -pub const RESET_SETTINGS_MODAL: &'static str = "reset_settings"; +pub const RESET_SETTINGS_CONFIRMATION_MODAL: &'static str = "reset_settings_confirmation"; impl Default for NetworkSettings { fn default() -> Self { @@ -53,7 +53,7 @@ impl Default for NetworkSettings { pool: PoolSetup::default(), dandelion: DandelionSetup::default(), modal_ids: vec![ - RESET_SETTINGS_MODAL + RESET_SETTINGS_CONFIRMATION_MODAL ] } } @@ -69,15 +69,15 @@ impl ModalContainer for NetworkSettings { modal: &Modal, _: &dyn PlatformCallbacks) { match modal.id { - RESET_SETTINGS_MODAL => reset_settings_confirmation_modal(ui, modal), + RESET_SETTINGS_CONFIRMATION_MODAL => reset_settings_confirmation_modal(ui, modal), _ => {} } } } -impl NetworkTab for NetworkSettings { - fn get_type(&self) -> NetworkTabType { - NetworkTabType::Settings +impl NodeTab for NetworkSettings { + fn get_type(&self) -> NodeTabType { + NodeTabType::Settings } fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { @@ -85,7 +85,7 @@ impl NetworkTab for NetworkSettings { self.current_modal_ui(ui, cb); ScrollArea::vertical() - .id_source("network_settings") + .id_salt("node_settings_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { @@ -210,7 +210,7 @@ fn reset_settings_ui(ui: &mut egui::Ui) { t!("network_settings.reset_settings")); View::action_button(ui, button_text, || { // Show modal to confirm settings reset. - Modal::new(RESET_SETTINGS_MODAL) + Modal::new(RESET_SETTINGS_CONFIRMATION_MODAL) .position(ModalPosition::Center) .title(t!("confirmation")) .show(); diff --git a/src/gui/views/network/setup/dandelion.rs b/src/gui/views/network/setup/dandelion.rs index 5194432..02ac7e2 100644 --- a/src/gui/views/network/setup/dandelion.rs +++ b/src/gui/views/network/setup/dandelion.rs @@ -141,7 +141,7 @@ impl DandelionSetup { ui.add_space(6.0); let epoch = NodeConfig::get_dandelion_epoch(); - View::button(ui, format!("{} {}", WATCH, epoch.clone()), Colors::button(), || { + View::button(ui, format!("{} {}", WATCH, &epoch), Colors::white_or_black(false), || { // Setup values for modal. self.epoch_edit = epoch; // Show epoch setup modal. @@ -218,8 +218,7 @@ impl DandelionSetup { ui.add_space(6.0); let embargo = NodeConfig::get_dandelion_embargo(); - View::button(ui, format!("{} {}", TIMER, embargo.clone()), Colors::button(), || { - // Setup values for modal. + View::button(ui, format!("{} {}", TIMER, &embargo), Colors::white_or_black(false), || { self.embargo_edit = embargo; // Show embargo setup modal. Modal::new(EMBARGO_MODAL) @@ -294,10 +293,10 @@ impl DandelionSetup { ); ui.add_space(6.0); - let agg = NodeConfig::get_dandelion_aggregation(); - View::button(ui, format!("{} {}", CLOCK_COUNTDOWN, agg.clone()), Colors::button(), || { + let ag = NodeConfig::get_dandelion_aggregation(); + View::button(ui, format!("{} {}", CLOCK_COUNTDOWN, &ag), Colors::white_or_black(false), || { // Setup values for modal. - self.aggregation_edit = agg; + self.aggregation_edit = ag; // Show aggregation setup modal. Modal::new(AGGREGATION_MODAL) .position(ModalPosition::CenterTop) @@ -372,7 +371,7 @@ impl DandelionSetup { ui.add_space(6.0); let stem_prob = NodeConfig::get_stem_probability(); - View::button(ui, format!("{}%", stem_prob.clone()), Colors::button(), || { + View::button(ui, format!("{}%", &stem_prob), Colors::white_or_black(false), || { // Setup values for modal. self.stem_prob_edit = stem_prob; // Show stem probability setup modal. diff --git a/src/gui/views/network/setup/node.rs b/src/gui/views/network/setup/node.rs index 9cca0c7..3773bf2 100644 --- a/src/gui/views/network/setup/node.rs +++ b/src/gui/views/network/setup/node.rs @@ -255,7 +255,7 @@ impl NodeSetup { ui.add_space(6.0); let (_, port) = NodeConfig::get_api_ip_port(); - View::button(ui, format!("{} {}", PLUG, port.clone()), Colors::button(), || { + View::button(ui, format!("{} {}", PLUG, &port), Colors::white_or_black(false), || { // Setup values for modal. self.api_port_edit = port; self.api_port_available_edit = self.is_api_port_available; @@ -283,7 +283,9 @@ impl NodeSetup { fn api_port_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("network_settings.api_port")).size(17.0).color(Colors::gray())); + ui.label(RichText::new(t!("network_settings.api_port")) + .size(17.0) + .color(Colors::gray())); ui.add_space(6.0); // Draw API port text edit. @@ -366,7 +368,7 @@ impl NodeSetup { format!("{} {}", SHIELD_SLASH, t!("network_settings.disabled")) }; - View::button(ui, secret_text, Colors::button(), || { + View::button(ui, secret_text, Colors::white_or_black(false), || { // Setup values for modal. self.secret_edit = secret_value.unwrap_or("".to_string()); // Show secret edit modal. @@ -449,7 +451,9 @@ impl NodeSetup { ui.add_space(6.0); let ftl = NodeConfig::get_ftl(); - View::button(ui, format!("{} {}", CLOCK_CLOCKWISE, ftl.clone()), Colors::button(), || { + View::button(ui, + format!("{} {}", CLOCK_CLOCKWISE, &ftl), + Colors::white_or_black(false), || { // Setup values for modal. self.ftl_edit = ftl; // Show ftl value setup modal. diff --git a/src/gui/views/network/setup/p2p.rs b/src/gui/views/network/setup/p2p.rs index 0c31f8c..99a644a 100644 --- a/src/gui/views/network/setup/p2p.rs +++ b/src/gui/views/network/setup/p2p.rs @@ -246,7 +246,9 @@ impl P2PSetup { ui.add_space(6.0); let port = NodeConfig::get_p2p_port(); - View::button(ui, format!("{} {}", PLUG, port.clone()), Colors::button(), || { + View::button(ui, + format!("{} {}", PLUG, &port), + Colors::white_or_black(false), || { // Setup values for modal. self.port_edit = port; self.port_available_edit = self.is_port_available; @@ -306,11 +308,9 @@ impl P2PSetup { // Save port at config if it's available. if available { NodeConfig::save_p2p_port(self.port_edit.parse::().unwrap()); - if Node::is_running() { Node::restart(); } - self.is_port_available = true; cb.hide_keyboard(); modal.close(); @@ -371,9 +371,7 @@ impl P2PSetup { .size(16.0) .color(Colors::inactive_text())); } - if !peers.is_empty() { - ui.add_space(12.0); - } + ui.add_space(12.0); let add_text = if peer_type == &PeerType::CustomSeed { format!("{} {}", PLUS_CIRCLE, t!("network_settings.add_seed")) @@ -381,7 +379,7 @@ impl P2PSetup { format!("{} {}", PLUS_CIRCLE, t!("network_settings.add_peer")) }; - View::button(ui, add_text, Colors::button(), || { + View::button(ui, add_text, Colors::white_or_black(false), || { // Setup values for modal. self.is_correct_address_edit = true; self.peer_edit = "".to_string(); @@ -508,7 +506,9 @@ impl P2PSetup { ui.add_space(6.0); let ban_window = NodeConfig::get_p2p_ban_window(); - View::button(ui, format!("{} {}", PROHIBIT_INSET, ban_window.clone()), Colors::button(), || { + View::button(ui, + format!("{} {}", PROHIBIT_INSET, &ban_window), + Colors::white_or_black(false), || { // Setup values for modal. self.ban_window_edit = ban_window; // Show ban window period setup modal. @@ -590,8 +590,9 @@ impl P2PSetup { ui.add_space(6.0); let max_inbound = NodeConfig::get_max_inbound_peers(); - let button_text = format!("{} {}", ARROW_FAT_LINES_DOWN, max_inbound.clone()); - View::button(ui, button_text, Colors::button(), || { + View::button(ui, + format!("{} {}", ARROW_FAT_LINES_DOWN, &max_inbound), + Colors::white_or_black(false), || { // Setup values for modal. self.max_inbound_count = max_inbound; // Show maximum number of inbound peers setup modal. @@ -666,10 +667,10 @@ impl P2PSetup { .color(Colors::gray()) ); ui.add_space(6.0); - let max_outbound = NodeConfig::get_max_outbound_peers(); - let button_text = format!("{} {}", ARROW_FAT_LINES_UP, max_outbound.clone()); - View::button(ui, button_text, Colors::button(), || { + View::button(ui, + format!("{} {}", ARROW_FAT_LINES_UP, &max_outbound), + Colors::white_or_black(false), || { // Setup values for modal. self.max_outbound_count = max_outbound; // Show maximum number of outbound peers setup modal. @@ -740,9 +741,10 @@ impl P2PSetup { /// Draw content to reset peers data. fn reset_peers_ui(&mut self, ui: &mut egui::Ui) { ui.add_space(4.0); - - let button_text = format!("{} {}", TRASH, t!("network_settings.reset_peers")); - View::colored_text_button(ui, button_text, Colors::red(), Colors::button(), || { + View::colored_text_button(ui, + format!("{} {}", TRASH, t!("network_settings.reset_peers")), + Colors::red(), + Colors::white_or_black(false), || { Node::reset_peers(false); self.peers_reset = true; }); diff --git a/src/gui/views/network/setup/pool.rs b/src/gui/views/network/setup/pool.rs index 375caa9..a6d9543 100644 --- a/src/gui/views/network/setup/pool.rs +++ b/src/gui/views/network/setup/pool.rs @@ -145,7 +145,7 @@ impl PoolSetup { ui.add_space(6.0); let fee = NodeConfig::get_base_fee(); - View::button(ui, format!("{} {}", HAND_COINS, fee.clone()), Colors::button(), || { + View::button(ui, format!("{} {}", HAND_COINS, &fee), Colors::white_or_black(false), || { // Setup values for modal. self.fee_base_edit = fee; // Show fee setup modal. @@ -195,7 +195,6 @@ impl PoolSetup { modal.close(); } }; - ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { @@ -220,9 +219,10 @@ impl PoolSetup { .color(Colors::gray()) ); ui.add_space(6.0); - let period = NodeConfig::get_reorg_cache_period(); - View::button(ui, format!("{} {}", CLOCK_COUNTDOWN, period.clone()), Colors::button(), || { + View::button(ui, + format!("{} {}", CLOCK_COUNTDOWN, &period), + Colors::white_or_black(false), || { // Setup values for modal. self.reorg_period_edit = period; // Show reorg period setup modal. @@ -299,7 +299,7 @@ impl PoolSetup { ui.add_space(6.0); let size = NodeConfig::get_max_pool_size(); - View::button(ui, format!("{} {}", CIRCLES_THREE, size.clone()), Colors::button(), || { + View::button(ui, format!("{} {}", CIRCLES_THREE, size), Colors::white_or_black(false), || { // Setup values for modal. self.pool_size_edit = size; // Show pool size setup modal. @@ -376,7 +376,9 @@ impl PoolSetup { ui.add_space(6.0); let size = NodeConfig::get_max_stempool_size(); - View::button(ui, format!("{} {}", BEZIER_CURVE, size.clone()), Colors::button(), || { + View::button(ui, + format!("{} {}", BEZIER_CURVE, &size), + Colors::white_or_black(false), || { // Setup values for modal. self.stempool_size_edit = size; // Show stempool size setup modal. @@ -453,7 +455,9 @@ impl PoolSetup { ui.add_space(6.0); let weight = NodeConfig::get_mineable_max_weight(); - View::button(ui, format!("{} {}", BOUNDING_BOX, weight.clone()), Colors::button(), || { + View::button(ui, + format!("{} {}", BOUNDING_BOX, &weight), + Colors::white_or_black(false), || { // Setup values for modal. self.max_weight_edit = weight; // Show total tx weight setup modal. diff --git a/src/gui/views/network/setup/stratum.rs b/src/gui/views/network/setup/stratum.rs index 9d65b00..2c955b9 100644 --- a/src/gui/views/network/setup/stratum.rs +++ b/src/gui/views/network/setup/stratum.rs @@ -189,7 +189,9 @@ impl StratumSetup { ui.add_space(8.0); // Show button to select wallet. - View::button(ui, t!("network_settings.choose_wallet"), Colors::button(), || { + View::button(ui, + t!("network_settings.choose_wallet"), + Colors::white_or_black(false), || { self.show_wallets_modal(); }); ui.add_space(12.0); @@ -260,7 +262,7 @@ impl StratumSetup { ui.add_space(6.0); let (_, port) = NodeConfig::get_stratum_address(); - View::button(ui, format!("{} {}", PLUG, port.clone()), Colors::button(), || { + View::button(ui, format!("{} {}", PLUG, &port), Colors::white_or_black(false), || { // Setup values for modal. self.stratum_port_edit = port; self.stratum_port_available_edit = self.is_port_available; @@ -359,7 +361,7 @@ impl StratumSetup { ui.add_space(6.0); let time = NodeConfig::get_stratum_attempt_time(); - View::button(ui, format!("{} {}", TIMER, time.clone()), Colors::button(), || { + View::button(ui, format!("{} {}", TIMER, &time), Colors::white_or_black(false), || { // Setup values for modal. self.attempt_time_edit = time; @@ -442,7 +444,7 @@ impl StratumSetup { ui.add_space(6.0); let diff = NodeConfig::get_stratum_min_share_diff(); - View::button(ui, format!("{} {}", BARBELL, diff.clone()), Colors::button(), || { + View::button(ui, format!("{} {}", BARBELL, &diff), Colors::white_or_black(false), || { // Setup values for modal. self.min_share_diff_edit = diff; diff --git a/src/gui/views/network/types.rs b/src/gui/views/network/types.rs index 419bbb0..1d29eb7 100644 --- a/src/gui/views/network/types.rs +++ b/src/gui/views/network/types.rs @@ -14,28 +14,28 @@ use crate::gui::platform::PlatformCallbacks; -/// Network tab content interface. -pub trait NetworkTab { - fn get_type(&self) -> NetworkTabType; +/// Integrated node tab content interface. +pub trait NodeTab { + fn get_type(&self) -> NodeTabType; fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks); } -/// Type of [`NetworkTab`] content. +/// Type of [`NodeTab`] content. #[derive(PartialEq)] -pub enum NetworkTabType { - Node, +pub enum NodeTabType { + Info, Metrics, Mining, Settings } -impl NetworkTabType { +impl NodeTabType { pub fn title(&self) -> String { match *self { - NetworkTabType::Node => { t!("network.node") } - NetworkTabType::Metrics => { t!("network.metrics") } - NetworkTabType::Mining => { t!("network.mining") } - NetworkTabType::Settings => { t!("network.settings") } + NodeTabType::Info => { t!("network.node") } + NodeTabType::Metrics => { t!("network.metrics") } + NodeTabType::Mining => { t!("network.mining") } + NodeTabType::Settings => { t!("network.settings") } } } } \ No newline at end of file diff --git a/src/gui/views/pull_to_refresh.rs b/src/gui/views/pull_to_refresh.rs index 6a2c2c3..ed545df 100644 --- a/src/gui/views/pull_to_refresh.rs +++ b/src/gui/views/pull_to_refresh.rs @@ -13,7 +13,7 @@ // limitations under the License. use egui::scroll_area::ScrollAreaOutput; -use egui::{Sense, Align2, Area, Color32, Id, Rect, Response, Widget, Vec2}; +use egui::{Sense, Align2, Area, Color32, Id, Rect, Response, Widget, Vec2, UiBuilder}; use egui::epaint::{emath::lerp, vec2, Pos2, Shape, Stroke}; /// A spinner widget used to indicate loading. @@ -195,7 +195,9 @@ impl PullToRefresh { ui: &mut egui::Ui, content: impl FnOnce(&mut egui::Ui) -> T, ) -> PullToRefreshResponse { - let mut child = ui.child_ui(ui.available_rect_before_wrap(), *ui.layout(), None); + let mut child = ui.new_child(UiBuilder::new() + .max_rect(ui.available_rect_before_wrap()) + .layout(*ui.layout())); let output = content(&mut child); diff --git a/src/gui/views/qr.rs b/src/gui/views/qr.rs index a0bf1a4..68ed708 100644 --- a/src/gui/views/qr.rs +++ b/src/gui/views/qr.rs @@ -16,7 +16,7 @@ use std::mem::size_of; use std::sync::Arc; use parking_lot::RwLock; use std::thread; -use egui::{SizeHint, TextureHandle}; +use egui::{SizeHint, TextureHandle, UiBuilder}; use egui::epaint::RectShape; use image::{ExtendedColorType, ImageEncoder}; use image::codecs::png::{CompressionType, FilterType, PngEncoder}; @@ -235,26 +235,23 @@ impl QrCodeContent { rect.max -= egui::emath::vec2(10.0, 0.0); // Create background shape. - let mut bg_shape = RectShape { + let mut bg_shape = RectShape::new( rect, - rounding: egui::Rounding::default(), - fill: egui::Color32::WHITE, - stroke: egui::Stroke::NONE, - blur_width: 0.0, - fill_texture_id: Default::default(), - uv: egui::Rect::ZERO - }; + egui::Rounding::default(), + egui::Color32::WHITE, + egui::Stroke::NONE + ); let bg_idx = ui.painter().add(bg_shape); // Draw QR code image content. - let mut content_rect = ui.allocate_ui_at_rect(rect, |ui| { + let mut content_rect = ui.allocate_new_ui(UiBuilder::new().max_rect(rect), |ui| { ui.add_space(10.0); let size = SizeHint::Size(ui.available_width() as u32, ui.available_width() as u32); self.texture_handle = Some(View::svg_image(ui, "qr_code", svg.as_slice(), Some(size))); ui.add_space(10.0); }).response.rect; - // Setup background shape to be painted behind content. + // Setup background size. content_rect.min -= egui::emath::vec2(10.0, 0.0); content_rect.max += egui::emath::vec2(10.0, 0.0); bg_shape.rect = content_rect; diff --git a/src/gui/views/scan.rs b/src/gui/views/scan.rs index 4431e59..76218ad 100644 --- a/src/gui/views/scan.rs +++ b/src/gui/views/scan.rs @@ -32,7 +32,7 @@ pub struct CameraScanModal { impl Default for CameraScanModal { fn default() -> Self { Self { - camera_content: None, + camera_content: Some(CameraContent::default()), qr_scan_result: None, } } @@ -51,7 +51,7 @@ impl CameraScanModal { View::horizontal_line(ui, Colors::item_stroke()); ui.add_space(3.0); ScrollArea::vertical() - .id_source(Id::from("qr_scan_result_input")) + .id_salt(Id::from("qr_scan_result_input")) .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .max_height(128.0) .auto_shrink([false; 2]) @@ -72,7 +72,7 @@ impl CameraScanModal { // Show copy button. ui.vertical_centered(|ui| { let copy_text = format!("{} {}", COPY, t!("copy")); - View::button(ui, copy_text, Colors::button(), || { + View::button(ui, copy_text, Colors::white_or_black(false), || { cb.copy_string_to_buffer(result_text.to_string()); self.qr_scan_result = None; modal.close(); @@ -81,22 +81,7 @@ impl CameraScanModal { ui.add_space(10.0); View::horizontal_line(ui, Colors::item_stroke()); ui.add_space(6.0); - } else if let Some(result) = self.camera_content.get_or_insert(CameraContent::default()) - .qr_scan_result() { - cb.stop_camera(); - self.camera_content = None; - on_result(&result); - // Set result and rename modal title. - self.qr_scan_result = Some(result); - Modal::set_title(t!("scan_result")); - } else { - ui.add_space(6.0); - self.camera_content.as_mut().unwrap().ui(ui, cb); - ui.add_space(6.0); - } - - if self.qr_scan_result.is_some() { // Setup spacing between buttons. ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); @@ -117,14 +102,28 @@ impl CameraScanModal { }); }); }); - } else { - ui.vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { - cb.stop_camera(); - self.camera_content = None; - modal.close(); + } else if let Some(camera_content) = self.camera_content.as_mut() { + if let Some(result) = camera_content.qr_scan_result() { + cb.stop_camera(); + self.camera_content = None; + on_result(&result); + + // Set result and rename modal title. + self.qr_scan_result = Some(result); + Modal::set_title(t!("scan_result")); + } else { + // Draw camera content. + ui.add_space(6.0); + self.camera_content.as_mut().unwrap().ui(ui, cb); + ui.add_space(12.0); + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { + cb.stop_camera(); + self.camera_content = None; + modal.close(); + }); }); - }); + } } ui.add_space(6.0); } diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index 3a83ef4..6e0eb95 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Margin, Id, Layout, Align}; +use egui::{Margin, Id, Layout, Align, UiBuilder}; use crate::gui::Colors; use crate::gui::views::{Content, View}; -use crate::gui::views::types::{TitleContentType, TitleType}; +use crate::gui::views::types::{LinePosition, TitleContentType, TitleType}; /// Title panel with left/right action buttons and text in the middle. pub struct TitlePanel { @@ -25,8 +25,8 @@ pub struct TitlePanel { } impl TitlePanel { - /// Default [`TitlePanel`] content height. - pub const DEFAULT_HEIGHT: f32 = 54.0; + /// Content height. + pub const HEIGHT: f32 = 54.0; /// Create new title panel with provided identifier. pub fn new(id: Id) -> Self { @@ -43,7 +43,7 @@ impl TitlePanel { // Draw title panel. egui::TopBottomPanel::top(self.id) .resizable(false) - .exact_height(Self::DEFAULT_HEIGHT + View::get_top_inset()) + .exact_height(Self::HEIGHT + View::get_top_inset()) .frame(egui::Frame { inner_margin: Margin { left: View::far_left_inset_margin(ui), @@ -51,7 +51,6 @@ impl TitlePanel { top: View::get_top_inset(), bottom: 0.0, }, - fill: Colors::yellow(), ..Default::default() }) .show_inside(ui, |ui| { @@ -68,40 +67,51 @@ impl TitlePanel { match title { TitleType::Single(content) => { let content_rect = { - let mut r = rect; - r.min.x += Self::DEFAULT_HEIGHT; - r.max.x -= Self::DEFAULT_HEIGHT; + let mut r = rect.clone(); + r.min.x += Self::HEIGHT; + r.max.x -= Self::HEIGHT; r }; - ui.allocate_ui_at_rect(content_rect, |ui| { + ui.allocate_new_ui(UiBuilder::new().max_rect(content_rect), |ui| { Self::title_text_content(ui, content); }); } TitleType::Dual(first, second) => { let first_rect = { - let mut r = rect; - r.max.x = r.min.x + Content::SIDE_PANEL_WIDTH - Self::DEFAULT_HEIGHT; - r.min.x += Self::DEFAULT_HEIGHT; + let mut r = rect.clone(); + r.max.x = r.min.x + Content::SIDE_PANEL_WIDTH - Self::HEIGHT; + r.min.x += Self::HEIGHT; r }; // Draw first title content. - ui.allocate_ui_at_rect(first_rect, |ui| { + ui.allocate_new_ui(UiBuilder::new().max_rect(first_rect), |ui| { Self::title_text_content(ui, first); }); let second_rect = { - let mut r = rect; - r.min.x = first_rect.max.x + 2.0 * Self::DEFAULT_HEIGHT; - r.max.x -= Self::DEFAULT_HEIGHT; + let mut r = rect.clone(); + r.min.x = first_rect.max.x + 2.0 * Self::HEIGHT; + r.max.x -= Self::HEIGHT; r }; // Draw second title content. - ui.allocate_ui_at_rect(second_rect, |ui| { + ui.allocate_new_ui(UiBuilder::new().max_rect(second_rect), |ui| { Self::title_text_content(ui, second); }); } } }); + + // Draw content divider line. + let r = { + let mut r = rect.clone(); + r.min.x -= View::far_left_inset_margin(ui); + r.max.x += View::far_right_inset_margin(ui); + r + }; + if Content::is_dual_panel_mode(ui.ctx()) { + View::line(ui, LinePosition::BOTTOM, &r, Colors::stroke()); + } }); } @@ -115,11 +125,11 @@ impl TitlePanel { } else { 0.0 }); - View::ellipsize_text(ui, text, 19.0, Colors::title(true)); + View::ellipsize_text(ui, text.to_uppercase(), 19.0, Colors::title(true)); } TitleContentType::WithSubTitle(text, subtitle, animate) => { ui.add_space(4.0); - View::ellipsize_text(ui, text, 18.0, Colors::title(true)); + View::ellipsize_text(ui, text.to_uppercase(), 18.0, Colors::title(true)); ui.add_space(-2.0); View::animate_text(ui, subtitle, 15.0, Colors::text(true), animate) } diff --git a/src/gui/views/types.rs b/src/gui/views/types.rs index 730e6a1..7b4a4b5 100644 --- a/src/gui/views/types.rs +++ b/src/gui/views/types.rs @@ -32,6 +32,11 @@ pub enum TitleContentType { WithSubTitle(String, String, bool) } +/// Stroke position against content. +pub enum LinePosition { + TOP, LEFT, RIGHT, BOTTOM +} + /// Position of [`Modal`] on the screen. #[derive(Clone)] pub enum ModalPosition { diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 21b25bd..9bc4b79 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -17,8 +17,8 @@ use std::sync::Arc; use parking_lot::RwLock; use lazy_static::lazy_static; -use egui::{Align, Button, CursorIcon, Layout, lerp, PointerState, Rect, Response, Rgba, RichText, Sense, SizeHint, Spinner, TextBuffer, TextStyle, TextureHandle, TextureOptions, Widget}; -use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke}; +use egui::{Align, Button, CursorIcon, Layout, lerp, PointerState, Rect, Response, Rgba, RichText, Sense, SizeHint, Spinner, TextBuffer, TextStyle, TextureHandle, TextureOptions, Widget, UiBuilder}; +use egui::epaint::{Color32, FontId, PathShape, PathStroke, RectShape, Rounding, Stroke}; use egui::epaint::text::TextWrapping; use egui::load::SizedTexture; use egui::os::OperatingSystem; @@ -30,7 +30,7 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{CHECK_SQUARE, CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SCAN, SQUARE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::types::TextEditOptions; +use crate::gui::views::types::{LinePosition, TextEditOptions}; pub struct View; @@ -78,14 +78,16 @@ impl View { rect.set_width(width); // Draw content. - ui.allocate_ui(rect.size(), |ui| { - (add_content)(ui); + ui.vertical_centered(|ui| { + ui.allocate_ui(rect.size(), |ui| { + (add_content)(ui); + }); }); } /// Get width and height of app window. - pub fn window_size(ui: &egui::Ui) -> (f32, f32) { - let rect = ui.ctx().screen_rect(); + pub fn window_size(ctx: &egui::Context) -> (f32, f32) { + let rect = ctx.screen_rect(); (rect.width(), rect.height()) } @@ -108,7 +110,7 @@ impl View { /// Calculate margin for far left view based on display insets (cutouts). pub fn far_right_inset_margin(ui: &mut egui::Ui) -> f32 { let container_width = ui.available_rect_before_wrap().max.x as i32; - let window_size = Self::window_size(ui); + let window_size = Self::window_size(ui.ctx()); let display_width = window_size.0 as i32; // Means end of the screen. if container_width == display_width { @@ -235,7 +237,7 @@ impl View { ui.style_mut().visuals.widgets.active.expansion = 0.0; // Setup fill colors. ui.visuals_mut().widgets.inactive.weak_bg_fill = Colors::white_or_black(false); - ui.visuals_mut().widgets.hovered.weak_bg_fill = Colors::button(); + ui.visuals_mut().widgets.hovered.weak_bg_fill = Colors::fill_lite(); ui.visuals_mut().widgets.active.weak_bg_fill = Colors::fill(); // Setup stroke colors. ui.visuals_mut().widgets.inactive.bg_stroke = Self::default_stroke(); @@ -325,7 +327,7 @@ impl View { action: impl FnOnce()) { // Setup button size. let mut rect = ui.available_rect_before_wrap(); - rect.set_width(32.0); + rect.set_width(42.0); let button_size = rect.size(); ui.scope(|ui| { @@ -336,12 +338,12 @@ impl View { ui.style_mut().visuals.widgets.active.expansion = 0.0; // Setup fill colors. ui.visuals_mut().widgets.inactive.weak_bg_fill = Colors::white_or_black(false); - ui.visuals_mut().widgets.hovered.weak_bg_fill = Colors::button(); + ui.visuals_mut().widgets.hovered.weak_bg_fill = Colors::fill_lite(); ui.visuals_mut().widgets.active.weak_bg_fill = Colors::fill(); - // Setup stroke colors. - ui.visuals_mut().widgets.inactive.bg_stroke = Self::default_stroke(); - ui.visuals_mut().widgets.hovered.bg_stroke = Self::hover_stroke(); - ui.visuals_mut().widgets.active.bg_stroke = Self::item_stroke(); + // Disable strokes. + ui.visuals_mut().widgets.inactive.bg_stroke = Stroke::NONE; + ui.visuals_mut().widgets.hovered.bg_stroke = Stroke::NONE; + ui.visuals_mut().widgets.active.bg_stroke = Stroke::NONE; // Setup button text color. let text_color = if let Some(c) = color { c } else { Colors::item_button() }; @@ -353,9 +355,18 @@ impl View { .ui(ui) .on_hover_cursor(CursorIcon::PointingHand); br.surrender_focus(); - if Self::touched(ui, br) { + if Self::touched(ui, br.clone()) { (action)(); } + + // Draw stroke. + let r = { + let mut r = ui.available_rect_before_wrap(); + r.min = br.rect.min; + r.min.x += 0.5; + r + }; + Self::line(ui, LinePosition::LEFT, &r, Colors::item_stroke()); }); } @@ -529,28 +540,20 @@ impl View { /// where is r = (top_left, top_right, bottom_left, bottom_right). /// | VALUE | /// | label | - pub fn rounded_box(ui: &mut egui::Ui, value: String, label: String, r: [bool; 4]) { + pub fn label_box(ui: &mut egui::Ui, text: String, label: String, r: [bool; 4]) { let rect = ui.available_rect_before_wrap(); // Create background shape. - let mut bg_shape = RectShape { - rect, - rounding: Rounding { - nw: if r[0] { 8.0 } else { 0.0 }, - ne: if r[1] { 8.0 } else { 0.0 }, - sw: if r[2] { 8.0 } else { 0.0 }, - se: if r[3] { 8.0 } else { 0.0 }, - }, - fill: Colors::TRANSPARENT, - stroke: Self::item_stroke(), - blur_width: 0.0, - fill_texture_id: Default::default(), - uv: Rect::ZERO - }; + let mut bg_shape = RectShape::new(rect, Rounding { + nw: if r[0] { 8.0 } else { 0.0 }, + ne: if r[1] { 8.0 } else { 0.0 }, + sw: if r[2] { 8.0 } else { 0.0 }, + se: if r[3] { 8.0 } else { 0.0 }, + }, Colors::fill_lite(), Self::item_stroke()); let bg_idx = ui.painter().add(bg_shape); // Draw box content. - let content_resp = ui.allocate_ui_at_rect(rect, |ui| { + let content_resp = ui.allocate_new_ui(UiBuilder::new().max_rect(rect), |ui| { ui.vertical_centered_justified(|ui| { ui.add_space(4.0); ui.scope(|ui| { @@ -558,7 +561,7 @@ impl View { ui.style_mut().spacing.item_spacing.y = -3.0; // Draw box value. - let mut job = LayoutJob::single_section(value, TextFormat { + let mut job = LayoutJob::single_section(text, TextFormat { font_id: FontId::proportional(17.0), color: Colors::white_or_black(true), ..Default::default() @@ -578,7 +581,7 @@ impl View { }); }).response; - // Setup background shape to be painted behind box content. + // Setup background shape size. bg_shape.rect = content_resp.rect; ui.painter().set(bg_idx, bg_shape); } @@ -590,7 +593,7 @@ impl View { let side_margin = 28.0; rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - height / 2.0); rect.max -= egui::emath::vec2(side_margin, 0.0); - ui.allocate_ui_at_rect(rect, |ui| { + ui.allocate_new_ui(UiBuilder::new().max_rect(rect), |ui| { (content)(ui); }); }); @@ -653,6 +656,47 @@ impl View { Stroke { width: 1.0, color }); } + /// Draw line for panel content. + pub fn line(ui: &mut egui::Ui, pos: LinePosition, rect: &Rect, color: Color32) { + let points = match pos { + LinePosition::RIGHT => { + vec![{ + let mut r = rect.clone(); + r.min.x = r.max.x; + r.min + }, rect.max] + } + LinePosition::BOTTOM => { + vec![{ + let mut r = rect.clone(); + r.min.y = r.max.y; + r.min + }, rect.max] + } + LinePosition::LEFT => { + vec![rect.min, { + let mut r = rect.clone(); + r.max.x = r.min.x; + r.max + }] + } + LinePosition::TOP => { + vec![rect.min, { + let mut r = rect.clone(); + r.max.y = r.min.y; + r.max + }] + } + }; + let stroke = PathShape { + points, + closed: false, + fill: Default::default(), + stroke: PathStroke::new(1.0, color), + }; + ui.painter().add(stroke); + } + /// Draw SVG image from provided data with optional provided size. pub fn svg_image(ui: &mut egui::Ui, name: &str, @@ -698,6 +742,19 @@ impl View { ); } + /// Draw semi-transparent cover at specified area. + pub fn content_cover_ui(ui: &mut egui::Ui, + rect: Rect, + id: impl std::hash::Hash, + mut on_click: impl FnMut()) { + let resp = ui.interact(rect, egui::Id::new(id), Sense::click_and_drag()); + if resp.clicked() || resp.dragged() { + on_click(); + } + let shape = RectShape::filled(resp.rect, Rounding::ZERO, Colors::semi_transparent()); + ui.painter().add(shape); + } + /// Get top display inset (cutout) size. pub fn get_top_inset() -> f32 { TOP_DISPLAY_INSET.load(Ordering::Relaxed) as f32 diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index b94a83e..e832f1d 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -21,7 +21,7 @@ use crate::gui::Colors; use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, FOLDER_OPEN, FOLDER_PLUS, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SUITCASE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, TitlePanel, View}; -use crate::gui::views::types::{ModalContainer, ModalPosition, TitleContentType, TitleType}; +use crate::gui::views::types::{ModalContainer, ModalPosition, LinePosition, TitleContentType, TitleType}; use crate::gui::views::wallets::creation::WalletCreation; use crate::gui::views::wallets::modals::{AddWalletModal, OpenWalletModal, WalletConnectionModal, WalletsModal}; use crate::gui::views::wallets::types::WalletTabType; @@ -149,14 +149,15 @@ impl WalletsContent { let creating_wallet = self.creating_wallet(); let showing_wallet = self.showing_wallet() && !creating_wallet; - let dual_panel = is_dual_panel_mode(ui); + let dual_panel = Self::is_dual_panel_mode(ui); let content_width = ui.available_width(); let list_hidden = creating_wallet || self.wallets.list().is_empty() + || (showing_wallet && self.wallet_content.as_ref().unwrap().qr_scan_content.is_some()) || (dual_panel && showing_wallet && !self.show_wallets_at_dual_panel) || (!dual_panel && showing_wallet); // Show title panel. - self.title_ui(ui, dual_panel, showing_wallet); + self.title_ui(ui, dual_panel, showing_wallet, cb); if showing_wallet { egui::SidePanel::right("wallet_panel") @@ -167,7 +168,6 @@ impl WalletsContent { content_width - Content::SIDE_PANEL_WIDTH }) .frame(egui::Frame { - fill: Colors::fill_deep(), ..Default::default() }) .show_inside(ui, |ui| { @@ -181,17 +181,19 @@ impl WalletsContent { if !list_hidden { egui::TopBottomPanel::bottom("wallets_bottom_panel") .frame(egui::Frame { - fill: Colors::fill(), inner_margin: Margin { left: View::far_left_inset_margin(ui) + View::TAB_ITEMS_PADDING, right: View::far_right_inset_margin(ui) + View::TAB_ITEMS_PADDING, top: View::TAB_ITEMS_PADDING, bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, + fill: Colors::fill(), ..Default::default() }) .resizable(false) .show_inside(ui, |ui| { + let rect = ui.available_rect_before_wrap(); + // Setup spacing between tabs. ui.style_mut().spacing.item_spacing = egui::vec2(View::TAB_ITEMS_PADDING, 0.0); // Setup vertical padding inside buttons. @@ -203,6 +205,16 @@ impl WalletsContent { self.show_add_wallet_modal(cb); }); }); + + // Draw content divider line. + let r = { + let mut r = rect.clone(); + r.min.y -= View::TAB_ITEMS_PADDING; + r.min.x -= View::TAB_ITEMS_PADDING; + r.max.x += View::TAB_ITEMS_PADDING; + r + }; + View::line(ui, LinePosition::TOP, &r, Colors::stroke()); }); egui::SidePanel::left("wallet_list_panel") @@ -213,18 +225,17 @@ impl WalletsContent { }) .resizable(false) .frame(egui::Frame { - stroke: View::item_stroke(), - fill: Colors::fill_deep(), inner_margin: Margin { left: View::far_left_inset_margin(ui) + 4.0, right: View::far_right_inset_margin(ui) + 4.0, top: 3.0, bottom: 4.0, }, + fill: Colors::fill_deep(), ..Default::default() }) .show_inside(ui, |ui| { - if !list_hidden && !dual_panel && !showing_wallet && !creating_wallet { + if !dual_panel && !showing_wallet { ui.ctx().request_repaint_after(Duration::from_millis(1000)); } // Show wallet list. @@ -232,12 +243,10 @@ impl WalletsContent { }); } - // Show central panel with wallet creation. egui::CentralPanel::default() .frame(egui::Frame { - stroke: View::item_stroke(), - fill: if self.creation_content.is_some() { - Colors::white_or_black(false) + fill: if creating_wallet { + Colors::TRANSPARENT } else { Colors::fill_deep() }, @@ -276,6 +285,8 @@ impl WalletsContent { self.show_add_wallet_modal(cb); }); }); + } else { + return; } }); } @@ -284,7 +295,8 @@ impl WalletsContent { pub fn showing_wallet(&self) -> bool { if let Some(wallet_content) = &self.wallet_content { let w = &wallet_content.wallet; - return w.is_open() && w.get_config().chain_type == AppConfig::chain_type(); + return w.is_open() && !w.is_deleted() && + w.get_config().chain_type == AppConfig::chain_type(); } false } @@ -301,7 +313,7 @@ impl WalletsContent { return; } // Close network panel on single panel mode. - if !Content::is_dual_panel_mode(ui) && Content::is_network_panel_open() { + if !Content::is_dual_panel_mode(ui.ctx()) && Content::is_network_panel_open() { Content::toggle_network_panel(); } // Pass data to single wallet or show wallets selection. @@ -337,19 +349,26 @@ impl WalletsContent { } /// Handle Back key event returning `false` when event was handled. - pub fn on_back(&mut self) -> bool { + pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool { if self.creation_content.is_some() { // Close wallet creation. let creation = self.creation_content.as_mut().unwrap(); if creation.on_back() { self.creation_content = None; } - return false + return false; } else { - // Close opened wallet. if self.showing_wallet() { + let content = self.wallet_content.as_mut().unwrap(); + // Close opened QR code scanner. + if content.qr_scan_content.is_some() { + cb.stop_camera(); + content.qr_scan_content = None; + return false; + } + // Close opened wallet. self.wallet_content = None; - return false + return false; } } true @@ -359,15 +378,27 @@ impl WalletsContent { fn title_ui(&mut self, ui: &mut egui::Ui, dual_panel: bool, - show_wallet: bool) { + show_wallet: bool, + cb: &dyn PlatformCallbacks) { let show_list = self.show_wallets_at_dual_panel; let creating_wallet = self.creating_wallet(); + let qr_scan = { + let mut scan = false; + if show_wallet { + scan = self.wallet_content.as_mut().unwrap().qr_scan_content.is_some(); + } + scan + }; // Setup title. let title_content = if show_wallet && (!dual_panel || (dual_panel && !show_list)) && !creating_wallet { let wallet_content = self.wallet_content.as_ref().unwrap(); let wallet_tab_type = wallet_content.current_tab.get_type(); - let title_text = wallet_tab_type.name().to_uppercase(); + let title_text = if qr_scan { + t!("scan_qr") + } else { + wallet_tab_type.name() + }; if wallet_tab_type == WalletTabType::Settings { TitleType::Single(TitleContentType::Title(title_text)) } else { @@ -375,16 +406,18 @@ impl WalletsContent { TitleType::Single(TitleContentType::WithSubTitle(title_text, subtitle_text, false)) } } else { - let title_text = if creating_wallet { + let title_text = if qr_scan { + t!("scan_qr") + } else if creating_wallet { t!("wallets.add") } else { t!("wallets.title") - }.to_uppercase(); - let dual_title = !creating_wallet && show_wallet && dual_panel; + }; + let dual_title = !qr_scan && !creating_wallet && show_wallet && dual_panel; if dual_title { let wallet_content = self.wallet_content.as_ref().unwrap(); let wallet_tab_type = wallet_content.current_tab.get_type(); - let wallet_title_text = wallet_tab_type.name().to_uppercase(); + let wallet_title_text = wallet_tab_type.name(); let wallet_title_content = if wallet_tab_type == WalletTabType::Settings { TitleContentType::Title(wallet_title_text) } else { @@ -401,6 +434,16 @@ impl WalletsContent { TitlePanel::new(Id::new("wallets_title_panel")).ui(title_content, |ui| { if show_wallet && !dual_panel { View::title_button_big(ui, ARROW_LEFT, |_| { + let wallet_qr_scan = self.wallet_content + .as_ref() + .unwrap() + .qr_scan_content + .is_some(); + if wallet_qr_scan { + cb.stop_camera(); + self.wallet_content.as_mut().unwrap().qr_scan_content = None; + return; + } self.wallet_content = None; }); } else if self.creation_content.is_some() { @@ -416,16 +459,23 @@ impl WalletsContent { self.creation_content = None; } } else if show_wallet && dual_panel { - let list_icon = if show_list { - SIDEBAR_SIMPLE + if qr_scan { + View::title_button_big(ui, ARROW_LEFT, |_| { + cb.stop_camera(); + self.wallet_content.as_mut().unwrap().qr_scan_content = None; + }); } else { - SUITCASE - }; - View::title_button_big(ui, list_icon, |_| { - self.show_wallets_at_dual_panel = !show_list; - AppConfig::toggle_show_wallets_at_dual_panel(); - }); - } else if !Content::is_dual_panel_mode(ui) { + let list_icon = if show_list { + SIDEBAR_SIMPLE + } else { + SUITCASE + }; + View::title_button_big(ui, list_icon, |_| { + self.show_wallets_at_dual_panel = !show_list; + AppConfig::toggle_show_wallets_at_dual_panel(); + }); + } + } else if !Content::is_dual_panel_mode(ui.ctx()) { View::title_button_big(ui, GLOBE, |_| { Content::toggle_network_panel(); }); @@ -446,38 +496,32 @@ impl WalletsContent { ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ScrollArea::vertical() - .id_source("wallet_list") + .id_salt("wallet_list_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - // Show application logo and name. - View::app_logo_name_version(ui); - ui.add_space(15.0); + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Show application logo and name. + View::app_logo_name_version(ui); + ui.add_space(15.0); - let mut list = self.wallets.list().clone(); - // Remove deleted wallet from the list. - list.retain(|w| { - let deleted = w.is_deleted(); - if deleted { - self.wallet_content = None; - self.wallets.remove(w.get_config().id); - ui.ctx().request_repaint(); - } - !deleted - }); - for wallet in &list { - // Check if wallet reopen is needed. - if wallet.reopen_needed() && !wallet.is_open() { - wallet.set_reopen(false); - self.show_opening_modal(wallet.clone(), None, cb); - } - // Draw wallet list item. - self.wallet_item_ui(ui, wallet, cb); - ui.add_space(5.0); + let list = self.wallets.list().clone(); + for w in &list { + // Remove deleted. + if w.is_deleted() { + self.wallet_content = None; + self.wallets.remove(w.get_config().id); + ui.ctx().request_repaint(); + continue; } - }); + // Check if wallet reopen is needed. + if w.reopen_needed() && !w.is_open() { + w.set_reopen(false); + self.show_opening_modal(w.clone(), None, cb); + } + self.wallet_item_ui(ui, w, cb); + ui.add_space(5.0); + } }); }); } @@ -596,11 +640,11 @@ impl WalletsContent { .show(); cb.show_keyboard(); } -} -/// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time. -fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool { - let dual_panel_root = Content::is_dual_panel_mode(ui); - let max_width = ui.available_width(); - dual_panel_root && max_width >= (Content::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset() + /// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time. + fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool { + let dual_panel_root = Content::is_dual_panel_mode(ui.ctx()); + let max_width = ui.available_width(); + dual_panel_root && max_width >= (Content::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset() + } } \ No newline at end of file diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index fd1f898..af90b0c 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -20,7 +20,7 @@ use crate::gui::Colors; use crate::gui::icons::{CHECK, CLIPBOARD_TEXT, COPY, SCAN}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, View, CameraScanModal}; -use crate::gui::views::types::{ModalContainer, ModalPosition, QrScanResult}; +use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition, QrScanResult}; use crate::gui::views::wallets::creation::MnemonicSetup; use crate::gui::views::wallets::creation::types::Step; use crate::gui::views::wallets::ConnectionSettings; @@ -112,27 +112,30 @@ impl WalletCreation { on_create: impl FnMut(Wallet)) { self.current_modal_ui(ui, cb); - // Show wallet creation step description and confirmation panel. egui::TopBottomPanel::bottom("wallet_creation_step_panel") .frame(egui::Frame { - stroke: View::item_stroke(), - fill: Colors::fill_deep(), inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 8.0, - right: View::get_right_inset() + 8.0, - top: 4.0, - bottom: View::get_bottom_inset(), + left: View::far_left_inset_margin(ui) + View::TAB_ITEMS_PADDING, + right: View::get_right_inset() + View::TAB_ITEMS_PADDING, + top: View::TAB_ITEMS_PADDING, + bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, + fill: Colors::fill_deep(), ..Default::default() }) .show_inside(ui, |ui| { - ui.vertical_centered(|ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 2.0, |ui| { - self.step_control_ui(ui, on_create, cb); - }); - }); - + // Draw divider line. + let rect = { + let mut r = ui.available_rect_before_wrap(); + r.min.y -= View::TAB_ITEMS_PADDING; + r.min.x -= View::far_left_inset_margin(ui) + View::TAB_ITEMS_PADDING; + r.max.x += View::get_right_inset() + View::TAB_ITEMS_PADDING; + r + }; + View::line(ui, LinePosition::TOP, &rect, Colors::item_stroke()); + // Show step control content. + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.step_control_ui(ui, on_create, cb); }); }); @@ -149,19 +152,17 @@ impl WalletCreation { }) .show_inside(ui, |ui| { ScrollArea::vertical() - .id_source(Id::from(format!("creation_step_scroll_{}", self.step.name()))) + .id_salt(Id::from(format!("creation_step_scroll_{}", self.step.name()))) .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show(ui, |ui| { - ui.vertical_centered(|ui| { - let max_width = if self.step == Step::SetupConnection { - Content::SIDE_PANEL_WIDTH * 1.3 - } else { - Content::SIDE_PANEL_WIDTH * 2.0 - }; - View::max_width_ui(ui, max_width, |ui| { - self.step_content_ui(ui, cb); - }); + let max_width = if self.step == Step::SetupConnection { + Content::SIDE_PANEL_WIDTH * 1.3 + } else { + Content::SIDE_PANEL_WIDTH * 2.0 + }; + View::max_width_ui(ui, max_width, |ui| { + self.step_content_ui(ui, cb); }); }); }); @@ -201,9 +202,8 @@ impl WalletCreation { self.mnemonic_setup.mnemonic.mode() == PhraseMode::Generate; if (self.mnemonic_setup.mnemonic.valid() && self.creation_error.is_none()) || generate_step { - ui.add_space(2.0); ui.label(RichText::new(step_text).size(16.0).color(Colors::gray())); - ui.add_space(2.0); + ui.add_space(6.0); } else { next = false; // Show error text. @@ -214,39 +214,35 @@ impl WalletCreation { .color(Colors::red())); ui.add_space(10.0); } else { - ui.add_space(2.0); ui.label(RichText::new(&t!("wallets.not_valid_phrase")) .size(16.0) .color(Colors::red())); - ui.add_space(2.0); + ui.add_space(4.0); }; } - // Setup buttons. + // Setup spacing between buttons. + ui.style_mut().spacing.item_spacing = egui::vec2(8.0, 0.0); + // Setup vertical padding inside button. + ui.style_mut().spacing.button_padding = egui::vec2(10.0, 7.0); + match step { Step::EnterMnemonic => { - ui.add_space(4.0); - - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - ui.columns(2, |columns| { // Show copy or paste button for mnemonic phrase step. columns[0].vertical_centered_justified(|ui| { match self.mnemonic_setup.mnemonic.mode() { PhraseMode::Generate => { - // Show copy button. - let c_t = format!("{} {}", COPY, t!("copy").to_uppercase()); - View::button(ui, - c_t.to_uppercase(), - Colors::white_or_black(false), || { + let c_t = format!("{} {}", + COPY, + t!("copy").to_uppercase()); + View::button(ui, c_t, Colors::white_or_black(false), || { cb.copy_string_to_buffer(self.mnemonic_setup .mnemonic .get_phrase()); }); } PhraseMode::Import => { - // Show paste button. let p_t = format!("{} {}", CLIPBOARD_TEXT, t!("paste").to_uppercase()); @@ -262,7 +258,9 @@ impl WalletCreation { if next { self.next_step_button_ui(ui, on_create); } else { - let scan_text = format!("{} {}", SCAN, t!("scan").to_uppercase()); + let scan_text = format!("{} {}", + SCAN, + t!("scan").to_uppercase()); View::button(ui, scan_text, Colors::white_or_black(false), || { self.scan_modal_content = Some(CameraScanModal::default()); // Show QR code scan modal. @@ -276,10 +274,8 @@ impl WalletCreation { } }); }); - ui.add_space(4.0); } Step::ConfirmMnemonic => { - ui.add_space(4.0); // Show next step or paste button. if next { self.next_step_button_ui(ui, on_create); @@ -290,17 +286,14 @@ impl WalletCreation { self.mnemonic_setup.mnemonic.import(&data); }); } - ui.add_space(4.0); } Step::SetupConnection => { if next { - ui.add_space(4.0); self.next_step_button_ui(ui, on_create); - ui.add_space(4.0); + ui.add_space(2.0); } } } - ui.add_space(3.0); } /// Draw button to go to next [`Step`]. @@ -311,7 +304,7 @@ impl WalletCreation { let (next_text, text_color, bg_color) = if self.step == Step::SetupConnection { (format!("{} {}", CHECK, t!("complete")), Colors::title(true), Colors::gold()) } else { - (t!("continue"), Colors::text_button(), Colors::white_or_black(false)) + (t!("continue"), Colors::green(), Colors::white_or_black(false)) }; // Show next step button. @@ -361,7 +354,7 @@ impl WalletCreation { Step::ConfirmMnemonic => self.mnemonic_setup.confirm_ui(ui, cb), Step::SetupConnection => { // Redraw if node is running. - if Node::is_running() && !Content::is_dual_panel_mode(ui) { + if Node::is_running() && !Content::is_dual_panel_mode(ui.ctx()) { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); } self.network_setup.create_ui(ui, cb) diff --git a/src/gui/views/wallets/creation/mnemonic.rs b/src/gui/views/wallets/creation/mnemonic.rs index abfae80..81a2a2d 100644 --- a/src/gui/views/wallets/creation/mnemonic.rs +++ b/src/gui/views/wallets/creation/mnemonic.rs @@ -216,7 +216,7 @@ impl MnemonicSetup { }; if edit { ui.add_space(6.0); - View::button(ui, PENCIL.to_string(), Colors::button(), || { + View::button(ui, PENCIL.to_string(), Colors::white_or_black(false), || { self.word_index_edit = num - 1; self.word_edit = word.text.clone(); self.valid_word_edit = word.valid; diff --git a/src/gui/views/wallets/modals/conn.rs b/src/gui/views/wallets/modals/conn.rs index 138b263..ad0dae5 100644 --- a/src/gui/views/wallets/modals/conn.rs +++ b/src/gui/views/wallets/modals/conn.rs @@ -65,7 +65,7 @@ impl WalletConnectionModal { } else { 350.0 }) - .id_source("integrated_node") + .id_salt("connections_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([true; 2]) .show(ui, |ui| { @@ -96,7 +96,7 @@ impl WalletConnectionModal { ui.add_space(6.0); // Show button to add new external node connection. let add_node_text = format!("{} {}", PLUS_CIRCLE, t!("wallets.add_node")); - View::button(ui, add_node_text, Colors::button(), || { + View::button(ui, add_node_text, Colors::white_or_black(false), || { self.ext_conn_content = Some(ExternalConnectionModal::new(None)); }); }); @@ -137,7 +137,7 @@ impl WalletConnectionModal { }); ui.add_space(2.0); - View::horizontal_line(ui, Colors::stroke()); + View::horizontal_line(ui, Colors::item_stroke()); ui.add_space(6.0); // Show button to close modal. diff --git a/src/gui/views/wallets/modals/wallets.rs b/src/gui/views/wallets/modals/wallets.rs index f76a8f5..bb1002c 100644 --- a/src/gui/views/wallets/modals/wallets.rs +++ b/src/gui/views/wallets/modals/wallets.rs @@ -64,7 +64,7 @@ impl WalletsModal { ui.add_space(4.0); ScrollArea::vertical() .max_height(373.0) - .id_source("select_wallet_list") + .id_salt("select_wallet_list_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([true; 2]) .show(ui, |ui| { @@ -83,7 +83,7 @@ impl WalletsModal { }); ui.add_space(2.0); - View::horizontal_line(ui, Colors::stroke()); + View::horizontal_line(ui, Colors::item_stroke()); ui.add_space(6.0); // Show button to close modal. diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 086a630..9c49ca3 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -13,16 +13,17 @@ // limitations under the License. use std::time::Duration; -use egui::{Align, Id, Layout, Margin, RichText}; +use egui::{Align, Id, Layout, Margin, RichText, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; use grin_chain::SyncStatus; use grin_core::core::amount_to_hr_string; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CHAT_CIRCLE_TEXT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, POWER, SCAN, SPINNER, USERS_THREE}; +use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CAMERA_ROTATE, CHAT_CIRCLE_TEXT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, POWER, SCAN, SPINNER, USERS_THREE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, Content, View, CameraScanModal}; -use crate::gui::views::types::{ModalPosition, QrScanResult}; +use crate::gui::views::{Modal, Content, View, CameraContent}; +use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition}; use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport}; use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::modals::WalletAccountsModal; @@ -35,21 +36,40 @@ use crate::wallet::types::{ConnectionMethod, WalletData}; pub struct WalletContent { /// Selected and opened wallet. pub wallet: Wallet, + /// Current tab content to show. + pub current_tab: Box, /// Wallet accounts [`Modal`] content. accounts_modal_content: Option, - /// QR code scan [`Modal`] content. - scan_modal_content: Option, - /// Current tab content to show. - pub current_tab: Box, + /// QR code scan content. + pub qr_scan_content: Option, + + /// List of allowed [`Modal`] ids for this [`ModalContainer`]. + allowed_modal_ids: Vec<&'static str> } /// Identifier for account list [`Modal`]. const ACCOUNT_LIST_MODAL: &'static str = "account_list_modal"; -/// Identifier for QR code scan [`Modal`]. -const QR_CODE_SCAN_MODAL: &'static str = "qr_code_scan_modal"; +impl ModalContainer for WalletContent { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.allowed_modal_ids + } + + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + match modal.id { + ACCOUNT_LIST_MODAL => { + if let Some(content) = self.accounts_modal_content.as_mut() { + Modal::ui(ui.ctx(), |ui, modal| { + content.ui(ui, &self.wallet, modal, cb); + }); + } + } + _ => {} + } + } +} impl WalletContent { /// Create new instance with optional data. @@ -57,8 +77,11 @@ impl WalletContent { let mut content = Self { wallet, accounts_modal_content: None, - scan_modal_content: None, + qr_scan_content: None, current_tab: Box::new(WalletTransactions::default()), + allowed_modal_ids: vec![ + ACCOUNT_LIST_MODAL, + ], }; if data.is_some() { content.on_data(data); @@ -68,114 +91,162 @@ impl WalletContent { /// Handle data from deeplink or opened file. pub fn on_data(&mut self, data: Option) { - // Provide data to messages. self.current_tab = Box::new(WalletMessages::new(data)); } /// Draw wallet content. pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - self.modal_content_ui(ui, cb); + ui.ctx().request_repaint_after(Duration::from_millis(1000)); + self.current_modal_ui(ui, cb); - let dual_panel = Content::is_dual_panel_mode(ui); + let dual_panel = Content::is_dual_panel_mode(ui.ctx()); + let show_wallets_dual = AppConfig::show_wallets_at_dual_panel(); let wallet = &self.wallet; + let wallet_id = wallet.identifier(); let data = wallet.get_data(); - let data_empty = data.is_none(); + let show_qr_scan = self.qr_scan_content.is_some(); let hide_tabs = Self::block_navigation_on_sync(wallet); - // Show wallet balance panel not on Settings tab with selected non-repairing - // wallet, when there is no error and data is not empty. - let mut show_balance = self.current_tab.get_type() != WalletTabType::Settings && !data_empty - && !wallet.sync_error() && !wallet.is_repairing() && !wallet.is_closing(); + // Show wallet account panel not on settings tab when navigation is not blocked and QR code + // scanner is not showing and wallet data is not empty. + let mut show_account = self.current_tab.get_type() != WalletTabType::Settings && !hide_tabs + && !wallet.sync_error() && data.is_some(); if wallet.get_current_connection() == ConnectionMethod::Integrated && !Node::is_running() { - show_balance = false; + show_account = false; } - egui::TopBottomPanel::top(Id::from("wallet_balance").with(wallet.identifier())) + // Close scanner when balance got hidden. + if !show_account && show_qr_scan { + cb.stop_camera(); + self.qr_scan_content = None; + } + egui::TopBottomPanel::top(Id::from("wallet_account").with(wallet.identifier())) .frame(egui::Frame { - fill: Colors::fill(), - stroke: View::item_stroke(), inner_margin: Margin { left: View::far_left_inset_margin(ui) + 4.0, right: View::get_right_inset() + 4.0, top: 4.0, bottom: 0.0, }, - outer_margin: Margin { - left: if dual_panel { - -0.5 - } else { - 0.0 - }, - right: 0.0, - top: 0.0, - bottom: if dual_panel { - -1.0 - } else { - -0.5 - }, - }, + fill: Colors::fill(), ..Default::default() }) - .show_animated_inside(ui, show_balance, |ui| { - ui.vertical_centered(|ui| { - if !dual_panel { - ui.add_space(1.0); - } - // Draw account info. + .show_animated_inside(ui, show_account, |ui| { + let rect = ui.available_rect_before_wrap(); + if show_qr_scan { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH, |ui| { + self.qr_scan_content.as_mut().unwrap().ui(ui, cb); + ui.add_space(6.0); + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("close"), Colors::white_or_black(false), || { + cb.stop_camera(); + self.qr_scan_content = None; + }); + }); + ui.add_space(6.0); + }); + } else { View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { self.account_ui(ui, data.unwrap(), cb); }); - }); + } + // Draw content divider lines. + let r = { + let mut r = rect.clone(); + r.min.x -= 4.0 + View::far_left_inset_margin(ui); + r.min.y -= 4.0; + r.max.x += 4.0 + View::get_right_inset(); + r + }; + View::line(ui, LinePosition::BOTTOM, &r, Colors::item_stroke()); + if dual_panel && show_wallets_dual { + View::line(ui, LinePosition::LEFT, &r, Colors::item_stroke()); + } }); - // Show wallet tabs panel. - egui::TopBottomPanel::bottom("wallet_tabs_content") + // Show wallet tabs. + let show_tabs = !hide_tabs && self.qr_scan_content.is_none(); + egui::TopBottomPanel::bottom("wallet_tabs") .frame(egui::Frame { - fill: Colors::fill(), inner_margin: Margin { left: View::far_left_inset_margin(ui) + View::TAB_ITEMS_PADDING, right: View::get_right_inset() + View::TAB_ITEMS_PADDING, top: View::TAB_ITEMS_PADDING, bottom: View::get_bottom_inset() + View::TAB_ITEMS_PADDING, }, + fill: Colors::fill(), ..Default::default() }) - .show_animated_inside(ui, !hide_tabs, |ui| { - ui.vertical_centered(|ui| { - // Draw wallet tabs. - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - self.tabs_ui(ui); - }); + .show_animated_inside(ui, show_tabs, |ui| { + let rect = ui.available_rect_before_wrap(); + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.tabs_ui(ui, cb); }); + let rect = { + let mut r = rect.clone(); + r.min.x -= View::far_left_inset_margin(ui) + View::TAB_ITEMS_PADDING; + r.min.y -= View::TAB_ITEMS_PADDING; + r.max.x += View::get_right_inset() + View::TAB_ITEMS_PADDING; + r.max.y += View::get_bottom_inset() + View::TAB_ITEMS_PADDING; + r + }; + // Draw content divider line. + View::line(ui, LinePosition::TOP, &rect, Colors::stroke()); }); - // Show tab content panel. + // Show tab content. egui::CentralPanel::default() .frame(egui::Frame { - outer_margin: Margin { - left: if dual_panel { - -0.5 - } else { - 0.0 - }, - right: 0.0, + inner_margin: Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::get_right_inset() + 4.0, top: 0.0, - bottom: 0.0, + bottom: 4.0, }, - stroke: View::item_stroke(), - fill: Colors::white_or_black(false), ..Default::default() }) .show_inside(ui, |ui| { - self.current_tab.ui(ui, &self.wallet, cb); + let rect = ui.available_rect_before_wrap(); + let tab_type = self.current_tab.get_type(); + let show_sync = (tab_type != WalletTabType::Settings || hide_tabs) && + sync_ui(ui, &self.wallet); + if !show_sync { + if tab_type != WalletTabType::Txs { + ui.add_space(3.0); + ScrollArea::vertical() + .id_salt(Id::from("wallet_scroll") + .with(tab_type.name()) + .with(wallet_id)) + .auto_shrink([false; 2]) + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .show(ui, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.current_tab.ui(ui, &self.wallet, cb); + }); + }); + } else { + self.current_tab.ui(ui, &self.wallet, cb); + } + } + let rect = { + let mut r = rect.clone(); + r.min.x -= View::far_left_inset_margin(ui) + 4.0; + r.max.x += View::get_right_inset() + 4.0; + r.max.y += 4.0; + r + }; + // Draw cover when QR code scanner is active. + if show_qr_scan { + View::content_cover_ui(ui, rect, "wallet_tab", || { + cb.stop_camera(); + self.qr_scan_content = None; + }); + } + // Draw content divider line. + if dual_panel && show_wallets_dual { + View::line(ui, LinePosition::LEFT, &rect, Colors::item_stroke()); + } }); - - // Refresh content after 1 second for synced wallet. - if !data_empty { - ui.ctx().request_repaint_after(Duration::from_millis(1000)); - } else { - ui.ctx().request_repaint(); - } } /// Check when to block tabs navigation on sync progress. @@ -191,64 +262,6 @@ impl WalletContent { (!integrated_node || integrated_node_ready)) } - /// Draw [`Modal`] content for this ui container. - fn modal_content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - match Modal::opened() { - None => {} - Some(id) => { - match id { - ACCOUNT_LIST_MODAL => { - if let Some(content) = self.accounts_modal_content.as_mut() { - Modal::ui(ui.ctx(), |ui, modal| { - content.ui(ui, &self.wallet, modal, cb); - }); - } - } - QR_CODE_SCAN_MODAL => { - let mut success = false; - if let Some(content) = self.scan_modal_content.as_mut() { - Modal::ui(ui.ctx(), |ui, modal| { - content.ui(ui, modal, cb, |result| { - match result { - QrScanResult::Slatepack(message) => { - success = true; - let msg = Some(message.to_string()); - let messages = WalletMessages::new(msg); - self.current_tab = Box::new(messages); - return; - } - QrScanResult::Address(receiver) => { - success = true; - let balance = self.wallet.get_data() - .unwrap() - .info - .amount_currently_spendable; - if balance > 0 { - let mut transport = WalletTransport::default(); - let rec = Some(receiver.to_string()); - transport.show_send_tor_modal(cb, rec); - self.current_tab = Box::new(transport); - return; - } - } - _ => {} - } - if success { - modal.close(); - } - }); - }); - } - if success { - self.scan_modal_content = None; - } - } - _ => {} - } - } - } - } - /// Draw wallet account content. fn account_ui(&mut self, ui: &mut egui::Ui, @@ -258,17 +271,12 @@ impl WalletContent { rect.set_height(75.0); // Draw round background. let rounding = View::item_rounding(0, 2, false); - ui.painter().rect(rect, rounding, Colors::button(), View::hover_stroke()); + ui.painter().rect(rect, rounding, Colors::fill_lite(), View::item_stroke()); ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { // Draw button to show QR code scanner. View::item_button(ui, View::item_rounding(0, 2, true), SCAN, None, || { - self.scan_modal_content = Some(CameraScanModal::default()); - Modal::new(QR_CODE_SCAN_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("scan_qr")) - .closeable(false) - .show(); + self.qr_scan_content = Some(CameraContent::default()); cb.start_camera(); }); @@ -346,15 +354,28 @@ impl WalletContent { }); } - /// Draw tab buttons in the bottom of the screen. - fn tabs_ui(&mut self, ui: &mut egui::Ui) { + /// Draw tab buttons at the bottom of the screen. + fn tabs_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.scope(|ui| { // Setup spacing between tabs. ui.style_mut().spacing.item_spacing = egui::vec2(View::TAB_ITEMS_PADDING, 0.0); - // Setup vertical padding inside tab button. + + // Show camera switch button at QR code scan. + if self.qr_scan_content.is_some() && cb.can_switch_camera() { + // Setup vertical padding inside tab button. + ui.style_mut().spacing.button_padding = egui::vec2(10.0, 4.0); + + ui.vertical_centered(|ui| { + View::tab_button(ui, CAMERA_ROTATE, false, |_| { + cb.switch_camera(); + }); + }); + return; + } + + // Setup vertical padding inside buttons. ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0); - // Draw tab buttons. let current_type = self.current_tab.get_type(); ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { @@ -384,101 +405,101 @@ impl WalletContent { }); }); } +} - /// Draw content when wallet is syncing and not ready to use, returns `true` at this case. - pub fn sync_ui(ui: &mut egui::Ui, wallet: &Wallet) -> bool { - if wallet.is_repairing() && !wallet.sync_error() { - Self::sync_progress_ui(ui, wallet); - return true; - } else if wallet.is_closing() { - Self::sync_progress_ui(ui, wallet); - return true; - } else if wallet.get_current_connection() == ConnectionMethod::Integrated { - if !Node::is_running() || Node::is_stopping() { - View::center_content(ui, 108.0, |ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.5, |ui| { - let text = t!("wallets.enable_node", "settings" => GEAR_FINE); - ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text())); - ui.add_space(8.0); - // Show button to enable integrated node at non-dual root panel mode - // or when network connections are not showing and node is not stopping - let dual_panel_root = Content::is_dual_panel_mode(ui); - if (!dual_panel_root || AppConfig::show_connections_network_panel()) - && !Node::is_stopping() { - let enable_text = format!("{} {}", POWER, t!("network.enable_node")); - View::action_button(ui, enable_text, || { - Node::start(); - }); - } - }); +/// Draw content when wallet is syncing and not ready to use, returns `true` at this case. +fn sync_ui(ui: &mut egui::Ui, wallet: &Wallet) -> bool { + if wallet.is_repairing() && !wallet.sync_error() { + sync_progress_ui(ui, wallet); + return true; + } else if wallet.is_closing() { + sync_progress_ui(ui, wallet); + return true; + } else if wallet.get_current_connection() == ConnectionMethod::Integrated { + if !Node::is_running() || Node::is_stopping() { + View::center_content(ui, 108.0, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + let text = t!("wallets.enable_node", "settings" => GEAR_FINE); + ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text())); + ui.add_space(8.0); + // Show button to enable integrated node at non-dual root panel mode + // or when network connections are not showing and node is not stopping + let dual_panel = Content::is_dual_panel_mode(ui.ctx()); + if (!dual_panel || AppConfig::show_connections_network_panel()) + && !Node::is_stopping() { + let enable_text = format!("{} {}", POWER, t!("network.enable_node")); + View::action_button(ui, enable_text, || { + Node::start(); + }); + } }); - return true - } else if wallet.sync_error() - && Node::get_sync_status() == Some(SyncStatus::NoSync) { - Self::sync_error_ui(ui, wallet); - return true; - } else if wallet.get_data().is_none() { - Self::sync_progress_ui(ui, wallet); - return true; - } - } else if wallet.sync_error() { - Self::sync_error_ui(ui, wallet); + }); + return true + } else if wallet.sync_error() + && Node::get_sync_status() == Some(SyncStatus::NoSync) { + sync_error_ui(ui, wallet); return true; } else if wallet.get_data().is_none() { - Self::sync_progress_ui(ui, wallet); + sync_progress_ui(ui, wallet); return true; } - false + } else if wallet.sync_error() { + sync_error_ui(ui, wallet); + return true; + } else if wallet.get_data().is_none() { + sync_progress_ui(ui, wallet); + return true; } + false +} - /// Draw wallet sync error content. - fn sync_error_ui(ui: &mut egui::Ui, wallet: &Wallet) { - View::center_content(ui, 108.0, |ui| { - let text = t!("wallets.wallet_loading_err", "settings" => GEAR_FINE); - ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text())); - ui.add_space(8.0); - let retry_text = format!("{} {}", ARROWS_CLOCKWISE, t!("retry")); - View::action_button(ui, retry_text, || { - wallet.set_sync_error(false); - }); +/// Draw wallet sync error content. +fn sync_error_ui(ui: &mut egui::Ui, wallet: &Wallet) { + View::center_content(ui, 108.0, |ui| { + let text = t!("wallets.wallet_loading_err", "settings" => GEAR_FINE); + ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text())); + ui.add_space(8.0); + let retry_text = format!("{} {}", ARROWS_CLOCKWISE, t!("retry")); + View::action_button(ui, retry_text, || { + wallet.set_sync_error(false); }); - } + }); +} - /// Draw wallet sync progress content. - pub fn sync_progress_ui(ui: &mut egui::Ui, wallet: &Wallet) { - View::center_content(ui, 162.0, |ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.5, |ui| { - View::big_loading_spinner(ui); - ui.add_space(18.0); - // Setup sync progress text. - let text = { - let int_node = wallet.get_current_connection() == ConnectionMethod::Integrated; - let int_ready = Node::get_sync_status() == Some(SyncStatus::NoSync); - let info_progress = wallet.info_sync_progress(); +/// Draw wallet sync progress content. +fn sync_progress_ui(ui: &mut egui::Ui, wallet: &Wallet) { + View::center_content(ui, 162.0, |ui| { + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + View::big_loading_spinner(ui); + ui.add_space(18.0); + // Setup sync progress text. + let text = { + let int_node = wallet.get_current_connection() == ConnectionMethod::Integrated; + let int_ready = Node::get_sync_status() == Some(SyncStatus::NoSync); + let info_progress = wallet.info_sync_progress(); - if wallet.is_closing() { - t!("wallets.wallet_closing") - } else if int_node && !int_ready { - t!("wallets.node_loading", "settings" => GEAR_FINE) - } else if wallet.is_repairing() { - let repair_progress = wallet.repairing_progress(); - if repair_progress == 0 { - t!("wallets.wallet_checking") - } else { - format!("{}: {}%", t!("wallets.wallet_checking"), repair_progress) - } - } else if info_progress != 100 { - if info_progress == 0 { - t!("wallets.wallet_loading") - } else { - format!("{}: {}%", t!("wallets.wallet_loading"), info_progress) - } + if wallet.is_closing() { + t!("wallets.wallet_closing") + } else if int_node && !int_ready { + t!("wallets.node_loading", "settings" => GEAR_FINE) + } else if wallet.is_repairing() { + let repair_progress = wallet.repairing_progress(); + if repair_progress == 0 { + t!("wallets.wallet_checking") } else { - t!("wallets.tx_loading") + format!("{}: {}%", t!("wallets.wallet_checking"), repair_progress) } - }; - ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text())); - }); + } else if info_progress != 100 { + if info_progress == 0 { + t!("wallets.wallet_loading") + } else { + format!("{}: {}%", t!("wallets.wallet_loading"), info_progress) + } + } else { + t!("wallets.tx_loading") + } + }; + ui.label(RichText::new(text).size(16.0).color(Colors::inactive_text())); }); - } + }); } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/messages/content.rs b/src/gui/views/wallets/wallet/messages/content.rs index f9af096..27670bf 100644 --- a/src/gui/views/wallets/wallet/messages/content.rs +++ b/src/gui/views/wallets/wallet/messages/content.rs @@ -14,7 +14,7 @@ use std::sync::Arc; use std::thread; -use egui::{Id, Margin, RichText, ScrollArea}; +use egui::{Id, RichText, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; use grin_core::core::amount_to_hr_string; use grin_wallet_libwallet::{Error, Slate, SlateState}; @@ -23,11 +23,11 @@ use parking_lot::RwLock; use crate::gui::Colors; use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, DOWNLOAD_SIMPLE, SCAN, UPLOAD_SIMPLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{FilePickButton, Modal, Content, View, CameraScanModal}; +use crate::gui::views::{FilePickButton, Modal, View, CameraScanModal}; use crate::gui::views::types::{ModalPosition, QrScanResult}; use crate::gui::views::wallets::wallet::messages::request::MessageRequestModal; use crate::gui::views::wallets::wallet::types::{SLATEPACK_MESSAGE_HINT, WalletTab, WalletTabType}; -use crate::gui::views::wallets::wallet::{WalletContent, WalletTransactionModal}; +use crate::gui::views::wallets::wallet::WalletTransactionModal; use crate::wallet::types::WalletTransaction; use crate::wallet::Wallet; @@ -60,10 +60,8 @@ pub struct WalletMessages { /// Identifier for amount input [`Modal`] to create invoice or sending request. const REQUEST_MODAL: &'static str = "messages_request_modal"; - /// Identifier for [`Modal`] modal to show transaction information. const TX_INFO_MODAL: &'static str = "messages_tx_info_modal"; - /// Identifier for [`Modal`] to scan Slatepack message from QR code. const SCAN_QR_MODAL: &'static str = "messages_scan_qr_modal"; @@ -73,38 +71,8 @@ impl WalletTab for WalletMessages { } fn ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { - if WalletContent::sync_ui(ui, wallet) { - return; - } - - // Show modal content for this ui container. self.modal_content_ui(ui, wallet, cb); - - egui::CentralPanel::default() - .frame(egui::Frame { - stroke: View::item_stroke(), - fill: Colors::white_or_black(false), - inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 4.0, - right: View::get_right_inset() + 4.0, - top: 3.0, - bottom: 4.0, - }, - ..Default::default() - }) - .show_inside(ui, |ui| { - ScrollArea::vertical() - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .id_source(Id::from("wallet_messages").with(wallet.get_config().id)) - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - self.ui(ui, wallet, cb); - }); - }); - }); - }); + self.messages_ui(ui, wallet, cb); } } @@ -124,8 +92,8 @@ impl WalletMessages { } } - /// Draw manual wallet transaction interaction content. - pub fn ui(&mut self, + /// Draw messages content. + fn messages_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { @@ -136,7 +104,6 @@ impl WalletMessages { } self.first_draw = false; } - ui.add_space(3.0); // Show creation of request to send or receive funds. @@ -224,7 +191,10 @@ impl WalletMessages { ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { let send_text = format!("{} {}", UPLOAD_SIMPLE, t!("wallets.send")); - View::colored_text_button(ui, send_text, Colors::red(), Colors::button(), || { + View::colored_text_button(ui, + send_text, + Colors::red(), + Colors::white_or_black(false), || { self.show_request_modal(false, cb); }); }); @@ -240,7 +210,10 @@ impl WalletMessages { /// Draw invoice request creation button. fn receive_button_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { let receive_text = format!("{} {}", DOWNLOAD_SIMPLE, t!("wallets.receive")); - View::colored_text_button(ui, receive_text, Colors::green(), Colors::button(), || { + View::colored_text_button(ui, + receive_text, + Colors::green(), + Colors::white_or_black(false), || { self.show_request_modal(true, cb); }); } @@ -253,7 +226,10 @@ impl WalletMessages { } else { t!("wallets.send") }; - Modal::new(REQUEST_MODAL).position(ModalPosition::CenterTop).title(title).show(); + Modal::new(REQUEST_MODAL) + .position(ModalPosition::CenterTop) + .title(title) + .show(); cb.show_keyboard(); } @@ -264,7 +240,9 @@ impl WalletMessages { cb: &dyn PlatformCallbacks) { // Setup description text. if !self.message_error.is_empty() { - ui.label(RichText::new(&self.message_error).size(16.0).color(Colors::red())); + ui.label(RichText::new(&self.message_error) + .size(16.0) + .color(Colors::red())); } else { ui.label(RichText::new(t!("wallets.input_slatepack_desc")) .size(16.0) @@ -280,7 +258,7 @@ impl WalletMessages { let scroll_id = Id::from("message_input_scroll").with(wallet.get_config().id); ScrollArea::vertical() - .id_source(scroll_id) + .id_salt(scroll_id) .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .max_height(128.0) .auto_shrink([false; 2]) @@ -386,7 +364,7 @@ impl WalletMessages { ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { let scan_text = format!("{} {}", SCAN, t!("scan")); - View::button(ui, scan_text, Colors::button(), || { + View::button(ui, scan_text, Colors::white_or_black(false), || { self.message_edit.clear(); self.message_error.clear(); self.scan_modal_content = Some(CameraScanModal::default()); @@ -402,7 +380,7 @@ impl WalletMessages { columns[1].vertical_centered_justified(|ui| { // Draw button to paste text from clipboard. let paste = format!("{} {}", CLIPBOARD_TEXT, t!("paste")); - View::button(ui, paste, Colors::button(), || { + View::button(ui, paste, Colors::white_or_black(false), || { let buf = cb.get_string_from_buffer(); let previous = self.message_edit.clone(); self.message_edit = buf.clone().trim().to_string(); @@ -427,7 +405,7 @@ impl WalletMessages { } else { // Draw button to clear message input. let clear_text = format!("{} {}", BROOM, t!("clear")); - View::button(ui, clear_text, Colors::button(), || { + View::button(ui, clear_text, Colors::white_or_black(false), || { self.message_edit.clear(); self.message_error.clear(); }); diff --git a/src/gui/views/wallets/wallet/modals/accounts.rs b/src/gui/views/wallets/wallet/modals/accounts.rs index 2974214..de6ffaa 100644 --- a/src/gui/views/wallets/wallet/modals/accounts.rs +++ b/src/gui/views/wallets/wallet/modals/accounts.rs @@ -130,7 +130,7 @@ impl WalletAccountsModal { // Show list of accounts. let size = self.accounts.len(); ScrollArea::vertical() - .id_source("account_list_modal_scroll") + .id_salt("account_list_modal_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .max_height(266.0) .auto_shrink([true; 2]) @@ -149,7 +149,7 @@ impl WalletAccountsModal { }); ui.add_space(2.0); - View::horizontal_line(ui, Colors::stroke()); + View::horizontal_line(ui, Colors::item_stroke()); ui.add_space(6.0); // Setup spacing between buttons. diff --git a/src/gui/views/wallets/wallet/modals/scan.rs b/src/gui/views/wallets/wallet/modals/scan.rs deleted file mode 100644 index 3ac383c..0000000 --- a/src/gui/views/wallets/wallet/modals/scan.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2024 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use egui::scroll_area::ScrollBarVisibility; -use egui::{Id, ScrollArea}; - -use crate::gui::Colors; -use crate::gui::icons::COPY; -use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{CameraContent, Modal, View}; -use crate::gui::views::types::QrScanResult; -use crate::wallet::Wallet; - -/// QR code scan [`Modal`] content. -pub struct WalletScanModal { - /// Camera content for QR scan [`Modal`]. - camera_content: Option, - /// QR code scan result - qr_scan_result: Option, -} - -impl Default for WalletScanModal { - fn default() -> Self { - Self { - camera_content: None, - qr_scan_result: None, - } - } -} - -impl WalletScanModal { - /// Draw [`Modal`] content. - pub fn ui(&mut self, - ui: &mut egui::Ui, - wallet: &Wallet, - modal: &Modal, - cb: &dyn PlatformCallbacks, - mut on_result: impl FnMut(&QrScanResult)) { - // Show scan result if exists or show camera content while scanning. - if let Some(result) = &self.qr_scan_result { - let mut result_text = result.text(); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(3.0); - ScrollArea::vertical() - .id_source(Id::from("qr_scan_result_input").with(wallet.get_config().id)) - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .max_height(128.0) - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.add_space(7.0); - egui::TextEdit::multiline(&mut result_text) - .font(egui::TextStyle::Small) - .desired_rows(5) - .interactive(false) - .desired_width(f32::INFINITY) - .show(ui); - ui.add_space(6.0); - }); - ui.add_space(2.0); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(10.0); - - // Show copy button. - ui.vertical_centered(|ui| { - let copy_text = format!("{} {}", COPY, t!("copy")); - View::button(ui, copy_text, Colors::button(), || { - cb.copy_string_to_buffer(result_text.to_string()); - self.qr_scan_result = None; - modal.close(); - }); - }); - ui.add_space(10.0); - View::horizontal_line(ui, Colors::item_stroke()); - ui.add_space(6.0); - } else if let Some(result) = self.camera_content.get_or_insert(CameraContent::default()) - .qr_scan_result() { - cb.stop_camera(); - self.camera_content = None; - on_result(&result); - - // Set result and rename modal title. - self.qr_scan_result = Some(result); - Modal::set_title(t!("scan_result")); - } else { - ui.add_space(6.0); - self.camera_content.as_mut().unwrap().ui(ui, cb); - ui.add_space(6.0); - } - - if self.qr_scan_result.is_some() { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("close"), Colors::white_or_black(false), || { - self.qr_scan_result = None; - self.camera_content = None; - modal.close(); - }); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("repeat"), Colors::white_or_black(false), || { - Modal::set_title(t!("scan_qr")); - self.qr_scan_result = None; - self.camera_content = Some(CameraContent::default()); - cb.start_camera(); - }); - }); - }); - } else { - ui.vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { - cb.stop_camera(); - self.camera_content = None; - modal.close(); - }); - }); - } - ui.add_space(6.0); - } -} \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/settings/common.rs b/src/gui/views/wallets/wallet/settings/common.rs index 60b5362..c67599a 100644 --- a/src/gui/views/wallets/wallet/settings/common.rs +++ b/src/gui/views/wallets/wallet/settings/common.rs @@ -62,7 +62,7 @@ impl Default for CommonSettings { impl CommonSettings { /// Draw common wallet settings content. pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { - // Show modal content for this ui container. + // Show modal content for this container. self.modal_content_ui(ui, wallet, cb); ui.vertical_centered(|ui| { @@ -80,7 +80,7 @@ impl CommonSettings { // Show wallet name setup. let name_text = format!("{} {}", PENCIL, t!("change")); - View::button(ui, name_text, Colors::button(), || { + View::button(ui, name_text, Colors::white_or_black(false), || { self.name_edit = config.name; // Show wallet name modal. Modal::new(NAME_EDIT_MODAL) @@ -98,7 +98,7 @@ impl CommonSettings { // Show wallet password setup. let pass_text = format!("{} {}", PASSWORD, t!("change")); - View::button(ui, pass_text, Colors::button(), || { + View::button(ui, pass_text, Colors::white_or_black(false), || { // Setup modal values. self.first_edit_pass_opening = true; self.old_pass_edit = "".to_string(); @@ -120,7 +120,7 @@ impl CommonSettings { // Show minimum amount of confirmations value setup. let min_conf_text = format!("{} {}", CLOCK_COUNTDOWN, config.min_confirmations); - View::button(ui, min_conf_text, Colors::button(), || { + View::button(ui, min_conf_text, Colors::white_or_black(false), || { self.min_confirmations_edit = config.min_confirmations.to_string(); // Show minimum amount of confirmations value modal. Modal::new(MIN_CONFIRMATIONS_EDIT_MODAL) diff --git a/src/gui/views/wallets/wallet/settings/connection.rs b/src/gui/views/wallets/wallet/settings/connection.rs index 5aa49e0..6e5eaf7 100644 --- a/src/gui/views/wallets/wallet/settings/connection.rs +++ b/src/gui/views/wallets/wallet/settings/connection.rs @@ -123,7 +123,7 @@ impl ConnectionSettings { // Show button to add new external node connection. let add_node_text = format!("{} {}", PLUS_CIRCLE, t!("wallets.add_node")); - View::button(ui, add_node_text, Colors::button(), || { + View::button(ui, add_node_text, Colors::white_or_black(false), || { self.ext_conn_modal = ExternalConnectionModal::new(None); Modal::new(ExternalConnectionModal::WALLET_ID) .position(ModalPosition::CenterTop) diff --git a/src/gui/views/wallets/wallet/settings/content.rs b/src/gui/views/wallets/wallet/settings/content.rs index 6f9ebae..c28925b 100644 --- a/src/gui/views/wallets/wallet/settings/content.rs +++ b/src/gui/views/wallets/wallet/settings/content.rs @@ -12,15 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Id, Margin, ScrollArea}; -use egui::scroll_area::ScrollBarVisibility; - -use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Content, View}; use crate::gui::views::wallets::{CommonSettings, ConnectionSettings, RecoverySettings}; use crate::gui::views::wallets::types::{WalletTab, WalletTabType}; -use crate::gui::views::wallets::WalletContent; use crate::wallet::Wallet; /// Wallet settings tab content. @@ -52,42 +46,11 @@ impl WalletTab for WalletSettings { ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { - // Show loading progress if navigation is blocked. - if WalletContent::block_navigation_on_sync(wallet) { - WalletContent::sync_progress_ui(ui, wallet); - return; - } - - // Show settings content panel. - egui::CentralPanel::default() - .frame(egui::Frame { - stroke: View::item_stroke(), - fill: Colors::white_or_black(false), - inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 4.0, - right: View::get_right_inset() + 4.0, - top: 3.0, - bottom: 4.0, - }, - ..Default::default() - }) - .show_inside(ui, |ui| { - ScrollArea::vertical() - .id_source(Id::from("wallet_settings_scroll").with(wallet.get_config().id)) - .auto_shrink([false; 2]) - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .show(ui, |ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - // Show common wallet setup. - self.common_setup.ui(ui, wallet, cb); - // Show wallet connections setup. - self.conn_setup.wallet_ui(ui, wallet, cb); - // Show wallet recovery setup. - self.recovery_setup.ui(ui, wallet, cb); - }); - }); - }); - }); + // Show common wallet setup. + self.common_setup.ui(ui, wallet, cb); + // Show wallet connections setup. + self.conn_setup.wallet_ui(ui, wallet, cb); + // Show wallet recovery setup. + self.recovery_setup.ui(ui, wallet, cb); } } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/settings/recovery.rs b/src/gui/views/wallets/wallet/settings/recovery.rs index 862b9e5..5326589 100644 --- a/src/gui/views/wallets/wallet/settings/recovery.rs +++ b/src/gui/views/wallets/wallet/settings/recovery.rs @@ -92,9 +92,11 @@ impl RecoverySettings { ui.add_space(6.0); // Draw button to restore the wallet. - let recover_text = format!("{} {}", LIFEBUOY, t!("wallets.recover")); ui.add_space(4.0); - View::colored_text_button(ui, recover_text, Colors::green(), Colors::button(), || { + View::colored_text_button(ui, + format!("{} {}", LIFEBUOY, t!("wallets.recover")), + Colors::green(), + Colors::white_or_black(false), || { wallet.delete_db(true); }); ui.add_space(6.0); @@ -112,7 +114,7 @@ impl RecoverySettings { // Draw button to show recovery phrase. let show_text = format!("{} {}", EYE, t!("show")); - View::button(ui, show_text, Colors::button(), || { + View::button(ui, show_text, Colors::white_or_black(false), || { self.show_recovery_phrase_modal(cb); }); @@ -123,8 +125,10 @@ impl RecoverySettings { ui.add_space(6.0); // Draw button to delete the wallet. - let delete_text = format!("{} {}", TRASH, t!("wallets.delete")); - View::colored_text_button(ui, delete_text, Colors::red(), Colors::button(), || { + View::colored_text_button(ui, + format!("{} {}", TRASH, t!("wallets.delete")), + Colors::red(), + Colors::white_or_black(false), || { Modal::new(DELETE_CONFIRMATION_MODAL) .position(ModalPosition::Center) .title(t!("confirmation")) diff --git a/src/gui/views/wallets/wallet/transport/content.rs b/src/gui/views/wallets/wallet/transport/content.rs index 1200515..0db7b09 100644 --- a/src/gui/views/wallets/wallet/transport/content.rs +++ b/src/gui/views/wallets/wallet/transport/content.rs @@ -12,18 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea}; -use egui::scroll_area::ScrollBarVisibility; +use egui::{Align, Layout, RichText, Rounding}; use crate::gui::Colors; use crate::gui::icons::{CHECK_CIRCLE, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, GLOBE_SIMPLE, POWER, QR_CODE, SHIELD_CHECKERED, SHIELD_SLASH, WARNING_CIRCLE, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, QrCodeContent, Content, View}; +use crate::gui::views::{Modal, QrCodeContent, View}; use crate::gui::views::types::ModalPosition; use crate::gui::views::wallets::wallet::transport::send::TransportSendModal; use crate::gui::views::wallets::wallet::transport::settings::TransportSettingsModal; use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType}; -use crate::gui::views::wallets::wallet::WalletContent; use crate::tor::{Tor, TorConfig}; use crate::wallet::types::WalletData; use crate::wallet::Wallet; @@ -49,39 +47,8 @@ impl WalletTab for WalletTransport { ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { - if WalletContent::sync_ui(ui, wallet) { - return; - } - - // Show modal content for this ui container. self.modal_content_ui(ui, wallet, cb); - - // Show transport content panel. - egui::CentralPanel::default() - .frame(egui::Frame { - stroke: View::item_stroke(), - fill: Colors::white_or_black(false), - inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 4.0, - right: View::get_right_inset() + 4.0, - top: 3.0, - bottom: 4.0, - }, - ..Default::default() - }) - .show_inside(ui, |ui| { - ScrollArea::vertical() - .id_source(Id::from("wallet_transport").with(wallet.get_config().id)) - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - self.ui(ui, wallet, cb); - }); - }); - }); - }); + self.transport_ui(ui, wallet, cb); } } @@ -106,7 +73,7 @@ impl Default for WalletTransport { impl WalletTransport { /// Draw wallet transport content. - pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { + fn transport_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { ui.add_space(3.0); ui.label(RichText::new(t!("transport.desc")) .size(16.0) @@ -180,7 +147,7 @@ impl WalletTransport { // Draw round background. let bg_rect = rect.clone(); let item_rounding = View::item_rounding(0, 2, false); - ui.painter().rect(bg_rect, item_rounding, Colors::button(), View::item_stroke()); + ui.painter().rect(bg_rect, item_rounding, Colors::fill_lite(), View::item_stroke()); ui.vertical(|ui| { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { @@ -286,7 +253,7 @@ impl WalletTransport { } else { View::item_rounding(1, 2, false) }; - ui.painter().rect(bg_rect, item_rounding, Colors::button(), View::item_stroke()); + ui.painter().rect(bg_rect, item_rounding, Colors::fill_lite(), View::item_stroke()); ui.vertical(|ui| { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { diff --git a/src/gui/views/wallets/wallet/transport/send.rs b/src/gui/views/wallets/wallet/transport/send.rs index e191262..4caa7af 100644 --- a/src/gui/views/wallets/wallet/transport/send.rs +++ b/src/gui/views/wallets/wallet/transport/send.rs @@ -96,6 +96,7 @@ impl TransportSendModal { /// Draw content to send. fn content_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, modal: &Modal, cb: &dyn PlatformCallbacks) { + ui.add_space(6.0); // Draw QR code scanner content if requested. if let Some(scanner) = self.address_scan_content.as_mut() { let mut on_stop = || { @@ -249,7 +250,11 @@ impl TransportSendModal { } /// Draw error content. - fn error_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn error_ui(&mut self, + ui: &mut egui::Ui, + wallet: &Wallet, + modal: &Modal, + cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("transport.tor_send_error")) @@ -342,7 +347,8 @@ impl TransportSendModal { let res = self.send_result.read().clone().unwrap(); match res { Ok(tx) => { - self.tx_info_content = Some(WalletTransactionModal::new(wallet, &tx, false)); + self.tx_info_content = + Some(WalletTransactionModal::new(wallet, &tx, false)); } Err(_) => { self.error = true; diff --git a/src/gui/views/wallets/wallet/txs/content.rs b/src/gui/views/wallets/wallet/txs/content.rs index 927add1..60cf7cf 100644 --- a/src/gui/views/wallets/wallet/txs/content.rs +++ b/src/gui/views/wallets/wallet/txs/content.rs @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::ops::Range; use std::time::{SystemTime, UNIX_EPOCH}; -use egui::{Align, Id, Layout, Margin, Rect, RichText, Rounding, ScrollArea}; +use egui::{Align, Id, Layout, Rect, RichText, Rounding, ScrollArea}; +use egui::epaint::RectShape; use egui::scroll_area::ScrollBarVisibility; use grin_core::core::amount_to_hr_string; use grin_wallet_libwallet::TxLogEntryType; @@ -22,10 +24,10 @@ use crate::gui::Colors; use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, BRIDGE, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK, CHECK_CIRCLE, DOTS_THREE_CIRCLE, FILE_TEXT, GEAR_FINE, PROHIBIT, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, PullToRefresh, Content, View}; -use crate::gui::views::types::ModalPosition; +use crate::gui::views::types::{LinePosition, ModalPosition}; use crate::gui::views::wallets::types::WalletTab; use crate::gui::views::wallets::wallet::types::{GRIN, WalletTabType}; -use crate::gui::views::wallets::wallet::{WalletContent, WalletTransactionModal}; +use crate::gui::views::wallets::wallet::WalletTransactionModal; use crate::wallet::types::{WalletData, WalletTransaction}; use crate::wallet::Wallet; @@ -57,32 +59,8 @@ impl WalletTab for WalletTransactions { } fn ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { - if WalletContent::sync_ui(ui, wallet) { - return; - } - - // Show modal content for this ui container. self.modal_content_ui(ui, wallet, cb); - - // Show wallet transactions content. - egui::CentralPanel::default() - .frame(egui::Frame { - stroke: View::item_stroke(), - fill: Colors::button(), - inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 4.0, - right: View::get_right_inset() + 4.0, - top: 0.0, - bottom: 4.0, - }, - ..Default::default() - }) - .show_inside(ui, |ui| { - ui.vertical_centered(|ui| { - let data = wallet.get_data().unwrap(); - self.txs_ui(ui, wallet, &data, cb); - }); - }); + self.txs_ui(ui, wallet, cb); } } @@ -91,86 +69,46 @@ const TX_INFO_MODAL: &'static str = "tx_info_modal"; /// Identifier for transaction cancellation confirmation [`Modal`]. const CANCEL_TX_CONFIRMATION_MODAL: &'static str = "cancel_tx_conf_modal"; - - impl WalletTransactions { /// Height of transaction list item. - pub const TX_ITEM_HEIGHT: f32 = 76.0; + pub const TX_ITEM_HEIGHT: f32 = 75.0; /// Draw transactions content. fn txs_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, - data: &WalletData, cb: &dyn PlatformCallbacks) { - let amount_conf = data.info.amount_awaiting_confirmation; - let amount_fin = data.info.amount_awaiting_finalization; - let amount_locked = data.info.amount_locked; - View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - // Show non-zero awaiting confirmation amount. - if amount_conf != 0 { - let awaiting_conf = amount_to_hr_string(amount_conf, true); - let rounding = if amount_fin != 0 || amount_locked != 0 { - [false, false, false, false] - } else { - [false, false, true, true] - }; - View::rounded_box(ui, - format!("{} ツ", awaiting_conf), - t!("wallets.await_conf_amount"), - rounding); - } - // Show non-zero awaiting finalization amount. - if amount_fin != 0 { - let awaiting_conf = amount_to_hr_string(amount_fin, true); - let rounding = if amount_locked != 0 { - [false, false, false, false] - } else { - [false, false, true, true] - }; - View::rounded_box(ui, - format!("{} ツ", awaiting_conf), - t!("wallets.await_fin_amount"), - rounding); - } - // Show non-zero locked amount. - if amount_locked != 0 { - let awaiting_conf = amount_to_hr_string(amount_locked, true); - View::rounded_box(ui, - format!("{} ツ", awaiting_conf), - t!("wallets.locked_amount"), - [false, false, true, true]); - } - - // Show message when txs are empty. - if let Some(txs) = data.txs.as_ref() { - if txs.is_empty() { - View::center_content(ui, 96.0, |ui| { - let empty_text = t!( - "wallets.txs_empty", - "message" => CHAT_CIRCLE_TEXT, - "transport" => BRIDGE, - "settings" => GEAR_FINE - ); - ui.label(RichText::new(empty_text).size(16.0).color(Colors::inactive_text())); - }); - return; - } - } - }); - - // Show loader when txs are not loaded. + let data = wallet.get_data().unwrap(); if data.txs.is_none() { ui.centered_and_justified(|ui| { View::big_loading_spinner(ui); }); return; } - + let txs = data.txs.as_ref().unwrap(); + let mut awaiting_amount = false; + View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Show message when txs are empty. + if txs.is_empty() { + View::center_content(ui, 96.0, |ui| { + let empty_text = t!( + "wallets.txs_empty", + "message" => CHAT_CIRCLE_TEXT, + "transport" => BRIDGE, + "settings" => GEAR_FINE + ); + ui.label(RichText::new(empty_text) + .size(16.0) + .color(Colors::inactive_text())); + }); + return; + } + // Draw awaiting amount info if exists. + awaiting_amount = self.awaiting_info_ui(ui, &data); + }); ui.add_space(4.0); // Show list of transactions. - let txs = data.txs.as_ref().unwrap(); let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(); let refresh = self.manual_sync.unwrap_or(0) + 1600 > now; let refresh_resp = PullToRefresh::new(refresh) @@ -178,57 +116,13 @@ impl WalletTransactions { .min_refresh_distance(70.0) .scroll_area_ui(ui, |ui| { ScrollArea::vertical() - .id_source(Id::from("txs_content").with(wallet.get_config().id)) + .id_salt(Id::from("wallet_tx_list_scroll").with(wallet.get_config().id)) .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .auto_shrink([false; 2]) .show_rows(ui, Self::TX_ITEM_HEIGHT, txs.len(), |ui, row_range| { ui.add_space(1.0); View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - let padding = amount_conf != 0 || amount_fin != 0 || amount_locked != 0; - for index in row_range { - let tx = txs.get(index).unwrap(); - let mut r = View::item_rounding(index, txs.len(), false); - let mut rect = ui.available_rect_before_wrap(); - if padding { - rect.min += egui::emath::vec2(6.0, 0.0); - rect.max -= egui::emath::vec2(6.0, 0.0); - } - rect.set_height(Self::TX_ITEM_HEIGHT); - Self::tx_item_ui(ui, tx, rect, r, &data, |ui| { - // Draw button to show transaction info. - if tx.data.tx_slate_id.is_some() { - r.nw = 0.0; - r.sw = 0.0; - View::item_button(ui, r, FILE_TEXT, None, || { - self.show_tx_info_modal(wallet, tx, false); - }); - } - - let wallet_loaded = wallet.foreign_api_port().is_some(); - - // Draw button to show transaction finalization. - if wallet_loaded && tx.can_finalize { - let (icon, color) = (CHECK, Some(Colors::green())); - View::item_button(ui, Rounding::default(), icon, color, || { - cb.hide_keyboard(); - self.show_tx_info_modal(wallet, tx, true); - }); - } - - // Draw button to cancel transaction. - if wallet_loaded && tx.can_cancel() { - let (icon, color) = (PROHIBIT, Some(Colors::red())); - View::item_button(ui, Rounding::default(), icon, color, || { - self.confirm_cancel_tx_id = Some(tx.data.id); - // Show transaction cancellation confirmation modal. - Modal::new(CANCEL_TX_CONFIRMATION_MODAL) - .position(ModalPosition::Center) - .title(t!("confirmation")) - .show(); - }); - } - }); - } + self.tx_list_ui(ui, awaiting_amount, row_range, wallet, txs, cb); }); }) }); @@ -242,6 +136,110 @@ impl WalletTransactions { } } + /// Draw transaction list content. + fn tx_list_ui(&mut self, + ui: &mut egui::Ui, + awaiting: bool, + row_range: Range, + wallet: &Wallet, + txs: &Vec, + cb: &dyn PlatformCallbacks) { + for index in row_range { + let mut rect = if awaiting { + let mut rect = ui.available_rect_before_wrap(); + rect.min += egui::emath::vec2(6.0, 0.0); + rect.max -= egui::emath::vec2(6.0, 0.0); + rect + } else { + ui.available_rect_before_wrap() + }; + rect.set_height(Self::TX_ITEM_HEIGHT); + + // Draw tx item background. + let mut r = View::item_rounding(index, txs.len(), false); + let p = ui.painter(); + p.rect(rect, r, Colors::fill_lite(), View::item_stroke()); + + let tx = txs.get(index).unwrap(); + let data = wallet.get_data().unwrap(); + Self::tx_item_ui(ui, tx, rect, &data, |ui| { + // Draw button to show transaction info. + if tx.data.tx_slate_id.is_some() { + r.nw = 0.0; + r.sw = 0.0; + View::item_button(ui, r, FILE_TEXT, None, || { + self.show_tx_info_modal(wallet, tx, false); + }); + } + + let wallet_loaded = wallet.foreign_api_port().is_some(); + + // Draw button to show transaction finalization. + if wallet_loaded && tx.can_finalize { + let (icon, color) = (CHECK, Some(Colors::green())); + View::item_button(ui, Rounding::default(), icon, color, || { + cb.hide_keyboard(); + self.show_tx_info_modal(wallet, tx, true); + }); + } + + // Draw button to cancel transaction. + if wallet_loaded && tx.can_cancel() { + let (icon, color) = (PROHIBIT, Some(Colors::red())); + View::item_button(ui, Rounding::default(), icon, color, || { + self.confirm_cancel_tx_id = Some(tx.data.id); + // Show transaction cancellation confirmation modal. + Modal::new(CANCEL_TX_CONFIRMATION_MODAL) + .position(ModalPosition::Center) + .title(t!("confirmation")) + .show(); + }); + } + }); + } + } + + /// Draw information about locked, finalizing or confirming balance, return `true` if exists. + fn awaiting_info_ui(&mut self, ui: &mut egui::Ui, data: &WalletData) -> bool { + let amount_conf = data.info.amount_awaiting_confirmation; + let amount_fin = data.info.amount_awaiting_finalization; + let amount_locked = data.info.amount_locked; + if amount_conf == 0 && amount_fin == 0 && amount_locked == 0 { + return false; + } + let rect = ui.available_rect_before_wrap(); + // Draw background. + let mut bg = RectShape::new(rect, Rounding { + nw: 0.0, + ne: 0.0, + sw: 8.0, + se: 8.0, + }, Colors::TRANSPARENT, View::item_stroke()); + let bg_idx = ui.painter().add(bg); + let resp = ui.allocate_ui(rect.size(), |ui| { + ui.vertical_centered_justified(|ui| { + // Correct vertical spacing between items. + ui.style_mut().spacing.item_spacing.y = -3.0; + if amount_conf != 0 { + // Draw awaiting confirmation amount. + awaiting_item_ui(ui, amount_conf, t!("wallets.await_conf_amount")); + } + if amount_fin != 0 { + // Draw awaiting confirmation amount. + awaiting_item_ui(ui, amount_fin, t!("wallets.await_fin_amount")); + } + if amount_locked != 0 { + // Draw locked amount. + awaiting_item_ui(ui, amount_locked, t!("wallets.locked_amount")); + } + }); + }).response; + // Setup background size. + bg.rect = resp.rect; + ui.painter().set(bg_idx, bg); + true + } + /// Draw [`Modal`] content for this ui container. fn modal_content_ui(&mut self, ui: &mut egui::Ui, @@ -273,13 +271,8 @@ impl WalletTransactions { pub fn tx_item_ui(ui: &mut egui::Ui, tx: &WalletTransaction, rect: Rect, - rounding: Rounding, data: &WalletData, buttons_ui: impl FnOnce(&mut egui::Ui)) { - // Draw round background. - let bg_rect = rect.clone(); - ui.painter().rect(bg_rect, rounding, Colors::TRANSPARENT, View::item_stroke()); - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Max), |ui| { ui.horizontal_centered(|ui| { // Draw buttons. @@ -480,4 +473,19 @@ impl WalletTransactions { ui.add_space(6.0); }); } +} + +/// Draw awaiting balance item content. +fn awaiting_item_ui(ui: &mut egui::Ui, amount: u64, label: String) { + let rect = ui.available_rect_before_wrap(); + View::line(ui, LinePosition::TOP, &rect, Colors::item_stroke()); + ui.add_space(4.0); + let amount_format = amount_to_hr_string(amount, true); + ui.label(RichText::new(format!("{} ツ", amount_format)) + .color(Colors::white_or_black(true)) + .size(17.0)); + ui.label(RichText::new(label) + .color(Colors::gray()) + .size(15.0)); + ui.add_space(4.0); } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/txs/tx.rs b/src/gui/views/wallets/wallet/txs/tx.rs index d1728aa..a708c00 100644 --- a/src/gui/views/wallets/wallet/txs/tx.rs +++ b/src/gui/views/wallets/wallet/txs/tx.rs @@ -53,7 +53,7 @@ pub struct WalletTransactionModal { qr_code_content: Option, /// QR code scanner content. - qr_scan_content: Option, + scan_qr_content: Option, /// Button to parse picked file content. file_pick_button: FilePickButton, @@ -93,7 +93,7 @@ impl WalletTransactionModal { finalizing: false, final_result: Arc::new(RwLock::new(None)), qr_code_content: None, - qr_scan_content: None, + scan_qr_content: None, file_pick_button: FilePickButton::default(), } } @@ -122,78 +122,9 @@ impl WalletTransactionModal { } let tx = txs.get(0).unwrap(); - if self.qr_code_content.is_none() && self.qr_scan_content.is_none() { - ui.add_space(6.0); - - let r = View::item_rounding(0, 2, false); - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(WalletTransactions::TX_ITEM_HEIGHT); - // Show transaction amount status and time. - WalletTransactions::tx_item_ui(ui, tx, rect, r, &data, |ui| { - if self.finalizing { - return; - } - // Show block height or buttons. - if let Some(h) = tx.height { - if h != 0 { - ui.add_space(6.0); - let height = format!("{} {}", CUBE, h.to_string()); - ui.with_layout(Layout::bottom_up(Align::Max), |ui| { - ui.add_space(3.0); - ui.label(RichText::new(height) - .size(15.0) - .color(Colors::text(false))); - }); - } - return; - } - - let wallet_loaded = wallet.foreign_api_port().is_some(); - - // Draw button to show transaction finalization or transaction info. - if wallet_loaded && tx.can_finalize { - let (icon, color) = if self.show_finalization { - (FILE_TEXT, None) - } else { - (CHECK, Some(Colors::green())) - }; - let mut r = r.clone(); - r.nw = 0.0; - r.sw = 0.0; - View::item_button(ui, r, icon, color, || { - cb.hide_keyboard(); - if self.show_finalization { - self.show_finalization = false; - return; - } - self.show_finalization = true; - }); - } - - // Draw button to cancel transaction. - if wallet_loaded && tx.can_cancel() { - View::item_button(ui, Rounding::default(), PROHIBIT, Some(Colors::red()), || { - cb.hide_keyboard(); - wallet.cancel(tx.data.id); - }); - } - }); - - // Show identifier. - if let Some(id) = tx.data.tx_slate_id { - let label = format!("{} {}", HASH_STRAIGHT, t!("id")); - Self::info_item_ui(ui, id.to_string(), label, true, cb); - } - // Show kernel. - if let Some(kernel) = tx.data.kernel_excess { - let label = format!("{} {}", FILE_ARCHIVE, t!("kernel")); - Self::info_item_ui(ui, kernel.0.to_hex(), label, true, cb); - } - // Show receiver address. - if let Some(rec) = tx.receiver() { - let label = format!("{} {}", CUBE, t!("network_mining.address")); - Self::info_item_ui(ui, rec.to_string(), label, true, cb); - } + // Show transaction information. + if self.qr_code_content.is_none() && self.scan_qr_content.is_none() { + self.info_ui(ui, tx, wallet, cb); } // Show Slatepack message interaction. @@ -220,21 +151,21 @@ impl WalletTransactionModal { }); }); }); - } else if self.qr_scan_content.is_some() { + } else if self.scan_qr_content.is_some() { ui.add_space(8.0); // Show buttons to close modal or scanner. ui.columns(2, |cols| { cols[0].vertical_centered_justified(|ui| { View::button(ui, t!("close"), Colors::white_or_black(false), || { cb.stop_camera(); - self.qr_scan_content = None; + self.scan_qr_content = None; modal.close(); }); }); cols[1].vertical_centered_justified(|ui| { View::button(ui, t!("back"), Colors::white_or_black(false), || { cb.stop_camera(); - self.qr_scan_content = None; + self.scan_qr_content = None; modal.enable_closing(); }); }); @@ -286,44 +217,91 @@ impl WalletTransactionModal { } } - /// Draw transaction information item content. - fn info_item_ui(ui: &mut egui::Ui, - value: String, - label: String, - copy: bool, - cb: &dyn PlatformCallbacks) { - // Setup layout size. + /// Draw transaction information content. + fn info_ui(&mut self, + ui: &mut egui::Ui, + tx: &WalletTransaction, + wallet: &Wallet, + cb: &dyn PlatformCallbacks) { + ui.add_space(6.0); + let mut rect = ui.available_rect_before_wrap(); - rect.set_height(50.0); + rect.set_height(WalletTransactions::TX_ITEM_HEIGHT); - // Draw round background. - let bg_rect = rect.clone(); - let mut rounding = View::item_rounding(1, 3, false); + // Draw tx item background. + let p = ui.painter(); + let r = View::item_rounding(0, 2, false); + p.rect(rect, r, Colors::TRANSPARENT, View::item_stroke()); - ui.painter().rect(bg_rect, rounding, Colors::fill(), View::item_stroke()); - - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Draw button to copy transaction info value. - if copy { - rounding.nw = 0.0; - rounding.sw = 0.0; - View::item_button(ui, rounding, COPY, None, || { - cb.copy_string_to_buffer(value.clone()); - }); + // Show transaction amount status and time. + let data = wallet.get_data().unwrap(); + WalletTransactions::tx_item_ui(ui, tx, rect, &data, |ui| { + if self.finalizing { + return; + } + // Show block height or buttons. + if let Some(h) = tx.height { + if h != 0 { + ui.add_space(6.0); + let height = format!("{} {}", CUBE, h.to_string()); + ui.with_layout(Layout::bottom_up(Align::Max), |ui| { + ui.add_space(3.0); + ui.label(RichText::new(height) + .size(15.0) + .color(Colors::text(false))); + }); + } + return; } - // Draw value information. - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(6.0); - ui.vertical(|ui| { - ui.add_space(3.0); - View::ellipsize_text(ui, value, 15.0, Colors::title(false)); - ui.label(RichText::new(label).size(15.0).color(Colors::gray())); - ui.add_space(3.0); + let wallet_loaded = wallet.foreign_api_port().is_some(); + + // Draw button to show transaction finalization or request info. + if wallet_loaded && tx.can_finalize { + let (icon, color) = if self.show_finalization { + (FILE_TEXT, None) + } else { + (CHECK, Some(Colors::green())) + }; + let r = View::item_rounding(0, 2, true); + View::item_button(ui, r, icon, color, || { + cb.hide_keyboard(); + if self.show_finalization { + self.show_finalization = false; + return; + } + self.show_finalization = true; }); - }); + } + // Draw button to cancel transaction. + if wallet_loaded && tx.can_cancel() { + let r = if tx.can_finalize { + Rounding::default() + } else { + View::item_rounding(0, 2, true) + }; + View::item_button(ui, r, PROHIBIT, Some(Colors::red()), || { + cb.hide_keyboard(); + wallet.cancel(tx.data.id); + }); + } }); + + // Show identifier. + if let Some(id) = tx.data.tx_slate_id { + let label = format!("{} {}", HASH_STRAIGHT, t!("id")); + info_item_ui(ui, id.to_string(), label, true, cb); + } + // Show kernel. + if let Some(kernel) = tx.data.kernel_excess { + let label = format!("{} {}", FILE_ARCHIVE, t!("kernel")); + info_item_ui(ui, kernel.0.to_hex(), label, true, cb); + } + // Show receiver address. + if let Some(rec) = tx.receiver() { + let label = format!("{} {}", CUBE, t!("network_mining.address")); + info_item_ui(ui, rec.to_string(), label, true, cb); + } } /// Draw Slatepack message content. @@ -336,8 +314,8 @@ impl WalletTransactionModal { ui.add_space(6.0); // Draw QR code scanner content if requested. - if let Some(qr_scan_content) = self.qr_scan_content.as_mut() { - if let Some(result) = qr_scan_content.qr_scan_result() { + if let Some(scan_content) = self.scan_qr_content.as_mut() { + if let Some(result) = scan_content.qr_scan_result() { cb.stop_camera(); // Setup value to finalization input field. @@ -345,9 +323,9 @@ impl WalletTransactionModal { self.on_finalization_input_change(tx, wallet, modal, cb); modal.enable_closing(); - self.qr_scan_content = None; + self.scan_qr_content = None; } else { - qr_scan_content.ui(ui, cb); + scan_content.ui(ui, cb); } return; } @@ -415,7 +393,7 @@ impl WalletTransactionModal { View::horizontal_line(ui, Colors::item_stroke()); ui.add_space(3.0); ScrollArea::vertical() - .id_source(scroll_id) + .id_salt(scroll_id) .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) .max_height(128.0) .auto_shrink([false; 2]) @@ -460,17 +438,17 @@ impl WalletTransactionModal { columns[0].vertical_centered_justified(|ui| { // Draw button to scan Slatepack message QR code. let qr_text = format!("{} {}", SCAN, t!("scan")); - View::button(ui, qr_text, Colors::button(), || { + View::button(ui, qr_text, Colors::fill_lite(), || { cb.hide_keyboard(); modal.disable_closing(); cb.start_camera(); - self.qr_scan_content = Some(CameraContent::default()); + self.scan_qr_content = Some(CameraContent::default()); }); }); columns[1].vertical_centered_justified(|ui| { // Draw button to paste data from clipboard. let paste_text = format!("{} {}", CLIPBOARD_TEXT, t!("paste")); - View::button(ui, paste_text, Colors::button(), || { + View::button(ui, paste_text, Colors::fill_lite(), || { self.finalize_edit = cb.get_string_from_buffer(); }); }); @@ -480,7 +458,7 @@ impl WalletTransactionModal { if self.finalize_error { // Draw button to clear message input. let clear_text = format!("{} {}", BROOM, t!("clear")); - View::button(ui, clear_text, Colors::button(), || { + View::button(ui, clear_text, Colors::fill_lite(), || { self.finalize_edit.clear(); self.finalize_error = false; }); @@ -501,7 +479,7 @@ impl WalletTransactionModal { columns[0].vertical_centered_justified(|ui| { // Draw button to show Slatepack message as QR code. let qr_text = format!("{} {}", QR_CODE, t!("qr_code")); - View::button(ui, qr_text.clone(), Colors::button(), || { + View::button(ui, qr_text.clone(), Colors::white_or_black(false), || { cb.hide_keyboard(); let text = self.response_edit.clone(); self.qr_code_content = Some(QrCodeContent::new(text, true)); @@ -510,7 +488,7 @@ impl WalletTransactionModal { columns[1].vertical_centered_justified(|ui| { // Draw copy button. let copy_text = format!("{} {}", COPY, t!("copy")); - View::button(ui, copy_text, Colors::button(), || { + View::button(ui, copy_text, Colors::white_or_black(false), || { cb.copy_string_to_buffer(self.response_edit.clone()); self.finalize_edit = "".to_string(); if tx.can_finalize { @@ -578,4 +556,44 @@ impl WalletTransactionModal { } } } +} + +/// Draw transaction information item content. +fn info_item_ui(ui: &mut egui::Ui, + value: String, + label: String, + copy: bool, + cb: &dyn PlatformCallbacks) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(50.0); + + // Draw round background. + let bg_rect = rect.clone(); + let mut rounding = View::item_rounding(1, 3, false); + + ui.painter().rect(bg_rect, rounding, Colors::fill(), View::item_stroke()); + + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to copy transaction info value. + if copy { + rounding.nw = 0.0; + rounding.sw = 0.0; + View::item_button(ui, rounding, COPY, None, || { + cb.copy_string_to_buffer(value.clone()); + }); + } + + // Draw value information. + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(6.0); + ui.vertical(|ui| { + ui.add_space(3.0); + View::ellipsize_text(ui, value, 15.0, Colors::title(false)); + ui.label(RichText::new(label).size(15.0).color(Colors::gray())); + ui.add_space(3.0); + }); + }); + }); } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a6d8840..7c8d525 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ extern crate rust_i18n; use eframe::NativeOptions; -use egui::{Context, Stroke}; +use egui::{Context, Stroke, Theme}; use lazy_static::lazy_static; use std::sync::Arc; use parking_lot::RwLock; @@ -77,6 +77,7 @@ fn android_main(app: AndroidApp) { options.wgpu_options.device_descriptor = std::sync::Arc::new(|_| { let base_limits = wgpu::Limits::downlevel_webgl2_defaults(); wgpu::DeviceDescriptor { + memory_hints: wgpu::MemoryHints::default(), label: Some("egui wgpu device"), required_features: wgpu::Features::default(), required_limits: wgpu::Limits { @@ -102,31 +103,22 @@ fn use_dark_theme(platform: &gui::platform::Android) -> bool { } /// [`App`] setup for [`eframe`]. -pub fn app_creator(app: App) -> eframe::AppCreator +pub fn app_creator(app: App) -> eframe::AppCreator<'static> where App: eframe::App, T: PlatformCallbacks { Box::new(|cc| { + setup_fonts(&cc.egui_ctx); // Setup images support. egui_extras::install_image_loaders(&cc.egui_ctx); - // Setup visuals. - setup_visuals(&cc.egui_ctx); - // Setup fonts. - setup_fonts(&cc.egui_ctx); - // Return app instance. Ok(Box::new(app)) }) } /// Entry point to start ui with [`eframe`]. -pub fn start(mut options: NativeOptions, app_creator: eframe::AppCreator) -> eframe::Result<()> { - options.default_theme = if AppConfig::dark_theme().unwrap_or(false) { - eframe::Theme::Dark - } else { - eframe::Theme::Light - }; +pub fn start(options: NativeOptions, app_creator: eframe::AppCreator) -> eframe::Result<()> { // Setup translations. setup_i18n(); // Start integrated node if needed. - if Settings::app_config_to_read().auto_start_node { + if AppConfig::autostart_node() { Node::start(); } // Launch graphical interface. @@ -135,6 +127,10 @@ pub fn start(mut options: NativeOptions, app_creator: eframe::AppCreator) -> efr /// Setup application [`egui::Style`] and [`egui::Visuals`]. pub fn setup_visuals(ctx: &Context) { + let use_dark = AppConfig::dark_theme().unwrap_or_else(|| { + ctx.system_theme().unwrap_or(Theme::Dark) == Theme::Dark + }); + let mut style = (*ctx.style()).clone(); // Setup selection. style.interaction.selectable_labels = false; @@ -155,7 +151,6 @@ pub fn setup_visuals(ctx: &Context) { ctx.set_style(style); // Setup visuals based on app color theme. - let use_dark = AppConfig::dark_theme().unwrap_or(false); let mut visuals = if use_dark { egui::Visuals::dark() } else { @@ -191,9 +186,9 @@ pub fn setup_fonts(ctx: &Context) { "../fonts/phosphor.ttf" )).tweak(egui::FontTweak { scale: 1.0, - y_offset_factor: -0.30, + y_offset_factor: -0.20, y_offset: 0.0, - baseline_offset_factor: 0.50, + baseline_offset_factor: 0.16, }), ); fonts diff --git a/src/main.rs b/src/main.rs index 9315e4d..68e1e91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,20 +108,10 @@ fn panic_info_message<'pi>(panic_info: &'pi std::panic::PanicHookInfo<'_>) -> &' #[cfg(not(target_os = "android"))] fn start_desktop_gui(platform: grim::gui::platform::Desktop) { use grim::AppConfig; - use dark_light::Mode; - - // Setup system theme if not set. - if let None = AppConfig::dark_theme() { - let dark = match dark_light::detect() { - Mode::Dark => true, - Mode::Light => false, - Mode::Default => false - }; - AppConfig::set_dark_theme(dark); - } - + let os = egui::os::OperatingSystem::from_target_os(); let (width, height) = AppConfig::window_size(); let mut viewport = egui::ViewportBuilder::default() + .with_min_inner_size([AppConfig::MIN_WIDTH, AppConfig::MIN_HEIGHT]) .with_inner_size([width, height]); @@ -134,10 +124,10 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) { viewport = viewport.with_position(egui::pos2(x, y)); } // Setup window decorations. - let is_mac = egui::os::OperatingSystem::from_target_os() == egui::os::OperatingSystem::Mac; + let is_mac = os == egui::os::OperatingSystem::Mac; viewport = viewport - .with_window_level(egui::WindowLevel::Normal) .with_fullsize_content_view(true) + .with_window_level(egui::WindowLevel::Normal) .with_title_shown(false) .with_titlebar_buttons_shown(false) .with_titlebar_shown(false) @@ -148,9 +138,9 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) { viewport, ..Default::default() }; - // Use Glow renderer for Windows. - let win = egui::os::OperatingSystem::from_target_os() == egui::os::OperatingSystem::Windows; - options.renderer = if win { + // Use Glow renderer for Windows and Mac. + let is_win = os == egui::os::OperatingSystem::Windows; + options.renderer = if is_win || is_mac { eframe::Renderer::Glow } else { eframe::Renderer::Wgpu @@ -161,7 +151,7 @@ fn start_desktop_gui(platform: grim::gui::platform::Desktop) { match grim::start(options.clone(), grim::app_creator(app)) { Ok(_) => {} Err(e) => { - if win { + if is_win || is_mac { panic!("{}", e); } // Start with another renderer on error. @@ -194,7 +184,7 @@ fn is_app_running(data: &Option) -> bool { }; let socket_path = grim::Settings::socket_path(); - let name = grim::Settings::socket_name(&socket_path)?; + let name = socket_name(&socket_path)?; // Connect to running application socket. let conn = Stream::connect(name).await?; @@ -250,7 +240,7 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) { if socket_path.exists() { let _ = std::fs::remove_file(&socket_path); } - let name = grim::Settings::socket_name(&socket_path)?; + let name = socket_name(&socket_path)?; // Create listener. let opts = ListenerOptions::new().name(name); @@ -282,4 +272,18 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) { } }); }); +} + +/// Get application socket name from provided path. +#[allow(dead_code)] +#[cfg(not(target_os = "android"))] +fn socket_name(path: &std::path::PathBuf) -> std::io::Result { + use interprocess::local_socket::{NameType, ToFsName, ToNsName}; + let name = if egui::os::OperatingSystem::Mac != egui::os::OperatingSystem::from_target_os() && + interprocess::local_socket::GenericNamespaced::is_supported() { + grim::Settings::SOCKET_NAME.to_ns_name::()? + } else { + path.clone().to_fs_name::()? + }; + Ok(name) } \ No newline at end of file diff --git a/src/settings/settings.rs b/src/settings/settings.rs index a1a95f7..35b4c29 100644 --- a/src/settings/settings.rs +++ b/src/settings/settings.rs @@ -13,17 +13,14 @@ // limitations under the License. use std::fs::{self, File}; -use std::io; use std::io::Write; use std::path::PathBuf; use std::sync::Arc; -use egui::os::OperatingSystem; use lazy_static::lazy_static; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use serde::de::DeserializeOwned; use serde::Serialize; use grin_config::ConfigError; -use interprocess::local_socket::{GenericFilePath, GenericNamespaced, Name, NameType, ToFsName, ToNsName}; use crate::node::NodeConfig; use crate::settings::AppConfig; @@ -148,17 +145,6 @@ impl Settings { socket_path } - /// Get desktop application socket name from provided path. - pub fn socket_name(path: &PathBuf) -> io::Result { - let name = if OperatingSystem::Mac != OperatingSystem::from_target_os() && - GenericNamespaced::is_supported() { - Self::SOCKET_NAME.to_ns_name::()? - } else { - path.clone().to_fs_name::()? - }; - Ok(name) - } - /// Get configuration file path from provided name and subdirectory if needed. pub fn config_path(config_name: &str, sub_dir: Option) -> PathBuf { let mut path = Self::base_path(sub_dir); diff --git a/src/tor/http.rs b/src/tor/http.rs index cd1825b..7cd0698 100644 --- a/src/tor/http.rs +++ b/src/tor/http.rs @@ -1,3 +1,17 @@ +// Copyright 2024 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use std::future::Future; use std::io::Error; use std::pin::Pin;