From 9d18ce52f5a5076ce94ea6642bf0d8220d552d6c Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Mon, 26 May 2025 22:17:26 +0100 Subject: [PATCH] refactor: `recv_glib` dependency arrays Adds a dependency array system to `recv_glib` which internally clones the passed deps and then passes by reference to the callback. This cleans up a lot of the big `{}` blocks full of `widget.clone()` and removes a lot of boilerplate. Yay! --- src/channels.rs | 107 ++++++++++-- src/config/common.rs | 21 +-- src/dynamic_value/dynamic_bool.rs | 10 +- src/dynamic_value/dynamic_string.rs | 10 +- src/ipc/server/mod.rs | 3 +- src/modules/bindmode.rs | 26 ++- src/modules/cairo.rs | 2 +- src/modules/clipboard.rs | 11 +- src/modules/clock.rs | 4 +- src/modules/custom/button.rs | 2 +- src/modules/custom/image.rs | 18 +- src/modules/custom/label.rs | 9 +- src/modules/custom/progress.rs | 5 +- src/modules/custom/slider.rs | 2 +- src/modules/focused.rs | 2 +- src/modules/keyboard.rs | 62 +++---- src/modules/label.rs | 12 +- src/modules/launcher/mod.rs | 201 +++++++++++---------- src/modules/menu/mod.rs | 34 ++-- src/modules/mod.rs | 6 +- src/modules/music/mod.rs | 260 +++++++++++++--------------- src/modules/networkmanager.rs | 2 +- src/modules/notifications.rs | 16 +- src/modules/script.rs | 9 +- src/modules/sysinfo/mod.rs | 2 +- src/modules/tray/mod.rs | 13 +- src/modules/upower.rs | 4 +- src/modules/volume.rs | 25 ++- src/modules/workspaces/mod.rs | 152 ++++++++-------- src/popup.rs | 24 +-- src/style.rs | 2 +- 31 files changed, 539 insertions(+), 517 deletions(-) diff --git a/src/channels.rs b/src/channels.rs index 0d48625..f24f4eb 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -95,20 +95,29 @@ pub trait MpscReceiverExt { /// Spawns a `GLib` future on the local thread, and calls `rx.recv()` /// in a loop, passing the message to `f`. /// - /// This allows use of `GObjects` and futures in the same context. - fn recv_glib(self, f: F) + /// This allows use of `GObjects` and futures in the same context.# + /// + /// `deps` is a single reference, or tuple of references of clonable objects, + /// to be consumed inside the closure. + /// This avoids needing to `element.clone()` everywhere. + fn recv_glib(self, deps: D, f: Fn) where - F: FnMut(T) + 'static; + D: Dependency, + D::Target: Clone + 'static, + Fn: FnMut(&D::Target, T) + 'static; } impl MpscReceiverExt for mpsc::Receiver { - fn recv_glib(mut self, mut f: F) + fn recv_glib(mut self, deps: D, mut f: Fn) where - F: FnMut(T) + 'static, + D: Dependency, + D::Target: Clone + 'static, + Fn: FnMut(&D::Target, T) + 'static, { + let deps = deps.clone_content(); glib::spawn_future_local(async move { while let Some(val) = self.recv().await { - f(val); + f(&deps, val); } }); } @@ -122,14 +131,22 @@ where /// in a loop, passing the message to `f`. /// /// This allows use of `GObjects` and futures in the same context. - fn recv_glib(self, f: F) + /// + /// `deps` is a single reference, or tuple of references of clonable objects, + /// to be consumed inside the closure. + /// This avoids needing to `element.clone()` everywhere. + fn recv_glib(self, deps: D, f: Fn) where - F: FnMut(T) + 'static; + D: Dependency, + D::Target: Clone + 'static, + Fn: FnMut(&D::Target, T) + 'static; /// Like [`BroadcastReceiverExt::recv_glib`], but the closure must return a [`Future`]. - fn recv_glib_async(self, f: Fn) + fn recv_glib_async(self, deps: D, f: Fn) where - Fn: FnMut(T) -> F + 'static, + D: Dependency, + D::Target: Clone + 'static, + Fn: FnMut(&D::Target, T) -> F + 'static, F: Future; } @@ -137,14 +154,17 @@ impl BroadcastReceiverExt for broadcast::Receiver where T: Debug + Clone + 'static, { - fn recv_glib(mut self, mut f: F) + fn recv_glib(mut self, deps: D, mut f: Fn) where - F: FnMut(T) + 'static, + D: Dependency, + D::Target: Clone + 'static, + Fn: FnMut(&D::Target, T) + 'static, { + let deps = deps.clone_content(); glib::spawn_future_local(async move { loop { match self.recv().await { - Ok(val) => f(val), + Ok(val) => f(&deps, val), Err(broadcast::error::RecvError::Lagged(count)) => { tracing::warn!( "Channel lagged behind by {count}, this may result in unexpected or broken behaviour" @@ -159,16 +179,19 @@ where }); } - fn recv_glib_async(mut self, mut f: Fn) + fn recv_glib_async(mut self, deps: D, mut f: Fn) where - Fn: FnMut(T) -> F + 'static, + D: Dependency, + D::Target: Clone + 'static, + Fn: FnMut(&D::Target, T) -> F + 'static, F: Future, { + let deps = deps.clone_content(); glib::spawn_future_local(async move { loop { match self.recv().await { Ok(val) => { - f(val).await; + f(&deps, val).await; } Err(broadcast::error::RecvError::Lagged(count)) => { tracing::warn!( @@ -184,3 +207,55 @@ where }); } } + +/// `recv_glib` callback dependency +/// or dependency tuple. +pub trait Dependency: Clone { + type Target; + + fn clone_content(&self) -> Self::Target; +} + +impl Dependency for () { + type Target = (); + + fn clone_content(&self) -> Self::Target {} +} + +impl<'a, T> Dependency for &'a T +where + T: Clone + 'a, +{ + type Target = T; + + fn clone_content(&self) -> T { + T::clone(self) + } +} + +macro_rules! impl_dependency { + ($($idx:tt $t:ident),+) => { + impl<'a, $($t),+> Dependency for ($(&'a $t),+) + where + $($t: Clone + 'a),+ + { + type Target = ($($t),+); + + fn clone_content(&self) -> Self::Target { + ($(self.$idx.clone()),+) + } + } + }; +} + +impl_dependency!(0 T1, 1 T2); +impl_dependency!(0 T1, 1 T2, 2 T3); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11); +impl_dependency!(0 T1, 1 T2, 2 T3, 3 T4, 4 T5, 5 T6, 6 T7, 7 T8, 8 T9, 9 T10, 10 T11, 11 T12); diff --git a/src/config/common.rs b/src/config/common.rs index 33e2d42..6682a91 100644 --- a/src/config/common.rs +++ b/src/config/common.rs @@ -301,8 +301,7 @@ impl CommonConfig { install_oneshot!(self.on_mouse_exit, connect_leave_notify_event); if let Some(tooltip) = self.tooltip { - let container = container.clone(); - dynamic_string(&tooltip, move |string| { + dynamic_string(&tooltip, container, move |container, string| { container.set_tooltip_text(Some(&string)); }); } @@ -314,19 +313,15 @@ impl CommonConfig { container.show_all(); }, |show_if| { + // need to keep clone here for the notify callback let container = container.clone(); - { - let revealer = revealer.clone(); - let container = container.clone(); - - show_if.subscribe(move |success| { - if success { - container.show_all(); - } - revealer.set_reveal_child(success); - }); - } + show_if.subscribe((revealer, &container), |(revealer, container), success| { + if success { + container.show_all(); + } + revealer.set_reveal_child(success); + }); revealer.connect_child_revealed_notify(move |revealer| { if !revealer.reveals_child() { diff --git a/src/dynamic_value/dynamic_bool.rs b/src/dynamic_value/dynamic_bool.rs index cff21a6..1b7bf81 100644 --- a/src/dynamic_value/dynamic_bool.rs +++ b/src/dynamic_value/dynamic_bool.rs @@ -1,6 +1,6 @@ #[cfg(feature = "ipc")] use crate::Ironbar; -use crate::channels::{AsyncSenderExt, MpscReceiverExt}; +use crate::channels::{AsyncSenderExt, Dependency, MpscReceiverExt}; use crate::script::Script; use crate::spawn; use cfg_if::cfg_if; @@ -19,9 +19,11 @@ pub enum DynamicBool { } impl DynamicBool { - pub fn subscribe(self, f: F) + pub fn subscribe(self, deps: D, f: F) where - F: FnMut(bool) + 'static, + D: Dependency, + D::Target: Clone + 'static, + F: FnMut(&D::Target, bool) + 'static, { let value = match self { Self::Unknown(input) => { @@ -43,7 +45,7 @@ impl DynamicBool { let (tx, rx) = mpsc::channel(32); - rx.recv_glib(f); + rx.recv_glib(deps, f); spawn(async move { match value { diff --git a/src/dynamic_value/dynamic_string.rs b/src/dynamic_value/dynamic_string.rs index 6611f66..d825f78 100644 --- a/src/dynamic_value/dynamic_string.rs +++ b/src/dynamic_value/dynamic_string.rs @@ -1,6 +1,6 @@ #[cfg(feature = "ipc")] use crate::Ironbar; -use crate::channels::{AsyncSenderExt, MpscReceiverExt}; +use crate::channels::{AsyncSenderExt, Dependency, MpscReceiverExt}; use crate::script::{OutputStream, Script}; use crate::{arc_mut, lock, spawn}; use tokio::sync::mpsc; @@ -26,9 +26,11 @@ enum DynamicStringSegment { /// label.set_label_escaped(&string); /// }); /// ``` -pub fn dynamic_string(input: &str, f: F) +pub fn dynamic_string(input: &str, deps: D, f: F) where - F: FnMut(String) + 'static, + D: Dependency, + D::Target: Clone + 'static, + F: FnMut(&D::Target, String) + 'static, { let (tokens, is_static) = parse_input(input); @@ -89,7 +91,7 @@ where } } - rx.recv_glib(f); + rx.recv_glib(deps, f); // initialize if is_static { diff --git a/src/ipc/server/mod.rs b/src/ipc/server/mod.rs index bdb7ef1..ced5fa6 100644 --- a/src/ipc/server/mod.rs +++ b/src/ipc/server/mod.rs @@ -65,8 +65,7 @@ impl Ipc { } }); - let application = application.clone(); - cmd_rx.recv_glib(move |command| { + cmd_rx.recv_glib(application, move |application, command| { let res = Self::handle_command(command, &application, &ironbar); res_tx.send_spawn(res); }); diff --git a/src/modules/bindmode.rs b/src/modules/bindmode.rs index 2af0aca..5370067 100644 --- a/src/modules/bindmode.rs +++ b/src/modules/bindmode.rs @@ -81,23 +81,17 @@ impl Module