libpanel/subclass/
workspace.rs

1use crate::{prelude::*, Workspace};
2use adw::subclass::prelude::*;
3use glib::translate::*;
4use glib::GString;
5use glib::Variant;
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 WorkspaceImpl: AdwApplicationWindowImpl {}
17
18unsafe impl<T: WorkspaceImpl> IsSubclassable<T> for Workspace {
19    fn class_init(class: &mut glib::Class<Self>) {
20        Self::parent_class_init::<T>(class);
21        unsafe {
22            let mut data = T::type_data();
23            let data = data.as_mut();
24            // Used to store actions for `install_action` and `rust_builder_scope`
25            data.set_class_data(<T as ObjectSubclassType>::type_(), Internal::default());
26        }
27    }
28}
29
30pub unsafe trait WorkspaceClassSubclassExt: ClassStruct {
31    fn install_action_async<Fut, F>(
32        &mut self,
33        action_name: &str,
34        parameter_type: Option<&str>,
35        activate: F,
36    ) where
37        F: Fn(
38                <<Self as ClassStruct>::Type as ObjectSubclass>::Type,
39                String,
40                Option<Variant>,
41            ) -> Fut
42            + 'static
43            + Clone,
44        Fut: Future<Output = ()>,
45    {
46        self.install_action(
47            action_name,
48            parameter_type,
49            move |this, action_name, parameter_type| {
50                let ctx = glib::MainContext::default();
51                let action_name = action_name.to_owned();
52                let parameter_type = parameter_type.map(ToOwned::to_owned);
53                ctx.spawn_local(glib::clone!(
54                    #[strong]
55                    this,
56                    #[strong]
57                    action_name,
58                    #[strong]
59                    parameter_type,
60                    #[strong]
61                    activate,
62                    async move {
63                        activate(this, action_name, parameter_type).await;
64                    }
65                ));
66            },
67        );
68    }
69
70    #[doc(alias = "panel_workspace_class_install_action")]
71    fn install_action<F>(&mut self, action_name: &str, parameter_type: Option<&str>, activate: F)
72    where
73        F: Fn(&<<Self as ClassStruct>::Type as ObjectSubclass>::Type, &str, Option<&Variant>)
74            + 'static,
75    {
76        unsafe {
77            // We store the activate callbacks in a HashMap<action_name, activate>
78            // so that we can retrieve f later on the activate_trampoline call
79            let mut data = <Self::Type as ObjectSubclassType>::type_data();
80            let data = data.as_mut();
81
82            let f: Box<F> = Box::new(activate);
83
84            let internal = data
85                .class_data_mut::<Internal>(<Self::Type as ObjectSubclassType>::type_())
86                .expect("Something bad happened at class_init, the internal class_data is missing");
87            let callback_ptr = Box::into_raw(f) as glib::ffi::gpointer;
88            internal
89                .actions
90                .insert(action_name.to_string(), callback_ptr);
91
92            unsafe extern "C" fn activate_trampoline<F, S>(
93                this: *mut libc::c_void,
94                action_name: *const libc::c_char,
95                parameter: *mut glib::ffi::GVariant,
96            ) where
97                S: ClassStruct,
98                <S as ClassStruct>::Type: ObjectSubclass,
99                F: Fn(&<<S as ClassStruct>::Type as ObjectSubclass>::Type, &str, Option<&Variant>)
100                    + 'static,
101            {
102                let action_name = GString::from_glib_borrow(action_name);
103
104                let data = <S::Type as ObjectSubclassType>::type_data();
105                let internal = data
106                    .as_ref()
107                    .class_data::<Internal>(<S::Type as ObjectSubclassType>::type_())
108                    .unwrap();
109                let activate_callback =
110                    *internal
111                        .actions
112                        .get(action_name.as_str())
113                        .unwrap_or_else(|| {
114                            panic!("Action name '{}' was not found", action_name.as_str());
115                        });
116
117                let workspace = Workspace::from_glib_borrow(this as *mut ffi::PanelWorkspace);
118
119                let f: &F = &*(activate_callback as *const F);
120                f(
121                    workspace.unsafe_cast_ref(),
122                    &action_name,
123                    Option::<Variant>::from_glib_borrow(parameter)
124                        .as_ref()
125                        .as_ref(),
126                )
127            }
128            let workspace_class = self as *mut _ as *mut ffi::PanelWorkspaceClass;
129            let callback = activate_trampoline::<F, Self>;
130            ffi::panel_workspace_class_install_action(
131                workspace_class,
132                action_name.to_glib_none().0,
133                parameter_type.to_glib_none().0,
134                Some(callback),
135            );
136        }
137    }
138
139    #[cfg(any(feature = "v1_4", docsrs))]
140    #[cfg_attr(docsrs, doc(cfg(feature = "v1_4")))]
141    #[doc(alias = "panel_workspace_class_install_property_action")]
142    fn install_property_action(&mut self, action_name: &str, property_name: &str) {
143        unsafe {
144            let workspace_class = self as *mut _ as *mut ffi::PanelWorkspaceClass;
145            ffi::panel_workspace_class_install_property_action(
146                workspace_class,
147                action_name.to_glib_none().0,
148                property_name.to_glib_none().0,
149            );
150        }
151    }
152}
153
154unsafe impl<T: ClassStruct> WorkspaceClassSubclassExt for T where T::Type: WorkspaceImpl {}