libpanel/subclass/
widget.rs

1use crate::{prelude::*, Widget};
2use glib::translate::*;
3use glib::GString;
4use glib::Variant;
5use gtk::subclass::prelude::*;
6use std::collections::HashMap;
7use std::future::Future;
8
9#[derive(Debug, Default)]
10struct Internal {
11    actions: HashMap<String, glib::ffi::gpointer>,
12}
13unsafe impl Sync for Internal {}
14unsafe impl Send for Internal {}
15
16pub trait PanelWidgetImpl: WidgetImpl {
17    fn default_focus(&self) -> Option<gtk::Widget> {
18        PanelWidgetImplExt::parent_default_focus(self)
19    }
20    fn presented(&self) {
21        PanelWidgetImplExt::parent_presented(self)
22    }
23}
24
25pub trait PanelWidgetImplExt: ObjectSubclass {
26    fn parent_default_focus(&self) -> Option<gtk::Widget>;
27    fn parent_presented(&self);
28}
29
30impl<T: PanelWidgetImpl> PanelWidgetImplExt for T {
31    fn parent_default_focus(&self) -> Option<gtk::Widget> {
32        unsafe {
33            let data = T::type_data();
34            let parent_class = data.as_ref().parent_class() as *mut ffi::PanelWidgetClass;
35            if let Some(f) = (*parent_class).get_default_focus {
36                return from_glib_none(f(self.obj().unsafe_cast_ref::<Widget>().to_glib_none().0));
37            }
38            None
39        }
40    }
41    fn parent_presented(&self) {
42        unsafe {
43            let data = T::type_data();
44            let parent_class = data.as_ref().parent_class() as *mut ffi::PanelWidgetClass;
45            if let Some(f) = (*parent_class).presented {
46                f(self.obj().unsafe_cast_ref::<Widget>().to_glib_none().0);
47            }
48        }
49    }
50}
51
52unsafe impl<T: PanelWidgetImpl> IsSubclassable<T> for Widget {
53    fn class_init(class: &mut glib::Class<Self>) {
54        Self::parent_class_init::<T>(class);
55        unsafe {
56            let mut data = T::type_data();
57            let data = data.as_mut();
58            // Used to store actions for `install_action` and `rust_builder_scope`
59            data.set_class_data(<T as ObjectSubclassType>::type_(), Internal::default());
60        }
61        let klass = class.as_mut();
62        klass.get_default_focus = Some(widget_get_default_focus::<T>);
63        klass.presented = Some(widget_presented::<T>);
64    }
65}
66
67unsafe extern "C" fn widget_get_default_focus<T: PanelWidgetImpl>(
68    ptr: *mut ffi::PanelWidget,
69) -> *mut gtk::ffi::GtkWidget {
70    let instance = &*(ptr as *mut T::Instance);
71    let imp = instance.imp();
72
73    PanelWidgetImpl::default_focus(imp).to_glib_none().0
74}
75
76unsafe extern "C" fn widget_presented<T: PanelWidgetImpl>(ptr: *mut ffi::PanelWidget) {
77    let instance = &*(ptr as *mut T::Instance);
78    let imp = instance.imp();
79
80    PanelWidgetImpl::presented(imp);
81}
82
83pub unsafe trait PanelWidgetClassSubclassExt: ClassStruct {
84    fn install_action_async<Fut, F>(
85        &mut self,
86        action_name: &str,
87        parameter_type: Option<&str>,
88        activate: F,
89    ) where
90        F: Fn(
91                <<Self as ClassStruct>::Type as ObjectSubclass>::Type,
92                String,
93                Option<Variant>,
94            ) -> Fut
95            + 'static
96            + Clone,
97        Fut: Future<Output = ()>,
98    {
99        self.install_action(
100            action_name,
101            parameter_type,
102            move |this, action_name, parameter_type| {
103                let ctx = glib::MainContext::default();
104                let action_name = action_name.to_owned();
105                let parameter_type = parameter_type.map(ToOwned::to_owned);
106                ctx.spawn_local(glib::clone!(
107                    #[strong]
108                    this,
109                    #[strong]
110                    action_name,
111                    #[strong]
112                    parameter_type,
113                    #[strong]
114                    activate,
115                    async move {
116                        activate(this, action_name, parameter_type).await;
117                    }
118                ));
119            },
120        );
121    }
122
123    #[doc(alias = "panel_widget_class_install_action")]
124    fn install_action<F>(&mut self, action_name: &str, parameter_type: Option<&str>, activate: F)
125    where
126        F: Fn(&<<Self as ClassStruct>::Type as ObjectSubclass>::Type, &str, Option<&Variant>)
127            + 'static,
128    {
129        unsafe {
130            // We store the activate callbacks in a HashMap<action_name, activate>
131            // so that we can retrieve f later on the activate_trampoline call
132            let mut data = <Self::Type as ObjectSubclassType>::type_data();
133            let data = data.as_mut();
134
135            let f: Box<F> = Box::new(activate);
136
137            let internal = data
138                .class_data_mut::<Internal>(<Self::Type as ObjectSubclassType>::type_())
139                .expect("Something bad happened at class_init, the internal class_data is missing");
140            let callback_ptr = Box::into_raw(f) as glib::ffi::gpointer;
141            internal
142                .actions
143                .insert(action_name.to_string(), callback_ptr);
144
145            unsafe extern "C" fn activate_trampoline<F, S>(
146                this: *mut gtk::ffi::GtkWidget,
147                action_name: *const libc::c_char,
148                parameter: *mut glib::ffi::GVariant,
149            ) where
150                S: ClassStruct,
151                <S as ClassStruct>::Type: ObjectSubclass,
152                F: Fn(&<<S as ClassStruct>::Type as ObjectSubclass>::Type, &str, Option<&Variant>)
153                    + 'static,
154            {
155                let action_name = GString::from_glib_borrow(action_name);
156
157                let data = <S::Type as ObjectSubclassType>::type_data();
158                let internal = data
159                    .as_ref()
160                    .class_data::<Internal>(<S::Type as ObjectSubclassType>::type_())
161                    .unwrap();
162                let activate_callback =
163                    *internal
164                        .actions
165                        .get(action_name.as_str())
166                        .unwrap_or_else(|| {
167                            panic!("Action name '{}' was not found", action_name.as_str());
168                        });
169
170                let widget = gtk::Widget::from_glib_borrow(this);
171
172                let f: &F = &*(activate_callback as *const F);
173                f(
174                    widget.unsafe_cast_ref(),
175                    &action_name,
176                    Option::<Variant>::from_glib_borrow(parameter)
177                        .as_ref()
178                        .as_ref(),
179                )
180            }
181            let widget_class = self as *mut _ as *mut ffi::PanelWidgetClass;
182            let callback = activate_trampoline::<F, Self>;
183            ffi::panel_widget_class_install_action(
184                widget_class,
185                action_name.to_glib_none().0,
186                parameter_type.to_glib_none().0,
187                Some(callback),
188            );
189        }
190    }
191    #[doc(alias = "panel_widget_class_install_property_action")]
192    fn install_property_action(&mut self, action_name: &str, property_name: &str) {
193        unsafe {
194            let widget_class = self as *mut _ as *mut ffi::PanelWidgetClass;
195            ffi::panel_widget_class_install_property_action(
196                widget_class,
197                action_name.to_glib_none().0,
198                property_name.to_glib_none().0,
199            );
200        }
201    }
202}
203
204unsafe impl<T: ClassStruct> PanelWidgetClassSubclassExt for T where T::Type: PanelWidgetImpl {}