gtk4/
accessible.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::{translate::*, Value};
4
5use crate::{
6    ffi, prelude::*, Accessible, AccessibleAutocomplete, AccessibleInvalidState,
7    AccessibleProperty, AccessibleRelation, AccessibleSort, AccessibleState, AccessibleTristate,
8    Orientation,
9};
10// rustdoc-stripper-ignore-next
11/// Trait containing manually implemented methods of
12/// [`Accessible`](crate::Accessible).
13///
14/// ```no_run
15/// # use gtk4 as gtk;
16/// # use gtk::prelude::*;
17/// let entry = gtk::Entry::new();
18/// let label = gtk::Label::new(Some("Entry"));
19/// entry.update_property(&[
20///     gtk::accessible::Property::Description("Test"),
21///     gtk::accessible::Property::Orientation(gtk::Orientation::Horizontal),
22/// ]);
23/// entry.update_relation(&[gtk::accessible::Relation::LabelledBy(&[label.upcast_ref()])]);
24/// entry.update_state(&[gtk::accessible::State::Invalid(
25///     gtk::AccessibleInvalidState::Grammar,
26/// )]);
27/// ```
28pub trait AccessibleExtManual: IsA<Accessible> {
29    #[doc(alias = "gtk_accessible_update_property")]
30    #[doc(alias = "gtk_accessible_update_property_value")]
31    fn update_property(&self, properties: &[Property]) {
32        let mut properties_ptr = vec![];
33        let mut values = vec![];
34        for prop in properties {
35            let (p, v) = prop.to_property_value();
36            properties_ptr.push(p.into_glib());
37            values.push(v);
38        }
39
40        unsafe {
41            ffi::gtk_accessible_update_property_value(
42                self.as_ref().to_glib_none().0,
43                properties.len() as i32,
44                mut_override(properties_ptr.as_ptr()),
45                ToGlibContainerFromSlice::to_glib_none_from_slice(&values).0,
46            )
47        }
48    }
49
50    #[doc(alias = "gtk_accessible_update_relation")]
51    #[doc(alias = "gtk_accessible_update_relation_value")]
52    fn update_relation(&self, relations: &[Relation]) {
53        let mut relations_ptr = vec![];
54        let mut values = vec![];
55        for relation in relations {
56            let (r, v) = relation.to_relation_value();
57            relations_ptr.push(r.into_glib());
58            values.push(v);
59        }
60
61        unsafe {
62            ffi::gtk_accessible_update_relation_value(
63                self.as_ref().to_glib_none().0,
64                relations.len() as i32,
65                mut_override(relations_ptr.as_ptr()),
66                ToGlibContainerFromSlice::to_glib_none_from_slice(&values).0,
67            )
68        }
69    }
70
71    #[doc(alias = "gtk_accessible_update_state")]
72    #[doc(alias = "gtk_accessible_update_state_value")]
73    fn update_state(&self, states: &[State]) {
74        let mut states_ptr = vec![];
75        let mut values = vec![];
76        for state in states {
77            let (s, v) = state.to_state_value();
78            states_ptr.push(s.into_glib());
79            values.push(v);
80        }
81
82        unsafe {
83            ffi::gtk_accessible_update_state_value(
84                self.as_ref().to_glib_none().0,
85                states.len() as i32,
86                mut_override(states_ptr.as_ptr()),
87                ToGlibContainerFromSlice::to_glib_none_from_slice(&values).0,
88            )
89        }
90    }
91}
92
93impl<O: IsA<Accessible>> AccessibleExtManual for O {}
94
95// rustdoc-stripper-ignore-next
96/// Type-safe enum container for
97/// [`AccessibleProperty`](crate::AccessibleProperty) values.
98#[derive(Debug)]
99#[non_exhaustive]
100pub enum Property<'p> {
101    Autocomplete(AccessibleAutocomplete),
102    Description(&'p str),
103    HasPopup(bool),
104    KeyShortcuts(&'p str),
105    Label(&'p str),
106    Level(i32),
107    Modal(bool),
108    MultiLine(bool),
109    MultiSelectable(bool),
110    Orientation(Orientation),
111    Placeholder(&'p str),
112    ReadOnly(bool),
113    Required(bool),
114    RoleDescription(&'p str),
115    Sort(AccessibleSort),
116    ValueMax(f64),
117    ValueMin(f64),
118    ValueNow(f64),
119    ValueText(&'p str),
120}
121
122impl Property<'_> {
123    fn to_property_value(&self) -> (AccessibleProperty, Value) {
124        use Property::*;
125
126        match self {
127            Autocomplete(v) => (AccessibleProperty::Autocomplete, v.into_glib().to_value()),
128            Description(v) => (AccessibleProperty::Description, v.to_value()),
129            HasPopup(v) => (AccessibleProperty::HasPopup, v.to_value()),
130            KeyShortcuts(v) => (AccessibleProperty::KeyShortcuts, v.to_value()),
131            Label(v) => (AccessibleProperty::Label, v.to_value()),
132            Level(v) => (AccessibleProperty::Level, v.to_value()),
133            Modal(v) => (AccessibleProperty::Modal, v.to_value()),
134            MultiLine(v) => (AccessibleProperty::MultiLine, v.to_value()),
135            MultiSelectable(v) => (AccessibleProperty::MultiSelectable, v.to_value()),
136            Orientation(v) => (AccessibleProperty::Orientation, v.into_glib().to_value()),
137            Placeholder(v) => (AccessibleProperty::Placeholder, v.to_value()),
138            ReadOnly(v) => (AccessibleProperty::ReadOnly, v.to_value()),
139            Required(v) => (AccessibleProperty::Required, v.to_value()),
140            RoleDescription(v) => (AccessibleProperty::RoleDescription, v.to_value()),
141            Sort(v) => (AccessibleProperty::Sort, v.into_glib().to_value()),
142            ValueMax(v) => (AccessibleProperty::ValueMax, v.to_value()),
143            ValueMin(v) => (AccessibleProperty::ValueMin, v.to_value()),
144            ValueNow(v) => (AccessibleProperty::ValueNow, v.to_value()),
145            ValueText(v) => (AccessibleProperty::ValueText, v.to_value()),
146        }
147    }
148}
149
150// rustdoc-stripper-ignore-next
151/// Type-safe enum container for
152/// [`AccessibleRelation`](crate::AccessibleRelation) values.
153#[derive(Debug)]
154#[non_exhaustive]
155pub enum Relation<'r> {
156    ActiveDescendant(&'r Accessible),
157    ColCount(i32),
158    ColIndex(i32),
159    ColIndexText(&'r str),
160    ColSpan(i32),
161    Controls(&'r [&'r Accessible]),
162    DescribedBy(&'r [&'r Accessible]),
163    Details(&'r [&'r Accessible]),
164    ErrorMessage(&'r [&'r Accessible]),
165    FlowTo(&'r [&'r Accessible]),
166    LabelledBy(&'r [&'r Accessible]),
167    Owns(&'r [&'r Accessible]),
168    PosInSet(i32),
169    RowCount(i32),
170    RowIndex(i32),
171    RowIndexText(&'r str),
172    RowSpan(i32),
173    SetSize(i32),
174}
175
176impl Relation<'_> {
177    fn to_relation_value(&self) -> (AccessibleRelation, Value) {
178        use Relation::*;
179
180        fn to_ref_list_value(objects: &[&Accessible]) -> Value {
181            skip_assert_initialized!();
182            let mut value = Value::from_type(glib::Type::POINTER);
183            let list =
184                ToGlibContainerFromSlice::<*mut glib::ffi::GList>::to_glib_container_from_slice(
185                    objects,
186                );
187            unsafe {
188                glib::gobject_ffi::g_value_set_pointer(
189                    value.to_glib_none_mut().0,
190                    list.0 as *mut std::ffi::c_void,
191                );
192            }
193            value
194        }
195
196        match self {
197            ActiveDescendant(v) => (AccessibleRelation::ActiveDescendant, v.to_value()),
198            ColCount(v) => (AccessibleRelation::ColCount, v.to_value()),
199            ColIndex(v) => (AccessibleRelation::ColIndex, v.to_value()),
200            ColIndexText(v) => (AccessibleRelation::ColIndexText, v.to_value()),
201            ColSpan(v) => (AccessibleRelation::ColSpan, v.to_value()),
202            Controls(v) => (AccessibleRelation::Controls, to_ref_list_value(v)),
203            DescribedBy(v) => (AccessibleRelation::DescribedBy, to_ref_list_value(v)),
204            Details(v) => (AccessibleRelation::Details, to_ref_list_value(v)),
205            ErrorMessage(v) => (AccessibleRelation::ErrorMessage, to_ref_list_value(v)),
206            FlowTo(v) => (AccessibleRelation::FlowTo, to_ref_list_value(v)),
207            LabelledBy(v) => (AccessibleRelation::LabelledBy, to_ref_list_value(v)),
208            Owns(v) => (AccessibleRelation::Owns, to_ref_list_value(v)),
209            PosInSet(v) => (AccessibleRelation::PosInSet, v.to_value()),
210            RowCount(v) => (AccessibleRelation::RowCount, v.to_value()),
211            RowIndex(v) => (AccessibleRelation::RowIndex, v.to_value()),
212            RowIndexText(v) => (AccessibleRelation::RowIndexText, v.to_value()),
213            RowSpan(v) => (AccessibleRelation::RowSpan, v.to_value()),
214            SetSize(v) => (AccessibleRelation::SetSize, v.to_value()),
215        }
216    }
217}
218
219// rustdoc-stripper-ignore-next
220/// Type-safe enum container for [`AccessibleState`](crate::AccessibleState)
221/// values.
222#[derive(Debug)]
223#[non_exhaustive]
224pub enum State {
225    Busy(bool),
226    Checked(AccessibleTristate),
227    Disabled(bool),
228    Expanded(Option<bool>),
229    Hidden(bool),
230    Invalid(AccessibleInvalidState),
231    Pressed(AccessibleTristate),
232    Selected(Option<bool>),
233}
234
235impl State {
236    fn to_state_value(&self) -> (AccessibleState, Value) {
237        use State::*;
238
239        fn to_optional_bool_value(b: &Option<bool>) -> Value {
240            skip_assert_initialized!();
241            b.map(|b| b as i32).unwrap_or(-1).to_value()
242        }
243
244        match self {
245            Busy(v) => (AccessibleState::Busy, v.to_value()),
246            Checked(v) => (AccessibleState::Checked, v.into_glib().to_value()),
247            Disabled(v) => (AccessibleState::Disabled, v.to_value()),
248            Expanded(v) => (AccessibleState::Expanded, to_optional_bool_value(v)),
249            Hidden(v) => (AccessibleState::Hidden, v.to_value()),
250            Invalid(v) => (AccessibleState::Invalid, v.into_glib().to_value()),
251            Pressed(v) => (AccessibleState::Pressed, v.into_glib().to_value()),
252            Selected(v) => (AccessibleState::Selected, to_optional_bool_value(v)),
253        }
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260    use crate::{self as gtk4, Button};
261
262    #[test]
263    fn test_accessible_update_property() {
264        let widget = glib::Object::new::<Button>();
265        widget.update_property(&[
266            Property::Autocomplete(AccessibleAutocomplete::Inline),
267            Property::Description("Test"),
268            Property::HasPopup(true),
269            Property::KeyShortcuts("Test"),
270            Property::Label("Test"),
271            Property::Level(123),
272            Property::Modal(true),
273            Property::MultiLine(true),
274            Property::MultiSelectable(true),
275            Property::Orientation(Orientation::Horizontal),
276            Property::Placeholder("Test"),
277            Property::ReadOnly(true),
278            Property::Required(true),
279            Property::RoleDescription("Test"),
280            Property::Sort(AccessibleSort::Ascending),
281            Property::ValueMax(1.0),
282            Property::ValueMin(1.0),
283            Property::ValueNow(1.0),
284            Property::ValueText("Test"),
285        ]);
286    }
287
288    #[test]
289    fn test_accessible_update_relation() {
290        use crate::prelude::*;
291
292        let widget = glib::Object::new::<Button>();
293        let other1 = glib::Object::new::<Button>();
294        let other2 = glib::Object::new::<Button>();
295        widget.update_relation(&[
296            Relation::ActiveDescendant(other1.upcast_ref()),
297            Relation::ColCount(123),
298            Relation::ColIndex(123),
299            Relation::ColIndexText("Test"),
300            Relation::ColSpan(123),
301            Relation::Controls(&[other1.upcast_ref(), other2.upcast_ref()]),
302            Relation::DescribedBy(&[other1.upcast_ref(), other2.upcast_ref()]),
303            Relation::Details(&[other1.upcast_ref(), other2.upcast_ref()]),
304            Relation::ErrorMessage(&[other1.upcast_ref()]),
305            Relation::FlowTo(&[other1.upcast_ref(), other2.upcast_ref()]),
306            Relation::LabelledBy(&[other1.upcast_ref(), other2.upcast_ref()]),
307            Relation::Owns(&[other1.upcast_ref(), other2.upcast_ref()]),
308            Relation::PosInSet(123),
309            Relation::RowCount(123),
310            Relation::RowIndex(123),
311            Relation::RowIndexText("Test"),
312            Relation::RowSpan(123),
313            Relation::SetSize(123),
314        ]);
315    }
316
317    #[test]
318    fn test_accessible_update_state() {
319        let widget = glib::Object::new::<Button>();
320        widget.update_state(&[
321            State::Busy(true),
322            State::Checked(AccessibleTristate::Mixed),
323            State::Disabled(true),
324            State::Expanded(Some(true)),
325            State::Hidden(true),
326            State::Invalid(AccessibleInvalidState::Grammar),
327            State::Pressed(AccessibleTristate::Mixed),
328            State::Selected(Some(true)),
329        ]);
330    }
331}