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