From 9c9e3986a10d8f091f97e0a03e72fef43d8dc711 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 12 Feb 2024 10:10:56 -0500 Subject: [PATCH] refactor: tiling applet --- Cargo.lock | 234 +++++++---- Cargo.toml | 3 +- cosmic-applet-tiling/Cargo.toml | 16 +- .../com.system76.CosmicAppletTiling.desktop | 1 + .../i18n/en/cosmic_applet_tiling.ftl | 10 +- cosmic-applet-tiling/src/main.rs | 2 + cosmic-applet-tiling/src/wayland.rs | 235 ++++++++++++ .../src/wayland_subscription.rs | 91 +++++ cosmic-applet-tiling/src/window.rs | 363 +++++++++++------- 9 files changed, 735 insertions(+), 220 deletions(-) create mode 100644 cosmic-applet-tiling/src/wayland.rs create mode 100644 cosmic-applet-tiling/src/wayland_subscription.rs diff --git a/Cargo.lock b/Cargo.lock index ab7a84d3..6e1308bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,27 +4,31 @@ version = 3 [[package]] name = "accesskit" -version = "0.11.0" -source = "git+https://github.com/wash2/accesskit.git?branch=winit-0.29#16e0d60cf91b255ed6d9ac5c47bd3d1e878f17d8" +version = "0.12.2" +source = "git+https://github.com/wash2/accesskit.git?branch=winit-0.29#5f9b61c8264000d001499c902562422e13efa7a8" [[package]] name = "accesskit_consumer" -version = "0.15.0" -source = "git+https://github.com/wash2/accesskit.git?branch=winit-0.29#16e0d60cf91b255ed6d9ac5c47bd3d1e878f17d8" +version = "0.17.0" +source = "git+https://github.com/wash2/accesskit.git?branch=winit-0.29#5f9b61c8264000d001499c902562422e13efa7a8" dependencies = [ "accesskit", ] [[package]] name = "accesskit_unix" -version = "0.5.0" -source = "git+https://github.com/wash2/accesskit.git?branch=winit-0.29#16e0d60cf91b255ed6d9ac5c47bd3d1e878f17d8" +version = "0.7.1" +source = "git+https://github.com/wash2/accesskit.git?branch=winit-0.29#5f9b61c8264000d001499c902562422e13efa7a8" dependencies = [ "accesskit", "accesskit_consumer", - "async-channel 1.9.0", + "async-channel", + "async-executor", + "async-task", "atspi", "futures-lite 1.13.0", + "futures-util", + "once_cell", "serde", "zbus", ] @@ -196,24 +200,13 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.9.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" -dependencies = [ - "concurrent-queue", - "event-listener 4.0.3", - "event-listener-strategy", + "event-listener 5.0.0", + "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] @@ -299,7 +292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", - "event-listener-strategy", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -396,29 +389,50 @@ dependencies = [ [[package]] name = "atspi" -version = "0.10.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "674e7a3376837b2e7d12d34d58ac47073c491dc3bf6f71a7adaf687d4d817faa" +checksum = "6059f350ab6f593ea00727b334265c4dfc7fd442ee32d264794bd9bdc68e87ca" dependencies = [ - "async-recursion", - "async-trait", - "atspi-macros", - "enumflags2", - "futures-lite 1.13.0", - "serde", - "tracing", - "zbus", - "zbus_names", + "atspi-common", + "atspi-connection", + "atspi-proxies", ] [[package]] -name = "atspi-macros" -version = "0.2.0" +name = "atspi-common" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb4870a32c0eaa17e35bca0e6b16020635157121fb7d45593d242c295bc768" +checksum = "92af95f966d2431f962bc632c2e68eda7777330158bf640c4af4249349b2cdf5" dependencies = [ - "quote", - "syn 1.0.109", + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-connection" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c65e7d70f86d4c0e3b2d585d9bf3f979f0b19d635a336725a88d279f76b939" +dependencies = [ + "atspi-common", + "atspi-proxies", + "futures-lite 1.13.0", + "zbus", +] + +[[package]] +name = "atspi-proxies" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6495661273703e7a229356dcbe8c8f38223d697aacfaf0e13590a9ac9977bb52" +dependencies = [ + "atspi-common", + "serde", + "zbus", ] [[package]] @@ -505,7 +519,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.1", + "async-channel", "async-lock 3.3.0", "async-task", "fastrand 2.0.1", @@ -552,9 +566,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytemuck" -version = "1.14.1" +version = "1.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9" +checksum = "ea31d69bda4949c1c1562c1e6f042a1caefac98cdc8a298260a2ff41c1e2d42b" dependencies = [ "bytemuck_derive", ] @@ -826,7 +840,7 @@ dependencies = [ "futures-util", "i18n-embed 0.13.9", "i18n-embed-fl 0.6.7", - "itertools 0.12.1", + "itertools 0.10.5", "libcosmic", "log", "nix 0.26.4", @@ -1007,12 +1021,17 @@ dependencies = [ name = "cosmic-applet-tiling" version = "0.1.0" dependencies = [ + "anyhow", + "cosmic-client-toolkit", + "cosmic-comp-config", + "cosmic-protocols", "cosmic-time", "i18n-embed 0.14.1", "i18n-embed-fl 0.7.0", "libcosmic", "once_cell", "rust-embed 8.2.0", + "tokio", "tracing", ] @@ -1062,10 +1081,20 @@ dependencies = [ "wayland-client 0.31.2", ] +[[package]] +name = "cosmic-comp-config" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-comp.git?rev=5eb5af4#5eb5af46756f2f2bc5e812cf68fe4c4b79efd924" +dependencies = [ + "cosmic-config", + "input", + "serde", +] + [[package]] name = "cosmic-config" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1085,7 +1114,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "quote", "syn 1.0.109", @@ -1108,7 +1137,7 @@ dependencies = [ [[package]] name = "cosmic-notifications-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-notifications#33ee0639630899f7cb61b61ffa4ae3f4ab4f1e23" +source = "git+https://github.com/pop-os/cosmic-notifications#a252fd2005e61c7d34a95fb203daea2881fd7694" dependencies = [ "cosmic-config", "serde", @@ -1117,7 +1146,7 @@ dependencies = [ [[package]] name = "cosmic-notifications-util" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-notifications#33ee0639630899f7cb61b61ffa4ae3f4ab4f1e23" +source = "git+https://github.com/pop-os/cosmic-notifications#a252fd2005e61c7d34a95fb203daea2881fd7694" dependencies = [ "bytemuck", "fast_image_resize", @@ -1138,7 +1167,7 @@ dependencies = [ [[package]] name = "cosmic-panel-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-panel#a92b60eeee5087ac4f5b64426aee8be806a4c80d" +source = "git+https://github.com/pop-os/cosmic-panel#47d5a704652860be3dff3787ee17bc88e007ff6d" dependencies = [ "anyhow", "cosmic-config", @@ -1173,8 +1202,8 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.10.0" -source = "git+https://github.com/pop-os/cosmic-text.git?rev=1b025ae#1b025ae56e0122cff5798b9f54fc56d47a182d2b" +version = "0.11.2" +source = "git+https://github.com/pop-os/cosmic-text.git#0cb6eba6e708e2743313ee0016162de7a0146353" dependencies = [ "bitflags 2.4.2", "fontdb", @@ -1196,7 +1225,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "almost", "cosmic-config", @@ -1770,6 +1799,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72557800024fabbaa2449dd4bf24e37b93702d457a4d4f2b0dd1f0f039f20c1" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" @@ -1780,6 +1820,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.0.0", + "pin-project-lite", +] + [[package]] name = "exr" version = "1.72.0" @@ -2374,7 +2424,7 @@ dependencies = [ [[package]] name = "glyphon" version = "0.5.0" -source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.5-wgpu#db9620f48ceef47e214f3a058b9504e9983ed987" +source = "git+https://github.com/pop-os/glyphon.git?tag=v0.5.0#1b0646ff8f74da92d3be704dfc2257d7f4d7eed8" dependencies = [ "cosmic-text", "etagere", @@ -2697,7 +2747,7 @@ dependencies = [ [[package]] name = "iced" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "iced_accessibility", "iced_core", @@ -2712,7 +2762,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "accesskit", "accesskit_unix", @@ -2721,7 +2771,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "bitflags 1.3.2", "iced_accessibility", @@ -2740,7 +2790,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "futures", "iced_core", @@ -2753,7 +2803,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2777,7 +2827,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2789,7 +2839,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "iced_accessibility", "iced_core", @@ -2801,7 +2851,7 @@ dependencies = [ [[package]] name = "iced_sctk" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "enum-repr", "float-cmp", @@ -2827,7 +2877,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "iced_core", "once_cell", @@ -2837,7 +2887,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "bytemuck", "cosmic-text", @@ -2854,7 +2904,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -2873,7 +2923,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "iced_renderer", "iced_runtime", @@ -2966,6 +3016,26 @@ dependencies = [ "libc", ] +[[package]] +name = "input" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7911ce3db9c10c5ab4a35c49af778a5f9a827bd0f7371d9be56175d8dd2740d0" +dependencies = [ + "bitflags 2.4.2", + "input-sys", + "io-lifetimes 1.0.11", + "libc", + "log", + "udev", +] + +[[package]] +name = "input-sys" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0" + [[package]] name = "instant" version = "0.1.12" @@ -3138,7 +3208,7 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#6ebda94c79a7d349bf5e5eba944d399ac1215106" +source = "git+https://github.com/pop-os/libcosmic#072a3d5ca0032df6caa9011860fc496500c2a840" dependencies = [ "apply", "ashpd", @@ -3160,6 +3230,7 @@ dependencies = [ "iced_sctk", "iced_style", "iced_tiny_skia", + "iced_wgpu", "iced_widget", "lazy_static", "nix 0.27.1", @@ -3643,9 +3714,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] @@ -3669,19 +3740,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -3702,9 +3772,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -4299,9 +4369,9 @@ checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" [[package]] name = "read-fonts" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7555e052e772f964a1c99f1434f6a2c3a47a5f8e4292236921f121a7753cb2b5" +checksum = "a1362980db95801b70031dd592dc052a44b1810ca9da8fbcf7b25983f3174ed0" dependencies = [ "font-types", ] @@ -4869,7 +4939,7 @@ dependencies = [ [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#0bb85989353f0d17deb593dedb00ee4392a871e7" +source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#6e75b1ad7e98397d37cb187886d05969bc480995" dependencies = [ "as-raw-xcb-connection", "bytemuck", @@ -5514,9 +5584,9 @@ checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-vo" @@ -6303,9 +6373,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.37" +version = "0.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" +checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" dependencies = [ "memchr", ] @@ -6356,7 +6426,7 @@ dependencies = [ [[package]] name = "xdg-shell-wrapper-config" version = "0.1.0" -source = "git+https://github.com/pop-os/xdg-shell-wrapper#b61b8ad1d679b306ae462b090514db728b4b93a4" +source = "git+https://github.com/pop-os/xdg-shell-wrapper#f7e2fac29229e31c9f50884fbf910ff8ff7ab6bd" dependencies = [ "serde", "wayland-protocols-wlr", diff --git a/Cargo.toml b/Cargo.toml index 03c3a8f6..7bfd800f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,13 +27,14 @@ cosmic-time = { git = "https://github.com/pop-os/cosmic-time", default-features "libcosmic", "once_cell", ] } + libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = [ "applet", "applet-token", "tokio", "wayland", "desktop", - "dbus-config" + "dbus-config", ] } zbus = { version = "3.14", default-features = false, features = ["tokio"] } tracing = "0.1" diff --git a/cosmic-applet-tiling/Cargo.toml b/cosmic-applet-tiling/Cargo.toml index 12febab5..026b1a92 100644 --- a/cosmic-applet-tiling/Cargo.toml +++ b/cosmic-applet-tiling/Cargo.toml @@ -8,9 +8,23 @@ edition = "2021" [dependencies] libcosmic.workspace = true +libcosmic.features = [ + "applet", + "applet-token", + "dbus-config", + "desktop", + "tokio", + "wayland", + "wgpu" +] cosmic-time.workspace = true +cctk.workspace = true +cosmic-protocols.workspace = true +anyhow = "1.0.79" once_cell = "1" i18n-embed = { version = "0.14.0", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.7.0" rust-embed = "8.0.0" -tracing = "0.1" \ No newline at end of file +tracing = "0.1" +cosmic-comp-config = { git = "https://github.com/pop-os/cosmic-comp.git", rev = "5eb5af4" } +tokio = { version = "1.17.0", features = ["sync", "rt"] } diff --git a/cosmic-applet-tiling/data/com.system76.CosmicAppletTiling.desktop b/cosmic-applet-tiling/data/com.system76.CosmicAppletTiling.desktop index 72df0581..026cfc08 100644 --- a/cosmic-applet-tiling/data/com.system76.CosmicAppletTiling.desktop +++ b/cosmic-applet-tiling/data/com.system76.CosmicAppletTiling.desktop @@ -11,3 +11,4 @@ Icon=com.system76.CosmicAppletTiling StartupNotify=true NoDisplay=true X-CosmicApplet=true +X-HostWaylandDisplay=true diff --git a/cosmic-applet-tiling/i18n/en/cosmic_applet_tiling.ftl b/cosmic-applet-tiling/i18n/en/cosmic_applet_tiling.ftl index 905617d1..19cacdea 100644 --- a/cosmic-applet-tiling/i18n/en/cosmic_applet_tiling.ftl +++ b/cosmic-applet-tiling/i18n/en/cosmic_applet_tiling.ftl @@ -1,4 +1,5 @@ tile-windows = Automatically tile windows +tile-current = Automatically tile current workspace shortcuts = Shortcuts navigate-windows = Navigate windows move-window = Move window @@ -8,7 +9,12 @@ active-hint = Active hint gaps = Gaps floating-window-exceptions = Floating window exceptions... window-management-settings = Window management settings... - +all-workspaces = All workspaces +per-workspace = Per workspace super = Super shift = Shift -arrow-keys = arrows \ No newline at end of file +arrow-keys = arrows +tiled = Tiled +floating = Floating +autotile-behavior = Tile windows on workspaces +new-workspace = New workspace behavior \ No newline at end of file diff --git a/cosmic-applet-tiling/src/main.rs b/cosmic-applet-tiling/src/main.rs index de23305c..84907cee 100644 --- a/cosmic-applet-tiling/src/main.rs +++ b/cosmic-applet-tiling/src/main.rs @@ -1,6 +1,8 @@ use crate::window::Window; mod localize; +mod wayland; +mod wayland_subscription; mod window; fn main() -> cosmic::iced::Result { diff --git a/cosmic-applet-tiling/src/wayland.rs b/cosmic-applet-tiling/src/wayland.rs new file mode 100644 index 00000000..c5dd87b1 --- /dev/null +++ b/cosmic-applet-tiling/src/wayland.rs @@ -0,0 +1,235 @@ +use calloop::channel::*; +use cctk::{ + sctk::{ + self, + output::{OutputHandler, OutputState}, + reexports::{calloop, calloop_wayland_source::WaylandSource, client as wayland_client}, + registry::{ProvidesRegistryState, RegistryState}, + }, + wayland_client::WEnum, + workspace::{WorkspaceHandler, WorkspaceState}, +}; +use cosmic::iced::futures; +use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::{self, TilingState}; +use futures::{channel::mpsc, executor::block_on, SinkExt}; +use std::{ + os::{ + fd::{FromRawFd, RawFd}, + unix::net::UnixStream, + }, + time::Duration, +}; +use tracing::error; +use wayland_client::{ + globals::registry_queue_init, + protocol::wl_output::{self, WlOutput}, +}; +use wayland_client::{Connection, QueueHandle}; + +pub fn spawn_workspaces(tx: mpsc::Sender) -> SyncSender { + let (workspaces_tx, workspaces_rx) = calloop::channel::sync_channel(100); + + let socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET") + .ok() + .and_then(|fd| { + fd.parse::() + .ok() + .map(|fd| unsafe { UnixStream::from_raw_fd(fd) }) + }); + + let conn = if let Some(socket) = socket { + Connection::from_socket(socket) + } else { + Connection::connect_to_env() + } + .map_err(anyhow::Error::msg); + + if let Ok(conn) = conn { + std::thread::spawn(move || { + let configured_output = std::env::var("COSMIC_PANEL_OUTPUT") + .ok() + .unwrap_or_default(); + let mut event_loop = calloop::EventLoop::::try_new().unwrap(); + let loop_handle = event_loop.handle(); + let (globals, event_queue) = registry_queue_init(&conn).unwrap(); + let qhandle = event_queue.handle(); + + WaylandSource::new(conn, event_queue) + .insert(loop_handle) + .unwrap(); + + let registry_state = RegistryState::new(&globals); + let mut state = State { + // Must be before `WorkspaceState` + output_state: OutputState::new(&globals, &qhandle), + configured_output, + workspace_state: WorkspaceState::new(®istry_state, &qhandle), + registry_state, + expected_output: None, + tx, + running: true, + have_workspaces: false, + }; + let loop_handle = event_loop.handle(); + loop_handle + .insert_source(workspaces_rx, |e, _, state| match e { + Event::Msg(autotile) => { + if let Some(w) = + state + .workspace_state + .workspace_groups() + .iter() + .find_map(|g| { + if let Some(o) = state.expected_output.as_ref() { + if !g.outputs.contains(o) { + return None; + } + } + g.workspaces.iter().find(|w| { + w.state.contains(&WEnum::Value( + zcosmic_workspace_handle_v1::State::Active, + )) + }) + }) + { + w.handle.set_tiling_state(autotile); + state + .workspace_state + .workspace_manager() + .get() + .unwrap() + .commit(); + } + } + Event::Closed => { + if let Ok(workspace_manager) = + state.workspace_state.workspace_manager().get() + { + for g in state.workspace_state.workspace_groups() { + g.handle.destroy(); + } + workspace_manager.stop(); + } + } + }) + .unwrap(); + while state.running { + event_loop + .dispatch(Duration::from_millis(16), &mut state) + .unwrap(); + } + }); + } else { + eprintln!("ENV variable WAYLAND_DISPLAY is missing. Exiting..."); + std::process::exit(1); + } + + workspaces_tx +} + +#[derive(Debug)] +pub struct State { + running: bool, + tx: mpsc::Sender, + configured_output: String, + expected_output: Option, + output_state: OutputState, + registry_state: RegistryState, + workspace_state: WorkspaceState, + have_workspaces: bool, +} + +impl State { + pub fn tiling_state(&self) -> Option { + self.workspace_state + .workspace_groups() + .iter() + .find_map(|g| { + if g.outputs + .iter() + .any(|o| Some(o) == self.expected_output.as_ref()) + { + g.workspaces.iter().find_map(|w| { + if w.state + .contains(&WEnum::Value(zcosmic_workspace_handle_v1::State::Active)) + { + w.tiling.and_then(|e| match e { + WEnum::Value(v) => Some(v), + _ => { + error!("No tiling state for the workspace"); + None + } + }) + } else { + None + } + }) + } else { + None + } + }) + } +} + +impl ProvidesRegistryState for State { + fn registry(&mut self) -> &mut RegistryState { + &mut self.registry_state + } + sctk::registry_handlers![OutputState,]; +} + +impl OutputHandler for State { + fn output_state(&mut self) -> &mut OutputState { + &mut self.output_state + } + + fn new_output( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + output: wl_output::WlOutput, + ) { + let info = self.output_state.info(&output).unwrap(); + if info.name.as_deref() == Some(&self.configured_output) { + self.expected_output = Some(output); + if self.have_workspaces { + if let Some(s) = self.tiling_state() { + let _ = block_on(self.tx.send(s)); + } + } + } + } + + fn update_output( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _output: wl_output::WlOutput, + ) { + } + + fn output_destroyed( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _output: wl_output::WlOutput, + ) { + } +} + +impl WorkspaceHandler for State { + fn workspace_state(&mut self) -> &mut WorkspaceState { + &mut self.workspace_state + } + + fn done(&mut self) { + self.have_workspaces = true; + if let Some(s) = self.tiling_state() { + let _ = block_on(self.tx.send(s)); + } + } +} + +cctk::delegate_workspace!(State); +sctk::delegate_output!(State); +sctk::delegate_registry!(State); diff --git a/cosmic-applet-tiling/src/wayland_subscription.rs b/cosmic-applet-tiling/src/wayland_subscription.rs new file mode 100644 index 00000000..e16e50d3 --- /dev/null +++ b/cosmic-applet-tiling/src/wayland_subscription.rs @@ -0,0 +1,91 @@ +use crate::wayland::{self}; +use cctk::sctk::reexports::calloop::channel::SyncSender; +use cosmic::iced::{ + self, + futures::{self, channel::mpsc, SinkExt, StreamExt}, + subscription, +}; +use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState; +use once_cell::sync::Lazy; +use tokio::sync::Mutex; + +pub static WAYLAND_RX: Lazy>>> = + Lazy::new(|| Mutex::new(None)); + +#[derive(Debug, Clone)] +pub enum WorkspacesUpdate { + State(TilingState), + Started(SyncSender), + Errored, +} + +pub fn workspaces() -> iced::Subscription { + subscription::channel( + std::any::TypeId::of::(), + 50, + move |mut output| async move { + let mut state = State::Waiting; + + loop { + state = start_listening(state, &mut output).await; + } + }, + ) +} + +async fn start_listening( + state: State, + output: &mut futures::channel::mpsc::Sender, +) -> State { + match state { + State::Waiting => { + let mut guard = WAYLAND_RX.lock().await; + let rx = { + if guard.is_none() { + if let Ok(WorkspacesWatcher { rx, tx }) = WorkspacesWatcher::new() { + *guard = Some(rx); + _ = output.send(WorkspacesUpdate::Started(tx)).await; + } else { + _ = output.send(WorkspacesUpdate::Errored).await; + return State::Error; + } + } + guard.as_mut().unwrap() + }; + if let Some(w) = rx.next().await { + _ = output.send(WorkspacesUpdate::State(w)).await; + State::Waiting + } else { + _ = output.send(WorkspacesUpdate::Errored).await; + State::Error + } + } + State::Error => cosmic::iced::futures::future::pending().await, + } +} + +pub enum State { + Waiting, + Error, +} + +pub struct WorkspacesWatcher { + rx: mpsc::Receiver, + tx: SyncSender, +} + +impl WorkspacesWatcher { + pub fn new() -> anyhow::Result { + let (tx, rx) = mpsc::channel(20); + let tx = wayland::spawn_workspaces(tx); + Ok(Self { tx, rx }) + } + + pub fn get_sender(&self) -> SyncSender { + self.tx.clone() + } + + pub async fn workspaces(&mut self) -> Option { + self.rx.next().await + } +} diff --git a/cosmic-applet-tiling/src/window.rs b/cosmic-applet-tiling/src/window.rs index 29c77a7d..96797425 100644 --- a/cosmic-applet-tiling/src/window.rs +++ b/cosmic-applet-tiling/src/window.rs @@ -1,34 +1,46 @@ -use crate::fl; +use crate::wayland_subscription::WorkspacesUpdate; +use crate::{fl, wayland_subscription}; +use cctk::sctk::reexports::calloop::channel::SyncSender; use cosmic::app::Core; -use cosmic::applet::{menu_button, padded_control}; -use cosmic::cosmic_config::{ConfigGet, ConfigSet}; +use cosmic::applet::padded_control; +use cosmic::cosmic_config::{Config, ConfigSet, CosmicConfigEntry}; use cosmic::iced::wayland::popup::{destroy_popup, get_popup}; use cosmic::iced::window::Id; use cosmic::iced::{Command, Length, Limits, Subscription}; -use cosmic::iced_core::Alignment; use cosmic::iced_style::application; use cosmic::iced_widget::{column, row}; -use cosmic::widget::{container, divider, spin_button, text}; +use cosmic::widget::segmented_button::{Entity, SingleSelectModel}; +use cosmic::widget::{ + container, divider, segmented_button, segmented_selection, spin_button, text, +}; use cosmic::{Element, Theme}; +use cosmic_comp_config::{CosmicCompConfig, TileBehavior}; +use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState; use cosmic_time::{anim, chain, id, Timeline}; use once_cell::sync::Lazy; +use std::thread; use std::time::Instant; use tracing::error; const ID: &str = "com.system76.CosmicAppletTiling"; -//const ON: &str = "com.system76.CosmicAppletTiling.On"; const OFF: &str = "com.system76.CosmicAppletTiling.Off"; static TILE_WINDOWS: Lazy = Lazy::new(id::Toggler::unique); +static ACTIVE_HINT: Lazy = Lazy::new(id::Toggler::unique); -#[derive(Default)] pub struct Window { core: Core, popup: Option, timeline: Timeline, - tile_windows: bool, - active_hint: spin_button::Model, - gaps: spin_button::Model, + config: CosmicCompConfig, + config_helper: Config, + autotile_behavior_model: segmented_button::SingleSelectModel, + new_workspace_behavior_model: segmented_button::SingleSelectModel, + autotile_global_entity: Entity, + new_workspace_entity: Entity, + /// may not match the config value if behavior is per-workspace + autotiled: bool, + workspace_tx: Option>, } #[derive(Clone, Debug)] @@ -37,12 +49,11 @@ pub enum Message { PopupClosed(Id), Frame(Instant), ToggleTileWindows(chain::Toggler, bool), - HandleActiveHint(spin_button::Message), - HandleGaps(spin_button::Message), - ViewAllShortcuts, - OpenFloatingWindowExceptions, - OpenWindowManagementSettings, - Ignore, + ToggleActiveHint(chain::Toggler, bool), + MyConfigUpdate(CosmicCompConfig), + TileMode(Entity), + WorkspaceUpdate(WorkspacesUpdate), + NewWorkspace(Entity), } impl cosmic::Application for Window { @@ -67,11 +78,55 @@ impl cosmic::Application for Window { gaps.value = core.system_theme().cosmic().gaps.1 as i32; let mut active_hint = spin_button::Model::default().max(99).min(0).step(1); active_hint.value = core.system_theme().cosmic().active_hint as i32; + let config_helper = + Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION).unwrap(); + let config = CosmicCompConfig::get_entry(&config_helper).unwrap_or_else(|(errs, c)| { + for err in errs { + error!(?err, "Error loading config"); + } + c + }); + let mut autotile_behavior_model = SingleSelectModel::default(); + let autotile_global_entity = autotile_behavior_model + .insert() + .text(fl!("all-workspaces")) + .id(); + let per = autotile_behavior_model + .insert() + .text(fl!("per-workspace")) + .id(); + autotile_behavior_model.activate(match config.autotile_behavior { + TileBehavior::Global => autotile_global_entity, + TileBehavior::PerWorkspace => per, + }); + + let mut new_workspace_behavior_model = SingleSelectModel::default(); + let new_workspace_entity = new_workspace_behavior_model + .insert() + .text(fl!("tiled")) + .id(); + let floating = new_workspace_behavior_model + .insert() + .text(fl!("floating")) + .id(); + new_workspace_behavior_model.activate(if config.autotile { + new_workspace_entity + } else { + floating + }); + let window = Self { core, - gaps, - active_hint, - ..Default::default() + popup: None, + timeline: Default::default(), + autotiled: config.autotile, + config, + config_helper, + autotile_behavior_model, + new_workspace_behavior_model, + autotile_global_entity, + new_workspace_entity, + workspace_tx: None, }; (window, Command::none()) } @@ -85,11 +140,33 @@ impl cosmic::Application for Window { .timeline .as_subscription() .map(|(_, now)| Message::Frame(now)); - Subscription::batch(vec![timeline]) + Subscription::batch(vec![ + timeline, + self.core + .watch_config::("com.system76.CosmicComp") + .map(|u| Message::MyConfigUpdate(u.config)), + wayland_subscription::workspaces().map(|e| Message::WorkspaceUpdate(e)), + ]) } fn update(&mut self, message: Self::Message) -> Command> { match message { + Message::WorkspaceUpdate(msg) => match msg { + WorkspacesUpdate::State(state) => { + self.autotiled = matches!(state, TilingState::TilingEnabled); + self.timeline.set_chain(if self.autotiled { + cosmic_time::chain::Toggler::on(TILE_WINDOWS.clone(), 1.0) + } else { + cosmic_time::chain::Toggler::off(TILE_WINDOWS.clone(), 1.0) + }); + } + WorkspacesUpdate::Started(tx) => { + self.workspace_tx = Some(tx); + } + WorkspacesUpdate::Errored => { + error!("Workspaces subscription failed..."); + } + }, Message::TogglePopup => { return if let Some(p) = self.popup.take() { destroy_popup(p) @@ -101,7 +178,7 @@ impl cosmic::Application for Window { .applet .get_popup_settings(Id::MAIN, new_id, None, None, None); popup_settings.positioner.size_limits = Limits::NONE - .max_width(372.0) + .max_width(400.0) .min_width(300.0) .min_height(200.0) .max_height(1080.0); @@ -116,103 +193,106 @@ impl cosmic::Application for Window { Message::Frame(now) => self.timeline.now(now), Message::ToggleTileWindows(chain, toggled) => { self.timeline.set_chain(chain).start(); - self.tile_windows = toggled + self.autotiled = toggled; + if matches!(self.config.autotile_behavior, TileBehavior::Global) { + self.config.autotile = toggled; + if toggled { + self.new_workspace_behavior_model.activate_position(0); + } else { + self.new_workspace_behavior_model.activate_position(1); + } + let helper = self.config_helper.clone(); + thread::spawn(move || { + if let Err(err) = helper.set("autotile", toggled) { + error!(?err, "Failed to set autotile {toggled}"); + } + }); + } else { + // set via protocol + if let Some(tx) = self.workspace_tx.as_ref() { + let state = if toggled { + TilingState::TilingEnabled + } else { + TilingState::FloatingOnly + }; + + if let Err(err) = tx.send(state) { + error!("Failed to send the tiling state update. {err:?}") + } + } + } } - Message::HandleActiveHint(msg) => { - match msg { - spin_button::Message::Increment => { - self.active_hint.update(spin_button::Message::Increment) + Message::ToggleActiveHint(chain, toggled) => { + self.timeline.set_chain(chain).start(); + self.config.active_hint = toggled; + + let helper = self.config_helper.clone(); + thread::spawn(move || { + if let Err(err) = helper.set("active_hint", toggled) { + error!(?err, "Failed to set active_hint {toggled}"); } - spin_button::Message::Decrement => { - self.active_hint.update(spin_button::Message::Decrement) + }); + } + Message::MyConfigUpdate(c) => { + if matches!(c.autotile_behavior, TileBehavior::Global) { + if c.autotile != self.config.autotile { + self.timeline.set_chain(if c.autotile { + cosmic_time::chain::Toggler::on(TILE_WINDOWS.clone(), 1.0) + } else { + cosmic_time::chain::Toggler::off(TILE_WINDOWS.clone(), 1.0) + }); } + self.autotile_behavior_model.activate_position(0); + } else { + if c.autotile != self.config.autotile { + self.new_workspace_behavior_model + .activate_position(if c.autotile { 0 } else { 1 }); + } + self.autotile_behavior_model.activate_position(1); + } + if c.active_hint != self.config.active_hint { + self.timeline.set_chain(if c.active_hint { + cosmic_time::chain::Toggler::on(ACTIVE_HINT.clone(), 1.0) + } else { + cosmic_time::chain::Toggler::off(ACTIVE_HINT.clone(), 1.0) + }); + } + + self.config = c; + } + Message::TileMode(e) => { + let behavior = if e == self.autotile_global_entity { + TileBehavior::Global + } else { + TileBehavior::PerWorkspace }; - let is_dark = self.core.system_theme().cosmic().is_dark; - let active_hint = self.active_hint.value; - return Command::perform( - async move { - let config = if is_dark { - cosmic::cosmic_theme::ThemeBuilder::dark_config() - } else { - cosmic::cosmic_theme::ThemeBuilder::light_config() - }; - let Ok(config) = config else { - return; - }; - if let Err(err) = ConfigSet::set(&config, "active_hint", active_hint) { - error!(?err, "Error setting active_hint"); - } - - let config = if is_dark { - cosmic::theme::CosmicTheme::dark_config() - } else { - cosmic::theme::CosmicTheme::light_config() - }; - let Ok(config) = config else { - return; - }; - - if let Err(err) = ConfigSet::set(&config, "active_hint", active_hint) { - error!(?err, "Error setting active_hint"); - } - }, - |_| cosmic::app::Message::App(Message::Ignore), - ); - } - Message::HandleGaps(msg) => { - match msg { - spin_button::Message::Increment => { - self.gaps.update(spin_button::Message::Increment) + self.config.autotile_behavior = behavior; + self.autotile_behavior_model.activate(e); + let helper = self.config_helper.clone(); + let need_to_reset_autotile = self.autotiled != self.config.autotile + && matches!(behavior, TileBehavior::Global); + if need_to_reset_autotile { + self.autotiled = self.config.autotile; + } + thread::spawn(move || { + if let Err(err) = helper.set("autotile_behavior", behavior) { + error!(?err, "Failed to set autotile_behavior {behavior:?}"); } - spin_button::Message::Decrement => { - self.gaps.update(spin_button::Message::Decrement) - } - }; - let is_dark = self.core.system_theme().cosmic().is_dark; - let gaps = self.gaps.value; - return Command::perform( - async move { - let config = if is_dark { - cosmic::cosmic_theme::ThemeBuilder::dark_config() - } else { - cosmic::cosmic_theme::ThemeBuilder::light_config() - }; - let Ok(config) = config else { - return; - }; - - let Ok(mut c_gaps) = ConfigGet::get::<(u32, u32)>(&config, "gaps") else { - error!("Error getting gaps"); - return; - }; - - c_gaps.1 = gaps as u32; - - if let Err(err) = ConfigSet::set(&config, "gaps", c_gaps) { - error!(?err, "Error setting gaps"); - } - - let config = if is_dark { - cosmic::theme::CosmicTheme::dark_config() - } else { - cosmic::theme::CosmicTheme::light_config() - }; - let Ok(config) = config else { - return; - }; - - if let Err(err) = ConfigSet::set(&config, "gaps", c_gaps) { - error!(?err, "Error setting gaps"); - } - }, - |_| cosmic::app::Message::App(Message::Ignore), - ); + }); + } + Message::NewWorkspace(e) => { + let autotile_new = self.new_workspace_entity == e; + self.config.autotile = autotile_new; + self.new_workspace_behavior_model.activate(e); + // set the config autotile behavior + let helper = self.config_helper.clone(); + thread::spawn(move || { + if let Err(err) = helper.set("autotile", autotile_new) { + error!(?err, "Failed to set autotile {autotile_new:?}"); + } + }); } - Message::ViewAllShortcuts => println!("View all shortcuts..."), - Message::OpenFloatingWindowExceptions => println!("Floating window exceptions..."), - Message::OpenWindowManagementSettings => println!("Window management settings..."), - Message::Ignore => {} } Command::none() } @@ -226,22 +306,47 @@ impl cosmic::Application for Window { } fn view_window(&self, _id: Id) -> Element { - let cosmic = self.core.system_theme().cosmic(); - let active_hint = cosmic.active_hint; - let gaps = cosmic.gaps.1; + let space_xxs = self.core.system_theme().cosmic().space_xxs(); + let mut new_workspace_behavior_button = + segmented_selection::horizontal(&self.new_workspace_behavior_model); + if matches!(self.config.autotile_behavior, TileBehavior::PerWorkspace) { + new_workspace_behavior_button = + new_workspace_behavior_button.on_activate(|e| Message::NewWorkspace(e)); + } let content_list = column![ padded_control(container( anim!( TILE_WINDOWS, &self.timeline, - fl!("tile-windows"), - self.tile_windows, + if matches!(self.config.autotile_behavior, TileBehavior::Global) { + fl!("tile-windows") + } else { + fl!("tile-current") + }, + self.autotiled, |chain, enable| { Message::ToggleTileWindows(chain, enable) }, ) .text_size(14) .width(Length::Fill), )) .width(Length::Fill), + padded_control( + column![ + divider::horizontal::default(), + column![ + text(fl!("autotile-behavior")).size(14), + segmented_selection::horizontal(&self.autotile_behavior_model) + .on_activate(|e| Message::TileMode(e)) + ], + divider::horizontal::default(), + column![ + text(fl!("new-workspace")).size(14), + new_workspace_behavior_button, + ] + ] + .spacing(space_xxs) + ), + padded_control(divider::horizontal::default()), padded_control(row!( text(fl!("navigate-windows")).size(14).width(Length::Fill), text(format!("{} + {}", fl!("super"), fl!("arrow-keys"))).size(14), @@ -264,26 +369,16 @@ impl cosmic::Application for Window { )), padded_control(divider::horizontal::default()), padded_control( - row!( - text(fl!("active-hint")).size(14).width(Length::Fill), - spin_button(active_hint.to_string(), Message::HandleActiveHint), + anim!( + ACTIVE_HINT, + &self.timeline, + fl!("active-hint"), + self.config.active_hint, + |chain, enable| { Message::ToggleActiveHint(chain, enable) }, ) - .align_items(Alignment::Center), + .text_size(14) + .width(Length::Fill), ), - padded_control( - row!( - text(fl!("gaps")).size(14).width(Length::Fill), - spin_button(gaps.to_string(), Message::HandleGaps), - ) - .align_items(Alignment::Center), - ), - padded_control(divider::horizontal::default()), - menu_button(text(fl!("view-all-shortcuts")).size(14)) - .on_press(Message::ViewAllShortcuts), - menu_button(text(fl!("floating-window-exceptions")).size(14)) - .on_press(Message::OpenFloatingWindowExceptions), - menu_button(text(fl!("window-management-settings")).size(14)) - .on_press(Message::OpenWindowManagementSettings) ] .padding([8, 0]);