From c749bce26a3508d72e2a5e47970e1a3da9f20340 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 17 Apr 2024 01:30:28 +0300 Subject: [PATCH] ui: wallet creation button, exit modal buttons order, back button behaviour at wallet, remember window position and size on exit, desktop icon --- .gitignore | 2 + Cargo.lock | 462 ++++++++++++++++++++- Cargo.toml | 3 +- app/build.gradle | 14 + img/icon.png | Bin 0 -> 35681 bytes locales/en.yml | 1 + locales/ru.yml | 1 + src/config.rs | 50 ++- src/gui/app.rs | 10 + src/gui/views/root.rs | 11 +- src/gui/views/views.rs | 44 +- src/gui/views/wallets/content.rs | 99 ++--- src/gui/views/wallets/creation/creation.rs | 13 +- src/gui/views/wallets/wallet/content.rs | 2 +- src/gui/views/wallets/wallet/info.rs | 6 +- src/lib.rs | 4 +- src/main.rs | 27 +- src/wallet/wallet.rs | 4 +- 18 files changed, 623 insertions(+), 130 deletions(-) create mode 100644 img/icon.png diff --git a/.gitignore b/.gitignore index 31708a2..f5507e3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ .cxx local.properties *.so +keystore +keystore.properties # Added by cargo /target diff --git a/Cargo.lock b/Cargo.lock index 508d8e3..6c6ca53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" version = "0.2.18" @@ -250,6 +256,18 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arboard" version = "3.3.2" @@ -258,7 +276,7 @@ checksum = "a2041f1943049c7978768d84e6d0fd95de98b76d6c4727b09e78ec253d29fa58" dependencies = [ "clipboard-win", "core-graphics", - "image", + "image 0.24.9", "log", "objc", "objc-foundation", @@ -281,6 +299,17 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2 1.0.79", + "quote 1.0.36", + "syn 2.0.58", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -572,6 +601,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec 0.7.4", + "log", + "nom", + "num-rational 0.4.1", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec 0.7.4", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -648,6 +700,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "0.9.1" @@ -666,6 +724,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bitstream-io" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da" + [[package]] name = "blake2-rfc" version = "0.2.18" @@ -902,6 +966,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -1225,6 +1299,12 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1489,7 +1569,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6368dbd2c6685fb84fc6e6a4749917ddc98905793fd06341c7e11a2504f2724" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2 0.4.30", "quote 0.6.13", "syn 0.15.44", @@ -1544,7 +1624,7 @@ dependencies = [ "glow", "glutin", "glutin-winit", - "image", + "image 0.24.9", "js-sys", "log", "objc", @@ -1621,7 +1701,7 @@ checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593" dependencies = [ "egui", "enum-map", - "image", + "image 0.24.9", "log", "mime_guess2", "serde", @@ -1847,6 +1927,22 @@ dependencies = [ "pin-project-lite 0.2.14", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -1940,6 +2036,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2209,6 +2314,16 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.28.1" @@ -2426,6 +2541,7 @@ dependencies = [ "grin_wallet_impls", "grin_wallet_libwallet", "grin_wallet_util", + "image 0.25.1", "jni", "lazy_static", "local-ip-address", @@ -2532,7 +2648,7 @@ dependencies = [ "log", "lru-cache", "num", - "num-bigint", + "num-bigint 0.2.6", "rand 0.6.5", "serde", "serde_derive", @@ -2823,7 +2939,7 @@ dependencies = [ "grin_wallet_util", "lazy_static", "log", - "num-bigint", + "num-bigint 0.2.6", "rand 0.6.5", "regex", "secrecy 0.6.0", @@ -2873,6 +2989,16 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2913,6 +3039,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -3008,7 +3140,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error", + "quick-error 1.2.3", ] [[package]] @@ -3243,6 +3375,45 @@ dependencies = [ "tiff", ] +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits 0.2.18", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a84a25dcae3ac487bc24ef280f9e20c79c9b1a3e5e32cbed3041d1c514aa87c" +dependencies = [ + "byteorder", + "thiserror", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "1.9.3" @@ -3272,6 +3443,17 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2 1.0.79", + "quote 1.0.36", + "syn 2.0.58", +] + [[package]] name = "intl-memoizer" version = "0.5.1" @@ -3328,6 +3510,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -3441,12 +3632,29 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libgit2-sys" version = "0.16.2+1.7.2" @@ -3629,6 +3837,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -3647,6 +3864,16 @@ dependencies = [ "libc", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if 1.0.0", + "rayon", +] + [[package]] name = "memchr" version = "2.7.2" @@ -3926,6 +4153,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.26.4" @@ -3960,6 +4193,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "normpath" version = "1.2.0" @@ -3984,11 +4223,11 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" dependencies = [ - "num-bigint", + "num-bigint 0.2.6", "num-complex", "num-integer", "num-iter", - "num-rational", + "num-rational 0.2.4", "num-traits 0.2.18", ] @@ -4003,6 +4242,17 @@ dependencies = [ "num-traits 0.2.18", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg 1.2.0", + "num-integer", + "num-traits 0.2.18", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -4013,6 +4263,17 @@ dependencies = [ "num-traits 0.2.18", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2 1.0.79", + "quote 1.0.36", + "syn 2.0.58", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -4040,7 +4301,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg 1.2.0", - "num-bigint", + "num-bigint 0.2.6", + "num-integer", + "num-traits 0.2.18", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg 1.2.0", + "num-bigint 0.4.4", "num-integer", "num-traits 0.2.18", ] @@ -4617,6 +4890,28 @@ name = "profiling" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote 1.0.36", + "syn 2.0.58", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] [[package]] name = "qr_code" @@ -4630,6 +4925,12 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.31.0" @@ -4847,6 +5148,56 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec 0.7.4", + "av1-grain", + "bitstream-io", + "built", + "cfg-if 1.0.0", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits 0.2.18", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error 2.0.1", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -4998,6 +5349,15 @@ dependencies = [ "winreg", ] +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.16.20" @@ -5007,7 +5367,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi 0.3.9", @@ -5526,6 +5886,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote 1.0.36", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -5638,6 +6007,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api 0.4.11", +] + [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -5677,7 +6055,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2 1.0.79", "quote 1.0.36", "syn 1.0.109", @@ -5752,6 +6130,25 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.12", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + [[package]] name = "tempfile" version = "3.10.1" @@ -6366,12 +6763,29 @@ dependencies = [ "serde", ] +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits 0.2.18", + "wasm-bindgen", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.4" @@ -7380,6 +7794,30 @@ dependencies = [ "thiserror", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "3.15.2" diff --git a/Cargo.toml b/Cargo.toml index 30de838..4efef2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,8 +47,9 @@ toml = "0.8.2" serde = "1.0.170" local-ip-address = "0.6.1" url = "2.4.0" +image = "0.25.1" -# stratum server +## stratum server serde_derive = "1.0.197" serde_json = "1.0.115" tokio = {version = "1.29.1", features = ["full"] } diff --git a/app/build.gradle b/app/build.gradle index 1431656..db59bf7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,6 +2,10 @@ plugins { id 'com.android.application' } +def keystorePropertiesFile = rootProject.file("keystore.properties") +def keystoreProperties = new Properties() +keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + android { compileSdk 33 ndkVersion '26.0.10792818' @@ -14,10 +18,20 @@ android { versionName "0.1.0" } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile file(keystoreProperties['storeFile']) + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release } debug { minifyEnabled false diff --git a/img/icon.png b/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eab6886150eb21285529974afb3332b6064e21fb GIT binary patch literal 35681 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 KMx~m~yZ-@srAqq% literal 0 HcmV?d00001 diff --git a/locales/en.yml b/locales/en.yml index bc5287a..67fa5c7 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -48,6 +48,7 @@ wallets: loading: Loading closing: Closing checking: Checking + default_wallet: Default wallet wallet_loading: Loading wallet wallet_closing: Closing wallet wallet_checking: Checking wallet diff --git a/locales/ru.yml b/locales/ru.yml index 40bffd5..1427e5f 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -48,6 +48,7 @@ wallets: loading: Загружается closing: Закрывается checking: Проверяется + default_wallet: Стандартный кошелёк wallet_loading: Загрузка кошелька wallet_closing: Закрытие кошелька wallet_checking: Проверка кошелька diff --git a/src/config.rs b/src/config.rs index 880489c..93b72b0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,11 +18,11 @@ use crate::node::NodeConfig; use crate::Settings; use crate::wallet::ConnectionsConfig; -/// Application configuration. +/// Application configuration, stored at toml file. #[derive(Serialize, Deserialize)] pub struct AppConfig { /// Run node server on startup. - pub auto_start_node: bool, + pub(crate) auto_start_node: bool, /// Chain type for node and wallets. pub(crate) chain_type: ChainTypes, @@ -30,8 +30,19 @@ pub struct AppConfig { show_wallets_at_dual_panel: bool, /// Flag to show all connections at network panel or integrated node info. show_connections_network_panel: bool, + + /// Width of the desktop window. + width: f32, + /// Height of the desktop window. + height: f32, + + /// Position of the desktop window. + x: Option, y: Option } +pub const DEFAULT_WIDTH: f32 = 1200.0; +pub const DEFAULT_HEIGHT: f32 = 720.0; + impl Default for AppConfig { fn default() -> Self { Self { @@ -39,6 +50,10 @@ impl Default for AppConfig { chain_type: ChainTypes::default(), show_wallets_at_dual_panel: false, show_connections_network_panel: false, + width: DEFAULT_WIDTH, + height: DEFAULT_HEIGHT, + x: None, + y: None, } } } @@ -124,4 +139,35 @@ impl AppConfig { w_app_config.show_connections_network_panel = !show; w_app_config.save(); } + + /// Save desktop window width and height. + pub fn save_window_size(w: f32, h: f32) { + let mut w_app_config = Settings::app_config_to_update(); + w_app_config.width = w; + w_app_config.height = h; + w_app_config.save(); + } + + /// Get desktop window width and height. + pub fn window_size() -> (f32, f32) { + let r_config = Settings::app_config_to_read(); + (r_config.width, r_config.height) + } + + /// Save desktop window position. + pub fn save_window_pos(x: f32, y: f32) { + let mut w_app_config = Settings::app_config_to_update(); + w_app_config.x = Some(x); + w_app_config.y = Some(y); + w_app_config.save(); + } + + /// Get desktop window position. + pub fn window_pos() -> Option<(f32, f32)> { + let r_config = Settings::app_config_to_read(); + if r_config.x.is_some() && r_config.y.is_some() { + return Some((r_config.x.unwrap(), r_config.y.unwrap())) + } + None + } } \ No newline at end of file diff --git a/src/gui/app.rs b/src/gui/app.rs index 93e81d1..c59d242 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -17,6 +17,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use egui::{Context, Modifiers}; use lazy_static::lazy_static; +use crate::AppConfig; use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::Root; @@ -59,6 +60,15 @@ impl eframe::App for PlatformApp { if !self.root.exit_allowed { ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose); Root::show_exit_modal(); + } else { + ctx.input(|i| { + if let Some(rect) = i.viewport().inner_rect { + AppConfig::save_window_size(rect.width(), rect.height()); + } + if let Some(rect) = i.viewport().outer_rect { + AppConfig::save_window_pos(rect.left(), rect.top()); + } + }); } } diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index b91598c..fba6d4f 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -22,6 +22,7 @@ use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, NetworkContent, View, WalletsContent}; use crate::gui::views::types::ModalContainer; use crate::node::Node; +use crate::AppConfig; lazy_static! { /// Global state to check if [`NetworkContent`] panel is open. @@ -200,6 +201,11 @@ impl Root { ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { View::button_ui(ui, t!("modal_exit.exit"), Colors::WHITE, |ui| { if !Node::is_running() { self.exit_allowed = true; @@ -212,11 +218,6 @@ impl Root { } }); }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::WHITE, || { - modal.close(); - }); - }); }); ui.add_space(6.0); }); diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 01a8894..05924c5 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -18,7 +18,7 @@ use std::sync::{Arc, RwLock}; use lazy_static::lazy_static; use egui::{Align, Button, CursorIcon, Layout, PointerState, Rect, Response, RichText, Sense, Spinner, TextBuffer, TextStyle, Widget}; -use egui::epaint::{CircleShape, Color32, FontId, RectShape, Rounding, Stroke}; +use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke}; use egui::epaint::text::TextWrapping; use egui::os::OperatingSystem; use egui::text::{LayoutJob, TextFormat}; @@ -271,46 +271,6 @@ impl View { }); } - /// Draw circle [`Button`] with icon. - pub fn circle_button(ui: &mut egui::Ui, icon: &'static str, action: impl FnOnce()) { - ui.scope(|ui| { - // Setup colors. - ui.visuals_mut().widgets.inactive.bg_fill = Colors::GOLD; - ui.visuals_mut().widgets.hovered.bg_fill = Colors::GOLD; - ui.visuals_mut().widgets.active.bg_fill = Colors::YELLOW; - - // Setup radius. - let mut r = 44.0 * 0.5; - let size = egui::Vec2::splat(2.0 * r + 5.0); - let (rect, mut br) = ui.allocate_at_least(size, Sense::click_and_drag()); - br = br.on_hover_cursor(CursorIcon::PointingHand); - - let mut icon_color = Colors::TEXT; - - // Increase radius and change icon size and color on-hover. - if br.hovered() { - r = r * 1.04; - icon_color = Colors::TITLE; - } - - let visuals = ui.style().interact(&br); - ui.painter().add(CircleShape { - center: rect.center(), - radius: r, - fill: visuals.bg_fill, - stroke: Self::DEFAULT_STROKE - }); - ui.allocate_ui_at_rect(rect, |ui| { - ui.centered_and_justified(|ui| { - ui.label(RichText::new(icon).color(icon_color).size(25.0)); - }); - }); - if Self::touched(ui, br) { - (action)(); - } - }); - } - /// Default height of [`egui::TextEdit`] view. const TEXT_EDIT_HEIGHT: f32 = 37.0; @@ -524,7 +484,7 @@ impl View { pub fn center_content(ui: &mut egui::Ui, height: f32, content: impl FnOnce(&mut egui::Ui)) { ui.vertical_centered(|ui| { let mut rect = ui.available_rect_before_wrap(); - let side_margin = 24.0; + 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| { diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 2024329..a520b03 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Align, Align2, Id, Layout, Margin, RichText, Rounding, ScrollArea, Widget}; +use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea, Widget}; use crate::AppConfig; use crate::gui::Colors; @@ -159,13 +159,43 @@ impl WalletsContent { } }); + // Flag to check if wallet list is hidden on the screen. + let list_hidden = content_width == 0.0 + || (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) + || (!dual_panel && show_wallet); + + // Setup flag to show wallets bottom panel if wallet is not showing + // at non-dual panel mode and network is no open or showing at dual panel mode. + let show_bottom_panel = + (!show_wallet && !dual_panel && !Root::is_network_panel_open()) || + (dual_panel && show_wallet); + + // Show wallets bottom panel. + egui::TopBottomPanel::bottom("wallets_bottom_panel") + .frame(egui::Frame { + fill: Colors::FILL, + stroke: View::DEFAULT_STROKE, + inner_margin: Margin { + left: View::get_left_inset() + 4.0, + right: View::far_right_inset_margin(ui) + 4.0, + top: 4.0, + bottom: View::get_bottom_inset() + 4.0, + }, + ..Default::default() + }) + .show_animated_inside(ui, !list_hidden && show_bottom_panel, |ui| { + // Setup vertical padding inside buttons. + ui.style_mut().spacing.button_padding = egui::vec2(10.0, 4.0); + + ui.vertical_centered(|ui| { + View::tab_button(ui, PLUS, false, || { + self.creation_content.show_name_pass_modal(cb); + }); + }); + }); + // Show non-empty list if wallet is not creating. if !empty_list && !create_wallet { - // Flag to check if wallet list is hidden on the screen. - let list_hidden = content_width == 0.0 - || (dual_panel && show_wallet && !self.show_wallets_at_dual_panel) - || (!dual_panel && show_wallet); - // Show wallet list panel. egui::CentralPanel::default() .frame(egui::Frame { @@ -192,23 +222,8 @@ impl WalletsContent { if list_hidden { return; } - - // Setup flag to show wallet creation button if wallet is not showing - // at non-dual panel mode or showing at dual panel mode. - let show_creation_button - = (!show_wallet && !dual_panel) || (dual_panel && show_wallet); - // Show list of wallets. - let scroll = - self.wallet_list_ui(ui, dual_panel, show_creation_button, cb); - - if show_creation_button { - // Setup right margin for button. - let mut right_margin = if dual_panel { wallet_panel_width } else { 0.0 }; - if scroll { right_margin += 6.0 } - // Show wallet creation button. - self.create_wallet_btn_ui(ui, right_margin, cb); - } + self.wallet_list_ui(ui, dual_panel, cb); }); } } @@ -309,13 +324,11 @@ impl WalletsContent { } } - /// Draw list of wallets. Returns `true` if scroller is showing. + /// Draw list of wallets. fn wallet_list_ui(&mut self, ui: &mut egui::Ui, dual_panel: bool, - show_creation_btn: bool, - cb: &dyn PlatformCallbacks) -> bool { - let mut scroller_showing = false; + cb: &dyn PlatformCallbacks) { ui.scope(|ui| { // Setup scroll bar color. ui.style_mut().visuals.widgets.inactive.bg_fill = Colors::ITEM_HOVER; @@ -344,22 +357,14 @@ impl WalletsContent { self.wallets.select(Some(wallet.get_config().id)); self.show_open_wallet_modal(cb); } - // Draw wallet list item. self.wallet_item_ui(ui, wallet, cb); ui.add_space(5.0); } - // Add space for wallet creation button. - if show_creation_btn { - ui.add_space(52.0); - } }); }); }); - // Scroller is showing if content size is larger than content on the screen. - scroller_showing = scroll.content_size.y > scroll.inner_rect.size().y; }); - scroller_showing } /// Draw wallet list item. @@ -485,24 +490,6 @@ impl WalletsContent { }); } - /// Draw floating button to show wallet creation [`Modal`]. - fn create_wallet_btn_ui(&mut self, - ui: &mut egui::Ui, - right_margin: f32, - cb: &dyn PlatformCallbacks) { - egui::Window::new("create_wallet_button") - .title_bar(false) - .resizable(false) - .collapsible(false) - .anchor(Align2::RIGHT_BOTTOM, egui::Vec2::new(-6.0 - right_margin, -6.0)) - .frame(egui::Frame::default()) - .show(ui.ctx(), |ui| { - View::circle_button(ui, PLUS, || { - self.creation_content.show_name_pass_modal(cb); - }); - }); - } - /// Show [`Modal`] to open selected wallet. pub fn show_open_wallet_modal(&mut self, cb: &dyn PlatformCallbacks) { // Reset modal values. @@ -599,8 +586,14 @@ impl WalletsContent { let can_go_back = self.creation_content.can_go_back(); if can_go_back { self.creation_content.back(); + return false + } else { + if self.wallets.is_selected_open() { + self.wallets.select(None); + return false + } } - !can_go_back + true } } diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index 9795a74..793ec22 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -75,8 +75,8 @@ impl WalletCreation { stroke: View::DEFAULT_STROKE, fill: Colors::FILL_DARK, inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 4.0, - right: View::get_right_inset() + 4.0, + left: View::far_left_inset_margin(ui) + 6.0, + right: View::get_right_inset() + 6.0, top: 4.0, bottom: View::get_bottom_inset() + 4.0, }, @@ -180,7 +180,7 @@ impl WalletCreation { self.copy_or_paste_button_ui(ui, cb); } else { // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + 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. @@ -239,7 +239,6 @@ impl WalletCreation { (text, Colors::WHITE) }; - // Show next step button. View::button(ui, next_text.to_uppercase(), color, || { self.step = if let Some(step) = &self.step { @@ -297,10 +296,10 @@ impl WalletCreation { match &self.step { None => { // Show wallet creation message if step is empty. - View::center_content(ui, 415.0 + View::get_bottom_inset(), |ui| { + View::center_content(ui, 350.0 + View::get_bottom_inset(), |ui| { ui.add( egui::Image::new(egui::include_image!("../../../../../img/logo.png")) - .fit_to_exact_size(vec2(200.0, 200.0)) + .fit_to_exact_size(vec2(180.0, 180.0)) ); ui.add_space(-15.0); ui.label(RichText::new("GRIM") @@ -362,7 +361,7 @@ impl WalletCreation { pub fn show_name_pass_modal(&mut self, cb: &dyn PlatformCallbacks) { // Reset modal values. self.modal_just_opened = true; - self.name_edit = String::from(""); + self.name_edit = t!("wallets.default_wallet"); self.pass_edit = String::from(""); // Show modal. Modal::new(Self::NAME_PASS_MODAL) diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 24bdb9a..27f2b91 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -210,7 +210,7 @@ impl WalletContent { ui.vertical(|ui| { ui.add_space(3.0); // Show spendable amount. - let amount = amount_to_hr_string(data.info.amount_currently_spendable, false); + let amount = amount_to_hr_string(data.info.amount_currently_spendable, true); let amount_text = format!("{} {}", amount, GRIN); ui.label(RichText::new(amount_text).size(18.0).color(Colors::BLACK)); ui.add_space(-2.0); diff --git a/src/gui/views/wallets/wallet/info.rs b/src/gui/views/wallets/wallet/info.rs index 0b40c06..c43af07 100644 --- a/src/gui/views/wallets/wallet/info.rs +++ b/src/gui/views/wallets/wallet/info.rs @@ -79,7 +79,7 @@ impl WalletInfo { // Show non-zero awaiting confirmation amount. if amount_awaiting_conf != 0 { - let awaiting_conf = amount_to_hr_string(amount_awaiting_conf, false); + let awaiting_conf = amount_to_hr_string(amount_awaiting_conf, true); let rounding = if amount_awaiting_fin != 0 || amount_locked != 0 { [false, false, false, false] } else { @@ -93,7 +93,7 @@ impl WalletInfo { // Show non-zero awaiting finalization amount. if amount_awaiting_fin != 0 { - let awaiting_conf = amount_to_hr_string(amount_awaiting_fin, false); + let awaiting_conf = amount_to_hr_string(amount_awaiting_fin, true); let rounding = if amount_locked != 0 { [false, false, false, false] } else { @@ -107,7 +107,7 @@ impl WalletInfo { // Show non-zero locked amount. if amount_locked != 0 { - let awaiting_conf = amount_to_hr_string(amount_locked, false); + let awaiting_conf = amount_to_hr_string(amount_locked, true); View::rounded_box(ui, format!("{} ツ", awaiting_conf), t!("wallets.locked_amount"), diff --git a/src/lib.rs b/src/lib.rs index 7260520..dad94d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,8 +122,10 @@ pub fn setup_visuals(ctx: &Context) { let mut style = (*ctx.style()).clone(); // Setup spacing for buttons. style.spacing.button_padding = egui::vec2(12.0, 8.0); - // Make scroll-bar thinner. + // Make scroll-bar thinner and lighter. style.spacing.scroll.bar_width = 4.0; + style.spacing.scroll.bar_outer_margin = -2.0; + style.spacing.scroll.foreground_color = false; // Disable spacing between items. style.spacing.item_spacing = egui::vec2(0.0, 0.0); // Setup radio button/checkbox size and spacing. diff --git a/src/main.rs b/src/main.rs index 06c73db..3133bb8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::sync::Arc; +use egui::{IconData, pos2}; +use grim::AppConfig; + pub fn main() { #[allow(dead_code)] #[cfg(not(target_os = "android"))] @@ -31,8 +35,29 @@ fn real_main() { use grim::gui::PlatformApp; let platform = Desktop::default(); + + // Desktop window size. + let (width, height) = AppConfig::window_size(); + + // Setup an icon. + let icon = image::open("img/icon.png").expect("Failed to open icon path").to_rgba8(); + let (icon_width, icon_height) = icon.dimensions(); + + let mut viewport = egui::ViewportBuilder::default() + .with_inner_size([width, height]) + .with_icon(Arc::new(IconData { + rgba: icon.into_raw(), + width: icon_width, + height: icon_height, + })); + + // Desktop window position. + if let Some((x, y)) = AppConfig::window_pos() { + viewport = viewport.with_position(pos2(x, y)); + } + let options = eframe::NativeOptions { - viewport: egui::ViewportBuilder::default().with_inner_size([1200.0, 720.0]), + viewport, ..Default::default() }; grim::start(options, grim::app_creator(PlatformApp::new(platform))); diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index 2c505a9..c1a4131 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -62,7 +62,7 @@ pub struct Wallet { reopen: Arc, /// Flag to check if wallet is open. is_open: Arc, - /// Flag to check if wallet is loading. + /// Flag to check if wallet is closing. closing: Arc, /// Flag to check if wallet was deleted to remove it from the list. deleted: Arc, @@ -306,7 +306,7 @@ impl Wallet { } } - /// Check if wallet was open. + /// Check if wallet is open. pub fn is_open(&self) -> bool { self.is_open.load(Ordering::Relaxed) }