libpanel/subclass/
widget.rs1use 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 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 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 {}