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