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