Skip to main content

gtk/subclass/
cell_layout.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for implementing the [`CellLayout`] interface.
5
6use std::{ffi::CStr, mem::ManuallyDrop};
7
8use glib::translate::*;
9
10use crate::{
11    CellArea, CellLayout, CellRenderer, TreeIter, TreeModel, ffi, prelude::*, subclass::prelude::*,
12};
13
14// rustdoc-stripper-ignore-next
15/// A cell data func set on a [`CellLayout`]'s [`CellRenderer`].
16///
17/// When implementing [`CellLayoutImpl::set_cell_data_func`], you will need to store these, keyed
18/// to the passed [`CellRenderer`], somewhere in your class's instance data.  Whenever your
19/// implementation needs to set attribute values on the cell renderers for a particular
20/// [`TreeIter`], you should use [`CellDataFunc::call`] in order to do so, if there is a
21/// [`CellDataFunc`] instance present for that cell renderer.
22///
23/// ## Example
24///
25/// ```ignore
26/// # use std::{cell::RefCell, collections::HashMap};
27/// # use gtk::{CellRenderer, TreeIter, TreeModel, subclass::prelude::*};
28/// #
29/// struct MyWidget {
30///    model: RefCell<TreeModel>,
31///    items: RefCell<Vec<MyItem>>,
32///    cells: RefCell<Vec<CellRenderer>>,
33///    cell_data_funcs: RefCell<HashMap<CellRenderer, CellDataFunc>>,
34///    // ... other fields
35/// }
36///
37/// impl CellLayoutImpl for MyWidget {
38///     fn set_cell_data_func(&self, cell: &CellRenderer, cell_data_func: Option<CellDataFunc>) {
39///         // Store or clear the passed CellDataFunc.
40///         if let Some(cell_data_func) = cell_data_func {
41///             self.cell_data_funcs.borrow_mut().insert(cell.clone(), cell_data_func);
42///         } else {
43///             self.cell_data_funcs.borrow_mut().remove(cell);
44///         }
45///         // Things have changed, so redraw.
46///         self.redraw();
47///     }
48/// }
49///
50/// impl MyWidget {
51///     fn redraw(&self) {
52///         for item in &*self.items.borrow() {
53///             let iter: TreeIter = item.iter(&*self.model.borrow());
54///             for cell in &*self.cells.borrow() {
55///                 if let Some(cell_data_func) = self.cell_data_funcs.borrow().get(cell) {
56///                     // There's a CellDataFunc for this CellRenderer, so call it so it can set
57///                     // renderer attributes for the item at this iter.
58///                     cell_data_func.call(&*self.obj(), cell, &*self.model.borrow(), &iter);
59///                 }
60///             }
61///             item.draw(&*self.cells.borrow());
62///         }
63///     }
64/// }
65/// ```
66pub struct CellDataFunc {
67    func: ffi::GtkCellLayoutDataFunc,
68    func_data: glib::ffi::gpointer,
69    destroy: glib::ffi::GDestroyNotify,
70}
71
72impl CellDataFunc {
73    // rustdoc-stripper-ignore-next
74    /// Calls the data func on the specified cell renderer, tree model, and iter.
75    ///
76    /// Usually this will set up `cell`'s attribute values correctly for `iter`.
77    pub fn call(
78        &self,
79        cell_layout: &impl IsA<CellLayout>,
80        cell: &impl IsA<CellRenderer>,
81        model: &impl IsA<TreeModel>,
82        iter: &TreeIter,
83    ) {
84        if let Some(func) = self.func.as_ref() {
85            unsafe {
86                func(
87                    cell_layout.as_ref().to_glib_none().0,
88                    cell.as_ref().to_glib_none().0,
89                    model.as_ref().to_glib_none().0,
90                    mut_override(iter.to_glib_none().0),
91                    self.func_data,
92                );
93            }
94        }
95    }
96}
97
98impl Drop for CellDataFunc {
99    fn drop(&mut self) {
100        if let Some(destroy_ptr) = self.destroy.take() {
101            unsafe {
102                destroy_ptr(self.func_data);
103            }
104        }
105    }
106}
107
108pub trait CellLayoutImpl: ObjectImpl + ObjectSubclass<Type: IsA<CellLayout>> {
109    fn pack_start(&self, cell: &CellRenderer, expand: bool) {
110        self.parent_pack_start(cell, expand);
111    }
112
113    fn pack_end(&self, cell: &CellRenderer, expand: bool) {
114        self.parent_pack_end(cell, expand);
115    }
116
117    fn clear(&self) {
118        self.parent_clear();
119    }
120
121    fn add_attribute(&self, cell: &CellRenderer, attribute: &str, column: i32) {
122        self.parent_add_attribute(cell, attribute, column);
123    }
124
125    fn set_cell_data_func(&self, cell: &CellRenderer, cell_data_func: Option<CellDataFunc>) {
126        self.parent_set_cell_data_func(cell, cell_data_func);
127    }
128
129    fn clear_attributes(&self, cell: &CellRenderer) {
130        self.parent_clear_attributes(cell);
131    }
132
133    fn reorder(&self, cell: &CellRenderer, position: i32) {
134        self.parent_reorder(cell, position);
135    }
136
137    #[doc(alias = "get_cells")]
138    fn cells(&self) -> Vec<CellRenderer> {
139        self.parent_cells()
140    }
141
142    #[doc(alias = "get_area")]
143    fn area(&self) -> Option<CellArea> {
144        self.parent_area()
145    }
146}
147
148pub trait CellLayoutImplExt: CellLayoutImpl {
149    fn parent_pack_start(&self, cell: &CellRenderer, expand: bool) {
150        unsafe {
151            let type_data = Self::type_data();
152            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
153                as *const ffi::GtkCellLayoutIface;
154
155            let func = (*parent_iface)
156                .pack_start
157                .expect("no parent \"pack_start\" implementation");
158            func(
159                self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
160                cell.to_glib_none().0,
161                expand.into_glib(),
162            );
163        }
164    }
165
166    fn parent_pack_end(&self, cell: &CellRenderer, expand: bool) {
167        unsafe {
168            let type_data = Self::type_data();
169            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
170                as *const ffi::GtkCellLayoutIface;
171
172            let func = (*parent_iface)
173                .pack_end
174                .expect("no parent \"pack_end\" implementation");
175            func(
176                self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
177                cell.to_glib_none().0,
178                expand.into_glib(),
179            );
180        }
181    }
182
183    fn parent_clear(&self) {
184        unsafe {
185            let type_data = Self::type_data();
186            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
187                as *const ffi::GtkCellLayoutIface;
188
189            let func = (*parent_iface)
190                .clear
191                .expect("no parent \"clear\" implementation");
192            func(self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0);
193        }
194    }
195
196    fn parent_add_attribute(&self, cell: &CellRenderer, attribute: &str, column: i32) {
197        unsafe {
198            let type_data = Self::type_data();
199            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
200                as *const ffi::GtkCellLayoutIface;
201
202            let func = (*parent_iface)
203                .add_attribute
204                .expect("no parent \"add_attribute\" implementation");
205            func(
206                self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
207                cell.to_glib_none().0,
208                attribute.to_glib_none().0,
209                column,
210            );
211        }
212    }
213
214    fn parent_set_cell_data_func(&self, cell: &CellRenderer, cell_data_func: Option<CellDataFunc>) {
215        unsafe {
216            let type_data = Self::type_data();
217            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
218                as *const ffi::GtkCellLayoutIface;
219
220            let func = (*parent_iface)
221                .set_cell_data_func
222                .expect("no parent \"set_cell_data_func\" implementation");
223
224            let (data_func, data_ptr, destroy_func) = if let Some(cell_data_func) = cell_data_func {
225                // Wrap in ManuallyDrop because we are transferring ownership to the parent class,
226                // and if we drop the `CellDataFunc` struct, then the `GDestroyNotify` will run.
227                let cell_data_func = ManuallyDrop::new(cell_data_func);
228                (
229                    cell_data_func.func,
230                    cell_data_func.func_data,
231                    cell_data_func.destroy,
232                )
233            } else {
234                (None, std::ptr::null_mut(), None)
235            };
236
237            func(
238                self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
239                cell.to_glib_none().0,
240                data_func,
241                data_ptr,
242                destroy_func,
243            );
244        }
245    }
246
247    fn parent_clear_attributes(&self, cell: &CellRenderer) {
248        unsafe {
249            let type_data = Self::type_data();
250            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
251                as *const ffi::GtkCellLayoutIface;
252
253            let func = (*parent_iface)
254                .clear_attributes
255                .expect("no parent \"clear_attributes\" implementation");
256            func(
257                self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
258                cell.to_glib_none().0,
259            );
260        }
261    }
262
263    fn parent_reorder(&self, cell: &CellRenderer, position: i32) {
264        unsafe {
265            let type_data = Self::type_data();
266            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
267                as *const ffi::GtkCellLayoutIface;
268
269            let func = (*parent_iface)
270                .reorder
271                .expect("no parent \"reorder\" implementation");
272            func(
273                self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
274                cell.to_glib_none().0,
275                position,
276            );
277        }
278    }
279
280    fn parent_cells(&self) -> Vec<CellRenderer> {
281        unsafe {
282            let type_data = Self::type_data();
283            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
284                as *const ffi::GtkCellLayoutIface;
285
286            let func = (*parent_iface)
287                .get_cells
288                .expect("no parent \"get_cells\" implementation");
289            FromGlibPtrContainer::from_glib_container(func(
290                self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
291            ))
292        }
293    }
294
295    fn parent_area(&self) -> Option<CellArea> {
296        unsafe {
297            let type_data = Self::type_data();
298            let parent_iface = type_data.as_ref().parent_interface::<CellLayout>()
299                as *const ffi::GtkCellLayoutIface;
300
301            (*parent_iface).get_area.and_then(|func| {
302                from_glib_none(func(
303                    self.obj().unsafe_cast_ref::<CellLayout>().to_glib_none().0,
304                ))
305            })
306        }
307    }
308}
309
310impl<T: CellLayoutImpl> CellLayoutImplExt for T {}
311
312unsafe impl<T: CellLayoutImpl> IsImplementable<T> for CellLayout {
313    fn interface_init(iface: &mut glib::Interface<Self>) {
314        let iface = iface.as_mut();
315
316        if !crate::rt::is_initialized() {
317            panic!("GTK has to be initialized first");
318        }
319
320        iface.pack_start = Some(cell_layout_pack_start::<T>);
321        iface.pack_end = Some(cell_layout_pack_end::<T>);
322        iface.clear = Some(cell_layout_clear::<T>);
323        iface.add_attribute = Some(cell_layout_add_attribute::<T>);
324        iface.set_cell_data_func = Some(cell_layout_set_cell_data_func::<T>);
325        iface.clear_attributes = Some(cell_layout_clear_attributes::<T>);
326        iface.reorder = Some(cell_layout_reorder::<T>);
327        iface.get_cells = Some(cell_layout_get_cells::<T>);
328        iface.get_area = Some(cell_layout_get_area::<T>);
329    }
330}
331
332unsafe extern "C" fn cell_layout_pack_start<T: CellLayoutImpl>(
333    cell_layout_ptr: *mut ffi::GtkCellLayout,
334    cell_ptr: *mut ffi::GtkCellRenderer,
335    expand: glib::ffi::gboolean,
336) {
337    assert!(!cell_layout_ptr.is_null());
338    assert!(!cell_ptr.is_null());
339
340    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
341    let imp = instance.imp();
342    unsafe {
343        imp.pack_start(&from_glib_borrow(cell_ptr), from_glib(expand));
344    }
345}
346
347unsafe extern "C" fn cell_layout_pack_end<T: CellLayoutImpl>(
348    cell_layout_ptr: *mut ffi::GtkCellLayout,
349    cell_ptr: *mut ffi::GtkCellRenderer,
350    expand: glib::ffi::gboolean,
351) {
352    assert!(!cell_layout_ptr.is_null());
353    assert!(!cell_ptr.is_null());
354
355    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
356    let imp = instance.imp();
357    unsafe {
358        imp.pack_end(&from_glib_borrow(cell_ptr), from_glib(expand));
359    }
360}
361
362unsafe extern "C" fn cell_layout_clear<T: CellLayoutImpl>(
363    cell_layout_ptr: *mut ffi::GtkCellLayout,
364) {
365    assert!(!cell_layout_ptr.is_null());
366
367    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
368    let imp = instance.imp();
369    imp.clear();
370}
371
372unsafe extern "C" fn cell_layout_add_attribute<T: CellLayoutImpl>(
373    cell_layout_ptr: *mut ffi::GtkCellLayout,
374    cell_ptr: *mut ffi::GtkCellRenderer,
375    attribute_ptr: *const glib::ffi::gchar,
376    column: i32,
377) {
378    assert!(!cell_layout_ptr.is_null());
379    assert!(!cell_ptr.is_null());
380    assert!(!attribute_ptr.is_null());
381
382    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
383    let imp = instance.imp();
384    unsafe {
385        let attribute = CStr::from_ptr(attribute_ptr).to_str().unwrap();
386        imp.add_attribute(&from_glib_borrow(cell_ptr), attribute, column);
387    }
388}
389
390unsafe extern "C" fn cell_layout_set_cell_data_func<T: CellLayoutImpl>(
391    cell_layout_ptr: *mut ffi::GtkCellLayout,
392    cell_ptr: *mut ffi::GtkCellRenderer,
393    func_ptr: ffi::GtkCellLayoutDataFunc,
394    func_data_ptr: glib::ffi::gpointer,
395    destroy_ptr: glib::ffi::GDestroyNotify,
396) {
397    assert!(!cell_layout_ptr.is_null());
398    assert!(!cell_ptr.is_null());
399
400    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
401    let imp = instance.imp();
402
403    let cell = unsafe { from_glib_borrow(cell_ptr) };
404    let cell_data_func = func_ptr.is_some().then(|| CellDataFunc {
405        func: func_ptr,
406        func_data: func_data_ptr,
407        destroy: destroy_ptr,
408    });
409
410    imp.set_cell_data_func(&cell, cell_data_func);
411}
412
413unsafe extern "C" fn cell_layout_clear_attributes<T: CellLayoutImpl>(
414    cell_layout_ptr: *mut ffi::GtkCellLayout,
415    cell_ptr: *mut ffi::GtkCellRenderer,
416) {
417    assert!(!cell_layout_ptr.is_null());
418    assert!(!cell_ptr.is_null());
419
420    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
421    let imp = instance.imp();
422    unsafe {
423        imp.clear_attributes(&from_glib_borrow(cell_ptr));
424    }
425}
426
427unsafe extern "C" fn cell_layout_reorder<T: CellLayoutImpl>(
428    cell_layout_ptr: *mut ffi::GtkCellLayout,
429    cell_ptr: *mut ffi::GtkCellRenderer,
430    position: i32,
431) {
432    assert!(!cell_layout_ptr.is_null());
433    assert!(!cell_ptr.is_null());
434
435    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
436    let imp = instance.imp();
437    unsafe {
438        imp.reorder(&from_glib_borrow(cell_ptr), position);
439    }
440}
441
442unsafe extern "C" fn cell_layout_get_cells<T: CellLayoutImpl>(
443    cell_layout_ptr: *mut ffi::GtkCellLayout,
444) -> *mut glib::ffi::GList {
445    assert!(!cell_layout_ptr.is_null());
446
447    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
448    let imp = instance.imp();
449    imp.cells().to_glib_container().0
450}
451
452unsafe extern "C" fn cell_layout_get_area<T: CellLayoutImpl>(
453    cell_layout_ptr: *mut ffi::GtkCellLayout,
454) -> *mut ffi::GtkCellArea {
455    assert!(!cell_layout_ptr.is_null());
456
457    let instance = unsafe { &*(cell_layout_ptr as *mut T::Instance) };
458    let imp = instance.imp();
459    imp.area().to_glib_none().0
460}