diff --git a/Cargo.lock b/Cargo.lock index 7144e09..8a20562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,15 +20,15 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.16.3" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b76d84ee70e30a4a7e39ab9018e2b17a6a09e31084176cc7c0b2dec036ba45" +checksum = "d3d3b8f9bae46a948369bc4a03e815d4ed6d616bd00de4051133a5019dc31c5a" [[package]] name = "accesskit_atspi_common" -version = "0.9.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5393c75d4666f580f4cac0a968bc97c36076bb536a129f28210dac54ee127ed" +checksum = "7c5dd55e6e94949498698daf4d48fb5659e824d7abec0d394089656ceaf99d4f" dependencies = [ "accesskit", "accesskit_consumer", @@ -40,33 +40,34 @@ dependencies = [ [[package]] name = "accesskit_consumer" -version = "0.24.3" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a12dc159d52233c43d9fe5415969433cbdd52c3d6e0df51bda7d447427b9986" +checksum = "f47983a1084940ba9a39c077a8c63e55c619388be5476ac04c804cfbd1e63459" dependencies = [ "accesskit", + "hashbrown 0.15.2", "immutable-chunkmap", ] [[package]] name = "accesskit_macos" -version = "0.17.4" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc6c1ecd82053d127961ad80a8beaa6004fb851a3a5b96506d7a6bd462403f6" +checksum = "7329821f3bd1101e03a7d2e03bd339e3ac0dc64c70b4c9f9ae1949e3ba8dece1" dependencies = [ "accesskit", "accesskit_consumer", + "hashbrown 0.15.2", "objc2", "objc2-app-kit", "objc2-foundation", - "once_cell", ] [[package]] name = "accesskit_unix" -version = "0.12.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7f5cf6165be10a54b2655fa2e0e12b2509f38ed6fc43e11c31fdb7ee6230bb" +checksum = "fcee751cc20d88678c33edaf9c07e8b693cd02819fe89053776f5313492273f5" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -82,12 +83,13 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.23.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "974e96c347384d9133427167fb8a58c340cb0496988dacceebdc1ed27071023b" +checksum = "24fcd5d23d70670992b823e735e859374d694a3d12bfd8dd32bd3bd8bedb5d81" dependencies = [ "accesskit", "accesskit_consumer", + "hashbrown 0.15.2", "paste", "static_assertions", "windows 0.58.0", @@ -96,9 +98,9 @@ dependencies = [ [[package]] name = "accesskit_winit" -version = "0.22.4" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aea3522719f1c44564d03e9469a8e2f3a98b3a8a880bd66d0789c6b9c4a669dd" +checksum = "6a6a48dad5530b6deb9fc7a52cc6c3bf72cdd9eb8157ac9d32d69f2427a5e879" dependencies = [ "accesskit", "accesskit_macos", @@ -164,9 +166,9 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-activity" @@ -261,9 +263,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arbitrary" @@ -297,7 +299,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -465,7 +467,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -500,7 +502,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -646,18 +648,18 @@ dependencies = [ [[package]] name = "bit-set" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bit_field" @@ -740,9 +742,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" dependencies = [ "bytemuck_derive", ] @@ -755,7 +757,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -772,9 +774,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bzip2" @@ -834,9 +836,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" dependencies = [ "jobserver", "libc", @@ -888,9 +890,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -912,9 +914,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -922,9 +924,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", @@ -941,14 +943,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clipboard-win" @@ -981,37 +983,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" -[[package]] -name = "com" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" -dependencies = [ - "com_macros", -] - -[[package]] -name = "com_macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" -dependencies = [ - "com_macros_support", - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "com_macros_support" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "combine" version = "4.6.7" @@ -1110,9 +1081,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -1188,17 +1159,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" -[[package]] -name = "d3d12" -version = "22.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbd1f579714e3c809ebd822c81ef148b1ceaeb3d535352afc73fd0c4c6a0017" -dependencies = [ - "bitflags 2.6.0", - "libloading", - "winapi", -] - [[package]] name = "deflate64" version = "0.1.9" @@ -1222,7 +1182,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1235,7 +1195,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.87", + "syn", ] [[package]] @@ -1255,7 +1215,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", "unicode-xid", ] @@ -1305,7 +1265,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1341,8 +1301,7 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "ecolor" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775cfde491852059e386c4e1deb4aef381c617dc364184c6f6afee99b87c402b" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "bytemuck", "emath", @@ -1351,8 +1310,7 @@ dependencies = [ [[package]] name = "eframe" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ac2645a9bf4826eb4e91488b1f17b8eaddeef09396706b2f14066461338e24f" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "ahash", "bytemuck", @@ -1361,7 +1319,7 @@ dependencies = [ "egui-wgpu", "egui-winit", "egui_glow", - "glow 0.14.2", + "glow 0.16.0", "glutin", "glutin-winit", "image", @@ -1372,7 +1330,7 @@ dependencies = [ "objc2-foundation", "parking_lot 0.12.3", "percent-encoding", - "pollster", + "pollster 0.4.0", "raw-window-handle", "static_assertions", "wasm-bindgen", @@ -1381,15 +1339,14 @@ dependencies = [ "web-time", "wgpu", "winapi", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "winit", ] [[package]] name = "egui" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53eafabcce0cb2325a59a98736efe0bf060585b437763f8c476957fb274bb974" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "accesskit", "ahash", @@ -1403,8 +1360,7 @@ dependencies = [ [[package]] name = "egui-wgpu" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00fd5d06d8405397e64a928fa0ef3934b3c30273ea7603e3dc4627b1f7a1a82" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "ahash", "bytemuck", @@ -1422,8 +1378,7 @@ dependencies = [ [[package]] name = "egui-winit" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9c430f4f816340e8e8c1b20eec274186b1be6bc4c7dfc467ed50d57abc36c6" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "accesskit_winit", "ahash", @@ -1440,8 +1395,7 @@ dependencies = [ [[package]] name = "egui_extras" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3c1f5cd8dfe2ade470a218696c66cf556fcfd701e7830fa2e9f4428292a2a1" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "ahash", "egui", @@ -1454,13 +1408,12 @@ dependencies = [ [[package]] name = "egui_glow" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e39bccc683cd43adab530d8f21a13eb91e80de10bcc38c3f1c16601b6f62b26" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "ahash", "bytemuck", "egui", - "glow 0.14.2", + "glow 0.16.0", "log", "memoffset 0.9.1", "wasm-bindgen", @@ -1477,8 +1430,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "emath" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fe0049ce51d0fb414d029e668dd72eb30bc2b739bf34296ed97bd33df544f3" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "bytemuck", ] @@ -1513,7 +1465,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1534,7 +1486,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1563,8 +1515,7 @@ dependencies = [ [[package]] name = "epaint" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a32af8da821bd4f43f2c137e295459ee2e1661d87ca8779dfa0eaf45d870e20f" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" dependencies = [ "ab_glyph", "ahash", @@ -1580,8 +1531,7 @@ dependencies = [ [[package]] name = "epaint_default_fonts" version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483440db0b7993cf77a20314f08311dbe95675092405518c0677aa08c151a3ea" +source = "git+https://github.com/emilk/egui#36a70e12c3a8a70308a4faa15799d557a5c0a064" [[package]] name = "equivalent" @@ -1591,12 +1541,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1618,9 +1568,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener", "pin-project-lite", @@ -1643,15 +1593,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -1704,15 +1654,15 @@ dependencies = [ [[package]] name = "flexi_logger" -version = "0.29.6" +version = "0.29.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26948e37cfcb1f2c2cd38e0602d3a8ab6b9472c0c6eff4516fc8def9a3124d7" +checksum = "4613c3fa90ebf91dff72ff383a9324329c017819711bda86142be81368ac6fad" dependencies = [ "chrono", "log", "nu-ansi-term", "regex", - "thiserror 1.0.69", + "thiserror 2.0.6", ] [[package]] @@ -1721,6 +1671,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1739,7 +1695,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1826,7 +1782,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -1921,9 +1877,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.13.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" dependencies = [ "js-sys", "slotmap", @@ -1933,9 +1889,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.14.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" dependencies = [ "js-sys", "slotmap", @@ -2030,15 +1986,14 @@ dependencies = [ [[package]] name = "gpu-allocator" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd4240fc91d3433d5e5b0fc5b67672d771850dc19bbee03c1381e19322803d7" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ "log", "presser", "thiserror 1.0.69", - "winapi", - "windows 0.52.0", + "windows 0.58.0", ] [[package]] @@ -2137,23 +2092,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" - -[[package]] -name = "hassle-rs" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "bitflags 2.6.0", - "com", - "libc", - "libloading", - "thiserror 1.0.69", - "widestring", - "winapi", + "foldhash", ] [[package]] @@ -2162,12 +2105,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.4.0" @@ -2206,9 +2143,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -2252,9 +2189,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -2444,7 +2381,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -2518,12 +2455,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", ] [[package]] @@ -2555,7 +2492,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -2599,9 +2536,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jni" @@ -2642,10 +2579,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2680,9 +2618,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.164" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libfuzzer-sys" @@ -2696,9 +2634,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -2712,7 +2650,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] @@ -2723,9 +2661,9 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "litrs" @@ -2886,11 +2824,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -2898,9 +2835,9 @@ dependencies = [ [[package]] name = "naga" -version = "22.1.0" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" +checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e" dependencies = [ "arrayvec", "bit-set", @@ -3066,7 +3003,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -3126,7 +3063,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -3439,7 +3376,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -3483,7 +3420,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -3517,9 +3454,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.14" +version = "0.17.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +checksum = "b67582bd5b65bdff614270e2ea89a1cf15bef71245cc1e5f7ea126977144211d" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -3536,7 +3473,7 @@ checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", "rustix", "tracing", @@ -3549,6 +3486,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "portable-pty" version = "0.8.1" @@ -3602,9 +3545,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3625,7 +3568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -3672,10 +3615,10 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls", "socket2", - "thiserror 2.0.3", + "thiserror 2.0.6", "tokio", "tracing", ] @@ -3690,11 +3633,11 @@ dependencies = [ "getrandom", "rand", "ring", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.6", "tinyvec", "tracing", "web-time", @@ -3702,9 +3645,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" +checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527" dependencies = [ "cfg_aliases 0.2.1", "libc", @@ -3872,9 +3815,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -4026,7 +3969,7 @@ dependencies = [ "objc2", "objc2-app-kit", "objc2-foundation", - "pollster", + "pollster 0.3.0", "raw-window-handle", "urlencoding", "wasm-bindgen", @@ -4070,9 +4013,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_version" @@ -4085,22 +4028,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.17" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "once_cell", "ring", @@ -4199,9 +4142,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -4217,13 +4160,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -4246,7 +4189,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -4440,9 +4383,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4521,7 +4464,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.87", + "syn", ] [[package]] @@ -4542,20 +4485,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -4564,9 +4496,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -4579,14 +4511,14 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] name = "sysinfo" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ae3f4f7d64646c46c4cae4e3f01d1c5d255c7406fdd7c7f999a94e488791" +checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af" dependencies = [ "core-foundation-sys", "libc", @@ -4667,11 +4599,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.6", ] [[package]] @@ -4682,18 +4614,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -4709,9 +4641,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "num-conv", @@ -4778,9 +4710,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -4801,17 +4733,16 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] @@ -4867,9 +4798,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -4878,20 +4809,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -4904,9 +4835,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ttf-parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "type-map" @@ -4951,9 +4882,9 @@ checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" @@ -4981,9 +4912,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -5091,9 +5022,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -5102,36 +5033,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5139,22 +5070,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-timer" @@ -5282,9 +5213,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -5302,9 +5233,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" +checksum = "ea9fe1ebb156110ff855242c1101df158b822487e4957b0556d9ffce9db0f535" dependencies = [ "block2", "core-foundation 0.10.0", @@ -5320,9 +5251,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -5335,9 +5266,9 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "22.1.0" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" +checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a" dependencies = [ "arrayvec", "cfg_aliases 0.1.1", @@ -5360,9 +5291,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "22.1.0" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" +checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ "arrayvec", "bit-vec", @@ -5385,9 +5316,9 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "22.0.0" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" +checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" dependencies = [ "android_system_properties", "arrayvec", @@ -5395,15 +5326,14 @@ dependencies = [ "bit-set", "bitflags 2.6.0", "block", + "bytemuck", "cfg_aliases 0.1.1", "core-graphics-types", - "d3d12", - "glow 0.13.1", + "glow 0.14.2", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hassle-rs", "js-sys", "khronos-egl", "libc", @@ -5425,26 +5355,21 @@ dependencies = [ "wasm-bindgen", "web-sys", "wgpu-types", - "winapi", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "22.0.0" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9d91f0e2c4b51434dfa6db77846f2793149d8e73f800fa2e41f52b8eac3c5d" +checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" dependencies = [ "bitflags 2.6.0", "js-sys", "web-sys", ] -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" - [[package]] name = "winapi" version = "0.3.9" @@ -5476,16 +5401,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.57.0" @@ -5548,7 +5463,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -5559,7 +5474,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -5570,7 +5485,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -5581,7 +5496,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -6008,15 +5923,15 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af310deaae937e48a26602b730250b4949e125f468f11e6990be3e5304ddd96f" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -6026,13 +5941,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", "synstructure", ] @@ -6128,7 +6043,7 @@ checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", "zbus-lockstep", "zbus_xml", "zvariant 4.2.0", @@ -6143,7 +6058,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn", "zvariant_utils 2.1.0", ] @@ -6156,7 +6071,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn", "zbus_names 4.1.0", "zvariant 5.1.0", "zvariant_utils 3.0.2", @@ -6216,27 +6131,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", "synstructure", ] @@ -6257,7 +6172,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -6279,14 +6194,14 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] name = "zip" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" +checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" dependencies = [ "aes", "arbitrary", @@ -6304,7 +6219,7 @@ dependencies = [ "pbkdf2", "rand", "sha1", - "thiserror 1.0.69", + "thiserror 2.0.6", "time", "zeroize", "zopfli", @@ -6415,7 +6330,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn", "zvariant_utils 2.1.0", ] @@ -6428,7 +6343,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn", "zvariant_utils 3.0.2", ] @@ -6440,7 +6355,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn", ] [[package]] @@ -6453,6 +6368,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.87", + "syn", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 77daf58..f02d0f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,11 @@ benri = "0.1.12" bytes = "1.8.0" dirs = "5.0.1" #-------------------------------------------------------------------------------- -egui = "0.29.1" -egui_extras = {version="0.29.1", features = ["image"] } +# egui = "0.29.1" +egui = {git="https://github.com/emilk/egui"} +# egui_extras = {version="0.29.1", features = ["image"] } +egui_extras = {git="https://github.com/emilk/egui", features = ["image"] } + ## 2023-12-28: https://github.com/hinto-janai/gupax/issues/68 ## ## 2024-03-18: Both `glow` and `wgpu` seem to crash: @@ -75,7 +78,8 @@ enclose = "1.2.0" bounded-vec-deque = {version="0.1.1", default-features=false} cfg-if = "1.0" flexi_logger = "0.29" -eframe = {version="0.29.1", features=["wgpu"]} +# eframe = {version="0.29.1", features=["wgpu"]} +eframe = {git="https://github.com/emilk/egui", features=["wgpu"]} strum = {version="0.26", features=["derive"]} # Unix dependencies [target.'cfg(unix)'.dependencies] @@ -97,7 +101,8 @@ sudo = "0.6.0" # linked as well which causes problems, so statically link it. lzma-sys = { version = "0.1", features = ["static"] } [dev-dependencies] -egui = {version="0.29.1", features=["callstack"]} +# egui = {version="0.29.1", features=["callstack"]} +egui = {git="https://github.com/emilk/egui", features=["callstack"]} # [target.'cfg(not(target_os = "macos"))'.dependencies] # tls-api-native-tls = "0.9.0" @@ -107,7 +112,7 @@ egui = {version="0.29.1", features=["callstack"]} # glow start on windows but not wgpu # need the same version that eframe is using with egui_wgpu # feature angle to enable support for old cpu on Windows -wgpu = {version = "22.1", features=["angle"]} +wgpu = {version = "23.0", features=["angle"]} zip = "2.2.0" is_elevated = "0.1.2" diff --git a/src/app/mod.rs b/src/app/mod.rs index ec33e39..ffeccfa 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,5 +1,13 @@ use crate::APP_DEFAULT_HEIGHT; use crate::APP_DEFAULT_WIDTH; +use crate::GUPAX_TAB_ABOUT; +use crate::GUPAX_TAB_GUPAX; +use crate::GUPAX_TAB_NODE; +use crate::GUPAX_TAB_P2POOL; +use crate::GUPAX_TAB_STATUS; +use crate::GUPAX_TAB_XMRIG; +use crate::GUPAX_TAB_XMRIG_PROXY; +use crate::GUPAX_TAB_XVB; use crate::GUPAX_VERSION; use crate::OS; use crate::cli::Cli; @@ -47,6 +55,7 @@ use log::debug; use log::error; use log::info; use log::warn; +use panels::middle::common::list_poolnode::PoolNode; use serde::Deserialize; use serde::Serialize; use std::path::PathBuf; @@ -87,10 +96,10 @@ pub struct App { pub update: Arc>, // State for update data [update.rs] pub file_window: Arc>, // State for the path selector in [Gupax] pub ping: Arc>, // Ping data found in [node.rs] - pub og_node_vec: Vec<(String, Node)>, // Manual Node database - pub node_vec: Vec<(String, Node)>, // Manual Node database - pub og_pool_vec: Vec<(String, Pool)>, // Manual Pool database - pub pool_vec: Vec<(String, Pool)>, // Manual Pool database + pub og_node_vec: Vec<(String, PoolNode)>, // Manual Node database + pub node_vec: Vec<(String, PoolNode)>, // Manual Node database + pub og_pool_vec: Vec<(String, PoolNode)>, // Manual Pool database + pub pool_vec: Vec<(String, PoolNode)>, // Manual Pool database pub diff: bool, // This bool indicates state changes // Restart state: // If Gupax updated itself, this represents that the @@ -136,7 +145,7 @@ pub struct App { // Static stuff pub benchmarks: Vec, // XMRig CPU benchmarks pub pid: sysinfo::Pid, // Gupax's PID - pub max_threads: usize, // Max amount of detected system threads + pub max_threads: u16, // Max amount of detected system threads pub now: Instant, // Internal timer pub exe: String, // Path for [Gupax] binary pub dir: String, // Directory [Gupax] binary is in @@ -311,7 +320,7 @@ impl App { pub_sys, benchmarks, pid, - max_threads: benri::threads!(), + max_threads: benri::threads!() as u16, now, admin: false, exe: String::new(), @@ -511,65 +520,83 @@ impl App { } // Handle [node_vec] overflow info!("App Init | Handling [node_vec] overflow"); - if og.p2pool.selected_index > app.og_node_vec.len() { + if og.p2pool.selected_node.index > app.og_node_vec.len() { warn!( "App | Overflowing manual node index [{} > {}]", - og.p2pool.selected_index, + og.p2pool.selected_node.index, app.og_node_vec.len() ); let (name, node) = match app.og_node_vec.first() { Some(zero) => zero.clone(), None => Node::new_tuple(), }; - og.p2pool.selected_index = 0; - og.p2pool.selected_name.clone_from(&name); - og.p2pool.selected_ip.clone_from(&node.ip); - og.p2pool.selected_rpc.clone_from(&node.rpc); - og.p2pool.selected_zmq.clone_from(&node.zmq); - app.state.p2pool.selected_index = 0; - app.state.p2pool.selected_name = name; - app.state.p2pool.selected_ip = node.ip; - app.state.p2pool.selected_rpc = node.rpc; - app.state.p2pool.selected_zmq = node.zmq; + og.p2pool.selected_node.index = 0; + og.p2pool.selected_node.name.clone_from(&name); + og.p2pool + .selected_node + .ip + .clone_from(&node.ip().to_string()); + og.p2pool + .selected_node + .rpc + .clone_from(&node.port().to_string()); + og.p2pool + .selected_node + .zmq_rig + .clone_from(&node.custom().to_string()); + app.state.p2pool.selected_node.index = 0; + app.state.p2pool.selected_node.name = name; + app.state.p2pool.selected_node.ip = node.ip().to_string(); + app.state.p2pool.selected_node.rpc = node.port().to_string(); + app.state.p2pool.selected_node.zmq_rig = node.custom().to_string(); } // Handle [pool_vec] overflow info!("App Init | Handling [pool_vec] overflow..."); - if og.xmrig.selected_index > app.og_pool_vec.len() { + if og.xmrig.selected_pool.index > app.og_pool_vec.len() { warn!( "App | Overflowing manual pool index [{} > {}], resetting to 1", - og.xmrig.selected_index, + og.xmrig.selected_pool.index, app.og_pool_vec.len() ); let (name, pool) = match app.og_pool_vec.first() { Some(zero) => zero.clone(), None => Pool::new_tuple(), }; - og.xmrig.selected_index = 0; - og.xmrig.selected_name.clone_from(&name); - og.xmrig.selected_ip.clone_from(&pool.ip); - og.xmrig.selected_port.clone_from(&pool.port); - app.state.xmrig.selected_index = 0; - app.state.xmrig.selected_name = name; - app.state.xmrig.selected_ip = pool.ip; - app.state.xmrig.selected_port = pool.port; - if og.xmrig_proxy.selected_index > app.og_pool_vec.len() { + og.xmrig.selected_pool.index = 0; + og.xmrig.selected_pool.name.clone_from(&name); + og.xmrig.selected_pool.ip.clone_from(&pool.ip().to_string()); + og.xmrig + .selected_pool + .rpc + .clone_from(&pool.port().to_string()); + app.state.xmrig.selected_pool.index = 0; + app.state.xmrig.selected_pool.name = name; + app.state.xmrig.selected_pool.ip = pool.ip().to_string(); + app.state.xmrig.selected_pool.rpc = pool.port().to_string(); + if og.xmrig_proxy.selected_pool.index > app.og_pool_vec.len() { warn!( "App | Overflowing manual pool index [{} > {}], resetting to 1", - og.xmrig_proxy.selected_index, + og.xmrig_proxy.selected_pool.index, app.og_pool_vec.len() ); let (name, pool) = match app.og_pool_vec.first() { Some(zero) => zero.clone(), None => Pool::new_tuple(), }; - og.xmrig_proxy.selected_index = 0; - og.xmrig_proxy.selected_name.clone_from(&name); - og.xmrig_proxy.selected_ip.clone_from(&pool.ip); - og.xmrig_proxy.selected_port.clone_from(&pool.port); - app.state.xmrig_proxy.selected_index = 0; - app.state.xmrig_proxy.selected_name = name; - app.state.xmrig_proxy.selected_ip = pool.ip; - app.state.xmrig_proxy.selected_port = pool.port; + og.xmrig_proxy.selected_pool.index = 0; + og.xmrig_proxy.selected_pool.name.clone_from(&name); + og.xmrig_proxy + .selected_pool + .ip + .clone_from(&pool.ip().to_string()); + og.xmrig_proxy + .selected_pool + .rpc + .clone_from(&pool.port().to_string()); + app.state.xmrig_proxy.selected_pool.index = 0; + app.state.xmrig_proxy.selected_pool.name = name; + app.state.xmrig_proxy.selected_pool.ip = pool.ip().to_string(); + app.state.xmrig_proxy.selected_pool.rpc = pool.port().to_string(); } } @@ -648,7 +675,7 @@ impl App { #[cold] #[inline(never)] - pub fn gather_backup_hosts(&self) -> Option> { + pub fn gather_backup_hosts(&self) -> Option> { if !self.state.p2pool.backup_host { return None; } @@ -695,7 +722,7 @@ impl App { zmq: zmq.into(), }; - vec.push(node); + vec.push(PoolNode::Node(node)); } if vec.is_empty() { @@ -747,6 +774,18 @@ impl Tab { Tab::Xvb => Some(ProcessName::Xvb), } } + pub fn msg_default_tab(&self) -> &str { + match self { + Tab::About => GUPAX_TAB_ABOUT, + Tab::Status => GUPAX_TAB_STATUS, + Tab::Gupax => GUPAX_TAB_GUPAX, + Tab::Node => GUPAX_TAB_NODE, + Tab::P2pool => GUPAX_TAB_P2POOL, + Tab::Xmrig => GUPAX_TAB_XMRIG, + Tab::XmrigProxy => GUPAX_TAB_XMRIG_PROXY, + Tab::Xvb => GUPAX_TAB_XVB, + } + } } //---------------------------------------------------------------------------------------------------- [Restart] Enum #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/app/panels/bottom.rs b/src/app/panels/bottom.rs index b634d1a..826b19d 100644 --- a/src/app/panels/bottom.rs +++ b/src/app/panels/bottom.rs @@ -207,16 +207,30 @@ impl crate::app::App { let restart_msg = format!("Restart {}", name); if process.waiting { ui.add_enabled_ui(false, |ui| { - ui.add_sized(size, Button::new("⟲")) + ui.add_sized(size, Button::new("▶")) .on_disabled_hover_text(process.run_middle_msg()); ui.add(Separator::default().grow(0.0)); ui.add_sized(size, Button::new("⏹")) .on_disabled_hover_text(process.run_middle_msg()); ui.add(Separator::default().grow(0.0)); - ui.add_sized(size, Button::new("▶")) + ui.add_sized(size, Button::new("⟲")) .on_disabled_hover_text(process.run_middle_msg()); }); } else if process.alive { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text(start_msg) + }); + ui.add(Separator::default().grow(0.0)); + if key.is_down() && !wants_input + || ui + .add_sized(size, Button::new("⏹")) + .on_hover_text(stop_msg) + .clicked() + { + process.stop(&self.helper); + } + ui.add(Separator::default().grow(0.0)); if key.is_up() && !wants_input || ui .add_sized(size, Button::new("⟲")) @@ -274,29 +288,7 @@ impl crate::app::App { } } } - ui.add(Separator::default().grow(0.0)); - if key.is_down() && !wants_input - || ui - .add_sized(size, Button::new("⏹")) - .on_hover_text(stop_msg) - .clicked() - { - process.stop(&self.helper); - } - ui.add(Separator::default().grow(0.0)); - ui.add_enabled_ui(false, |ui| { - ui.add_sized(size, Button::new("▶")) - .on_disabled_hover_text(start_msg) - }); } else { - ui.add_enabled_ui(false, |ui| { - ui.add_sized(size, Button::new("⟲")) - .on_disabled_hover_text(restart_msg); - ui.add(Separator::default().grow(0.0)); - ui.add_sized(size, Button::new("⏹")) - .on_disabled_hover_text(stop_msg); - ui.add(Separator::default().grow(0.0)); - }); let text_err = self.start_ready(process).err().unwrap_or_default(); let ui_enabled = text_err.is_empty(); ui.add_enabled_ui(ui_enabled, |ui| { @@ -354,6 +346,14 @@ impl crate::app::App { } } }); + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text(stop_msg); + ui.add(Separator::default().grow(0.0)); + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text(restart_msg); + ui.add(Separator::default().grow(0.0)); + }); } }); } diff --git a/src/app/panels/middle/common/console.rs b/src/app/panels/middle/common/console.rs new file mode 100644 index 0000000..48218e2 --- /dev/null +++ b/src/app/panels/middle/common/console.rs @@ -0,0 +1,73 @@ +use std::sync::{Arc, Mutex}; + +use egui::{Label, TextEdit, TextStyle, Ui}; + +use crate::{DARK_GRAY, helper::Process, miscs::height_txt_before_button, regex::num_lines}; + +pub fn console(ui: &mut Ui, text: &str) { + let nb_lines = num_lines(text); + let height = ui.available_height() / 2.8; + egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { + ui.style_mut().override_text_style = Some(TextStyle::Small); + egui::ScrollArea::vertical() + .stick_to_bottom(true) + .max_width(ui.available_width()) + .max_height(height) + .auto_shrink([false; 2]) + // .show_viewport(ui, |ui, _| { + .show_rows( + ui, + ui.text_style_height(&TextStyle::Small), + nb_lines, + |ui, row_range| { + for i in row_range { + if let Some(line) = text.lines().nth(i) { + ui.label(line); + } + } + }, + ); + }); +} + +// input args +pub fn input_args_field( + ui: &mut Ui, + buffer: &mut String, + process: &Arc>, + hint: &str, + hover: &str, +) { + ui.style_mut().spacing.text_edit_width = ui.available_width(); + let response = ui + .add(TextEdit::hint_text(TextEdit::singleline(buffer), hint)) + .on_hover_text(hover); + // If the user pressed enter, dump buffer contents into the process STDIN + if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { + response.request_focus(); // Get focus back + let buffer = std::mem::take(buffer); // Take buffer + let mut process = process.lock().unwrap(); + if process.is_alive() { + process.input.push(buffer); + } // Push only if alive + } +} + +// Command arguments +pub fn start_options_field(ui: &mut Ui, arguments: &mut String, hint: &str, hover: &str) { + ui.group(|ui| { + ui.horizontal(|ui| { + ui.add_sized( + [0.0, height_txt_before_button(ui, &TextStyle::Body)], + Label::new("Command arguments:"), + ); + ui.style_mut().spacing.text_edit_width = ui.available_width(); + ui.add(TextEdit::hint_text(TextEdit::singleline(arguments), hint)) + .on_hover_text(hover); + arguments.truncate(1024); + }) + }); + if !arguments.is_empty() { + ui.disable(); + } +} diff --git a/src/app/panels/middle/common/header_tab.rs b/src/app/panels/middle/common/header_tab.rs new file mode 100644 index 0000000..f287ce1 --- /dev/null +++ b/src/app/panels/middle/common/header_tab.rs @@ -0,0 +1,83 @@ +use egui::{Hyperlink, Image, Separator, TextStyle, TextWrapMode, Ui}; +use log::debug; + +use crate::SPACE; +/// logo first, first hyperlink will be the header, description under. +/// will take care of centering widgets if boerder weight is more than 0. +#[allow(clippy::needless_range_loop)] +pub fn header_tab( + ui: &mut Ui, + logo: Option, + // text, link, hover text + links: &[(&str, &str, &str)], + subtitle: Option, + one_line_center: bool, +) { + // width - logo and links and separators divided by double the size of logo (can't know width of links). + ui.style_mut().wrap_mode = Some(TextWrapMode::Extend); + ui.style_mut().override_text_style = Some(TextStyle::Heading); + ui.add_space(SPACE); + if one_line_center { + let height = 64.0; + let nb_links = links.len(); + let border_weight = ((ui.available_width() + - ((height * 4.0 * nb_links as f32) + if logo.is_some() { height * 2.0 } else { 0.0 })) + / (height * 2.0)) + .max(0.0) as usize; + // nb_columns add logo if exist plus number of links with separator for each + number of column for border space + let nb_columns = if logo.is_some() { 1 } else { 0 } + (links.len() * 2) + border_weight * 2; + ui.columns(nb_columns, |col| { + // first column for left border + for n in 0..(border_weight) { + col[n].vertical_centered(|ui| ui.add_space(0.0)); + debug!("left side space: {}", n); + } + // jump first column, stop less 2, because begin at 0 and end with space column. + let mut nb_col = border_weight; + if let Some(logo) = logo { + debug!("logo: {}", nb_col); + col[nb_col].vertical_centered(|ui| ui.add_sized([height, height], logo)); + nb_col += 1; + } + for link in links { + debug!("separator: {}", nb_col); + col[nb_col].vertical_centered(|ui| { + ui.add_sized( + [height / 8.0, height], + Separator::default().vertical().spacing(height / 8.0), + ) + }); + nb_col += 1; + + debug!("link: {}", nb_col); + col[nb_col].vertical_centered(|ui| { + ui.add_sized( + [ui.available_width(), height], + Hyperlink::from_label_and_url(link.0, link.1), + ); + }); + nb_col += 1; + } + + for n in nb_col..(nb_col + border_weight) { + debug!("right side border space: {}", n); + col[n].vertical_centered(|ui| ui.add_space(0.0)); + } + }); + } else { + // top down + ui.vertical_centered(|ui| { + if let Some(source) = logo { + ui.add(source); + } + for link in links { + ui.hyperlink_to(link.0, link.1); + } + ui.style_mut().override_text_style = Some(TextStyle::Body); + }); + } + if let Some(desc) = subtitle { + ui.label(desc); + } + ui.add_space(SPACE); +} diff --git a/src/app/panels/middle/common/list_poolnode.rs b/src/app/panels/middle/common/list_poolnode.rs new file mode 100644 index 0000000..93cd16c --- /dev/null +++ b/src/app/panels/middle/common/list_poolnode.rs @@ -0,0 +1,330 @@ +use egui::{Button, ComboBox, RichText, SelectableLabel, Ui}; +use log::{debug, info}; + +use crate::{ + LIST_ADD, LIST_CLEAR, LIST_DELETE, LIST_SAVE, + disk::{node::Node, pool::Pool, state::SelectedPoolNode}, +}; +#[derive(Clone, Debug, PartialEq)] +pub enum PoolNode { + Node(Node), + Pool(Pool), +} + +impl PoolNode { + pub fn ip(&self) -> &str { + match &self { + PoolNode::Node(n) => &n.ip, + PoolNode::Pool(p) => &p.ip, + } + } + pub fn port(&self) -> &str { + match &self { + PoolNode::Node(n) => &n.rpc, + PoolNode::Pool(p) => &p.port, + } + } + pub fn custom(&self) -> &str { + match &self { + PoolNode::Node(n) => &n.zmq, + PoolNode::Pool(p) => &p.rig, + } + } + pub fn custom_name(&self) -> &str { + match &self { + PoolNode::Node(_) => "ZMQ", + PoolNode::Pool(_) => "rig", + } + } + fn set_ip(&mut self, new_ip: String) { + match self { + PoolNode::Node(n) => n.ip = new_ip, + PoolNode::Pool(p) => p.ip = new_ip, + } + } + fn set_port(&mut self, new_port: String) { + match self { + PoolNode::Node(n) => n.rpc = new_port, + PoolNode::Pool(p) => p.port = new_port, + } + } + fn set_custom(&mut self, new_custom: String) { + match self { + PoolNode::Node(n) => n.zmq = new_custom, + PoolNode::Pool(p) => p.rig = new_custom, + } + } + // pub fn from_vec_node(vec_node: Vec<(String, Node)>) -> Vec<(String, Self)> { + // vec_node + // .into_iter() + // .map(|(name, node)| (name, PoolNode::Node(node))) + // .collect() + // } + // pub fn from_vec_pool(vec_node: Vec<(String, Pool)>) -> Vec<(String, Self)> { + // vec_node + // .into_iter() + // .map(|(name, pool)| (name, PoolNode::Pool(pool))) + // .collect() + // } +} +/// compatible for P2Pool and Xmrig/Proxy +/// current is (name, ip, port, zmq/rig) +pub fn list_poolnode( + ui: &mut Ui, + current: &mut (&mut String, &mut String, &mut String, &mut String), + selected: &mut SelectedPoolNode, + node_vec: &mut Vec<(String, PoolNode)>, + incorrect_input: bool, +) { + ui.vertical(|ui| { + ui.spacing_mut().item_spacing.y = 0.0; + // ui.spacing_mut().button_padding.x = ui.available_width() / 2.0; + let width = ui.available_width(); + // [Manual node selection] + // [Ping List] + debug!("P2Pool Tab | Rendering [Node List]"); + // [Menu] + menu_list_node(ui, node_vec, width, selected, current); + let node_vec_len = node_vec.len(); + // [Add/Save] + ui.horizontal(|ui| { + add_save_node( + ui, + selected, + node_vec, + current, + node_vec_len, + incorrect_input, + ); + }); + // [Delete] + ui.horizontal(|ui| { + delete_node(ui, selected, node_vec, current, node_vec_len); + }); + // [Clear] + ui.horizontal(|ui| { + clear_node(ui, current); + }); + }); +} +// slider H/s + +fn clear_node(ui: &mut Ui, current: &mut (&mut String, &mut String, &mut String, &mut String)) { + ui.add_enabled_ui( + !current.0.is_empty() + || !current.1.is_empty() + || !current.2.is_empty() + || !current.3.is_empty(), + |ui| { + if ui + .add_sized([ui.available_width(), 0.0], Button::new("Clear")) + .on_hover_text(LIST_CLEAR) + .clicked() + { + current.0.clear(); + current.1.clear(); + current.2.clear(); + current.3.clear(); + } + }, + ); +} +fn menu_list_node( + ui: &mut Ui, + node_vec: &mut [(String, PoolNode)], + width: f32, + selected: &mut SelectedPoolNode, + current: &mut (&mut String, &mut String, &mut String, &mut String), +) { + let text = RichText::new(format!("{}. {}", selected.index + 1, selected.name)); + ComboBox::from_id_salt("manual_nodes") + .selected_text(text) + .width(width) + .show_ui(ui, |ui| { + for (n, (name, node)) in node_vec.iter().enumerate() { + let text = RichText::new(format!( + "{}. {}\n IP: {}\n RPC: {}\n {}: {}", + n + 1, + name, + node.ip(), + node.port(), + node.custom_name(), + node.custom() + )); + if ui + .add(SelectableLabel::new(selected.name == **name, text)) + .clicked() + { + selected.index = n; + let node = node.clone(); + selected.name.clone_from(name); + selected.ip.clone_from(&node.ip().to_string()); + selected.rpc.clone_from(&node.port().to_string()); + selected.zmq_rig.clone_from(&node.custom().to_string()); + current.0.clone_from(name); + *current.1 = node.ip().to_string(); + *current.2 = node.port().to_string(); + *current.3 = node.custom().to_string(); + } + } + }); +} +fn add_save_node( + ui: &mut Ui, + selected: &mut SelectedPoolNode, + node_vec: &mut Vec<(String, PoolNode)>, + current: &mut (&mut String, &mut String, &mut String, &mut String), + node_vec_len: usize, + incorrect_input: bool, +) { + // list should never be empty unless state edited by hand. + let is_node = matches!(node_vec[0].1, PoolNode::Node(_)); + // [Add/Save] + let mut exists = false; + let mut save_diff = true; + let mut existing_index = 0; + for (name, node) in node_vec.iter() { + if *name == *current.0 { + exists = true; + if *current.1 == node.ip() && *current.2 == node.port() && *current.3 == node.custom() { + save_diff = false; + } + break; + } + existing_index += 1; + } + let text = if exists { LIST_SAVE } else { LIST_ADD }; + let text = format!( + "{}\n Currently selected node: {}. {}\n Current amount of {}: {}/1000", + text, + selected.index + 1, + selected.name, + if is_node { "nodes" } else { "pools" }, + node_vec_len + ); + // If the node already exists, show [Save] and mutate the already existing node + if exists { + ui.add_enabled_ui(!incorrect_input && save_diff, |ui| { + if ui + .add_sized([ui.available_width(), 0.0], Button::new("Save")) + .on_hover_text(text) + .clicked() + { + let ip = current.1.clone(); + let rpc = current.2.clone(); + // zmq can be rig in case of Pool + let zmq = current.3.clone(); + let poolnode = &mut node_vec[existing_index].1; + poolnode.set_ip(ip); + poolnode.set_port(rpc); + poolnode.set_custom(zmq); + info!( + "Node | S | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, {}: {}]", + existing_index + 1, + current.0, + current.1, + current.2, + poolnode.custom_name(), + current.3 + ); + selected.index = existing_index; + selected.ip.clone_from(current.1); + selected.rpc.clone_from(current.2); + selected.zmq_rig.clone_from(current.3); + } + }); + // Else, add to the list + } else { + ui.add_enabled_ui(!incorrect_input && node_vec_len < 1000, |ui| { + if ui + .add_sized([ui.available_width(), 0.0], Button::new("Add")) + .on_hover_text(text) + .clicked() + { + let ip = current.1.clone(); + let rpc = current.2.clone(); + // zmq can be rig in case of Pool + let zmq = current.3.clone(); + let poolnode = match node_vec[existing_index].1 { + PoolNode::Node(_) => PoolNode::Node(Node { ip, rpc, zmq }), + PoolNode::Pool(_) => PoolNode::Pool(Pool { + rig: zmq, + ip, + port: rpc, + }), + }; + info!( + "Node | A | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, {}: {}]", + node_vec_len, + current.0, + current.1, + current.2, + poolnode.custom_name(), + current.3 + ); + node_vec.push((current.0.clone(), poolnode)); + selected.index = node_vec_len; + selected.name.clone_from(current.0); + selected.ip.clone_from(current.1); + selected.rpc.clone_from(current.2); + selected.zmq_rig.clone_from(current.3); + } + }); + } +} + +fn delete_node( + ui: &mut Ui, + selected: &mut SelectedPoolNode, + node_vec: &mut Vec<(String, PoolNode)>, + current: &mut (&mut String, &mut String, &mut String, &mut String), + node_vec_len: usize, +) { + ui.add_enabled_ui(node_vec_len > 1, |ui| { + let text = format!( + "{}\n Currently selected node: {}. {}\n Current amount of nodes: {}/1000", + LIST_DELETE, + selected.index + 1, + selected.name, + node_vec_len + ); + if ui + .add_sized([ui.available_width(), 0.0], Button::new("Delete")) + .on_hover_text(text) + .clicked() + { + let new_name; + let new_node; + match selected.index { + 0 => { + new_name = node_vec[1].0.clone(); + new_node = node_vec[1].1.clone(); + node_vec.remove(0); + } + _ => { + node_vec.remove(selected.index); + selected.index -= 1; + new_name = node_vec[selected.index].0.clone(); + new_node = node_vec[selected.index].1.clone(); + } + }; + selected.name.clone_from(&new_name); + selected.ip = new_node.ip().to_string(); + selected.rpc = new_node.port().to_string(); + selected.zmq_rig = new_node.custom().to_string(); + *current.0 = new_name; + *current.1 = new_node.ip().to_string(); + *current.2 = new_node.port().to_string(); + *current.3 = new_node.custom().to_string(); + info!( + "Node | D | [index: {}, name: \"{}\", ip: \"{}\", port: {}, {}: {}]", + selected.index, + selected.name, + selected.ip, + selected.rpc, + new_node.custom_name(), + selected.zmq_rig + ); + } + }); +} diff --git a/src/app/panels/middle/common/mod.rs b/src/app/panels/middle/common/mod.rs new file mode 100644 index 0000000..05d780b --- /dev/null +++ b/src/app/panels/middle/common/mod.rs @@ -0,0 +1,4 @@ +pub mod console; +pub mod header_tab; +pub mod list_poolnode; +pub mod state_edit_field; diff --git a/src/app/panels/middle/common/state_edit_field.rs b/src/app/panels/middle/common/state_edit_field.rs new file mode 100644 index 0000000..a5f0670 --- /dev/null +++ b/src/app/panels/middle/common/state_edit_field.rs @@ -0,0 +1,212 @@ +use std::ops::RangeInclusive; +use std::sync::{Arc, Mutex}; + +use egui::{Color32, Label, RichText, Slider, TextEdit}; +use egui::{TextStyle, Ui}; + +use crate::components::gupax::{FileType, FileWindow}; +use crate::disk::state::Gupax; +use crate::miscs::height_txt_before_button; +use crate::regex::Regexes; +use crate::{ + GREEN, GUPAX_SELECT, LIGHT_GRAY, NODE_DB_DIR, NODE_DB_PATH_EMPTY, NODE_PATH_OK, RED, SPACE, +}; + +pub fn slider_state_field( + ui: &mut Ui, + description: &str, + hover_msg: &str, + field: &mut u16, + range: RangeInclusive, +) { + ui.horizontal(|ui| { + ui.add_sized( + [0.0, height_txt_before_button(ui, &TextStyle::Body)], + Label::new(description), + ); + // not sure what's the right calculation to make + ui.style_mut().spacing.slider_width = (ui.available_width() + - ui.spacing().item_spacing.x * 4.0 + - ui.spacing().scroll.bar_width + - SPACE * 2.0 + + 2.0) + .max(80.0); + ui.add_sized( + [0.0, height_txt_before_button(ui, &TextStyle::Body)], + Slider::new(field, range), + ) + .on_hover_text(hover_msg); + }); +} + +pub struct StateTextEdit<'a> { + description: &'a str, + max_ch: u8, + help_msg: &'a str, + validations: &'a [fn(&str) -> bool], + text_edit_width: f32, + text_style: TextStyle, +} +#[allow(unused)] +impl<'a> StateTextEdit<'a> { + pub fn new(ui: &Ui) -> Self { + StateTextEdit { + description: "", + max_ch: 30, + help_msg: "", + validations: &[], + text_edit_width: ui.text_style_height(&TextStyle::Body) * 18.0, + text_style: TextStyle::Body, + } + } + pub fn build(self, ui: &mut Ui, state_field: &mut String) -> bool { + let mut valid = false; + ui.horizontal_centered(|ui| { + let color; + let symbol; + let mut input_validated = true; + let len; + let inside_space; + for v in self.validations { + if !v(state_field) { + input_validated = false; + } + } + if state_field.is_empty() { + symbol = "➖"; + color = Color32::LIGHT_GRAY; + } else if input_validated { + symbol = "✔"; + color = Color32::from_rgb(100, 230, 100); + valid = true; + } else { + symbol = "❌"; + color = Color32::from_rgb(230, 50, 50); + } + match self.max_ch { + x if x >= 100 => { + len = format!("{:03}", state_field.len()); + inside_space = ""; + } + 10..99 => { + len = format!("{:02}", state_field.len()); + inside_space = " "; + } + _ => { + len = format!("{}", state_field.len()); + inside_space = " "; + } + } + let text = format!( + "{}[{}{}/{}{}]{}", + self.description, inside_space, len, self.max_ch, inside_space, symbol + ); + ui.add_sized( + [0.0, height_txt_before_button(ui, &self.text_style)], + Label::new(RichText::new(text).color(color)), + ); + // allocate the size to leave half of the total width free. + ui.spacing_mut().text_edit_width = self.text_edit_width; + ui.text_edit_singleline(state_field) + .on_hover_text(self.help_msg); + state_field.truncate(self.max_ch.into()); + }); + valid + } + pub fn description(mut self, description: &'a str) -> Self { + self.description = description; + self + } + pub fn max_ch(mut self, max_ch: u8) -> Self { + self.max_ch = max_ch; + self + } + pub fn help_msg(mut self, help_msg: &'a str) -> Self { + self.help_msg = help_msg; + self + } + pub fn validations(mut self, validations: &'a [fn(&str) -> bool]) -> Self { + self.validations = validations; + self + } + pub fn text_style(mut self, text_style: TextStyle) -> Self { + self.text_style = text_style; + self + } + pub fn text_edit_width(mut self, text_edit_width: f32) -> Self { + self.text_edit_width = text_edit_width; + self + } + pub fn text_edit_width_half_left(mut self, ui: &Ui) -> Self { + self.text_edit_width = ui.available_width() / 2.0; + self + } + pub fn text_edit_width_same_as_max_ch(mut self, ui: &Ui) -> Self { + self.text_edit_width = ui.text_style_height(&self.text_style) * self.max_ch as f32; + self + } +} + +// path to choose +pub fn path_db_field(ui: &mut Ui, path: &mut String, file_window: &Arc>) { + ui.horizontal(|ui| { + let symbol; + let color; + let hover; + if path.is_empty() { + symbol = "➖"; + color = LIGHT_GRAY; + hover = NODE_DB_PATH_EMPTY; + } else if !Gupax::path_is_dir(path) { + symbol = "❌"; + color = RED; + hover = NODE_DB_DIR; + } else { + symbol = "✔"; + color = GREEN; + hover = NODE_PATH_OK; + } + let text = ["Node Database Directory ", symbol].concat(); + ui.add_sized( + [0.0, height_txt_before_button(ui, &TextStyle::Body)], + Label::new(RichText::new(text).color(color)), + ); + let window_busy = file_window.lock().unwrap().thread; + ui.add_enabled_ui(!window_busy, |ui| { + if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { + Gupax::spawn_file_window_thread(file_window, FileType::NodeDB); + } + ui.spacing_mut().text_edit_width = ui.available_width(); + ui.text_edit_singleline(path).on_hover_text(hover); + }); + }); +} +pub fn monero_address_field(address: &mut String, ui: &mut Ui, hover: &str) { + ui.group(|ui| { + let text; + let color; + let len = format!("{:02}", address.len()); + if address.is_empty() { + text = format!("Monero Address [{}/95] ➖", len); + color = Color32::LIGHT_GRAY; + } else if Regexes::addr_ok(address) { + text = format!("Monero Address [{}/95] ✔", len); + color = Color32::from_rgb(100, 230, 100); + } else { + text = format!("Monero Address [{}/95] ❌", len); + color = Color32::from_rgb(230, 50, 50); + } + ui.style_mut().spacing.text_edit_width = ui.available_width(); + ui.vertical_centered(|ui| { + ui.label(RichText::new(text).color(color)); + // ui.set_max_width(95.0 * 3.0); + ui.add_space(SPACE); + ui.add( + TextEdit::hint_text(TextEdit::singleline(address), "4...") + .horizontal_align(egui::Align::Center), + ) + .on_hover_text(hover); + address.truncate(95); + }); + }); +} diff --git a/src/app/panels/middle/gupax.rs b/src/app/panels/middle/gupax.rs index 31915e4..874f2a9 100644 --- a/src/app/panels/middle/gupax.rs +++ b/src/app/panels/middle/gupax.rs @@ -5,10 +5,13 @@ use crate::components::gupax::*; use crate::components::update::Update; use crate::components::update::check_binary_path; use crate::disk::state::*; +use crate::miscs::height_txt_before_button; use log::debug; use std::path::Path; use std::sync::Arc; use std::sync::Mutex; +use strum::EnumCount; +use strum::IntoEnumIterator; impl Gupax { #[inline(always)] // called once #[allow(clippy::too_many_arguments)] @@ -20,7 +23,6 @@ impl Gupax { file_window: &Arc>, error_state: &mut ErrorState, restart: &Arc>, - size: Vec2, _frame: &mut eframe::Frame, _ctx: &egui::Context, ui: &mut egui::Ui, @@ -28,33 +30,30 @@ impl Gupax { ) { // Update button + Progress bar debug!("Gupaxx Tab | Rendering [Update] button + progress bar"); + let height_font = ui.text_style_height(&TextStyle::Body); egui::ScrollArea::vertical().show(ui, |ui| { + ui.style_mut().spacing.item_spacing = [height_font, height_font].into(); ui.group(|ui| { - let button = if self.simple { - size.y / 5.0 - } else { - size.y / 15.0 - }; - let height = if self.simple { - size.y / 5.0 - } else { - size.y / 10.0 - }; - let width = size.x - SPACE; let updating = *update.lock().unwrap().updating.lock().unwrap(); - ui.vertical(|ui| { + ui.vertical_centered(|ui| { + ui.add_space(height_font); + ui.style_mut().spacing.button_padding = ui.style().spacing.button_padding * 3.0; // If [Gupax] is being built for a Linux distro, // disable built-in updating completely. #[cfg(feature = "distro")] ui.disable(); #[cfg(feature = "distro")] - ui.add_sized([width, button], Button::new("Updates are disabled")) + // ui.add_sized([width, button], Button::new("Updates are disabled")) + // .on_disabled_hover_text(DISTRO_NO_UPDATE); + ui.button("Updates are disabled") .on_disabled_hover_text(DISTRO_NO_UPDATE); #[cfg(not(feature = "distro"))] ui.add_enabled_ui(!updating && *restart.lock().unwrap() == Restart::No, |ui| { #[cfg(not(feature = "distro"))] + // if ui + // .add_sized([width, button], Button::new("Check for updates")) if ui - .add_sized([width, button], Button::new("Check for updates")) + .button("Check for updates") .on_hover_text(GUPAX_UPDATE) .clicked() { @@ -68,8 +67,6 @@ impl Gupax { ); } }); - }); - ui.vertical(|ui| { ui.add_enabled_ui(updating, |ui| { let prog = *update.lock().unwrap().prog.lock().unwrap(); let msg = format!( @@ -78,479 +75,346 @@ impl Gupax { prog, "%" ); - ui.add_sized([width, height * 1.4], Label::new(RichText::new(msg))); - let height = height / 2.0; - let size = vec2(width, height); + ui.label(msg); if updating { - ui.add_sized(size, Spinner::new().size(height)); + ui.spinner(); } else { - ui.add_sized(size, Label::new("...")); + ui.label("..."); } - ui.add_sized( - size, - ProgressBar::new( - update.lock().unwrap().prog.lock().unwrap().round() / 100.0, - ), - ); + ui.add(ProgressBar::new( + update.lock().unwrap().prog.lock().unwrap().round() / 100.0, + )); }); }); }); - debug!("Gupaxx Tab | Rendering bool buttons"); - ui.horizontal(|ui| { - ui.group(|ui| { - egui::ScrollArea::horizontal().show(ui, |ui| { - let width = (size.x - SPACE * 17.0) / 8.0; - let height = if self.simple { - size.y / 10.0 - } else { - size.y / 15.0 - }; - let size = vec2(width, height); - ui.style_mut().override_text_style = Some(egui::TextStyle::Small); - ui.add_sized(size, Checkbox::new(&mut self.auto_update, "Auto-Update")) - .on_hover_text(GUPAX_AUTO_UPDATE); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.bundled, "Bundle")) - .on_hover_text(GUPAX_BUNDLED_UPDATE); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.auto_node, "Auto-Node")) - .on_hover_text(GUPAX_AUTO_NODE); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool")) - .on_hover_text(GUPAX_AUTO_P2POOL); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig")) - .on_hover_text(GUPAX_AUTO_XMRIG); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.auto_xp, "Auto-Proxy")) - .on_hover_text(GUPAX_AUTO_XMRIG_PROXY); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.auto_xvb, "Auto-XvB")) - .on_hover_text(GUPAX_AUTO_XVB); - ui.separator(); - ui.add_sized( - size, - Checkbox::new(&mut self.ask_before_quit, "Confirm quit"), - ) - .on_hover_text(GUPAX_ASK_BEFORE_QUIT); - ui.separator(); - ui.add_sized( - size, - Checkbox::new(&mut self.save_before_quit, "Save on quit"), - ) - .on_hover_text(GUPAX_SAVE_BEFORE_QUIT); - }); + // debug!("Gupaxx Tab | Rendering bool buttons"); + ui.group(|ui| { + ui.vertical_centered(|ui| { + ui.add(Label::new( + RichText::new("Default Behaviour") + .underline() + .color(LIGHT_GRAY), + )) }); + ui.separator(); + self.horizontal_flex_auto_start(ui, AutoStart::ALL); }); - if self.simple { return; } - debug!("Gupaxx Tab | Rendering P2Pool/XMRig path selection"); - // P2Pool/XMRig binary path selection + debug!("Gupaxx Tab | Rendering Node/P2Pool/XMRig/XMRig-Proxy path selection"); // need to clone bool so file_window is not locked across a thread let window_busy = file_window.lock().unwrap().thread.to_owned(); - let height = size.y / 28.0; - let text_edit = (ui.available_width() / 10.0) - SPACE; ui.group(|ui| { - ui.add_sized( - [ui.available_width(), height / 2.0], - Label::new( - RichText::new("Node/P2Pool/XMRig/XMRig-Proxy PATHs") - .underline() - .color(LIGHT_GRAY), - ), - ) - .on_hover_text("Gupaxx is online"); - ui.separator(); - ui.horizontal(|ui| { - if self.node_path.is_empty() { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("Node Binary Path ➖").color(LIGHT_GRAY)), - ) - .on_hover_text(NODE_PATH_EMPTY); - } else if !Self::path_is_file(&self.node_path) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("Node Binary Path ❌").color(RED)), - ) - .on_hover_text(NODE_PATH_NOT_FILE); - } else if !check_binary_path(&self.node_path, ProcessName::Node) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("Node Binary Path ❌").color(RED)), - ) - .on_hover_text(NODE_PATH_NOT_VALID); - } else { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("Node Binary Path ✔").color(GREEN)), - ) - .on_hover_text(NODE_PATH_OK); - } - ui.spacing_mut().text_edit_width = ui.available_width() - SPACE; - ui.add_enabled_ui(!window_busy, |ui| { - if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { - Self::spawn_file_window_thread(file_window, FileType::Node); - } - ui.add_sized( - [ui.available_width(), height], - TextEdit::singleline(&mut self.node_path), - ) - .on_hover_text(GUPAX_PATH_NODE); - }); - }); - ui.horizontal(|ui| { - if self.p2pool_path.is_empty() { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("P2Pool Binary Path ➖").color(LIGHT_GRAY)), - ) - .on_hover_text(P2POOL_PATH_EMPTY); - } else if !Self::path_is_file(&self.p2pool_path) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("P2Pool Binary Path ❌").color(RED)), - ) - .on_hover_text(P2POOL_PATH_NOT_FILE); - } else if !check_binary_path(&self.p2pool_path, ProcessName::P2pool) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("P2Pool Binary Path ❌").color(RED)), - ) - .on_hover_text(P2POOL_PATH_NOT_VALID); - } else { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("P2Pool Binary Path ✔").color(GREEN)), - ) - .on_hover_text(P2POOL_PATH_OK); - } - ui.spacing_mut().text_edit_width = ui.available_width() - SPACE; - ui.add_enabled_ui(!window_busy, |ui| { - if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { - Self::spawn_file_window_thread(file_window, FileType::P2pool); - } - ui.add_sized( - [ui.available_width(), height], - TextEdit::singleline(&mut self.p2pool_path), - ) - .on_hover_text(GUPAX_PATH_P2POOL); - }); - }); - ui.horizontal(|ui| { - if self.xmrig_path.is_empty() { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("XMRig Binary Path ➖").color(LIGHT_GRAY)), - ) - .on_hover_text(XMRIG_PATH_EMPTY); - } else if !Self::path_is_file(&self.xmrig_path) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("XMRig Binary Path ❌").color(RED)), - ) - .on_hover_text(XMRIG_PATH_NOT_FILE); - } else if !check_binary_path(&self.xmrig_path, ProcessName::Xmrig) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("XMRig Binary Path ❌").color(RED)), - ) - .on_hover_text(XMRIG_PATH_NOT_VALID); - } else { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("XMRig Binary Path ✔").color(GREEN)), - ) - .on_hover_text(XMRIG_PATH_OK); - } - ui.spacing_mut().text_edit_width = ui.available_width() - SPACE; - ui.add_enabled_ui(!window_busy, |ui| { - if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { - Self::spawn_file_window_thread(file_window, FileType::Xmrig); - } - ui.add_sized( - [ui.available_width(), height], - TextEdit::singleline(&mut self.xmrig_path), - ) - .on_hover_text(GUPAX_PATH_XMRIG); - }); - }); - ui.horizontal(|ui| { - if self.xmrig_proxy_path.is_empty() { - ui.add_sized( - [text_edit, height], - Label::new( - RichText::new("XMRig-Proxy Binary Path ➖").color(LIGHT_GRAY), - ), - ) - .on_hover_text(XMRIG_PROXY_PATH_EMPTY); - } else if !Self::path_is_file(&self.xmrig_proxy_path) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("XMRig-Proxy Binary Path ❌").color(RED)), - ) - .on_hover_text(XMRIG_PROXY_PATH_NOT_FILE); - } else if !crate::components::update::check_binary_path( - &self.xmrig_proxy_path, - ProcessName::XmrigProxy, - ) { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("XMRig-Proxy Binary Path ❌").color(RED)), - ) - .on_hover_text(XMRIG_PROXY_PATH_NOT_VALID); - } else { - ui.add_sized( - [text_edit, height], - Label::new(RichText::new("XMRig-Proxy Binary Path ✔").color(GREEN)), - ) - .on_hover_text(XMRIG_PROXY_PATH_OK); - } - ui.spacing_mut().text_edit_width = ui.available_width() - SPACE; - ui.add_enabled_ui(!window_busy, |ui| { - if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { - Self::spawn_file_window_thread(file_window, FileType::XmrigProxy); - } - ui.add_sized( - [ui.available_width(), height], - TextEdit::singleline(&mut self.xmrig_proxy_path), - ) - .on_hover_text(GUPAX_PATH_XMRIG_PROXY); + ui.push_id(2, |ui| { + ui.vertical_centered(|ui| { + ui.add(Label::new( + RichText::new("Node/P2Pool/XMRig/XMRig-Proxy PATHs") + .underline() + .color(LIGHT_GRAY), + )) + .on_hover_text("Gupaxx is online"); + }); + ui.separator(); + ScrollArea::horizontal().show(ui, |ui| { + ui.vertical(|ui| { + BundledProcess::iter().for_each(|name| { + path_binary( + self.path_binary(&name), + name.process_name(), + ui, + window_busy, + file_window, + ) + }); + }); }); }); + let mut guard = file_window.lock().unwrap(); + if guard.picked_p2pool { + self.p2pool_path.clone_from(&guard.p2pool_path); + guard.picked_p2pool = false; + } + if guard.picked_xmrig { + self.xmrig_path.clone_from(&guard.xmrig_path); + guard.picked_xmrig = false; + } + if guard.picked_xp { + self.xmrig_proxy_path.clone_from(&guard.xmrig_proxy_path); + guard.picked_xp = false; + } + if guard.picked_node { + self.node_path.clone_from(&guard.node_path); + guard.picked_node = false; + } + drop(guard); }); - let mut guard = file_window.lock().unwrap(); - if guard.picked_p2pool { - self.p2pool_path.clone_from(&guard.p2pool_path); - guard.picked_p2pool = false; - } - if guard.picked_xmrig { - self.xmrig_path.clone_from(&guard.xmrig_path); - guard.picked_xmrig = false; - } - if guard.picked_xp { - self.xmrig_proxy_path.clone_from(&guard.xmrig_proxy_path); - guard.picked_xp = false; - } - if guard.picked_node { - self.node_path.clone_from(&guard.node_path); - guard.picked_node = false; - } - drop(guard); - - let height = ui.available_height() / 6.0; - // Saved [Tab] debug!("Gupaxx Tab | Rendering [Tab] selector"); ui.group(|ui| { - let width = (size.x / 7.0) - (SPACE * 1.93); - let size = vec2(width, height); - ui.add_sized( - [ui.available_width(), height / 2.0], - Label::new(RichText::new("Default Tab").underline().color(LIGHT_GRAY)), - ) - .on_hover_text(GUPAX_TAB); + ui.vertical_centered(|ui| { + ui.add(Label::new( + RichText::new("Default Tab").underline().color(LIGHT_GRAY), + )) + .on_hover_text(GUPAX_TAB); + }); ui.separator(); - ui.horizontal(|ui| { - if ui - .add_sized(size, SelectableLabel::new(self.tab == Tab::About, "About")) - .on_hover_text(GUPAX_TAB_ABOUT) - .clicked() - { - self.tab = Tab::About; - } - ui.separator(); - if ui - .add_sized( - size, - SelectableLabel::new(self.tab == Tab::Status, "Status"), - ) - .on_hover_text(GUPAX_TAB_STATUS) - .clicked() - { - self.tab = Tab::Status; - } - ui.separator(); - if ui - .add_sized(size, SelectableLabel::new(self.tab == Tab::Gupax, "Gupaxx")) - .on_hover_text(GUPAX_TAB_GUPAX) - .clicked() - { - self.tab = Tab::Gupax; - } - ui.separator(); - if ui - .add_sized(size, SelectableLabel::new(self.tab == Tab::Node, "Node")) - .on_hover_text(GUPAX_TAB_NODE) - .clicked() - { - self.tab = Tab::Node; - } - ui.separator(); - if ui - .add_sized( - size, - SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"), - ) - .on_hover_text(GUPAX_TAB_P2POOL) - .clicked() - { - self.tab = Tab::P2pool; - } - ui.separator(); - if ui - .add_sized(size, SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig")) - .on_hover_text(GUPAX_TAB_XMRIG) - .clicked() - { - self.tab = Tab::Xmrig; - } - if ui - .add_sized(size, SelectableLabel::new(self.tab == Tab::Xvb, "XvB")) - .on_hover_text(GUPAX_TAB_XVB) - .clicked() - { - self.tab = Tab::Xvb; - } - }) + ui.push_id(1, |ui| { + ScrollArea::horizontal().show(ui, |ui| { + ui.horizontal(|ui| { + let width = (ui.available_width() / Tab::COUNT as f32) + - (ui.spacing().button_padding.y * 2.0 + + ui.spacing().item_spacing.x) + - SPACE; + Tab::iter().enumerate().for_each(|(count, tab)| { + if ui + .add_sized( + [width, height_txt_before_button(ui, &TextStyle::Button)], + SelectableLabel::new(self.tab == tab, tab.to_string()), + ) + .on_hover_text(tab.msg_default_tab()) + .clicked() + { + self.tab = tab; + } + + if count + 1 != Tab::COUNT { + ui.separator(); + } + }) + }); + }); + }); }); // Gupax App resolution sliders debug!("Gupaxx Tab | Rendering resolution sliders"); ui.group(|ui| { - ui.add_sized( - [ui.available_width(), height / 2.0], - Label::new( - RichText::new("Width/Height Adjust") + ui.vertical_centered(|ui| { + ui.add(Label::new( + RichText::new("Width/Height/Scaling Adjustment") .underline() .color(LIGHT_GRAY), - ), - ) - .on_hover_text(GUPAX_ADJUST); - ui.separator(); - ui.vertical(|ui| { - let width = size.x / 10.0; - ui.spacing_mut().icon_width = width / 25.0; - ui.spacing_mut().slider_width = width * 7.6; - match self.ratio { - Ratio::None => (), - Ratio::Width => { - let width = self.selected_width as f64; - let height = (width / 1.333).round(); - self.selected_height = height as u16; - } - Ratio::Height => { - let height = self.selected_height as f64; - let width = (height * 1.333).round(); - self.selected_width = width as u16; - } - } - let height = height / 3.5; - let size = vec2(width, height); - ui.horizontal(|ui| { - ui.add_enabled_ui(self.ratio != Ratio::Height, |ui| { - ui.add_sized( - size, - Label::new(format!( - " Width [{}-{}]:", - APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16 - )), - ); - ui.add_sized( - size, - Slider::new( - &mut self.selected_width, - APP_MIN_WIDTH as u16..=APP_MAX_WIDTH as u16, - ), - ) - .on_hover_text(GUPAX_WIDTH); - }); - }); - ui.horizontal(|ui| { - ui.add_enabled_ui(self.ratio != Ratio::Width, |ui| { - ui.add_sized( - size, - Label::new(format!( - "Height [{}-{}]:", - APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16 - )), - ); - ui.add_sized( - size, - Slider::new( - &mut self.selected_height, - APP_MIN_HEIGHT as u16..=APP_MAX_HEIGHT as u16, - ), - ) - .on_hover_text(GUPAX_HEIGHT); - }); - }); - ui.horizontal(|ui| { - ui.add_sized( - size, - Label::new(format!("Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")), - ); - ui.add_sized( - size, - Slider::new(&mut self.selected_scale, APP_MIN_SCALE..=APP_MAX_SCALE) - .step_by(0.1), - ) - .on_hover_text(GUPAX_SCALE); - }); + )) + .on_hover_text(GUPAX_ADJUST); + ui.separator(); }); - ui.style_mut().override_text_style = Some(egui::TextStyle::Button); - ui.separator(); - // Width/Height locks ui.horizontal(|ui| { - use Ratio::*; - let width = (size.x / 4.0) - (SPACE * 1.5); - let size = vec2(width, height); - if ui - .add_sized( - size, - SelectableLabel::new(self.ratio == Width, "Lock to width"), - ) - .on_hover_text(GUPAX_LOCK_WIDTH) - .clicked() - { - self.ratio = Width; - } - ui.separator(); - if ui - .add_sized( - size, - SelectableLabel::new(self.ratio == Height, "Lock to height"), - ) - .on_hover_text(GUPAX_LOCK_HEIGHT) - .clicked() - { - self.ratio = Height; - } - ui.separator(); - if ui - .add_sized(size, SelectableLabel::new(self.ratio == None, "No lock")) - .on_hover_text(GUPAX_NO_LOCK) - .clicked() - { - self.ratio = None; - } - if ui - .add_sized(size, Button::new("Set")) - .on_hover_text(GUPAX_SET) - .clicked() - { - let size = - Vec2::new(self.selected_width as f32, self.selected_height as f32); - ui.ctx() - .send_viewport_cmd(egui::viewport::ViewportCommand::InnerSize(size)); - *must_resize = true; - } - }) + ScrollArea::horizontal().show(ui, |ui| { + ui.vertical(|ui| { + match self.ratio { + Ratio::None => (), + Ratio::Width => { + let width = self.selected_width as f64; + let height = (width / 1.333).round(); + self.selected_height = height as u16; + } + Ratio::Height => { + let height = self.selected_height as f64; + let width = (height * 1.333).round(); + self.selected_width = width as u16; + } + } + // let height = height / 3.5; + // let size = vec2(width, height); + ui.horizontal(|ui| { + ui.add_enabled_ui(self.ratio != Ratio::Height, |ui| { + ui.label(format!( + " Width [{}-{}]:", + APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16 + )); + ui.add(Slider::new( + &mut self.selected_width, + APP_MIN_WIDTH as u16..=APP_MAX_WIDTH as u16, + )) + .on_hover_text(GUPAX_WIDTH); + }); + }); + ui.horizontal(|ui| { + ui.add_enabled_ui(self.ratio != Ratio::Width, |ui| { + ui.label(format!( + " Height [{}-{}]:", + APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16 + )); + ui.add(Slider::new( + &mut self.selected_height, + APP_MIN_HEIGHT as u16..=APP_MAX_HEIGHT as u16, + )) + .on_hover_text(GUPAX_HEIGHT); + }); + }); + ui.horizontal(|ui| { + ui.label(format!(" Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")); + ui.add( + Slider::new( + &mut self.selected_scale, + APP_MIN_SCALE..=APP_MAX_SCALE, + ) + .step_by(0.1), + ) + .on_hover_text(GUPAX_SCALE); + }); + }); + ui.style_mut().override_text_style = Some(egui::TextStyle::Button); + ui.separator(); + // Width/Height locks + ui.vertical(|ui| { + use Ratio::*; + ui.horizontal(|ui| { + if ui + .selectable_label(self.ratio == Width, "Lock to width") + .on_hover_text(GUPAX_LOCK_WIDTH) + .clicked() + { + self.ratio = Width; + } + ui.separator(); + if ui + .selectable_label(self.ratio == Height, "Lock to height") + .on_hover_text(GUPAX_LOCK_HEIGHT) + .clicked() + { + self.ratio = Height; + } + ui.separator(); + if ui + .selectable_label(self.ratio == None, "No lock") + .on_hover_text(GUPAX_NO_LOCK) + .clicked() + { + self.ratio = None; + } + ui.separator(); + if ui.button("Set").on_hover_text(GUPAX_SET).clicked() { + let size = Vec2::new( + self.selected_width as f32, + self.selected_height as f32, + ); + ui.ctx().send_viewport_cmd( + egui::viewport::ViewportCommand::InnerSize(size), + ); + *must_resize = true; + } + }); + }); + }) + }); + }); + }); + } + /// widget: AutoStart variant and selectable label (true) or checkbox (false) + pub fn horizontal_flex_auto_start(&mut self, ui: &mut Ui, auto_starts: &[AutoStart]) { + let text_style = TextStyle::Button; + // let height = ui.style().text_styles.get(&text_style).unwrap().size; + ui.style_mut().override_text_style = Some(text_style); + // width = (width - / number of tab) - (space between widget * 2.0 + space of separator / 2.0) + // ui.style_mut().spacing.item_spacing.x = 4.0; + let spacing = 2.0; + // ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| { + // ui.horizontal(|ui| { + ScrollArea::horizontal().show(ui, |ui| { + ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| { + let width = (((ui.available_width()) / auto_starts.len() as f32) + - ((ui.style().spacing.item_spacing.x * 2.0) + (spacing / 2.0))) + .max(0.0); + // TODO: calculate minimum width needed, if ui.available width is less, show items on two lines, then on 3 etc.. + // checkbox padding + item spacing + text + separator + let size = [width, 0.0]; + let len = auto_starts.iter().len(); + for (count, auto) in auto_starts.iter().enumerate() { + ui.horizontal(|ui| { + ui.vertical(|ui| { + ui.horizontal(|ui| { + let mut is_checked = self.auto.is_enabled(auto); + let widget = Checkbox::new(&mut is_checked, auto.to_string()); + + if ui + .add_sized(size, widget) + .on_hover_text(auto.help_msg()) + .clicked() + { + self.auto.enable(auto, is_checked); + } + }); + // add a space to prevent selectable button to be at the same line as the end of the top bar. Make it the same spacing as separators. + ui.add_space(spacing * 4.0); + }); + if count + 1 != len { + ui.add(Separator::default().spacing(spacing).vertical()); + } + }); + } }); }); } } +fn path_binary( + path: &mut String, + name: ProcessName, + ui: &mut Ui, + window_busy: bool, + file_window: &Arc>, +) { + // align correctly even with different length of name by adapting the space just after. + let flex_space = " ".repeat( + ProcessName::iter() + .enumerate() + .max_by(|(_, a), (_, b)| { + a.to_string() + .len() + .partial_cmp(&b.to_string().len()) + .expect("ProcessName should have values") + }) + .expect("Iterator cant' be empty") + .1 + .to_string() + .len() + - name.to_string().len() + + 1, + ); + let msg = format!(" {name}{flex_space}Binary Path"); + // need to precise the height of text or there will be an misalignment with the button if it's bigger than the text. + let height = + (ui.style().spacing.button_padding.y * 2.0) + ui.text_style_height(&TextStyle::Body); + ui.horizontal(|ui| { + if path.is_empty() { + ui.add_sized( + [0.0, height], + Label::new(RichText::new(msg + " ➖").color(LIGHT_GRAY)), + ) + .on_hover_text(name.msg_binary_path_empty()); + } else if !Gupax::path_is_file(path) { + ui.add_sized( + [0.0, height], + Label::new(RichText::new(msg + " ❌").color(RED)), + ) + .on_hover_text(name.msg_binary_path_not_file()); + } else if !check_binary_path(path, name) { + ui.add_sized( + [0.0, height], + Label::new(RichText::new(msg + " ❌").color(RED)), + ) + .on_hover_text(name.msg_binary_path_invalid()); + } else { + ui.add_sized( + [0.0, height], + Label::new(RichText::new(msg + " ✔").color(GREEN)), + ) + .on_hover_text(name.msg_binary_path_ok()); + } + ui.spacing_mut().text_edit_width = (ui.available_width() - SPACE).max(0.0); + ui.add_enabled_ui(!window_busy, |ui| { + if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { + Gupax::spawn_file_window_thread( + file_window, + name.file_type() + .expect("XvB process should not be called in a function related to path"), + ); + } + ui.text_edit_singleline(path) + .on_hover_text(name.msg_path_edit()); + }); + }); +} diff --git a/src/app/panels/middle/mod.rs b/src/app/panels/middle/mod.rs index 36654fd..720a8a0 100644 --- a/src/app/panels/middle/mod.rs +++ b/src/app/panels/middle/mod.rs @@ -1,11 +1,15 @@ use crate::app::Tab; use crate::app::eframe_impl::ProcessStatesGui; use crate::app::keys::KeyPressed; +use crate::components::gupax::FileWindow; use crate::helper::ProcessName; +use crate::regex::REGEXES; use crate::utils::constants::*; +use common::state_edit_field::StateTextEdit; use egui::*; use log::debug; mod about; +pub mod common; mod gupax; mod node; mod p2pool; @@ -47,7 +51,6 @@ impl crate::app::App { self.max_threads, &self.gupax_p2pool_api, &self.benchmarks, - self.size, ctx, ui, ); @@ -62,7 +65,6 @@ impl crate::app::App { &self.file_window, &mut self.error_state, &self.restart, - self.size, frame, ctx, ui, @@ -76,7 +78,6 @@ impl crate::app::App { &self.node, &self.node_api, &mut self.node_stdin, - self.size, &self.file_window, ui, ); @@ -91,7 +92,6 @@ impl crate::app::App { &self.p2pool, &self.p2pool_api, &mut self.p2pool_stdin, - self.size, ctx, ui, ); @@ -104,7 +104,6 @@ impl crate::app::App { &self.xmrig, &self.xmrig_api, &mut self.xmrig_stdin, - self.size, ctx, ui, ); @@ -117,7 +116,6 @@ impl crate::app::App { &mut self.pool_vec, &self.xmrig_proxy_api, &mut self.xmrig_proxy_stdin, - self.size, ui, ); } @@ -125,7 +123,6 @@ impl crate::app::App { debug!("App | Entering [XvB] Tab"); crate::disk::state::Xvb::show( &mut self.state.xvb, - self.size, &self.state.p2pool.address, ctx, ui, @@ -139,3 +136,49 @@ impl crate::app::App { }); } } + +// Common widgets that will appears on multiple panels. + +// header + +// console + +// sliders in/out peers/log + +// menu node + +// premade state edit field +// return boolean to know if the field input is validated. +fn rpc_port_field(field: &mut String, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" RPC PORT ") + .max_ch(5) + .help_msg(NODE_API_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, field) +} +fn zmq_port_field(field: &mut String, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" ZMQ PORT ") + .max_ch(5) + .help_msg(NODE_ZMQ_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, field) +} +fn rpc_bind_field(field: &mut String, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description("RPC BIND IP ") + .max_ch(255) + .help_msg(NODE_API_BIND) + .validations(&[|x| REGEXES.ipv4.is_match(x), |x| REGEXES.domain.is_match(x)]) + .build(ui, field) +} + +fn zmq_bind_field(field: &mut String, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description("API BIND IP ") + .max_ch(255) + .help_msg(NODE_ZMQ_BIND) + .validations(&[|x| REGEXES.ipv4.is_match(x), |x| REGEXES.domain.is_match(x)]) + .build(ui, field) +} diff --git a/src/app/panels/middle/node.rs b/src/app/panels/middle/node.rs index 2ab6dfb..b282dd2 100644 --- a/src/app/panels/middle/node.rs +++ b/src/app/panels/middle/node.rs @@ -1,21 +1,19 @@ +use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field}; +use crate::app::panels::middle::common::state_edit_field::{path_db_field, slider_state_field}; +use crate::app::panels::middle::{rpc_bind_field, rpc_port_field, zmq_bind_field, zmq_port_field}; use crate::{ - GUPAX_SELECT, NODE_API_BIND, NODE_API_PORT, NODE_ARGUMENTS, NODE_DB_DIR, NODE_DB_PATH_EMPTY, - NODE_DNS_BLOCKLIST, NODE_DNS_CHECKPOINT, NODE_INPUT, NODE_PATH_OK, NODE_PRUNNING, NODE_URL, - NODE_ZMQ_BIND, NODE_ZMQ_PORT, + NODE_ARGUMENTS, NODE_DNS_BLOCKLIST, NODE_DNS_CHECKPOINT, NODE_INPUT, NODE_PRUNNING, NODE_URL, }; -use egui::{Color32, Label, RichText, Slider, TextEdit, TextStyle, Ui, Vec2}; -use regex::Regex; +use egui::{Label, TextStyle}; use std::sync::{Arc, Mutex}; use log::debug; -use crate::components::gupax::{FileType, FileWindow}; -use crate::disk::state::{Gupax, Node}; +use crate::components::gupax::FileWindow; +use crate::disk::state::Node; use crate::helper::Process; use crate::helper::node::PubNodeApi; -use crate::regex::{REGEXES, num_lines}; -use crate::utils::constants::DARK_GRAY; -use crate::{GREEN, LIGHT_GRAY, P2POOL_IN, P2POOL_LOG, P2POOL_OUT, RED, SPACE}; +use crate::{P2POOL_IN, P2POOL_LOG, P2POOL_OUT, SPACE}; impl Node { #[inline(always)] // called once @@ -24,102 +22,47 @@ impl Node { process: &Arc>, api: &Arc>, buffer: &mut String, - size: Vec2, file_window: &Arc>, ui: &mut egui::Ui, ) { - let width = size.x; - let height = size.y; - let space_h = height / 48.0; - let text_height = size.y / 25.0; - let txt_description_width = size.x * 0.1; + ui.style_mut().override_text_style = Some(TextStyle::Body); + ui.vertical_centered(|ui| { + ui.add_space(SPACE); + ui.style_mut().override_text_style = Some(TextStyle::Heading); + ui.hyperlink_to("Monerod", NODE_URL); + ui.style_mut().override_text_style = None; + ui.add(Label::new("C++ Monero Node")); + ui.add_space(SPACE); + }); + // console output for log + debug!("Node Tab | Rendering [Console]"); egui::ScrollArea::vertical().show(ui, |ui| { - ui.vertical_centered(|ui| { - ui.add_space(space_h); - ui.style_mut().override_text_style = Some(TextStyle::Heading); - ui.hyperlink_to("Monerod", NODE_URL); - ui.style_mut().override_text_style = Some(TextStyle::Body); - ui.add(Label::new("C++ Monero Node")); - ui.add_space(space_h); - }); - // console output for log - debug!("Node Tab | Rendering [Console]"); + let text = &api.lock().unwrap().output; ui.group(|ui| { - let text = &api.lock().unwrap().output; - let nb_lines = num_lines(text); - let height = size.y / 2.8; - let width = (size.x - (space_h / 2.0)).max(0.0); - egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { - ui.style_mut().override_text_style = Some(TextStyle::Small); - egui::ScrollArea::vertical() - .stick_to_bottom(true) - .max_width(width) - .max_height(height) - .auto_shrink([false; 2]) - // .show_viewport(ui, |ui, _| { - .show_rows( - ui, - ui.text_style_height(&TextStyle::Small), - nb_lines, - |ui, row_range| { - for i in row_range { - if let Some(line) = text.lines().nth(i) { - ui.label(line); - } - } - }, - ); - }); + console(ui, text); + if !self.simple { + ui.separator(); + input_args_field( + ui, + buffer, + process, + r#"Commands: help, status, set_log , diff"#, + NODE_INPUT, + ); + } }); //---------------------------------------------------------------------------------------------------- [Advanced] Console if !self.simple { - ui.separator(); - let response = ui - .add_sized( - [width, text_height], - TextEdit::hint_text( - TextEdit::singleline(buffer), - r#"Commands: help, status, set_log , diff"#, - ), - ) - .on_hover_text(NODE_INPUT); - // If the user pressed enter, dump buffer contents into the process STDIN - if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { - response.request_focus(); // Get focus back - let buffer = std::mem::take(buffer); // Take buffer - let mut process = process.lock().unwrap(); // Lock - if process.is_alive() { - process.input.push(buffer); - } // Push only if alive - } - //---------------------------------------------------------------------------------------------------- Arguments debug!("Node Tab | Rendering [Arguments]"); - ui.group(|ui| { - ui.horizontal(|ui| { - ui.add_sized( - [txt_description_width, text_height], - Label::new("Command arguments:"), - ); - ui.add_sized( - [ui.available_width(), text_height], - TextEdit::hint_text( - TextEdit::singleline(&mut self.arguments), - r#"--zmq-pub tcp://127.0.0.1:18081"#, - ), - ) - .on_hover_text(NODE_ARGUMENTS); - self.arguments.truncate(1024); - }) - }); - if !self.arguments.is_empty() { - ui.disable(); - } + start_options_field( + ui, + &mut self.arguments, + r#"--zmq-pub tcp://127.0.0.1:18081"#, + NODE_ARGUMENTS, + ); //---------------------------------------------------------------------------------------------------- Prunned checkbox - ui.add_space(space_h); - ui.style_mut().spacing.icon_width_inner = width / 45.0; - ui.style_mut().spacing.icon_width = width / 35.0; - ui.style_mut().spacing.icon_spacing = space_h; + ui.add_space(SPACE); debug!("Node Tab | Rendering DNS and Prunning buttons"); ui.horizontal(|ui| { ui.group(|ui| { @@ -134,276 +77,69 @@ impl Node { }); }); - ui.add_space(space_h); - // idea - // need to warn the user if local firewall is blocking port - // need to warn the user if NAT is blocking port - // need to show local ip address - // need to show public ip - // text edit width is 4x bigger than description. Which makes half of the total width on screen less a space. - - // (width - (width - ui.available_width()) - (ui.spacing().item_spacing.x * 4.5)) - // / 2.0; + ui.add_space(SPACE); + // // idea + // // need to warn the user if local firewall is blocking port + // // need to warn the user if NAT is blocking port + // // need to show local ip address + // // need to show public ip ui.horizontal(|ui| { - ui.group(|ui| { - ui.vertical(|ui| { - rpc_bind_field(self, ui, txt_description_width, text_height, width); - rpc_port_field(self, ui, txt_description_width, text_height, width); - ui.add_space(space_h); - zmq_bind_field(self, ui, txt_description_width, text_height, width); - zmq_port_field(self, ui, txt_description_width, text_height, width); - }); - }); - - //---------------------------------------------------------------------------------------------------- In/Out peers - debug!("Node Tab | Rendering sliders elements"); - ui.vertical(|ui| { + egui::ScrollArea::horizontal().show(ui, |ui| { ui.group(|ui| { - ui.style_mut().override_text_style = Some(TextStyle::Small); - ui.horizontal(|ui| { - // ui.label("Out peers [10-450]:"); - ui.add_sized( - [txt_description_width, text_height], - Label::new("Out peers [2-450]:"), - ); - // not sure what's the right calculation to make - ui.style_mut().spacing.slider_width = (ui.available_width() - - ui.spacing().item_spacing.x * 4.0 - - ui.spacing().scroll.bar_width - - (SPACE * 2.0)) - .max(0.0); - ui.add(Slider::new(&mut self.out_peers, 2..=450)) - .on_hover_text(P2POOL_OUT); - // ui.add_space(ui.available_width() - 4.0); + ui.vertical(|ui| { + rpc_bind_field(&mut self.api_ip, ui); + rpc_port_field(&mut self.api_port, ui); + ui.add_space(SPACE); + zmq_bind_field(&mut self.zmq_ip, ui); + zmq_port_field(&mut self.zmq_port, ui); }); - ui.horizontal(|ui| { - // ui.label("In peers [10-450]:"); - ui.add_sized( - [txt_description_width, text_height], - Label::new("In peers [2-450]:"), + }); + + //---------------------------------------------------------------------------------------------------- In/Out peers + debug!("Node Tab | Rendering sliders elements"); + ui.vertical(|ui| { + ui.group(|ui| { + ui.add_space(SPACE); + slider_state_field( + ui, + "Out peers [2-450]:", + P2POOL_OUT, + &mut self.out_peers, + 2..=450, ); - ui.style_mut().spacing.slider_width = (ui.available_width() - - ui.spacing().item_spacing.x * 4.0 - - ui.spacing().scroll.bar_width - - (SPACE * 2.0)) - .max(0.0); - ui.add(Slider::new(&mut self.in_peers, 2..=450)) - .on_hover_text(P2POOL_IN); - }); - ui.horizontal(|ui| { - // ui.label("Log level [ 0-4 ]:"); - ui.add_sized( - [txt_description_width, text_height], - Label::new("Log level [ 0-4 ] :"), + ui.add_space(SPACE); + slider_state_field( + ui, + "In peers [2-450]:", + P2POOL_IN, + &mut self.in_peers, + 2..=450, ); - ui.style_mut().spacing.slider_width = (ui.available_width() - - ui.spacing().item_spacing.x * 4.0 - - ui.spacing().scroll.bar_width - - (SPACE * 2.0)) - .max(0.0); - ui.add(Slider::new(&mut self.log_level, 0..=4)) - .on_hover_text(P2POOL_LOG); + ui.add_space(SPACE); + slider_state_field( + ui, + "Log level [ 0-4 ]:", + P2POOL_LOG, + &mut self.log_level, + 0..=6, + ); + ui.add_space(SPACE); }); }); }); }); //---------------------------------------------------------------------------------------------------- DB path - ui.add_space(space_h); + ui.add_space(SPACE); ui.group(|ui| { - path_db_field(self, ui, txt_description_width, text_height, file_window); + path_db_field(ui, &mut self.path_db, file_window); + let mut guard = file_window.lock().unwrap(); + if guard.picked_nodedb { + self.path_db.clone_from(&guard.nodedb_path); + guard.picked_nodedb = false; + } }); - ui.add_space(space_h); + ui.add_space(SPACE); } }); } } - -fn rpc_bind_field( - state: &mut Node, - ui: &mut Ui, - txt_description_width: f32, - text_height: f32, - width: f32, -) { - state_edit_field( - &mut state.api_ip, - ui, - txt_description_width, - text_height, - width, - "RPC BIND IP ", - 255, - NODE_API_BIND, - vec![®EXES.ipv4, ®EXES.domain], - ); -} - -fn rpc_port_field( - state: &mut Node, - ui: &mut Ui, - txt_description_width: f32, - text_height: f32, - width: f32, -) { - state_edit_field( - &mut state.api_port, - ui, - txt_description_width, - text_height, - width, - " RPC PORT ", - 5, - NODE_API_PORT, - vec![®EXES.port], - ); -} -fn zmq_bind_field( - state: &mut Node, - ui: &mut Ui, - txt_description_width: f32, - text_height: f32, - width: f32, -) { - state_edit_field( - &mut state.zmq_ip, - ui, - txt_description_width, - text_height, - width, - "API BIND IP ", - 255, - NODE_ZMQ_BIND, - vec![®EXES.ipv4, ®EXES.domain], - ); -} -fn zmq_port_field( - state: &mut Node, - ui: &mut Ui, - txt_description_width: f32, - text_height: f32, - width: f32, -) { - state_edit_field( - &mut state.zmq_port, - ui, - txt_description_width, - text_height, - width, - " ZMQ PORT ", - 5, - NODE_ZMQ_PORT, - vec![®EXES.port], - ); -} - -fn path_db_field( - state: &mut Node, - ui: &mut Ui, - txt_description_width: f32, - text_height: f32, - file_window: &Arc>, -) { - ui.horizontal(|ui| { - let symbol; - let color; - let hover; - if state.path_db.is_empty() { - symbol = "➖"; - color = LIGHT_GRAY; - hover = NODE_DB_PATH_EMPTY; - } else if !Gupax::path_is_dir(&state.path_db) { - symbol = "❌"; - color = RED; - hover = NODE_DB_DIR; - } else { - symbol = "✔"; - color = GREEN; - hover = NODE_PATH_OK; - } - let text = ["Node Database Directory ", symbol].concat(); - ui.add_sized( - [txt_description_width, text_height], - Label::new(RichText::new(text).color(color)), - ); - ui.spacing_mut().text_edit_width = - (ui.available_width() - (ui.spacing().item_spacing.x * 8.0) - SPACE * 2.0).max(0.0); - let window_busy = file_window.lock().unwrap().thread; - ui.add_enabled_ui(!window_busy, |ui| { - if ui.button("Open").on_hover_text(GUPAX_SELECT).clicked() { - Gupax::spawn_file_window_thread(file_window, FileType::NodeDB); - } - ui.text_edit_singleline(&mut state.path_db) - .on_hover_text(hover); - }); - }); - - let mut guard = file_window.lock().unwrap(); - if guard.picked_nodedb { - state.path_db.clone_from(&guard.nodedb_path); - guard.picked_nodedb = false; - } -} -#[allow(clippy::too_many_arguments)] -fn state_edit_field( - state_field: &mut String, - ui: &mut Ui, - txt_description_width: f32, - text_height: f32, - width: f32, - description: &str, - max_ch: u8, - help_msg: &str, - validations: Vec<&Regex>, -) { - ui.horizontal(|ui| { - let color; - let symbol; - let mut input_validated = true; - let len; - let inside_space; - for v in validations { - if !v.is_match(state_field) { - input_validated = false; - } - } - if state_field.is_empty() { - symbol = "➖"; - color = Color32::LIGHT_GRAY; - } else if input_validated { - symbol = "✔"; - color = Color32::from_rgb(100, 230, 100); - } else { - symbol = "❌"; - color = Color32::from_rgb(230, 50, 50); - } - match max_ch { - x if x >= 100 => { - len = format!("{:03}", state_field.len()); - inside_space = ""; - } - 10..99 => { - len = format!("{:02}", state_field.len()); - inside_space = " "; - } - _ => { - len = format!("{}", state_field.len()); - inside_space = " "; - } - } - let text = format!( - "{}[{}{}/{}{}]{}", - description, inside_space, len, max_ch, inside_space, symbol - ); - ui.add_sized( - [txt_description_width, text_height], - Label::new(RichText::new(text).color(color)), - ); - // allocate the size to leave half of the total width free. - ui.spacing_mut().text_edit_width = ((width / 2.0) - - (width - ui.available_width() - ui.spacing().scroll.bar_width) - - ui.spacing().item_spacing.x * 2.5) - .max(0.0); - ui.text_edit_singleline(state_field).on_hover_text(help_msg); - state_field.truncate(max_ch.into()); - }); -} diff --git a/src/app/panels/middle/p2pool/advanced.rs b/src/app/panels/middle/p2pool/advanced.rs index 3219b9e..33728cd 100644 --- a/src/app/panels/middle/p2pool/advanced.rs +++ b/src/app/panels/middle/p2pool/advanced.rs @@ -1,313 +1,154 @@ -use crate::disk::node::Node; +use crate::app::panels::middle::common::list_poolnode::{PoolNode, list_poolnode}; +use crate::app::panels::middle::common::state_edit_field::{StateTextEdit, slider_state_field}; +use crate::miscs::height_txt_before_button; use crate::{disk::state::P2pool, utils::regex::REGEXES}; -use egui::Checkbox; -use egui::Slider; -use egui::{Button, Vec2}; use crate::constants::*; -use egui::{Color32, ComboBox, Label, RichText, SelectableLabel, Ui}; +use egui::{Checkbox, SelectableLabel, Ui}; use log::*; impl P2pool { - pub(super) fn advanced( - &mut self, - ui: &mut Ui, - size: Vec2, - text_edit: f32, - node_vec: &mut Vec<(String, Node)>, - ) { - let height = size.y / 16.0; - let space_h = size.y / 128.0; + pub(super) fn advanced(&mut self, ui: &mut Ui, node_vec: &mut Vec<(String, PoolNode)>) { + // let height = size.y / 16.0; + // let space_h = size.y / 128.0; debug!("P2Pool Tab | Rendering [Node List] elements"); let mut incorrect_input = false; // This will disable [Add/Delete] on bad input // [Monero node IP/RPC/ZMQ] ui.horizontal(|ui| { - ui.group(|ui| { - let width = size.x/10.0; - ui.vertical(|ui| { - ui.spacing_mut().text_edit_width = width*3.32; - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:02}", self.name.len()); - if self.name.is_empty() { - text = format!("Name [ {}/30 ]➖", len); - color = Color32::LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.name.is_match(&self.name) { - text = format!("Name [ {}/30 ]✔", len); - color = Color32::from_rgb(100, 230, 100); - } else { - text = format!("Name [ {}/30 ]❌", len); - color = Color32::from_rgb(230, 50, 50); - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.name).on_hover_text(P2POOL_NAME); - self.name.truncate(30); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:03}", self.ip.len()); - if self.ip.is_empty() { - text = format!(" IP [{}/255]➖", len); - color = Color32::LIGHT_GRAY; - incorrect_input = true; - } else if self.ip == "localhost" || REGEXES.ipv4.is_match(&self.ip) || REGEXES.domain.is_match(&self.ip) { - text = format!(" IP [{}/255]✔", len); - color = Color32::from_rgb(100, 230, 100); - } else { - text = format!(" IP [{}/255]❌", len); - color = Color32::from_rgb(230, 50, 50); - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.ip).on_hover_text(P2POOL_NODE_IP); - self.ip.truncate(255); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = self.rpc.len(); - if self.rpc.is_empty() { - text = format!(" RPC [ {}/5 ]➖", len); - color = Color32::LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.port.is_match(&self.rpc) { - text = format!(" RPC [ {}/5 ]✔", len); - color = Color32::from_rgb(100, 230, 100); - } else { - text = format!(" RPC [ {}/5 ]❌", len); - color = Color32::from_rgb(230, 50, 50); - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.rpc).on_hover_text(P2POOL_RPC_PORT); - self.rpc.truncate(5); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = self.zmq.len(); - if self.zmq.is_empty() { - text = format!(" ZMQ [ {}/5 ]➖", len); - color = Color32::LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.port.is_match(&self.zmq) { - text = format!(" ZMQ [ {}/5 ]✔", len); - color = Color32::from_rgb(100, 230, 100); - } else { - text = format!(" ZMQ [ {}/5 ]❌", len); - color = Color32::from_rgb(230, 50, 50); - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.zmq).on_hover_text(P2POOL_ZMQ_PORT); - self.zmq.truncate(5); - }); - }); - - ui.vertical(|ui| { - let width = ui.available_width(); - ui.add_space(1.0); - // [Manual node selection] - ui.spacing_mut().slider_width = width - 8.0; - ui.spacing_mut().icon_width = width / 25.0; - // [Ping List] - debug!("P2Pool Tab | Rendering [Node List]"); - let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name)); - ComboBox::from_id_salt("manual_nodes").selected_text(text).width(width).show_ui(ui, |ui| { - for (n, (name, node)) in node_vec.iter().enumerate() { - let text = RichText::new(format!("{}. {}\n IP: {}\n RPC: {}\n ZMQ: {}", n+1, name, node.ip, node.rpc, node.zmq)); - if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() { - self.selected_index = n; - let node = node.clone(); - self.selected_name.clone_from(name); - self.selected_ip.clone_from(&node.ip); - self.selected_rpc.clone_from(&node.rpc); - self.selected_zmq.clone_from(&node.zmq); - self.name.clone_from(name); - self.ip = node.ip; - self.rpc = node.rpc; - self.zmq = node.zmq; - } - } - }); - // [Add/Save] - let node_vec_len = node_vec.len(); - let mut exists = false; - let mut save_diff = true; - let mut existing_index = 0; - for (name, node) in node_vec.iter() { - if *name == self.name { - exists = true; - if self.ip == node.ip && self.rpc == node.rpc && self.zmq == node.zmq { - save_diff = false; - } - break - } - existing_index += 1; - } - ui.horizontal(|ui| { - let text = if exists { LIST_SAVE } else { LIST_ADD }; - let text = format!("{}\n Currently selected node: {}. {}\n Current amount of nodes: {}/1000", text, self.selected_index+1, self.selected_name, node_vec_len); - // If the node already exists, show [Save] and mutate the already existing node - if exists { - ui.add_enabled_ui(!incorrect_input && save_diff, |ui|{ - if ui.add_sized([width, text_edit], Button::new("Save")).on_hover_text(text).clicked() { - let node = Node { - ip: self.ip.clone(), - rpc: self.rpc.clone(), - zmq: self.zmq.clone(), - }; - node_vec[existing_index].1 = node; - self.selected_index = existing_index; - self.selected_ip.clone_from(&self.ip); - self.selected_rpc.clone_from(&self.rpc); - self.selected_zmq.clone_from(&self.zmq); - info!("Node | S | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", existing_index+1, self.name, self.ip, self.rpc, self.zmq); - } - }); - // Else, add to the list - } else { - ui.add_enabled_ui(!incorrect_input && node_vec_len < 1000, |ui| { - if ui.add_sized([width, text_edit], Button::new("Add")).on_hover_text(text).clicked() { - let node = Node { - ip: self.ip.clone(), - rpc: self.rpc.clone(), - zmq: self.zmq.clone(), - }; - node_vec.push((self.name.clone(), node)); - self.selected_index = node_vec_len; - self.selected_name.clone_from(&self.name); - self.selected_ip.clone_from(&self.ip); - self.selected_rpc.clone_from(&self.rpc); - self.selected_zmq.clone_from(&self.zmq); - info!("Node | A | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", node_vec_len, self.name, self.ip, self.rpc, self.zmq); - } - }); - } - }); - // [Delete] - ui.horizontal(|ui| { - ui.add_enabled_ui(node_vec_len > 1, |ui|{ - let text = format!("{}\n Currently selected node: {}. {}\n Current amount of nodes: {}/1000", LIST_DELETE, self.selected_index+1, self.selected_name, node_vec_len); - if ui.add_sized([width, text_edit], Button::new("Delete")).on_hover_text(text).clicked() { - let new_name; - let new_node; - match self.selected_index { - 0 => { - new_name = node_vec[1].0.clone(); - new_node = node_vec[1].1.clone(); - node_vec.remove(0); - } - _ => { - node_vec.remove(self.selected_index); - self.selected_index -= 1; - new_name = node_vec[self.selected_index].0.clone(); - new_node = node_vec[self.selected_index].1.clone(); - } - }; - self.selected_name.clone_from(&new_name); - self.selected_ip.clone_from(&new_node.ip); - self.selected_rpc.clone_from(&new_node.rpc); - self.selected_zmq.clone_from(&new_node.zmq); - self.name = new_name; - self.ip = new_node.ip; - self.rpc = new_node.rpc; - self.zmq = new_node.zmq; - info!("Node | D | [index: {}, name: \"{}\", ip: \"{}\", rpc: {}, zmq: {}]", self.selected_index, self.selected_name, self.selected_ip, self.selected_rpc, self.selected_zmq); - } - }); - }); - ui.horizontal(|ui| { - ui.add_enabled_ui(!self.name.is_empty() || !self.ip.is_empty() || !self.rpc.is_empty() || !self.zmq.is_empty(), |ui|{ - if ui.add_sized([width, text_edit], Button::new("Clear")).on_hover_text(LIST_CLEAR).clicked() { - self.name.clear(); - self.ip.clear(); - self.rpc.clear(); - self.zmq.clear(); - } - - }); - }); - }); - }); - }); + ui.group(|ui| { + // let width = size.x/10.0; + ui.vertical(|ui| { + if !self.name_field(ui) { + incorrect_input = false; + } + if !self.ip_field(ui) { + incorrect_input = false; + } + if !self.rpc_port_field(ui) { + incorrect_input = false; + } + if !self.zmq_port_field(ui) { + incorrect_input = false; + } + }); + list_poolnode( + ui, + &mut (&mut self.name, &mut self.ip, &mut self.rpc, &mut self.zmq), + &mut self.selected_node, + node_vec, + incorrect_input, + ); + }); + }); // ui.add_space(space_h); debug!("P2Pool Tab | Rendering [Main/Mini/Peers/Log] elements"); // [Main/Mini] ui.horizontal(|ui| { - let height = height / 4.0; + // let height = height / 4.0; ui.group(|ui| { - ui.horizontal(|ui| { - let width = (size.x / 4.0) - SPACE; - let height = height + space_h; - if ui - .add_sized( - [width, height], - SelectableLabel::new(!self.mini, "P2Pool Main"), + ui.vertical(|ui| { + let height = height_txt_before_button(ui, &egui::TextStyle::Button) * 1.9; + ui.horizontal(|ui| { + let width = (ui.available_width() / 4.0) - SPACE; + if ui + // if ui.add_sized(, ) + // .selectable_label(!self.mini, "P2Pool Main") + .add_sized( + [width, height], + SelectableLabel::new(!self.mini, "P2Pool Main"), + ) + .on_hover_text(P2POOL_MAIN) + .clicked() + { + self.mini = false; + } + if ui + // .selectable_label(!self.mini, "P2Pool Mini") + // if ui + .add_sized( + [width, height], + SelectableLabel::new(self.mini, "P2Pool Mini"), + ) + .on_hover_text(P2POOL_MINI) + .clicked() + { + self.mini = true; + } + }); + debug!("P2Pool Tab | Rendering Backup host button"); + ui.group(|ui| { + // [Backup host] + ui.add_sized( + [(ui.available_width() / 2.0) - (SPACE * 2.0), height], + Checkbox::new(&mut self.backup_host, "Backup host"), ) - .on_hover_text(P2POOL_MAIN) - .clicked() - { - self.mini = false; - } - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.mini, "P2Pool Mini"), - ) - .on_hover_text(P2POOL_MINI) - .clicked() - { - self.mini = true; - } - }) + // ui.checkbox(&mut self.backup_host, "Backup host") + .on_hover_text(P2POOL_BACKUP_HOST_ADVANCED); + }); + }); }); // [Out/In Peers] + [Log Level] ui.group(|ui| { ui.vertical(|ui| { - let text = (ui.available_width() / 10.0) - SPACE; - let width = (text * 8.0) - SPACE; - let height = height / 3.0; - ui.style_mut().spacing.slider_width = width / 1.1; - ui.style_mut().spacing.interact_size.y = height; - ui.style_mut().override_text_style = Some(egui::TextStyle::Small); - ui.horizontal(|ui| { - ui.add_sized([text, height], Label::new("Out peers [10-450]:")); - ui.add_sized([width, height], Slider::new(&mut self.out_peers, 10..=450)) - .on_hover_text(P2POOL_OUT); - ui.add_space(ui.available_width() - 4.0); - }); - ui.horizontal(|ui| { - ui.add_sized([text, height], Label::new(" In peers [10-450]:")); - ui.add_sized([width, height], Slider::new(&mut self.in_peers, 10..=450)) - .on_hover_text(P2POOL_IN); - }); - ui.horizontal(|ui| { - ui.add_sized([text, height], Label::new(" Log level [0-6]:")); - ui.add_sized([width, height], Slider::new(&mut self.log_level, 0..=6)) - .on_hover_text(P2POOL_LOG); - }); + ui.add_space(SPACE); + slider_state_field( + ui, + "Out peers [2-450]:", + P2POOL_OUT, + &mut self.out_peers, + 2..=450, + ); + ui.add_space(SPACE); + slider_state_field( + ui, + "In peers [2-450]:", + P2POOL_IN, + &mut self.in_peers, + 2..=450, + ); + ui.add_space(SPACE); + slider_state_field( + ui, + "Log level [ 0-6 ]:", + P2POOL_LOG, + &mut self.log_level, + 0..=6, + ); }) }); }); - - debug!("P2Pool Tab | Rendering Backup host button"); - ui.group(|ui| { - let width = size.x - SPACE; - let height = ui.available_height(); - ui.style_mut().spacing.icon_width = height; - ui.style_mut().spacing.icon_width_inner = height * 0.9; - // [Backup host] - ui.add_sized( - [width, height], - Checkbox::new(&mut self.backup_host, "Backup host"), - ) - .on_hover_text(P2POOL_BACKUP_HOST_ADVANCED); - }); + } + fn name_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" Name ") + .max_ch(30) + .help_msg(P2POOL_NAME) + .validations(&[|x| REGEXES.name.is_match(x)]) + .build(ui, &mut self.name) + } + fn rpc_port_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" RPC PORT ") + .max_ch(5) + .help_msg(P2POOL_RPC_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, &mut self.rpc) + } + fn zmq_port_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" ZMQ PORT ") + .max_ch(5) + .help_msg(P2POOL_ZMQ_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, &mut self.zmq) + } + fn ip_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" IP ") + .max_ch(255) + .help_msg(P2POOL_NODE_IP) + .validations(&[|x| REGEXES.ipv4.is_match(x), |x| REGEXES.domain.is_match(x)]) + .build(ui, &mut self.ip) } } diff --git a/src/app/panels/middle/p2pool/mod.rs b/src/app/panels/middle/p2pool/mod.rs index 1fdda52..c5a709b 100644 --- a/src/app/panels/middle/p2pool/mod.rs +++ b/src/app/panels/middle/p2pool/mod.rs @@ -1,7 +1,6 @@ -use crate::disk::node::Node; +use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field}; use crate::disk::state::{P2pool, State}; use crate::helper::p2pool::PubP2poolApi; -use crate::regex::num_lines; // Gupax - GUI Uniting P2Pool And XMRig // // Copyright (c) 2022-2023 hinto-janai @@ -18,12 +17,13 @@ use crate::regex::num_lines; // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::{components::node::*, constants::*, helper::*, utils::regex::Regexes}; -use egui::{Color32, Label, RichText, TextEdit, TextStyle, Vec2, vec2}; +use crate::{components::node::*, constants::*, helper::*}; use log::*; use std::sync::{Arc, Mutex}; +use super::common::list_poolnode::PoolNode; + mod advanced; mod simple; @@ -32,143 +32,51 @@ impl P2pool { #[allow(clippy::too_many_arguments)] pub fn show( &mut self, - node_vec: &mut Vec<(String, Node)>, + node_vec: &mut Vec<(String, PoolNode)>, _og: &Arc>, ping: &Arc>, process: &Arc>, api: &Arc>, buffer: &mut String, - size: Vec2, _ctx: &egui::Context, ui: &mut egui::Ui, ) { - let height = size.y; - let width = size.x; - let text_edit = size.y / 25.0; //---------------------------------------------------------------------------------------------------- [Simple] Console // debug!("P2Pool Tab | Rendering [Console]"); egui::ScrollArea::vertical().show(ui, |ui| { + let text = &api.lock().unwrap().output; ui.group(|ui| { - let text = &api.lock().unwrap().output; - let nb_lines = num_lines(text); - let (height, width) = if self.simple { - ((size.y * 0.38) - SPACE, size.x - SPACE) - } else { - ( - if size.y < 600.0 { - size.y * 0.22 - SPACE - } else { - size.y * 0.36 - SPACE - }, - width - SPACE, - ) - }; - egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { - ui.style_mut().override_text_style = Some(egui::TextStyle::Small); - egui::ScrollArea::vertical() - .stick_to_bottom(true) - .max_width(width) - .max_height(height) - .auto_shrink([false; 2]) - // .show_viewport(ui, |ui, _| { - .show_rows( - ui, - ui.text_style_height(&TextStyle::Small), - nb_lines, - |ui, row_range| { - for i in row_range { - if let Some(line) = text.lines().nth(i) { - ui.label(line); - } - } - }, - ); - }); + console(ui, text); if !self.simple { - //---------------------------------------------------------------------------------------------------- [Advanced] Console ui.separator(); - let response = ui - .add_sized( - [width, text_edit], - TextEdit::hint_text( - TextEdit::singleline(buffer), - r#"Type a command (e.g "help" or "status") and press Enter"#, - ), - ) - .on_hover_text(P2POOL_INPUT); - // If the user pressed enter, dump buffer contents into the process STDIN - if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { - response.request_focus(); // Get focus back - let buffer = std::mem::take(buffer); // Take buffer - let mut process = process.lock().unwrap(); // Lock - if process.is_alive() { - process.input.push(buffer); - } // Push only if alive - } + input_args_field( + ui, + buffer, + process, + r#"Type a command (e.g "help" or "status") and press Enter"#, + P2POOL_INPUT, + ); } }); - - //---------------------------------------------------------------------------------------------------- Args if !self.simple { - debug!("P2Pool Tab | Rendering [Arguments]"); - ui.group(|ui| { - ui.horizontal(|ui| { - let width = (width / 10.0) - SPACE; - ui.add_sized([width, text_edit], Label::new("Command arguments:")); - ui.add_sized( - [ui.available_width(), text_edit], - TextEdit::hint_text( - TextEdit::singleline(&mut self.arguments), - r#"--wallet <...> --host <...>"#, - ), - ) - .on_hover_text(P2POOL_ARGUMENTS); - self.arguments.truncate(1024); - }) - }); - if !self.arguments.is_empty() { - ui.disable() - } - } - - //---------------------------------------------------------------------------------------------------- Address - debug!("P2Pool Tab | Rendering [Address]"); - ui.group(|ui| { - let width = width - SPACE; - ui.spacing_mut().text_edit_width = (width) - (SPACE * 3.0); - let text; - let color; - let len = format!("{:02}", self.address.len()); - if self.address.is_empty() { - text = format!("Monero Address [{}/95] ➖", len); - color = Color32::LIGHT_GRAY; - } else if Regexes::addr_ok(&self.address) { - text = format!("Monero Address [{}/95] ✔", len); - color = Color32::from_rgb(100, 230, 100); - } else { - text = format!("Monero Address [{}/95] ❌", len); - color = Color32::from_rgb(230, 50, 50); - } - ui.add_sized( - [width, text_edit], - Label::new(RichText::new(text).color(color)), + start_options_field( + ui, + &mut self.arguments, + r#"--wallet <...> --host <...>"#, + P2POOL_ARGUMENTS, ); - ui.add_sized( - [width, text_edit], - TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4..."), - ) - .on_hover_text(P2POOL_ADDRESS); - self.address.truncate(95); - }); + } + debug!("P2Pool Tab | Rendering [Address]"); + crate::app::panels::middle::common::state_edit_field::monero_address_field( + &mut self.address, + ui, + P2POOL_ADDRESS, + ); - // let height = ui.available_height(); - let size = vec2(width, height); if self.simple { - //---------------------------------------------------------------------------------------------------- Simple - self.simple(ui, size, ping); - //---------------------------------------------------------------------------------------------------- Advanced + self.simple(ui, ping); } else { - self.advanced(ui, size, text_edit, node_vec); + self.advanced(ui, node_vec); } }); } diff --git a/src/app/panels/middle/p2pool/simple.rs b/src/app/panels/middle/p2pool/simple.rs index 9316c2c..2242ded 100644 --- a/src/app/panels/middle/p2pool/simple.rs +++ b/src/app/panels/middle/p2pool/simple.rs @@ -1,47 +1,32 @@ use std::sync::Arc; use std::sync::Mutex; -use crate::app::panels::middle::Hyperlink; use crate::app::panels::middle::ProgressBar; -use crate::app::panels::middle::Spinner; use crate::components::node::Ping; use crate::components::node::RemoteNode; use crate::components::node::format_ip_location; use crate::components::node::format_ms; use crate::disk::state::P2pool; +use crate::miscs::height_txt_before_button; use egui::Button; use egui::Checkbox; -use egui::Vec2; +use egui::ScrollArea; +use egui::TextStyle; +use egui::TextWrapMode; use egui::vec2; use crate::constants::*; -use egui::{Color32, ComboBox, Label, RichText, Ui}; +use egui::{Color32, ComboBox, RichText, Ui}; use log::*; impl P2pool { - pub(super) fn simple(&mut self, ui: &mut Ui, size: Vec2, ping: &Arc>) { - // [Node] - let height = size.y / 13.0; - let space_h = size.y / 96.0; - ui.spacing_mut().slider_width = (size.x - 16.0).max(0.0); - ui.spacing_mut().icon_width = size.x / 25.0; - - // [Auto-select] if we haven't already. - // Using [Arc>] as an intermediary here - // saves me the hassle of wrapping [state: State] completely - // and [.lock().unwrap()]ing it everywhere. - // Two atomic bools = enough to represent this data - - // local or remote - // button bool + pub(super) fn simple(&mut self, ui: &mut Ui, ping: &Arc>) { ui.vertical_centered(|ui|{ - ui.add_space(space_h); + ui.add_space(SPACE); ui.checkbox(&mut self.local_node, "Use a local node").on_hover_text("If checked (recommended), p2pool will automatically use the local node.\nCheck the Node tab to start a local node.\nIf unchecked, p2pool will attempt to use a remote node."); }); - ui.add_space(space_h * 2.0); - + ui.add_space(SPACE * 2.0); // if checked, use only local node // if unchecked, show remote nodes. - // disable remote if local is checked. let visible = !self.local_node; debug!("P2Pool Tab | Running [auto-select] check"); @@ -54,7 +39,6 @@ impl P2pool { } drop(ping); } - ui.add_enabled_ui(visible, |ui| { ui.vertical(|ui| { ui.horizontal(|ui| { @@ -74,9 +58,11 @@ impl P2pool { debug!("P2Pool Tab | Rendering [ComboBox] of Remote Nodes"); let ip_location = format_ip_location(&self.node, false); let text = RichText::new(format!(" ⏺ {}ms | {}", ms, ip_location)).color(color); + ui.style_mut().override_text_style = Some(egui::TextStyle::Small); + ui.spacing_mut().item_spacing.y = 0.0; ComboBox::from_id_salt("remote_nodes") .selected_text(text) - .width(size.x) + .width(ui.available_width()) .show_ui(ui, |ui| { for data in ping.lock().unwrap().nodes.iter() { let ms = format_ms(data.ms); @@ -87,123 +73,151 @@ impl P2pool { } }); }); - - ui.add_space(space_h); - + ui.add_space(SPACE); debug!("P2Pool Tab | Rendering [Select fastest ... Ping] buttons"); - ui.horizontal(|ui| { - let width = ((size.x / 5.0) - 6.0).max(0.0); - let size = vec2(width, height); - // [Select random node] - if ui - .add_sized(size, Button::new("Select random node")) - .on_hover_text(P2POOL_SELECT_RANDOM) - .clicked() - { - self.node = RemoteNode::get_random(&self.node); - } - // [Select fastest node] - if ui - .add_sized(size, Button::new("Select fastest node")) - .on_hover_text(P2POOL_SELECT_FASTEST) - .clicked() - && ping.lock().unwrap().pinged - { - self.node = ping.lock().unwrap().fastest.to_string(); - } - // [Ping Button] - ui.add_enabled_ui(!ping.lock().unwrap().pinging, |ui| { - if ui - .add_sized(size, Button::new("Ping remote nodes")) - .on_hover_text(P2POOL_PING) - .clicked() - { - Ping::spawn_thread(ping); - } - }); - // [Last <-] - if ui - .add_sized(size, Button::new("⬅ Last")) - .on_hover_text(P2POOL_SELECT_LAST) - .clicked() - { - let ping = ping.lock().unwrap(); - match ping.pinged { - true => { - self.node = RemoteNode::get_last_from_ping(&self.node, &ping.nodes) + ScrollArea::horizontal() + .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) + .id_salt("horizontal") + .show(ui, |ui| { + ui.style_mut().override_text_style = Some(egui::TextStyle::Button); + ui.horizontal(|ui| { + ui.style_mut().wrap_mode = Some(TextWrapMode::Extend); + // ui.columns_const(|[col1, col2, col3, col4, col5]| { + let width = ((ui.available_width() / 5.0) + - (ui.spacing().item_spacing.x * (4.0 / 5.0))) + .max(20.0); + let height = height_txt_before_button(ui, &TextStyle::Button) * 2.0; + // [Select random node] + ui.style_mut().override_text_valign = Some(egui::Align::Center); + if ui + .add_sized([width, height], Button::new("Select random node")) + .on_hover_text(P2POOL_SELECT_RANDOM) + .clicked() + { + self.node = RemoteNode::get_random(&self.node); } - false => self.node = RemoteNode::get_last(&self.node), - } - drop(ping); - } - // [Next ->] - if ui - .add_sized(size, Button::new("Next ➡")) - .on_hover_text(P2POOL_SELECT_NEXT) - .clicked() - { - let ping = ping.lock().unwrap(); - match ping.pinged { - true => { - self.node = RemoteNode::get_next_from_ping(&self.node, &ping.nodes) + // [Select fastest node] + if ui + .add_sized([width, height], Button::new("Select fastest node")) + .on_hover_text(P2POOL_SELECT_FASTEST) + .clicked() + && ping.lock().unwrap().pinged + { + self.node = ping.lock().unwrap().fastest.to_string(); } - false => self.node = RemoteNode::get_next(&self.node), - } - drop(ping); - } - }); + // [Ping Button] + ui.add_enabled_ui(!ping.lock().unwrap().pinging, |ui| { + if ui + .add_sized([width, height], Button::new("Ping remote nodes")) + .on_hover_text(P2POOL_PING) + .clicked() + { + Ping::spawn_thread(ping); + } + }); + // [Last <-] + if ui + .add_sized([width, height], Button::new("⬅ Last")) + .on_hover_text(P2POOL_SELECT_LAST) + .clicked() + { + let ping = ping.lock().unwrap(); + match ping.pinged { + true => { + self.node = + RemoteNode::get_last_from_ping(&self.node, &ping.nodes) + } + false => self.node = RemoteNode::get_last(&self.node), + } + drop(ping); + } + // [Next ->] + if ui + .add_sized([width, height], Button::new("Next ➡")) + .on_hover_text(P2POOL_SELECT_NEXT) + .clicked() + { + let ping = ping.lock().unwrap(); + match ping.pinged { + true => { + self.node = + RemoteNode::get_next_from_ping(&self.node, &ping.nodes) + } + false => self.node = RemoteNode::get_next(&self.node), + } + drop(ping); + } + }); - ui.vertical(|ui| { - let height = height / 2.0; - let pinging = ping.lock().unwrap().pinging; - ui.add_enabled_ui(pinging, |ui| { - let prog = ping.lock().unwrap().prog.round(); - let msg = - RichText::new(format!("{} ... {}%", ping.lock().unwrap().msg, prog)); - let height = height / 1.25; - let size = vec2(size.x, height); - ui.add_space(space_h); - ui.add_sized(size, Label::new(msg)); - ui.add_space(space_h); - if pinging { - ui.add_sized(size, Spinner::new().size(height)); - } else { - ui.add_sized(size, Label::new("...")); - } - ui.add_sized(size, ProgressBar::new(prog.round() / 100.0)); - ui.add_space(space_h); + ui.vertical_centered(|ui| { + // let height = height / 2.0; + let pinging = ping.lock().unwrap().pinging; + ui.add_enabled_ui(pinging, |ui| { + let prog = ping.lock().unwrap().prog.round(); + let msg = RichText::new(format!( + "{} ... {}%", + ping.lock().unwrap().msg, + prog + )); + // let height = height / 1.25; + // let size = vec2(size.x, height); + ui.add_space(SPACE); + ui.label(msg); + ui.add_space(SPACE); + if pinging { + ui.spinner(); + } else { + ui.label("..."); + } + ui.add(ProgressBar::new(prog.round() / 100.0)); + ui.add_space(SPACE); + }); + }); + + debug!("P2Pool Tab | Rendering [Auto-*] buttons"); + ui.group(|ui| { + ui.horizontal(|ui| { + let width = + (((ui.available_width() - ui.spacing().item_spacing.x) / 3.0) + - SPACE * 1.5) + .max(ui.text_style_height(&TextStyle::Button) * 7.0); + let size = vec2( + width, + height_txt_before_button(ui, &TextStyle::Button) * 2.0, + ); + // [Auto-node] + ui.add_sized( + size, + Checkbox::new(&mut self.auto_select, "Auto-select"), + ) + // ui.checkbox(&mut self.auto_select, "Auto-select") + .on_hover_text(P2POOL_AUTO_SELECT); + ui.separator(); + // [Auto-node] + ui.add_sized(size, Checkbox::new(&mut self.auto_ping, "Auto-ping")) + // ui.checkbox(&mut self.auto_ping, "Auto-ping") + .on_hover_text(P2POOL_AUTO_NODE); + ui.separator(); + // [Backup host] + ui.add_sized( + size, + Checkbox::new(&mut self.backup_host, "Backup host"), + ) + // ui.checkbox(&mut self.backup_host, "Backup host") + .on_hover_text(P2POOL_BACKUP_HOST_SIMPLE); + }) + }); }); - }); }); - - debug!("P2Pool Tab | Rendering [Auto-*] buttons"); - ui.group(|ui| { - ui.horizontal(|ui| { - let width = ((size.x / 3.0) - (SPACE * 1.75)).max(0.0); - let size = vec2(width, height); - // [Auto-node] - ui.add_sized(size, Checkbox::new(&mut self.auto_select, "Auto-select")) - .on_hover_text(P2POOL_AUTO_SELECT); - ui.separator(); - // [Auto-node] - ui.add_sized(size, Checkbox::new(&mut self.auto_ping, "Auto-ping")) - .on_hover_text(P2POOL_AUTO_NODE); - ui.separator(); - // [Backup host] - ui.add_sized(size, Checkbox::new(&mut self.backup_host, "Backup host")) - .on_hover_text(P2POOL_BACKUP_HOST_SIMPLE); - }) - }); - debug!("P2Pool Tab | Rendering warning text"); - ui.add_sized( - [size.x, height / 2.0], - Hyperlink::from_label_and_url( + ui.add_space(SPACE); + ui.vertical_centered(|ui| { + ui.hyperlink_to( "WARNING: It is recommended to run/use your own Monero Node (hover for details)", "https://github.com/Cyrix126/gupaxx#running-a-local-monero-node", - ), - ) - .on_hover_text(P2POOL_COMMUNITY_NODE_WARNING); + ) + .on_hover_text(P2POOL_COMMUNITY_NODE_WARNING); + }); }); } } diff --git a/src/app/panels/middle/status/benchmarks.rs b/src/app/panels/middle/status/benchmarks.rs index 77e4da0..bcbecef 100644 --- a/src/app/panels/middle/status/benchmarks.rs +++ b/src/app/panels/middle/status/benchmarks.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex}; use crate::{app::Benchmark, disk::state::Status, helper::xrig::xmrig::PubXmrigApi}; -use egui::{Hyperlink, ProgressBar, ScrollArea, Spinner, Vec2}; +use egui::{ProgressBar, ScrollArea, TextWrapMode}; use egui_extras::{Column, TableBuilder}; use readable::num::{Float, Percent, Unsigned}; @@ -11,85 +11,56 @@ use log::*; impl Status { pub(super) fn benchmarks( &mut self, - size: Vec2, ui: &mut egui::Ui, benchmarks: &[Benchmark], xmrig_alive: bool, xmrig_api: &Arc>, ) { debug!("Status Tab | Rendering [Benchmarks]"); - let text = size.y / 20.0; + let text = ui.text_style_height(&egui::TextStyle::Body); let double = text * 2.0; - let log = size.y / 3.0; - let width = size.x; + let width = ui.available_width(); // [0], The user's CPU (most likely). let cpu = &benchmarks[0]; ui.horizontal(|ui| { - let width = (width / 2.0) - (SPACE * 1.666); - let min_height = log; ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_height(min_height); - ui.add_sized( - [width, text], - Label::new(RichText::new("Your CPU").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_CPU); - ui.add_sized([width, text], Label::new(cpu.cpu.as_str())); - ui.add_sized( - [width, text], - Label::new(RichText::new("Total Benchmarks").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_BENCHMARKS); - ui.add_sized([width, text], Label::new(format!("{}", cpu.benchmarks))); - ui.add_sized( - [width, text], - Label::new(RichText::new("Rank").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_RANK); - ui.add_sized( - [width, text], - Label::new(format!("{}/{}", cpu.rank, &benchmarks.len())), - ); + ui.set_max_width(ui.available_width() / 2.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new("Your CPU").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_YOUR_CPU); + ui.label(cpu.cpu.as_str()); + ui.label(RichText::new("Total Banchmarks").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_YOUR_BENCHMARKS); + ui.label(format!("{}", cpu.benchmarks)); + // ui.add_sized([width, text], Label::new(format!("{}", cpu.benchmarks))); + ui.label(RichText::new("Rank").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_YOUR_RANK); + ui.label(format!("{}/{}", cpu.rank, &benchmarks.len())); }) }); ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_height(min_height); - ui.add_sized( - [width, text], - Label::new(RichText::new("High Hashrate").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_HIGH); - ui.add_sized( - [width, text], - Label::new(format!("{} H/s", Float::from_0(cpu.high.into()))), - ); - ui.add_sized( - [width, text], - Label::new(RichText::new("Average Hashrate").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_AVERAGE); - ui.add_sized( - [width, text], - Label::new(format!("{} H/s", Float::from_0(cpu.average.into()))), - ); - ui.add_sized( - [width, text], - Label::new(RichText::new("Low Hashrate").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_LOW); - ui.add_sized( - [width, text], - Label::new(format!("{} H/s", Float::from_0(cpu.low.into()))), - ); + ui.vertical_centered(|ui| { + ui.label(RichText::new("High Hashrate").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_YOUR_HIGH); + ui.label(format!("{} H/s", Float::from_0(cpu.high.into()))); + ui.label(RichText::new("Average Hashrate").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_YOUR_AVERAGE); + ui.label(format!("{} H/s", Float::from_0(cpu.average.into()))); + ui.label(RichText::new("Low Hashrate").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_YOUR_LOW); + ui.label(RichText::new(format!( + "{} H/s", + Float::from_0(cpu.low.into()) + ))); }) }) }); // User's CPU hashrate comparison (if XMRig is alive). - ui.scope(|ui| { + // User's CPU hashrate comparison (if XMRig is alive). + ui.vertical_centered(|ui| { + ui.add_space(SPACE); if xmrig_alive { let api = xmrig_api.lock().unwrap(); let percent = (api.hashrate_raw / cpu.high) * 100.0; @@ -102,11 +73,11 @@ impl Status { human, api.hashrate )), ); - ui.add_sized([width, text], ProgressBar::new(1.0)); + ui.add(ProgressBar::new(1.0)); } else if api.hashrate_raw == 0.0 { - ui.add_sized([width, text], Label::new("Measuring hashrate...")); - ui.add_sized([width, text], Spinner::new().size(text)); - ui.add_sized([width, text], ProgressBar::new(0.0)); + ui.label("Measuring hashrate..."); + ui.spinner(); + ui.add(ProgressBar::new(0.0)); } else { ui.add_sized( [width, double], @@ -115,7 +86,7 @@ impl Status { human, api.hashrate )), ); - ui.add_sized([width, text], ProgressBar::new(percent / 100.0)); + ui.add(ProgressBar::new(percent / 100.0)); } } else { ui.add_enabled_ui(xmrig_alive, |ui| { @@ -123,22 +94,21 @@ impl Status { [width, double], Label::new("XMRig is offline. Hashrate cannot be determined."), ); - ui.add_sized([width, text], ProgressBar::new(0.0)); + ui.add(ProgressBar::new(0.0)); }); } + ui.add_space(SPACE); + // Comparison + ui.group(|ui| { + ui.hyperlink_to("Other CPUs", "https://xmrig.com/benchmark") + .on_hover_text(STATUS_SUBMENU_OTHER_CPUS); + }); }); // Comparison - ui.group(|ui| { - ui.add_sized( - [width, text], - Hyperlink::from_label_and_url("Other CPUs", "https://xmrig.com/benchmark"), - ) - .on_hover_text(STATUS_SUBMENU_OTHER_CPUS); - }); let width_column = width / 20.0; let (cpu, bar, high, average, low, rank, bench) = ( - width_column * 10.0, + width_column * 6.0, width_column * 3.0, width_column * 2.0, width_column * 2.0, @@ -146,6 +116,7 @@ impl Status { width_column, width_column * 2.0, ); + ui.style_mut().wrap_mode = Some(TextWrapMode::Extend); ScrollArea::horizontal().show(ui, |ui| { TableBuilder::new(ui) .columns(Column::auto(), 7) diff --git a/src/app/panels/middle/status/mod.rs b/src/app/panels/middle/status/mod.rs index 0debc47..7a4405e 100644 --- a/src/app/panels/middle/status/mod.rs +++ b/src/app/panels/middle/status/mod.rs @@ -15,8 +15,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use egui::Vec2; - use crate::{ app::{Benchmark, eframe_impl::ProcessStatesGui}, disk::{gupax_p2pool_api::GupaxP2poolApi, state::Status, status::*}, @@ -51,10 +49,9 @@ impl Status { p2pool_img: &Arc>, xmrig_img: &Arc>, states: &ProcessStatesGui, - max_threads: usize, + max_threads: u16, gupax_p2pool_api: &Arc>, benchmarks: &[Benchmark], - size: Vec2, _ctx: &egui::Context, ui: &mut egui::Ui, ) { @@ -62,7 +59,6 @@ impl Status { if self.submenu == Submenu::Processes { self.processes( sys, - size, ui, node_api, p2pool_api, @@ -77,7 +73,6 @@ impl Status { //---------------------------------------------------------------------------------------------------- [P2Pool] } else if self.submenu == Submenu::P2pool { self.p2pool( - size, ui, gupax_p2pool_api, states.is_alive(ProcessName::P2pool), @@ -86,7 +81,6 @@ impl Status { //---------------------------------------------------------------------------------------------------- [Benchmarks] } else if self.submenu == Submenu::Benchmarks { self.benchmarks( - size, ui, benchmarks, states.is_alive(ProcessName::Xmrig), diff --git a/src/app/panels/middle/status/p2pool.rs b/src/app/panels/middle/status/p2pool.rs index 3373b6a..49f7f1b 100644 --- a/src/app/panels/middle/status/p2pool.rs +++ b/src/app/panels/middle/status/p2pool.rs @@ -1,7 +1,8 @@ use std::sync::{Arc, Mutex}; -use egui::{Label, RichText, SelectableLabel, Slider, TextEdit, Vec2}; +use egui::{Label, RichText, ScrollArea, SelectableLabel, Separator, Slider, TextStyle}; use readable::num::Unsigned; +use strum::{EnumCount, IntoEnumIterator}; use crate::{ disk::{ @@ -16,445 +17,345 @@ use crate::{ impl Status { pub fn p2pool( &mut self, - size: Vec2, ui: &mut egui::Ui, gupax_p2pool_api: &Arc>, p2pool_alive: bool, p2pool_api: &Arc>, ) { let api = gupax_p2pool_api.lock().unwrap(); - let height = size.y; - let width = size.x; - let text = height / 25.0; - let log = height / 2.8; + // let height = size.y; + // let width = size.x; + // let text = height / 25.0; + // let log = height / 2.8; // Payout Text + PayoutView buttons - ui.group(|ui| { - ui.horizontal(|ui| { - let width = (width / 3.0) - (SPACE * 4.0); - ui.add_sized( - [width, text], - Label::new( - RichText::new(format!("Total Payouts: {}", api.payout)) - .underline() - .color(LIGHT_GRAY), - ), - ) - .on_hover_text(STATUS_SUBMENU_PAYOUT); - ui.separator(); - ui.add_sized( - [width, text], - Label::new( - RichText::new(format!("Total XMR: {}", api.xmr)) - .underline() - .color(LIGHT_GRAY), - ), - ) - .on_hover_text(STATUS_SUBMENU_XMR); - let width = width / 4.0; - ui.separator(); - if ui - .add_sized( - [width, text], - SelectableLabel::new(self.payout_view == PayoutView::Latest, "Latest"), - ) - .on_hover_text(STATUS_SUBMENU_LATEST) - .clicked() - { - self.payout_view = PayoutView::Latest; - } - ui.separator(); - if ui - .add_sized( - [width, text], - SelectableLabel::new(self.payout_view == PayoutView::Oldest, "Oldest"), - ) - .on_hover_text(STATUS_SUBMENU_OLDEST) - .clicked() - { - self.payout_view = PayoutView::Oldest; - } - ui.separator(); - if ui - .add_sized( - [width, text], - SelectableLabel::new(self.payout_view == PayoutView::Biggest, "Biggest"), - ) - .on_hover_text(STATUS_SUBMENU_BIGGEST) - .clicked() - { - self.payout_view = PayoutView::Biggest; - } - ui.separator(); - if ui - .add_sized( - [width, text], - SelectableLabel::new(self.payout_view == PayoutView::Smallest, "Smallest"), - ) - .on_hover_text(STATUS_SUBMENU_SMALLEST) - .clicked() - { - self.payout_view = PayoutView::Smallest; - } - }); - ui.separator(); - // Actual logs - egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { - egui::ScrollArea::vertical() - .stick_to_bottom(self.payout_view == PayoutView::Oldest) - .max_width(width) - .max_height(log) - .auto_shrink([false; 2]) - .show_viewport(ui, |ui, _| { - ui.style_mut().override_text_style = Some(egui::TextStyle::Body); - match self.payout_view { - PayoutView::Latest => ui.add_sized( - [width, log], - TextEdit::multiline(&mut api.log_rev.as_str()), + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); + ui.style_mut().override_text_style = Some(TextStyle::Body); + let size_text = ui.text_style_height(&TextStyle::Body); + let height = (ui.style().spacing.button_padding.y * 2.0) + size_text; + ScrollArea::vertical().show(ui, |ui| { + ui.group(|ui| { + ScrollArea::horizontal().show(ui, |ui| { + ui.horizontal(|ui| { + let width = + ((ui.available_width() / 3.0) - (SPACE * 4.0)).max(size_text * 9.0); + let width_button = + ((ui.available_width() / 3.0 / 4.0) - SPACE).max(size_text * 4.0); + ui.add_sized( + [width, height], + Label::new( + RichText::new(format!("Total Payouts: {}", api.payout)) + .underline() + .color(LIGHT_GRAY), ), - PayoutView::Oldest => ui.add_sized( - [width, log], - TextEdit::multiline(&mut api.log.as_str()), + ) + .on_hover_text(STATUS_SUBMENU_PAYOUT); + ui.add(Separator::default().vertical()); + ui.add_sized( + [width, height], + Label::new( + RichText::new(format!("Total XMR: {}", api.xmr)) + .underline() + .color(LIGHT_GRAY), ), - PayoutView::Biggest => ui.add_sized( - [width, log], - TextEdit::multiline(&mut api.payout_high.as_str()), - ), - PayoutView::Smallest => ui.add_sized( - [width, log], - TextEdit::multiline(&mut api.payout_low.as_str()), - ), - }; + ) + .on_hover_text(STATUS_SUBMENU_XMR); + // }); + ui.add(Separator::default().vertical()); + PayoutView::iter().enumerate().for_each(|(count, p)| { + if ui + .add_sized( + [width_button, height], + SelectableLabel::new(self.payout_view == p, p.to_string()), + ) + .on_hover_text(p.msg_help()) + .clicked() + { + self.payout_view = p; + } + if count + 1 < PayoutView::COUNT { + ui.add(Separator::default().vertical()); + } + }); }); - }); - }); - drop(api); - // Payout/Share Calculator - let button = (width / 20.0) - (SPACE * 1.666); - ui.group(|ui| { - ui.horizontal(|ui| { - ui.set_min_width(width - SPACE); - if ui - .add_sized( - [button * 2.0, text], - SelectableLabel::new(!self.manual_hash, "Automatic"), - ) - .on_hover_text(STATUS_SUBMENU_AUTOMATIC) - .clicked() - { - self.manual_hash = false; - } - ui.separator(); - if ui - .add_sized( - [button * 2.0, text], - SelectableLabel::new(self.manual_hash, "Manual"), - ) - .on_hover_text(STATUS_SUBMENU_MANUAL) - .clicked() - { - self.manual_hash = true; - } - ui.separator(); - ui.add_enabled_ui(self.manual_hash, |ui| { - if ui - .add_sized( - [button, text], - SelectableLabel::new(self.hash_metric == Hash::Hash, "Hash"), - ) - .on_hover_text(STATUS_SUBMENU_HASH) - .clicked() - { - self.hash_metric = Hash::Hash; - } - ui.separator(); - if ui - .add_sized( - [button, text], - SelectableLabel::new(self.hash_metric == Hash::Kilo, "Kilo"), - ) - .on_hover_text(STATUS_SUBMENU_KILO) - .clicked() - { - self.hash_metric = Hash::Kilo; - } - ui.separator(); - if ui - .add_sized( - [button, text], - SelectableLabel::new(self.hash_metric == Hash::Mega, "Mega"), - ) - .on_hover_text(STATUS_SUBMENU_MEGA) - .clicked() - { - self.hash_metric = Hash::Mega; - } - ui.separator(); - if ui - .add_sized( - [button, text], - SelectableLabel::new(self.hash_metric == Hash::Giga, "Giga"), - ) - .on_hover_text(STATUS_SUBMENU_GIGA) - .clicked() - { - self.hash_metric = Hash::Giga; - } - ui.separator(); - ui.spacing_mut().slider_width = button * 11.5; - ui.add_sized( - [button * 14.0, text], - Slider::new(&mut self.hashrate, 1.0..=1_000.0), - ); }); - }) - }); - // Actual stats - ui.add_enabled_ui(p2pool_alive, |ui| { - let text = height / 25.0; - let width = (width / 3.0) - (SPACE * 1.666); - let min_height = ui.available_height() / 1.3; - let api = p2pool_api.lock().unwrap(); - ui.horizontal(|ui| { - ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_height(min_height); - ui.add_sized( - [width, text], - Label::new(RichText::new("Monero Difficulty").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_MONERO_DIFFICULTY); - ui.add_sized([width, text], Label::new(api.monero_difficulty.as_str())); - ui.add_sized( - [width, text], - Label::new(RichText::new("Monero Hashrate").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_MONERO_HASHRATE); - ui.add_sized([width, text], Label::new(api.monero_hashrate.as_str())); - ui.add_sized( - [width, text], - Label::new(RichText::new("P2Pool Difficulty").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_DIFFICULTY); - ui.add_sized([width, text], Label::new(api.p2pool_difficulty.as_str())); - ui.add_sized( - [width, text], - Label::new(RichText::new("P2Pool Hashrate").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_HASHRATE); - ui.add_sized([width, text], Label::new(api.p2pool_hashrate.as_str())); - }) - }); - ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_height(min_height); - if self.manual_hash { - let hashrate = - Hash::convert_to_hash(self.hashrate, self.hash_metric) as u64; - let p2pool_share_mean = PubP2poolApi::calculate_share_or_block_time( - hashrate, - api.p2pool_difficulty_u64, - ); - let solo_block_mean = PubP2poolApi::calculate_share_or_block_time( - hashrate, - api.monero_difficulty_u64, - ); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Manually Inputted Hashrate") - .underline() - .color(BONE), - ), - ); - ui.add_sized( - [width, text], - Label::new(format!("{} H/s", Unsigned::from(hashrate))), - ); - ui.add_sized( - [width, text], - Label::new( - RichText::new("P2Pool Block Mean").underline().color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN); - ui.add_sized( - [width, text], - Label::new(api.p2pool_block_mean.to_string()), - ); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your P2Pool Share Mean") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN); - ui.add_sized([width, text], Label::new(p2pool_share_mean.to_string())); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your Solo Block Mean") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN); - ui.add_sized([width, text], Label::new(solo_block_mean.to_string())); - } else { - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your P2Pool Hashrate") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE); - ui.add_sized( - [width, text], - Label::new(format!("{} H/s", api.hashrate_1h)), - ); - ui.add_sized( - [width, text], - Label::new( - RichText::new("P2Pool Block Mean").underline().color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN); - ui.add_sized( - [width, text], - Label::new(api.p2pool_block_mean.to_string()), - ); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your P2Pool Share Mean") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN); - ui.add_sized( - [width, text], - Label::new(api.p2pool_share_mean.to_string()), - ); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your Solo Block Mean") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN); - ui.add_sized( - [width, text], - Label::new(api.solo_block_mean.to_string()), - ); - } - }) - }); - ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_height(min_height); - if self.manual_hash { - let hashrate = - Hash::convert_to_hash(self.hashrate, self.hash_metric) as u64; - let user_p2pool_percent = PubP2poolApi::calculate_dominance( - hashrate, - api.p2pool_hashrate_u64, - ); - let user_monero_percent = PubP2poolApi::calculate_dominance( - hashrate, - api.monero_hashrate_u64, - ); - ui.add_sized( - [width, text], - Label::new(RichText::new("P2Pool Miners").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_MINERS); - ui.add_sized([width, text], Label::new(api.miners.as_str())); - ui.add_sized( - [width, text], - Label::new( - RichText::new("P2Pool Dominance").underline().color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE); - ui.add_sized([width, text], Label::new(api.p2pool_percent.as_str())); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your P2Pool Dominance") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE); - ui.add_sized([width, text], Label::new(user_p2pool_percent.as_str())); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your Monero Dominance") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE); - ui.add_sized([width, text], Label::new(user_monero_percent.as_str())); - } else { - ui.add_sized( - [width, text], - Label::new(RichText::new("P2Pool Miners").underline().color(BONE)), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_MINERS); - ui.add_sized([width, text], Label::new(api.miners.as_str())); - ui.add_sized( - [width, text], - Label::new( - RichText::new("P2Pool Dominance").underline().color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE); - ui.add_sized([width, text], Label::new(api.p2pool_percent.as_str())); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your P2Pool Dominance") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE); - ui.add_sized( - [width, text], - Label::new(api.user_p2pool_percent.as_str()), - ); - ui.add_sized( - [width, text], - Label::new( - RichText::new("Your Monero Dominance") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE); - ui.add_sized( - [width, text], - Label::new(api.user_monero_percent.as_str()), - ); - } - }) + // ui.separator(); + // Actual logs + egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { + egui::ScrollArea::vertical() + .stick_to_bottom(self.payout_view == PayoutView::Oldest) + .max_width(ui.available_width()) + .max_height(ui.available_height() / 2.8) + .auto_shrink([false; 2]) + .show_viewport(ui, |ui, _| { + ui.style_mut().override_text_style = Some(egui::TextStyle::Body); + ui.style_mut().spacing.text_edit_width = ui.available_width(); + match self.payout_view { + PayoutView::Latest => { + ui.text_edit_multiline(&mut api.log_rev.as_str()) + } + PayoutView::Oldest => ui.text_edit_multiline(&mut api.log.as_str()), + PayoutView::Biggest => { + ui.text_edit_multiline(&mut api.payout_high.as_str()) + } + PayoutView::Smallest => { + ui.text_edit_multiline(&mut api.payout_low.as_str()) + } + }; + }); }); }); - // Tick bar - ui.add_sized( - [ui.available_width(), text], - Label::new(api.calculate_tick_bar()), - ) - .on_hover_text(STATUS_SUBMENU_PROGRESS_BAR); + // }); drop(api); + // Payout/Share Calculator + // let button = (width / 20.0) - (SPACE * 1.666); + ui.group(|ui| { + ui.set_width(ui.available_width()); + ui.horizontal(|ui| { + // ui.set_min_width(width - SPACE); + let width = ui.available_width() / 10.0; + if ui + .add_sized( + [width, height], + SelectableLabel::new(!self.manual_hash, "Automatic"), + ) + .on_hover_text(STATUS_SUBMENU_AUTOMATIC) + .clicked() + { + self.manual_hash = false; + } + ui.separator(); + if ui + .add_sized( + [width, height], + SelectableLabel::new(self.manual_hash, "Manual"), + ) + .on_hover_text(STATUS_SUBMENU_MANUAL) + .clicked() + { + self.manual_hash = true; + } + ui.separator(); + ui.add_enabled_ui(self.manual_hash, |ui| { + if ui + .selectable_label(self.hash_metric == Hash::Hash, "Hash") + .on_hover_text(STATUS_SUBMENU_HASH) + .clicked() + { + self.hash_metric = Hash::Hash; + } + ui.separator(); + if ui + .selectable_label(self.hash_metric == Hash::Kilo, "Kilo") + .on_hover_text(STATUS_SUBMENU_KILO) + .clicked() + { + self.hash_metric = Hash::Kilo; + } + ui.separator(); + if ui + .selectable_label(self.hash_metric == Hash::Mega, "Mega") + .on_hover_text(STATUS_SUBMENU_MEGA) + .clicked() + { + self.hash_metric = Hash::Mega; + } + ui.separator(); + if ui + .selectable_label(self.hash_metric == Hash::Giga, "Giga") + .on_hover_text(STATUS_SUBMENU_GIGA) + .clicked() + { + self.hash_metric = Hash::Giga; + } + ui.separator(); + ui.spacing_mut().slider_width = (ui.available_width() / 1.2).max(0.0); + ui.add_sized( + [0.0, height], + Slider::new(&mut self.hashrate, 1.0..=1_000.0) + .suffix(format!(" {}", self.hash_metric)), + ); + }); + }) + }); + // Actual stats + ui.add_space(height / 2.0); + ui.add_enabled_ui(p2pool_alive, |ui| { + let min_height = ui.available_height() / 1.5; + let api = p2pool_api.lock().unwrap(); + ui.horizontal(|ui| { + ui.columns_const(|[col1, col2, col3]| { + col1.group(|ui| { + ui.vertical_centered(|ui| { + ui.add_space(height * 2.0); + ui.set_min_height(min_height); + ui.label( + RichText::new("Monero Difficulty").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_MONERO_DIFFICULTY); + ui.label(api.monero_difficulty.as_str()); + ui.label(RichText::new("Monero Hashrate").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_MONERO_HASHRATE); + ui.label(api.monero_hashrate.as_str()); + ui.label( + RichText::new("P2Pool Difficulty").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_DIFFICULTY); + ui.label(api.p2pool_difficulty.as_str()); + ui.label(RichText::new("P2Pool Hashrate").underline().color(BONE)) + .on_hover_text(STATUS_SUBMENU_P2POOL_HASHRATE); + ui.label(api.p2pool_hashrate.as_str()); + }) + }); + col2.group(|ui| { + ui.vertical_centered(|ui| { + ui.add_space(height * 2.0); + ui.set_min_height(min_height); + if self.manual_hash { + let hashrate = + Hash::convert_to_hash(self.hashrate, self.hash_metric) + as u64; + let p2pool_share_mean = + PubP2poolApi::calculate_share_or_block_time( + hashrate, + api.p2pool_difficulty_u64, + ); + let solo_block_mean = + PubP2poolApi::calculate_share_or_block_time( + hashrate, + api.monero_difficulty_u64, + ); + ui.label( + RichText::new("Manually Inputted Hashrate") + .underline() + .color(BONE), + ); + ui.label(format!("{} H/s", Unsigned::from(hashrate))); + ui.label( + RichText::new("P2Pool Block Mean").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN); + ui.label(api.p2pool_block_mean.to_string()); + ui.label( + RichText::new("Your P2Pool Share Mean") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN); + ui.label(p2pool_share_mean.to_string()); + ui.label( + RichText::new("Your Solo Block Mean") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN); + ui.label(solo_block_mean.to_string()); + } else { + ui.label( + RichText::new("Your P2Pool Hashrate") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE); + ui.label(format!("{} H/s", api.hashrate_1h)); + ui.label( + RichText::new("P2Pool Block Mean").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN); + ui.label(api.p2pool_block_mean.to_string()); + ui.label( + RichText::new("Your P2Pool Share Mean") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN); + ui.label(api.p2pool_share_mean.to_string()); + ui.label( + RichText::new("Your Solo Block Mean") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN); + ui.label(api.solo_block_mean.to_string()); + } + }) + }); + col3.group(|ui| { + ui.vertical_centered(|ui| { + ui.add_space(height * 2.0); + ui.set_min_height(min_height); + if self.manual_hash { + let hashrate = + Hash::convert_to_hash(self.hashrate, self.hash_metric) + as u64; + let user_p2pool_percent = PubP2poolApi::calculate_dominance( + hashrate, + api.p2pool_hashrate_u64, + ); + let user_monero_percent = PubP2poolApi::calculate_dominance( + hashrate, + api.monero_hashrate_u64, + ); + ui.label( + RichText::new("P2Pool Miners").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_MINERS); + ui.label(api.miners.as_str()); + ui.label( + RichText::new("P2Pool Dominance").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE); + ui.label(api.p2pool_percent.as_str()); + ui.label( + RichText::new("Your P2Pool Dominance") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE); + ui.label(user_p2pool_percent.as_str()); + ui.label( + RichText::new("Your Monero Dominance") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE); + ui.label(user_monero_percent.as_str()); + } else { + ui.label( + RichText::new("P2Pool Miners").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_MINERS); + ui.label(api.miners.as_str()); + ui.label( + RichText::new("P2Pool Dominance").underline().color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL_DOMINANCE); + ui.label(api.p2pool_percent.as_str()); + ui.label( + RichText::new("Your P2Pool Dominance") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE); + ui.label(api.user_p2pool_percent.as_str()); + ui.label( + RichText::new("Your Monero Dominance") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_SUBMENU_YOUR_MONERO_DOMINANCE); + ui.label(api.user_monero_percent.as_str()); + } + }) + }); + }); + }); + // Tick bar + ui.vertical_centered(|ui| { + ui.label(api.calculate_tick_bar()) + .on_hover_text(STATUS_SUBMENU_PROGRESS_BAR); + }); + drop(api); + }); }); } } diff --git a/src/app/panels/middle/status/processes.rs b/src/app/panels/middle/status/processes.rs index 4e7d17f..a49e70d 100644 --- a/src/app/panels/middle/status/processes.rs +++ b/src/app/panels/middle/status/processes.rs @@ -1,4 +1,4 @@ -use egui::{ScrollArea, Ui, Vec2}; +use egui::{ScrollArea, Ui}; use readable::up::UptimeFull; use std::sync::{Arc, Mutex}; @@ -12,14 +12,13 @@ use crate::helper::xvb::{PubXvbApi, rounds::XvbRound}; use crate::helper::{ProcessName, Sys}; use crate::constants::*; -use egui::{Label, RichText, TextStyle}; +use egui::{RichText, TextStyle}; use log::*; impl Status { #[allow(clippy::too_many_arguments)] pub(super) fn processes( &mut self, sys: &Arc>, - size: Vec2, ui: &mut egui::Ui, node_api: &Arc>, p2pool_api: &Arc>, @@ -28,642 +27,417 @@ impl Status { xmrig_proxy_api: &Arc>, xmrig_img: &Arc>, xvb_api: &Arc>, - max_threads: usize, + max_threads: u16, states: &ProcessStatesGui, ) { - // set fixed text size, temporary solution before refactoring text/widget size. - let width = ((size.x / 5.0) - (SPACE * 1.7500)).max(0.0); - let height = size.y / 25.0; - // height must be height - top - bottom - space * 2 - space of text - let size: Vec2 = [width, height].into(); - let min_height = (size.y - SPACE).max(0.0); - // min width must allow to display text without wrapping. - let size_text = ui.text_style_height(&TextStyle::Body); - // ui.spacing_mut().item_spacing = Vec2::new(2.0, 2.0); - let min_width = size_text * 14.0; - let min_size: Vec2 = [min_width, min_height].into(); - ui.horizontal(|ui| { - ScrollArea::horizontal().show(ui, |ui| { - ui.set_min_height(min_height * 34.2); - gupax(ui, min_size, size, sys); - node( - ui, - min_size, - size, - states.is_alive(ProcessName::Node), - node_api, - ); - p2pool( - ui, - min_size, - size, - states.is_alive(ProcessName::P2pool), - p2pool_api, - p2pool_img, - ); - xmrig( - ui, - min_size, - size, - states.is_alive(ProcessName::Xmrig), - xmrig_api, - xmrig_img, - max_threads, - ); - xmrig_proxy( - ui, - min_size, - size, - states.is_alive(ProcessName::XmrigProxy), - xmrig_proxy_api, - ); - xvb( - ui, - min_size, - size, - states.is_alive(ProcessName::Xvb), - xvb_api, - ); - }) + let width_column = ui.text_style_height(&TextStyle::Body) * 16.0; + let height_column = width_column * 2.4; + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); + // let width = ((ui.available_width() / 5.0) - (SPACE * 1.7500)).max(0.0); + ScrollArea::vertical().show(ui, |ui| { + ui.horizontal(|ui| { + ScrollArea::horizontal().show(ui, |ui| { + ui.vertical(|ui| { + ui.group(|ui| { + ui.set_width(width_column); + ui.set_height(height_column); + ui.vertical_centered(|ui| { + // ui.set_min_width(ui.text_style_height(&TextStyle::Body) * 2.0); + gupax(ui, sys); + }); + }); + }); + ui.vertical(|ui| { + ui.group(|ui| { + ui.set_width(width_column); + ui.set_height(height_column); + ui.vertical_centered(|ui| { + node(ui, states.is_alive(ProcessName::Node), node_api); + }); + }); + }); + ui.vertical(|ui| { + ui.group(|ui| { + ui.set_width(width_column); + ui.set_height(height_column); + ui.vertical_centered(|ui| { + p2pool( + ui, + states.is_alive(ProcessName::P2pool), + p2pool_api, + p2pool_img, + ); + }); + }); + }); + ui.vertical(|ui| { + ui.group(|ui| { + ui.set_width(width_column); + ui.set_height(height_column); + ui.vertical_centered(|ui| { + xmrig( + ui, + states.is_alive(ProcessName::Xmrig), + xmrig_api, + xmrig_img, + max_threads, + ); + }); + }); + }); + ui.vertical(|ui| { + ui.group(|ui| { + ui.set_width(width_column); + ui.set_height(height_column); + ui.vertical_centered(|ui| { + xmrig_proxy( + ui, + states.is_alive(ProcessName::XmrigProxy), + xmrig_proxy_api, + ); + }); + }); + }); + ui.vertical(|ui| { + ui.group(|ui| { + ui.set_width(width_column); + ui.set_height(height_column); + ui.vertical_centered(|ui| { + xvb(ui, states.is_alive(ProcessName::Xvb), xvb_api); + }); + }); + }); + }); + }); }); } } -fn gupax(ui: &mut Ui, min_size: Vec2, size: Vec2, sys: &Arc>) { - ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_size(min_size); - ui.set_min_height(min_size.y * 34.0); - debug!("Status Tab | Rendering [Gupaxx]"); - // ui.set_min_size([min_size.x, min_size.y / 2.0].into()); - ui.add_sized( - size, - Label::new( - RichText::new("[Gupaxx]") - .color(LIGHT_GRAY) - .text_style(TextStyle::Heading), - ), - ) - .on_hover_text("Gupaxx is online"); - let sys = sys.lock().unwrap(); - ui.add_sized( - size, - Label::new(RichText::new("Uptime").underline().color(BONE)), - ) - .on_hover_text(STATUS_GUPAX_UPTIME); - ui.add_sized(size, Label::new(sys.gupax_uptime.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Gupaxx CPU").underline().color(BONE)), - ) - .on_hover_text(STATUS_GUPAX_CPU_USAGE); - ui.add_sized(size, Label::new(sys.gupax_cpu_usage.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Gupaxx Memory").underline().color(BONE)), - ) - .on_hover_text(STATUS_GUPAX_MEMORY_USAGE); - ui.add_sized(size, Label::new(sys.gupax_memory_used_mb.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("System CPU").underline().color(BONE)), - ) - .on_hover_text(STATUS_GUPAX_SYSTEM_CPU_USAGE); - ui.add_sized(size, Label::new(sys.system_cpu_usage.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("System Memory").underline().color(BONE)), - ) - .on_hover_text(STATUS_GUPAX_SYSTEM_MEMORY); - ui.add_sized(size, Label::new(sys.system_memory.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("System CPU Model").underline().color(BONE)), - ) - .on_hover_text(STATUS_GUPAX_SYSTEM_CPU_MODEL); - ui.add_sized(size, Label::new(sys.system_cpu_model.to_string())); - drop(sys); - }) - }); + +fn gupax(ui: &mut Ui, sys: &Arc>) { + ui.label( + RichText::new("[Gupaxx]") + .color(LIGHT_GRAY) + .text_style(TextStyle::Heading), + ) + .on_hover_text("Gupaxx is online"); + let sys = sys.lock().unwrap(); + ui.label(RichText::new("Uptime").underline().color(BONE)) + .on_hover_text(STATUS_GUPAX_UPTIME); + ui.label(sys.gupax_uptime.to_string()); + ui.label(RichText::new("Gupaxx CPU").underline().color(BONE)) + .on_hover_text(STATUS_GUPAX_CPU_USAGE); + ui.label(sys.gupax_cpu_usage.to_string()); + ui.label(RichText::new("Gupaxx Memory").underline().color(BONE)) + .on_hover_text(STATUS_GUPAX_MEMORY_USAGE); + ui.label(sys.gupax_memory_used_mb.to_string()); + ui.label(RichText::new("System CPU").underline().color(BONE)) + .on_hover_text(STATUS_GUPAX_SYSTEM_CPU_USAGE); + ui.label(sys.system_cpu_usage.to_string()); + ui.label(RichText::new("System Memory").underline().color(BONE)) + .on_hover_text(STATUS_GUPAX_SYSTEM_MEMORY); + ui.label(sys.system_memory.to_string()); + ui.label(RichText::new("System CPU Model").underline().color(BONE)) + .on_hover_text(STATUS_GUPAX_SYSTEM_CPU_MODEL); + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap); + ui.label(sys.system_cpu_model.to_string()); + drop(sys); } fn p2pool( ui: &mut Ui, - min_size: Vec2, - size: Vec2, p2pool_alive: bool, p2pool_api: &Arc>, p2pool_img: &Arc>, ) { - ui.group(|ui| { - ui.vertical(|ui| { - ScrollArea::vertical().show(ui, |ui| { - ui.set_min_height(min_size.y * 34.0); - ui.set_min_size(min_size); - debug!("Status Tab | Rendering [P2Pool]"); - ui.add_enabled_ui(p2pool_alive, |ui| { - ui.add_sized( - size, - Label::new( - RichText::new("[P2Pool]") - .color(LIGHT_GRAY) - .text_style(TextStyle::Heading), - ), - ) - .on_hover_text("P2Pool is online") - .on_disabled_hover_text("P2Pool is offline"); - ui.style_mut().override_text_style = Some(TextStyle::Small); - let size = [size.x, size.y / 1.4]; - let api = p2pool_api.lock().unwrap(); - ui.add_sized( - size, - Label::new(RichText::new("Uptime").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_UPTIME); - ui.add_sized(size, Label::new(format!("{}", api.uptime))); - ui.add_sized( - size, - Label::new(RichText::new("Current Shares").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_CURRENT_SHARES); - ui.add_sized(size, Label::new(api.sidechain_shares.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Shares Found").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_SHARES); - ui.add_sized( - size, - Label::new( - (if let Some(s) = api.shares_found { - s.to_string() - } else { - UNKNOWN_DATA.to_string() - }) - .to_string(), - ), - ); - ui.add_sized( - size, - Label::new(RichText::new("Payouts").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_PAYOUTS); - ui.add_sized(size, Label::new(format!("Total: {}", api.payouts))); - ui.add_sized( - size, - Label::new(format!( - "[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]", - api.payouts_hour, api.payouts_day, api.payouts_month - )), - ); - ui.add_sized( - size, - Label::new(RichText::new("XMR Mined").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_XMR); - ui.add_sized(size, Label::new(format!("Total: {:.13} XMR", api.xmr))); - ui.add_sized( - size, - Label::new(format!( - "[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]", - api.xmr_hour, api.xmr_day, api.xmr_month - )), - ); - ui.add_sized( - size, - Label::new( - RichText::new("Hashrate (15m/1h/24h)") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_P2POOL_HASHRATE); - ui.add_sized( - size, - Label::new(format!( - "[{} H/s] [{} H/s] [{} H/s]", - api.hashrate_15m, api.hashrate_1h, api.hashrate_24h - )), - ); - ui.add_sized( - size, - Label::new(RichText::new("Miners Connected").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_CONNECTIONS); - ui.add_sized(size, Label::new(format!("{}", api.connections))); - ui.add_sized( - size, - Label::new(RichText::new("Effort").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_EFFORT); - ui.add_sized( - size, - Label::new(format!( - "[Average: {}] [Current: {}]", - api.average_effort, api.current_effort - )), - ); - let img = p2pool_img.lock().unwrap(); - ui.add_sized( - size, - Label::new(RichText::new("Monero Node").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_MONERO_NODE); - ui.add_sized( - size, - Label::new(format!( - "[IP: {}]\n[RPC: {}] [ZMQ: {}]", - &img.host, &img.rpc, &img.zmq - )), - ); - ui.add_sized( - size, - Label::new(RichText::new("Sidechain").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_POOL); - ui.add_sized(size, Label::new(&img.mini)); - ui.add_sized( - size, - Label::new(RichText::new("Address").underline().color(BONE)), - ) - .on_hover_text(STATUS_P2POOL_ADDRESS); - ui.add_sized(size, Label::new(&img.address)); - drop(img); - drop(api); - }); + ui.add_enabled_ui(p2pool_alive, |ui| { + ui.label( + RichText::new("[P2Pool]") + .color(LIGHT_GRAY) + .text_style(TextStyle::Heading), + ) + .on_hover_text("P2Pool is online") + .on_disabled_hover_text("P2Pool is offline"); + ui.style_mut().override_text_style = Some(TextStyle::Small); + let api = p2pool_api.lock().unwrap(); + ui.label(RichText::new("Uptime").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_UPTIME); + ui.label(format!("{}", api.uptime)); + ui.label(RichText::new("Current Shares").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_CURRENT_SHARES); + ui.label(api.sidechain_shares.to_string()); + ui.label(RichText::new("Shares Found").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_SHARES); + ui.label( + (if let Some(s) = api.shares_found { + s.to_string() + } else { + UNKNOWN_DATA.to_string() }) - }) + .to_string(), + ); + ui.label(RichText::new("Payouts").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_PAYOUTS); + ui.label(format!("Total: {}", api.payouts)); + ui.label(format!( + "[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]", + api.payouts_hour, api.payouts_day, api.payouts_month + )); + ui.label(RichText::new("XMR Mined").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_XMR); + ui.label(format!("Total: {:.13} XMR", api.xmr)); + ui.label(format!( + "[{:.7}/hour]\n[{:.7}/day]\n[{:.7}/month]", + api.xmr_hour, api.xmr_day, api.xmr_month + )); + ui.label( + RichText::new("Hashrate (15m/1h/24h)") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_P2POOL_HASHRATE); + ui.label(format!( + "[{} H/s]\n[{} H/s]\n[{} H/s]", + api.hashrate_15m, api.hashrate_1h, api.hashrate_24h + )); + ui.label(RichText::new("Miners Connected").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_CONNECTIONS); + ui.label(format!("{}", api.connections)); + ui.label(RichText::new("Effort").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_EFFORT); + ui.label(format!( + "[Average: {}] [Current: {}]", + api.average_effort, api.current_effort + )); + let img = p2pool_img.lock().unwrap(); + ui.label(RichText::new("Monero Node").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_MONERO_NODE); + ui.label(format!( + "IP: {}]\n[RPC: {}] [ZMQ: {}]", + &img.host, &img.rpc, &img.zmq + )); + ui.label(RichText::new("Sidechain").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_POOL); + ui.label(&img.mini); + ui.label(RichText::new("Address").underline().color(BONE)) + .on_hover_text(STATUS_P2POOL_ADDRESS); + ui.label(&img.address); + drop(img); + drop(api); }); } #[allow(clippy::too_many_arguments)] fn xmrig_proxy( ui: &mut Ui, - min_size: Vec2, - size: Vec2, xmrig_proxy_alive: bool, xmrig_proxy_api: &Arc>, ) { - ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_height(min_size.y * 34.0); - debug!("Status Tab | Rendering [XMRig-Proxy]"); - ui.add_enabled_ui(xmrig_proxy_alive, |ui| { - ui.set_min_size(min_size); - ui.add_sized( - size, - Label::new( - RichText::new("[XMRig-Proxy]") - .color(LIGHT_GRAY) - .text_style(TextStyle::Heading), - ), - ) - .on_hover_text("XMRig-Proxy is online") - .on_disabled_hover_text("XMRig-Proxy is offline"); - let api = xmrig_proxy_api.lock().unwrap(); - ui.add_sized( - size, - Label::new(RichText::new("Uptime").underline().color(BONE)), - ) - .on_hover_text(STATUS_XMRIG_PROXY_UPTIME); - ui.add_sized(size, Label::new(UptimeFull::from(api.uptime).as_str())); - ui.add_sized( - size, - Label::new( - RichText::new("Hashrate\n(1m/10m/1h/12h/24h)") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_XMRIG_PROXY_HASHRATE); - ui.add_sized( - size, - Label::new(format!( - "[{} H/s] [{} H/s]\n[{} H/s] [{} H/s] [{} H/s]", - api.hashrate_1m, - api.hashrate_10m, - api.hashrate_1h, - api.hashrate_12h, - api.hashrate_24h - )), - ); - ui.add_sized( - size, - Label::new(format!( - "[Accepted: {}]\n[Rejected: {}]", - api.accepted, api.rejected - )), - ); - ui.add_sized( - size, - Label::new(RichText::new("Pool").underline().color(BONE)), - ) - .on_hover_text(STATUS_XMRIG_PROXY_POOL); - ui.add_sized(size, Label::new(api.node.to_string())); - drop(api); - }); - }) + ui.add_enabled_ui(xmrig_proxy_alive, |ui| { + ui.label( + RichText::new("[XMRig-Proxy]") + .color(LIGHT_GRAY) + .text_style(TextStyle::Heading), + ) + .on_hover_text("XMRig-Proxy is online") + .on_disabled_hover_text("XMRig-Proxy is offline"); + let api = xmrig_proxy_api.lock().unwrap(); + ui.label(RichText::new("Uptime").underline().color(BONE)) + .on_hover_text(STATUS_XMRIG_PROXY_UPTIME); + ui.label(UptimeFull::from(api.uptime).as_str()); + ui.label( + RichText::new("Hashrate\n(1m/10m/1h/12h/24h)") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_XMRIG_PROXY_HASHRATE); + ui.label(format!( + "[{} H/s]\n[{} H/s]\n[{} H/s]\n[{} H/s]\n[{} H/s]", + api.hashrate_1m, api.hashrate_10m, api.hashrate_1h, api.hashrate_12h, api.hashrate_24h + )); + ui.label(format!( + "[Accepted: {}]\n[Rejected: {}]", + api.accepted, api.rejected + )); + ui.label(RichText::new("Pool").underline().color(BONE)) + .on_hover_text(STATUS_XMRIG_PROXY_POOL); + ui.label(api.node.to_string()); + drop(api); }); } #[allow(clippy::too_many_arguments)] fn xmrig( ui: &mut Ui, - min_size: Vec2, - size: Vec2, xmrig_alive: bool, xmrig_api: &Arc>, xmrig_img: &Arc>, - max_threads: usize, + max_threads: u16, ) { - ui.group(|ui| { - // ScrollArea::vertical().show(ui, |ui| { - ui.vertical(|ui| { - ui.set_min_height(min_size.y * 34.0); - ui.spacing_mut().item_spacing = Vec2::new(2.0, 2.0); - debug!("Status Tab | Rendering [XMRig]"); - ui.add_enabled_ui(xmrig_alive, |ui| { - // ui.set_min_size(min_size); - ui.add_sized( - size, - Label::new( - RichText::new("[XMRig]") - .color(LIGHT_GRAY) - .text_style(TextStyle::Heading), - ), - ) - .on_hover_text("XMRig is online") - .on_disabled_hover_text("XMRig is offline"); - let api = xmrig_api.lock().unwrap(); - ui.add_sized( - size, - Label::new(RichText::new("Uptime").underline().color(BONE)), - ) - .on_hover_text(STATUS_XMRIG_UPTIME); - ui.add_sized(size, Label::new(UptimeFull::from(api.uptime).as_str())); - ui.add_sized(size, Label::new(api.resources.to_string())); - ui.add_sized( - size, - Label::new( - RichText::new("Hashrate\n(10s/1m/15m)") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_XMRIG_HASHRATE); - ui.add_sized(size, Label::new(api.hashrate.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Difficulty").underline().color(BONE)), - ) - .on_hover_text(STATUS_XMRIG_DIFFICULTY); - ui.add_sized(size, Label::new(api.diff.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Shares").underline().color(BONE)), - ) - .on_hover_text(STATUS_XMRIG_SHARES); - ui.add_sized( - size, - Label::new(format!( - "[Accepted: {}]\n[Rejected: {}]", - api.accepted, api.rejected - )), - ); - ui.add_sized( - size, - Label::new(RichText::new("Pool").underline().color(BONE)), - ) - .on_hover_text(STATUS_XMRIG_POOL); - ui.add_sized(size, Label::new(api.node.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Threads").underline().color(BONE)), - ) - .on_hover_text(STATUS_XMRIG_THREADS); - ui.add_sized( - size, - Label::new(format!( - "{}/{}", - &xmrig_img.lock().unwrap().threads, - max_threads - )), - ); - drop(api); - }); - }) - // }) + debug!("Status Tab | Rendering [XMRig]"); + ui.add_enabled_ui(xmrig_alive, |ui| { + // ui.set_min_size(min_size); + ui.label( + RichText::new("[XMRig]") + .color(LIGHT_GRAY) + .text_style(TextStyle::Heading), + ) + .on_hover_text("XMRig is online") + .on_disabled_hover_text("XMRig is offline"); + let api = xmrig_api.lock().unwrap(); + ui.label(RichText::new("Uptime").underline().color(BONE)) + .on_hover_text(STATUS_XMRIG_UPTIME); + ui.label(UptimeFull::from(api.uptime).as_str()); + ui.label(api.resources.to_string()); + ui.label( + RichText::new("Hashrate\n(10s/1m/15m)") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_XMRIG_HASHRATE); + ui.label(api.hashrate.to_string()); + ui.label(RichText::new("Difficulty").underline().color(BONE)) + .on_hover_text(STATUS_XMRIG_DIFFICULTY); + ui.label(api.diff.to_string()); + ui.label(RichText::new("Shares").underline().color(BONE)) + .on_hover_text(STATUS_XMRIG_SHARES); + ui.label(format!( + "[Accepted: {}]\n[Rejected: {}]", + api.accepted, api.rejected + )); + ui.label(RichText::new("Pool").underline().color(BONE)) + .on_hover_text(STATUS_XMRIG_POOL); + ui.label(api.node.to_string()); + ui.label(RichText::new("Threads").underline().color(BONE)) + .on_hover_text(STATUS_XMRIG_THREADS); + ui.label(format!( + "{}/{}", + &xmrig_img.lock().unwrap().threads, + max_threads + )); + drop(api); }); } -fn xvb(ui: &mut Ui, min_size: Vec2, size: Vec2, xvb_alive: bool, xvb_api: &Arc>) { +fn xvb(ui: &mut Ui, xvb_alive: bool, xvb_api: &Arc>) { // let api = &xvb_api.lock().unwrap().stats_pub; let enabled = xvb_alive; - ui.group(|ui| { - ScrollArea::vertical().show(ui, |ui| { - ui.set_min_height(min_size.y * 34.0); - ui.vertical(|ui| { - debug!("Status Tab | Rendering [XvB]"); - ui.add_enabled_ui(enabled, |ui| { - // for now there is no API ping or /health, so we verify if the field reward_yearly is empty or not. - // ui.set_min_size(min_size); - ui.add_sized( - size, - Label::new( - RichText::new("[XvB Raffle]") - .color(LIGHT_GRAY) - .text_style(TextStyle::Heading), - ), - ) - .on_hover_text("XvB API stats") - .on_disabled_hover_text("No data received from XvB API"); - // [Round Type] - ui.add_sized( - size, - Label::new(RichText::new("Round Type").underline().color(BONE)), - ) - .on_hover_text(STATUS_XVB_ROUND_TYPE); - ui.add_sized(size, Label::new(api.round_type.to_string())); - // [Time Remaining] - ui.add_sized( - size, - Label::new( - RichText::new("Round Time Remaining") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_XVB_TIME_REMAIN); - ui.add_sized(size, Label::new(format!("{} minutes", api.time_remain))); - // Donated Hashrate - ui.add_sized( - size, - Label::new(RichText::new("Bonus Hashrate").underline().color(BONE)), - ) - .on_hover_text(STATUS_XVB_DONATED_HR); - ui.add_sized( - size, - Label::new(format!( - "{}kH/s\n+\n{}kH/s\ndonated by\n{} donors\n with\n{} miners", - api.bonus_hr, api.donate_hr, api.donate_miners, api.donate_workers - )), - ); - // Players - ui.add_sized( - size, - Label::new(RichText::new("Players").underline().color(BONE)), - ) - .on_hover_text(STATUS_XVB_PLAYERS); - ui.add_sized( - size, - Label::new(format!( - "[Registered: {}]\n[Playing: {}]", - api.players, api.players_round - )), - ); - // Winner - ui.add_sized( - size, - Label::new(RichText::new("Winner").underline().color(BONE)), - ) - .on_hover_text(STATUS_XVB_WINNER); - ui.add_sized(size, Label::new(&api.winner)); - // Share effort - ui.add_sized( - size, - Label::new(RichText::new("Share Effort").underline().color(BONE)), - ) - .on_hover_text(STATUS_XVB_SHARE); - ui.add_sized(size, Label::new(api.share_effort.to_string())); - // Block reward - ui.add_sized( - size, - Label::new(RichText::new("Block Reward").underline().color(BONE)), - ) - .on_hover_text(STATUS_XVB_BLOCK_REWARD); - ui.add_sized(size, Label::new(api.block_reward.to_string())); - // reward yearly - ui.add_sized( - size, - Label::new( - RichText::new("Est. Reward (Yearly)") - .underline() - .color(BONE), - ), - ) - .on_hover_text(STATUS_XVB_YEARLY); - if api.reward_yearly.is_empty() { - ui.add_sized(size, Label::new("No information".to_string())); - } else { - ui.add_sized( - size, - Label::new(format!( - "{}: {} XMR\n{}: {} XMR\n{}: {} XMR\n{}: {} XMR\n{}: {} XMR", - XvbRound::Vip, - api.reward_yearly[0], - XvbRound::Donor, - api.reward_yearly[1], - XvbRound::DonorVip, - api.reward_yearly[2], - XvbRound::DonorWhale, - api.reward_yearly[3], - XvbRound::DonorMega, - api.reward_yearly[4] - )), - ); - } - }); - }) - // by round - }); + debug!("Status Tab | Rendering [XvB]"); + ui.add_enabled_ui(enabled, |ui| { + // for now there is no API ping or /health, so we verify if the field reward_yearly is empty or not. + // ui.set_min_size(min_size); + ui.label( + RichText::new("[XvB Raffle]") + .color(LIGHT_GRAY) + .text_style(TextStyle::Heading), + ) + .on_hover_text("XvB API stats") + .on_disabled_hover_text("No data received from XvB API"); + // [Round Type] + ui.label(RichText::new("Round Type").underline().color(BONE)) + .on_hover_text(STATUS_XVB_ROUND_TYPE); + ui.label(api.round_type.to_string()); + // [Time Remaining] + ui.label( + RichText::new("Round Time Remaining") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_XVB_TIME_REMAIN); + ui.label(format!("{} minutes", api.time_remain)); + // Donated Hashrate + ui.label(RichText::new("Bonus Hashrate").underline().color(BONE)) + .on_hover_text(STATUS_XVB_DONATED_HR); + ui.label(format!( + "{}kH/s\n+\n{}kH/s\ndonated by\n{} donors\n with\n{} miners", + api.bonus_hr, api.donate_hr, api.donate_miners, api.donate_workers + )); + // Players + ui.label(RichText::new("Players").underline().color(BONE)) + .on_hover_text(STATUS_XVB_PLAYERS); + ui.label(format!( + "[Registered: {}]\n[Playing: {}]", + api.players, api.players_round + )); + // Winner + ui.label(RichText::new("Winner").underline().color(BONE)) + .on_hover_text(STATUS_XVB_WINNER); + ui.label(&api.winner); + // Share effort + ui.label(RichText::new("Share Effort").underline().color(BONE)) + .on_hover_text(STATUS_XVB_SHARE); + ui.label(api.share_effort.to_string()); + // Block reward + ui.label(RichText::new("Block Reward").underline().color(BONE)) + .on_hover_text(STATUS_XVB_BLOCK_REWARD); + ui.label(api.block_reward.to_string()); + // reward yearly + ui.label( + RichText::new("Est. Reward (Yearly)") + .underline() + .color(BONE), + ) + .on_hover_text(STATUS_XVB_YEARLY); + if api.reward_yearly.is_empty() { + ui.label("No information".to_string()); + } else { + ui.label(format!( + "{}: {} XMR\n{}: {} XMR\n{}: {} XMR\n{}: {} XMR\n{}: {} XMR", + XvbRound::Vip, + api.reward_yearly[0], + XvbRound::Donor, + api.reward_yearly[1], + XvbRound::DonorVip, + api.reward_yearly[2], + XvbRound::DonorWhale, + api.reward_yearly[3], + XvbRound::DonorMega, + api.reward_yearly[4] + )); + } }); } #[allow(clippy::too_many_arguments)] -fn node( - ui: &mut Ui, - min_size: Vec2, - size: Vec2, - node_alive: bool, - node_api: &Arc>, -) { - ui.group(|ui| { - ui.vertical(|ui| { - ui.set_min_height(min_size.y * 34.0); - debug!("Status Tab | Rendering [Node]"); - ui.add_enabled_ui(node_alive, |ui| { - ui.set_min_size(min_size); - ui.add_sized( - size, - Label::new( - RichText::new("[Node]") - .color(LIGHT_GRAY) - .text_style(TextStyle::Heading), - ), - ) - .on_hover_text("Node is online") - .on_disabled_hover_text("Node is offline"); - let api = node_api.lock().unwrap(); - ui.add_sized( - size, - Label::new(RichText::new("Uptime").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_UPTIME); - ui.add_sized(size, Label::new(api.uptime.to_string())); +fn node(ui: &mut Ui, node_alive: bool, node_api: &Arc>) { + debug!("Status Tab | Rendering [Node]"); + ui.add_enabled_ui(node_alive, |ui| { + ui.label( + RichText::new("[Node]") + .color(LIGHT_GRAY) + .text_style(TextStyle::Heading), + ) + .on_hover_text("Node is online") + .on_disabled_hover_text("Node is offline"); + let api = node_api.lock().unwrap(); + ui.label(RichText::new("Uptime").underline().color(BONE)) + .on_hover_text(STATUS_NODE_UPTIME); + ui.label(api.uptime.to_string()); - ui.add_sized( - size, - Label::new(RichText::new("Block Height").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_BLOCK_HEIGHT); - ui.add_sized(size, Label::new(api.blockheight.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Network Difficulty").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_DIFFICULTY); - ui.add_sized(size, Label::new(api.difficulty.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Database size").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_DB_SIZE); - ui.add_sized(size, Label::new(api.database_size.to_owned())); - ui.add_sized( - size, - Label::new(RichText::new("Free space").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_FREESPACE); - ui.add_sized(size, Label::new(api.free_space.to_owned())); - ui.add_sized( - size, - Label::new(RichText::new("Network Type").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_NETTYPE); - ui.add_sized(size, Label::new(api.nettype.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Outgoing peers").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_OUT); - ui.add_sized(size, Label::new(api.outgoing_connections.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Incoming peers").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_IN); - ui.add_sized(size, Label::new(api.incoming_connections.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Synchronized").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_SYNC); - ui.add_sized(size, Label::new(api.synchronized.to_string())); - ui.add_sized( - size, - Label::new(RichText::new("Status").underline().color(BONE)), - ) - .on_hover_text(STATUS_NODE_STATUS); - ui.add_sized(size, Label::new(api.status.to_string())); - drop(api); - }); - }) + ui.label(RichText::new("Block Height").underline().color(BONE)) + .on_hover_text(STATUS_NODE_BLOCK_HEIGHT); + ui.label(api.blockheight.to_string()); + ui.label(RichText::new("Network Difficulty").underline().color(BONE)) + .on_hover_text(STATUS_NODE_DIFFICULTY); + ui.label(api.difficulty.to_string()); + ui.label(RichText::new("Database size").underline().color(BONE)) + .on_hover_text(STATUS_NODE_DB_SIZE); + ui.label(api.database_size.to_owned()); + ui.label(RichText::new("Free space").underline().color(BONE)) + .on_hover_text(STATUS_NODE_FREESPACE); + ui.label(api.free_space.to_owned()); + ui.label(RichText::new("Network Type").underline().color(BONE)) + .on_hover_text(STATUS_NODE_NETTYPE); + ui.label(api.nettype.to_string()); + ui.label(RichText::new("Outgoing peers").underline().color(BONE)) + .on_hover_text(STATUS_NODE_OUT); + ui.label(api.outgoing_connections.to_string()); + ui.label(RichText::new("Incoming peers").underline().color(BONE)) + .on_hover_text(STATUS_NODE_IN); + ui.label(api.incoming_connections.to_string()); + ui.label(RichText::new("Synchronized").underline().color(BONE)) + .on_hover_text(STATUS_NODE_SYNC); + ui.label(api.synchronized.to_string()); + ui.label(RichText::new("Status").underline().color(BONE)) + .on_hover_text(STATUS_NODE_STATUS); + ui.label(api.status.to_string()); + drop(api); }); } diff --git a/src/app/panels/middle/xmrig.rs b/src/app/panels/middle/xmrig.rs index e050aa1..1a00cb5 100644 --- a/src/app/panels/middle/xmrig.rs +++ b/src/app/panels/middle/xmrig.rs @@ -15,481 +15,195 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field}; +use crate::app::panels::middle::common::list_poolnode::list_poolnode; +use crate::app::panels::middle::common::state_edit_field::{ + monero_address_field, slider_state_field, +}; use crate::constants::*; -use crate::disk::pool::Pool; use crate::disk::state::Xmrig; use crate::helper::Process; use crate::helper::xrig::xmrig::PubXmrigApi; -use crate::regex::{REGEXES, num_lines}; -use crate::utils::regex::Regexes; -use egui::{ - Button, Checkbox, ComboBox, Label, RichText, SelectableLabel, Slider, TextEdit, TextStyle, - Vec2, vec2, -}; +use crate::miscs::height_txt_before_button; +use crate::regex::REGEXES; +use egui::{Checkbox, Ui, vec2}; use log::*; use std::sync::{Arc, Mutex}; +use super::common::list_poolnode::PoolNode; +use super::common::state_edit_field::StateTextEdit; + impl Xmrig { #[inline(always)] // called once #[allow(clippy::too_many_arguments)] pub fn show( &mut self, - pool_vec: &mut Vec<(String, Pool)>, + pool_vec: &mut Vec<(String, PoolNode)>, process: &Arc>, api: &Arc>, buffer: &mut String, - size: Vec2, _ctx: &egui::Context, ui: &mut egui::Ui, ) { - let text_edit = size.y / 25.0; - //---------------------------------------------------------------------------------------------------- [Simple] Console debug!("XMRig Tab | Rendering [Console]"); egui::ScrollArea::vertical().show(ui, |ui| { - ui.group(|ui| { - let text = &api.lock().unwrap().output; - let nb_lines = num_lines(text); - let (height, width) = if self.simple { - (size.y / 1.5, size.x - SPACE) - } else { - (size.y / 2.8, size.x - SPACE) - }; - egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { - ui.style_mut().override_text_style = Some(TextStyle::Small); - egui::ScrollArea::vertical() - .stick_to_bottom(true) - .max_width(width) - .max_height(height) - .auto_shrink([false; 2]) - // .show_viewport(ui, |ui, _| { - .show_rows( - ui, - ui.text_style_height(&TextStyle::Small), - nb_lines, - |ui, row_range| { - for i in row_range { - if let Some(line) = text.lines().nth(i) { - ui.label(line); - } - } - }, - ); - }); - //---------------------------------------------------------------------------------------------------- [Advanced] Console - if !self.simple { - ui.separator(); - let response = ui - .add_sized( - [width, text_edit], - TextEdit::hint_text( - TextEdit::singleline(buffer), - r#"Commands: [h]ashrate, [p]ause, [r]esume, re[s]ults, [c]onnection"#, - ), - ) - .on_hover_text(XMRIG_INPUT); - // If the user pressed enter, dump buffer contents into the process STDIN - if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { - response.request_focus(); // Get focus back - let buffer = std::mem::take(buffer); // Take buffer - let mut process = process.lock().unwrap(); // Lock - if process.is_alive() { - process.input.push(buffer); - } // Push only if alive - } - } - }); - - //---------------------------------------------------------------------------------------------------- Arguments - if !self.simple { - debug!("XMRig Tab | Rendering [Arguments]"); ui.group(|ui| { - ui.horizontal(|ui| { - let width = (size.x / 10.0) - SPACE; - ui.add_sized([width, text_edit], Label::new("Command arguments:")); - ui.add_sized( - [ui.available_width(), text_edit], - TextEdit::hint_text( - TextEdit::singleline(&mut self.arguments), - r#"--url <...> --user <...> --config <...>"#, - ), - ) - .on_hover_text(XMRIG_ARGUMENTS); - self.arguments.truncate(1024); - }) - }); - ui.add_enabled_ui(self.arguments.is_empty(), |ui|{ - - //---------------------------------------------------------------------------------------------------- Address - debug!("XMRig Tab | Rendering [Address]"); - ui.group(|ui| { - let width = size.x - SPACE; - ui.spacing_mut().text_edit_width = (width) - (SPACE * 3.0); - let text; - let color; - let len = format!("{:02}", self.address.len()); - if self.address.is_empty() { - text = format!("Monero Address [{}/95] ➖", len); - color = LIGHT_GRAY; - } else if Regexes::addr_ok(&self.address) { - text = format!("Monero Address [{}/95] ✔", len); - color = GREEN; - } else { - text = format!("Monero Address [{}/95] ❌", len); - color = RED; - } - ui.add_sized( - [width, text_edit], - Label::new(RichText::new(text).color(color)), - ); - ui.add_sized( - [width, text_edit], - TextEdit::hint_text(TextEdit::singleline(&mut self.address), "4..."), - ) - .on_hover_text(XMRIG_ADDRESS); - self.address.truncate(95); - }); - }); - } - - //---------------------------------------------------------------------------------------------------- Threads - if self.simple { - ui.add_space(SPACE); - } - debug!("XMRig Tab | Rendering [Threads]"); - ui.vertical(|ui| { - let width = size.x / 10.0; - let text_width = width * 2.4; - ui.spacing_mut().slider_width = width * 6.5; - ui.spacing_mut().icon_width = width / 25.0; - ui.horizontal(|ui| { - ui.add_sized( - [text_width, text_edit], - Label::new(format!("Threads [1-{}]:", self.max_threads)), - ); - ui.add_sized( - [width, text_edit], - Slider::new(&mut self.current_threads, 1..=self.max_threads), - ) - .on_hover_text(XMRIG_THREADS); - }); - #[cfg(not(target_os = "linux"))] // Pause on active isn't supported on Linux - ui.horizontal(|ui| { - ui.add_sized( - [text_width, text_edit], - Label::new("Pause on active [0-255]:".to_string()), - ); - ui.add_sized([width, text_edit], Slider::new(&mut self.pause, 0..=255)) - .on_hover_text(format!("{} [{}] seconds.", XMRIG_PAUSE, self.pause)); - }); - }); - - //---------------------------------------------------------------------------------------------------- Simple - if !self.simple { - debug!("XMRig Tab | Rendering [Pool List] elements"); - let width = ui.available_width() - 10.0; - let mut incorrect_input = false; // This will disable [Add/Delete] on bad input - // [Pool IP/Port] - ui.horizontal(|ui| { - ui.group(|ui| { - let width = width/10.0; - ui.vertical(|ui| { - ui.spacing_mut().text_edit_width = width*3.32; - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:02}", self.name.len()); - if self.name.is_empty() { - text = format!("Name [ {}/30 ]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.name.is_match(&self.name) { - text = format!("Name [ {}/30 ]✔", len); - color = GREEN; - } else { - text = format!("Name [ {}/30 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.name).on_hover_text(XMRIG_NAME); - self.name.truncate(30); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:03}", self.ip.len()); - if self.ip.is_empty() { - text = format!(" IP [{}/255]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if self.ip == "localhost" || REGEXES.ipv4.is_match(&self.ip) || REGEXES.domain.is_match(&self.ip) { - text = format!(" IP [{}/255]✔", len); - color = GREEN; - } else { - text = format!(" IP [{}/255]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.ip).on_hover_text(XMRIG_IP); - self.ip.truncate(255); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = self.port.len(); - if self.port.is_empty() { - text = format!("Port [ {}/5 ]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.port.is_match(&self.port) { - text = format!("Port [ {}/5 ]✔", len); - color = GREEN; - } else { - text = format!("Port [ {}/5 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.port).on_hover_text(XMRIG_PORT); - self.port.truncate(5); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:02}", self.rig.len()); - if self.rig.is_empty() { - text = format!(" Rig [ {}/30 ]➖", len); - color = LIGHT_GRAY; - } else if REGEXES.name.is_match(&self.rig) { - text = format!(" Rig [ {}/30 ]✔", len); - color = GREEN; - } else { - text = format!(" Rig [ {}/30 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.rig).on_hover_text(XMRIG_RIG); - self.rig.truncate(30); - }); - }); - - ui.vertical(|ui| { - let width = ui.available_width(); - ui.add_space(1.0); - // [Manual node selection] - ui.spacing_mut().slider_width = width - 8.0; - ui.spacing_mut().icon_width = width / 25.0; - // [Node List] - debug!("XMRig Tab | Rendering [Node List] ComboBox"); - let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name)); - ComboBox::from_id_salt("manual_pool").selected_text(text).width(width).show_ui(ui, |ui| { - for (n, (name, pool)) in pool_vec.iter().enumerate() { - let text = format!("{}. {}\n IP: {}\n Port: {}\n Rig: {}", n+1, name, pool.ip, pool.port, pool.rig); - if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() { - self.selected_index = n; - let pool = pool.clone(); - self.selected_name.clone_from(name); - self.selected_rig.clone_from(&pool.rig); - self.selected_ip.clone_from(&pool.ip); - self.selected_port.clone_from(&pool.port); - self.name.clone_from(name); - self.rig = pool.rig; - self.ip = pool.ip; - self.port = pool.port; - } - } - }); - // [Add/Save] - let pool_vec_len = pool_vec.len(); - let mut exists = false; - let mut save_diff = true; - let mut existing_index = 0; - for (name, pool) in pool_vec.iter() { - if *name == self.name { - exists = true; - if self.rig == pool.rig && self.ip == pool.ip && self.port == pool.port { - save_diff = false; - } - break - } - existing_index += 1; - } - ui.horizontal(|ui| { - let text = if exists { LIST_SAVE } else { LIST_ADD }; - let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", text, self.selected_index+1, self.selected_name, pool_vec_len); - // If the pool already exists, show [Save] and mutate the already existing pool - if exists { - ui.add_enabled_ui(!incorrect_input && save_diff, |ui|{ - if ui.add_sized([width, text_edit], Button::new("Save")).on_hover_text(text).clicked() { - let pool = Pool { - rig: self.rig.clone(), - ip: self.ip.clone(), - port: self.port.clone(), - }; - pool_vec[existing_index].1 = pool; - self.selected_name.clone_from(&self.name); - self.selected_rig.clone_from(&self.rig); - self.selected_ip.clone_from(&self.ip); - self.selected_port.clone_from(&self.port); - info!("Node | S | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", existing_index+1, self.name, self.ip, self.port, self.rig); - } - }); - // Else, add to the list - } else { - ui.add_enabled_ui(!incorrect_input && pool_vec_len < 1000, |ui|{ - if ui.add_sized([width, text_edit], Button::new("Add")).on_hover_text(text).clicked() { - let pool = Pool { - rig: self.rig.clone(), - ip: self.ip.clone(), - port: self.port.clone(), - }; - pool_vec.push((self.name.clone(), pool)); - self.selected_index = pool_vec_len; - self.selected_name.clone_from(&self.name); - self.selected_rig.clone_from(&self.rig); - self.selected_ip.clone_from(&self.ip); - self.selected_port.clone_from(&self.port); - info!("Node | A | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", pool_vec_len, self.name, self.ip, self.port, self.rig); - } - }); - } - }); - // [Delete] - ui.horizontal(|ui| { - ui.add_enabled_ui(pool_vec_len > 1, |ui|{ - let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", LIST_DELETE, self.selected_index+1, self.selected_name, pool_vec_len); - if ui.add_sized([width, text_edit], Button::new("Delete")).on_hover_text(text).clicked() { - let new_name; - let new_pool; - match self.selected_index { - 0 => { - new_name = pool_vec[1].0.clone(); - new_pool = pool_vec[1].1.clone(); - pool_vec.remove(0); - } - _ => { - pool_vec.remove(self.selected_index); - self.selected_index -= 1; - new_name = pool_vec[self.selected_index].0.clone(); - new_pool = pool_vec[self.selected_index].1.clone(); - } - }; - self.selected_name.clone_from(&new_name); - self.selected_rig.clone_from(&new_pool.rig); - self.selected_ip.clone_from(&new_pool.ip); - self.selected_port.clone_from(&new_pool.port); - self.name = new_name; - self.rig = new_pool.rig; - self.ip = new_pool.ip; - self.port = new_pool.port; - info!("Node | D | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig\"{}\"]", self.selected_index, self.selected_name, self.selected_ip, self.selected_port, self.selected_rig); -} - }); - - }); - ui.horizontal(|ui| { - ui.add_enabled_ui(!self.name.is_empty() || !self.ip.is_empty() || !self.port.is_empty(), |ui|{ - if ui.add_sized([width, text_edit], Button::new("Clear")).on_hover_text(LIST_CLEAR).clicked() { - self.name.clear(); - self.rig.clear(); - self.ip.clear(); - self.port.clear(); - } - }); - }); - }); - }); - }); - ui.add_space(5.0); - - debug!("XMRig Tab | Rendering [API] TextEdits"); - // [HTTP API IP/Port] - ui.group(|ui| { - ui.horizontal(|ui| { - ui.vertical(|ui| { - let width = width / 10.0; - ui.spacing_mut().text_edit_width = width * 2.39; - // HTTP API - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:03}", self.api_ip.len()); - if self.api_ip.is_empty() { - text = format!("HTTP API IP [{}/255]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if self.api_ip == "localhost" - || REGEXES.ipv4.is_match(&self.api_ip) - || REGEXES.domain.is_match(&self.api_ip) - { - text = format!("HTTP API IP [{}/255]✔", len); - color = GREEN; - } else { - text = format!("HTTP API IP [{}/255]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized( - [width, text_edit], - Label::new(RichText::new(text).color(color)), - ); - ui.text_edit_singleline(&mut self.api_ip) - .on_hover_text(XMRIG_API_IP); - self.api_ip.truncate(255); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = self.api_port.len(); - if self.api_port.is_empty() { - text = format!("HTTP API Port [ {}/5 ]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.port.is_match(&self.api_port) { - text = format!("HTTP API Port [ {}/5 ]✔", len); - color = GREEN; - } else { - text = format!("HTTP API Port [ {}/5 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized( - [width, text_edit], - Label::new(RichText::new(text).color(color)), - ); - ui.text_edit_singleline(&mut self.api_port) - .on_hover_text(XMRIG_API_PORT); - self.api_port.truncate(5); - }); - }); - + let text = &api.lock().unwrap().output; + console(ui, text); + if !self.simple { ui.separator(); - - debug!("XMRig Tab | Rendering [TLS/Keepalive] buttons"); - ui.vertical(|ui| { - // TLS/Keepalive - ui.horizontal(|ui| { - let width = (ui.available_width() / 2.0) - 11.0; - let height = text_edit * 2.0; - let size = vec2(width, height); - // let mut style = (*ctx.style()).clone(); - // style.spacing.icon_width_inner = width / 8.0; - // style.spacing.icon_width = width / 6.0; - // style.spacing.icon_spacing = 20.0; - // ctx.set_style(style); - ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection")) - .on_hover_text(XMRIG_TLS); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive")) - .on_hover_text(XMRIG_KEEPALIVE); + input_args_field( + ui, + buffer, + process, + r#"Commands: [h]ashrate, [p]ause, [r]esume, re[s]ults, [c]onnection"#, + XMRIG_INPUT, + ); + } + }); + if !self.simple { + debug!("XMRig Tab | Rendering [Arguments]"); + ui.horizontal(|ui| { + start_options_field( + ui, + &mut self.arguments, + r#"--url <...> --user <...> --config <...>"#, + XMRIG_ARGUMENTS, + ); + }); + ui.add_enabled_ui(self.arguments.is_empty(), |ui| { + debug!("XMRig Tab | Rendering [Address]"); + monero_address_field(&mut self.address, ui, XMRIG_ADDRESS); + }); + } + if self.simple { + ui.add_space(SPACE); + } + debug!("XMRig Tab | Rendering [Threads]"); + ui.vertical_centered(|ui| { + ui.set_max_width(ui.available_width() * 0.75); + slider_state_field( + ui, + &format!("Threads [1-{}]:", self.max_threads), + XMRIG_THREADS, + &mut self.current_threads, + 1..=self.max_threads, + ); + #[cfg(not(target_os = "linux"))] // Pause on active isn't supported on Linux + slider_state_field( + ui, + "Pause on active [0-255]:", + &format!("{} [{}] seconds.", XMRIG_PAUSE, self.pause), + &mut self.pause, + 0..=255, + ); + }); + if !self.simple { + debug!("XMRig Tab | Rendering [Pool List] elements"); + let mut incorrect_input = false; // This will disable [Add/Delete] on bad input + ui.horizontal(|ui| { + ui.group(|ui| { + ui.vertical(|ui| { + if !self.name_field(ui) { + incorrect_input = false; + } + if !self.ip_field(ui) { + incorrect_input = false; + } + if !self.rpc_port_field(ui) { + incorrect_input = false; + } + if !self.rig_field(ui) { + incorrect_input = false; + } + }); + ui.vertical(|ui| { + list_poolnode( + ui, + &mut (&mut self.name, &mut self.ip, &mut self.port, &mut self.rig), + &mut self.selected_pool, + pool_vec, + incorrect_input, + ); }); }); }); - }); - } - }); + ui.add_space(5.0); + debug!("XMRig Tab | Rendering [API] TextEdits"); + // [HTTP API IP/Port] + ui.group(|ui| { + ui.horizontal(|ui| { + ui.vertical(|ui| { + self.api_ip_field(ui); + self.api_port_field(ui); + }); + ui.separator(); + debug!("XMRig Tab | Rendering [TLS/Keepalive] buttons"); + ui.vertical(|ui| { + // TLS/Keepalive + ui.horizontal(|ui| { + let width = (ui.available_width() / 2.0) - 11.0; + let height = + height_txt_before_button(ui, &egui::TextStyle::Button) * 2.0; + let size = vec2(width, height); + ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection")) + .on_hover_text(XMRIG_TLS); + ui.separator(); + ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive")) + .on_hover_text(XMRIG_KEEPALIVE); + }); + }); + }); + }); + } + }); + } + fn name_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" Name ") + .max_ch(30) + .help_msg(XMRIG_NAME) + .validations(&[|x| REGEXES.name.is_match(x)]) + .build(ui, &mut self.name) + } + fn rpc_port_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" RPC PORT ") + .max_ch(5) + .help_msg(XMRIG_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, &mut self.port) + } + fn ip_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" IP ") + .max_ch(255) + .help_msg(XMRIG_IP) + .validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)]) + .build(ui, &mut self.ip) + } + fn rig_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" Name ") + .max_ch(30) + .help_msg(XMRIG_RIG) + .build(ui, &mut self.rig) + } + fn api_ip_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" API IP ") + .max_ch(255) + .help_msg(XMRIG_API_IP) + .validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)]) + .build(ui, &mut self.api_ip) + } + fn api_port_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" API PORT ") + .max_ch(5) + .help_msg(XMRIG_API_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, &mut self.api_port) } } diff --git a/src/app/panels/middle/xmrig_proxy.rs b/src/app/panels/middle/xmrig_proxy.rs index 1f9dfdd..aaa361c 100644 --- a/src/app/panels/middle/xmrig_proxy.rs +++ b/src/app/panels/middle/xmrig_proxy.rs @@ -1,439 +1,201 @@ -use egui::{ - Button, Checkbox, ComboBox, Label, RichText, SelectableLabel, TextEdit, TextStyle, Vec2, vec2, -}; +use egui::{Checkbox, Label, TextStyle, Ui, vec2}; use std::sync::{Arc, Mutex}; -use log::{debug, info}; +use log::debug; -use crate::disk::pool::Pool; +use crate::app::panels::middle::common::console::{console, input_args_field, start_options_field}; +use crate::app::panels::middle::common::list_poolnode::list_poolnode; use crate::disk::state::XmrigProxy; use crate::helper::Process; use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi; -use crate::regex::{REGEXES, num_lines}; -use crate::utils::constants::DARK_GRAY; +use crate::miscs::height_txt_before_button; +use crate::regex::REGEXES; use crate::{ - GREEN, LIGHT_GRAY, LIST_ADD, LIST_CLEAR, LIST_DELETE, LIST_SAVE, RED, SPACE, XMRIG_API_IP, - XMRIG_API_PORT, XMRIG_IP, XMRIG_KEEPALIVE, XMRIG_NAME, XMRIG_PORT, XMRIG_PROXY_ARGUMENTS, - XMRIG_PROXY_INPUT, XMRIG_PROXY_REDIRECT, XMRIG_PROXY_URL, XMRIG_RIG, XMRIG_TLS, + SPACE, XMRIG_API_IP, XMRIG_API_PORT, XMRIG_IP, XMRIG_KEEPALIVE, XMRIG_NAME, XMRIG_PORT, + XMRIG_PROXY_ARGUMENTS, XMRIG_PROXY_INPUT, XMRIG_PROXY_REDIRECT, XMRIG_PROXY_URL, XMRIG_RIG, + XMRIG_TLS, }; +use super::common::list_poolnode::PoolNode; +use super::common::state_edit_field::StateTextEdit; + impl XmrigProxy { #[inline(always)] // called once pub fn show( &mut self, process: &Arc>, - pool_vec: &mut Vec<(String, Pool)>, + pool_vec: &mut Vec<(String, PoolNode)>, api: &Arc>, buffer: &mut String, - size: Vec2, ui: &mut egui::Ui, ) { - let width = size.x; - let height = size.y; - let space_h = height / 48.0; - let text_edit = size.y / 25.0; - egui::ScrollArea::vertical().show(ui, |ui| { ui.vertical_centered(|ui| { - ui.add_space(space_h); + ui.add_space(SPACE); ui.style_mut().override_text_style = Some(TextStyle::Heading); ui.hyperlink_to("XMRig-Proxy", XMRIG_PROXY_URL); ui.style_mut().override_text_style = Some(TextStyle::Body); ui.add(Label::new("High performant proxy for your miners")); - ui.add_space(space_h); + ui.add_space(SPACE); }); // console output for log debug!("Xmrig-Proxy Tab | Rendering [Console]"); - ui.group(|ui| { - let text = &api.lock().unwrap().output; - let nb_lines = num_lines(text); - let height = size.y / 2.8; - let width = size.x - (space_h / 2.0); - egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { - ui.style_mut().override_text_style = Some(egui::TextStyle::Small - ); - egui::ScrollArea::vertical() - .stick_to_bottom(true) - .max_width(width) - .max_height(height) - .auto_shrink([false; 2]) - // .show_viewport(ui, |ui, _| { - .show_rows( - ui, - ui.text_style_height(&TextStyle::Small), - nb_lines, - |ui, row_range| { - for i in row_range { - if let Some(line) = text.lines().nth(i) { - ui.label(line); - } - } - }, - ); - }); - }); - //---------------------------------------------------------------------------------------------------- [Advanced] Console - if !self.simple { - ui.separator(); - let response = ui - .add_sized( - [width, text_edit], - TextEdit::hint_text( - TextEdit::singleline(buffer), - r#"Commands: [h]ashrate, [c]onnections, [v]erbose, [w]orkers"#, - ), - ) - .on_hover_text(XMRIG_PROXY_INPUT); - // If the user pressed enter, dump buffer contents into the process STDIN - if response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) { - response.request_focus(); // Get focus back - let buffer = std::mem::take(buffer); // Take buffer - let mut process = process.lock().unwrap(); // Lock - if process.is_alive() { - process.input.push(buffer); - } // Push only if alive - } - - //---------------------------------------------------------------------------------------------------- Arguments - debug!("XMRig-Proxy Tab | Rendering [Arguments]"); + egui::ScrollArea::vertical().show(ui, |ui| { ui.group(|ui| { - ui.horizontal(|ui| { - let width = (size.x / 10.0) - SPACE; - ui.add_sized([width, text_edit], Label::new("Command arguments:")); - ui.add_sized( - [ui.available_width(), text_edit], - TextEdit::hint_text( - TextEdit::singleline(&mut self.arguments), - r#"--url <...> --user <...> --config <...>"#, - ), - ) - .on_hover_text(XMRIG_PROXY_ARGUMENTS); - self.arguments.truncate(1024); - }) - }); - if !self.arguments.is_empty() { - ui.disable(); - } - ui.add_space(space_h); - ui.style_mut().spacing.icon_width_inner = width / 45.0; - ui.style_mut().spacing.icon_width = width / 35.0; - ui.style_mut().spacing.icon_spacing = space_h; - ui.checkbox( - &mut self.redirect_local_xmrig, - "Auto Redirect local Xmrig to Xmrig-Proxy", - ) - .on_hover_text(XMRIG_PROXY_REDIRECT); - - // idea - // need to warn the user if local firewall is blocking port - // need to warn the user if NAT is blocking port - // need to show local ip address - // need to show public ip - - debug!("XMRig-Proxy Tab | Rendering [Pool List] elements"); - let width = ui.available_width() - 10.0; - let mut incorrect_input = false; // This will disable [Add/Delete] on bad input - // [Pool IP/Port] - ui.horizontal(|ui| { - ui.group(|ui| { - let width = width/10.0; - ui.vertical(|ui| { - ui.spacing_mut().text_edit_width = width*3.32; - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:02}", self.name.len()); - if self.name.is_empty() { - text = format!("Name [ {}/30 ]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.name.is_match(&self.name) { - text = format!("Name [ {}/30 ]✔", len); - color = GREEN; - } else { - text = format!("Name [ {}/30 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.name).on_hover_text(XMRIG_NAME); - self.name.truncate(30); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:03}", self.p2pool_ip.len()); - if self.p2pool_ip.is_empty() { - text = format!(" IP [{}/255]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if self.p2pool_ip == "localhost" || REGEXES.ipv4.is_match(&self.p2pool_ip) || REGEXES.domain.is_match(&self.p2pool_ip) { - text = format!(" IP [{}/255]✔", len); - color = GREEN; - } else { - text = format!(" IP [{}/255]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.p2pool_ip).on_hover_text(XMRIG_IP); - self.p2pool_ip.truncate(255); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = self.p2pool_port.len(); - if self.p2pool_port.is_empty() { - text = format!("Port [ {}/5 ]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.port.is_match(&self.p2pool_port) { - text = format!("Port [ {}/5 ]✔", len); - color = GREEN; - } else { - text = format!("Port [ {}/5 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.p2pool_port).on_hover_text(XMRIG_PORT); - self.p2pool_port.truncate(5); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:02}", self.rig.len()); - if self.rig.is_empty() { - text = format!(" Rig [ {}/30 ]➖", len); - color = LIGHT_GRAY; - } else if REGEXES.name.is_match(&self.rig) { - text = format!(" Rig [ {}/30 ]✔", len); - color = GREEN; - } else { - text = format!(" Rig [ {}/30 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized([width, text_edit], Label::new(RichText::new(text).color(color))); - ui.text_edit_singleline(&mut self.rig).on_hover_text(XMRIG_RIG); - self.rig.truncate(30); - }); - }); - - ui.vertical(|ui| { - let width = ui.available_width(); - ui.add_space(1.0); - // [Manual node selection] - ui.spacing_mut().slider_width = width - 8.0; - ui.spacing_mut().icon_width = width / 25.0; - // [Node List] - debug!("XMRig-Proxy Tab | Rendering [Node List] ComboBox"); - let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name)); - ComboBox::from_id_salt("manual_pool").selected_text(text).width(width).show_ui(ui, |ui| { - for (n, (name, pool)) in pool_vec.iter().enumerate() { - let text = format!("{}. {}\n IP: {}\n Port: {}\n Rig: {}", n+1, name, pool.ip, pool.port, pool.rig); - if ui.add(SelectableLabel::new(self.selected_name == *name, text)).clicked() { - self.selected_index = n; - let pool = pool.clone(); - self.selected_name.clone_from(name); - self.selected_rig.clone_from(&pool.rig); - self.selected_ip.clone_from(&pool.ip); - self.selected_port.clone_from(&pool.port); - self.name.clone_from(name); - self.rig = pool.rig; - self.p2pool_ip = pool.ip; - self.p2pool_port = pool.port; - } - } - }); - // [Add/Save] - let pool_vec_len = pool_vec.len(); - let mut exists = false; - let mut save_diff = true; - let mut existing_index = 0; - for (name, pool) in pool_vec.iter() { - if *name == self.name { - exists = true; - if self.rig == pool.rig && self.p2pool_ip == pool.ip && self.p2pool_port == pool.port { - save_diff = false; - } - break - } - existing_index += 1; - } - ui.horizontal(|ui| { - let text = if exists { LIST_SAVE } else { LIST_ADD }; - let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", text, self.selected_index+1, self.selected_name, pool_vec_len); - // If the pool already exists, show [Save] and mutate the already existing pool - if exists { - ui.add_enabled_ui(!incorrect_input && save_diff, |ui|{ - if ui.add_sized([width, text_edit], Button::new("Save")).on_hover_text(text).clicked() { - let pool = Pool { - rig: self.rig.clone(), - ip: self.p2pool_ip.clone(), - port: self.p2pool_port.clone(), - }; - pool_vec[existing_index].1 = pool; - self.selected_name.clone_from(&self.name); - self.selected_rig.clone_from(&self.rig); - self.selected_ip.clone_from(&self.p2pool_ip); - self.selected_port.clone_from(&self.p2pool_port); - info!("Node | S | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", existing_index+1, self.name, self.p2pool_ip, self.p2pool_port, self.rig); - } - - }); - // Else, add to the list - } else { - ui.add_enabled_ui(!incorrect_input && pool_vec_len < 1000, |ui|{ - if ui.add_sized([width, text_edit], Button::new("Add")).on_hover_text(text).clicked() { - let pool = Pool { - rig: self.rig.clone(), - ip: self.p2pool_ip.clone(), - port: self.p2pool_port.clone(), - }; - pool_vec.push((self.name.clone(), pool)); - self.selected_index = pool_vec_len; - self.selected_name.clone_from(&self.name); - self.selected_rig.clone_from(&self.rig); - self.selected_ip.clone_from(&self.p2pool_ip); - self.selected_port.clone_from(&self.p2pool_port); - info!("Node | A | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig: \"{}\"]", pool_vec_len, self.name, self.p2pool_ip, self.p2pool_port, self.rig); - } - - }); - } - }); - // [Delete] - ui.horizontal(|ui| { - ui.add_enabled_ui(pool_vec_len > 1, |ui|{ - let text = format!("{}\n Currently selected pool: {}. {}\n Current amount of pools: {}/1000", LIST_DELETE, self.selected_index+1, self.selected_name, pool_vec_len); - if ui.add_sized([width, text_edit], Button::new("Delete")).on_hover_text(text).clicked() { - let new_name; - let new_pool; - match self.selected_index { - 0 => { - new_name = pool_vec[1].0.clone(); - new_pool = pool_vec[1].1.clone(); - pool_vec.remove(0); - } - _ => { - pool_vec.remove(self.selected_index); - self.selected_index -= 1; - new_name = pool_vec[self.selected_index].0.clone(); - new_pool = pool_vec[self.selected_index].1.clone(); - } - }; - self.selected_name.clone_from(&new_name); - self.selected_rig.clone_from(&new_pool.rig); - self.selected_ip.clone_from(&new_pool.ip); - self.selected_port.clone_from(&new_pool.port); - self.name = new_name; - self.rig = new_pool.rig; - self.p2pool_ip = new_pool.ip; - self.p2pool_port = new_pool.port; - info!("Node | D | [index: {}, name: \"{}\", ip: \"{}\", port: {}, rig\"{}\"]", self.selected_index, self.selected_name, self.selected_ip, self.selected_port, self.selected_rig); - } - }); - }); - ui.horizontal(|ui| { - ui.add_enabled_ui(!self.name.is_empty() || !self.p2pool_ip.is_empty() || !self.p2pool_port.is_empty(), |ui|{ - if ui.add_sized([width, text_edit], Button::new("Clear")).on_hover_text(LIST_CLEAR).clicked() { - self.name.clear(); - self.rig.clear(); - self.p2pool_ip.clear(); - self.p2pool_port.clear(); - } - }); - }); - }); - }); - }); - ui.add_space(5.0); - - debug!("XMRig-Proxy Tab | Rendering [API] TextEdits"); - // [HTTP API IP/Port] - ui.group(|ui| { - ui.horizontal(|ui| { - ui.vertical(|ui| { - let width = width / 10.0; - ui.spacing_mut().text_edit_width = width * 2.39; - // HTTP API - ui.horizontal(|ui| { - let text; - let color; - let len = format!("{:03}", self.api_ip.len()); - if self.api_ip.is_empty() { - text = format!("HTTP API IP [{}/255]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if self.api_ip == "localhost" - || REGEXES.ipv4.is_match(&self.api_ip) - || REGEXES.domain.is_match(&self.api_ip) - { - text = format!("HTTP API IP [{}/255]✔", len); - color = GREEN; - } else { - text = format!("HTTP API IP [{}/255]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized( - [width, text_edit], - Label::new(RichText::new(text).color(color)), - ); - ui.text_edit_singleline(&mut self.api_ip) - .on_hover_text(XMRIG_API_IP); - self.api_ip.truncate(255); - }); - ui.horizontal(|ui| { - let text; - let color; - let len = self.api_port.len(); - if self.api_port.is_empty() { - text = format!("HTTP API Port [ {}/5 ]➖", len); - color = LIGHT_GRAY; - incorrect_input = true; - } else if REGEXES.port.is_match(&self.api_port) { - text = format!("HTTP API Port [ {}/5 ]✔", len); - color = GREEN; - } else { - text = format!("HTTP API Port [ {}/5 ]❌", len); - color = RED; - incorrect_input = true; - } - ui.add_sized( - [width, text_edit], - Label::new(RichText::new(text).color(color)), - ); - ui.text_edit_singleline(&mut self.api_port) - .on_hover_text(XMRIG_API_PORT); - self.api_port.truncate(5); - }); - }); - + let text = &api.lock().unwrap().output; + console(ui, text); + //---------------------------------------------------------------------------------------------------- [Advanced] Console + if !self.simple { ui.separator(); + input_args_field( + ui, + buffer, + process, + r#"Commands: [h]ashrate, [c]onnections, [v]erbose, [w]orkers"#, + XMRIG_PROXY_INPUT, + ); + } + }); + if !self.simple { + //---------------------------------------------------------------------------------------------------- Arguments + debug!("XMRig-Proxy Tab | Rendering [Arguments]"); + ui.horizontal(|ui| { + start_options_field( + ui, + &mut self.arguments, + r#"--url <...> --user <...> --config <...>"#, + XMRIG_PROXY_ARGUMENTS, + ); + }); + if !self.arguments.is_empty() { + ui.disable(); + } + ui.add_space(SPACE); + // ui.style_mut().spacing.icon_width_inner = width / 45.0; + // ui.style_mut().spacing.icon_width = width / 35.0; + // ui.style_mut().spacing.icon_spacing = space_h; + ui.checkbox( + &mut self.redirect_local_xmrig, + "Auto Redirect local Xmrig to Xmrig-Proxy", + ) + .on_hover_text(XMRIG_PROXY_REDIRECT); - debug!("XMRig-Proxy Tab | Rendering [TLS/Keepalive] buttons"); - ui.vertical(|ui| { - // TLS/Keepalive - ui.horizontal(|ui| { - let width = (ui.available_width() / 2.0) - 11.0; - let height = text_edit * 2.0; - let size = vec2(width, height); - // let mut style = (*ctx.style()).clone(); - // style.spacing.icon_width_inner = width / 8.0; - // style.spacing.icon_width = width / 6.0; - // style.spacing.icon_spacing = 20.0; - // ctx.set_style(style); - ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection")) - .on_hover_text(XMRIG_TLS); - ui.separator(); - ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive")) - .on_hover_text(XMRIG_KEEPALIVE); + // idea + // need to warn the user if local firewall is blocking port + // need to warn the user if NAT is blocking port + // need to show local ip address + // need to show public ip + + debug!("XMRig-Proxy Tab | Rendering [Pool List] elements"); + // let width = ui.available_width() - 10.0; + let mut incorrect_input = false; // This will disable [Add/Delete] on bad input + // [Pool IP/Port] + ui.horizontal(|ui| { + ui.group(|ui| { + // let width = width / 10.0; + ui.vertical(|ui| { + if !self.name_field(ui) { + incorrect_input = false; + } + if !self.ip_field(ui) { + incorrect_input = false; + } + if !self.rpc_port_field(ui) { + incorrect_input = false; + } + if !self.rig_field(ui) { + incorrect_input = false; + } + }); + + ui.vertical(|ui| { + list_poolnode( + ui, + &mut (&mut self.name, &mut self.ip, &mut self.port, &mut self.rig), + &mut self.selected_pool, + pool_vec, + incorrect_input, + ); }); }); }); - }); - } - }); + ui.add_space(5.0); + + debug!("XMRig-Proxy Tab | Rendering [API] TextEdits"); + // [HTTP API IP/Port] + ui.group(|ui| { + ui.horizontal(|ui| { + ui.vertical(|ui| { + // HTTP API + self.api_ip_field(ui); + self.api_port_field(ui); + }); + + ui.separator(); + + debug!("XMRig-Proxy Tab | Rendering [TLS/Keepalive] buttons"); + ui.vertical(|ui| { + // TLS/Keepalive + ui.horizontal(|ui| { + let width = (ui.available_width() / 2.0) - 11.0; + let height = height_txt_before_button(ui, &TextStyle::Button) * 2.0; + let size = vec2(width, height); + ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection")) + .on_hover_text(XMRIG_TLS); + ui.separator(); + ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive")) + .on_hover_text(XMRIG_KEEPALIVE); + }); + }); + }); + }); + } + }); + } + fn name_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" Name ") + .max_ch(30) + .help_msg(XMRIG_NAME) + .validations(&[|x| REGEXES.name.is_match(x)]) + .build(ui, &mut self.name) + } + fn rpc_port_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" RPC PORT ") + .max_ch(5) + .help_msg(XMRIG_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, &mut self.port) + } + fn ip_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" IP ") + .max_ch(255) + .help_msg(XMRIG_IP) + .validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)]) + .build(ui, &mut self.ip) + } + fn rig_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" Name ") + .max_ch(30) + .help_msg(XMRIG_RIG) + .build(ui, &mut self.rig) + } + fn api_ip_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" API IP ") + .max_ch(255) + .help_msg(XMRIG_API_IP) + .validations(&[|x| REGEXES.ipv4.is_match(x) || REGEXES.domain.is_match(x)]) + .build(ui, &mut self.api_ip) + } + fn api_port_field(&mut self, ui: &mut Ui) -> bool { + StateTextEdit::new(ui) + .description(" API PORT ") + .max_ch(5) + .help_msg(XMRIG_API_PORT) + .validations(&[|x| REGEXES.port.is_match(x)]) + .build(ui, &mut self.api_port) } } diff --git a/src/app/panels/middle/xvb.rs b/src/app/panels/middle/xvb.rs index 5d42e58..e35bbfd 100644 --- a/src/app/panels/middle/xvb.rs +++ b/src/app/panels/middle/xvb.rs @@ -1,30 +1,33 @@ use std::sync::{Arc, Mutex}; -use egui::{Image, RichText, TextEdit, TextStyle, Ui, Vec2, vec2}; +use egui::{Align, Image, RichText, ScrollArea, TextStyle, Ui}; use log::debug; use readable::num::Float; use readable::up::Uptime; +use strum::EnumCount; use crate::XVB_MINING_ON_FIELD; +use crate::app::panels::middle::common::console::console; +use crate::app::panels::middle::common::header_tab::header_tab; +use crate::app::panels::middle::common::state_edit_field::StateTextEdit; use crate::disk::state::{ManualDonationLevel, ManualDonationMetric, XvbMode}; use crate::helper::xrig::xmrig::PubXmrigApi; use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi; use crate::helper::xvb::PubXvbApi; use crate::helper::xvb::priv_stats::RuntimeMode; -use crate::regex::num_lines; +use crate::miscs::height_txt_before_button; use crate::utils::constants::{ - GREEN, LIGHT_GRAY, ORANGE, RED, XVB_DONATED_1H_FIELD, XVB_DONATED_24H_FIELD, - XVB_DONATION_LEVEL_DONOR_HELP, XVB_DONATION_LEVEL_MEGA_DONOR_HELP, - XVB_DONATION_LEVEL_VIP_DONOR_HELP, XVB_DONATION_LEVEL_WHALE_DONOR_HELP, XVB_FAILURE_FIELD, - XVB_HELP, XVB_HERO_SELECT, XVB_MANUAL_SLIDER_MANUAL_P2POOL_HELP, - XVB_MANUAL_SLIDER_MANUAL_XVB_HELP, XVB_MODE_MANUAL_DONATION_LEVEL_HELP, - XVB_MODE_MANUAL_P2POOL_HELP, XVB_MODE_MANUAL_XVB_HELP, XVB_ROUND_TYPE_FIELD, XVB_TOKEN_FIELD, - XVB_TOKEN_LEN, XVB_URL_RULES, XVB_WINNER_FIELD, + ORANGE, XVB_DONATED_1H_FIELD, XVB_DONATED_24H_FIELD, XVB_DONATION_LEVEL_DONOR_HELP, + XVB_DONATION_LEVEL_MEGA_DONOR_HELP, XVB_DONATION_LEVEL_VIP_DONOR_HELP, + XVB_DONATION_LEVEL_WHALE_DONOR_HELP, XVB_FAILURE_FIELD, XVB_HELP, XVB_HERO_SELECT, + XVB_MANUAL_SLIDER_MANUAL_P2POOL_HELP, XVB_MANUAL_SLIDER_MANUAL_XVB_HELP, + XVB_MODE_MANUAL_DONATION_LEVEL_HELP, XVB_MODE_MANUAL_P2POOL_HELP, XVB_MODE_MANUAL_XVB_HELP, + XVB_ROUND_TYPE_FIELD, XVB_TOKEN_LEN, XVB_URL_RULES, XVB_WINNER_FIELD, }; use crate::utils::regex::Regexes; use crate::{ constants::{BYTES_XVB, SPACE}, - utils::constants::{DARK_GRAY, XVB_URL}, + utils::constants::XVB_URL, }; impl crate::disk::state::Xvb { @@ -32,7 +35,6 @@ impl crate::disk::state::Xvb { #[allow(clippy::too_many_arguments)] pub fn show( &mut self, - size: Vec2, address: &str, _ctx: &egui::Context, ui: &mut egui::Ui, @@ -41,96 +43,47 @@ impl crate::disk::state::Xvb { gui_api_xp: &Arc>, is_alive: bool, ) { + // let text_edit = ui.available_height() / 25.0; + // let website_height = ui.available_height() / 10.0; + + // logo and website link + let logo = Some(Image::from_bytes("bytes:/xvb.png", BYTES_XVB)); + header_tab( + ui, + logo, + &[ + ("XMRvsBEAST", XVB_URL, ""), + ( + "Rules", + XVB_URL_RULES, + "Click here to read the rules and understand how the raffle works.", + ), + ("FAQ", "https://xmrvsbeast.com/p2pool/faq.html", ""), + ], + None, + true, + ); egui::ScrollArea::vertical().show(ui, |ui| { - let text_edit = size.y / 25.0; - let website_height = size.y / 10.0; - let width = size.x; - let height = size.y; - let space_h = height / 48.0; - - // logo and website link - ui.vertical_centered(|ui| { - ui.add_sized( - [width, website_height], - Image::from_bytes("bytes:/xvb.png", BYTES_XVB), - ); - ui.style_mut().override_text_style = Some(TextStyle::Heading); - ui.add_space(space_h); - ui.hyperlink_to("XMRvsBeast", XVB_URL); - ui.add_space(space_h); - }); - // console output for log debug!("XvB Tab | Rendering [Console]"); ui.group(|ui| { let text = &api.lock().unwrap().output; - let nb_lines = num_lines(text); - let height = size.y / 2.8; - let width = size.x - (space_h / 2.0); - egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { - ui.style_mut().override_text_style = Some(TextStyle::Small); - egui::ScrollArea::vertical() - .stick_to_bottom(true) - .max_width(width) - .max_height(height) - .auto_shrink([false; 2]) - // .show_viewport(ui, |ui, _| { - .show_rows( - ui, - ui.text_style_height(&TextStyle::Small), - nb_lines, - |ui, row_range| { - for i in row_range { - if let Some(line) = text.lines().nth(i) { - ui.label(line); - } - } - }, - ); - }); + // let nb_lines = num_lines(text); + console(ui, text); }); // input token - let len_token = format!("{}", self.token.len()); - let (text, color) = if self.token.is_empty() { - ( - format!("{} [{}/{}] ➖", XVB_TOKEN_FIELD, len_token, XVB_TOKEN_LEN), - LIGHT_GRAY, - ) - } else if self.token.parse::().is_ok() && self.token.len() < XVB_TOKEN_LEN { - ( - format!("{} [{}/{}]", XVB_TOKEN_FIELD, len_token, XVB_TOKEN_LEN), - GREEN, - ) - } else if self.token.parse::().is_ok() && self.token.len() == XVB_TOKEN_LEN { - (format!("{} ✔", XVB_TOKEN_FIELD), GREEN) - } else { - ( - format!("{} [{}/{}] ❌", XVB_TOKEN_FIELD, len_token, XVB_TOKEN_LEN), - RED, - ) - }; - ui.add_space(space_h); + ui.add_space(SPACE); ui.horizontal(|ui| { - // hovering text is difficult because egui doesn't hover over inner widget. But on disabled does. - ui.group(|ui| { - ui.colored_label(color, text) - .on_hover_text(XVB_HELP); - ui.add( - TextEdit::singleline(&mut self.token) - .char_limit(9) - .desired_width(ui.text_style_height(&TextStyle::Body) * 9.0) - .vertical_align(egui::Align::Center), - ).on_hover_text(XVB_HELP) + ui.group(|ui|{ + ui.style_mut().override_text_valign = Some(Align::Center); + // ui.set_height(height_txt_before_button(ui, &TextStyle::Body)); + self.field_token(ui); }); - // .on_hover_text(XVB_HELP); - ui.add_space(height / 48.0); - ui.style_mut().spacing.icon_width_inner = width / 45.0; - ui.style_mut().spacing.icon_width = width / 35.0; - ui.style_mut().spacing.icon_spacing = space_h; // --------------------------- XVB Simple ------------------------------------------- if self.simple { + ui.add_space(SPACE); ui.checkbox(&mut self.simple_hero_mode, "Hero Mode").on_hover_text(XVB_HERO_SELECT); // set runtime mode immediately if we are on simple mode. if self.simple_hero_mode { @@ -140,14 +93,19 @@ impl crate::disk::state::Xvb { } } }); - ui.add_space(space_h); + ui.add_space(SPACE); // --------------------------- XVB Advanced ----------------------------------------- if !self.simple { ui.group(|ui| { ui.vertical_centered(|ui| { - ui.horizontal(|ui| { - egui::ComboBox::from_label("") + ui.style_mut().override_text_valign = Some(Align::Center); + ui.set_height(0.0); + // ui.horizontal_centered(|ui| { + ui.set_height(0.0); + let text_height = height_txt_before_button(ui, &TextStyle::Heading) * 1.4; + // let text_height = 0.0; + egui::ComboBox::from_label("").height(XvbMode::COUNT as f32 * (ui.text_style_height(&TextStyle::Button) + (ui.spacing().button_padding.y * 2.0) + ui.spacing().item_spacing.y)) .selected_text(self.mode.to_string()) .show_ui(ui, |ui| { ui.selectable_value(&mut self.mode, XvbMode::Auto, @@ -166,7 +124,7 @@ impl crate::disk::state::Xvb { }); if self.mode == XvbMode::ManualXvb || self.mode == XvbMode::ManualP2pool { - ui.add_space(space_h); + ui.add_space(SPACE); let default_xmrig_hashrate = match self.manual_donation_metric { ManualDonationMetric::Hash => 1_000.0, @@ -203,22 +161,22 @@ impl crate::disk::state::Xvb { ui.horizontal(|ui| { - if ui.add(egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Hash, "Hash")).clicked() { + if ui.add_sized([0.0, text_height],egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Hash, "Hash")).clicked() { self.manual_donation_metric = ManualDonationMetric::Hash; self.manual_slider_amount = self.manual_amount_raw; } - if ui.add(egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Kilo, "Kilo")).clicked() { + if ui.add_sized([0.0, text_height],egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Kilo, "Kilo")).clicked() { self.manual_donation_metric = ManualDonationMetric::Kilo; self.manual_slider_amount = self.manual_amount_raw / 1000.0; }; - if ui.add(egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Mega, "Mega")).clicked() { + if ui.add_sized([0.0, text_height],egui::SelectableLabel::new(self.manual_donation_metric == ManualDonationMetric::Mega, "Mega")).clicked() { self.manual_donation_metric = ManualDonationMetric::Mega; self.manual_slider_amount = self.manual_amount_raw / 1_000_000.0; }; - - ui.spacing_mut().slider_width = width * 0.5; + // less menu, less metrics buttons,less space, less metrics. + ui.spacing_mut().slider_width = ui.available_width() * 0.3; ui.add_sized( - [width, text_edit], + [ui.available_width(), text_height], egui::Slider::new(&mut self.manual_slider_amount, 0.0..=(hashrate_xmrig as f64)) .text(self.manual_donation_metric.to_string()) .max_decimals(3) @@ -228,6 +186,8 @@ impl crate::disk::state::Xvb { } if self.mode == XvbMode::ManualDonationLevel { + ui.add_space(SPACE); + ui.horizontal(|ui| { ui.radio_value(&mut self.manual_donation_level, ManualDonationLevel::Donor, ManualDonationLevel::Donor.to_string()) .on_hover_text(XVB_DONATION_LEVEL_DONOR_HELP); @@ -242,10 +202,12 @@ impl crate::disk::state::Xvb { .on_hover_text(XVB_DONATION_LEVEL_MEGA_DONOR_HELP); api.lock().unwrap().stats_priv.runtime_manual_donation_level = self.manual_donation_level.clone().into(); + }); } + ui.add_space(SPACE); }); }); - }); + // }); // Update manual_amount_raw based on slider match self.manual_donation_metric { @@ -263,18 +225,18 @@ impl crate::disk::state::Xvb { // Set runtime_mode & runtime_manual_amount api.lock().unwrap().stats_priv.runtime_mode = self.mode.clone().into(); api.lock().unwrap().stats_priv.runtime_manual_amount = self.manual_amount_raw; - ui.add_space(space_h); + ui.add_space(SPACE); // allow user to modify the buffer for p2pool // button ui.add_sized( - [width, text_edit], + [ui.available_width() * 0.8, height_txt_before_button(ui, &TextStyle::Button)], egui::Slider::new(&mut self.p2pool_buffer, -100..=100) .text("% P2Pool Buffer" ) ).on_hover_text("Set the % amount of additional HR to send to p2pool. Will reduce (if positive) or augment (if negative) the chances to miss the p2pool window"); } - ui.add_space(space_h); + ui.add_space(SPACE); // need to warn the user if no address is set in p2pool tab if !Regexes::addr_ok(address) { debug!("XvB Tab | Rendering warning text"); @@ -284,100 +246,62 @@ impl crate::disk::state::Xvb { }); } // private stats - ui.add_space(space_h); + ui.add_space(SPACE); // ui.add_enabled_ui(is_alive, |ui| { ui.add_enabled_ui(is_alive, |ui| { let api = &api.lock().unwrap(); let priv_stats = &api.stats_priv; let current_node = &api.current_node; - let width_stat = (ui.available_width() - SPACE * 4.0) / 5.0; - let height_stat = 0.0; - let size_stat = vec2(width_stat, height_stat); - ui.horizontal(|ui| { - let round = match &priv_stats.round_participate { - Some(r) => r.to_string(), - None => "None".to_string(), - }; - ui.add_sized(size_stat, |ui: &mut Ui| { - ui.group(|ui| { - let size_stat = vec2( - ui.available_width(), - 0.0, // + ui.spacing().item_spacing.y, - ); - ui.add_sized(size_stat, |ui: &mut Ui| { - ui.vertical_centered(|ui| { - ui.label(XVB_FAILURE_FIELD); - ui.label(priv_stats.fails.to_string()); - }) - .response - }); - ui.separator(); - ui.add_sized(size_stat, |ui: &mut Ui| { - ui.vertical_centered(|ui| { - ui.label(XVB_DONATED_1H_FIELD); - ui.label( - [ + let style_height = ui.text_style_height(&TextStyle::Body); + ui.spacing_mut().item_spacing = [style_height * 2.0, style_height * 2.0].into(); + + // let width_stat = (ui.available_width() - SPACE * 4.0) / 5.0; + let width_column = ui.text_style_height(&TextStyle::Body) * 16.0; + let height_column = 0.0; + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); + ScrollArea::horizontal().id_salt("horizontal").show(ui, |ui| { + ui.horizontal(|ui| { + // Failures + stat_box(ui, XVB_FAILURE_FIELD, &priv_stats.fails.to_string(), width_column, height_column, style_height); + stat_box(ui, XVB_DONATED_1H_FIELD, + &[ Float::from_3(priv_stats.donor_1hr_avg as f64).to_string(), " kH/s".to_string(), ] - .concat(), - ); - }) - .response - }); - ui.separator(); - ui.add_sized(size_stat, |ui: &mut Ui| { - ui.vertical_centered(|ui| { - ui.label(XVB_DONATED_24H_FIELD); - ui.label( - [ + .concat() + , width_column, height_column, style_height); + stat_box(ui, XVB_DONATED_24H_FIELD, + &[ Float::from_3(priv_stats.donor_24hr_avg as f64).to_string(), " kH/s".to_string(), ] - .concat(), - ); - }) - .response - }); - ui.separator(); + .concat() + , width_column, height_column, style_height); ui.add_enabled_ui(priv_stats.round_participate.is_some(), |ui| { - ui.add_sized(size_stat, |ui: &mut Ui| { - ui.vertical_centered(|ui| { - ui.label(XVB_ROUND_TYPE_FIELD); - ui.label(round); - }) - .response - }) + let round = match &priv_stats.round_participate { + Some(r) => r.to_string(), + None => "None".to_string(), + }; + stat_box(ui, XVB_ROUND_TYPE_FIELD, &round, width_column, height_column, style_height); + }).response .on_disabled_hover_text( "You do not yet have a share in the PPLNS Window.", ); - }); - ui.separator(); - ui.add_sized(size_stat, |ui: &mut Ui| { - ui.vertical_centered(|ui| { - ui.label(XVB_WINNER_FIELD); - ui.label(if priv_stats.win_current { + stat_box(ui, XVB_WINNER_FIELD, +if priv_stats.win_current { "You are Winning the round !" } else { "You are not the winner" - }); - }) - .response - }); - }) - .response - }); + } + , width_column, height_column, style_height); }); - // indicators - ui.horizontal(|ui| { - ui.add_sized(size_stat, |ui: &mut Ui| { + }); + ui.vertical(|ui| { ui.group(|ui| { - let size_stat = vec2( - ui.available_width(), - 0.0, // + ui.spacing().item_spacing.y, - ); - ui.add_sized(size_stat, |ui: &mut Ui| { - ui.vertical_centered(|ui| { + ui.set_width(width_column); + ui.set_height(height_column); + ui.vertical_centered(|ui| { + ui.add_space(SPACE); ui.label(XVB_MINING_ON_FIELD) .on_hover_text_at_pointer(&priv_stats.msg_indicator); ui.label( @@ -389,24 +313,44 @@ impl crate::disk::state::Xvb { ui.label(Uptime::from(priv_stats.time_switch_node).to_string()) .on_hover_text_at_pointer(&priv_stats.msg_indicator) }) - .response - }) - }) + }); + }) .response - .on_disabled_hover_text("Algorithm is not running.") + .on_disabled_hover_text("Algorithm is not running."); + // indicators }) // currently mining on }); - }); - // Rules link help - ui.horizontal_centered(|ui| { - // can't have horizontal and vertical centering work together so fix by this. - ui.add_space((width / 2.0) - (ui.text_style_height(&TextStyle::Heading) * 1.5)); - ui.style_mut().override_text_style = Some(TextStyle::Heading); - ui.hyperlink_to("Rules", XVB_URL_RULES) - .on_hover_text("Click here to read the rules and understand how the raffle works."); - }); - - }); + } + fn field_token(&mut self, ui: &mut Ui) { + StateTextEdit::new(ui) + .help_msg(XVB_HELP) + .max_ch(XVB_TOKEN_LEN as u8) + .text_edit_width_same_as_max_ch(ui) + .description(" Token ") + .validations(&[|x| x.parse::().is_ok() && x.len() == XVB_TOKEN_LEN]) + .build(ui, &mut self.token); } } +fn stat_box( + ui: &mut Ui, + title: &str, + value: &str, + column_width: f32, + column_height: f32, + style_height: f32, +) { + ui.vertical(|ui| { + ui.group(|ui| { + ui.set_width(column_width); + ui.set_height(column_height); + ui.vertical_centered(|ui| { + ui.spacing_mut().item_spacing = [style_height / 2.0, style_height / 2.0].into(); + ui.add_space(SPACE * 3.0); + ui.label(title); + ui.label(value); + ui.add_space(SPACE); + }); + }); + }); +} diff --git a/src/app/panels/quit_error.rs b/src/app/panels/quit_error.rs index 2f28ebb..dde605a 100644 --- a/src/app/panels/quit_error.rs +++ b/src/app/panels/quit_error.rs @@ -189,7 +189,7 @@ impl crate::app::App { .add_sized([width, height / 2.0], Button::new("Quit")) .clicked() { - if self.state.gupax.save_before_quit { + if self.state.gupax.auto.save_before_quit { self.save_before_quit(); } exit(0); diff --git a/src/app/panels/top.rs b/src/app/panels/top.rs index d0bb408..03ffb5c 100644 --- a/src/app/panels/top.rs +++ b/src/app/panels/top.rs @@ -12,8 +12,6 @@ impl crate::app::App { ui.style_mut().spacing.item_spacing.x = 4.0; // spacing of separator, will reduce width size of the button. Low value so that tabs can be selected easily. let spacing_separator = 2.0; - // TODO if screen smaller, go on two lines. - // TODO if screen really to small, go on tab per line. ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| { ui.style_mut().override_text_style = Some(TextStyle::Heading); let height = ui diff --git a/src/app/quit.rs b/src/app/quit.rs index 6bd590d..882999d 100644 --- a/src/app/quit.rs +++ b/src/app/quit.rs @@ -16,11 +16,11 @@ impl App { return None; } info!("quit"); - if self.state.gupax.ask_before_quit { + if self.state.gupax.auto.ask_before_quit { // If we're already on the [ask_before_quit] screen and // the user tried to exit again, exit. if self.error_state.quit_twice { - if self.state.gupax.save_before_quit { + if self.state.gupax.auto.save_before_quit { self.save_before_quit(); } return Some(ViewportCommand::Close); @@ -32,7 +32,7 @@ impl App { Some(ViewportCommand::CancelClose) // Else, just quit. } else { - if self.state.gupax.save_before_quit { + if self.state.gupax.auto.save_before_quit { self.save_before_quit(); } Some(ViewportCommand::Close) diff --git a/src/components/update.rs b/src/components/update.rs index 8502eed..f7fa2fb 100644 --- a/src/components/update.rs +++ b/src/components/update.rs @@ -241,7 +241,7 @@ impl Update { #[cfg(feature = "distro")] return; // verify validity of absolute path for p2pool, xmrig and xmrig-proxy only if we want to update them. - if og.lock().unwrap().gupax.bundled { + if og.lock().unwrap().gupax.auto.bundled { // Check P2Pool path for safety // Attempt relative to absolute path // it's ok if file doesn't exist. User could enable bundled version for the first time. @@ -465,7 +465,7 @@ impl Update { // arch // standalone or bundled // archive extension - let bundle = if og.lock().unwrap().gupax.bundled { + let bundle = if og.lock().unwrap().gupax.auto.bundled { "bundle" } else { "standalone" @@ -577,7 +577,7 @@ impl Update { path.display() ); // if bundled, create directory for p2pool, xmrig and xmrig-proxy if not present - if og.lock().unwrap().gupax.bundled + if og.lock().unwrap().gupax.auto.bundled && (name == P2POOL_BINARY || name == XMRIG_BINARY || name == XMRIG_PROXY_BINARY diff --git a/src/disk/node.rs b/src/disk/node.rs index 8c78407..dc83531 100644 --- a/src/disk/node.rs +++ b/src/disk/node.rs @@ -1,25 +1,25 @@ -use crate::disk::*; +use crate::{app::panels::middle::common::list_poolnode::PoolNode, disk::*}; use serde::{Deserialize, Serialize}; //---------------------------------------------------------------------------------------------------- [Node] Impl impl Node { - pub fn localhost() -> Self { - Self { + pub fn localhost() -> PoolNode { + PoolNode::Node(Self { ip: "localhost".to_string(), rpc: "18081".to_string(), zmq: "18083".to_string(), - } + }) } - pub fn new_vec() -> Vec<(String, Self)> { + pub fn new_vec() -> Vec<(String, PoolNode)> { vec![("Local Monero Node".to_string(), Self::localhost())] } - pub fn new_tuple() -> (String, Self) { + pub fn new_tuple() -> (String, PoolNode) { ("Local Monero Node".to_string(), Self::localhost()) } // Convert [String] to [Node] Vec - pub fn from_str_to_vec(string: &str) -> Result, TomlError> { + pub fn from_str_to_vec(string: &str) -> Result, TomlError> { let nodes: toml::map::Map = match toml::de::from_str(string) { Ok(map) => { info!("Node | Parse ... OK"); @@ -73,20 +73,23 @@ impl Node { } }; let node = Node { ip, rpc, zmq }; - vec.push((key.clone(), node)); + vec.push((key.clone(), PoolNode::Node(node))); } Ok(vec) } // Convert [Vec<(String, Self)>] into [String] // that can be written as a proper TOML file - pub fn to_string(vec: &[(String, Self)]) -> Result { + pub fn to_string(vec: &[(String, PoolNode)]) -> Result { let mut toml = String::new(); for (key, value) in vec.iter() { write!( toml, "[\'{}\']\nip = {:#?}\nrpc = {:#?}\nzmq = {:#?}\n\n", - key, value.ip, value.rpc, value.zmq, + key, + value.ip(), + value.port(), + value.custom(), )?; } Ok(toml) @@ -97,7 +100,7 @@ impl Node { // |_ Create a default file if not found // 2. Deserialize [String] into a proper [Struct] // |_ Attempt to merge if deserialization fails - pub fn get(path: &PathBuf) -> Result, TomlError> { + pub fn get(path: &PathBuf) -> Result, TomlError> { // Read let file = File::Node; let string = match read_to_string(file, path) { @@ -114,7 +117,7 @@ impl Node { // Completely overwrite current [node.toml] // with a new default version, and return [Vec]. - pub fn create_new(path: &PathBuf) -> Result, TomlError> { + pub fn create_new(path: &PathBuf) -> Result, TomlError> { info!("Node | Creating new default..."); let new = Self::new_vec(); let string = Self::to_string(&Self::new_vec())?; @@ -124,7 +127,7 @@ impl Node { } // Save [Node] onto disk file [node.toml] - pub fn save(vec: &[(String, Self)], path: &PathBuf) -> Result<(), TomlError> { + pub fn save(vec: &[(String, PoolNode)], path: &PathBuf) -> Result<(), TomlError> { info!("Node | Saving to disk ... [{}]", path.display()); let string = Self::to_string(vec)?; match fs::write(path, string) { diff --git a/src/disk/pool.rs b/src/disk/pool.rs index a75c586..d3d3383 100644 --- a/src/disk/pool.rs +++ b/src/disk/pool.rs @@ -1,23 +1,25 @@ +use crate::app::panels::middle::common::list_poolnode::PoolNode; + use super::*; //---------------------------------------------------------------------------------------------------- [Pool] impl impl Pool { - pub fn p2pool() -> Self { - Self { + pub fn p2pool() -> PoolNode { + PoolNode::Pool(Self { rig: GUPAX_VERSION_UNDERSCORE.to_string(), ip: "localhost".to_string(), port: "3333".to_string(), - } + }) } - pub fn new_vec() -> Vec<(String, Self)> { + pub fn new_vec() -> Vec<(String, PoolNode)> { vec![("Local P2Pool".to_string(), Self::p2pool())] } - pub fn new_tuple() -> (String, Self) { + pub fn new_tuple() -> (String, PoolNode) { ("Local P2Pool".to_string(), Self::p2pool()) } - pub fn from_str_to_vec(string: &str) -> Result, TomlError> { + pub fn from_str_to_vec(string: &str) -> Result, TomlError> { let pools: toml::map::Map = match toml::de::from_str(string) { Ok(map) => { info!("Pool | Parse ... OK"); @@ -72,24 +74,27 @@ impl Pool { } }; let pool = Pool { rig, ip, port }; - vec.push((key.clone(), pool)); + vec.push((key.clone(), PoolNode::Pool(pool))); } Ok(vec) } - pub fn to_string(vec: &[(String, Self)]) -> Result { + pub fn to_string(vec: &[(String, PoolNode)]) -> Result { let mut toml = String::new(); for (key, value) in vec.iter() { write!( toml, "[\'{}\']\nrig = {:#?}\nip = {:#?}\nport = {:#?}\n\n", - key, value.rig, value.ip, value.port, + key, + value.custom(), + value.ip(), + value.port(), )?; } Ok(toml) } - pub fn get(path: &PathBuf) -> Result, TomlError> { + pub fn get(path: &PathBuf) -> Result, TomlError> { // Read let file = File::Pool; let string = match read_to_string(file, path) { @@ -104,7 +109,7 @@ impl Pool { Self::from_str_to_vec(&string) } - pub fn create_new(path: &PathBuf) -> Result, TomlError> { + pub fn create_new(path: &PathBuf) -> Result, TomlError> { info!("Pool | Creating new default..."); let new = Self::new_vec(); let string = Self::to_string(&Self::new_vec())?; @@ -113,7 +118,7 @@ impl Pool { Ok(new) } - pub fn save(vec: &[(String, Self)], path: &PathBuf) -> Result<(), TomlError> { + pub fn save(vec: &[(String, PoolNode)], path: &PathBuf) -> Result<(), TomlError> { info!("Pool | Saving to disk ... [{}]", path.display()); let string = Self::to_string(vec)?; match fs::write(path, string) { diff --git a/src/disk/state.rs b/src/disk/state.rs index 8437d5e..1fb7023 100644 --- a/src/disk/state.rs +++ b/src/disk/state.rs @@ -1,8 +1,9 @@ use anyhow::Result; use rand::{Rng, distributions::Alphanumeric, thread_rng}; +use strum::{EnumCount, EnumIter}; use super::*; -use crate::{components::node::RemoteNode, disk::status::*}; +use crate::{components::node::RemoteNode, disk::status::*, helper::ProcessName}; //---------------------------------------------------------------------------------------------------- [State] Impl impl Default for State { fn default() -> Self { @@ -12,7 +13,7 @@ impl Default for State { impl State { pub fn new() -> Self { - let max_threads = benri::threads!(); + let max_threads = benri::threads!() as u16; let current_threads = if max_threads == 1 { 1 } else { max_threads / 2 }; Self { status: Status::default(), @@ -132,7 +133,6 @@ impl State { } } } - // Take [String] as input, merge it with whatever the current [default] is, // leaving behind old keys+values and updating [default] with old valid ones. pub fn merge(old: &str) -> Result { @@ -179,14 +179,7 @@ pub struct Status { #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Gupax { pub simple: bool, - pub auto_update: bool, - pub auto_p2pool: bool, - pub auto_node: bool, - pub auto_xmrig: bool, - pub auto_xp: bool, - pub auto_xvb: bool, - pub ask_before_quit: bool, - pub save_before_quit: bool, + pub auto: AutoEnabled, pub p2pool_path: String, pub node_path: String, pub xmrig_path: String, @@ -200,9 +193,94 @@ pub struct Gupax { pub selected_scale: f32, pub tab: Tab, pub ratio: Ratio, - pub bundled: bool, } +#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] +pub struct AutoEnabled { + pub update: bool, + pub bundled: bool, + pub ask_before_quit: bool, + pub save_before_quit: bool, + pub processes: Vec, +} +impl AutoEnabled { + pub fn enable(&mut self, auto: &AutoStart, enable: bool) { + match auto { + AutoStart::Update => self.update = enable, + AutoStart::Bundle => self.bundled = enable, + AutoStart::AskBeforeQuit => self.ask_before_quit = enable, + AutoStart::SaveBeforequit => self.save_before_quit = enable, + AutoStart::Process(p) => { + let processes = &mut self.processes; + if !processes.iter().any(|a| a == p) && enable { + self.processes.push(*p); + } else if let Some(i) = processes.iter().position(|a| a == p) { + if !enable { + processes.remove(i); + } + } + } + } + } + pub fn is_enabled(&self, auto: &AutoStart) -> bool { + match auto { + AutoStart::Update => self.update, + AutoStart::Bundle => self.bundled, + AutoStart::AskBeforeQuit => self.ask_before_quit, + AutoStart::SaveBeforequit => self.save_before_quit, + AutoStart::Process(p) => self.processes.iter().any(|a| a == p), + } + } +} +#[derive(PartialEq, strum::Display, EnumCount, EnumIter)] +pub enum AutoStart { + #[strum(to_string = "Auto-Update")] + Update, + Bundle, + #[strum(to_string = "Confirm quit")] + AskBeforeQuit, + #[strum(to_string = "Save on exit")] + SaveBeforequit, + #[strum(to_string = "Auto-{0}")] + Process(ProcessName), +} +impl AutoStart { + pub const fn help_msg(&self) -> &str { + match self { + AutoStart::Update => GUPAX_AUTO_UPDATE, + AutoStart::Bundle => GUPAX_BUNDLED_UPDATE, + AutoStart::AskBeforeQuit => GUPAX_ASK_BEFORE_QUIT, + AutoStart::SaveBeforequit => GUPAX_SAVE_BEFORE_QUIT, + AutoStart::Process(p) => p.msg_auto_help(), + } + } + // todo: generate as const with all process in middle ? + // Would necessities unstable feature https://github.com/rust-lang/rust/issues/87575 + pub const ALL: &[AutoStart] = &[ + AutoStart::Update, + AutoStart::Bundle, + AutoStart::Process(ProcessName::Node), + AutoStart::Process(ProcessName::P2pool), + AutoStart::Process(ProcessName::Xmrig), + AutoStart::Process(ProcessName::XmrigProxy), + AutoStart::Process(ProcessName::Xvb), + AutoStart::AskBeforeQuit, + AutoStart::SaveBeforequit, + ]; + // non const: + // let mut autos = AutoStart::iter().collect::>(); + // // remove ProcessName default + // autos.remove(AutoStart::COUNT - 1); + // // insert ProcessName before AskBeforeQuit + // let before_quit_index = autos + // .iter() + // .position(|a| *a == AutoStart::AskBeforeQuit) + // .expect("Before quit should be in iter"); + // ProcessName::iter() + // .rev() + // .for_each(|p| autos.insert(before_quit_index, AutoStart::Process(p))); + // autos +} #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] pub struct P2pool { pub simple: bool, @@ -213,7 +291,7 @@ pub struct P2pool { pub backup_host: bool, pub out_peers: u16, pub in_peers: u16, - pub log_level: u8, + pub log_level: u16, pub node: String, pub arguments: String, pub address: String, @@ -221,11 +299,17 @@ pub struct P2pool { pub ip: String, pub rpc: String, pub zmq: String, - pub selected_index: usize, - pub selected_name: String, - pub selected_ip: String, - pub selected_rpc: String, - pub selected_zmq: String, + pub selected_node: SelectedPoolNode, +} + +// compatible for P2Pool and Xmrig/Proxy +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] +pub struct SelectedPoolNode { + pub index: usize, + pub name: String, + pub ip: String, + pub rpc: String, + pub zmq_rig: String, } #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] @@ -235,7 +319,7 @@ pub struct Node { pub api_port: String, pub out_peers: u16, pub in_peers: u16, - pub log_level: u8, + pub log_level: u16, pub arguments: String, pub zmq_ip: String, pub zmq_port: String, @@ -268,13 +352,13 @@ impl Default for Node { #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)] pub struct Xmrig { pub simple: bool, - pub pause: u8, + pub pause: u16, pub simple_rig: String, pub arguments: String, pub tls: bool, pub keepalive: bool, - pub max_threads: usize, - pub current_threads: usize, + pub max_threads: u16, + pub current_threads: u16, pub address: String, pub api_ip: String, pub api_port: String, @@ -282,11 +366,7 @@ pub struct Xmrig { pub rig: String, pub ip: String, pub port: String, - pub selected_index: usize, - pub selected_name: String, - pub selected_rig: String, - pub selected_ip: String, - pub selected_port: String, + pub selected_pool: SelectedPoolNode, pub token: String, } @@ -307,15 +387,41 @@ pub struct XmrigProxy { pub api_port: String, pub p2pool_ip: String, pub p2pool_port: String, - pub selected_index: usize, - pub selected_name: String, - pub selected_rig: String, - pub selected_ip: String, - pub selected_port: String, + pub selected_pool: SelectedPoolNode, pub token: String, pub redirect_local_xmrig: bool, } +impl Gupax { + pub fn path_binary(&mut self, process: &BundledProcess) -> &mut String { + match process { + BundledProcess::Node => &mut self.node_path, + BundledProcess::P2Pool => &mut self.p2pool_path, + BundledProcess::Xmrig => &mut self.xmrig_path, + BundledProcess::XmrigProxy => &mut self.xmrig_proxy_path, + } + } +} + +// do not include process that are from Gupaxx +#[derive(EnumIter)] +pub enum BundledProcess { + Node, + P2Pool, + Xmrig, + XmrigProxy, +} +impl BundledProcess { + pub fn process_name(&self) -> ProcessName { + match self { + BundledProcess::Node => ProcessName::Node, + BundledProcess::P2Pool => ProcessName::P2pool, + BundledProcess::Xmrig => ProcessName::Xmrig, + BundledProcess::XmrigProxy => ProcessName::XmrigProxy, + } + } +} + impl Default for XmrigProxy { fn default() -> Self { XmrigProxy { @@ -335,11 +441,13 @@ impl Default for XmrigProxy { port: "3355".to_string(), p2pool_ip: "localhost".to_string(), p2pool_port: "3333".to_string(), - selected_index: 0, - selected_name: "Local P2Pool".to_string(), - selected_ip: "localhost".to_string(), - selected_rig: GUPAX_VERSION_UNDERSCORE.to_string(), - selected_port: "3333".to_string(), + selected_pool: SelectedPoolNode { + index: 0, + name: "Local P2Pool".to_string(), + ip: "localhost".to_string(), + rpc: "3333".to_string(), + zmq_rig: GUPAX_VERSION_UNDERSCORE.to_string(), + }, api_ip: "localhost".to_string(), api_port: "18089".to_string(), tls: false, @@ -361,7 +469,7 @@ pub struct Xvb { pub p2pool_buffer: i8, } -#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize, Default)] +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize, Default, EnumCount, EnumIter)] pub enum XvbMode { #[default] Auto, @@ -435,6 +543,20 @@ pub struct Version { } //---------------------------------------------------------------------------------------------------- [State] Defaults +impl Default for AutoEnabled { + fn default() -> Self { + Self { + update: false, + #[cfg(feature = "bundle")] + bundled: true, + #[cfg(not(feature = "bundle"))] + bundled: false, + ask_before_quit: true, + save_before_quit: true, + processes: Vec::new(), + } + } +} impl Default for Status { fn default() -> Self { Self { @@ -452,14 +574,7 @@ impl Default for Gupax { fn default() -> Self { Self { simple: true, - auto_update: false, - auto_p2pool: false, - auto_node: false, - auto_xmrig: false, - auto_xp: false, - auto_xvb: false, - ask_before_quit: true, - save_before_quit: true, + auto: AutoEnabled::default(), p2pool_path: DEFAULT_P2POOL_PATH.to_string(), xmrig_path: DEFAULT_XMRIG_PATH.to_string(), node_path: DEFAULT_NODE_PATH.to_string(), @@ -473,10 +588,6 @@ impl Default for Gupax { selected_scale: APP_DEFAULT_SCALE, ratio: Ratio::Width, tab: Tab::Xvb, - #[cfg(feature = "bundle")] - bundled: true, - #[cfg(not(feature = "bundle"))] - bundled: false, } } } @@ -500,17 +611,19 @@ impl Default for P2pool { ip: "localhost".to_string(), rpc: "18081".to_string(), zmq: "18083".to_string(), - selected_index: 0, - selected_name: "Local Monero Node".to_string(), - selected_ip: "localhost".to_string(), - selected_rpc: "18081".to_string(), - selected_zmq: "18083".to_string(), + selected_node: SelectedPoolNode { + index: 0, + name: "Local Monero Node".to_string(), + ip: "localhost".to_string(), + rpc: "18081".to_string(), + zmq_rig: "18083".to_string(), + }, } } } impl Xmrig { - fn with_threads(max_threads: usize, current_threads: usize) -> Self { + fn with_threads(max_threads: u16, current_threads: u16) -> Self { let xmrig = Self::default(); Self { max_threads, @@ -531,17 +644,19 @@ impl Default for Xmrig { rig: GUPAX_VERSION_UNDERSCORE.to_string(), ip: "localhost".to_string(), port: "3333".to_string(), - selected_index: 0, - selected_name: "Local P2Pool".to_string(), - selected_ip: "localhost".to_string(), - selected_rig: GUPAX_VERSION_UNDERSCORE.to_string(), - selected_port: "3333".to_string(), api_ip: "localhost".to_string(), api_port: "18088".to_string(), tls: false, keepalive: false, current_threads: 1, max_threads: 1, + selected_pool: SelectedPoolNode { + index: 0, + name: "Local Monero Node".to_string(), + ip: "localhost".to_string(), + rpc: "18081".to_string(), + zmq_rig: "18083".to_string(), + }, token: thread_rng() .sample_iter(Alphanumeric) .take(16) diff --git a/src/disk/status.rs b/src/disk/status.rs index bda0ceb..93ac7e4 100644 --- a/src/disk/status.rs +++ b/src/disk/status.rs @@ -1,3 +1,6 @@ +use derive_more::derive::Display; +use strum::{EnumCount, EnumIter}; + use super::*; //---------------------------------------------------------------------------------------------------- [Submenu] enum for [Status] tab #[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)] @@ -25,7 +28,9 @@ impl Display for Submenu { //---------------------------------------------------------------------------------------------------- [PayoutView] enum for [Status/P2Pool] tab // The enum buttons for selecting which "view" to sort the payout log in. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)] +#[derive( + Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize, Display, EnumIter, EnumCount, +)] pub enum PayoutView { Latest, // Shows the most recent logs first Oldest, // Shows the oldest logs first @@ -33,6 +38,17 @@ pub enum PayoutView { Smallest, // Shows lowest to highest payouts } +impl PayoutView { + pub const fn msg_help(&self) -> &str { + match self { + Self::Latest => STATUS_SUBMENU_LATEST, + Self::Oldest => STATUS_SUBMENU_OLDEST, + Self::Biggest => STATUS_SUBMENU_SMALLEST, + Self::Smallest => STATUS_SUBMENU_BIGGEST, + } + } +} + impl PayoutView { fn new() -> Self { Self::Latest @@ -45,12 +61,6 @@ impl Default for PayoutView { } } -impl Display for PayoutView { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{:?}", self) - } -} - //---------------------------------------------------------------------------------------------------- [Hash] enum for [Status/P2Pool] #[derive(Clone, Copy, Eq, PartialEq, Debug, Deserialize, Serialize)] #[allow(clippy::enum_variant_names)] @@ -110,8 +120,10 @@ impl Hash { impl Display for Hash { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - Hash::Hash => write!(f, "Hash"), - _ => write!(f, "{:?}hash", self), + Hash::Hash => write!(f, "H/s"), + Hash::Kilo => write!(f, "KH/s"), + Hash::Mega => write!(f, "MH/s"), + Hash::Giga => write!(f, "GH/s"), } } } diff --git a/src/disk/tests.rs b/src/disk/tests.rs index 4cf91ac..5074ba8 100644 --- a/src/disk/tests.rs +++ b/src/disk/tests.rs @@ -51,6 +51,13 @@ mod test { ratio = "Width" bundled = false + [gupax.auto] + update = false + bundled = false + ask_before_quit = false + save_before_quit = true + processes = [] + [status] submenu = "P2pool" payout_view = "Oldest" @@ -76,11 +83,13 @@ mod test { ip = "192.168.1.123" rpc = "18089" zmq = "18083" - selected_index = 0 - selected_name = "Local Monero Node" - selected_ip = "192.168.1.123" - selected_rpc = "18089" - selected_zmq = "18083" + + [p2pool.selected_node] + index = 0 + name = "Local Monero Node" + ip = "localhost" + rpc = "18081" + zmq_rig = "18083" [xmrig] simple = true @@ -98,13 +107,17 @@ mod test { rig = "Gupaxx" ip = "192.168.1.122" port = "3333" - selected_index = 1 - selected_name = "linux" - selected_rig = "Gupaxx" - selected_ip = "192.168.1.122" - selected_port = "3333" token = "testtoken" + + [xmrig.selected_pool] + index = 0 + name = "Local Monero Node" + ip = "localhost" + rpc = "18081" + zmq_rig = "18083" + + [xmrig_proxy] simple = true arguments = "" @@ -121,13 +134,15 @@ mod test { p2pool_ip = "localhost" p2pool_port = "18088" token = "testtoken" - selected_index = 1 - selected_name = "linux" - selected_rig = "Gupaxx" - selected_ip = "192.168.1.122" - selected_port = "3333" redirect_local_xmrig = true + [xmrig_proxy.selected_pool] + index = 0 + name = "Local Monero Node" + ip = "localhost" + rpc = "18081" + zmq_rig = "18083" + [xvb] simple = true simple_hero_mode = true diff --git a/src/helper/mod.rs b/src/helper/mod.rs index 82d893b..b83eb2c 100644 --- a/src/helper/mod.rs +++ b/src/helper/mod.rs @@ -33,6 +33,7 @@ // This also includes all things related to handling the child processes (P2Pool/XMRig): // piping their stdout/stderr/stdin, accessing their APIs (HTTP + disk files), etc. +use crate::components::gupax::FileType; use crate::components::update::{NODE_BINARY, P2POOL_BINARY, XMRIG_BINARY, XMRIG_PROXY_BINARY}; //---------------------------------------------------------------------------------------------------- Import use crate::helper::xrig::xmrig_proxy::PubXmrigProxyApi; @@ -46,6 +47,7 @@ use log::*; use node::PubNodeApi; use portable_pty::Child; use readable::up::Uptime; +use serde::{Deserialize, Serialize}; use std::fmt::Write; use std::path::Path; use std::{ @@ -54,7 +56,7 @@ use std::{ thread, time::*, }; -use strum::EnumIter; +use strum::{EnumCount, EnumIter}; use self::xvb::{PubXvbApi, nodes::XvbNode}; pub mod node; @@ -250,18 +252,21 @@ impl Default for ProcessSignal { } } -#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, EnumIter)] +#[derive( + Copy, Clone, Eq, PartialEq, Debug, Display, EnumIter, EnumCount, Serialize, Deserialize, Default, +)] pub enum ProcessName { Node, P2pool, Xmrig, #[display("Proxy")] XmrigProxy, + #[default] Xvb, } impl ProcessName { - pub fn binary_name(&self) -> &str { + pub const fn binary_name(&self) -> &str { match self { ProcessName::Node => NODE_BINARY, ProcessName::P2pool => P2POOL_BINARY, @@ -270,6 +275,69 @@ impl ProcessName { ProcessName::Xvb => "", } } + pub const fn msg_binary_path_empty(&self) -> &str { + match self { + ProcessName::Node => NODE_PATH_EMPTY, + ProcessName::P2pool => P2POOL_PATH_EMPTY, + ProcessName::Xmrig => XMRIG_PATH_EMPTY, + ProcessName::XmrigProxy => XMRIG_PROXY_PATH_EMPTY, + ProcessName::Xvb => "", + } + } + pub const fn msg_binary_path_not_file(&self) -> &str { + match self { + ProcessName::Node => NODE_PATH_NOT_FILE, + ProcessName::P2pool => P2POOL_PATH_NOT_FILE, + ProcessName::Xmrig => XMRIG_PATH_NOT_FILE, + ProcessName::XmrigProxy => XMRIG_PROXY_PATH_NOT_FILE, + ProcessName::Xvb => "", + } + } + pub const fn msg_binary_path_invalid(&self) -> &str { + match self { + ProcessName::Node => NODE_PATH_NOT_VALID, + ProcessName::P2pool => P2POOL_PATH_NOT_VALID, + ProcessName::Xmrig => XMRIG_PATH_NOT_VALID, + ProcessName::XmrigProxy => XMRIG_PROXY_PATH_NOT_VALID, + ProcessName::Xvb => "", + } + } + pub const fn msg_binary_path_ok(&self) -> &str { + match self { + ProcessName::Node => NODE_PATH_OK, + ProcessName::P2pool => P2POOL_PATH_OK, + ProcessName::Xmrig => XMRIG_PATH_OK, + ProcessName::XmrigProxy => XMRIG_PROXY_PATH_OK, + ProcessName::Xvb => "", + } + } + pub const fn msg_path_edit(&self) -> &str { + match self { + ProcessName::Node => GUPAX_PATH_NODE, + ProcessName::P2pool => GUPAX_PATH_P2POOL, + ProcessName::Xmrig => GUPAX_PATH_XMRIG, + ProcessName::XmrigProxy => GUPAX_PATH_XMRIG_PROXY, + ProcessName::Xvb => "", + } + } + pub const fn msg_auto_help(&self) -> &str { + match self { + ProcessName::Node => GUPAX_AUTO_NODE, + ProcessName::P2pool => GUPAX_AUTO_P2POOL, + ProcessName::Xmrig => GUPAX_AUTO_XMRIG, + ProcessName::XmrigProxy => GUPAX_AUTO_XMRIG_PROXY, + ProcessName::Xvb => GUPAX_AUTO_XVB, + } + } + pub const fn file_type(&self) -> Option { + match self { + ProcessName::Node => Some(FileType::Node), + ProcessName::P2pool => Some(FileType::P2pool), + ProcessName::Xmrig => Some(FileType::Xmrig), + ProcessName::XmrigProxy => Some(FileType::XmrigProxy), + ProcessName::Xvb => None, + } + } } impl std::fmt::Display for ProcessState { @@ -374,7 +442,7 @@ impl Helper { pub_sys: &mut Sys, pid: &sysinfo::Pid, helper: &Helper, - max_threads: usize, + max_threads: u16, ) { let gupax_uptime = helper.uptime.to_string(); let cpu = &sysinfo.cpus()[0]; @@ -416,7 +484,7 @@ impl Helper { helper: &Arc>, mut sysinfo: sysinfo::System, pid: sysinfo::Pid, - max_threads: usize, + max_threads: u16, ) { // The ordering of these locks is _very_ important. They MUST be in sync with how the main GUI thread locks stuff // or a deadlock will occur given enough time. They will eventually both want to lock the [Arc] the other diff --git a/src/helper/p2pool.rs b/src/helper/p2pool.rs index b927b51..8e0e2cf 100644 --- a/src/helper/p2pool.rs +++ b/src/helper/p2pool.rs @@ -1,5 +1,6 @@ use super::Helper; use super::Process; +use crate::app::panels::middle::common::list_poolnode::PoolNode; use crate::components::node::RemoteNode; use crate::disk::state::P2pool; use crate::helper::ProcessName; @@ -18,7 +19,7 @@ use crate::regex::estimated_hr; use crate::regex::nb_current_shares; use crate::{ constants::*, - disk::{gupax_p2pool_api::GupaxP2poolApi, node::Node}, + disk::gupax_p2pool_api::GupaxP2poolApi, helper::{MONERO_BLOCK_TIME_IN_SECONDS, P2POOL_BLOCK_TIME_IN_SECONDS}, human::*, macros::*, @@ -171,7 +172,7 @@ impl Helper { helper: &Arc>, state: &P2pool, path: &Path, - backup_hosts: Option>, + backup_hosts: Option>, ) { info!("P2Pool | Attempting to restart..."); helper.lock().unwrap().p2pool.lock().unwrap().signal = ProcessSignal::Restart; @@ -200,7 +201,7 @@ impl Helper { helper: &Arc>, state: &P2pool, path: &Path, - backup_hosts: Option>, + backup_hosts: Option>, ) { helper.lock().unwrap().p2pool.lock().unwrap().state = ProcessState::Middle; @@ -253,7 +254,7 @@ impl Helper { helper: &Arc>, state: &P2pool, path: &Path, - backup_hosts: Option>, + backup_hosts: Option>, ) -> (Vec, PathBuf, PathBuf, PathBuf) { let mut args = Vec::with_capacity(500); let path = path.to_path_buf(); @@ -282,13 +283,13 @@ impl Helper { // Push other nodes if `backup_host`. if let Some(nodes) = backup_hosts { for node in nodes { - if (node.ip.as_str(), node.rpc.as_str(), node.zmq.as_str()) != (ip, rpc, zmq) { + if (node.ip(), node.port(), node.custom()) != (ip, rpc, zmq) { args.push("--host".to_string()); - args.push(node.ip.to_string()); + args.push(node.ip().to_string()); args.push("--rpc-port".to_string()); - args.push(node.rpc.to_string()); + args.push(node.port().to_string()); args.push("--zmq-port".to_string()); - args.push(node.zmq.to_string()); + args.push(node.custom().to_string()); } } } @@ -395,20 +396,18 @@ impl Helper { // Push other nodes if `backup_host`. if let Some(nodes) = backup_hosts { for node in nodes { - let ip = if node.ip == "localhost" { + let ip = if node.ip() == "localhost" { "127.0.0.1" } else { - &node.ip + node.ip() }; - if (node.ip.as_str(), node.rpc.as_str(), node.zmq.as_str()) - != (ip, &state.rpc, &state.zmq) - { + if (node.ip(), node.port(), node.custom()) != (ip, &state.rpc, &state.zmq) { args.push("--host".to_string()); - args.push(node.ip.to_string()); + args.push(node.ip().to_string()); args.push("--rpc-port".to_string()); - args.push(node.rpc.to_string()); + args.push(node.port().to_string()); args.push("--zmq-port".to_string()); - args.push(node.zmq.to_string()); + args.push(node.custom().to_string()); } } } @@ -420,9 +419,9 @@ impl Helper { "P2Pool Main".to_string() }, address: Self::head_tail_of_monero_address(&state.address), - host: state.selected_ip.to_string(), - rpc: state.selected_rpc.to_string(), - zmq: state.selected_zmq.to_string(), + host: state.selected_node.ip.to_string(), + rpc: state.selected_node.rpc.to_string(), + zmq: state.selected_node.zmq_rig.to_string(), out_peers: state.out_peers.to_string(), in_peers: state.in_peers.to_string(), }; diff --git a/src/helper/tests.rs b/src/helper/tests.rs index 5c5d99f..28dd82a 100644 --- a/src/helper/tests.rs +++ b/src/helper/tests.rs @@ -298,13 +298,13 @@ Uptime = 0h 2m 4s assert_eq!(p.miners.to_string(), "1,000"); assert_eq!( p.solo_block_mean.to_string(), - "5 months, 21 days, 9 hours, 52 minutes" + "5 months\n21 days\n9 hours\n52 minutes" ); assert_eq!( p.p2pool_block_mean.to_string(), - "3 days, 11 hours, 20 minutes" + "3 days\n11 hours\n20 minutes" ); - assert_eq!(p.p2pool_share_mean.to_string(), "8 minutes, 20 seconds"); + assert_eq!(p.p2pool_share_mean.to_string(), "8 minutes\n20 seconds"); assert_eq!(p.p2pool_percent.to_string(), "0.040000%"); assert_eq!(p.user_p2pool_percent.to_string(), "2.000000%"); assert_eq!(p.user_monero_percent.to_string(), "0.000800%"); diff --git a/src/inits.rs b/src/inits.rs index 3aec985..15c609d 100644 --- a/src/inits.rs +++ b/src/inits.rs @@ -24,6 +24,7 @@ use std::time::Instant; #[cold] #[inline(never)] +// everything is resized from here with the scale. pub fn init_text_styles(ctx: &egui::Context, pixels_per_point: f32) { ctx.all_styles_mut(|style| { style.text_styles = [ @@ -34,13 +35,15 @@ pub fn init_text_styles(ctx: &egui::Context, pixels_per_point: f32) { (Heading, FontId::new(22.0, egui::FontFamily::Monospace)), ] .into(); - style.spacing.icon_width_inner = 32.0; - style.spacing.icon_width = 64.0; - style.spacing.icon_spacing = 20.0; - style.spacing.scroll = egui::style::ScrollStyle { - bar_width: 8.0, - ..egui::style::ScrollStyle::solid() - }; + style.spacing.icon_width_inner = 24.0; + style.spacing.icon_width = 48.0; + style.spacing.icon_spacing = 16.0; + style.spacing.button_padding = [8.0, 8.0].into(); + style.spacing.item_spacing = [8.0, 8.0].into(); + // style.spacing.scroll = egui::style::ScrollStyle { + // bar_width: 8.0, + // ..egui::style::ScrollStyle::solid() + // }; }); // Make sure scale f32 is a regular number. let pixels_per_point = clamp_scale(pixels_per_point); @@ -134,7 +137,7 @@ pub fn init_auto(app: &mut App) { // [Auto-Update] #[cfg(not(feature = "distro"))] - if app.state.gupax.auto_update { + if app.state.gupax.auto.is_enabled(&AutoStart::Update) { Update::spawn_thread( &app.og, &app.state.gupax, @@ -155,7 +158,12 @@ pub fn init_auto(app: &mut App) { } // [Auto-Node] - if app.state.gupax.auto_node { + if app + .state + .gupax + .auto + .is_enabled(&AutoStart::Process(ProcessName::Node)) + { if !Gupax::path_is_file(&app.state.gupax.node_path) { warn!("Gupaxx | Node path is not a file! Skipping auto-node..."); } else if !check_binary_path(&app.state.gupax.node_path, ProcessName::Node) { @@ -177,7 +185,12 @@ pub fn init_auto(app: &mut App) { info!("Skipping auto-node..."); } // [Auto-P2Pool] - if app.state.gupax.auto_p2pool { + if app + .state + .gupax + .auto + .is_enabled(&AutoStart::Process(ProcessName::P2pool)) + { if !Regexes::addr_ok(&app.state.p2pool.address) { warn!("Gupaxx | P2Pool address is not valid! Skipping auto-p2pool..."); } else if !Gupax::path_is_file(&app.state.gupax.p2pool_path) { @@ -202,7 +215,12 @@ pub fn init_auto(app: &mut App) { } // [Auto-XMRig] - if app.state.gupax.auto_xmrig { + if app + .state + .gupax + .auto + .is_enabled(&AutoStart::Process(ProcessName::Xmrig)) + { if !Gupax::path_is_file(&app.state.gupax.xmrig_path) { warn!("Gupaxx | XMRig path is not an executable! Skipping auto-xmrig..."); } else if !check_binary_path(&app.state.gupax.xmrig_path, ProcessName::Xmrig) { @@ -226,7 +244,12 @@ pub fn init_auto(app: &mut App) { info!("Skipping auto-xmrig..."); } // [Auto-XMRig-Proxy] - if app.state.gupax.auto_xp { + if app + .state + .gupax + .auto + .is_enabled(&AutoStart::Process(ProcessName::XmrigProxy)) + { if !Gupax::path_is_file(&app.state.gupax.xmrig_proxy_path) { warn!("Gupaxx | Xmrig-Proxy path is not a file! Skipping auto-xmrig_proxy..."); } else if !check_binary_path(&app.state.gupax.xmrig_proxy_path, ProcessName::XmrigProxy) { @@ -247,7 +270,12 @@ pub fn init_auto(app: &mut App) { info!("Skipping auto-XMRig-Proxy..."); } // [Auto-XvB] - if app.state.gupax.auto_xvb { + if app + .state + .gupax + .auto + .is_enabled(&AutoStart::Process(ProcessName::Xvb)) + { Helper::start_xvb( &app.helper, &app.state.xvb, diff --git a/src/miscs.rs b/src/miscs.rs index 68a7b1d..460cad6 100644 --- a/src/miscs.rs +++ b/src/miscs.rs @@ -132,6 +132,8 @@ pub fn cmp_f64(a: f64, b: f64) -> std::cmp::Ordering { use crate::disk::gupax_p2pool_api::GupaxP2poolApi; use crate::helper::ProcessName; use chrono::Local; +use egui::TextStyle; +use egui::Ui; use log::error; use log::warn; use regex::Regex; @@ -182,3 +184,7 @@ pub fn client() -> ClientWithMiddleware { )) .build() } +/// to get the right height that a text must take before a button to be aligned in the center correctly. +pub fn height_txt_before_button(ui: &Ui, style: &TextStyle) -> f32 { + ui.style().spacing.button_padding.y * 2.0 + ui.text_style_height(style) +} diff --git a/src/utils/constants.rs b/src/utils/constants.rs index d282c52..9558312 100644 --- a/src/utils/constants.rs +++ b/src/utils/constants.rs @@ -348,6 +348,7 @@ pub const GUPAX_TAB_STATUS: &str = "Set the tab Gupaxx starts on to: Status"; pub const GUPAX_TAB_GUPAX: &str = "Set the tab Gupaxx starts on to: Gupaxx"; pub const GUPAX_TAB_P2POOL: &str = "Set the tab Gupaxx starts on to: P2Pool"; pub const GUPAX_TAB_XMRIG: &str = "Set the tab Gupaxx starts on to: XMRig"; +pub const GUPAX_TAB_XMRIG_PROXY: &str = "Set the tab Gupaxx starts on to: Proxy"; pub const GUPAX_TAB_XVB: &str = "Set the tab Gupaxx starts on to: XvB"; pub const GUPAX_TAB_NODE: &str = "Set the default tab Gupaxx starts on to: Node"; @@ -526,7 +527,6 @@ pub const XVB_TIME_ALGO: u32 = 600; pub const XVB_MIN_TIME_SEND: u32 = (XVB_TIME_ALGO as f32 * 0.01) as u32; pub const XVB_TOKEN_LEN: usize = 9; pub const XVB_HERO_SELECT: &str = "Donate as much as possible while keeping a share on p2pool, increases the odds of your round winning\nWhen modified, the algorithm will use the new choice at the next decision."; -pub const XVB_TOKEN_FIELD: &str = "Token"; pub const XVB_FAILURE_FIELD: &str = "Failures"; pub const XVB_DONATED_1H_FIELD: &str = "Donated last hour"; pub const XVB_DONATED_24H_FIELD: &str = "Donated last 24 hours"; diff --git a/src/utils/human.rs b/src/utils/human.rs index c489d12..84e80fc 100644 --- a/src/utils/human.rs +++ b/src/utils/human.rs @@ -59,7 +59,7 @@ impl HumanTime { ) -> std::fmt::Result { if value > 0 { if *started { - f.write_str(", ")?; + f.write_str("\n")?; } write!(f, "{} {}", value, name)?; if value > 1 { @@ -332,64 +332,64 @@ mod test { assert!(HumanTime::into_human(Duration::from_secs(2)).to_string() == "2 seconds"); assert!(HumanTime::into_human(Duration::from_secs(59)).to_string() == "59 seconds"); assert!(HumanTime::into_human(Duration::from_secs(60)).to_string() == "1 minute"); - assert!(HumanTime::into_human(Duration::from_secs(61)).to_string() == "1 minute, 1 second"); + assert!(HumanTime::into_human(Duration::from_secs(61)).to_string() == "1 minute\n1 second"); assert!( - HumanTime::into_human(Duration::from_secs(62)).to_string() == "1 minute, 2 seconds" + HumanTime::into_human(Duration::from_secs(62)).to_string() == "1 minute\n2 seconds" ); assert!(HumanTime::into_human(Duration::from_secs(120)).to_string() == "2 minutes"); assert!( - HumanTime::into_human(Duration::from_secs(121)).to_string() == "2 minutes, 1 second" + HumanTime::into_human(Duration::from_secs(121)).to_string() == "2 minutes\n1 second" ); assert!( - HumanTime::into_human(Duration::from_secs(122)).to_string() == "2 minutes, 2 seconds" + HumanTime::into_human(Duration::from_secs(122)).to_string() == "2 minutes\n2 seconds" ); assert!( - HumanTime::into_human(Duration::from_secs(179)).to_string() == "2 minutes, 59 seconds" + HumanTime::into_human(Duration::from_secs(179)).to_string() == "2 minutes\n59 seconds" ); assert!( HumanTime::into_human(Duration::from_secs(3599)).to_string() - == "59 minutes, 59 seconds" + == "59 minutes\n59 seconds" ); assert!(HumanTime::into_human(Duration::from_secs(3600)).to_string() == "1 hour"); - assert!(HumanTime::into_human(Duration::from_secs(3601)).to_string() == "1 hour, 1 second"); + assert!(HumanTime::into_human(Duration::from_secs(3601)).to_string() == "1 hour\n1 second"); assert!( - HumanTime::into_human(Duration::from_secs(3602)).to_string() == "1 hour, 2 seconds" + HumanTime::into_human(Duration::from_secs(3602)).to_string() == "1 hour\n2 seconds" ); - assert!(HumanTime::into_human(Duration::from_secs(3660)).to_string() == "1 hour, 1 minute"); + assert!(HumanTime::into_human(Duration::from_secs(3660)).to_string() == "1 hour\n1 minute"); assert!( - HumanTime::into_human(Duration::from_secs(3720)).to_string() == "1 hour, 2 minutes" + HumanTime::into_human(Duration::from_secs(3720)).to_string() == "1 hour\n2 minutes" ); assert!( HumanTime::into_human(Duration::from_secs(86399)).to_string() - == "23 hours, 59 minutes, 59 seconds" + == "23 hours\n59 minutes\n59 seconds" ); assert!(HumanTime::into_human(Duration::from_secs(86400)).to_string() == "1 day"); - assert!(HumanTime::into_human(Duration::from_secs(86401)).to_string() == "1 day, 1 second"); + assert!(HumanTime::into_human(Duration::from_secs(86401)).to_string() == "1 day\n1 second"); assert!( - HumanTime::into_human(Duration::from_secs(86402)).to_string() == "1 day, 2 seconds" + HumanTime::into_human(Duration::from_secs(86402)).to_string() == "1 day\n2 seconds" ); - assert!(HumanTime::into_human(Duration::from_secs(86460)).to_string() == "1 day, 1 minute"); + assert!(HumanTime::into_human(Duration::from_secs(86460)).to_string() == "1 day\n1 minute"); assert!( - HumanTime::into_human(Duration::from_secs(86520)).to_string() == "1 day, 2 minutes" + HumanTime::into_human(Duration::from_secs(86520)).to_string() == "1 day\n2 minutes" ); - assert!(HumanTime::into_human(Duration::from_secs(90000)).to_string() == "1 day, 1 hour"); - assert!(HumanTime::into_human(Duration::from_secs(93600)).to_string() == "1 day, 2 hours"); + assert!(HumanTime::into_human(Duration::from_secs(90000)).to_string() == "1 day\n1 hour"); + assert!(HumanTime::into_human(Duration::from_secs(93600)).to_string() == "1 day\n2 hours"); assert!( HumanTime::into_human(Duration::from_secs(604799)).to_string() - == "6 days, 23 hours, 59 minutes, 59 seconds" + == "6 days\n23 hours\n59 minutes\n59 seconds" ); assert!(HumanTime::into_human(Duration::from_secs(604800)).to_string() == "7 days"); assert!(HumanTime::into_human(Duration::from_secs(2630016)).to_string() == "1 month"); assert!( HumanTime::into_human(Duration::from_secs(3234815)).to_string() - == "1 month, 6 days, 23 hours, 59 minutes, 59 seconds" + == "1 month\n6 days\n23 hours\n59 minutes\n59 seconds" ); assert!(HumanTime::into_human(Duration::from_secs(5260032)).to_string() == "2 months"); assert!(HumanTime::into_human(Duration::from_secs(31557600)).to_string() == "1 year"); assert!(HumanTime::into_human(Duration::from_secs(63115200)).to_string() == "2 years"); assert_eq!( HumanTime::into_human(Duration::from_secs(18446744073709551615)).to_string(), - "584542046090 years, 7 months, 15 days, 17 hours, 5 minutes, 3 seconds", + "584542046090 years\n7 months\n15 days\n17 hours\n5 minutes\n3 seconds", ); } }