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 std::ptr;
4
5use glib::{ParamSpec, Value, gobject_ffi, translate::*};
6
7use glib::object::IsA;
8use glib::subclass::prelude::*;
9
10use glib::object::Cast;
11
12use super::widget::WidgetImpl;
13use crate::Widget;
14use crate::WidgetPath;
15use crate::{Container, ffi};
16
17pub trait ContainerImpl: WidgetImpl + ObjectSubclass<Type: IsA<Container>> {
18    // rustdoc-stripper-ignore-next
19    /// Child properties installed for this type.
20    ///
21    /// Override and return an array of [`ParamSpec`] to register new child properties on your
22    /// subclass.
23    fn child_properties() -> &'static [ParamSpec] {
24        &[]
25    }
26
27    /// Adds `widget` to `container`. Typically used for simple containers
28    /// such as [`Window`][crate::Window], [`Frame`][crate::Frame], or [`Button`][crate::Button]; for more complicated
29    /// layout containers such as [`Box`][crate::Box] or [`Grid`][crate::Grid], this function will
30    /// pick default packing parameters that may not be correct. So
31    /// consider functions such as [`BoxExt::pack_start()`][crate::prelude::BoxExt::pack_start()] and
32    /// [`GridExt::attach()`][crate::prelude::GridExt::attach()] as an alternative to [`ContainerExt::add()`][crate::prelude::ContainerExt::add()] in
33    /// those cases. A widget may be added to only one container at a time;
34    /// you can’t place the same widget inside two different containers.
35    ///
36    /// Note that some containers, such as [`ScrolledWindow`][crate::ScrolledWindow] or [`ListBox`][crate::ListBox],
37    /// may add intermediate children between the added widget and the
38    /// container.
39    /// ## `widget`
40    /// a widget to be placed inside `container`
41    fn add(&self, widget: &Widget) {
42        self.parent_add(widget)
43    }
44
45    /// Removes `widget` from `container`. `widget` must be inside `container`.
46    /// Note that `container` will own a reference to `widget`, and that this
47    /// may be the last reference held; so removing a widget from its
48    /// container can destroy that widget. If you want to use `widget`
49    /// again, you need to add a reference to it before removing it from
50    /// a container, using `g_object_ref()`. If you don’t want to use `widget`
51    /// again it’s usually more efficient to simply destroy it directly
52    /// using `gtk_widget_destroy()` since this will remove it from the
53    /// container and help break any circular reference count cycles.
54    /// ## `widget`
55    /// a current child of `container`
56    fn remove(&self, widget: &Widget) {
57        self.parent_remove(widget)
58    }
59
60    /// Signal emitted when a size recalculation is needed.
61    fn check_resize(&self) {
62        self.parent_check_resize()
63    }
64
65    /// Sets, or unsets if `child` is [`None`], the focused child of `container`.
66    ///
67    /// This function emits the GtkContainer::set_focus_child signal of
68    /// `container`. Implementations of [`Container`][crate::Container] can override the
69    /// default behaviour by overriding the class closure of this signal.
70    ///
71    /// This is function is mostly meant to be used by widgets. Applications can use
72    /// [`WidgetExt::grab_focus()`][crate::prelude::WidgetExt::grab_focus()] to manually set the focus to a specific widget.
73    /// ## `child`
74    /// a [`Widget`][crate::Widget], or [`None`]
75    fn set_focus_child(&self, widget: Option<&Widget>) {
76        self.parent_set_focus_child(widget)
77    }
78
79    /// Returns the type of the children supported by the container.
80    ///
81    /// Note that this may return `G_TYPE_NONE` to indicate that no more
82    /// children can be added, e.g. for a [`Paned`][crate::Paned] which already has two
83    /// children.
84    ///
85    /// # Returns
86    ///
87    /// a `GType`.
88    fn child_type(&self) -> glib::Type {
89        self.parent_child_type()
90    }
91
92    /// Returns a newly created widget path representing all the widget hierarchy
93    /// from the toplevel down to and including `child`.
94    /// ## `child`
95    /// a child of `container`
96    ///
97    /// # Returns
98    ///
99    /// A newly created [`WidgetPath`][crate::WidgetPath]
100    #[doc(alias = "get_path_for_child")]
101    fn path_for_child(&self, widget: &Widget) -> WidgetPath {
102        self.parent_path_for_child(widget)
103    }
104
105    /// Invokes `callback` on each direct child of `container`, including
106    /// children that are considered “internal” (implementation details
107    /// of the container). “Internal” children generally weren’t added
108    /// by the user of the container, but were added by the container
109    /// implementation itself.
110    ///
111    /// Most applications should use [`ContainerExt::foreach()`][crate::prelude::ContainerExt::foreach()], rather
112    /// than [`ContainerExt::forall()`][crate::prelude::ContainerExt::forall()].
113    /// ## `callback`
114    /// a callback
115    /// ## `callback_data`
116    /// callback user data
117    fn forall(&self, include_internals: bool, callback: &Callback) {
118        self.parent_forall(include_internals, callback);
119    }
120
121    /// Set a property on a child of container.
122    fn set_child_property(&self, _child: &Widget, _id: usize, _value: &Value, _pspec: &ParamSpec) {
123        unimplemented!()
124    }
125
126    /// Get a property from a child of container.
127    #[doc(alias = "get_child_property")]
128    fn child_property(&self, _child: &Widget, _id: usize, _pspec: &ParamSpec) -> Value {
129        unimplemented!()
130    }
131}
132
133pub trait ContainerImplExt: ContainerImpl {
134    fn parent_add(&self, widget: &Widget) {
135        unsafe {
136            let data = Self::type_data();
137            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
138            if let Some(f) = (*parent_class).add {
139                f(
140                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
141                    widget.to_glib_none().0,
142                )
143            }
144        }
145    }
146    fn parent_remove(&self, widget: &Widget) {
147        unsafe {
148            let data = Self::type_data();
149            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
150            if let Some(f) = (*parent_class).remove {
151                f(
152                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
153                    widget.to_glib_none().0,
154                )
155            }
156        }
157    }
158    fn parent_check_resize(&self) {
159        unsafe {
160            let data = Self::type_data();
161            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
162            if let Some(f) = (*parent_class).check_resize {
163                f(self.obj().unsafe_cast_ref::<Container>().to_glib_none().0)
164            }
165        }
166    }
167    fn parent_set_focus_child(&self, widget: Option<&Widget>) {
168        unsafe {
169            let data = Self::type_data();
170            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
171            if let Some(f) = (*parent_class).set_focus_child {
172                f(
173                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
174                    widget.to_glib_none().0,
175                )
176            }
177        }
178    }
179    fn parent_child_type(&self) -> glib::Type {
180        unsafe {
181            let data = Self::type_data();
182            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
183            if let Some(f) = (*parent_class).child_type {
184                from_glib(f(self
185                    .obj()
186                    .unsafe_cast_ref::<Container>()
187                    .to_glib_none()
188                    .0))
189            } else {
190                glib::Type::UNIT
191            }
192        }
193    }
194    fn parent_path_for_child(&self, widget: &Widget) -> WidgetPath {
195        unsafe {
196            let data = Self::type_data();
197            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
198            let f = (*parent_class)
199                .get_path_for_child
200                .expect("No parent class impl for \"get_path_for_child\"");
201            from_glib_none(f(
202                self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
203                widget.to_glib_none().0,
204            ))
205        }
206    }
207    fn parent_forall(&self, include_internals: bool, callback: &Callback) {
208        unsafe {
209            let data = Self::type_data();
210            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkContainerClass;
211            if let Some(f) = (*parent_class).forall {
212                f(
213                    self.obj().unsafe_cast_ref::<Container>().to_glib_none().0,
214                    include_internals.into_glib(),
215                    callback.callback,
216                    callback.user_data,
217                )
218            }
219        }
220    }
221}
222
223impl<T: ContainerImpl> ContainerImplExt for T {}
224
225unsafe impl<T: ContainerImpl> IsSubclassable<T> for Container {
226    fn class_init(class: &mut ::glib::Class<Self>) {
227        Self::parent_class_init::<T>(class);
228
229        if !crate::rt::is_initialized() {
230            panic!("GTK has to be initialized first");
231        }
232
233        let klass = class.as_mut();
234        klass.add = Some(container_add::<T>);
235        klass.remove = Some(container_remove::<T>);
236        klass.check_resize = Some(container_check_resize::<T>);
237        klass.set_focus_child = Some(container_set_focus_child::<T>);
238        klass.child_type = Some(container_child_type::<T>);
239        klass.get_path_for_child = Some(container_get_path_for_child::<T>);
240        klass.forall = Some(container_forall::<T>);
241        klass.set_child_property = Some(container_set_child_property::<T>);
242        klass.get_child_property = Some(container_get_child_property::<T>);
243
244        let pspecs = <T as ContainerImpl>::child_properties();
245        if !pspecs.is_empty() {
246            unsafe {
247                let mut pspecs_ptrs = std::iter::once(ptr::null_mut())
248                    .chain(pspecs.iter().map(|pspec| pspec.to_glib_none().0))
249                    .collect::<Vec<_>>();
250                ffi::gtk_container_class_install_child_properties(
251                    klass,
252                    pspecs_ptrs.len() as u32,
253                    pspecs_ptrs.as_mut_ptr(),
254                );
255            }
256        }
257    }
258}
259
260unsafe extern "C" fn container_add<T: ContainerImpl>(
261    ptr: *mut ffi::GtkContainer,
262    wdgtptr: *mut ffi::GtkWidget,
263) {
264    unsafe {
265        let instance = &*(ptr as *mut T::Instance);
266        let imp = instance.imp();
267        let widget: Borrowed<Widget> = from_glib_borrow(wdgtptr);
268
269        imp.add(&widget)
270    }
271}
272
273unsafe extern "C" fn container_remove<T: ContainerImpl>(
274    ptr: *mut ffi::GtkContainer,
275    wdgtptr: *mut ffi::GtkWidget,
276) {
277    unsafe {
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.remove(&widget)
283    }
284}
285
286unsafe extern "C" fn container_check_resize<T: ContainerImpl>(ptr: *mut ffi::GtkContainer) {
287    unsafe {
288        let instance = &*(ptr as *mut T::Instance);
289        let imp = instance.imp();
290
291        imp.check_resize()
292    }
293}
294
295unsafe extern "C" fn container_set_focus_child<T: ContainerImpl>(
296    ptr: *mut ffi::GtkContainer,
297    wdgtptr: *mut ffi::GtkWidget,
298) {
299    unsafe {
300        let instance = &*(ptr as *mut T::Instance);
301        let imp = instance.imp();
302        let widget: Borrowed<Option<Widget>> = from_glib_borrow(wdgtptr);
303
304        imp.set_focus_child(widget.as_ref().as_ref())
305    }
306}
307
308unsafe extern "C" fn container_child_type<T: ContainerImpl>(
309    ptr: *mut ffi::GtkContainer,
310) -> glib::ffi::GType {
311    unsafe {
312        let instance = &*(ptr as *mut T::Instance);
313        let imp = instance.imp();
314
315        imp.child_type().into_glib()
316    }
317}
318
319unsafe extern "C" fn container_get_path_for_child<T: ContainerImpl>(
320    ptr: *mut ffi::GtkContainer,
321    wdgtptr: *mut ffi::GtkWidget,
322) -> *mut ffi::GtkWidgetPath {
323    unsafe {
324        let instance = &*(ptr as *mut T::Instance);
325        let imp = instance.imp();
326        let widget: Borrowed<Widget> = from_glib_borrow(wdgtptr);
327
328        imp.path_for_child(&widget).to_glib_none().0
329    }
330}
331
332unsafe extern "C" fn container_forall<T>(
333    ptr: *mut ffi::GtkContainer,
334    include_internals: glib::ffi::gboolean,
335    callback: ffi::GtkCallback,
336    user_data: glib::ffi::gpointer,
337) where
338    T: ObjectSubclass + ContainerImpl,
339{
340    unsafe {
341        let instance = &*(ptr as *mut T::Instance);
342        let imp = instance.imp();
343        let callback = Callback {
344            callback,
345            user_data,
346        };
347
348        imp.forall(from_glib(include_internals), &callback)
349    }
350}
351
352unsafe extern "C" fn container_set_child_property<T: ContainerImpl>(
353    ptr: *mut ffi::GtkContainer,
354    childptr: *mut ffi::GtkWidget,
355    property_id: libc::c_uint,
356    valueptr: *mut gobject_ffi::GValue,
357    pspecptr: *mut gobject_ffi::GParamSpec,
358) {
359    unsafe {
360        let instance = &*(ptr as *mut T::Instance);
361        let imp = instance.imp();
362        let child: Borrowed<Widget> = from_glib_borrow(childptr);
363        let value: Borrowed<glib::Value> = from_glib_borrow(valueptr);
364        let pspec: Borrowed<ParamSpec> = from_glib_borrow(pspecptr);
365
366        imp.set_child_property(&child, property_id as usize, &value, &pspec);
367    }
368}
369
370unsafe extern "C" fn container_get_child_property<T: ContainerImpl>(
371    ptr: *mut ffi::GtkContainer,
372    childptr: *mut ffi::GtkWidget,
373    property_id: libc::c_uint,
374    valueptr: *mut gobject_ffi::GValue,
375    pspecptr: *mut gobject_ffi::GParamSpec,
376) {
377    unsafe {
378        let instance = &*(ptr as *mut T::Instance);
379        let imp = instance.imp();
380        let child: Borrowed<Widget> = from_glib_borrow(childptr);
381        let pspec: Borrowed<ParamSpec> = from_glib_borrow(pspecptr);
382
383        let v = imp.child_property(&child, property_id as usize, &pspec);
384
385        // Unset first just in case there's anything in there already.
386        gobject_ffi::g_value_unset(valueptr);
387        // Then consume `v` and transfer ownership of its bits to `valueptr`.
388        std::ptr::write(valueptr, v.into_raw());
389    }
390}
391
392#[derive(Debug)]
393pub struct Callback {
394    callback: ffi::GtkCallback,
395    user_data: glib::ffi::gpointer,
396}
397
398impl Callback {
399    pub fn call(&self, widget: &Widget) {
400        unsafe {
401            if let Some(callback) = self.callback {
402                callback(widget.to_glib_none().0, self.user_data);
403            }
404        }
405    }
406}
407
408pub unsafe trait ContainerClassSubclassExt: ClassStruct {
409    #[doc(alias = "gtk_container_class_handle_border_width")]
410    fn handle_border_width(&mut self) {
411        unsafe {
412            let widget_class = self as *mut _ as *mut ffi::GtkContainerClass;
413            ffi::gtk_container_class_handle_border_width(widget_class);
414        }
415    }
416}
417
418unsafe impl<T: ClassStruct> ContainerClassSubclassExt for T where T::Type: ContainerImpl {}