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::{translate::*, Quark, Slice};
6
7pub use crate::auto::functions::*;
8use crate::{ffi, prelude::*, AboutDialog, StyleProvider, Window};
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::ModifierType::CONTROL_MASK`][crate::gdk::ModifierType::CONTROL_MASK] mark 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] or [`None`] to use the default 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/// parseable by gtk_accelerator_parse().
100///
101/// For example, if you pass in `GDK_KEY_q` and [`gdk::ModifierType::CONTROL_MASK`][crate::gdk::ModifierType::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 parseable by gtk_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] or [`None`] to use the default 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        let mut error = std::ptr::null_mut();
347        let _ = ffi::gtk_show_uri_full_finish(parent_ptr as *mut ffi::GtkWindow, res, &mut error);
348        let result = if error.is_null() {
349            Ok(())
350        } else {
351            Err(from_glib_full(error))
352        };
353        let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
354            Box_::from_raw(user_data as *mut _);
355        let callback = callback.into_inner();
356        callback(result);
357    }
358    let callback = show_uri_full_trampoline::<P>;
359    unsafe {
360        ffi::gtk_show_uri_full(
361            parent.map(|p| p.as_ref()).to_glib_none().0,
362            uri.to_glib_none().0,
363            timestamp,
364            cancellable.map(|p| p.as_ref()).to_glib_none().0,
365            Some(callback),
366            Box_::into_raw(user_data) as *mut _,
367        );
368    }
369}
370
371#[cfg_attr(feature = "v4_10", deprecated = "Since 4.10")]
372#[allow(deprecated)]
373pub fn show_uri_full_future(
374    parent: Option<&(impl IsA<Window> + Clone + 'static)>,
375    uri: &str,
376    timestamp: u32,
377) -> Pin<Box_<dyn std::future::Future<Output = Result<(), glib::Error>> + 'static>> {
378    skip_assert_initialized!();
379    let parent = parent.map(ToOwned::to_owned);
380    let uri = String::from(uri);
381    Box_::pin(gio::GioFuture::new(&(), move |_obj, cancellable, send| {
382        show_uri_full(
383            parent.as_ref().map(::std::borrow::Borrow::borrow),
384            &uri,
385            timestamp,
386            Some(cancellable),
387            move |res| {
388                send.resolve(res);
389            },
390        );
391    }))
392}
393
394/// A convenience function for showing an application’s about dialog.
395///
396/// The constructed dialog is associated with the parent window and
397/// reused for future invocations of this function.
398/// ## `parent`
399/// the parent top-level window
400/// ## `first_property_name`
401/// the name of the first property
402#[doc(alias = "gtk_show_about_dialog")]
403pub fn show_about_dialog<P: IsA<Window>>(parent: Option<&P>, properties: &[(&str, &dyn ToValue)]) {
404    assert_initialized_main_thread!();
405    static QUARK: OnceLock<Quark> = OnceLock::new();
406    let quark = *QUARK.get_or_init(|| Quark::from_str("gtk-rs-about-dialog"));
407
408    unsafe {
409        if let Some(d) = parent.and_then(|p| p.qdata::<AboutDialog>(quark)) {
410            d.as_ref().show();
411        } else {
412            let mut builder = glib::Object::builder::<AboutDialog>();
413            for (key, value) in properties {
414                builder = builder.property(key, *value);
415            }
416            let about_dialog = builder.build();
417            about_dialog.set_hide_on_close(true);
418
419            // cache the dialog if a parent is set
420            if let Some(dialog_parent) = parent {
421                about_dialog.set_modal(true);
422                about_dialog.set_transient_for(parent);
423                about_dialog.set_destroy_with_parent(true);
424                dialog_parent.set_qdata(quark, about_dialog.clone());
425            }
426
427            about_dialog.show();
428        };
429    }
430}
431
432/// Return the type ids that have been registered after
433/// calling gtk_test_register_all_types().
434///
435/// # Returns
436///
437///
438///    0-terminated array of type ids
439#[doc(alias = "gtk_test_list_all_types")]
440pub fn test_list_all_types() -> Slice<glib::Type> {
441    unsafe {
442        let mut n_types = std::mem::MaybeUninit::uninit();
443        let types = ffi::gtk_test_list_all_types(n_types.as_mut_ptr());
444        Slice::from_glib_container_num(types as *mut _, n_types.assume_init() as usize)
445    }
446}
447
448#[doc(alias = "gtk_style_context_add_provider_for_display")]
449#[doc(alias = "add_provider_for_display")]
450pub fn style_context_add_provider_for_display(
451    display: &impl IsA<gdk::Display>,
452    provider: &impl IsA<StyleProvider>,
453    priority: u32,
454) {
455    skip_assert_initialized!();
456    unsafe {
457        ffi::gtk_style_context_add_provider_for_display(
458            display.as_ref().to_glib_none().0,
459            provider.as_ref().to_glib_none().0,
460            priority,
461        );
462    }
463}
464
465#[doc(alias = "gtk_style_context_remove_provider_for_display")]
466#[doc(alias = "remove_provider_for_display")]
467pub fn style_context_remove_provider_for_display(
468    display: &impl IsA<gdk::Display>,
469    provider: &impl IsA<StyleProvider>,
470) {
471    skip_assert_initialized!();
472    unsafe {
473        ffi::gtk_style_context_remove_provider_for_display(
474            display.as_ref().to_glib_none().0,
475            provider.as_ref().to_glib_none().0,
476        );
477    }
478}