From de11767d3b4f5aa7c3380c7af1615097bfaf7e1a Mon Sep 17 00:00:00 2001 From: inorishio Date: Mon, 25 May 2026 23:15:00 +0200 Subject: [PATCH 01/15] zshell-img-tools crate reduction to 53, process release fix, blur-passes, scale impl, settings passes setting, scale only avail in config --- Config/Config.qml | 2 + Config/Screenshot.qml | 2 + Modules/Settings/Categories/Screenshot.qml | 13 + zshell-img-tools/Cargo.lock | 810 +-------------------- zshell-img-tools/Cargo.toml | 8 +- zshell-img-tools/README.md | 6 - zshell-img-tools/src/config.rs | 6 +- zshell-img-tools/src/effects.rs | 61 +- zshell-img-tools/src/main.rs | 119 +-- 9 files changed, 143 insertions(+), 884 deletions(-) delete mode 100644 zshell-img-tools/README.md diff --git a/Config/Config.qml b/Config/Config.qml index 2f6686e..6122fd5 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -293,10 +293,12 @@ Singleton { return { enable_pp: screenshot.enable_pp, mode: screenshot.mode, + scale: screenshot.scale, corner_radius: screenshot.corner_radius, drop_shadow: screenshot.drop_shadow, rounded_corners: screenshot.rounded_corners, shadow_blur_radius: screenshot.shadow_blur_radius, + shadow_blur_passes: screenshot.shadow_blur_passes, shadow_color: screenshot.shadow_color, shadow_offset_x: screenshot.shadow_offset_x, shadow_offset_y: screenshot.shadow_offset_y diff --git a/Config/Screenshot.qml b/Config/Screenshot.qml index 7ea47bc..793e1a8 100644 --- a/Config/Screenshot.qml +++ b/Config/Screenshot.qml @@ -6,6 +6,8 @@ JsonObject { property bool enable_pp: true property string mode: "manual" property bool rounded_corners: false + property real scale: 1.0 + property int shadow_blur_passes: 1 property real shadow_blur_radius: 22.0 property list shadow_color: [0, 0, 0, 160] property real shadow_offset_x: 5.0 diff --git a/Modules/Settings/Categories/Screenshot.qml b/Modules/Settings/Categories/Screenshot.qml index 3d69d93..ec7f3a5 100644 --- a/Modules/Settings/Categories/Screenshot.qml +++ b/Modules/Settings/Categories/Screenshot.qml @@ -105,6 +105,19 @@ SettingsPage { visible: Config.screenshot.mode === "manual" } + SettingSpinBox { + min: 1 + name: "Shadow passes" + object: Config.screenshot + setting: "shadow_blur_passes" + step: 1 + visible: Config.screenshot.mode === "manual" + } + + Separator { + visible: Config.screenshot.mode === "manual" + } + SettingSpinBox { min: 0 name: "Shadow offset X" diff --git a/zshell-img-tools/Cargo.lock b/zshell-img-tools/Cargo.lock index 85359cd..8c7321e 100644 --- a/zshell-img-tools/Cargo.lock +++ b/zshell-img-tools/Cargo.lock @@ -8,47 +8,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - [[package]] name = "anyhow" version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "arrayref" version = "0.3.9" @@ -61,103 +26,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey", - "rayon", - "thiserror", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" -[[package]] -name = "bitstream-io" -version = "4.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" -dependencies = [ - "no_std_io2", -] - -[[package]] -name = "built" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - [[package]] name = "bytemuck" version = "1.25.0" @@ -170,30 +50,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" -[[package]] -name = "cc" -version = "1.2.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "crc32fast" version = "1.5.0" @@ -203,84 +65,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "equator" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] - -[[package]] -name = "equator-macro" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "exr" -version = "1.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fax" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" - [[package]] name = "fdeflate" version = "0.3.7" @@ -290,12 +74,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - [[package]] name = "flate2" version = "1.1.9" @@ -306,39 +84,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", -] - -[[package]] -name = "gif" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - [[package]] name = "image" version = "0.25.10" @@ -347,56 +92,9 @@ checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", "moxcms", "num-traits", - "png 0.18.1", - "qoi", - "ravif", - "rayon", - "rgb", - "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imgref" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" - -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", + "png", ] [[package]] @@ -405,63 +103,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom", - "libc", -] - -[[package]] -name = "lebe" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" -dependencies = [ - "arbitrary", - "cc", -] - [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - [[package]] name = "memchr" version = "2.8.0" @@ -488,77 +135,6 @@ dependencies = [ "pxfm", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "no_std_io2" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550" -dependencies = [ - "memchr", -] - -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -568,59 +144,19 @@ dependencies = [ "autocfg", ] -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "png" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.1", + "bitflags", "crc32fast", "fdeflate", "flate2", "miniz_oxide", ] -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -630,46 +166,12 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "pxfm" version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quote" version = "1.0.45" @@ -679,123 +181,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand", - "rand_chacha", - "simd_helpers", - "thiserror", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - -[[package]] -name = "rayon" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rgb" -version = "0.8.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - [[package]] name = "serde" version = "1.0.228" @@ -839,39 +224,12 @@ dependencies = [ "zmij", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "simd-adler32" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - [[package]] name = "strict-num" version = "0.1.1" @@ -889,40 +247,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tiff" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error", - "weezl", - "zune-jpeg", -] - [[package]] name = "tiny-skia" version = "0.11.4" @@ -934,7 +258,6 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png 0.17.16", "tiny-skia-path", ] @@ -955,109 +278,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "v_frame" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "weezl" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - -[[package]] -name = "zerocopy" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zmij" version = "1.0.21" @@ -1066,7 +286,7 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zshell-img-tools" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "image", @@ -1074,27 +294,3 @@ dependencies = [ "serde_json", "tiny-skia", ] - -[[package]] -name = "zune-core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" -dependencies = [ - "zune-core", -] diff --git a/zshell-img-tools/Cargo.toml b/zshell-img-tools/Cargo.toml index 95a19e1..52fde40 100644 --- a/zshell-img-tools/Cargo.toml +++ b/zshell-img-tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zshell-img-tools" -version = "0.1.0" +version = "0.2.0" edition = "2024" [[bin]] @@ -8,10 +8,10 @@ name = "zshell-img-tools" path = "src/main.rs" [dependencies] -image = { version = "0.25", features = ["png"] } -tiny-skia = "0.11" +image = { version = "0.25", default-features = false, features = ["png"] } +tiny-skia = { version = "0.11", default-features = false, features = ["std", "simd"] } serde = { version = "1", features = ["derive"] } -anyhow = "1" +anyhow = "1.0" serde_json = "1.0.149" [profile.release] diff --git a/zshell-img-tools/README.md b/zshell-img-tools/README.md deleted file mode 100644 index bbe4940..0000000 --- a/zshell-img-tools/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# What_That_Claude_DO? - -What That Claude Do? (WTCD) -A repository of random things I ask Claude to do for me. - -In this case it is creating a screenshot tool diff --git a/zshell-img-tools/src/config.rs b/zshell-img-tools/src/config.rs index 3b502f1..f9bcbb3 100644 --- a/zshell-img-tools/src/config.rs +++ b/zshell-img-tools/src/config.rs @@ -11,13 +11,15 @@ pub struct Config { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EffectsConfig { pub mode: String, - pub rounded_corners: bool, pub corner_radius: f32, pub drop_shadow: bool, + pub rounded_corners: bool, + pub scale: f32, pub shadow_blur_radius: f32, + pub shadow_blur_passes: u32, + pub shadow_color: [u8; 4], pub shadow_offset_x: f32, pub shadow_offset_y: f32, - pub shadow_color: [u8; 4], } impl Config { diff --git a/zshell-img-tools/src/effects.rs b/zshell-img-tools/src/effects.rs index f79726f..fcdd236 100644 --- a/zshell-img-tools/src/effects.rs +++ b/zshell-img-tools/src/effects.rs @@ -16,6 +16,7 @@ pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage { cfg.shadow_blur_radius, cfg.shadow_offset_x, cfg.shadow_offset_y, + cfg.shadow_blur_passes, cfg.shadow_color, ) } else { @@ -52,11 +53,16 @@ pub fn apply_drop_shadow( blur_radius: f32, offset_x: f32, offset_y: f32, + blur_passes: u32, shadow_color: [u8; 4], ) -> RgbaImage { let (iw, ih) = img.dimensions(); let br = blur_radius.ceil() as u32; - let spread = br * 2; + let bp = blur_passes; + // Original idea + // let spread = br * bp; + // Claude is hallucinating but let's try it **Worked btw** + let spread = (br as f32 * (bp as f32).sqrt() * 2.0).ceil() as u32; let extra_left = spread + (-offset_x).max(0.0).ceil() as u32; let extra_top = spread + (-offset_y).max(0.0).ceil() as u32; @@ -86,8 +92,11 @@ pub fn apply_drop_shadow( tint_pixmap_as_shadow(&mut shadow_pixmap, shadow_color); + // Shadow let shadow_img = pixmap_to_rgba_image(shadow_pixmap); - let blurred = box_blur_rgba(&shadow_img, br); + // Shadow blur + let blurred = box_blur_rgba(&shadow_img, br, bp); + // Shadow pos let blurred_pixmap = rgba_image_to_pixmap(&blurred); let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap"); @@ -136,6 +145,7 @@ fn rounded_rect_path(x: f32, y: f32, w: f32, h: f32, r: f32) -> Path { pb.finish().expect("rounded rect path") } +// Shadow pos fn rgba_image_to_pixmap(img: &RgbaImage) -> Pixmap { let (w, h) = img.dimensions(); let mut pixmap = Pixmap::new(w, h).expect("pixmap alloc"); @@ -154,6 +164,7 @@ fn rgba_image_to_pixmap(img: &RgbaImage) -> Pixmap { pixmap } +// Shadow fn pixmap_to_rgba_image(pixmap: Pixmap) -> RgbaImage { let (w, h) = (pixmap.width(), pixmap.height()); let mut out = RgbaImage::new(w, h); @@ -176,31 +187,16 @@ fn pixmap_to_rgba_image(pixmap: Pixmap) -> RgbaImage { out } -fn tint_pixmap_as_shadow(pixmap: &mut Pixmap, color: [u8; 4]) { - let [sr, sg, sb, _] = color; - for px in pixmap.pixels_mut() { - let a = px.alpha(); - if a > 0 { - let af = a as f32 / 255.0; - *px = tiny_skia::PremultipliedColorU8::from_rgba( - (sr as f32 * af) as u8, - (sg as f32 * af) as u8, - (sb as f32 * af) as u8, - a, - ) - .unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT); - } - } -} - -fn box_blur_rgba(img: &RgbaImage, radius: u32) -> RgbaImage { +// Shadow blur +fn box_blur_rgba(img: &RgbaImage, radius: u32, bp: u32) -> RgbaImage { if radius == 0 { return img.clone(); } - let mut buf = sliding_horizontal(img, radius); - buf = sliding_vertical(&buf, radius); - buf = sliding_horizontal(&buf, radius); - buf = sliding_vertical(&buf, radius); + let mut buf = img.clone(); + for _ in 0..bp { + buf = sliding_horizontal(&buf, radius); + buf = sliding_vertical(&buf, radius); + } buf } @@ -250,6 +246,23 @@ fn sliding_horizontal(img: &RgbaImage, radius: u32) -> RgbaImage { out } +fn tint_pixmap_as_shadow(pixmap: &mut Pixmap, color: [u8; 4]) { + let [sr, sg, sb, _] = color; + for px in pixmap.pixels_mut() { + let a = px.alpha(); + if a > 0 { + let af = a as f32 / 255.0; + *px = tiny_skia::PremultipliedColorU8::from_rgba( + (sr as f32 * af) as u8, + (sg as f32 * af) as u8, + (sb as f32 * af) as u8, + a, + ) + .unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT); + } + } +} + fn sliding_vertical(img: &RgbaImage, radius: u32) -> RgbaImage { let (w, h) = img.dimensions(); let r = radius as i32; diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index 73c8d30..12c24d9 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -1,21 +1,21 @@ mod config; mod effects; -use anyhow::{Context, Result, bail}; +use anyhow::{bail, Context, Result}; use std::io::Write as _; use std::process::{Command, Stdio}; -/// CLI overrides that map 1:1 to `EffectsConfig` fields. -/// All fields are `Option` so we can tell "not supplied" from any concrete value. #[derive(Default)] struct CliOverrides { rounded_corners: Option, corner_radius: Option, drop_shadow: Option, + scale: Option, shadow_blur_radius: Option, + shadow_blur_passes: Option, shadow_offset_x: Option, shadow_offset_y: Option, - /// Accepted as four comma-separated u8 values, e.g. `255,0,0,200` + // Accepted as four comma-separated u8 values, e.g. `255,0,0,200` shadow_color: Option<[u8; 4]>, } @@ -30,24 +30,24 @@ fn parse_bool(s: &str) -> Result { fn parse_shadow_color(s: &str) -> Result<[u8; 4]> { let parts: Vec<&str> = s.split(',').collect(); if parts.len() != 4 { - bail!("--shadow_color expects four comma-separated u8 values, e.g. 255,0,0,200"); + bail!("--shadow-color expects four comma-separated u8 values, e.g. 255,0,0,200"); } let r = parts[0] .trim() .parse::() - .context("shadow_color red channel")?; + .context("shadow-color red channel")?; let g = parts[1] .trim() .parse::() - .context("shadow_color green channel")?; + .context("shadow-color green channel")?; let b = parts[2] .trim() .parse::() - .context("shadow_color blue channel")?; + .context("shadow-color blue channel")?; let a = parts[3] .trim() .parse::() - .context("shadow_color alpha channel")?; + .context("shadow-color alpha channel")?; Ok([r, g, b, a]) } @@ -68,67 +68,82 @@ fn main() -> Result<()> { .context("Expected a path after --image")?, ); } - "--rounded_corners" => { + "--rounded-corners" => { i += 1; let val = args .get(i) - .context("Expected true/false after --rounded_corners")?; + .context("Expected true/false after --rounded-corners")?; overrides.rounded_corners = Some(parse_bool(val)?); } - "--corner_radius" => { + "--corner-radius" => { i += 1; let val = args .get(i) - .context("Expected a number after --corner_radius")?; + .context("Expected a number after --corner-radius")?; overrides.corner_radius = Some( val.parse::() - .context("--corner_radius must be a number")?, + .context("--corner-radius must be a number")?, ); } - "--drop_shadow" => { + "--drop-shadow" => { i += 1; let val = args .get(i) - .context("Expected true/false after --drop_shadow")?; + .context("Expected true/false after --drop-shadow")?; overrides.drop_shadow = Some(parse_bool(val)?); } - "--shadow_blur_radius" => { + "--shadow-blur-radius" => { i += 1; let val = args .get(i) - .context("Expected a number after --shadow_blur_radius")?; + .context("Expected a number after --shadow-blur-radius")?; overrides.shadow_blur_radius = Some( val.parse::() - .context("--shadow_blur_radius must be a number")?, + .context("--shadow-blur-radius must be a number")?, ); } - "--shadow_offset_x" => { + "--shadow-offset-x" => { i += 1; let val = args .get(i) - .context("Expected a number after --shadow_offset_x")?; + .context("Expected a number after --shadow-offset-x")?; overrides.shadow_offset_x = Some( val.parse::() - .context("--shadow_offset_x must be a number")?, + .context("--shadow-offset-x must be a number")?, ); } - "--shadow_offset_y" => { + "--shadow-offset-y" => { i += 1; let val = args .get(i) - .context("Expected a number after --shadow_offset_y")?; + .context("Expected a number after --shadow-offset-y")?; overrides.shadow_offset_y = Some( val.parse::() - .context("--shadow_offset_y must be a number")?, + .context("--shadow-offset-y must be a number")?, ); } - "--shadow_color" => { + "--shadow-blur-passes" => { i += 1; let val = args .get(i) - .context("Expected r,g,b,a after --shadow_color")?; + .context("Expected a number after --shadow-blur-passes")?; + overrides.shadow_blur_passes = Some( + val.parse::() + .context("--shadow-blur-passes must be a number")?, + ); + } + "--shadow-color" => { + i += 1; + let val = args + .get(i) + .context("Expected r,g,b,a after --shadow-color")?; overrides.shadow_color = Some(parse_shadow_color(val)?); } + "--scale" => { + i += 1; + let val = args.get(i).context("Expected a number after --scale")?; + overrides.scale = Some(val.parse::().context("--scale must be a number")?); + } unknown => bail!("Unknown argument: {unknown}"), } i += 1; @@ -158,9 +173,22 @@ fn main() -> Result<()> { if let Some(v) = overrides.shadow_offset_y { effects.shadow_offset_y = v; } + if let Some(v) = overrides.shadow_blur_passes { + effects.shadow_blur_passes = v; + } if let Some(v) = overrides.shadow_color { effects.shadow_color = v; } + if let Some(v) = overrides.scale { + effects.scale = v; + } + } + + if effects.scale != 1.0 { + effects.corner_radius *= effects.scale; + effects.shadow_blur_radius *= effects.scale; + effects.shadow_offset_x *= effects.scale; + effects.shadow_offset_y *= effects.scale; } if let Err(e) = process_image(&image_path, &effects) { @@ -191,21 +219,30 @@ fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> { .spawn() .context("Failed to spawn swappy. Is it installed and in PATH?")?; - child - .stdin - .take() - .context("Failed to get swappy stdin")? - .write_all(&png_bytes) - .context("Failed to write image data to swappy")?; - - let status = child.wait().context("Failed to wait for swappy")?; - - if !status.success() { - eprintln!( - "swappy exited with non-zero status for '{}': {}", - path, status - ); + // Writes the PNG bytes to swappy's stdin and then closes + if let Some(mut stdin) = child.stdin.take() { + stdin + .write_all(&png_bytes) + .context("Failed to write image data to swappy")?; } + // Writes the PNG bytes to swappy's stdin and waits for swappy to close + // child + // .stdin + // .take() + // .context("Failed to get swappy stdin")? + // .write_all(&png_bytes) + // .context("Failed to write image data to swappy")? + // .spawn(); + // + // let status = child.await().context("Failed to wait for swappy")?; + // + // if !status.success() { + // eprintln!( + // "swappy exited with non-zero status for '{}': {}", + // path, status + // ); + // } + Ok(()) } -- 2.47.3 From f00af9d70f296099fef7f864c6285df907b80b60 Mon Sep 17 00:00:00 2001 From: zach Date: Tue, 26 May 2026 01:12:36 +0200 Subject: [PATCH 02/15] actually fixed config fetching in hyprland lua --- Helpers/GameMode.qml | 2 +- Helpers/Hypr.qml | 1 - Helpers/Picker.qml | 4 +- Modules/Settings/Categories/Screenshot.qml | 32 ++--- Plugins/ZShell/Internal/hyprextras.cpp | 131 ++++++++++++++++++--- Plugins/ZShell/Internal/hyprextras.hpp | 11 +- 6 files changed, 139 insertions(+), 42 deletions(-) diff --git a/Helpers/GameMode.qml b/Helpers/GameMode.qml index 6cbeb68..19b3485 100644 --- a/Helpers/GameMode.qml +++ b/Helpers/GameMode.qml @@ -36,7 +36,7 @@ Singleton { PersistentProperties { id: props - property bool enabled: Hypr.options["animations:enabled"] === 0 + property bool enabled: Hypr.options.animations.enabled === 0 reloadableId: "gamemode" } diff --git a/Helpers/Hypr.qml b/Helpers/Hypr.qml index 2d3eb31..f607b31 100644 --- a/Helpers/Hypr.qml +++ b/Helpers/Hypr.qml @@ -158,6 +158,5 @@ Singleton { HyprExtras { id: extras - } } diff --git a/Helpers/Picker.qml b/Helpers/Picker.qml index 27fc924..55686c2 100644 --- a/Helpers/Picker.qml +++ b/Helpers/Picker.qml @@ -30,8 +30,8 @@ MouseArea { property real ey: screen.height required property LazyLoader loader property bool onClient - property real realBorderWidth: onClient ? (Hypr.options["general:border_size"] ?? 1) : 2 - property real realRounding: onClient ? (Hypr.options["decoration:rounding"] ?? 0) : 0 + property real realBorderWidth: onClient ? (Hypr.options.general.border_size ?? 1) : 2 + property real realRounding: onClient ? (Hypr.options.decoration.rounding ?? 0) : 0 property real rsx: Math.min(sx, ex) property real rsy: Math.min(sy, ey) required property ShellScreen screen diff --git a/Modules/Settings/Categories/Screenshot.qml b/Modules/Settings/Categories/Screenshot.qml index ec7f3a5..260e7f9 100644 --- a/Modules/Settings/Categories/Screenshot.qml +++ b/Modules/Settings/Categories/Screenshot.qml @@ -43,7 +43,7 @@ SettingsPage { } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSpinBox { @@ -51,34 +51,34 @@ SettingsPage { name: "Corner radius" object: Config.screenshot setting: "corner_radius" + shouldBeActive: Config.screenshot.mode === "manual" step: 1 - visible: Config.screenshot.mode === "manual" } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSwitch { name: "Enable drop shadow" object: Config.screenshot setting: "drop_shadow" - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSwitch { name: "Enable rounded corners" object: Config.screenshot setting: "rounded_corners" - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSpinBox { @@ -86,23 +86,23 @@ SettingsPage { name: "Shadow blur radius" object: Config.screenshot setting: "shadow_blur_radius" + shouldBeActive: Config.screenshot.mode === "manual" step: 1 - visible: Config.screenshot.mode === "manual" } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSwitch { name: "Shadow color broken atm" object: Config.Screenshot setting: "shadow_color" - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSpinBox { @@ -110,12 +110,12 @@ SettingsPage { name: "Shadow passes" object: Config.screenshot setting: "shadow_blur_passes" + shouldBeActive: Config.screenshot.mode === "manual" step: 1 - visible: Config.screenshot.mode === "manual" } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSpinBox { @@ -123,12 +123,12 @@ SettingsPage { name: "Shadow offset X" object: Config.screenshot setting: "shadow_offset_x" + shouldBeActive: Config.screenshot.mode === "manual" step: 1 - visible: Config.screenshot.mode === "manual" } Separator { - visible: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" } SettingSpinBox { @@ -136,8 +136,8 @@ SettingsPage { name: "Shadow offset Y" object: Config.screenshot setting: "shadow_offset_y" + shouldBeActive: Config.screenshot.mode === "manual" step: 1 - visible: Config.screenshot.mode === "manual" } } } diff --git a/Plugins/ZShell/Internal/hyprextras.cpp b/Plugins/ZShell/Internal/hyprextras.cpp index 0d372ce..a3056d1 100644 --- a/Plugins/ZShell/Internal/hyprextras.cpp +++ b/Plugins/ZShell/Internal/hyprextras.cpp @@ -1,8 +1,13 @@ #include "hyprextras.hpp" #include "hyprdevices.hpp" +#include +#include + #include #include +#include +#include #include #include #include @@ -163,6 +168,65 @@ static QString buildHlConfigCall(const QString& key, const QVariant& value) { return out; } +static QVariant parseGetOptionValue(const QJsonObject& obj) { + if (obj.contains(QStringLiteral("bool"))) { + return obj.value(QStringLiteral("bool")).toBool(); + } + + if (obj.contains(QStringLiteral("int"))) { + return obj.value(QStringLiteral("int")).toInt(); + } + + if (obj.contains(QStringLiteral("float"))) { + return obj.value(QStringLiteral("float")).toDouble(); + } + + if (obj.contains(QStringLiteral("str"))) { + return obj.value(QStringLiteral("str")).toString(); + } + + if (obj.contains(QStringLiteral("current"))) { + return obj.value(QStringLiteral("current")).toVariant(); + } + + if (obj.contains(QStringLiteral("value"))) { + return obj.value(QStringLiteral("value")).toVariant(); + } + + if (obj.contains(QStringLiteral("data"))) { + const auto data = obj.value(QStringLiteral("data")); + if (data.isObject()) { + const auto d = data.toObject(); + if (d.contains(QStringLiteral("current"))) { + return d.value(QStringLiteral("current")).toVariant(); + } + if (d.contains(QStringLiteral("value"))) { + return d.value(QStringLiteral("value")).toVariant(); + } + } else { + return data.toVariant(); + } + } + + return {}; +} + +static void insertNestedValue(QVariantMap& root, const QStringList& path, const QVariant& value) { + if (path.isEmpty()) { + return; + } + + if (path.size() == 1) { + root.insert(path.first(), value); + return; + } + + const QString head = path.first(); + QVariantMap child = root.value(head).toMap(); + insertNestedValue(child, path.mid(1), value); + root.insert(head, child); +} + } // namespace HyprExtras::HyprExtras(QObject* parent) @@ -203,7 +267,7 @@ HyprExtras::HyprExtras(QObject* parent) m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly); } -QVariantHash HyprExtras::options() const { +QVariantMap HyprExtras::options() const { return m_options; } @@ -269,30 +333,59 @@ void HyprExtras::refreshOptions() { m_optionsRefresh->close(); } - m_optionsRefresh = makeRequestJson(QStringLiteral("descriptions"), [this](bool success, const QJsonDocument& response) { - m_optionsRefresh.reset(); - if (!success) { + ++m_optionsRefreshGeneration; + const quint64 generation = m_optionsRefreshGeneration; + + static const QStringList optionKeys = { + QStringLiteral("general:border_size"), + QStringLiteral("decoration:rounding"), + QStringLiteral("animations:enabled"), + }; + + auto nextOptions = std::make_shared(); + + auto step = std::make_shared >(); + *step = [this, generation, nextOptions, step](int index) { + if (generation != m_optionsRefreshGeneration) { return; } - const auto options = response.array(); - bool dirty = false; - - for (const auto& o : std::as_const(options)) { - const auto obj = o.toObject(); - const auto key = obj.value(QStringLiteral("value")).toString(); - const auto value = obj.value(QStringLiteral("data")).toObject().value(QStringLiteral("current")).toVariant(); - - if (m_options.value(key) != value) { - dirty = true; - m_options.insert(key, value); + if (index >= optionKeys.size()) { + if (m_options != *nextOptions) { + m_options = *nextOptions; + emit optionsChanged(); } + return; } - if (dirty) { - emit optionsChanged(); - } - }); + const QString key = optionKeys.at(index); + + m_optionsRefresh = makeRequestJson( + QStringLiteral("getoption ") + key, + [this, generation, nextOptions, step, index, key](bool success, const QJsonDocument& response) + { + m_optionsRefresh.reset(); + + if (generation != m_optionsRefreshGeneration) { + return; + } + + if (success && response.isObject()) { + const QVariant value = parseGetOptionValue(response.object()); + if (value.isValid()) { + insertNestedValue(*nextOptions, key.split(QLatin1Char(':'), Qt::SkipEmptyParts), value); + } else { + qCWarning(lcHypr) << "refreshOptions: getoption returned no usable value for" << key; + } + } else if (!success) { + qCWarning(lcHypr) << "refreshOptions: getoption request error for" << key; + } + + (*step)(index + 1); + }); + }; + + (*step)(0); } void HyprExtras::refreshDevices() { diff --git a/Plugins/ZShell/Internal/hyprextras.hpp b/Plugins/ZShell/Internal/hyprextras.hpp index fb4ba00..afcac8d 100644 --- a/Plugins/ZShell/Internal/hyprextras.hpp +++ b/Plugins/ZShell/Internal/hyprextras.hpp @@ -1,9 +1,13 @@ #pragma once +#include + +#include #include #include #include #include +#include #include namespace ZShell::internal::hypr { @@ -15,13 +19,13 @@ Q_OBJECT QML_ELEMENT Q_MOC_INCLUDE("hyprdevices.hpp") -Q_PROPERTY(QVariantHash options READ options NOTIFY optionsChanged) +Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged) Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT) public: explicit HyprExtras(QObject* parent = nullptr); -[[nodiscard]] QVariantHash options() const; +[[nodiscard]] QVariantMap options() const; [[nodiscard]] HyprDevices* devices() const; Q_INVOKABLE void message(const QString& message); @@ -42,11 +46,12 @@ QString m_eventSocket; QLocalSocket* m_socket; bool m_socketValid; -QVariantHash m_options; +QVariantMap m_options; HyprDevices* const m_devices; SocketPtr m_optionsRefresh; SocketPtr m_devicesRefresh; +quint64 m_optionsRefreshGeneration = 0; void socketError(QLocalSocket::LocalSocketError error) const; void socketStateChanged(QLocalSocket::LocalSocketState state); -- 2.47.3 From 4ea74ed5168bbdfca1b831ab14a15c795e3c7853 Mon Sep 17 00:00:00 2001 From: zach Date: Tue, 26 May 2026 01:25:09 +0200 Subject: [PATCH 03/15] pass monitor scale to screenshot tool --- Helpers/Picker.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Helpers/Picker.qml b/Helpers/Picker.qml index 55686c2..e9a150e 100644 --- a/Helpers/Picker.qml +++ b/Helpers/Picker.qml @@ -66,7 +66,9 @@ MouseArea { function save(): void { const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`); - const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--image"] : ["swappy", "-f"]; + const scale = Hypr.monitorFor(screen).scale; + console.log(scale); + const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", scale, "--image"] : ["swappy", "-f"]; ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path])); closeAnim.start(); } -- 2.47.3 From 6aedf6f8b7cf3d0f98957d57823250aa8089fec8 Mon Sep 17 00:00:00 2001 From: inorishio Date: Tue, 26 May 2026 11:41:31 +0200 Subject: [PATCH 04/15] scale fix --- Config/Config.qml | 1 - Config/Screenshot.qml | 1 - zshell-img-tools/src/config.rs | 1 - zshell-img-tools/src/main.rs | 36 ++++++++-------------------------- 4 files changed, 8 insertions(+), 31 deletions(-) diff --git a/Config/Config.qml b/Config/Config.qml index 6122fd5..66fbe6d 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -293,7 +293,6 @@ Singleton { return { enable_pp: screenshot.enable_pp, mode: screenshot.mode, - scale: screenshot.scale, corner_radius: screenshot.corner_radius, drop_shadow: screenshot.drop_shadow, rounded_corners: screenshot.rounded_corners, diff --git a/Config/Screenshot.qml b/Config/Screenshot.qml index 793e1a8..e250ffc 100644 --- a/Config/Screenshot.qml +++ b/Config/Screenshot.qml @@ -6,7 +6,6 @@ JsonObject { property bool enable_pp: true property string mode: "manual" property bool rounded_corners: false - property real scale: 1.0 property int shadow_blur_passes: 1 property real shadow_blur_radius: 22.0 property list shadow_color: [0, 0, 0, 160] diff --git a/zshell-img-tools/src/config.rs b/zshell-img-tools/src/config.rs index f9bcbb3..a60a521 100644 --- a/zshell-img-tools/src/config.rs +++ b/zshell-img-tools/src/config.rs @@ -14,7 +14,6 @@ pub struct EffectsConfig { pub corner_radius: f32, pub drop_shadow: bool, pub rounded_corners: bool, - pub scale: f32, pub shadow_blur_radius: f32, pub shadow_blur_passes: u32, pub shadow_color: [u8; 4], diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index 12c24d9..82da274 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -10,7 +10,6 @@ struct CliOverrides { rounded_corners: Option, corner_radius: Option, drop_shadow: Option, - scale: Option, shadow_blur_radius: Option, shadow_blur_passes: Option, shadow_offset_x: Option, @@ -56,6 +55,7 @@ fn main() -> Result<()> { let mut image_path: Option = None; let mut overrides = CliOverrides::default(); + let mut scale: Option = None; let mut i = 0; while i < args.len() { @@ -142,7 +142,7 @@ fn main() -> Result<()> { "--scale" => { i += 1; let val = args.get(i).context("Expected a number after --scale")?; - overrides.scale = Some(val.parse::().context("--scale must be a number")?); + scale = Some(val.parse::().context("--scale must be a number")?); } unknown => bail!("Unknown argument: {unknown}"), } @@ -179,16 +179,14 @@ fn main() -> Result<()> { if let Some(v) = overrides.shadow_color { effects.shadow_color = v; } - if let Some(v) = overrides.scale { - effects.scale = v; - } } - if effects.scale != 1.0 { - effects.corner_radius *= effects.scale; - effects.shadow_blur_radius *= effects.scale; - effects.shadow_offset_x *= effects.scale; - effects.shadow_offset_y *= effects.scale; + // if scale is set do + if let Some(scale) = scale.filter(|&s| s != 1.0) { + effects.corner_radius *= scale; + effects.shadow_blur_radius *= scale; + effects.shadow_offset_x *= scale; + effects.shadow_offset_y *= scale; } if let Err(e) = process_image(&image_path, &effects) { @@ -226,23 +224,5 @@ fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> { .context("Failed to write image data to swappy")?; } - // Writes the PNG bytes to swappy's stdin and waits for swappy to close - // child - // .stdin - // .take() - // .context("Failed to get swappy stdin")? - // .write_all(&png_bytes) - // .context("Failed to write image data to swappy")? - // .spawn(); - // - // let status = child.await().context("Failed to wait for swappy")?; - // - // if !status.success() { - // eprintln!( - // "swappy exited with non-zero status for '{}': {}", - // path, status - // ); - // } - Ok(()) } -- 2.47.3 From a3f38e64147d4794801d5b1fb8a1f9b8c61f6983 Mon Sep 17 00:00:00 2001 From: zach Date: Tue, 26 May 2026 16:46:45 +0200 Subject: [PATCH 05/15] fetch necessary hyprland options for screenshot tool --- Helpers/Picker.qml | 9 +++++--- Plugins/ZShell/Internal/hyprextras.cpp | 30 +++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Helpers/Picker.qml b/Helpers/Picker.qml index e9a150e..85372ec 100644 --- a/Helpers/Picker.qml +++ b/Helpers/Picker.qml @@ -34,8 +34,13 @@ MouseArea { property real realRounding: onClient ? (Hypr.options.decoration.rounding ?? 0) : 0 property real rsx: Math.min(sx, ex) property real rsy: Math.min(sy, ey) + readonly property real scaleRatio: Hypr.monitorFor(screen).scale required property ShellScreen screen property real sh: Math.abs(sy - ey) + readonly property color shadowColor: Hypr.options.decoration.shadow.color + readonly property var shadowOffset: Hypr.options.decoration.shadow.offset + readonly property int shadowRange: Hypr.options.decoration.shadow.range + readonly property int shadowRenderPower: Hypr.options.decoration.shadow.render_power property real ssx property real ssy property real sw: Math.abs(sx - ex) @@ -66,9 +71,7 @@ MouseArea { function save(): void { const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`); - const scale = Hypr.monitorFor(screen).scale; - console.log(scale); - const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", scale, "--image"] : ["swappy", "-f"]; + const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, "--shadow-blur-radius", root.shadowRange, "--image"] : ["swappy", "-f"]; ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path])); closeAnim.start(); } diff --git a/Plugins/ZShell/Internal/hyprextras.cpp b/Plugins/ZShell/Internal/hyprextras.cpp index a3056d1..60805d4 100644 --- a/Plugins/ZShell/Internal/hyprextras.cpp +++ b/Plugins/ZShell/Internal/hyprextras.cpp @@ -5,12 +5,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -168,13 +170,30 @@ static QString buildHlConfigCall(const QString& key, const QVariant& value) { return out; } +static QColor colorFromInt(quint32 value) { + const int a = (value >> 24) & 0xFF; + const int r = (value >> 16) & 0xFF; + const int g = (value >> 8) & 0xFF; + const int b = value & 0xFF; + + return QColor(r, g, b, a); +} + static QVariant parseGetOptionValue(const QJsonObject& obj) { if (obj.contains(QStringLiteral("bool"))) { return obj.value(QStringLiteral("bool")).toBool(); } if (obj.contains(QStringLiteral("int"))) { - return obj.value(QStringLiteral("int")).toInt(); + const auto value = obj.value(QStringLiteral("int")).toInt(); + + const auto option = obj.value(QStringLiteral("option")).toString(); + + if (option.contains(QStringLiteral("color")) || option.contains(QStringLiteral("col."))) { + return colorFromInt(static_cast(value)); + } + + return value; } if (obj.contains(QStringLiteral("float"))) { @@ -193,6 +212,10 @@ static QVariant parseGetOptionValue(const QJsonObject& obj) { return obj.value(QStringLiteral("value")).toVariant(); } + if (obj.contains(QStringLiteral("vec2"))) { + return obj.value(QStringLiteral("vec2")).toVariant(); + } + if (obj.contains(QStringLiteral("data"))) { const auto data = obj.value(QStringLiteral("data")); if (data.isObject()) { @@ -340,6 +363,11 @@ void HyprExtras::refreshOptions() { QStringLiteral("general:border_size"), QStringLiteral("decoration:rounding"), QStringLiteral("animations:enabled"), + QStringLiteral("decoration:shadow:enabled"), + QStringLiteral("decoration:shadow:offset"), + QStringLiteral("decoration:shadow:color"), + QStringLiteral("decoration:shadow:range"), + QStringLiteral("decoration:shadow:render_power"), }; auto nextOptions = std::make_shared(); -- 2.47.3 From d098375da65fca423af5a89fc0fd0c9f3246c24a Mon Sep 17 00:00:00 2001 From: inorishio Date: Thu, 28 May 2026 18:55:03 +0200 Subject: [PATCH 06/15] SHOULD have a handler for config/CLI parses --- zshell-img-tools/src/config.rs | 2 - zshell-img-tools/src/effects.rs | 5 +- zshell-img-tools/src/main.rs | 197 ++++++++++++++++++-------------- 3 files changed, 116 insertions(+), 88 deletions(-) diff --git a/zshell-img-tools/src/config.rs b/zshell-img-tools/src/config.rs index a60a521..48e8819 100644 --- a/zshell-img-tools/src/config.rs +++ b/zshell-img-tools/src/config.rs @@ -10,12 +10,10 @@ pub struct Config { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EffectsConfig { - pub mode: String, pub corner_radius: f32, pub drop_shadow: bool, pub rounded_corners: bool, pub shadow_blur_radius: f32, - pub shadow_blur_passes: u32, pub shadow_color: [u8; 4], pub shadow_offset_x: f32, pub shadow_offset_y: f32, diff --git a/zshell-img-tools/src/effects.rs b/zshell-img-tools/src/effects.rs index fcdd236..7124a9f 100644 --- a/zshell-img-tools/src/effects.rs +++ b/zshell-img-tools/src/effects.rs @@ -16,7 +16,6 @@ pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage { cfg.shadow_blur_radius, cfg.shadow_offset_x, cfg.shadow_offset_y, - cfg.shadow_blur_passes, cfg.shadow_color, ) } else { @@ -53,12 +52,12 @@ pub fn apply_drop_shadow( blur_radius: f32, offset_x: f32, offset_y: f32, - blur_passes: u32, + // blur_passes: u32, shadow_color: [u8; 4], ) -> RgbaImage { let (iw, ih) = img.dimensions(); let br = blur_radius.ceil() as u32; - let bp = blur_passes; + let bp = 1; // Original idea // let spread = br * bp; // Claude is hallucinating but let's try it **Worked btw** diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index 82da274..d255147 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -11,7 +11,6 @@ struct CliOverrides { corner_radius: Option, drop_shadow: Option, shadow_blur_radius: Option, - shadow_blur_passes: Option, shadow_offset_x: Option, shadow_offset_y: Option, // Accepted as four comma-separated u8 values, e.g. `255,0,0,200` @@ -50,7 +49,24 @@ fn parse_shadow_color(s: &str) -> Result<[u8; 4]> { Ok([r, g, b, a]) } -fn main() -> Result<()> { +fn extract_image_path() -> Option { + let args: Vec = std::env::args().skip(1).collect(); + args.windows(2) + .find(|w| w[0] == "--image") + .map(|w| w[1].clone()) +} + +fn main() { + let image_path = extract_image_path(); + if let Some(path) = image_path { + if let Err(e) = run() { + eprintln!("Error: {}", e); + push_image(&path).ok(); + } + } +} + +fn run() -> Result<()> { let args: Vec = std::env::args().skip(1).collect(); let mut image_path: Option = None; @@ -61,125 +77,97 @@ fn main() -> Result<()> { while i < args.len() { match args[i].as_str() { "--image" => { - i += 1; - image_path = Some( - args.get(i) - .cloned() - .context("Expected a path after --image")?, - ); + image_path = Some(next_arg(&args, &mut i, "--image")?); } - "--rounded-corners" => { - i += 1; - let val = args - .get(i) - .context("Expected true/false after --rounded-corners")?; - overrides.rounded_corners = Some(parse_bool(val)?); + "--corner" => { + let val = next_arg(&args, &mut i, "--corners")?; + overrides.rounded_corners = Some(parse_bool(&val)?); } "--corner-radius" => { - i += 1; - let val = args - .get(i) - .context("Expected a number after --corner-radius")?; + let val = next_arg(&args, &mut i, "--corner-radius")?; overrides.corner_radius = Some( val.parse::() .context("--corner-radius must be a number")?, ); } - "--drop-shadow" => { - i += 1; - let val = args - .get(i) - .context("Expected true/false after --drop-shadow")?; - overrides.drop_shadow = Some(parse_bool(val)?); + "--shadow" => { + let val = next_arg(&args, &mut i, "--shadow")?; + overrides.drop_shadow = Some(parse_bool(&val)?); } - "--shadow-blur-radius" => { - i += 1; - let val = args - .get(i) - .context("Expected a number after --shadow-blur-radius")?; + "--shadow-blur" => { + let val = next_arg(&args, &mut i, "--shadow-blur")?; overrides.shadow_blur_radius = Some( val.parse::() .context("--shadow-blur-radius must be a number")?, ); } "--shadow-offset-x" => { - i += 1; - let val = args - .get(i) - .context("Expected a number after --shadow-offset-x")?; + let val = next_arg(&args, &mut i, "--shadow-offset-x")?; overrides.shadow_offset_x = Some( val.parse::() .context("--shadow-offset-x must be a number")?, ); } "--shadow-offset-y" => { - i += 1; - let val = args - .get(i) - .context("Expected a number after --shadow-offset-y")?; + let val = next_arg(&args, &mut i, "--shadow-offset-y")?; overrides.shadow_offset_y = Some( val.parse::() .context("--shadow-offset-y must be a number")?, ); } - "--shadow-blur-passes" => { - i += 1; - let val = args - .get(i) - .context("Expected a number after --shadow-blur-passes")?; - overrides.shadow_blur_passes = Some( - val.parse::() - .context("--shadow-blur-passes must be a number")?, - ); - } "--shadow-color" => { - i += 1; - let val = args - .get(i) - .context("Expected r,g,b,a after --shadow-color")?; - overrides.shadow_color = Some(parse_shadow_color(val)?); + let val = next_arg(&args, &mut i, "--shadow-color")?; + overrides.shadow_color = Some(parse_shadow_color(&val)?); } "--scale" => { - i += 1; - let val = args.get(i).context("Expected a number after --scale")?; + let val = next_arg(&args, &mut i, "--scale")?; scale = Some(val.parse::().context("--scale must be a number")?); } - unknown => bail!("Unknown argument: {unknown}"), + unknown => bail!("Unknown argument: {}", unknown), } + i += 1; } let image_path = image_path.context("Missing --image ")?; - let config = config::Config::load().context("Failed to load config")?; - - let mut effects = config.screenshot; - if effects.mode == "auto" { - if let Some(v) = overrides.rounded_corners { - effects.rounded_corners = v; + // Check if any arguments were provided + let cli_args_provided = overrides.rounded_corners.is_some() + || overrides.corner_radius.is_some() + || overrides.drop_shadow.is_some() + || overrides.shadow_blur_radius.is_some() + || overrides.shadow_offset_x.is_some() + || overrides.shadow_offset_y.is_some() + || overrides.shadow_color.is_some(); + let mut effects = if cli_args_provided { + // If all args provided + let rounded_corners = overrides.rounded_corners.context("Missing --corner")?; + let corner_radius = overrides.corner_radius.context("Missing --corner-radius")?; + let drop_shadow = overrides.drop_shadow.context("Missing --shadow")?; + let shadow_blur_radius = overrides + .shadow_blur_radius + .context("Missing --shadow-blur")?; + let shadow_offset_x = overrides + .shadow_offset_x + .context("Missing --shadow-offset-x")?; + let shadow_offset_y = overrides + .shadow_offset_y + .context("Missing --shadow-offset-y")?; + let shadow_color = overrides.shadow_color.context("Missing --shadow-color")?; + config::EffectsConfig { + rounded_corners, + corner_radius, + drop_shadow, + shadow_blur_radius, + shadow_offset_x, + shadow_offset_y, + shadow_color, } - if let Some(v) = overrides.corner_radius { - effects.corner_radius = v; - } - if let Some(v) = overrides.drop_shadow { - effects.drop_shadow = v; - } - if let Some(v) = overrides.shadow_blur_radius { - effects.shadow_blur_radius = v; - } - if let Some(v) = overrides.shadow_offset_x { - effects.shadow_offset_x = v; - } - if let Some(v) = overrides.shadow_offset_y { - effects.shadow_offset_y = v; - } - if let Some(v) = overrides.shadow_blur_passes { - effects.shadow_blur_passes = v; - } - if let Some(v) = overrides.shadow_color { - effects.shadow_color = v; - } - } + } else { + // If not all args were provided use config file + let config = config::Config::load()?; + config.screenshot + }; // if scale is set do if let Some(scale) = scale.filter(|&s| s != 1.0) { @@ -196,6 +184,49 @@ fn main() -> Result<()> { Ok(()) } +fn next_arg(args: &[String], i: &mut usize, flag: &str) -> Result { + *i += 1; + + let val = args + .get(*i) + .context(format!("Expected value after {}", flag))?; + + if val.starts_with('-') { + bail!("Expected value after {}, found flag {}", flag, val); + } + + Ok(val.clone()) +} + +fn push_image(path: &str) -> Result<()> { + let img = image::open(path) + .with_context(|| format!("Failed to open image '{path}'"))? + .into_rgba8(); + + let mut png_bytes: Vec = Vec::new(); + image::DynamicImage::ImageRgba8(img) + .write_to( + &mut std::io::Cursor::new(&mut png_bytes), + image::ImageFormat::Png, + ) + .context("Failed to encode processed image as PNG")?; + + let mut child = Command::new("swappy") + .args(["-f", "-"]) + .stdin(Stdio::piped()) + .spawn() + .context("Failed to spawn swappy. Is it installed and in PATH?")?; + + // Writes the PNG bytes to swappy's stdin and then closes + if let Some(mut stdin) = child.stdin.take() { + stdin + .write_all(&png_bytes) + .context("Failed to write image data to swappy")?; + } + + Ok(()) +} + fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> { let img = image::open(path) .with_context(|| format!("Failed to open image '{path}'"))? -- 2.47.3 From 0305f5a2be80d42d7ccbfb8d08b66ebb173d9a12 Mon Sep 17 00:00:00 2001 From: inorishio Date: Thu, 28 May 2026 19:11:47 +0200 Subject: [PATCH 07/15] --corners > --corner --- zshell-img-tools/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index d255147..2d38575 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -80,7 +80,7 @@ fn run() -> Result<()> { image_path = Some(next_arg(&args, &mut i, "--image")?); } "--corner" => { - let val = next_arg(&args, &mut i, "--corners")?; + let val = next_arg(&args, &mut i, "--corner")?; overrides.rounded_corners = Some(parse_bool(&val)?); } "--corner-radius" => { -- 2.47.3 From 9a026b2484edb8d6bf2338f23f492093c034714e Mon Sep 17 00:00:00 2001 From: zach Date: Thu, 28 May 2026 20:21:22 +0200 Subject: [PATCH 08/15] respect new arguments --- Config/Config.qml | 9 ++-- Config/Screenshot.qml | 9 ++-- Modules/Settings/Categories/Screenshot.qml | 56 +++++++++++----------- scripts/SettingsIndex.mjs | 18 +++---- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/Config/Config.qml b/Config/Config.qml index e1a6d6d..272e6b2 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -300,11 +300,10 @@ Singleton { return { enable_pp: screenshot.enable_pp, mode: screenshot.mode, - corner_radius: screenshot.corner_radius, - drop_shadow: screenshot.drop_shadow, - rounded_corners: screenshot.rounded_corners, - shadow_blur_radius: screenshot.shadow_blur_radius, - shadow_blur_passes: screenshot.shadow_blur_passes, + radius: screenshot.radius, + shadow: screenshot.shadow, + rounding: screenshot.rounding, + shadow_blur: screenshot.shadow_blur, shadow_color: screenshot.shadow_color, shadow_offset_x: screenshot.shadow_offset_x, shadow_offset_y: screenshot.shadow_offset_y diff --git a/Config/Screenshot.qml b/Config/Screenshot.qml index e250ffc..cd6dd49 100644 --- a/Config/Screenshot.qml +++ b/Config/Screenshot.qml @@ -1,13 +1,12 @@ import Quickshell.Io JsonObject { - property real corner_radius: 12.0 - property bool drop_shadow: true property bool enable_pp: true property string mode: "manual" - property bool rounded_corners: false - property int shadow_blur_passes: 1 - property real shadow_blur_radius: 22.0 + property real radius: 12.0 + property bool rounding: false + property bool shadow: true + property real shadow_blur: 22.0 property list shadow_color: [0, 0, 0, 160] property real shadow_offset_x: 5.0 property real shadow_offset_y: 5.0 diff --git a/Modules/Settings/Categories/Screenshot.qml b/Modules/Settings/Categories/Screenshot.qml index 260e7f9..21757d9 100644 --- a/Modules/Settings/Categories/Screenshot.qml +++ b/Modules/Settings/Categories/Screenshot.qml @@ -50,7 +50,7 @@ SettingsPage { min: 0 name: "Corner radius" object: Config.screenshot - setting: "corner_radius" + setting: "radius" shouldBeActive: Config.screenshot.mode === "manual" step: 1 } @@ -60,9 +60,9 @@ SettingsPage { } SettingSwitch { - name: "Enable drop shadow" + name: "Enable shadow" object: Config.screenshot - setting: "drop_shadow" + setting: "shadow" shouldBeActive: Config.screenshot.mode === "manual" } @@ -73,7 +73,7 @@ SettingsPage { SettingSwitch { name: "Enable rounded corners" object: Config.screenshot - setting: "rounded_corners" + setting: "rounding" shouldBeActive: Config.screenshot.mode === "manual" } @@ -83,9 +83,9 @@ SettingsPage { SettingSpinBox { min: 0 - name: "Shadow blur radius" + name: "Shadow blur amount" object: Config.screenshot - setting: "shadow_blur_radius" + setting: "shadow_blur" shouldBeActive: Config.screenshot.mode === "manual" step: 1 } @@ -94,29 +94,29 @@ SettingsPage { shouldBeActive: Config.screenshot.mode === "manual" } - SettingSwitch { - name: "Shadow color broken atm" - object: Config.Screenshot - setting: "shadow_color" - shouldBeActive: Config.screenshot.mode === "manual" - } + // SettingSwitch { + // name: "Shadow color broken atm" + // object: Config.Screenshot + // setting: "shadow_color" + // shouldBeActive: Config.screenshot.mode === "manual" + // } + // + // Separator { + // shouldBeActive: Config.screenshot.mode === "manual" + // } - Separator { - shouldBeActive: Config.screenshot.mode === "manual" - } - - SettingSpinBox { - min: 1 - name: "Shadow passes" - object: Config.screenshot - setting: "shadow_blur_passes" - shouldBeActive: Config.screenshot.mode === "manual" - step: 1 - } - - Separator { - shouldBeActive: Config.screenshot.mode === "manual" - } + // SettingSpinBox { + // min: 1 + // name: "Shadow passes" + // object: Config.screenshot + // setting: "shadow_blur_passes" + // shouldBeActive: Config.screenshot.mode === "manual" + // step: 1 + // } + // + // Separator { + // shouldBeActive: Config.screenshot.mode === "manual" + // } SettingSpinBox { min: 0 diff --git a/scripts/SettingsIndex.mjs b/scripts/SettingsIndex.mjs index d5aec75..ba5eaed 100644 --- a/scripts/SettingsIndex.mjs +++ b/scripts/SettingsIndex.mjs @@ -1011,7 +1011,7 @@ export const settingsIndex = [ keywords: ["corner", "radius"], }, { - name: "Enable drop shadow", + name: "Enable shadow", category: "screenshot", categoryName: "Screenshot", section: "Screenshot", @@ -1025,19 +1025,19 @@ export const settingsIndex = [ keywords: ["rounded", "corners"], }, { - name: "Shadow blur radius", + name: "Shadow blur amount", category: "screenshot", categoryName: "Screenshot", section: "Screenshot", keywords: ["blur", "shadow", "radius"], }, - { - name: "Shadow color", - category: "screenshot", - categoryName: "Screenshot", - section: "Screenshot", - keywords: ["color", "shadow"], - }, + // { + // name: "Shadow color", + // category: "screenshot", + // categoryName: "Screenshot", + // section: "Screenshot", + // keywords: ["color", "shadow"], + // }, { name: "Shadow offset X", category: "screenshot", -- 2.47.3 From d0a3a0a2691443ec2c5e7de260acd72fcec47bb4 Mon Sep 17 00:00:00 2001 From: inorishio Date: Thu, 28 May 2026 20:23:28 +0200 Subject: [PATCH 09/15] updated --- zshell-img-tools/src/config.rs | 8 ++-- zshell-img-tools/src/effects.rs | 18 ++++----- zshell-img-tools/src/main.rs | 68 +++++++++++++++------------------ 3 files changed, 43 insertions(+), 51 deletions(-) diff --git a/zshell-img-tools/src/config.rs b/zshell-img-tools/src/config.rs index 48e8819..a6eddda 100644 --- a/zshell-img-tools/src/config.rs +++ b/zshell-img-tools/src/config.rs @@ -10,10 +10,10 @@ pub struct Config { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EffectsConfig { - pub corner_radius: f32, - pub drop_shadow: bool, - pub rounded_corners: bool, - pub shadow_blur_radius: f32, + pub radius: f32, + pub shadow: bool, + pub rounding: bool, + pub shadow_blur: f32, pub shadow_color: [u8; 4], pub shadow_offset_x: f32, pub shadow_offset_y: f32, diff --git a/zshell-img-tools/src/effects.rs b/zshell-img-tools/src/effects.rs index 7124a9f..fab36ab 100644 --- a/zshell-img-tools/src/effects.rs +++ b/zshell-img-tools/src/effects.rs @@ -5,15 +5,15 @@ use tiny_skia::{ }; pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage { - let img = if cfg.rounded_corners { - apply_rounded_corners(img, cfg.corner_radius) + let img = if cfg.rounding { + apply_rounding(img, cfg.radius) } else { img }; - if cfg.drop_shadow { - apply_drop_shadow( + if cfg.shadow { + apply_shadow( img, - cfg.shadow_blur_radius, + cfg.shadow_blur, cfg.shadow_offset_x, cfg.shadow_offset_y, cfg.shadow_color, @@ -23,7 +23,7 @@ pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage { } } -pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage { +pub fn apply_rounding(img: RgbaImage, radius: f32) -> RgbaImage { let (w, h) = img.dimensions(); let mut mask = Pixmap::new(w, h).expect("mask pixmap"); let path = rounded_rect_path(0.0, 0.0, w as f32, h as f32, radius); @@ -47,16 +47,16 @@ pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage { pixmap_to_rgba_image(pixmap) } -pub fn apply_drop_shadow( +pub fn apply_shadow( img: RgbaImage, - blur_radius: f32, + blur: f32, offset_x: f32, offset_y: f32, // blur_passes: u32, shadow_color: [u8; 4], ) -> RgbaImage { let (iw, ih) = img.dimensions(); - let br = blur_radius.ceil() as u32; + let br = blur.ceil() as u32; let bp = 1; // Original idea // let spread = br * bp; diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index 2d38575..4071565 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -7,10 +7,10 @@ use std::process::{Command, Stdio}; #[derive(Default)] struct CliOverrides { - rounded_corners: Option, - corner_radius: Option, - drop_shadow: Option, - shadow_blur_radius: Option, + rounding: Option, + radius: Option, + shadow: Option, + shadow_blur: Option, shadow_offset_x: Option, shadow_offset_y: Option, // Accepted as four comma-separated u8 values, e.g. `255,0,0,200` @@ -57,12 +57,9 @@ fn extract_image_path() -> Option { } fn main() { - let image_path = extract_image_path(); - if let Some(path) = image_path { - if let Err(e) = run() { - eprintln!("Error: {}", e); - push_image(&path).ok(); - } + if let Some(path) = extract_image_path() && let Err(e) = run() { + eprintln!("Error: {}", e); + push_image(&path).ok(); } } @@ -79,26 +76,23 @@ fn run() -> Result<()> { "--image" => { image_path = Some(next_arg(&args, &mut i, "--image")?); } - "--corner" => { - let val = next_arg(&args, &mut i, "--corner")?; - overrides.rounded_corners = Some(parse_bool(&val)?); + "--rounding" => { + let val = next_arg(&args, &mut i, "--rounding")?; + overrides.rounding = Some(parse_bool(&val)?); } - "--corner-radius" => { - let val = next_arg(&args, &mut i, "--corner-radius")?; - overrides.corner_radius = Some( - val.parse::() - .context("--corner-radius must be a number")?, - ); + "--radius" => { + let val = next_arg(&args, &mut i, "--radius")?; + overrides.radius = Some(val.parse::().context("--radius must be a number")?); } "--shadow" => { let val = next_arg(&args, &mut i, "--shadow")?; - overrides.drop_shadow = Some(parse_bool(&val)?); + overrides.shadow = Some(parse_bool(&val)?); } "--shadow-blur" => { let val = next_arg(&args, &mut i, "--shadow-blur")?; - overrides.shadow_blur_radius = Some( + overrides.shadow_blur = Some( val.parse::() - .context("--shadow-blur-radius must be a number")?, + .context("--shadow-blur must be a number")?, ); } "--shadow-offset-x" => { @@ -132,21 +126,19 @@ fn run() -> Result<()> { let image_path = image_path.context("Missing --image ")?; // Check if any arguments were provided - let cli_args_provided = overrides.rounded_corners.is_some() - || overrides.corner_radius.is_some() - || overrides.drop_shadow.is_some() - || overrides.shadow_blur_radius.is_some() + let cli_args_provided = overrides.rounding.is_some() + || overrides.radius.is_some() + || overrides.shadow.is_some() + || overrides.shadow_blur.is_some() || overrides.shadow_offset_x.is_some() || overrides.shadow_offset_y.is_some() || overrides.shadow_color.is_some(); let mut effects = if cli_args_provided { // If all args provided - let rounded_corners = overrides.rounded_corners.context("Missing --corner")?; - let corner_radius = overrides.corner_radius.context("Missing --corner-radius")?; - let drop_shadow = overrides.drop_shadow.context("Missing --shadow")?; - let shadow_blur_radius = overrides - .shadow_blur_radius - .context("Missing --shadow-blur")?; + let rounding = overrides.rounding.context("Missing --rounding")?; + let radius = overrides.radius.context("Missing --radius")?; + let shadow = overrides.shadow.context("Missing --shadow")?; + let shadow_blur = overrides.shadow_blur.context("Missing --shadow-blur")?; let shadow_offset_x = overrides .shadow_offset_x .context("Missing --shadow-offset-x")?; @@ -155,10 +147,10 @@ fn run() -> Result<()> { .context("Missing --shadow-offset-y")?; let shadow_color = overrides.shadow_color.context("Missing --shadow-color")?; config::EffectsConfig { - rounded_corners, - corner_radius, - drop_shadow, - shadow_blur_radius, + rounding, + radius, + shadow, + shadow_blur, shadow_offset_x, shadow_offset_y, shadow_color, @@ -171,8 +163,8 @@ fn run() -> Result<()> { // if scale is set do if let Some(scale) = scale.filter(|&s| s != 1.0) { - effects.corner_radius *= scale; - effects.shadow_blur_radius *= scale; + effects.radius *= scale; + effects.shadow_blur *= scale; effects.shadow_offset_x *= scale; effects.shadow_offset_y *= scale; } -- 2.47.3 From 760b8bfb9384d2404a9d459fbd3c302ace4ae2c0 Mon Sep 17 00:00:00 2001 From: zach Date: Thu, 28 May 2026 23:16:04 +0200 Subject: [PATCH 10/15] pass hyprland options when auto is enabled --- Helpers/Picker.qml | 11 ++++++++++- Modules/Settings/Categories/Screenshot.qml | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Helpers/Picker.qml b/Helpers/Picker.qml index 85372ec..c0188d5 100644 --- a/Helpers/Picker.qml +++ b/Helpers/Picker.qml @@ -26,6 +26,7 @@ MouseArea { return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating); }); } + readonly property int cornerRadius: Hypr.options.decoration.rounding property real ex: screen.width property real ey: screen.height required property LazyLoader loader @@ -38,6 +39,7 @@ MouseArea { required property ShellScreen screen property real sh: Math.abs(sy - ey) readonly property color shadowColor: Hypr.options.decoration.shadow.color + readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled readonly property var shadowOffset: Hypr.options.decoration.shadow.offset readonly property int shadowRange: Hypr.options.decoration.shadow.range readonly property int shadowRenderPower: Hypr.options.decoration.shadow.render_power @@ -71,7 +73,14 @@ MouseArea { function save(): void { const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`); - const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, "--shadow-blur-radius", root.shadowRange, "--image"] : ["swappy", "-f"]; + const rounding = root.cornerRadius > 0; + const shadow_blur = root.shadowRange / root.shadowRenderPower; + const r = Math.floor(root.shadowColor.r * 256); + const g = Math.floor(root.shadowColor.b * 256); + const b = Math.floor(root.shadowColor.g * 256); + const a = Math.floor(root.shadowColor.a * 256); + const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : []; + const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : ["swappy", "-f"]; ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path])); closeAnim.start(); } diff --git a/Modules/Settings/Categories/Screenshot.qml b/Modules/Settings/Categories/Screenshot.qml index 21757d9..e3fa032 100644 --- a/Modules/Settings/Categories/Screenshot.qml +++ b/Modules/Settings/Categories/Screenshot.qml @@ -20,7 +20,7 @@ SettingsPage { } CustomSplitButtonRow { - // active: true + active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1] label: qsTr("Effects mode") menuItems: [ -- 2.47.3 From 1d667bedfd0b4de01b4f99bb5f67d0ae0f46e698 Mon Sep 17 00:00:00 2001 From: zach Date: Thu, 28 May 2026 23:19:57 +0200 Subject: [PATCH 11/15] incorrect color channel assignment fixed --- Helpers/Picker.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Helpers/Picker.qml b/Helpers/Picker.qml index c0188d5..f48951f 100644 --- a/Helpers/Picker.qml +++ b/Helpers/Picker.qml @@ -76,8 +76,8 @@ MouseArea { const rounding = root.cornerRadius > 0; const shadow_blur = root.shadowRange / root.shadowRenderPower; const r = Math.floor(root.shadowColor.r * 256); - const g = Math.floor(root.shadowColor.b * 256); - const b = Math.floor(root.shadowColor.g * 256); + const g = Math.floor(root.shadowColor.g * 256); + const b = Math.floor(root.shadowColor.b * 256); const a = Math.floor(root.shadowColor.a * 256); const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : []; const cmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : ["swappy", "-f"]; -- 2.47.3 From 164c0a18c2d1e203e7c4462224214c09d6b1de16 Mon Sep 17 00:00:00 2001 From: zach Date: Fri, 29 May 2026 23:44:27 +0200 Subject: [PATCH 12/15] disable screenshot options in gui when disabling effects --- Components/CustomSpinBox.qml | 11 ++-- Components/CustomSplitButton.qml | 18 +++--- Components/CustomSwitch.qml | 8 +-- Modules/Settings/Categories/General.qml | 4 +- Modules/Settings/Categories/Screenshot.qml | 69 ++++++++++++---------- 5 files changed, 57 insertions(+), 53 deletions(-) diff --git a/Components/CustomSpinBox.qml b/Components/CustomSpinBox.qml index 5a4245b..72479c8 100644 --- a/Components/CustomSpinBox.qml +++ b/Components/CustomSpinBox.qml @@ -28,6 +28,7 @@ RowLayout { CustomTextField { id: textField + color: root.enabled ? DynamicColors.palette.m3onSurface : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5) implicitHeight: upButton.implicitHeight inputMethodHints: Qt.ImhFormattedNumbersOnly leftPadding: Appearance.padding.normal @@ -36,7 +37,7 @@ RowLayout { text: root.isEditing ? text : root.displayText background: CustomRect { - color: DynamicColors.tPalette.m3surfaceContainerHigh + color: root.enabled ? DynamicColors.tPalette.m3surfaceContainerHigh : DynamicColors.tPalette.m3surfaceContainerLow implicitWidth: 100 radius: Appearance.rounding.full } @@ -85,7 +86,7 @@ RowLayout { CustomRect { id: upButton - color: DynamicColors.palette.m3primary + color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 implicitWidth: implicitHeight radius: Appearance.rounding.full @@ -113,13 +114,13 @@ RowLayout { id: upIcon anchors.centerIn: parent - color: DynamicColors.palette.m3onPrimary + color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5) text: "keyboard_arrow_up" } } CustomRect { - color: DynamicColors.palette.m3primary + color: root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 implicitWidth: implicitHeight radius: Appearance.rounding.full @@ -147,7 +148,7 @@ RowLayout { id: downIcon anchors.centerIn: parent - color: DynamicColors.palette.m3onPrimary + color: root.enabled ? DynamicColors.palette.m3onPrimary : Qt.alpha(DynamicColors.palette.m3onSurface, 0.5) text: "keyboard_arrow_down" } } diff --git a/Components/CustomSplitButton.qml b/Components/CustomSplitButton.qml index 6f9ad95..3621a71 100644 --- a/Components/CustomSplitButton.qml +++ b/Components/CustomSplitButton.qml @@ -35,14 +35,10 @@ Row { } function openDropdown(): void { - if (root.disabled) - return; SettingsDropdowns.open(menu, root); } function toggleDropdown(): void { - if (root.disabled) - return; SettingsDropdowns.toggle(menu, root); } @@ -55,7 +51,7 @@ Row { CustomRect { bottomRightRadius: Appearance.rounding.small / 2 - color: root.disabled ? root.disabledColor : root.color + color: !root.enabled ? root.disabledColor : root.color implicitHeight: expandBtn.implicitHeight implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2 radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) @@ -69,7 +65,7 @@ Row { } color: root.textColor - disabled: root.disabled + disabled: !root.enabled rect.bottomRightRadius: parent.bottomRightRadius rect.topRightRadius: parent.topRightRadius } @@ -86,7 +82,7 @@ Row { Layout.alignment: Qt.AlignVCenter animate: true - color: root.disabled ? root.disabledTextColor : root.textColor + color: !root.enabled ? root.disabledTextColor : root.textColor fill: 1 text: root.active?.activeIcon ?? root.fallbackIcon } @@ -98,7 +94,7 @@ Row { Layout.preferredWidth: implicitWidth animate: true clip: true - color: root.disabled ? root.disabledTextColor : root.textColor + color: !root.enabled ? root.disabledTextColor : root.textColor text: root.active?.activeText ?? root.fallbackText Behavior on Layout.preferredWidth { @@ -116,7 +112,7 @@ Row { property real rad: root.expanded ? implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) : Appearance.rounding.small / 2 bottomLeftRadius: rad - color: root.disabled ? root.disabledColor : root.color + color: !root.enabled ? root.disabledColor : root.color implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2 implicitWidth: implicitHeight radius: implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) @@ -135,7 +131,7 @@ Row { } color: root.textColor - disabled: root.disabled + disabled: !root.enabled rect.bottomLeftRadius: parent.bottomLeftRadius rect.topLeftRadius: parent.topLeftRadius } @@ -145,7 +141,7 @@ Row { anchors.centerIn: parent anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4) - color: root.disabled ? root.disabledTextColor : root.textColor + color: !root.enabled ? root.disabledTextColor : root.textColor rotation: root.expanded ? 180 : 0 text: "expand_more" diff --git a/Components/CustomSwitch.qml b/Components/CustomSwitch.qml index aa6e069..45f4246 100644 --- a/Components/CustomSwitch.qml +++ b/Components/CustomSwitch.qml @@ -12,7 +12,7 @@ Switch { implicitWidth: implicitIndicatorWidth indicator: CustomRect { - color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) + color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, root.cLayer) implicitHeight: 13 + 7 * 2 implicitWidth: implicitHeight * 1.7 radius: Appearance.rounding.full @@ -21,7 +21,7 @@ Switch { readonly property real nonAnimWidth: root.pressed ? implicitHeight * 1.3 : implicitHeight anchors.verticalCenter: parent.verticalCenter - color: root.checked ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1) + color: root.checked && root.enabled ? DynamicColors.palette.m3onPrimary : DynamicColors.layer(DynamicColors.palette.m3outline, root.cLayer + 1) implicitHeight: parent.implicitHeight - 10 implicitWidth: nonAnimWidth radius: Appearance.rounding.full @@ -38,7 +38,7 @@ Switch { CustomRect { anchors.fill: parent - color: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface + color: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface opacity: root.pressed ? 0.1 : root.hovered ? 0.08 : 0 radius: parent.radius @@ -114,7 +114,7 @@ Switch { fillColor: "transparent" startX: icon.start1.x startY: icon.start1.y - strokeColor: root.checked ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest + strokeColor: root.checked && root.enabled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3surfaceContainerHighest strokeWidth: Appearance.font.size.larger * 0.15 Behavior on strokeColor { diff --git a/Modules/Settings/Categories/General.qml b/Modules/Settings/Categories/General.qml index 3d7eaba..59ba5d2 100644 --- a/Modules/Settings/Categories/General.qml +++ b/Modules/Settings/Categories/General.qml @@ -69,7 +69,7 @@ SettingsPage { CustomSplitButtonRow { active: Config.general.color.mode === "light" ? menuItems[0] : menuItems[1] - buttonAlias.disabled: !Config.general.color.schemeGeneration + enabled: Config.general.color.schemeGeneration label: qsTr("Scheme mode") menuItems: [ @@ -103,7 +103,7 @@ SettingsPage { id: schemeType active: root.schemeTypeItem(menuItems, Config.colors.schemeType) - buttonAlias.disabled: !Config.general.color.schemeGeneration + enabled: Config.general.color.schemeGeneration label: qsTr("Scheme type") z: 2 diff --git a/Modules/Settings/Categories/Screenshot.qml b/Modules/Settings/Categories/Screenshot.qml index e3fa032..10528a5 100644 --- a/Modules/Settings/Categories/Screenshot.qml +++ b/Modules/Settings/Categories/Screenshot.qml @@ -21,6 +21,7 @@ SettingsPage { CustomSplitButtonRow { active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1] + enabled: Config.screenshot.enable_pp label: qsTr("Effects mode") menuItems: [ @@ -46,31 +47,8 @@ SettingsPage { shouldBeActive: Config.screenshot.mode === "manual" } - SettingSpinBox { - min: 0 - name: "Corner radius" - object: Config.screenshot - setting: "radius" - shouldBeActive: Config.screenshot.mode === "manual" - step: 1 - } - - Separator { - shouldBeActive: Config.screenshot.mode === "manual" - } - - SettingSwitch { - name: "Enable shadow" - object: Config.screenshot - setting: "shadow" - shouldBeActive: Config.screenshot.mode === "manual" - } - - Separator { - shouldBeActive: Config.screenshot.mode === "manual" - } - SettingSwitch { + enabled: Config.screenshot.enable_pp name: "Enable rounded corners" object: Config.screenshot setting: "rounding" @@ -78,15 +56,16 @@ SettingsPage { } Separator { - shouldBeActive: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding } SettingSpinBox { + enabled: Config.screenshot.enable_pp min: 0 - name: "Shadow blur amount" + name: "Corner radius" object: Config.screenshot - setting: "shadow_blur" - shouldBeActive: Config.screenshot.mode === "manual" + setting: "radius" + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.rounding step: 1 } @@ -94,6 +73,32 @@ SettingsPage { shouldBeActive: Config.screenshot.mode === "manual" } + SettingSwitch { + enabled: Config.screenshot.enable_pp + name: "Enable shadow" + object: Config.screenshot + setting: "shadow" + shouldBeActive: Config.screenshot.mode === "manual" + } + + Separator { + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow + } + + SettingSpinBox { + enabled: Config.screenshot.enable_pp + min: 0 + name: "Shadow blur amount" + object: Config.screenshot + setting: "shadow_blur" + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow + step: 1 + } + + Separator { + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow + } + // SettingSwitch { // name: "Shadow color broken atm" // object: Config.Screenshot @@ -119,24 +124,26 @@ SettingsPage { // } SettingSpinBox { + enabled: Config.screenshot.enable_pp min: 0 name: "Shadow offset X" object: Config.screenshot setting: "shadow_offset_x" - shouldBeActive: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow step: 1 } Separator { - shouldBeActive: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow } SettingSpinBox { + enabled: Config.screenshot.enable_pp min: 0 name: "Shadow offset Y" object: Config.screenshot setting: "shadow_offset_y" - shouldBeActive: Config.screenshot.mode === "manual" + shouldBeActive: Config.screenshot.mode === "manual" && Config.screenshot.shadow step: 1 } } -- 2.47.3 From 8ea295d1ce1d24b7da61ea823d40c50b146362d9 Mon Sep 17 00:00:00 2001 From: inorishio Date: Sat, 30 May 2026 20:40:14 +0200 Subject: [PATCH 13/15] Some prework for the binary, linter issue is resolved, unnecessary comments removed --- zshell-img-tools/.rustfmt.toml | 2 ++ zshell-img-tools/src/effects.rs | 7 ------- zshell-img-tools/src/main.rs | 31 +++++++++++++++++++++++++++---- 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 zshell-img-tools/.rustfmt.toml diff --git a/zshell-img-tools/.rustfmt.toml b/zshell-img-tools/.rustfmt.toml new file mode 100644 index 0000000..f3e454b --- /dev/null +++ b/zshell-img-tools/.rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2024" +style_edition = "2024" diff --git a/zshell-img-tools/src/effects.rs b/zshell-img-tools/src/effects.rs index fab36ab..39fa7a9 100644 --- a/zshell-img-tools/src/effects.rs +++ b/zshell-img-tools/src/effects.rs @@ -52,15 +52,11 @@ pub fn apply_shadow( blur: f32, offset_x: f32, offset_y: f32, - // blur_passes: u32, shadow_color: [u8; 4], ) -> RgbaImage { let (iw, ih) = img.dimensions(); let br = blur.ceil() as u32; let bp = 1; - // Original idea - // let spread = br * bp; - // Claude is hallucinating but let's try it **Worked btw** let spread = (br as f32 * (bp as f32).sqrt() * 2.0).ceil() as u32; let extra_left = spread + (-offset_x).max(0.0).ceil() as u32; @@ -91,11 +87,8 @@ pub fn apply_shadow( tint_pixmap_as_shadow(&mut shadow_pixmap, shadow_color); - // Shadow let shadow_img = pixmap_to_rgba_image(shadow_pixmap); - // Shadow blur let blurred = box_blur_rgba(&shadow_img, br, bp); - // Shadow pos let blurred_pixmap = rgba_image_to_pixmap(&blurred); let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap"); diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index 4071565..dfd0fc3 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -1,7 +1,7 @@ mod config; mod effects; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result, bail}; use std::io::Write as _; use std::process::{Command, Stdio}; @@ -57,7 +57,11 @@ fn extract_image_path() -> Option { } fn main() { - if let Some(path) = extract_image_path() && let Err(e) = run() { + // Fundamental issue when supplying args it won't give output unless --image is used. + // Will have to be fixed in a later patch upcoming week + if let Some(path) = extract_image_path() + && let Err(e) = run() + { eprintln!("Error: {}", e); push_image(&path).ok(); } @@ -73,6 +77,22 @@ fn run() -> Result<()> { let mut i = 0; while i < args.len() { match args[i].as_str() { + "--help" => { + println!(); + println!("Usage: zshell-img-tools [options]"); + println!(); + println!("All options are required"); + println!("Options:"); + println!(" --rounding Enable or disable rounded corners"); + println!(" --radius Set the radius for rounded corners"); + println!(" --shadow Enable or disable shadow"); + println!(" --shadow-blur Set the blur radius for the shadow"); + println!(" --shadow-offset-x Set the horizontal offset for the shadow"); + println!(" --shadow-offset-y Set the vertical offset for the shadow"); + println!(" --shadow-color Set the color of the shadow as comma-separated RGBA values (0-255)"); + println!(" --scale Scale all effects by this factor (e.g. 2.0 for display scale)"); + return Ok(()); + } "--image" => { image_path = Some(next_arg(&args, &mut i, "--image")?); } @@ -117,7 +137,11 @@ fn run() -> Result<()> { let val = next_arg(&args, &mut i, "--scale")?; scale = Some(val.parse::().context("--scale must be a number")?); } - unknown => bail!("Unknown argument: {}", unknown), + unknown => { + let unknown_args = unknown.to_string(); + println!("Warning: Unknown argument '{}'", unknown); + next_arg(&args, &mut i, &unknown_args)?; + } } i += 1; @@ -125,7 +149,6 @@ fn run() -> Result<()> { let image_path = image_path.context("Missing --image ")?; - // Check if any arguments were provided let cli_args_provided = overrides.rounding.is_some() || overrides.radius.is_some() || overrides.shadow.is_some() -- 2.47.3 From 0d7c5d199ac81b18d3ed16301c8d82f8c3d730ae Mon Sep 17 00:00:00 2001 From: Inorishio Date: Sat, 30 May 2026 20:59:30 +0200 Subject: [PATCH 14/15] Update zshell-img-tools/src/main.rs Lint had issues with --help so for now I'm just removing it. --- zshell-img-tools/src/main.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index dfd0fc3..b8e8a33 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -77,22 +77,6 @@ fn run() -> Result<()> { let mut i = 0; while i < args.len() { match args[i].as_str() { - "--help" => { - println!(); - println!("Usage: zshell-img-tools [options]"); - println!(); - println!("All options are required"); - println!("Options:"); - println!(" --rounding Enable or disable rounded corners"); - println!(" --radius Set the radius for rounded corners"); - println!(" --shadow Enable or disable shadow"); - println!(" --shadow-blur Set the blur radius for the shadow"); - println!(" --shadow-offset-x Set the horizontal offset for the shadow"); - println!(" --shadow-offset-y Set the vertical offset for the shadow"); - println!(" --shadow-color Set the color of the shadow as comma-separated RGBA values (0-255)"); - println!(" --scale Scale all effects by this factor (e.g. 2.0 for display scale)"); - return Ok(()); - } "--image" => { image_path = Some(next_arg(&args, &mut i, "--image")?); } -- 2.47.3 From 7f3397e27c3c39b0725ba9c850f2df13ca2f6b5e Mon Sep 17 00:00:00 2001 From: inorishio Date: Sun, 31 May 2026 00:23:13 +0200 Subject: [PATCH 15/15] changes comments --- zshell-img-tools/src/main.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zshell-img-tools/src/main.rs b/zshell-img-tools/src/main.rs index b8e8a33..acb2a7e 100644 --- a/zshell-img-tools/src/main.rs +++ b/zshell-img-tools/src/main.rs @@ -13,7 +13,6 @@ struct CliOverrides { shadow_blur: Option, shadow_offset_x: Option, shadow_offset_y: Option, - // Accepted as four comma-separated u8 values, e.g. `255,0,0,200` shadow_color: Option<[u8; 4]>, } @@ -141,7 +140,6 @@ fn run() -> Result<()> { || overrides.shadow_offset_y.is_some() || overrides.shadow_color.is_some(); let mut effects = if cli_args_provided { - // If all args provided let rounding = overrides.rounding.context("Missing --rounding")?; let radius = overrides.radius.context("Missing --radius")?; let shadow = overrides.shadow.context("Missing --shadow")?; @@ -163,12 +161,10 @@ fn run() -> Result<()> { shadow_color, } } else { - // If not all args were provided use config file let config = config::Config::load()?; config.screenshot }; - // if scale is set do if let Some(scale) = scale.filter(|&s| s != 1.0) { effects.radius *= scale; effects.shadow_blur *= scale; -- 2.47.3