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 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 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 {}