Skip to content

Commit 9760c25

Browse files
jf2048bilelmoussaoui
authored andcommitted
Allow chaining expressions with Closure objects
1 parent ba42b69 commit 9760c25

File tree

3 files changed

+58
-19
lines changed

3 files changed

+58
-19
lines changed

examples/expressions/main.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
mod metadata;
22
mod note;
33

4+
use glib::closure;
5+
46
use gtk::gio;
57
use gtk::glib;
68
use gtk::prelude::*;
@@ -48,11 +50,12 @@ fn build_ui(app: &gtk::Application) {
4850

4951
metadata_expression
5052
.chain_property::<Metadata>("last-modified")
51-
.chain_closure(|args| {
52-
let last_modified: glib::DateTime = args[1].get().unwrap();
53-
format!("Last Modified: {}", last_modified.format_iso8601().unwrap())
54-
})
55-
.bind(&last_modified_label, "label", gtk::Widget::NONE);
53+
.chain_closure_obj::<String>(closure!(
54+
|_: gtk::ListItem, last_modified: glib::DateTime| {
55+
format!("Last Modified: {}", last_modified.format_iso8601().unwrap())
56+
}
57+
))
58+
.bind(&last_modified_label, "label", Some(list_item));
5659

5760
list_item.set_child(Some(&hbox));
5861
});

gtk4/src/closure_expression.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,13 @@ define_expression!(
2323

2424
impl ClosureExpression {
2525
#[doc(alias = "gtk_closure_expression_new")]
26-
pub fn new<F, R>(params: impl IntoIterator<Item = impl AsRef<Expression>>, callback: F) -> Self
26+
pub fn new<R, I, E>(params: I, closure: glib::RustClosure) -> Self
2727
where
28-
F: Fn(&[Value]) -> R + 'static,
2928
R: ValueType,
29+
I: IntoIterator<Item = E>,
30+
E: AsRef<Expression>,
3031
{
3132
assert_initialized_main_thread!();
32-
let closure = glib::Closure::new_local(move |values| {
33-
let ret = callback(values);
34-
Some(ret.to_value())
35-
});
3633

3734
let params = params
3835
.into_iter()
@@ -42,21 +39,27 @@ impl ClosureExpression {
4239
unsafe {
4340
from_glib_full(ffi::gtk_closure_expression_new(
4441
R::Type::static_type().into_glib(),
45-
closure.to_glib_none().0,
42+
closure.as_ref().to_glib_none().0,
4643
params.len() as u32,
4744
params.to_glib_full(),
4845
))
4946
}
5047
}
5148

5249
#[doc(alias = "gtk_closure_expression_new")]
53-
pub fn with_closure<R, I, E>(params: I, closure: glib::Closure) -> Self
50+
pub fn with_callback<F, R>(
51+
params: impl IntoIterator<Item = impl AsRef<Expression>>,
52+
callback: F,
53+
) -> Self
5454
where
55+
F: Fn(&[Value]) -> R + 'static,
5556
R: ValueType,
56-
I: IntoIterator<Item = E>,
57-
E: AsRef<Expression>,
5857
{
5958
assert_initialized_main_thread!();
59+
let closure = glib::Closure::new_local(move |values| {
60+
let ret = callback(values);
61+
Some(ret.to_value())
62+
});
6063

6164
let params = params
6265
.into_iter()

gtk4/src/expression.rs

+37-4
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,47 @@ impl Expression {
171171
}
172172

173173
// rustdoc-stripper-ignore-next
174-
/// Create a [`gtk::ClosureExpression`] with self as a parameter. This is useful in long
175-
/// chains of [`gtk::Expression`]s.
176-
pub fn chain_closure<F, R>(&self, f: F) -> crate::ClosureExpression
174+
/// Create a [`ClosureExpression`](crate::ClosureExpression) from a [`glib::Closure`] with self
175+
/// as the second parameter and `R` as the return type. The return type is checked at run-time
176+
/// and must always be specified. This is useful in long chains of
177+
/// [`Expression`](crate::Expression)s when using the [`glib::closure!`] macro.
178+
///
179+
/// Note that the first parameter will always be the `this` object bound to the expression. If
180+
/// `None` is passed as `this` then the type of the first parameter must be
181+
/// `Option<glib::Object>` otherwise type checking will panic.
182+
///
183+
/// ```no_run
184+
/// # use gtk4 as gtk;
185+
/// use gtk::prelude::*;
186+
/// use gtk::glib;
187+
/// use glib::{closure, Object};
188+
///
189+
/// let button = gtk::Button::new();
190+
/// button.set_label("Hello");
191+
/// let label = button
192+
/// .property_expression("label")
193+
/// .chain_closure::<String>(closure!(|_: Option<Object>, label: &str| {
194+
/// format!("{} World", label)
195+
/// }))
196+
/// .evaluate_as::<String, _>(gtk::Widget::NONE);
197+
/// assert_eq!(label.unwrap(), "Hello World");
198+
/// ```
199+
pub fn chain_closure<R>(&self, closure: glib::RustClosure) -> crate::ClosureExpression
200+
where
201+
R: glib::value::ValueType,
202+
{
203+
crate::ClosureExpression::new::<R, _, _>(&[self], closure)
204+
}
205+
206+
// rustdoc-stripper-ignore-next
207+
/// Create a [`ClosureExpression`](crate::ClosureExpression) with self as the second parameter.
208+
/// This is useful in long chains of [`Expression`](crate::Expression)s.
209+
pub fn chain_closure_with_callback<F, R>(&self, f: F) -> crate::ClosureExpression
177210
where
178211
F: Fn(&[glib::Value]) -> R + 'static,
179212
R: glib::value::ValueType,
180213
{
181-
crate::ClosureExpression::new(&[self], f)
214+
crate::ClosureExpression::with_callback(&[self], f)
182215
}
183216
}
184217

0 commit comments

Comments
 (0)