1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{prelude::*, Binding, Object};
4
5impl Binding {
6 #[doc(alias = "get_source")]
7 pub fn source(&self) -> Option<Object> {
8 self.property(property_name:"source")
9 }
10
11 #[doc(alias = "get_target")]
12 pub fn target(&self) -> Option<Object> {
13 self.property(property_name:"target")
14 }
15}
16
17#[cfg(test)]
18mod test {
19 use crate::{prelude::*, subclass::prelude::*};
20
21 #[test]
22 fn binding() {
23 let source = TestObject::default();
24 let target = TestObject::default();
25
26 assert!(source.find_property("name").is_some());
27 source
28 .bind_property("name", &target, "name")
29 .bidirectional()
30 .build();
31
32 source.set_name("test_source_name");
33 assert_eq!(source.name(), target.name());
34
35 target.set_name("test_target_name");
36 assert_eq!(source.name(), target.name());
37 }
38
39 #[test]
40 fn binding_to_transform_with_values() {
41 let source = TestObject::default();
42 let target = TestObject::default();
43
44 source
45 .bind_property("name", &target, "name")
46 .sync_create()
47 .transform_to_with_values(|_binding, value| {
48 let value = value.get::<&str>().unwrap();
49 Some(format!("{value} World").to_value())
50 })
51 .transform_from_with_values(|_binding, value| {
52 let value = value.get::<&str>().unwrap();
53 Some(format!("{value} World").to_value())
54 })
55 .build();
56
57 source.set_name("Hello");
58 assert_eq!(target.name(), "Hello World");
59 }
60
61 #[test]
62 fn binding_from_transform_with_values() {
63 let source = TestObject::default();
64 let target = TestObject::default();
65
66 source
67 .bind_property("name", &target, "name")
68 .sync_create()
69 .bidirectional()
70 .transform_to_with_values(|_binding, value| {
71 let value = value.get::<&str>().unwrap();
72 Some(format!("{value} World").to_value())
73 })
74 .transform_from_with_values(|_binding, value| {
75 let value = value.get::<&str>().unwrap();
76 Some(format!("{value} World").to_value())
77 })
78 .build();
79
80 target.set_name("Hello");
81 assert_eq!(source.name(), "Hello World");
82 }
83
84 #[test]
85 fn binding_to_transform_ref() {
86 let source = TestObject::default();
87 let target = TestObject::default();
88
89 source
90 .bind_property("name", &target, "name")
91 .sync_create()
92 .transform_to(|_binding, value: &str| Some(format!("{value} World")))
93 .transform_from(|_binding, value: &str| Some(format!("{value} World")))
94 .build();
95
96 source.set_name("Hello");
97 assert_eq!(target.name(), "Hello World");
98 }
99
100 #[test]
101 fn binding_to_transform_owned_ref() {
102 let source = TestObject::default();
103 let target = TestObject::default();
104
105 source
106 .bind_property("name", &target, "name")
107 .sync_create()
108 .transform_to(|_binding, value: String| Some(format!("{value} World")))
109 .transform_from(|_binding, value: &str| Some(format!("{value} World")))
110 .build();
111
112 source.set_name("Hello");
113 assert_eq!(target.name(), "Hello World");
114 }
115
116 #[test]
117 fn binding_from_transform() {
118 let source = TestObject::default();
119 let target = TestObject::default();
120
121 source
122 .bind_property("name", &target, "name")
123 .sync_create()
124 .bidirectional()
125 .transform_to(|_binding, value: &str| Some(format!("{value} World")))
126 .transform_from(|_binding, value: &str| Some(format!("{value} World")))
127 .build();
128
129 target.set_name("Hello");
130 assert_eq!(source.name(), "Hello World");
131 }
132
133 #[test]
134 fn binding_to_transform_with_values_change_type() {
135 let source = TestObject::default();
136 let target = TestObject::default();
137
138 source
139 .bind_property("name", &target, "enabled")
140 .sync_create()
141 .transform_to_with_values(|_binding, value| {
142 let value = value.get::<&str>().unwrap();
143 Some((value == "Hello").to_value())
144 })
145 .transform_from_with_values(|_binding, value| {
146 let value = value.get::<bool>().unwrap();
147 Some((if value { "Hello" } else { "World" }).to_value())
148 })
149 .build();
150
151 source.set_name("Hello");
152 assert!(target.enabled());
153
154 source.set_name("Hello World");
155 assert!(!target.enabled());
156 }
157
158 #[test]
159 fn binding_from_transform_values_change_type() {
160 let source = TestObject::default();
161 let target = TestObject::default();
162
163 source
164 .bind_property("name", &target, "enabled")
165 .sync_create()
166 .bidirectional()
167 .transform_to_with_values(|_binding, value| {
168 let value = value.get::<&str>().unwrap();
169 Some((value == "Hello").to_value())
170 })
171 .transform_from_with_values(|_binding, value| {
172 let value = value.get::<bool>().unwrap();
173 Some((if value { "Hello" } else { "World" }).to_value())
174 })
175 .build();
176
177 target.set_enabled(true);
178 assert_eq!(source.name(), "Hello");
179 target.set_enabled(false);
180 assert_eq!(source.name(), "World");
181 }
182
183 #[test]
184 fn binding_to_transform_change_type() {
185 let source = TestObject::default();
186 let target = TestObject::default();
187
188 source
189 .bind_property("name", &target, "enabled")
190 .sync_create()
191 .transform_to(|_binding, value: &str| Some(value == "Hello"))
192 .transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
193 .build();
194
195 source.set_name("Hello");
196 assert!(target.enabled());
197
198 source.set_name("Hello World");
199 assert!(!target.enabled());
200 }
201
202 #[test]
203 fn binding_from_transform_change_type() {
204 let source = TestObject::default();
205 let target = TestObject::default();
206
207 source
208 .bind_property("name", &target, "enabled")
209 .sync_create()
210 .bidirectional()
211 .transform_to(|_binding, value: &str| Some(value == "Hello"))
212 .transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
213 .build();
214
215 target.set_enabled(true);
216 assert_eq!(source.name(), "Hello");
217 target.set_enabled(false);
218 assert_eq!(source.name(), "World");
219 }
220
221 mod imp {
222 use std::cell::RefCell;
223
224 use once_cell::sync::Lazy;
225
226 use super::*;
227 use crate as glib;
228
229 #[derive(Debug, Default)]
230 pub struct TestObject {
231 pub name: RefCell<String>,
232 pub enabled: RefCell<bool>,
233 }
234
235 #[crate::object_subclass]
236 impl ObjectSubclass for TestObject {
237 const NAME: &'static str = "TestBinding";
238 type Type = super::TestObject;
239 }
240
241 impl ObjectImpl for TestObject {
242 fn properties() -> &'static [crate::ParamSpec] {
243 static PROPERTIES: Lazy<Vec<crate::ParamSpec>> = Lazy::new(|| {
244 vec![
245 crate::ParamSpecString::builder("name")
246 .explicit_notify()
247 .build(),
248 crate::ParamSpecBoolean::builder("enabled")
249 .explicit_notify()
250 .build(),
251 ]
252 });
253 PROPERTIES.as_ref()
254 }
255
256 fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> crate::Value {
257 let obj = self.obj();
258 match pspec.name() {
259 "name" => obj.name().to_value(),
260 "enabled" => obj.enabled().to_value(),
261 _ => unimplemented!(),
262 }
263 }
264
265 fn set_property(&self, _id: usize, value: &crate::Value, pspec: &crate::ParamSpec) {
266 let obj = self.obj();
267 match pspec.name() {
268 "name" => obj.set_name(value.get().unwrap()),
269 "enabled" => obj.set_enabled(value.get().unwrap()),
270 _ => unimplemented!(),
271 };
272 }
273 }
274 }
275
276 crate::wrapper! {
277 pub struct TestObject(ObjectSubclass<imp::TestObject>);
278 }
279
280 impl Default for TestObject {
281 fn default() -> Self {
282 crate::Object::new()
283 }
284 }
285
286 impl TestObject {
287 fn name(&self) -> String {
288 self.imp().name.borrow().clone()
289 }
290
291 fn set_name(&self, name: &str) {
292 if name != self.imp().name.replace(name.to_string()).as_str() {
293 self.notify("name");
294 }
295 }
296
297 fn enabled(&self) -> bool {
298 *self.imp().enabled.borrow()
299 }
300
301 fn set_enabled(&self, enabled: bool) {
302 if enabled != self.imp().enabled.replace(enabled) {
303 self.notify("enabled");
304 }
305 }
306 }
307}
308