relm4/extensions/
mod.rs

1mod container;
2mod iter_children;
3mod object_ext;
4mod remove;
5mod set_child;
6
7#[cfg(test)]
8mod tests;
9mod widget_ext;
10
11pub use container::RelmContainerExt;
12pub use iter_children::RelmIterChildrenExt;
13pub use object_ext::RelmObjectExt;
14pub use remove::{RelmRemoveAllExt, RelmRemoveExt};
15pub use set_child::RelmSetChildExt;
16pub use widget_ext::RelmWidgetExt;
17
18use gtk::prelude::{
19    ApplicationExt, ApplicationExtManual, Cast, IsA, ListBoxRowExt, StaticType, WidgetExt,
20};
21
22/// Get a reference to a widget.
23///
24/// This trait is an extension of [`AsRef`]
25/// that always returns `&`[`gtk::Widget`].
26pub trait WidgetRef {
27    /// Returns a reference to a widget.
28    ///
29    /// Like [`AsRef::as_ref`] it will auto-dereference.
30    fn widget_ref(&self) -> &gtk::Widget;
31}
32
33impl<T: AsRef<gtk::Widget>> WidgetRef for T {
34    fn widget_ref(&self) -> &gtk::Widget {
35        self.as_ref()
36    }
37}
38
39/// A trait that describes a widget template.
40///
41/// Widget templates can be created manually by implementing this trait
42/// or by using the [`widget_template`](crate::widget_template) macro.
43pub trait WidgetTemplate:
44    Sized + std::fmt::Debug + AsRef<Self::Root> + std::ops::Deref<Target = Self::Root>
45{
46    /// The root of the template.
47    type Root;
48
49    /// The parameter used to initialize the template.
50    type Init;
51
52    /// Initializes the template.
53    fn init(init: Self::Init) -> Self;
54}
55
56/// Additional methods for `gtk::builders::ApplicationBuilder`
57pub trait ApplicationBuilderExt {
58    /// Convenience method for launching an application and initializing the window.
59    fn launch<F>(self, init: F)
60    where
61        F: Fn(gtk::Application, gtk::ApplicationWindow) + 'static;
62}
63
64impl ApplicationBuilderExt for gtk::builders::ApplicationBuilder {
65    fn launch<F>(self, init: F)
66    where
67        F: Fn(gtk::Application, gtk::ApplicationWindow) + 'static,
68    {
69        let app = self.build();
70
71        app.connect_activate(move |app| {
72            let window = gtk::ApplicationWindow::new(app);
73
74            init(app.clone(), window.clone());
75
76            window.set_visible(true);
77        });
78
79        app.run();
80    }
81}
82
83/// Additional methods for `gtk::ListBox`.
84pub trait RelmListBoxExt {
85    /// Get the index of a widget attached to a listbox.
86    fn index_of_child(&self, widget: &impl AsRef<gtk::Widget>) -> Option<i32>;
87
88    /// Remove the row of a child attached a listbox.
89    fn remove_row_of_child(&self, widget: &impl AsRef<gtk::Widget>);
90
91    /// Get the row of a widget attached to a listbox.
92    fn row_of_child(&self, widget: &impl AsRef<gtk::Widget>) -> Option<gtk::ListBoxRow>;
93}
94
95impl RelmListBoxExt for gtk::ListBox {
96    fn index_of_child(&self, widget: &impl AsRef<gtk::Widget>) -> Option<i32> {
97        self.row_of_child(widget).map(|row| row.index())
98    }
99
100    fn remove_row_of_child(&self, widget: &impl AsRef<gtk::Widget>) {
101        if let Some(row) = self.row_of_child(widget) {
102            row.set_child(None::<&gtk::Widget>);
103            self.remove(&row);
104        }
105    }
106
107    fn row_of_child(&self, widget: &impl AsRef<gtk::Widget>) -> Option<gtk::ListBoxRow> {
108        if let Some(row) = widget.as_ref().ancestor(gtk::ListBoxRow::static_type())
109            && let Some(row) = row.downcast_ref::<gtk::ListBoxRow>()
110            && let Some(parent_widget) = row.parent()
111            && let Some(parent_box) = parent_widget.downcast_ref::<Self>()
112            && parent_box == self
113        {
114            return Some(row.clone());
115        }
116
117        None
118    }
119}
120
121/// Type of children inside a container.
122///
123/// For example, `gtk::ListBox` only contains `gtk::ListBoxRow` widgets
124/// as children. If you add any other kind of widget, a row is automatically
125/// inserted between the list box and the widget.
126///
127/// For simple widgets like `gtk::Box`, the children type will be `gtk::Widget`,
128/// meaning that it can be any widget type.
129pub trait ContainerChild {
130    /// Type of container children.
131    type Child: IsA<gtk::Widget>;
132}
133
134macro_rules! container_child_impl {
135    ($($type:ty: $child:ty), +) => {
136        $(
137            impl ContainerChild for $type {
138                type Child = $child;
139            }
140        )+
141    };
142    ($($type:ty), +) => {
143        $(
144            #[allow(deprecated)]
145            impl ContainerChild for $type {
146                type Child = gtk::Widget;
147            }
148        )+
149    };
150}
151
152container_child_impl! {
153    gtk::Box,
154    gtk::Fixed,
155    gtk::Grid,
156    gtk::ActionBar,
157    gtk::Stack,
158    gtk::HeaderBar,
159    gtk::InfoBar,
160    gtk::Button,
161    gtk::ComboBox,
162    gtk::FlowBox,
163    gtk::FlowBoxChild,
164    gtk::Frame,
165    gtk::Popover,
166    gtk::Window,
167    gtk::ApplicationWindow,
168    gtk::ListBox,
169    gtk::ListBoxRow,
170    gtk::ScrolledWindow,
171    gtk::Dialog,
172    gtk::LinkButton,
173    gtk::ToggleButton,
174    gtk::Overlay,
175    gtk::Revealer,
176    gtk::WindowHandle,
177    gtk::Expander,
178    gtk::AspectFrame
179}
180
181#[cfg(feature = "libadwaita")]
182#[cfg_attr(docsrs, doc(cfg(feature = "libadwaita")))]
183mod libadwaita {
184    use super::ContainerChild;
185
186    container_child_impl! {
187        adw::TabView,
188        adw::Window,
189        adw::Bin,
190        adw::ApplicationWindow,
191        adw::Clamp,
192        adw::ClampScrollable,
193        adw::SplitButton,
194        adw::StatusPage,
195        adw::PreferencesGroup,
196        adw::ToastOverlay,
197        adw::ExpanderRow,
198        adw::Carousel,
199        adw::Squeezer,
200        adw::Leaflet
201    }
202    container_child_impl! {
203        adw::PreferencesPage: adw::PreferencesGroup
204    }
205
206    #[cfg(all(feature = "libadwaita", feature = "gnome_45"))]
207    mod gnome_45 {
208        use super::ContainerChild;
209
210        container_child_impl! {
211            adw::NavigationView: adw::NavigationPage,
212            adw::NavigationSplitView: adw::NavigationPage
213        }
214        container_child_impl! {
215            adw::NavigationPage,
216            adw::BreakpointBin,
217            adw::OverlaySplitView,
218            adw::ToolbarView
219        }
220    }
221}
222
223#[cfg(feature = "libpanel")]
224#[cfg_attr(docsrs, doc(cfg(feature = "libpanel")))]
225mod libpanel {
226    use super::ContainerChild;
227    container_child_impl! {
228        panel::Frame: panel::Widget
229    }
230    container_child_impl! {
231        panel::Paned,
232        panel::Widget
233    }
234}