diff --git a/glib/src/subclass/object.rs b/glib/src/subclass/object.rs index f3a81c5e4fd0..b6e548c6cd7e 100644 --- a/glib/src/subclass/object.rs +++ b/glib/src/subclass/object.rs @@ -422,6 +422,19 @@ mod test { .build(), super::Signal::builder("create-string") .return_type::() + .accumulator(|_hint, acc, val| { + // join all strings from signal handlers by newline + let mut acc = acc + .get_owned::>() + .unwrap() + .map(|mut acc| { + acc.push('\n'); + acc + }) + .unwrap_or_default(); + acc.push_str(val.get::<&str>().unwrap()); + std::ops::ControlFlow::Continue(acc.to_value()) + }) .build(), super::Signal::builder("create-child-object") .return_type::() @@ -891,13 +904,17 @@ mod test { let obj = Object::with_type(SimpleObject::static_type()); obj.connect("create-string", false, move |_args| { - Some("return value".to_value()) + Some("return value 1".to_value()) + }); + + obj.connect("create-string", false, move |_args| { + Some("return value 2".to_value()) }); let signal_id = imp::SimpleObject::signals()[2].signal_id(); let value = obj.emit::(signal_id, &[]); - assert_eq!(value, "return value"); + assert_eq!(value, "return value 1\nreturn value 2"); } #[test] diff --git a/glib/src/subclass/signal.rs b/glib/src/subclass/signal.rs index bcaa18f4ff3d..0af9e04fa090 100644 --- a/glib/src/subclass/signal.rs +++ b/glib/src/subclass/signal.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::{fmt, num::NonZeroU32, ptr, sync::Mutex}; +use std::{fmt, num::NonZeroU32, ops::ControlFlow, ptr, sync::Mutex}; use crate::{ ffi, gobject_ffi, prelude::*, translate::*, utils::is_canonical_pspec_name, Closure, @@ -18,7 +18,12 @@ pub struct SignalBuilder { return_type: SignalType, class_handler: Option Option + Send + Sync + 'static>>, accumulator: Option< - Box bool + Send + Sync + 'static>, + Box< + dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow + + Send + + Sync + + 'static, + >, >, } @@ -352,7 +357,12 @@ enum SignalRegistration { Unregistered { class_handler: Option Option + Send + Sync + 'static>>, accumulator: Option< - Box bool + Send + Sync + 'static>, + Box< + dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow + + Send + + Sync + + 'static, + >, >, }, Registered { @@ -480,7 +490,10 @@ impl SignalBuilder { /// This is called if multiple signal handlers are connected to the signal for accumulating the /// return values into a single value. pub fn accumulator< - F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static, + F: Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow + + Send + + Sync + + 'static, >( mut self, func: F, @@ -633,7 +646,7 @@ impl Signal { let accumulator = &*(data as *const ( SignalType, Box< - dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + dyn Fn(&SignalInvocationHint, Value, &Value) -> ControlFlow + Send + Sync + 'static, @@ -651,8 +664,23 @@ impl Signal { handler_return.type_() ); - let res = (accumulator.1)(&SignalInvocationHint(*ihint), return_accu, handler_return) - .into_glib(); + let control_flow = (accumulator.1)( + &SignalInvocationHint(*ihint), + std::mem::replace(return_accu, Value::uninitialized()), + handler_return, + ); + + let res = match control_flow { + ControlFlow::Continue(val) => { + *return_accu = val; + true.into_glib() + } + + ControlFlow::Break(val) => { + *return_accu = val; + false.into_glib() + } + }; assert!( return_accu.type_().is_a(return_type.into()),