relm4/binding/
mod.rs

1//! Easy data bindings between objects.
2//!
3//! Particularly, this module provides types and traits
4//! that simplify connecting with the primary property of an object.
5//! In this context, *primary* means that the property is the most important and
6//! most accessed value stored by the object.
7//! Usually, this is very easy to figure out, such as the *active* property for [`gtk::ToggleButton`].
8//! In any case, the primary property type are always documented.
9//!
10//! To find out which widgets are currently supported and which property is considered as primary,
11//! please look at the list implementers for [`ConnectBinding`].
12//! Contributions to add support for more widgets are always welcome.
13
14mod bindings;
15mod widgets;
16
17pub use bindings::*;
18
19use std::ops::{Deref, DerefMut};
20
21use gtk::{glib, prelude::IsA};
22
23/// A trait that allows type-safe bindings between to the primary properties of two objects.
24pub trait ConnectBinding {
25    /// The type of the primary property.
26    type Target;
27
28    /// Create a type-safe bidirectional between the primary property of an object
29    /// and a [`Binding`].
30    fn bind<B: Binding<Target = Self::Target>>(&self, binding: &B);
31}
32
33/// Extension for [`ConnectBinding`].
34/// This trait is not implemented manually, but through
35/// automatically implemented for all types that implement
36/// [`ConnectBinding`].
37pub trait ConnectBindingExt {
38    /// The type of the primary property.
39    /// Inherited from [`ConnectBinding::Target`].
40    type Target;
41
42    /// Create an object and immediately connect it with a [`Binding`].
43    fn with_binding<B: Binding<Target = Self::Target>>(binding: &B) -> Self;
44}
45
46impl<T> ConnectBindingExt for T
47where
48    T: Default + ConnectBinding,
49{
50    type Target = <T as ConnectBinding>::Target;
51
52    fn with_binding<B: Binding<Target = Self::Target>>(binding: &B) -> Self {
53        let obj = Self::default();
54        obj.bind(binding);
55        obj
56    }
57}
58
59#[derive(Debug)]
60/// A RAII-guard that stores a value to
61/// a [`Binding`].
62///
63/// Once dropped, it will automatically update
64/// the value of the primary property.
65pub struct BindingGuard<B: Binding> {
66    value: Option<B::Target>,
67    inner: B,
68}
69
70impl<B: Binding> Deref for BindingGuard<B> {
71    type Target = <B as Binding>::Target;
72
73    fn deref(&self) -> &Self::Target {
74        self.value.as_ref().unwrap()
75    }
76}
77
78impl<B: Binding> DerefMut for BindingGuard<B> {
79    fn deref_mut(&mut self) -> &mut Self::Target {
80        self.value.as_mut().unwrap()
81    }
82}
83
84impl<B: Binding> Drop for BindingGuard<B> {
85    fn drop(&mut self) {
86        self.inner.set(self.value.take().unwrap());
87    }
88}
89
90/// A [`glib::Object`] with one primary property.
91pub trait Binding: Clone + IsA<glib::Object> {
92    /// The type of the primary property.
93    type Target;
94
95    #[must_use]
96    /// The name of the primary property.
97    fn property_name() -> &'static str {
98        "value"
99    }
100
101    /// Get a new [`BindingGuard`] from the object.
102    ///
103    /// Once dropped, the [`BindingGuard`] will
104    /// automatically update the value of the
105    /// primary property.
106    fn guard(&self) -> BindingGuard<Self> {
107        BindingGuard {
108            value: Some(self.get()),
109            inner: self.clone(),
110        }
111    }
112
113    /// Get the value of the primary property.
114    fn get(&self) -> Self::Target;
115
116    /// Set the value of the primary property.
117    fn set(&self, value: Self::Target);
118}