From 1985e2fc5f87e3bbe4906e81badd26492fe4e986 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 6 Aug 2024 22:43:43 +0859 Subject: [PATCH] feat: test multi layershell feature --- Cargo.lock | 419 +++++++++++++++++++++++++++++++++-- Cargo.toml | 14 +- misc/text-plain.svg | 3 + src/launcher.rs | 149 +++++++++++++ src/launcher/applications.rs | 179 +++++++++++++++ src/main.rs | 90 +++++++- 6 files changed, 834 insertions(+), 20 deletions(-) create mode 100644 misc/text-plain.svg create mode 100644 src/launcher.rs create mode 100644 src/launcher/applications.rs diff --git a/Cargo.lock b/Cargo.lock index f0ecae8..ea24131 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,6 +356,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "bit-set" version = "0.5.3" @@ -557,6 +563,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -737,7 +753,7 @@ dependencies = [ "log", "rangemap", "rustc-hash", - "rustybuzz", + "rustybuzz 0.11.0", "self_cell", "swash", "sys-locale", @@ -829,6 +845,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + [[package]] name = "digest" version = "0.10.7" @@ -1064,6 +1086,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "flume" version = "0.11.0" @@ -1088,7 +1116,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" dependencies = [ - "roxmltree", + "roxmltree 0.20.0", ] [[package]] @@ -1266,6 +1294,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gif" version = "0.13.1" @@ -1282,6 +1320,37 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "gio" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398e3da68749fdc32783cbf7521ec3f65c9cf946db8c7774f8460af49e52c6e2" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4feb96b31c32730ea3e1e89aecd2e4e37ecb1c473ad8f685e3430a159419f63" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.52.0", +] + [[package]] name = "gl_generator" version = "0.14.0" @@ -1299,6 +1368,51 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +[[package]] +name = "glib" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee90a615ce05be7a32932cfb8adf2c4bbb4700e80d37713c981fb24c0c56238" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da558d8177c0c8c54368818b508a4244e1286fce2858cef4e547023f0cfa5ef" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "glib-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4958c26e5a01c9af00dea669a97369eccbec29a8e6d125c24ea2d85ee7467b60" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "glow" version = "0.13.1" @@ -1332,6 +1446,17 @@ dependencies = [ "wgpu", ] +[[package]] +name = "gobject-sys" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6908864f5ffff15b56df7e90346863904f49b949337ed0456b9287af61903b8" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "gpu-alloc" version = "0.6.0" @@ -1429,6 +1554,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1531,9 +1662,8 @@ dependencies = [ [[package]] name = "iced_layershell" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c69e0ccd60f048905eccd69d129782e9cd73a89c795ba05ca5530859310094" +version = "0.4.0-dev" +source = "git+https://github.com/waycrate/exwlshelleventloop#da73950973d703ecb7ee5faa2c9f5364fe946cc9" dependencies = [ "futures", "iced", @@ -1593,8 +1723,9 @@ dependencies = [ "bytemuck", "cosmic-text", "iced_graphics", - "kurbo", + "kurbo 0.10.4", "log", + "resvg", "rustc-hash", "softbuffer", "tiny-skia", @@ -1616,6 +1747,7 @@ dependencies = [ "iced_graphics", "log", "once_cell", + "resvg", "wgpu", ] @@ -1672,7 +1804,7 @@ dependencies = [ "byteorder", "color_quant", "exr", - "gif", + "gif 0.13.1", "jpeg-decoder", "num-traits", "png", @@ -1680,6 +1812,12 @@ dependencies = [ "tiff", ] +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + [[package]] name = "indexmap" version = "2.3.0" @@ -1780,6 +1918,15 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + [[package]] name = "kurbo" version = "0.10.4" @@ -1798,23 +1945,25 @@ dependencies = [ "anyhow", "env_logger", "futures-util", + "gio", "iced", "iced_futures", "iced_layershell", "iced_runtime", "once_cell", + "regex", "serde", "tokio", "tracing", "tracing-subscriber", + "xdg", "zbus", ] [[package]] name = "layershellev" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfda1617b6b9b35482a2da99a35cd60e19a000e02b4b4ca555cc814b217b7c02" +version = "0.4.0-dev" +source = "git+https://github.com/waycrate/exwlshelleventloop#da73950973d703ecb7ee5faa2c9f5364fe946cc9" dependencies = [ "bitflags 2.6.0", "log", @@ -2457,6 +2606,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2535,7 +2690,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit", + "toml_edit 0.21.1", ] [[package]] @@ -2648,6 +2803,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rctree" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" + [[package]] name = "read-fonts" version = "0.20.0" @@ -2729,6 +2890,41 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "resvg" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7980f653f9a7db31acff916a262c3b78c562919263edea29bf41a056e20497" +dependencies = [ + "gif 0.12.0", + "jpeg-decoder", + "log", + "pico-args", + "png", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12bc8d2f72df26a5d3178022df33720fbede0d31d82c7291662eff89836994d" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "roxmltree" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" +dependencies = [ + "xmlparser", +] + [[package]] name = "roxmltree" version = "0.20.0" @@ -2760,6 +2956,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustybuzz" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71cd15fef9112a1f94ac64b58d1e4628192631ad6af4dc69997f995459c874e7" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "smallvec", + "ttf-parser 0.19.2", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "rustybuzz" version = "0.11.0" @@ -2848,6 +3060,15 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2883,6 +3104,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -3068,6 +3298,9 @@ name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] [[package]] name = "svg_fmt" @@ -3075,6 +3308,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca" +[[package]] +name = "svgtypes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71499ff2d42f59d26edb21369a308ede691421f79ebc0f001e2b1fd3a7c9e52" +dependencies = [ + "kurbo 0.9.5", + "siphasher", +] + [[package]] name = "swash" version = "0.1.18" @@ -3117,6 +3360,25 @@ dependencies = [ "libc", ] +[[package]] +name = "system-deps" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c81f13d9a334a6c242465140bd262fae382b752ff2011c4f7419919a9c97922" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" version = "3.11.0" @@ -3264,11 +3526,26 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.20", +] + [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3278,7 +3555,20 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.18", ] [[package]] @@ -3421,6 +3711,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.13" @@ -3433,6 +3729,67 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "usvg" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51daa774fe9ee5efcf7b4fec13019b8119cda764d9a8b5b06df02bb1445c656" +dependencies = [ + "base64", + "log", + "pico-args", + "usvg-parser", + "usvg-text-layout", + "usvg-tree", + "xmlwriter", +] + +[[package]] +name = "usvg-parser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c88a5ffaa338f0e978ecf3d4e00d8f9f493e29bed0752e1a808a1db16afc40" +dependencies = [ + "data-url", + "flate2", + "imagesize", + "kurbo 0.9.5", + "log", + "roxmltree 0.18.1", + "simplecss", + "siphasher", + "svgtypes", + "usvg-tree", +] + +[[package]] +name = "usvg-text-layout" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2374378cb7a3fb8f33894e0fdb8625e1bbc4f25312db8d91f862130b541593" +dependencies = [ + "fontdb", + "kurbo 0.9.5", + "log", + "rustybuzz 0.10.0", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "usvg-tree", +] + +[[package]] +name = "usvg-tree" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cacb0c5edeaf3e80e5afcf5b0d4004cc1d36318befc9a7c6606507e5d0f4062" +dependencies = [ + "rctree", + "strict-num", + "svgtypes", + "tiny-skia-path", +] + [[package]] name = "utf8parse" version = "0.2.2" @@ -3445,6 +3802,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -3550,9 +3913,8 @@ dependencies = [ [[package]] name = "waycrate_xkbkeycode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64ac0605a51165a4407bea25077ee668fda780692d0e5abb5872c1d9fe52ae6" +version = "0.4.0-dev" +source = "git+https://github.com/waycrate/exwlshelleventloop#da73950973d703ecb7ee5faa2c9f5364fe946cc9" dependencies = [ "bitflags 2.6.0", "log", @@ -4184,6 +4546,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -4222,6 +4593,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d491ee231a51ae64a5b762114c3ac2104b967aadba1de45c86ca42cf051513b7" +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + [[package]] name = "xdg-home" version = "1.2.0" @@ -4271,6 +4648,18 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "xxhash-rust" version = "0.8.12" diff --git a/Cargo.toml b/Cargo.toml index 675a72a..127a9f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -iced = { version = "0.12", features = ["tokio", "debug", "image", "advanced"] } +iced = { version = "0.12", features = [ + "tokio", + "debug", + "image", + "advanced", + "svg", +] } #iced_native = "0.12" iced_runtime = "0.12" -iced_layershell = "0.3.0" +iced_layershell = { git = "https://github.com/waycrate/exwlshelleventloop", package = "iced_layershell" } tokio = { version = "1.39", features = ["full"] } iced_futures = "0.12.0" env_logger = "0.11.5" @@ -23,3 +29,7 @@ zbus = { version = "4.4.0", default-features = false, features = ["tokio"] } tracing-subscriber = "0.3.18" anyhow = "1.0.86" alsa = "0.9.0" + +gio = "0.20.0" +regex = "1.10.5" +xdg = "2.5.2" diff --git a/misc/text-plain.svg b/misc/text-plain.svg new file mode 100644 index 0000000..05d51e8 --- /dev/null +++ b/misc/text-plain.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/launcher.rs b/src/launcher.rs new file mode 100644 index 0000000..fb4fef7 --- /dev/null +++ b/src/launcher.rs @@ -0,0 +1,149 @@ +mod applications; + +use applications::{all_apps, App}; +use iced::widget::{column, scrollable, text_input}; +use iced::{Command, Element, Event, Length}; +use iced_runtime::command::Action; +use iced_runtime::window::Action as WindowAction; + +use super::Message; + +use std::sync::LazyLock; + +static SCROLLABLE_ID: LazyLock = LazyLock::new(scrollable::Id::unique); +pub static INPUT_ID: LazyLock = LazyLock::new(text_input::Id::unique); + +pub struct Launcher { + text: String, + apps: Vec, + scrollpos: usize, + pub shoud_delete: bool, +} + +impl Launcher { + pub fn new() -> Self { + Self { + text: "".to_string(), + apps: all_apps(), + scrollpos: 0, + shoud_delete: false, + } + } + + pub fn focus_input(&self) -> Command { + text_input::focus(INPUT_ID.clone()) + } + + pub fn update(&mut self, message: Message, id: iced::window::Id) -> Command { + use iced::keyboard::key::Named; + use iced_runtime::keyboard; + match message { + Message::SearchSubmit => { + let re = regex::Regex::new(&self.text).ok(); + let index = self + .apps + .iter() + .enumerate() + .filter(|(_, app)| { + if re.is_none() { + return true; + } + let re = re.as_ref().unwrap(); + + re.is_match(app.title().to_lowercase().as_str()) + || re.is_match(app.description().to_lowercase().as_str()) + }) + .enumerate() + .find(|(index, _)| *index == self.scrollpos); + if let Some((_, (_, app))) = index { + app.launch(); + self.shoud_delete = true; + Command::single(Action::Window(WindowAction::Close(id))) + } else { + Command::none() + } + } + Message::SearchEditChanged(edit) => { + self.scrollpos = 0; + self.text = edit; + Command::none() + } + Message::Launch(index) => { + self.apps[index].launch(); + self.shoud_delete = true; + Command::single(Action::Window(WindowAction::Close(id))) + } + Message::IcedEvent(event) => { + let mut len = self.apps.len(); + + let re = regex::Regex::new(&self.text).ok(); + if let Some(re) = re { + len = self + .apps + .iter() + .filter(|app| { + re.is_match(app.title().to_lowercase().as_str()) + || re.is_match(app.description().to_lowercase().as_str()) + }) + .count(); + } + if let Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) + | Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) = event + { + match key { + keyboard::Key::Named(Named::ArrowUp) => { + if self.scrollpos == 0 { + return Command::none(); + } + self.scrollpos -= 1; + } + keyboard::Key::Named(Named::ArrowDown) => { + if self.scrollpos >= len - 1 { + return Command::none(); + } + self.scrollpos += 1; + } + keyboard::Key::Named(Named::Escape) => { + self.shoud_delete = true; + return Command::single(Action::Window(WindowAction::Close(id))); + } + _ => {} + } + } + text_input::focus(INPUT_ID.clone()) + } + _ => Command::none(), + } + } + + pub fn view(&self) -> Element { + let re = regex::Regex::new(&self.text).ok(); + let text_ip: Element = text_input("put the launcher name", &self.text) + .padding(10) + .on_input(Message::SearchEditChanged) + .on_submit(Message::SearchSubmit) + .id(INPUT_ID.clone()) + .into(); + let bottom_vec: Vec> = self + .apps + .iter() + .enumerate() + .filter(|(_, app)| { + if re.is_none() { + return true; + } + let re = re.as_ref().unwrap(); + + re.is_match(app.title().to_lowercase().as_str()) + || re.is_match(app.description().to_lowercase().as_str()) + }) + .enumerate() + .filter(|(index, _)| *index >= self.scrollpos) + .map(|(filter_index, (index, app))| app.view(index, filter_index == self.scrollpos)) + .collect(); + let bottom: Element = scrollable(column(bottom_vec).width(Length::Fill)) + .id(SCROLLABLE_ID.clone()) + .into(); + column![text_ip, bottom].into() + } +} diff --git a/src/launcher/applications.rs b/src/launcher/applications.rs new file mode 100644 index 0000000..04e9869 --- /dev/null +++ b/src/launcher/applications.rs @@ -0,0 +1,179 @@ +use std::path::PathBuf; +use std::str::FromStr; + +use gio::{AppLaunchContext, DesktopAppInfo}; + +use gio::prelude::*; +use iced::widget::{button, column, image, row, svg, text}; +use iced::{theme, Pixels}; +use iced::{Element, Length}; + +use super::Message; + +static DEFAULT_ICON: &[u8] = include_bytes!("../../misc/text-plain.svg"); + +#[allow(unused)] +#[derive(Debug, Clone)] +pub struct App { + appinfo: DesktopAppInfo, + name: String, + descriptions: Option, + pub categrades: Option>, + pub actions: Option>, + icon: Option, +} + +impl App { + pub fn launch(&self) { + if let Err(err) = self.appinfo.launch(&[], AppLaunchContext::NONE) { + println!("{}", err); + }; + } + + pub fn title(&self) -> &str { + &self.name + } + + fn icon(&self) -> Element { + match &self.icon { + Some(path) => { + if path + .as_os_str() + .to_str() + .is_some_and(|pathname| pathname.ends_with("png")) + { + image(image::Handle::from_path(path)) + .width(Length::Fixed(80.)) + .height(Length::Fixed(80.)) + .into() + } else { + svg(svg::Handle::from_path(path)) + .width(Length::Fixed(80.)) + .height(Length::Fixed(80.)) + .into() + } + } + None => svg(svg::Handle::from_memory(DEFAULT_ICON)) + .width(Length::Fixed(80.)) + .height(Length::Fixed(80.)) + .into(), + } + } + + pub fn description(&self) -> &str { + match &self.descriptions { + None => "", + Some(description) => description, + } + } + + pub fn view(&self, index: usize, selected: bool) -> Element { + button( + row![ + self.icon(), + column![ + text(self.title()).size(Pixels::from(20)), + text(self.description()).size(Pixels::from(10)) + ] + .spacing(4) + ] + .spacing(10), + ) + .on_press(Message::Launch(index)) + .width(Length::Fill) + .height(Length::Fixed(85.)) + .style(if selected { + theme::Button::Primary + } else { + theme::Button::Secondary + }) + .into() + } +} + +static ICONS_SIZE: &[&str] = &["256x256", "128x128"]; + +static THEMES_LIST: &[&str] = &["breeze", "Adwaita"]; + +fn get_icon_path_from_xdgicon(iconname: &str) -> Option { + let scalable_icon_path = + xdg::BaseDirectories::with_prefix("icons/hicolor/scalable/apps").unwrap(); + if let Some(iconpath) = scalable_icon_path.find_data_file(format!("{iconname}.svg")) { + return Some(iconpath); + } + for prefix in ICONS_SIZE { + let iconpath = + xdg::BaseDirectories::with_prefix(&format!("icons/hicolor/{prefix}/apps")).unwrap(); + if let Some(iconpath) = iconpath.find_data_file(format!("{iconname}.png")) { + return Some(iconpath); + } + } + let pixmappath = xdg::BaseDirectories::with_prefix("pixmaps").unwrap(); + if let Some(iconpath) = pixmappath.find_data_file(format!("{iconname}.svg")) { + return Some(iconpath); + } + if let Some(iconpath) = pixmappath.find_data_file(format!("{iconname}.png")) { + return Some(iconpath); + } + for themes in THEMES_LIST { + let iconpath = + xdg::BaseDirectories::with_prefix(&format!("icons/{themes}/apps/48")).unwrap(); + if let Some(iconpath) = iconpath.find_data_file(format!("{iconname}.svg")) { + return Some(iconpath); + } + let iconpath = + xdg::BaseDirectories::with_prefix(&format!("icons/{themes}/apps/64")).unwrap(); + if let Some(iconpath) = iconpath.find_data_file(format!("{iconname}.svg")) { + return Some(iconpath); + } + } + None +} + +fn get_icon_path(iconname: &str) -> Option { + if iconname.contains('/') { + PathBuf::from_str(iconname).ok() + } else { + get_icon_path_from_xdgicon(iconname) + } +} + +pub fn all_apps() -> Vec { + let re = regex::Regex::new(r"([a-zA-Z]+);").unwrap(); + gio::AppInfo::all() + .iter() + .filter(|app| app.should_show() && app.downcast_ref::().is_some()) + .map(|app| app.clone().downcast::().unwrap()) + .map(|app| App { + appinfo: app.clone(), + name: app.name().to_string(), + descriptions: app.description(), + categrades: match app.categories() { + None => None, + Some(categrades) => { + let tomatch = categrades.to_string(); + let tips = re + .captures_iter(&tomatch) + .map(|unit| unit.get(1).unwrap().as_str().to_string()) + .collect(); + Some(tips) + } + }, + actions: { + let actions = app.list_actions(); + if actions.is_empty() { + None + } else { + Some(actions) + } + }, + icon: match &app.icon() { + None => None, + Some(icon) => { + let iconname = gio::prelude::IconExt::to_string(icon).unwrap(); + get_icon_path(iconname.as_str()) + } + }, + }) + .collect() +} diff --git a/src/main.rs b/src/main.rs index 9940a2b..1a68fe6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,20 @@ use iced::widget::{button, container, row, slider, text, Space}; -use iced::{executor, Font}; +use iced::{executor, Event, Font}; use iced::{Command, Element, Length, Theme}; +use iced_layershell::actions::{ + LayershellCustomActionsWithIdAndInfo, LayershellCustomActionsWithInfo, +}; +use launcher::Launcher; use zbus_mpirs::ServiceInfo; -use iced_layershell::reexport::{Anchor, Layer}; +use iced_layershell::reexport::{Anchor, KeyboardInteractivity, Layer, NewLayerShellSettings}; use iced_layershell::settings::{LayerShellSettings, Settings}; use iced_layershell::MultiApplication; +use iced_runtime::command::Action; +use iced_runtime::window::Action as WindowAction; mod aximer; +mod launcher; mod zbus_mpirs; pub fn main() -> Result<(), iced_layershell::Error> { @@ -25,12 +32,17 @@ pub fn main() -> Result<(), iced_layershell::Error> { }) } +#[derive(Debug, Clone, Copy)] +struct LauncherInfo; + #[derive(Default)] struct LalaMusicBar { service_data: Option, left: i64, right: i64, bar_index: SliderIndex, + launcher: Option, + launcherid: Option, } #[derive(Copy, Clone, Default)] @@ -149,6 +161,11 @@ enum Message { UpdateRight(u8), SliderIndexNext, SliderIndexPre, + ToggleLauncher, + SearchEditChanged(String), + SearchSubmit, + Launch(usize), + IcedEvent(Event), } async fn get_metadata_initial() -> Option { @@ -167,6 +184,7 @@ impl MultiApplication for LalaMusicBar { type Flags = (); type Executor = executor::Default; type Theme = Theme; + type WindowInfo = LauncherInfo; fn new(_flags: Self::Flags) -> (Self, Command) { ( @@ -175,6 +193,8 @@ impl MultiApplication for LalaMusicBar { left: aximer::get_left().unwrap_or(0), right: aximer::get_right().unwrap_or(0), bar_index: SliderIndex::Balance, + launcher: None, + launcherid: None, }, Command::perform(get_metadata_initial(), Message::DBusInfoUpdate), ) @@ -184,6 +204,22 @@ impl MultiApplication for LalaMusicBar { String::from("Mpirs_panel") } + fn id_info(&self, id: iced_futures::core::window::Id) -> Option<&Self::WindowInfo> { + if self.launcherid.is_some_and(|tid| tid == id) { + Some(&LauncherInfo) + } else { + None + } + } + + fn set_id_info(&mut self, id: iced_futures::core::window::Id, _info: Self::WindowInfo) { + self.launcherid = Some(id); + } + + fn remove_id(&mut self, _id: iced_futures::core::window::Id) { + self.launcherid.take(); + } + fn update(&mut self, message: Message) -> Command { match message { Message::DBusInfoUpdate(data) => self.service_data = data, @@ -270,11 +306,57 @@ impl MultiApplication for LalaMusicBar { } Message::SliderIndexNext => self.bar_index = self.bar_index.next(), Message::SliderIndexPre => self.bar_index = self.bar_index.pre(), + Message::ToggleLauncher => { + if self.launcher.is_some() { + if let Some(id) = self.launcherid { + self.launcher.take(); + return Command::single(Action::Window(WindowAction::Close(id))); + } + return Command::none(); + } + self.launcher = Some(Launcher::new()); + return Command::batch(vec![ + Command::single( + LayershellCustomActionsWithIdAndInfo::new( + iced::window::Id::MAIN, + LayershellCustomActionsWithInfo::NewLayerShell(( + NewLayerShellSettings { + size: Some((500, 700)), + exclusize_zone: None, + anchor: Anchor::Left | Anchor::Bottom, + layer: Layer::Top, + margins: None, + keyboard_interactivity: KeyboardInteractivity::Exclusive, + }, + LauncherInfo, + )), + ) + .into(), + ), + self.launcher.as_ref().unwrap().focus_input(), + ]); + } + _ => { + if let Some(launcher) = self.launcher.as_mut() { + if let Some(id) = self.launcherid { + let cmd = launcher.update(message, id); + if launcher.shoud_delete { + self.launcher.take(); + } + return cmd; + } + } + } } Command::none() } - fn view(&self, _id: iced::window::Id) -> Element { + fn view(&self, id: iced::window::Id) -> Element { + if let Some(LauncherInfo) = self.id_info(id) { + if let Some(launcher) = &self.launcher { + return launcher.view(); + } + } let title = self .service_data .as_ref() @@ -339,6 +421,7 @@ impl MultiApplication for LalaMusicBar { let sound_slider = self.sound_slider(); let col = row![ + button("L").on_press(Message::ToggleLauncher), title, Space::with_width(Length::Fill), buttons, @@ -360,6 +443,7 @@ impl MultiApplication for LalaMusicBar { iced::time::every(std::time::Duration::from_secs(1)) .map(|_| Message::RequestDBusInfoUpdate), iced::time::every(std::time::Duration::from_secs(5)).map(|_| Message::UpdateBalance), + iced::event::listen().map(Message::IcedEvent), ]) }