Skip to main content

gtk4/
functions.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{boxed::Box as Box_, pin::Pin, sync::OnceLock};
4
5use glib::{Quark, Slice, translate::*};
6
7pub use crate::auto::functions::*;
8use crate::{AboutDialog, StyleProvider, Window, ffi, prelude::*};
9
10/// Determines whether a given keyval and modifier mask constitute
11/// a valid keyboard accelerator.
12///
13/// For example, the `GDK_KEY_a` keyval plus `GDK_CONTROL_MASK` mask is valid,
14/// and matches the “Ctrl+a” accelerator. But, you can't, for instance, use
15/// the `GDK_KEY_Control_L` keyval as an accelerator.
16/// ## `keyval`
17/// a GDK keyval
18/// ## `modifiers`
19/// modifier mask
20///
21/// # Returns
22///
23/// true if the accelerator is valid
24#[doc(alias = "gtk_accelerator_valid")]
25pub fn accelerator_valid(keyval: gdk::Key, modifiers: gdk::ModifierType) -> bool {
26    assert_initialized_main_thread!();
27    unsafe {
28        from_glib(ffi::gtk_accelerator_valid(
29            keyval.into_glib(),
30            modifiers.into_glib(),
31        ))
32    }
33}
34
35/// Converts an accelerator keyval and modifier mask into a string
36/// which can be used to represent the accelerator to the user.
37/// ## `accelerator_key`
38/// accelerator keyval
39/// ## `accelerator_mods`
40/// accelerator modifier mask
41///
42/// # Returns
43///
44/// a newly-allocated string representing the accelerator
45#[doc(alias = "gtk_accelerator_get_label")]
46pub fn accelerator_get_label(
47    accelerator_key: gdk::Key,
48    accelerator_mods: gdk::ModifierType,
49) -> glib::GString {
50    assert_initialized_main_thread!();
51    unsafe {
52        from_glib_full(ffi::gtk_accelerator_get_label(
53            accelerator_key.into_glib(),
54            accelerator_mods.into_glib(),
55        ))
56    }
57}
58
59/// Converts an accelerator keyval and modifier mask
60/// into a string that can be displayed to the user.
61///
62/// The string may be translated.
63///
64/// This function is similar to [`accelerator_get_label()`][crate::accelerator_get_label()],
65/// but handling keycodes. This is only useful for system-level
66/// components, applications should use [`accelerator_get_label()`][crate::accelerator_get_label()]
67/// instead.
68/// ## `display`
69/// a [`gdk::Display`][crate::gdk::Display]
70/// ## `accelerator_key`
71/// accelerator keyval
72/// ## `keycode`
73/// accelerator keycode
74/// ## `accelerator_mods`
75/// accelerator modifier mask
76///
77/// # Returns
78///
79/// a newly-allocated string representing the accelerator
80#[doc(alias = "gtk_accelerator_get_label_with_keycode")]
81pub fn accelerator_get_label_with_keycode(
82    display: Option<&impl IsA<gdk::Display>>,
83    accelerator_key: gdk::Key,
84    keycode: u32,
85    accelerator_mods: gdk::ModifierType,
86) -> glib::GString {
87    assert_initialized_main_thread!();
88    unsafe {
89        from_glib_full(ffi::gtk_accelerator_get_label_with_keycode(
90            display.map(|p| p.as_ref()).to_glib_none().0,
91            accelerator_key.into_glib(),
92            keycode,
93            accelerator_mods.into_glib(),
94        ))
95    }
96}
97
98/// q`.
99///
100/// If you need to display accelerators in the user interface,
101/// see [`accelerator_get_label()`][crate::accelerator_get_label()].
102/// ## `accelerator_key`
103/// accelerator keyval
104/// ## `accelerator_mods`
105/// accelerator modifier mask
106///
107/// # Returns
108///
109/// a newly-allocated accelerator name
110#[doc(alias = "gtk_accelerator_name")]
111pub fn accelerator_name(
112    accelerator_key: gdk::Key,
113    accelerator_mods: gdk::ModifierType,
114) -> glib::GString {
115    assert_initialized_main_thread!();
116    unsafe {
117        from_glib_full(ffi::gtk_accelerator_name(
118            accelerator_key.into_glib(),
119            accelerator_mods.into_glib(),
120        ))
121    }
122}
123
124/// Converts an accelerator keyval and modifier mask
125/// into a string that can be parsed by [`accelerator_parse_with_keycode()`][crate::accelerator_parse_with_keycode()].
126///
127/// This is similar to [`accelerator_name()`][crate::accelerator_name()] but handling keycodes.
128/// This is only useful for system-level components, applications
129/// should use [`accelerator_name()`][crate::accelerator_name()] instead.
130/// ## `display`
131/// a [`gdk::Display`][crate::gdk::Display]
132/// ## `accelerator_key`
133/// accelerator keyval
134/// ## `keycode`
135/// accelerator keycode
136/// ## `accelerator_mods`
137/// accelerator modifier mask
138///
139/// # Returns
140///
141/// a newly allocated accelerator name.
142#[doc(alias = "gtk_accelerator_name_with_keycode")]
143pub fn accelerator_name_with_keycode(
144    display: Option<&impl IsA<gdk::Display>>,
145    accelerator_key: gdk::Key,
146    keycode: u32,
147    accelerator_mods: gdk::ModifierType,
148) -> glib::GString {
149    assert_initialized_main_thread!();
150    unsafe {
151        from_glib_full(ffi::gtk_accelerator_name_with_keycode(
152            display.map(|p| p.as_ref()).to_glib_none().0,
153            accelerator_key.into_glib(),
154            keycode,
155            accelerator_mods.into_glib(),
156        ))
157    }
158}
159
160/// ` for `GDK_HYPER_MASK`
161///
162/// If the parse operation fails, @accelerator_key and @accelerator_mods will
163/// be set to 0 (zero).
164/// ## `accelerator`
165/// string representing an accelerator
166///
167/// # Returns
168///
169/// whether parsing succeeded
170///
171/// ## `accelerator_key`
172/// return location for accelerator keyval
173///
174/// ## `accelerator_mods`
175/// return location for accelerator
176///   modifier mask
177#[doc(alias = "gtk_accelerator_parse")]
178pub fn accelerator_parse(accelerator: impl IntoGStr) -> Option<(gdk::Key, gdk::ModifierType)> {
179    assert_initialized_main_thread!();
180    unsafe {
181        accelerator.run_with_gstr(|accelerator| {
182            let mut accelerator_key = std::mem::MaybeUninit::uninit();
183            let mut accelerator_mods = std::mem::MaybeUninit::uninit();
184            let ret = from_glib(ffi::gtk_accelerator_parse(
185                accelerator.as_ptr(),
186                accelerator_key.as_mut_ptr(),
187                accelerator_mods.as_mut_ptr(),
188            ));
189            if ret {
190                Some((
191                    gdk::Key::from_glib(accelerator_key.assume_init()),
192                    from_glib(accelerator_mods.assume_init()),
193                ))
194            } else {
195                None
196            }
197        })
198    }
199}
200
201/// Parses a string representing an accelerator.
202///
203/// This is similar to [`accelerator_parse()`][crate::accelerator_parse()] but handles keycodes as
204/// well. This is only useful for system-level components, applications should
205/// use [`accelerator_parse()`][crate::accelerator_parse()] instead.
206///
207/// If @accelerator_codes is given and the result stored in it is non-[`None`],
208/// the result must be freed with g_free().
209///
210/// If a keycode is present in the accelerator and no @accelerator_codes
211/// is given, the parse will fail.
212///
213/// If the parse fails, @accelerator_key, @accelerator_mods and
214/// @accelerator_codes will be set to 0 (zero).
215/// ## `accelerator`
216/// string representing an accelerator
217/// ## `display`
218/// the [`gdk::Display`][crate::gdk::Display] to look up @accelerator_codes in
219///
220/// # Returns
221///
222/// true if parsing succeeded
223///
224/// ## `accelerator_key`
225/// return location for accelerator keyval
226///
227/// ## `accelerator_codes`
228///
229///   return location for accelerator keycodes
230///
231/// ## `accelerator_mods`
232/// return location for accelerator
233///   modifier mask
234#[doc(alias = "gtk_accelerator_parse_with_keycode")]
235pub fn accelerator_parse_with_keycode(
236    accelerator: impl IntoGStr,
237    display: Option<&impl IsA<gdk::Display>>,
238) -> Option<(gdk::Key, Slice<u32>, gdk::ModifierType)> {
239    assert_initialized_main_thread!();
240    unsafe {
241        accelerator.run_with_gstr(|accelerator| {
242            let mut accelerator_key = std::mem::MaybeUninit::uninit();
243            let mut accelerator_codes_ptr = std::ptr::null_mut();
244            let mut accelerator_mods = std::mem::MaybeUninit::uninit();
245            let success = from_glib(ffi::gtk_accelerator_parse_with_keycode(
246                accelerator.as_ptr(),
247                display.map(|p| p.as_ref()).to_glib_none().0,
248                accelerator_key.as_mut_ptr(),
249                &mut accelerator_codes_ptr,
250                accelerator_mods.as_mut_ptr(),
251            ));
252            if success {
253                let mut len = 0;
254                if !accelerator_codes_ptr.is_null() {
255                    while std::ptr::read(accelerator_codes_ptr.add(len)) != 0 {
256                        len += 1;
257                    }
258                }
259                let accelerator_codes = Slice::from_glib_full_num(accelerator_codes_ptr, len);
260                Some((
261                    gdk::Key::from_glib(accelerator_key.assume_init()),
262                    accelerator_codes,
263                    from_glib(accelerator_mods.assume_init()),
264                ))
265            } else {
266                None
267            }
268        })
269    }
270}
271
272/// This function launches the default application for showing
273/// a given uri.
274///
275/// The @callback will be called when the launch is completed.
276///
277/// This is the recommended call to be used as it passes information
278/// necessary for sandbox helpers to parent their dialogs properly.
279///
280/// # Deprecated since 4.10
281///
282/// Use [`FileLauncher::launch()`][crate::FileLauncher::launch()] or
283///   [`UriLauncher::launch()`][crate::UriLauncher::launch()] instead
284/// ## `parent`
285/// parent window
286/// ## `uri`
287/// the uri to show
288/// ## `timestamp`
289/// timestamp from the event that triggered this call, or `GDK_CURRENT_TIME`
290/// ## `cancellable`
291/// a `GCancellable` to cancel the launch
292/// ## `callback`
293/// a callback to call when the action is complete
294#[doc(alias = "gtk_show_uri_full")]
295#[doc(alias = "gtk_show_uri_full_finish")]
296#[cfg_attr(feature = "v4_10", deprecated = "Since 4.10")]
297#[allow(deprecated)]
298pub fn show_uri_full<P: FnOnce(Result<(), glib::Error>) + 'static>(
299    parent: Option<&impl IsA<Window>>,
300    uri: &str,
301    timestamp: u32,
302    cancellable: Option<&impl IsA<gio::Cancellable>>,
303    callback: P,
304) {
305    assert_initialized_main_thread!();
306    let main_context = glib::MainContext::ref_thread_default();
307    let is_main_context_owner = main_context.is_owner();
308    let has_acquired_main_context = (!is_main_context_owner)
309        .then(|| main_context.acquire().ok())
310        .flatten();
311    assert!(
312        is_main_context_owner || has_acquired_main_context.is_some(),
313        "Async operations only allowed if the thread is owning the MainContext"
314    );
315
316    let user_data: Box_<glib::thread_guard::ThreadGuard<P>> =
317        Box_::new(glib::thread_guard::ThreadGuard::new(callback));
318    unsafe extern "C" fn show_uri_full_trampoline<P: FnOnce(Result<(), glib::Error>) + 'static>(
319        parent_ptr: *mut glib::gobject_ffi::GObject,
320        res: *mut gio::ffi::GAsyncResult,
321        user_data: glib::ffi::gpointer,
322    ) {
323        unsafe {
324            let mut error = std::ptr::null_mut();
325            let _ =
326                ffi::gtk_show_uri_full_finish(parent_ptr as *mut ffi::GtkWindow, res, &mut error);
327            let result = if error.is_null() {
328                Ok(())
329            } else {
330                Err(from_glib_full(error))
331            };
332            let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
333                Box_::from_raw(user_data as *mut _);
334            let callback = callback.into_inner();
335            callback(result);
336        }
337    }
338    let callback = show_uri_full_trampoline::<P>;
339    unsafe {
340        ffi::gtk_show_uri_full(
341            parent.map(|p| p.as_ref()).to_glib_none().0,
342            uri.to_glib_none().0,
343            timestamp,
344            cancellable.map(|p| p.as_ref()).to_glib_none().0,
345            Some(callback),
346            Box_::into_raw(user_data) as *mut _,
347        );
348    }
349}
350
351#[cfg_attr(feature = "v4_10", deprecated = "Since 4.10")]
352#[allow(deprecated)]
353pub fn show_uri_full_future(
354    parent: Option<&(impl IsA<Window> + Clone + 'static)>,
355    uri: &str,
356    timestamp: u32,
357) -> Pin<Box_<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
358    skip_assert_initialized!();
359    let parent = parent.map(ToOwned::to_owned);
360    let uri = String::from(uri);
361    Box_::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
362        show_uri_full(
363            parent.as_ref().map(::std::borrow::Borrow::borrow),
364            &uri,
365            timestamp,
366            Some(cancellable),
367            move |res| {
368                send.resolve(res);
369            },
370        );
371    }))
372}
373
374/// A convenience function for showing an application’s about dialog.
375///
376/// The constructed dialog is associated with the parent window and
377/// reused for future invocations of this function.
378/// ## `parent`
379/// the parent top-level window
380/// ## `first_property_name`
381/// the name of the first property
382#[doc(alias = "gtk_show_about_dialog")]
383pub fn show_about_dialog<P: IsA<Window>>(parent: Option<&P>, properties: &[(&str, &dyn ToValue)]) {
384    assert_initialized_main_thread!();
385    static QUARK: OnceLock<Quark> = OnceLock::new();
386    let quark = *QUARK.get_or_init(|| Quark::from_str("gtk-rs-about-dialog"));
387
388    unsafe {
389        if let Some(d) = parent.and_then(|p| p.qdata::<AboutDialog>(quark)) {
390            d.as_ref().show();
391        } else {
392            let mut builder = glib::Object::builder::<AboutDialog>();
393            for (key, value) in properties {
394                builder = builder.property(key, *value);
395            }
396            let about_dialog = builder.build();
397            about_dialog.set_hide_on_close(true);
398
399            // cache the dialog if a parent is set
400            if let Some(dialog_parent) = parent {
401                about_dialog.set_modal(true);
402                about_dialog.set_transient_for(parent);
403                about_dialog.set_destroy_with_parent(true);
404                dialog_parent.set_qdata(quark, about_dialog.clone());
405            }
406
407            about_dialog.show();
408        };
409    }
410}
411
412/// Return the type ids that have been registered after
413/// calling gtk_test_register_all_types().
414///
415/// # Returns
416///
417///
418///    0-terminated array of type ids
419#[doc(alias = "gtk_test_list_all_types")]
420pub fn test_list_all_types() -> Slice<glib::Type> {
421    unsafe {
422        let mut n_types = std::mem::MaybeUninit::uninit();
423        let types = ffi::gtk_test_list_all_types(n_types.as_mut_ptr());
424        Slice::from_glib_container_num(types as *mut _, n_types.assume_init() as usize)
425    }
426}
427
428#[doc(alias = "gtk_style_context_add_provider_for_display")]
429#[doc(alias = "add_provider_for_display")]
430pub fn style_context_add_provider_for_display(
431    display: &impl IsA<gdk::Display>,
432    provider: &impl IsA<StyleProvider>,
433    priority: u32,
434) {
435    skip_assert_initialized!();
436    unsafe {
437        ffi::gtk_style_context_add_provider_for_display(
438            display.as_ref().to_glib_none().0,
439            provider.as_ref().to_glib_none().0,
440            priority,
441        );
442    }
443}
444
445#[doc(alias = "gtk_style_context_remove_provider_for_display")]
446#[doc(alias = "remove_provider_for_display")]
447pub fn style_context_remove_provider_for_display(
448    display: &impl IsA<gdk::Display>,
449    provider: &impl IsA<StyleProvider>,
450) {
451    skip_assert_initialized!();
452    unsafe {
453        ffi::gtk_style_context_remove_provider_for_display(
454            display.as_ref().to_glib_none().0,
455            provider.as_ref().to_glib_none().0,
456        );
457    }
458}