relm4/extensions/
widget_ext.rs

1use gtk::prelude::{Cast, IsA, StaticType, WidgetExt};
2
3/// Trait that extends [`gtk::prelude::WidgetExt`].
4///
5/// This trait's main goal is to reduce redundant code and
6/// to provide helpful methods for the widgets macro of relm4-macros.
7pub trait RelmWidgetExt {
8    /// Attach widget to a `gtk::SizeGroup`.
9    fn set_size_group(&self, size_group: &gtk::SizeGroup);
10
11    /// Locate the top level window this widget is attached to.
12    ///
13    /// Equivalent to `widget.ancestor(gtk::Window::static_type())`, then casting.
14    fn toplevel_window(&self) -> Option<gtk::Window>;
15
16    /// Set margin at start, end, top and bottom all at once.
17    fn set_margin_all(&self, margin: i32) {
18        self.set_margin_horizontal(margin);
19        self.set_margin_vertical(margin);
20    }
21
22    /// Set margin at top and bottom at once.
23    fn set_margin_vertical(&self, margin: i32);
24
25    /// Set margin at start and end at once.
26    fn set_margin_horizontal(&self, margin: i32);
27
28    /// Set both horizontal and vertical expand properties at once.
29    fn set_expand(&self, expand: bool);
30
31    /// Set both horizontal and vertical align properties at once.
32    fn set_align(&self, align: gtk::Align);
33
34    /// Add class name if active is [`true`] and
35    /// remove class name if active is [`false`]
36    fn set_class_active(&self, class: &str, active: bool);
37
38    /// Add inline CSS instructions to a widget.
39    /// ```
40    /// # use relm4::RelmWidgetExt;
41    /// # gtk::init().unwrap();
42    /// # let widget = gtk::Button::new();
43    /// widget.inline_css("border: 1px solid red");
44    /// ```
45    fn inline_css(&self, style: &str);
46
47    /// Sets the tooltip text of a widget and enables is.
48    ///
49    /// This is basically, the same as using [`WidgetExt::set_has_tooltip()`]
50    /// and [`WidgetExt::set_tooltip_text()`], but with fewer steps.
51    fn set_tooltip(&self, test: &str);
52}
53
54impl<T: IsA<gtk::Widget>> RelmWidgetExt for T {
55    fn set_size_group(&self, size_group: &gtk::SizeGroup) {
56        size_group.add_widget(self);
57    }
58
59    fn toplevel_window(&self) -> Option<gtk::Window> {
60        self.ancestor(gtk::Window::static_type())
61            .and_then(|widget| widget.dynamic_cast::<gtk::Window>().ok())
62    }
63
64    fn set_margin_vertical(&self, margin: i32) {
65        self.set_margin_top(margin);
66        self.set_margin_bottom(margin);
67    }
68
69    fn set_margin_horizontal(&self, margin: i32) {
70        self.set_margin_start(margin);
71        self.set_margin_end(margin);
72    }
73
74    fn set_class_active(&self, class: &str, active: bool) {
75        if active {
76            self.add_css_class(class);
77        } else {
78            self.remove_css_class(class);
79        }
80    }
81
82    fn set_expand(&self, expand: bool) {
83        self.set_hexpand(expand);
84        self.set_vexpand(expand);
85    }
86
87    fn set_align(&self, align: gtk::Align) {
88        self.set_halign(align);
89        self.set_valign(align);
90    }
91
92    #[allow(deprecated)]
93    fn inline_css(&self, style: &str) {
94        use gtk::prelude::StyleContextExt;
95
96        let context = self.style_context();
97        let provider = gtk::CssProvider::new();
98
99        let data = if style.ends_with(';') {
100            ["*{", style, "}"].concat()
101        } else {
102            ["*{", style, ";}"].concat()
103        };
104
105        provider.load_from_data(&data);
106        context.add_provider(&provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
107    }
108
109    fn set_tooltip(&self, text: &str) {
110        self.set_has_tooltip(true);
111        self.set_tooltip_text(Some(text));
112    }
113}