gtk4/
constraint_layout.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::translate::*;
4
5use crate::{ffi, prelude::*, Constraint, ConstraintLayout, Widget};
6
7impl ConstraintLayout {
8    /// Creates a list of constraints from a VFL description.
9    ///
10    /// The Visual Format Language, VFL, is based on Apple's AutoLayout [VFL](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html).
11    ///
12    /// The `views` dictionary is used to match [`ConstraintTarget`][crate::ConstraintTarget]
13    /// instances to the symbolic view name inside the VFL.
14    ///
15    /// The VFL grammar is:
16    ///
17    /// ```text
18    ///        <visualFormatString> = (<orientation>)?
19    ///                               (<superview><connection>)?
20    ///                               <view>(<connection><view>)*
21    ///                               (<connection><superview>)?
22    ///               <orientation> = 'H' | 'V'
23    ///                 <superview> = '|'
24    ///                <connection> = '' | '-' <predicateList> '-' | '-'
25    ///             <predicateList> = <simplePredicate> | <predicateListWithParens>
26    ///           <simplePredicate> = <metricName> | <positiveNumber>
27    ///   <predicateListWithParens> = '(' <predicate> (',' <predicate>)* ')'
28    ///                 <predicate> = (<relation>)? <objectOfPredicate> (<operatorList>)? ('@' <priority>)?
29    ///                  <relation> = '==' | '<=' | '>='
30    ///         <objectOfPredicate> = <constant> | <viewName> | ('.' <attributeName>)?
31    ///                  <priority> = <positiveNumber> | 'required' | 'strong' | 'medium' | 'weak'
32    ///                  <constant> = <number>
33    ///              <operatorList> = (<multiplyOperator>)? (<addOperator>)?
34    ///          <multiplyOperator> = [ '*' | '/' ] <positiveNumber>
35    ///               <addOperator> = [ '+' | '-' ] <positiveNumber>
36    ///                  <viewName> = [A-Za-z_]([A-Za-z0-9_]*) // A C identifier
37    ///                <metricName> = [A-Za-z_]([A-Za-z0-9_]*) // A C identifier
38    ///             <attributeName> = 'top' | 'bottom' | 'left' | 'right' | 'width' | 'height' |
39    ///                               'start' | 'end' | 'centerX' | 'centerY' | 'baseline'
40    ///            <positiveNumber> // A positive real number parseable by g_ascii_strtod()
41    ///                    <number> // A real number parseable by g_ascii_strtod()
42    /// ```
43    ///
44    /// **Note**: The VFL grammar used by GTK is slightly different than the one
45    /// defined by Apple, as it can use symbolic values for the constraint's
46    /// strength instead of numeric values; additionally, GTK allows adding
47    /// simple arithmetic operations inside predicates.
48    ///
49    /// Examples of VFL descriptions are:
50    ///
51    /// ```text
52    ///   // Default spacing
53    ///   [button]-[textField]
54    ///
55    ///   // Width constraint
56    ///   [button(>=50)]
57    ///
58    ///   // Connection to super view
59    ///   |-50-[purpleBox]-50-|
60    ///
61    ///   // Vertical layout
62    ///   V:[topField]-10-[bottomField]
63    ///
64    ///   // Flush views
65    ///   [maroonView][blueView]
66    ///
67    ///   // Priority
68    ///   [button(100@strong)]
69    ///
70    ///   // Equal widths
71    ///   [button1(==button2)]
72    ///
73    ///   // Multiple predicates
74    ///   [flexibleButton(>=70,<=100)]
75    ///
76    ///   // A complete line of layout
77    ///   |-[find]-[findNext]-[findField(>=20)]-|
78    ///
79    ///   // Operators
80    ///   [button1(button2 / 3 + 50)]
81    ///
82    ///   // Named attributes
83    ///   [button1(==button2.height)]
84    /// ```
85    /// ## `lines`
86    /// an array of Visual Format Language lines
87    ///   defining a set of constraints
88    /// ## `hspacing`
89    /// default horizontal spacing value, or -1 for the fallback value
90    /// ## `vspacing`
91    /// default vertical spacing value, or -1 for the fallback value
92    /// ## `views`
93    /// a dictionary of `[ name, target ]`
94    ///   pairs; the `name` keys map to the view names in the VFL lines, while
95    ///   the `target` values map to children of the widget using a [`ConstraintLayout`][crate::ConstraintLayout],
96    ///   or guides
97    ///
98    /// # Returns
99    ///
100    /// the list of
101    ///   [`Constraint`][crate::Constraint] instances that were added to the layout
102    #[doc(alias = "gtk_constraint_layout_add_constraints_from_descriptionv")]
103    #[doc(alias = "gtk_constraint_layout_add_constraints_from_description")]
104    #[doc(alias = "add_constraints_from_descriptionv")]
105    pub fn add_constraints_from_description<'a, W: IsA<Widget>>(
106        &self,
107        lines: impl IntoStrV,
108        hspacing: i32,
109        vspacing: i32,
110        views: impl IntoIterator<Item = (&'a str, &'a W)>,
111    ) -> Result<Vec<Constraint>, glib::Error> {
112        unsafe {
113            let mut err = std::ptr::null_mut();
114            let hash_table = glib::ffi::g_hash_table_new_full(
115                Some(glib::ffi::g_str_hash),
116                Some(glib::ffi::g_str_equal),
117                Some(glib::ffi::g_free),
118                Some(glib::ffi::g_free),
119            );
120
121            for (key, widget) in views {
122                let key_ptr: *mut libc::c_char = key.to_glib_full();
123                glib::ffi::g_hash_table_insert(
124                    hash_table,
125                    key_ptr as *mut _,
126                    widget.to_glib_full() as *mut _,
127                );
128            }
129
130            lines.run_with_strv(|lines| {
131                let out = ffi::gtk_constraint_layout_add_constraints_from_descriptionv(
132                    self.to_glib_none().0,
133                    lines.as_ptr() as *const _,
134                    lines.len() as _,
135                    hspacing,
136                    vspacing,
137                    hash_table,
138                    &mut err,
139                );
140                if !err.is_null() {
141                    Err(from_glib_full(err))
142                } else {
143                    Ok(FromGlibPtrContainer::from_glib_container(out))
144                }
145            })
146        }
147    }
148}