gtk4/subclass/
application.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for subclassing [`Application`].
5
6use glib::translate::*;
7
8use crate::{ffi, prelude::*, subclass::prelude::*, Application, Window};
9
10pub trait GtkApplicationImpl: ApplicationImpl + ObjectSubclass<Type: IsA<Application>> {
11    fn window_added(&self, window: &Window) {
12        self.parent_window_added(window)
13    }
14
15    fn window_removed(&self, window: &Window) {
16        self.parent_window_removed(window)
17    }
18
19    #[cfg(feature = "v4_22")]
20    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
21    fn save_state(&self, state: &glib::VariantDict) -> bool {
22        self.parent_save_state(state)
23    }
24
25    #[cfg(feature = "v4_22")]
26    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
27    fn restore_state(&self, reason: crate::RestoreReason, state: &glib::Variant) -> bool {
28        self.parent_restore_state(reason, state)
29    }
30
31    #[cfg(feature = "v4_22")]
32    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
33    fn restore_window(&self, reason: crate::RestoreReason, state: &glib::Variant) {
34        self.parent_restore_window(reason, state)
35    }
36}
37
38pub trait GtkApplicationImplExt: GtkApplicationImpl {
39    fn parent_window_added(&self, window: &Window) {
40        unsafe {
41            let data = Self::type_data();
42            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkApplicationClass;
43            if let Some(f) = (*parent_class).window_added {
44                f(
45                    self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
46                    window.to_glib_none().0,
47                )
48            }
49        }
50    }
51
52    fn parent_window_removed(&self, window: &Window) {
53        unsafe {
54            let data = Self::type_data();
55            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkApplicationClass;
56            if let Some(f) = (*parent_class).window_removed {
57                f(
58                    self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
59                    window.to_glib_none().0,
60                )
61            }
62        }
63    }
64
65    #[cfg(feature = "v4_22")]
66    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
67    fn parent_save_state(&self, state: &glib::VariantDict) -> bool {
68        unsafe {
69            let data = Self::type_data();
70            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkApplicationClass;
71            if let Some(f) = (*parent_class).save_state {
72                from_glib(f(
73                    self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
74                    state.to_glib_none().0,
75                ))
76            } else {
77                false
78            }
79        }
80    }
81
82    #[cfg(feature = "v4_22")]
83    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
84    fn parent_restore_state(&self, reason: crate::RestoreReason, state: &glib::Variant) -> bool {
85        unsafe {
86            let data = Self::type_data();
87            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkApplicationClass;
88            if let Some(f) = (*parent_class).restore_state {
89                from_glib(f(
90                    self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
91                    reason.into_glib(),
92                    state.to_glib_none().0,
93                ))
94            } else {
95                false
96            }
97        }
98    }
99
100    #[cfg(feature = "v4_22")]
101    #[cfg_attr(docsrs, doc(cfg(feature = "v4_22")))]
102    fn parent_restore_window(&self, reason: crate::RestoreReason, state: &glib::Variant) {
103        unsafe {
104            let data = Self::type_data();
105            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkApplicationClass;
106            if let Some(f) = (*parent_class).restore_window {
107                f(
108                    self.obj().unsafe_cast_ref::<Application>().to_glib_none().0,
109                    reason.into_glib(),
110                    state.to_glib_none().0,
111                )
112            }
113        }
114    }
115}
116
117impl<T: GtkApplicationImpl> GtkApplicationImplExt for T {}
118
119unsafe impl<T: GtkApplicationImpl> IsSubclassable<T> for Application {
120    fn class_init(class: &mut ::glib::Class<Self>) {
121        // Override the original `GtkApplication` startup implementation so that
122        // we can set GTK as initialized right afterwards.
123        {
124            use std::sync;
125
126            // Needed because the function pointer is not `Send+Sync` otherwise but has to
127            // be to be stored in a `static mut`.
128            struct WrapFn(unsafe extern "C" fn(*mut gio::ffi::GApplication));
129            unsafe impl Send for WrapFn {}
130            unsafe impl Sync for WrapFn {}
131
132            static ONCE: sync::Once = sync::Once::new();
133            static mut OLD_STARTUP: Option<WrapFn> = None;
134
135            // One the very first call replace the original `GtkApplication` startup with a
136            // function that first calls the original one and then marks gtk-rs as
137            // initialized.
138            ONCE.call_once(|| unsafe {
139                let base_klass =
140                    glib::gobject_ffi::g_type_class_ref(ffi::gtk_application_get_type());
141                debug_assert!(!base_klass.is_null());
142
143                let app_klass = &mut *(base_klass as *mut gio::ffi::GApplicationClass);
144                OLD_STARTUP = app_klass.startup.map(WrapFn);
145
146                unsafe extern "C" fn replace_startup(app: *mut gio::ffi::GApplication) {
147                    if let Some(WrapFn(old_startup)) = OLD_STARTUP {
148                        old_startup(app);
149                    }
150                    crate::rt::set_initialized();
151                }
152
153                app_klass.startup = Some(replace_startup);
154
155                glib::gobject_ffi::g_type_class_unref(base_klass);
156            });
157        }
158
159        Self::parent_class_init::<T>(class);
160
161        let klass = class.as_mut();
162        klass.window_added = Some(application_window_added::<T>);
163        klass.window_removed = Some(application_window_removed::<T>);
164
165        #[cfg(feature = "v4_22")]
166        {
167            klass.save_state = Some(application_save_state::<T>);
168            klass.restore_state = Some(application_restore_state::<T>);
169            klass.restore_window = Some(application_restore_window::<T>);
170        }
171    }
172}
173
174unsafe extern "C" fn application_window_added<T: GtkApplicationImpl>(
175    ptr: *mut ffi::GtkApplication,
176    wptr: *mut ffi::GtkWindow,
177) {
178    let instance = &*(ptr as *mut T::Instance);
179    let imp = instance.imp();
180
181    imp.window_added(&from_glib_borrow(wptr))
182}
183
184unsafe extern "C" fn application_window_removed<T: GtkApplicationImpl>(
185    ptr: *mut ffi::GtkApplication,
186    wptr: *mut ffi::GtkWindow,
187) {
188    let instance = &*(ptr as *mut T::Instance);
189    let imp = instance.imp();
190
191    imp.window_removed(&from_glib_borrow(wptr))
192}
193
194#[cfg(feature = "v4_22")]
195unsafe extern "C" fn application_save_state<T: GtkApplicationImpl>(
196    ptr: *mut ffi::GtkApplication,
197    state: *mut glib::ffi::GVariantDict,
198) -> glib::ffi::gboolean {
199    let instance = &*(ptr as *mut T::Instance);
200    let imp = instance.imp();
201
202    imp.save_state(&from_glib_borrow(state)).into_glib()
203}
204
205#[cfg(feature = "v4_22")]
206unsafe extern "C" fn application_restore_state<T: GtkApplicationImpl>(
207    ptr: *mut ffi::GtkApplication,
208    reason: ffi::GtkRestoreReason,
209    state: *mut glib::ffi::GVariant,
210) -> glib::ffi::gboolean {
211    let instance = &*(ptr as *mut T::Instance);
212    let imp = instance.imp();
213
214    imp.restore_state(from_glib(reason), &from_glib_borrow(state))
215        .into_glib()
216}
217
218#[cfg(feature = "v4_22")]
219unsafe extern "C" fn application_restore_window<T: GtkApplicationImpl>(
220    ptr: *mut ffi::GtkApplication,
221    reason: ffi::GtkRestoreReason,
222    state: *mut glib::ffi::GVariant,
223) {
224    let instance = &*(ptr as *mut T::Instance);
225    let imp = instance.imp();
226
227    imp.restore_window(from_glib(reason), &from_glib_borrow(state))
228}