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            if let Some(row) = row.downcast_ref::<gtk::ListBoxRow>() {
110                if let Some(parent_widget) = row.parent() {
111                    if let Some(parent_box) = parent_widget.downcast_ref::<Self>() {
112                        if parent_box == self {
113                            return Some(row.clone());
114                        }
115                    }
116                }
117            }
118        }
119
120        None
121    }
122}
123
124/// Type of children inside a container.
125///
126/// For example, `gtk::ListBox` only contains `gtk::ListBoxRow` widgets
127/// as children. If you add any other kind of widget, a row is automatically
128/// inserted between the list box and the widget.
129///
130/// For simple widgets like `gtk::Box`, the children type will be `gtk::Widget`,
131/// meaning that it can be any widget type.
132pub trait ContainerChild {
133    /// Type of container children.
134    type Child: IsA<gtk::Widget>;
135}
136
137macro_rules! container_child_impl {
138    ($($type:ty: $child:ty), +) => {
139        $(
140            impl ContainerChild for $type {
141                type Child = $child;
142            }
143        )+
144    };
145    ($($type:ty), +) => {
146        $(
147            #[allow(deprecated)]
148            impl ContainerChild for $type {
149                type Child = gtk::Widget;
150            }
151        )+
152    };
153}
154
155container_child_impl! {
156    gtk::Box,
157    gtk::Fixed,
158    gtk::Grid,
159    gtk::ActionBar,
160    gtk::Stack,
161    gtk::HeaderBar,
162    gtk::InfoBar,
163    gtk::Button,
164    gtk::ComboBox,
165    gtk::FlowBoxChild,
166    gtk::Frame,
167    gtk::Popover,
168    gtk::Window,
169    gtk::ApplicationWindow,
170    gtk::ListBoxRow,
171    gtk::ScrolledWindow,
172    gtk::Dialog,
173    gtk::LinkButton,
174    gtk::ToggleButton,
175    gtk::Overlay,
176    gtk::Revealer,
177    gtk::WindowHandle,
178    gtk::Expander,
179    gtk::AspectFrame
180}
181
182container_child_impl! {
183    gtk::ListBox: gtk::ListBoxRow,
184    gtk::FlowBox: gtk::FlowBoxChild
185}
186
187#[cfg(feature = "libadwaita")]
188#[cfg_attr(docsrs, doc(cfg(feature = "libadwaita")))]
189mod libadwaita {
190    use super::ContainerChild;
191
192    container_child_impl! {
193        adw::TabView,
194        adw::Window,
195        adw::Bin,
196        adw::ApplicationWindow,
197        adw::Clamp,
198        adw::ClampScrollable,
199        adw::SplitButton,
200        adw::StatusPage,
201        adw::PreferencesGroup,
202        adw::ToastOverlay,
203        adw::ExpanderRow,
204        adw::Carousel,
205        adw::Squeezer,
206        adw::Leaflet
207    }
208    container_child_impl! {
209        adw::PreferencesPage: adw::PreferencesGroup
210    }
211
212    #[cfg(all(feature = "libadwaita", feature = "gnome_45"))]
213    mod gnome_45 {
214        use super::ContainerChild;
215
216        container_child_impl! {
217            adw::NavigationView: adw::NavigationPage,
218            adw::NavigationSplitView: adw::NavigationPage
219        }
220        container_child_impl! {
221            adw::NavigationPage,
222            adw::BreakpointBin,
223            adw::OverlaySplitView,
224            adw::ToolbarView
225        }
226    }
227}