Skip to main content

gtk/subclass/
container.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::translate::*;
4
5use glib::subclass::prelude::*;
6
7use glib::Cast;
8
9use super::widget::WidgetImpl;
10use crate::Container;
11use crate::Widget;
12use crate::WidgetPath;
13
14pub trait ContainerImpl: ContainerImplExt + WidgetImpl {
15    /// Adds `widget` to `self`. Typically used for simple containers
16    /// such as [`Window`][crate::Window], [`Frame`][crate::Frame], or [`Button`][crate::Button]; for more complicated
17    /// layout containers such as [`Box`][crate::Box] or [`Grid`][crate::Grid], this function will
18    /// pick default packing parameters that may not be correct. So
19    /// consider functions such as [`BoxExt::pack_start()`][crate::prelude::BoxExt::pack_start()] and
20    /// [`GridExt::attach()`][crate::prelude::GridExt::attach()] as an alternative to [`ContainerExt::add()`][crate::prelude::ContainerExt::add()] in
21    /// those cases. A widget may be added to only one container at a time;
22    /// you can’t place the same widget inside two different containers.
23    ///
24    /// Note that some containers, such as [`ScrolledWindow`][crate::ScrolledWindow] or [`ListBox`][crate::ListBox],
25    /// may add intermediate children between the added widget and the
26    /// container.
27    /// ## `widget`
28    /// a widget to be placed inside `self`
29    fn add(&self, widget: &Widget) {
30        self.parent_add(widget)
31    }
32
33    /// Removes `widget` from `self`. `widget` must be inside `self`.
34    /// Note that `self` will own a reference to `widget`, and that this
35    /// may be the last reference held; so removing a widget from its
36    /// container can destroy that widget. If you want to use `widget`
37    /// again, you need to add a reference to it before removing it from
38    /// a container, using `g_object_ref()`. If you don’t want to use `widget`
39    /// again it’s usually more efficient to simply destroy it directly
40    /// using `gtk_widget_destroy()` since this will remove it from the
41    /// container and help break any circular reference count cycles.
42    /// ## `widget`
43    /// a current child of `self`
44    fn remove(&self, widget: &Widget) {
45        self.parent_remove(widget)
46    }
47
48    fn check_resize(&self) {
49        self.parent_check_resize()
50    }
51
52    /// Sets, or unsets if `child` is [`None`], the focused child of `self`.
53    ///
54    /// This function emits the GtkContainer::set_focus_child signal of
55    /// `self`. Implementations of [`Container`][crate::Container] can override the
56    /// default behaviour by overriding the class closure of this signal.
57    ///
58    /// This is function is mostly meant to be used by widgets. Applications can use
59    /// [`WidgetExt::grab_focus()`][crate::prelude::WidgetExt::grab_focus()] to manually set the focus to a specific widget.
60    /// ## `child`
61    /// a [`Widget`][crate::Widget], or [`None`]
62    fn set_focus_child(&self, widget: Option<&Widget>) {
63        self.parent_set_focus_child(widget)
64    }
65
66    /// Returns the type of the children supported by the container.
67    ///
68    /// Note that this may return `G_TYPE_NONE` to indicate that no more
69    /// children can be added, e.g. for a [`Paned`][crate::Paned] which already has two
70    /// children.
71    ///
72    /// # Returns
73    ///
74    /// a `GType`.
75    fn child_type(&self) -> glib::Type {
76        self.parent_child_type()
77    }
78
79    /// Returns a newly created widget path representing all the widget hierarchy
80    /// from the toplevel down to and including `child`.
81    /// ## `child`
82    /// a child of `self`
83    ///
84    /// # Returns
85    ///
86    /// A newly created [`WidgetPath`][crate::WidgetPath]
87    #[doc(alias = "get_path_for_child")]
88    fn path_for_child(&self, widget: &Widget) -> WidgetPath {
89        self.parent_path_for_child(widget)
90    }
91
92    /// Invokes `callback` on each direct child of `self`, including
93    /// children that are considered “internal” (implementation details
94    /// of the container). “Internal” children generally weren’t added
95    /// by the user of the container, but were added by the container
96    /// implementation itself.
97    ///
98    /// Most applications should use [`ContainerExt::foreach()`][crate::prelude::ContainerExt::foreach()], rather
99    /// than [`ContainerExt::forall()`][crate::prelude::ContainerExt::forall()].
100    /// ## `callback`
101    /// a callback
102    /// ## `callback_data`
103    /// callback user data
104    fn forall(&self, include_internals: bool, callback: &Callback) {
105        self.parent_forall(include_internals, callback);
106    }
107}
108
109mod sealed {
110    pub trait Sealed {}
111    impl<T: super::ContainerImpl> Sealed for T {}
112}
113
114pub trait ContainerImplExt: ObjectSubclass + sealed::Sealed {
115    fn parent_add(&self, widget: &Widget) {
116        unsafe {
117            let data = Self::type_data();
118            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
119            if let Some(f) = (*parent_class).add {
120                f(
121                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
122                    widget.to_glib_none().0,
123                )
124            }
125        }
126    }
127    fn parent_remove(&self, widget: &Widget) {
128        unsafe {
129            let data = Self::type_data();
130            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
131            if let Some(f) = (*parent_class).remove {
132                f(
133                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
134                    widget.to_glib_none().0,
135                )
136            }
137        }
138    }
139    fn parent_check_resize(&self) {
140        unsafe {
141            let data = Self::type_data();
142            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
143            if let Some(f) = (*parent_class).check_resize {
144                f(self.obj().unsafe_cast_ref::<Container>().to_glib_none().0)
145            }
146        }
147    }
148    fn parent_set_focus_child(&self, widget: Option<&Widget>) {
149        unsafe {
150            let data = Self::type_data();
151            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
152            if let Some(f) = (*parent_class).set_focus_child {
153                f(
154                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
155                    widget.to_glib_none().0,
156                )
157            }
158        }
159    }
160    fn parent_child_type(&self) -> glib::Type {
161        unsafe {
162            let data = Self::type_data();
163            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
164            if let Some(f) = (*parent_class).child_type {
165                from_glib(f(self
166                    .obj()
167                    .unsafe_cast_ref::<Container>()
168                    .to_glib_none()
169                    .0))
170            } else {
171                glib::Type::UNIT
172            }
173        }
174    }
175    fn parent_path_for_child(&self, widget: &Widget) -> WidgetPath {
176        unsafe {
177            let data = Self::type_data();
178            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
179            let f = (*parent_class)
180                .get_path_for_child
181                .expect("No parent class impl for \"get_path_for_child\"");
182            from_glib_none(f(
183                self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
184                widget.to_glib_none().0,
185            ))
186        }
187    }
188    fn parent_forall(&self, include_internals: bool, callback: &Callback) {
189        unsafe {
190            let data = Self::type_data();
191            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
192            if let Some(f) = (*parent_class).forall {
193                f(
194                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
195                    include_internals.into_glib(),
196                    callback.callback,
197                    callback.user_data,
198                )
199            }
200        }
201    }
202}
203
204impl<T: ContainerImpl> ContainerImplExt for T {}
205
206unsafe impl<T: ContainerImpl> IsSubclassable<T> for Container {
207    fn class_init(class: &mut ::glib::Class<Self>) {
208        Self::parent_class_init::<T>(class);
209
210        if !crate::rt::is_initialized() {
211            panic!("GTK has to be initialized first");
212        }
213
214        let klass = class.as_mut();
215        klass.add = Some(container_add::<T>);
216        klass.remove = Some(container_remove::<T>);
217        klass.check_resize = Some(container_check_resize::<T>);
218        klass.set_focus_child = Some(container_set_focus_child::<T>);
219        klass.child_type = Some(container_child_type::<T>);
220        klass.get_path_for_child = Some(container_get_path_for_child::<T>);
221        klass.forall = Some(container_forall::<T>);
222    }
223}
224
225unsafe extern "C" fn container_add<T: ContainerImpl>(
226    ptr: *mut ffi::GtkContainer,
227    wdgtptr: *mut ffi::GtkWidget,
228) {
229    let instance = &*(ptr as *mut T::Instance);
230    let imp = instance.imp();
231    let widget: Borrowed<Widget> = from_glib_borrow(wdgtptr);
232
233    imp.add(&widget)
234}
235
236unsafe extern "C" fn container_remove<T: ContainerImpl>(
237    ptr: *mut ffi::GtkContainer,
238    wdgtptr: *mut ffi::GtkWidget,
239) {
240    let instance = &*(ptr as *mut T::Instance);
241    let imp = instance.imp();
242    let widget: Borrowed<Widget> = from_glib_borrow(wdgtptr);
243
244    imp.remove(&widget)
245}
246
247unsafe extern "C" fn container_check_resize<T: ContainerImpl>(ptr: *mut ffi::GtkContainer) {
248    let instance = &*(ptr as *mut T::Instance);
249    let imp = instance.imp();
250
251    imp.check_resize()
252}
253
254unsafe extern "C" fn container_set_focus_child<T: ContainerImpl>(
255    ptr: *mut ffi::GtkContainer,
256    wdgtptr: *mut ffi::GtkWidget,
257) {
258    let instance = &*(ptr as *mut T::Instance);
259    let imp = instance.imp();
260    let widget: Borrowed<Option<Widget>> = from_glib_borrow(wdgtptr);
261
262    imp.set_focus_child(widget.as_ref().as_ref())
263}
264
265unsafe extern "C" fn container_child_type<T: ContainerImpl>(
266    ptr: *mut ffi::GtkContainer,
267) -> glib::ffi::GType {
268    let instance = &*(ptr as *mut T::Instance);
269    let imp = instance.imp();
270
271    imp.child_type().into_glib()
272}
273
274unsafe extern "C" fn container_get_path_for_child<T: ContainerImpl>(
275    ptr: *mut ffi::GtkContainer,
276    wdgtptr: *mut ffi::GtkWidget,
277) -> *mut ffi::GtkWidgetPath {
278    let instance = &*(ptr as *mut T::Instance);
279    let imp = instance.imp();
280    let widget: Borrowed<Widget> = from_glib_borrow(wdgtptr);
281
282    imp.path_for_child(&widget).to_glib_none().0
283}
284
285unsafe extern "C" fn container_forall<T: ObjectSubclass>(
286    ptr: *mut ffi::GtkContainer,
287    include_internals: glib::ffi::gboolean,
288    callback: ffi::GtkCallback,
289    user_data: glib::ffi::gpointer,
290) where
291    T: ContainerImpl,
292{
293    let instance = &*(ptr as *mut T::Instance);
294    let imp = instance.imp();
295    let callback = Callback {
296        callback,
297        user_data,
298    };
299
300    imp.forall(from_glib(include_internals), &callback)
301}
302
303#[derive(Debug)]
304pub struct Callback {
305    callback: ffi::GtkCallback,
306    user_data: glib::ffi::gpointer,
307}
308
309impl Callback {
310    pub fn call(&self, widget: &Widget) {
311        unsafe {
312            if let Some(callback) = self.callback {
313                callback(widget.to_glib_none().0, self.user_data);
314            }
315        }
316    }
317}
318
319pub unsafe trait ContainerClassSubclassExt: ClassStruct {
320    #[doc(alias = "gtk_container_class_handle_border_width")]
321    fn handle_border_width(&mut self) {
322        unsafe {
323            let widget_class = self as *mut _ as *mut ffi::GtkContainerClass;
324            ffi::gtk_container_class_handle_border_width(widget_class);
325        }
326    }
327}
328
329unsafe impl<T: ClassStruct> ContainerClassSubclassExt for T where T::Type: ContainerImpl {}