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/// Converts an accelerator keyval and modifier mask into a string
99/// that can be parsed by [`accelerator_parse()`][crate::accelerator_parse()].
100///
101/// For example, if you pass in `GDK_KEY_q` and `GDK_CONTROL_MASK`,
102/// this function returns `<Control>q`.
103///
104/// If you need to display accelerators in the user interface,
105/// see [`accelerator_get_label()`][crate::accelerator_get_label()].
106/// ## `accelerator_key`
107/// accelerator keyval
108/// ## `accelerator_mods`
109/// accelerator modifier mask
110///
111/// # Returns
112///
113/// a newly-allocated accelerator name
114#[doc(alias = "gtk_accelerator_name")]
115pub fn accelerator_name(
116    accelerator_key: gdk::Key,
117    accelerator_mods: gdk::ModifierType,
118) -> glib::GString {
119    assert_initialized_main_thread!();
120    unsafe {
121        from_glib_full(ffi::gtk_accelerator_name(
122            accelerator_key.into_glib(),
123            accelerator_mods.into_glib(),
124        ))
125    }
126}
127
128/// Converts an accelerator keyval and modifier mask
129/// into a string that can be parsed by [`accelerator_parse_with_keycode()`][crate::accelerator_parse_with_keycode()].
130///
131/// This is similar to [`accelerator_name()`][crate::accelerator_name()] but handling keycodes.
132/// This is only useful for system-level components, applications
133/// should use [`accelerator_name()`][crate::accelerator_name()] instead.
134/// ## `display`
135/// a [`gdk::Display`][crate::gdk::Display]
136/// ## `accelerator_key`
137/// accelerator keyval
138/// ## `keycode`
139/// accelerator keycode
140/// ## `accelerator_mods`
141/// accelerator modifier mask
142///
143/// # Returns
144///
145/// a newly allocated accelerator name.
146#[doc(alias = "gtk_accelerator_name_with_keycode")]
147pub fn accelerator_name_with_keycode(
148    display: Option<&impl IsA<gdk::Display>>,
149    accelerator_key: gdk::Key,
150    keycode: u32,
151    accelerator_mods: gdk::ModifierType,
152) -> glib::GString {
153    assert_initialized_main_thread!();
154    unsafe {
155        from_glib_full(ffi::gtk_accelerator_name_with_keycode(
156            display.map(|p| p.as_ref()).to_glib_none().0,
157            accelerator_key.into_glib(),
158            keycode,
159            accelerator_mods.into_glib(),
160        ))
161    }
162}
163
164/// Parses a string representing an accelerator.
165///
166/// The format looks like “`<Control>a`” or “`<Shift><Alt>F1`”.
167///
168/// The parser is fairly liberal and allows lower or upper case, and also
169/// abbreviations such as “`<Ctl>`” and “`<Ctrl>`”.
170///
171/// Key names are parsed using `keyval_from_name()`. For character keys
172/// the name is not the symbol, but the lowercase name, e.g. one would use
173/// “`<Ctrl>minus`” instead of “`<Ctrl>-`”.
174///
175/// Modifiers are enclosed in angular brackets `<>`, and match the
176/// [`gdk::ModifierType`][crate::gdk::ModifierType] mask:
177///
178/// - `<Shift>` for `GDK_SHIFT_MASK`
179/// - `<Ctrl>` for `GDK_CONTROL_MASK`
180/// - `<Alt>` for `GDK_ALT_MASK`
181/// - `<Meta>` for `GDK_META_MASK`
182/// - `<Super>` for `GDK_SUPER_MASK`
183/// - `<Hyper>` for `GDK_HYPER_MASK`
184///
185/// If the parse operation fails, @accelerator_key and @accelerator_mods will
186/// be set to 0 (zero).
187/// ## `accelerator`
188/// string representing an accelerator
189///
190/// # Returns
191///
192/// whether parsing succeeded
193///
194/// ## `accelerator_key`
195/// return location for accelerator keyval
196///
197/// ## `accelerator_mods`
198/// return location for accelerator
199///   modifier mask
200#[doc(alias = "gtk_accelerator_parse")]
201pub fn accelerator_parse(accelerator: impl IntoGStr) -> Option<(gdk::Key, gdk::ModifierType)> {
202    assert_initialized_main_thread!();
203    unsafe {
204        accelerator.run_with_gstr(|accelerator| {
205            let mut accelerator_key = std::mem::MaybeUninit::uninit();
206            let mut accelerator_mods = std::mem::MaybeUninit::uninit();
207            let ret = from_glib(ffi::gtk_accelerator_parse(
208                accelerator.as_ptr(),
209                accelerator_key.as_mut_ptr(),
210                accelerator_mods.as_mut_ptr(),
211            ));
212            if ret {
213                Some((
214                    gdk::Key::from_glib(accelerator_key.assume_init()),
215                    from_glib(accelerator_mods.assume_init()),
216                ))
217            } else {
218                None
219            }
220        })
221    }
222}
223
224/// Parses a string representing an accelerator.
225///
226/// This is similar to [`accelerator_parse()`][crate::accelerator_parse()] but handles keycodes as
227/// well. This is only useful for system-level components, applications should
228/// use [`accelerator_parse()`][crate::accelerator_parse()] instead.
229///
230/// If @accelerator_codes is given and the result stored in it is non-[`None`],
231/// the result must be freed with g_free().
232///
233/// If a keycode is present in the accelerator and no @accelerator_codes
234/// is given, the parse will fail.
235///
236/// If the parse fails, @accelerator_key, @accelerator_mods and
237/// @accelerator_codes will be set to 0 (zero).
238/// ## `accelerator`
239/// string representing an accelerator
240/// ## `display`
241/// the [`gdk::Display`][crate::gdk::Display] to look up @accelerator_codes in
242///
243/// # Returns
244///
245/// true if parsing succeeded
246///
247/// ## `accelerator_key`
248/// return location for accelerator keyval
249///
250/// ## `accelerator_codes`
251///
252///   return location for accelerator keycodes
253///
254/// ## `accelerator_mods`
255/// return location for accelerator
256///   modifier mask
257#[doc(alias = "gtk_accelerator_parse_with_keycode")]
258pub fn accelerator_parse_with_keycode(
259    accelerator: impl IntoGStr,
260    display: Option<&impl IsA<gdk::Display>>,
261) -> Option<(gdk::Key, Slice<u32>, gdk::ModifierType)> {
262    assert_initialized_main_thread!();
263    unsafe {
264        accelerator.run_with_gstr(|accelerator| {
265            let mut accelerator_key = std::mem::MaybeUninit::uninit();
266            let mut accelerator_codes_ptr = std::ptr::null_mut();
267            let mut accelerator_mods = std::mem::MaybeUninit::uninit();
268            let success = from_glib(ffi::gtk_accelerator_parse_with_keycode(
269                accelerator.as_ptr(),
270                display.map(|p| p.as_ref()).to_glib_none().0,
271                accelerator_key.as_mut_ptr(),
272                &mut accelerator_codes_ptr,
273                accelerator_mods.as_mut_ptr(),
274            ));
275            if success {
276                let mut len = 0;
277                if !accelerator_codes_ptr.is_null() {
278                    while std::ptr::read(accelerator_codes_ptr.add(len)) != 0 {
279                        len += 1;
280                    }
281                }
282                let accelerator_codes = Slice::from_glib_full_num(accelerator_codes_ptr, len);
283                Some((
284                    gdk::Key::from_glib(accelerator_key.assume_init()),
285                    accelerator_codes,
286                    from_glib(accelerator_mods.assume_init()),
287                ))
288            } else {
289                None
290            }
291        })
292    }
293}
294
295/// This function launches the default application for showing
296/// a given uri.
297///
298/// The @callback will be called when the launch is completed.
299///
300/// This is the recommended call to be used as it passes information
301/// necessary for sandbox helpers to parent their dialogs properly.
302///
303/// # Deprecated since 4.10
304///
305/// Use [`FileLauncher::launch()`][crate::FileLauncher::launch()] or
306///   [`UriLauncher::launch()`][crate::UriLauncher::launch()] instead
307/// ## `parent`
308/// parent window
309/// ## `uri`
310/// the uri to show
311/// ## `timestamp`
312/// timestamp from the event that triggered this call, or `GDK_CURRENT_TIME`
313/// ## `cancellable`
314/// a `GCancellable` to cancel the launch
315/// ## `callback`
316/// a callback to call when the action is complete
317#[doc(alias = "gtk_show_uri_full")]
318#[doc(alias = "gtk_show_uri_full_finish")]
319#[cfg_attr(feature = "v4_10", deprecated = "Since 4.10")]
320#[allow(deprecated)]
321pub fn show_uri_full<P: FnOnce(Result<(), glib::Error>) + 'static>(
322    parent: Option<&impl IsA<Window>>,
323    uri: &str,
324    timestamp: u32,
325    cancellable: Option<&impl IsA<gio::Cancellable>>,
326    callback: P,
327) {
328    assert_initialized_main_thread!();
329    let main_context = glib::MainContext::ref_thread_default();
330    let is_main_context_owner = main_context.is_owner();
331    let has_acquired_main_context = (!is_main_context_owner)
332        .then(|| main_context.acquire().ok())
333        .flatten();
334    assert!(
335        is_main_context_owner || has_acquired_main_context.is_some(),
336        "Async operations only allowed if the thread is owning the MainContext"
337    );
338
339    let user_data: Box_<glib::thread_guard::ThreadGuard<P>> =
340        Box_::new(glib::thread_guard::ThreadGuard::new(callback));
341    unsafe extern "C" fn show_uri_full_trampoline<P: FnOnce(Result<(), glib::Error>) + 'static>(
342        parent_ptr: *mut glib::gobject_ffi::GObject,
343        res: *mut gio::ffi::GAsyncResult,
344        user_data: glib::ffi::gpointer,
345    ) {
346        unsafe {
347            let mut error = std::ptr::null_mut();
348            let _ =
349                ffi::gtk_show_uri_full_finish(parent_ptr as *mut ffi::GtkWindow, res, &mut error);
350            let result = if error.is_null() {
351                Ok(())
352            } else {
353                Err(from_glib_full(error))
354            };
355            let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
356                Box_::from_raw(user_data as *mut _);
357            let callback = callback.into_inner();
358            callback(result);
359        }
360    }
361    let callback = show_uri_full_trampoline::<P>;
362    unsafe {
363        ffi::gtk_show_uri_full(
364            parent.map(|p| p.as_ref()).to_glib_none().0,
365            uri.to_glib_none().0,
366            timestamp,
367            cancellable.map(|p| p.as_ref()).to_glib_none().0,
368            Some(callback),
369            Box_::into_raw(user_data) as *mut _,
370        );
371    }
372}
373
374#[cfg_attr(feature = "v4_10", deprecated = "Since 4.10")]
375#[allow(deprecated)]
376pub fn show_uri_full_future(
377    parent: Option<&(impl IsA<Window> + Clone + 'static)>,
378    uri: &str,
379    timestamp: u32,
380) -> Pin<Box_<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
381    skip_assert_initialized!();
382    let parent = parent.map(ToOwned::to_owned);
383    let uri = String::from(uri);
384    Box_::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
385        show_uri_full(
386            parent.as_ref().map(::std::borrow::Borrow::borrow),
387            &uri,
388            timestamp,
389            Some(cancellable),
390            move |res| {
391                send.resolve(res);
392            },
393        );
394    }))
395}
396
397/// A convenience function for showing an application’s about dialog.
398///
399/// The constructed dialog is associated with the parent window and
400/// reused for future invocations of this function.
401/// ## `parent`
402/// the parent top-level window
403/// ## `first_property_name`
404/// the name of the first property
405#[doc(alias = "gtk_show_about_dialog")]
406pub fn show_about_dialog<P: IsA<Window>>(parent: Option<&P>, properties: &[(&str, &dyn ToValue)]) {
407    assert_initialized_main_thread!();
408    static QUARK: OnceLock<Quark> = OnceLock::new();
409    let quark = *QUARK.get_or_init(|| Quark::from_str("gtk-rs-about-dialog"));
410
411    unsafe {
412        if let Some(d) = parent.and_then(|p| p.qdata::<AboutDialog>(quark)) {
413            d.as_ref().show();
414        } else {
415            let mut builder = glib::Object::builder::<AboutDialog>();
416            for (key, value) in properties {
417                builder = builder.property(key, *value);
418            }
419            let about_dialog = builder.build();
420            about_dialog.set_hide_on_close(true);
421
422            // cache the dialog if a parent is set
423            if let Some(dialog_parent) = parent {
424                about_dialog.set_modal(true);
425                about_dialog.set_transient_for(parent);
426                about_dialog.set_destroy_with_parent(true);
427                dialog_parent.set_qdata(quark, about_dialog.clone());
428            }
429
430            about_dialog.show();
431        };
432    }
433}
434
435/// Return the type ids that have been registered after
436/// calling gtk_test_register_all_types().
437///
438/// # Returns
439///
440///
441///    0-terminated array of type ids
442#[doc(alias = "gtk_test_list_all_types")]
443pub fn test_list_all_types() -> Slice<glib::Type> {
444    unsafe {
445        let mut n_types = std::mem::MaybeUninit::uninit();
446        let types = ffi::gtk_test_list_all_types(n_types.as_mut_ptr());
447        Slice::from_glib_container_num(types as *mut _, n_types.assume_init() as usize)
448    }
449}
450
451#[doc(alias = "gtk_style_context_add_provider_for_display")]
452#[doc(alias = "add_provider_for_display")]
453pub fn style_context_add_provider_for_display(
454    display: &impl IsA<gdk::Display>,
455    provider: &impl IsA<StyleProvider>,
456    priority: u32,
457) {
458    skip_assert_initialized!();
459    unsafe {
460        ffi::gtk_style_context_add_provider_for_display(
461            display.as_ref().to_glib_none().0,
462            provider.as_ref().to_glib_none().0,
463            priority,
464        );
465    }
466}
467
468#[doc(alias = "gtk_style_context_remove_provider_for_display")]
469#[doc(alias = "remove_provider_for_display")]
470pub fn style_context_remove_provider_for_display(
471    display: &impl IsA<gdk::Display>,
472    provider: &impl IsA<StyleProvider>,
473) {
474    skip_assert_initialized!();
475    unsafe {
476        ffi::gtk_style_context_remove_provider_for_display(
477            display.as_ref().to_glib_none().0,
478            provider.as_ref().to_glib_none().0,
479        );
480    }
481}