libpanel/subclass/
workbench.rs

1use crate::{prelude::*, Workbench};
2use glib::thread_guard::ThreadGuard;
3use glib::translate::*;
4use glib::GString;
5use glib::Variant;
6use gtk::subclass::prelude::*;
7use std::collections::HashMap;
8use std::{future::Future, pin::Pin};
9
10#[derive(Debug, Default)]
11struct Internal {
12    actions: HashMap<String, glib::ffi::gpointer>,
13}
14unsafe impl Sync for Internal {}
15unsafe impl Send for Internal {}
16
17pub trait WorkbenchImpl: WindowGroupImpl {
18    fn activate(&self) {
19        WorkbenchImplExt::parent_activate(self)
20    }
21    fn unload_future(&self) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>> {
22        self.parent_unload_future()
23    }
24}
25
26pub trait WorkbenchImplExt: ObjectSubclass {
27    fn parent_activate(&self);
28    fn parent_unload_future(
29        &self,
30    ) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>>;
31}
32
33impl<T: WorkbenchImpl> WorkbenchImplExt for T {
34    fn parent_activate(&self) {
35        unsafe {
36            let data = T::type_data();
37            let parent_class = data.as_ref().parent_class() as *mut ffi::PanelWorkbenchClass;
38            if let Some(f) = (*parent_class).activate {
39                f(self.obj().unsafe_cast_ref::<Workbench>().to_glib_none().0);
40            }
41        }
42    }
43    fn parent_unload_future(
44        &self,
45    ) -> Pin<Box<dyn Future<Output = Result<(), glib::Error>> + 'static>> {
46        unsafe {
47            let type_data = T::type_data();
48            let parent_class = type_data.as_ref().parent_class() as *mut ffi::PanelWorkbenchClass;
49            let unload_async = (*parent_class)
50                .unload_async
51                .expect("No parent class implementation for \"unload_async\"");
52
53            unsafe extern "C" fn parent_unload_async_callback<T>(
54                source_object: *mut glib::gobject_ffi::GObject,
55                res: *mut gio::ffi::GAsyncResult,
56                user_data: glib::ffi::gpointer,
57            ) where
58                T: WorkbenchImpl,
59            {
60                let type_data = T::type_data();
61                let parent_class =
62                    type_data.as_ref().parent_class() as *mut ffi::PanelWorkbenchClass;
63                let unload_finish = (*parent_class)
64                    .unload_finish
65                    .expect("No parent class implementation for \"unload_finish\"");
66
67                let ret: Box<ThreadGuard<gio::GioFutureResult<Result<(), glib::Error>>>> =
68                    Box::from_raw(user_data as *mut _);
69                let ret = ret.into_inner();
70
71                let mut error = std::ptr::null_mut();
72                unload_finish(source_object as *mut _, res, &mut error);
73                let result = if error.is_null() {
74                    Ok(())
75                } else {
76                    Err(from_glib_full(error))
77                };
78
79                ret.resolve(result);
80            }
81
82            Box::pin(gio::GioFuture::new(
83                &*self.obj(),
84                move |obj, cancellable, res| {
85                    let user_data = Box::new(ThreadGuard::new(res));
86                    unload_async(
87                        obj.unsafe_cast_ref::<Workbench>().to_glib_none().0,
88                        cancellable.to_glib_none().0,
89                        Some(parent_unload_async_callback::<T>),
90                        Box::into_raw(user_data) as *mut _,
91                    );
92                },
93            ))
94        }
95    }
96}
97
98unsafe impl<T: WorkbenchImpl> IsSubclassable<T> for Workbench {
99    fn class_init(class: &mut glib::Class<Self>) {
100        Self::parent_class_init::<T>(class);
101        unsafe {
102            let mut data = T::type_data();
103            let data = data.as_mut();
104            // Used to store actions for `install_action` and `rust_builder_scope`
105            data.set_class_data(<T as ObjectSubclassType>::type_(), Internal::default());
106        }
107        let klass = class.as_mut();
108        klass.activate = Some(panel_workbench_activate::<T>);
109        klass.unload_async = Some(panel_workbench_unload_async::<T>);
110        klass.unload_finish = Some(panel_workbench_unload_finish);
111    }
112}
113
114unsafe extern "C" fn panel_workbench_activate<T: WorkbenchImpl>(ptr: *mut ffi::PanelWorkbench) {
115    let instance = &*(ptr as *mut T::Instance);
116    let imp = instance.imp();
117
118    WorkbenchImpl::activate(imp);
119}
120
121unsafe extern "C" fn panel_workbench_unload_async<T: WorkbenchImpl>(
122    workbench: *mut ffi::PanelWorkbench,
123    cancellable: *mut gio::ffi::GCancellable,
124    callback: gio::ffi::GAsyncReadyCallback,
125    user_data: glib::ffi::gpointer,
126) {
127    let instance = &*(workbench as *mut T::Instance);
128    let imp = instance.imp();
129    let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable);
130    let workbench: Option<Workbench> = callback.map(|_| from_glib_none(workbench));
131
132    let fut = imp.unload_future();
133    glib::MainContext::default().spawn_local(async move {
134        let res = fut.await;
135        if let Some(callback) = callback {
136            let t = gio::LocalTask::new(
137                Some(workbench.unwrap_unchecked().upcast_ref::<glib::Object>()),
138                cancellable.as_ref(),
139                move |task: gio::LocalTask<bool>, source_object: Option<&glib::Object>| {
140                    let result: *mut gio::ffi::GAsyncResult =
141                        task.upcast_ref::<gio::AsyncResult>().to_glib_none().0;
142                    let source_object: *mut glib::gobject_ffi::GObject =
143                        source_object.to_glib_none().0;
144                    callback(source_object, result, user_data)
145                },
146            );
147            t.return_result(res.map(|_| true));
148        }
149    });
150}
151
152unsafe extern "C" fn panel_workbench_unload_finish(
153    workbench: *mut ffi::PanelWorkbench,
154    res: *mut gio::ffi::GAsyncResult,
155    error: *mut *mut glib::ffi::GError,
156) -> glib::ffi::gboolean {
157    let workbench = from_glib_borrow::<_, Workbench>(workbench);
158    let res: gio::AsyncResult = from_glib_none(res);
159    let t = res.downcast::<gio::LocalTask<bool>>().unwrap();
160    assert!(gio::LocalTask::<bool>::is_valid(
161        &t,
162        Some(workbench.as_ref())
163    ));
164    let ret = t.propagate();
165    match ret {
166        Ok(v) => {
167            assert!(v);
168            true.into_glib()
169        }
170        Err(e) => {
171            if !error.is_null() {
172                *error = e.into_glib_ptr();
173            }
174            false.into_glib()
175        }
176    }
177}
178
179pub unsafe trait WorkbenchClassSubclassExt: ClassStruct {
180    fn install_action_async<Fut, F>(
181        &mut self,
182        action_name: &str,
183        parameter_type: Option<&str>,
184        activate: F,
185    ) where
186        F: Fn(
187                <<Self as ClassStruct>::Type as ObjectSubclass>::Type,
188                String,
189                Option<Variant>,
190            ) -> Fut
191            + 'static
192            + Clone,
193        Fut: Future<Output = ()>,
194    {
195        self.install_action(
196            action_name,
197            parameter_type,
198            move |this, action_name, parameter_type| {
199                let ctx = glib::MainContext::default();
200                let action_name = action_name.to_owned();
201                let parameter_type = parameter_type.map(ToOwned::to_owned);
202                ctx.spawn_local(glib::clone!(
203                    #[strong]
204                    this,
205                    #[strong]
206                    action_name,
207                    #[strong]
208                    parameter_type,
209                    #[strong]
210                    activate,
211                    async move {
212                        activate(this, action_name, parameter_type).await;
213                    }
214                ));
215            },
216        );
217    }
218
219    #[doc(alias = "panel_workbench_class_install_action")]
220    fn install_action<F>(&mut self, action_name: &str, parameter_type: Option<&str>, activate: F)
221    where
222        F: Fn(&<<Self as ClassStruct>::Type as ObjectSubclass>::Type, &str, Option<&Variant>)
223            + 'static,
224    {
225        unsafe {
226            // We store the activate callbacks in a HashMap<action_name, activate>
227            // so that we can retrieve f later on the activate_trampoline call
228            let mut data = <Self::Type as ObjectSubclassType>::type_data();
229            let data = data.as_mut();
230
231            let f: Box<F> = Box::new(activate);
232
233            let internal = data
234                .class_data_mut::<Internal>(<Self::Type as ObjectSubclassType>::type_())
235                .expect("Something bad happened at class_init, the internal class_data is missing");
236            let callback_ptr = Box::into_raw(f) as glib::ffi::gpointer;
237            internal
238                .actions
239                .insert(action_name.to_string(), callback_ptr);
240
241            unsafe extern "C" fn activate_trampoline<F, S>(
242                this: *mut libc::c_void,
243                action_name: *const libc::c_char,
244                parameter: *mut glib::ffi::GVariant,
245            ) where
246                S: ClassStruct,
247                <S as ClassStruct>::Type: ObjectSubclass,
248                F: Fn(&<<S as ClassStruct>::Type as ObjectSubclass>::Type, &str, Option<&Variant>)
249                    + 'static,
250            {
251                let action_name = GString::from_glib_borrow(action_name);
252
253                let data = <S::Type as ObjectSubclassType>::type_data();
254                let internal = data
255                    .as_ref()
256                    .class_data::<Internal>(<S::Type as ObjectSubclassType>::type_())
257                    .unwrap();
258                let activate_callback =
259                    *internal
260                        .actions
261                        .get(action_name.as_str())
262                        .unwrap_or_else(|| {
263                            panic!("Action name '{}' was not found", action_name.as_str());
264                        });
265
266                let workbench = Workbench::from_glib_borrow(this as *mut ffi::PanelWorkbench);
267
268                let f: &F = &*(activate_callback as *const F);
269                f(
270                    workbench.unsafe_cast_ref(),
271                    &action_name,
272                    Option::<Variant>::from_glib_borrow(parameter)
273                        .as_ref()
274                        .as_ref(),
275                )
276            }
277            let workbench_class = self as *mut _ as *mut ffi::PanelWorkbenchClass;
278            let callback = activate_trampoline::<F, Self>;
279            ffi::panel_workbench_class_install_action(
280                workbench_class,
281                action_name.to_glib_none().0,
282                parameter_type.to_glib_none().0,
283                Some(callback),
284            );
285        }
286    }
287
288    #[cfg(any(feature = "v1_4", docsrs))]
289    #[cfg_attr(docsrs, doc(cfg(feature = "v1_4")))]
290    #[doc(alias = "panel_workbench_class_install_property_action")]
291    fn install_property_action(&mut self, action_name: &str, property_name: &str) {
292        unsafe {
293            let workbench_class = self as *mut _ as *mut ffi::PanelWorkbenchClass;
294            ffi::panel_workbench_class_install_property_action(
295                workbench_class,
296                action_name.to_glib_none().0,
297                property_name.to_glib_none().0,
298            );
299        }
300    }
301}
302
303unsafe impl<T: ClassStruct> WorkbenchClassSubclassExt for T where T::Type: WorkbenchImpl {}