1use enumflags2::{bitflags, BitFlag, BitFlags, FromBitsError};
2use serde::{
3 de::{self, Deserializer, Visitor},
4 ser::{SerializeSeq, Serializer},
5 Deserialize, Serialize,
6};
7use std::fmt;
8use zvariant::{Signature, Type};
9
10/// Used by various interfaces indicating every possible state
11/// of an accessibility object.
12#[bitflags]
13#[non_exhaustive]
14#[repr(u64)]
15#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
16#[serde(rename_all = "kebab-case")]
17pub enum State {
18 /// Indicates an invalid state - probably an error condition.
19 #[default]
20 Invalid,
21 /// Indicates a window is currently the active window, or
22 /// an object is the active subelement within a container or table.
23 ///
24 /// `Active` should not be used for objects which have
25 /// [`State::Focusable`] or [`State::Selectable`]: Those objects should use
26 /// [`State::Focused`] and [`State::Selected`] respectively.
27 ///
28 /// `Active` is a means to indicate that an object which is not
29 /// focusable and not selectable is the currently-active item within its
30 /// parent container.
31 Active,
32 /// Indicates that the object is armed.
33 Armed,
34 /// Indicates the current object is busy, i.e. onscreen
35 /// representation is in the process of changing, or the object is
36 /// temporarily unavailable for interaction due to activity already in progress.
37 Busy,
38 /// Indicates this object is currently checked.
39 Checked,
40 /// Indicates this object is collapsed.
41 Collapsed,
42 /// Indicates that this object no longer has a valid
43 /// backing widget (for instance, if its peer object has been destroyed).
44 Defunct,
45 /// Indicates the user can change the contents of this object.
46 Editable,
47 /// Indicates that this object is enabled, i.e. that it
48 /// currently reflects some application state. Objects that are "greyed out"
49 /// may lack this state, and may lack the [`State::Sensitive`] if direct
50 /// user interaction cannot cause them to acquire `Enabled`.
51 ///
52 /// See [`State::Sensitive`].
53 Enabled,
54 /// Indicates this object allows progressive
55 /// disclosure of its children.
56 Expandable,
57 /// Indicates this object is expanded.
58 Expanded,
59 /// Indicates this object can accept keyboard focus,
60 /// which means all events resulting from typing on the keyboard will
61 /// normally be passed to it when it has focus.
62 Focusable,
63 /// Indicates this object currently has the keyboard focus.
64 Focused,
65 /// Indicates that the object has an associated tooltip.
66 HasTooltip,
67 /// Indicates the orientation of this object is horizontal.
68 Horizontal,
69 /// Indicates this object is minimized and is
70 /// represented only by an icon.
71 Iconified,
72 /// Indicates something must be done with this object
73 /// before the user can interact with an object in a different window.
74 Modal,
75 /// Indicates this (text) object can contain multiple
76 /// lines of text.
77 MultiLine,
78 /// Indicates this object allows more than one of
79 /// its children to be selected at the same time, or in the case of text
80 /// objects, that the object supports non-contiguous text selections.
81 Multiselectable,
82 /// Indicates this object paints every pixel within its
83 /// rectangular region. It also indicates an alpha value of unity, if it
84 /// supports alpha blending.
85 Opaque,
86 /// Indicates this object is currently pressed.
87 Pressed,
88 /// Indicates the size of this object's size is not fixed.
89 Resizable,
90 /// Indicates this object is the child of an object
91 /// that allows its children to be selected and that this child is one of
92 /// those children that can be selected.
93 Selectable,
94 /// Indicates this object is the child of an object that
95 /// allows its children to be selected and that this child is one of those
96 /// children that has been selected.
97 Selected,
98 /// Indicates this object is sensitive, e.g. to user
99 /// interaction. `Sensitive` usually accompanies.
100 /// [`State::Enabled`] for user-actionable controls, but may be found in the
101 /// absence of [`State::Enabled`] if the current visible state of the control
102 /// is "disconnected" from the application state. In such cases, direct user
103 /// interaction can often result in the object gaining `Sensitive`,
104 /// for instance if a user makes an explicit selection using an object whose
105 /// current state is ambiguous or undefined.
106 ///
107 /// See [`State::Enabled`], [`State::Indeterminate`].
108 Sensitive,
109 /// Indicates this object, the object's parent, the
110 /// object's parent's parent, and so on, are all 'shown' to the end-user,
111 /// i.e. subject to "exposure" if blocking or obscuring objects do not
112 /// interpose between this object and the top of the window stack.
113 Showing,
114 /// Indicates this (text) object can contain only a
115 /// single line of text.
116 SingleLine,
117 /// Indicates that the information returned for this object
118 /// may no longer be synchronized with the application state. This can occur
119 /// if the object has [`State::Transient`], and can also occur towards the
120 /// end of the object peer's lifecycle.
121 Stale,
122 /// Indicates this object is transient.
123 Transient,
124 /// Indicates the orientation of this object is vertical;
125 /// for example this state may appear on such objects as scrollbars, text
126 /// objects (with vertical text flow), separators, etc.
127 Vertical,
128 /// Indicates this object is visible, e.g. has been
129 /// explicitly marked for exposure to the user. `Visible` is no
130 /// guarantee that the object is actually unobscured on the screen, only that
131 /// it is 'potentially' visible, barring obstruction, being scrolled or clipped
132 /// out of the field of view, or having an ancestor container that has not yet
133 /// made visible. A widget is potentially onscreen if it has both
134 /// `Visible` and [`State::Showing`]. The absence of
135 /// `Visible` and [`State::Showing`] is
136 /// semantically equivalent to saying that an object is 'hidden'.
137 Visible,
138 /// Indicates that "active-descendant-changed"
139 /// event is sent when children become 'active' (i.e. are selected or
140 /// navigated to onscreen). Used to prevent need to enumerate all children
141 /// in very large containers, like tables. The presence of
142 /// `ManagesDescendants` is an indication to the client that the
143 /// children should not, and need not, be enumerated by the client.
144 /// Objects implementing this state are expected to provide relevant state
145 /// notifications to listening clients, for instance notifications of
146 /// visibility changes and activation of their contained child objects, without
147 /// the client having previously requested references to those children.
148 ManagesDescendants,
149 /// Indicates that a check box or other boolean
150 /// indicator is in a state other than checked or not checked.
151 ///
152 /// This usually means that the boolean value reflected or controlled by the
153 /// object does not apply consistently to the entire current context.
154 /// For example, a checkbox for the "Bold" attribute of text may have
155 /// `Indeterminate` if the currently selected text contains a mixture
156 /// of weight attributes. In many cases interacting with a
157 /// `Indeterminate` object will cause the context's corresponding
158 /// boolean attribute to be homogenized, whereupon the object will lose
159 /// `Indeterminate` and a corresponding state-changed event will be
160 /// fired.
161 Indeterminate,
162 /// Indicates that user interaction with this object is
163 /// 'required' from the user, for instance before completing the
164 /// processing of a form.
165 Required,
166 /// Indicates that an object's onscreen content
167 /// is truncated, e.g. a text value in a spreadsheet cell.
168 Truncated,
169 /// Indicates this object's visual representation is
170 /// dynamic, not static. This state may be applied to an object during an
171 /// animated 'effect' and be removed from the object once its visual
172 /// representation becomes static. Some applications, notably content viewers,
173 /// may not be able to detect all kinds of animated content. Therefore the
174 /// absence of this state should not be taken as
175 /// definitive evidence that the object's visual representation is
176 /// static; this state is advisory.
177 Animated,
178 /// This object has indicated an error condition
179 /// due to failure of input validation. For instance, a form control may
180 /// acquire this state in response to invalid or malformed user input.
181 InvalidEntry,
182 /// This state indicates that the object
183 /// in question implements some form of typeahead or
184 /// pre-selection behavior whereby entering the first character of one or more
185 /// sub-elements causes those elements to scroll into view or become
186 /// selected. Subsequent character input may narrow the selection further as
187 /// long as one or more sub-elements match the string. This state is normally
188 /// only useful and encountered on objects that implement [`crate::interface::Interface::Selection`].
189 /// In some cases the typeahead behavior may result in full or partial
190 /// completion of the data in the input field, in which case
191 /// these input events may trigger text-changed events from the source.
192 SupportsAutocompletion,
193 /// Indicates that the object in
194 /// question supports text selection. It should only be exposed on objects
195 /// which implement the [`crate::interface::Interface::Text`] interface, in order to distinguish this state
196 /// from [`State::Selectable`], which infers that the object in question is a
197 /// selectable child of an object which implements [`crate::interface::Interface::Selection`]. While
198 /// similar, text selection and subelement selection are distinct operations.
199 SelectableText,
200 /// Indicates that the object in question is
201 /// the 'default' interaction object in a dialog, i.e. the one that gets
202 /// activated if the user presses "Enter" when the dialog is initially
203 /// posted.
204 IsDefault,
205 /// Indicates that the object (typically a
206 /// hyperlink) has already been activated or invoked, with the result that
207 /// some backing data has been downloaded or rendered.
208 Visited,
209 /// Indicates this object has the potential to
210 /// be checked, such as a checkbox or toggle-able table cell.
211 Checkable,
212 /// Indicates that the object has a popup
213 /// context menu or sub-level menu which may or may not be
214 /// showing. This means that activation renders conditional content.
215 /// Note that ordinary tooltips are not considered popups in this
216 /// context.
217 HasPopup,
218 /// Indicates that an object which is [`State::Enabled`] and
219 /// [`State::Sensitive`] has a value which can be read, but not modified, by the
220 /// user.
221 ReadOnly,
222}
223
224impl From<State> for String {
225 fn from(state: State) -> String {
226 match state {
227 State::Invalid => "invalid",
228 State::Active => "active",
229 State::Armed => "armed",
230 State::Busy => "busy",
231 State::Checked => "checked",
232 State::Collapsed => "collapsed",
233 State::Defunct => "defunct",
234 State::Editable => "editable",
235 State::Enabled => "enabled",
236 State::Expandable => "expandable",
237 State::Expanded => "expanded",
238 State::Focusable => "focusable",
239 State::Focused => "focused",
240 State::HasTooltip => "has-tooltip",
241 State::Horizontal => "horizontal",
242 State::Iconified => "iconified",
243 State::Modal => "modal",
244 State::MultiLine => "multi-line",
245 State::Multiselectable => "multiselectable",
246 State::Opaque => "opaque",
247 State::Pressed => "pressed",
248 State::Resizable => "resizable",
249 State::Selectable => "selectable",
250 State::Selected => "selected",
251 State::Sensitive => "sensitive",
252 State::Showing => "showing",
253 State::SingleLine => "single-line",
254 State::Stale => "stale",
255 State::Transient => "transient",
256 State::Vertical => "vertical",
257 State::Visible => "visible",
258 State::ManagesDescendants => "manages-descendants",
259 State::Indeterminate => "indeterminate",
260 State::Required => "required",
261 State::Truncated => "truncated",
262 State::Animated => "animated",
263 State::InvalidEntry => "invalid-entry",
264 State::SupportsAutocompletion => "supports-autocompletion",
265 State::SelectableText => "selectable-text",
266 State::IsDefault => "is-default",
267 State::Visited => "visited",
268 State::Checkable => "checkable",
269 State::HasPopup => "has-popup",
270 State::ReadOnly => "read-only",
271 }
272 .to_string()
273 }
274}
275
276impl From<String> for State {
277 fn from(string: String) -> State {
278 (&*string).into()
279 }
280}
281
282impl From<&str> for State {
283 fn from(string: &str) -> State {
284 match string {
285 "active" => State::Active,
286 "armed" => State::Armed,
287 "busy" => State::Busy,
288 "checked" => State::Checked,
289 "collapsed" => State::Collapsed,
290 "defunct" => State::Defunct,
291 "editable" => State::Editable,
292 "enabled" => State::Enabled,
293 "expandable" => State::Expandable,
294 "expanded" => State::Expanded,
295 "focusable" => State::Focusable,
296 "focused" => State::Focused,
297 "has-tooltip" => State::HasTooltip,
298 "horizontal" => State::Horizontal,
299 "iconified" => State::Iconified,
300 "modal" => State::Modal,
301 "multi-line" => State::MultiLine,
302 "multiselectable" => State::Multiselectable,
303 "opaque" => State::Opaque,
304 "pressed" => State::Pressed,
305 "resizable" => State::Resizable,
306 "selectable" => State::Selectable,
307 "selected" => State::Selected,
308 "sensitive" => State::Sensitive,
309 "showing" => State::Showing,
310 "single-line" => State::SingleLine,
311 "stale" => State::Stale,
312 "transient" => State::Transient,
313 "vertical" => State::Vertical,
314 "visible" => State::Visible,
315 "manages-descendants" => State::ManagesDescendants,
316 "indeterminate" => State::Indeterminate,
317 "required" => State::Required,
318 "truncated" => State::Truncated,
319 "animated" => State::Animated,
320 "invalid-entry" => State::InvalidEntry,
321 "supports-autocompletion" => State::SupportsAutocompletion,
322 "selectable-text" => State::SelectableText,
323 "is-default" => State::IsDefault,
324 "visited" => State::Visited,
325 "checkable" => State::Checkable,
326 "has-popup" => State::HasPopup,
327 "read-only" => State::ReadOnly,
328 _ => State::Invalid,
329 }
330 }
331}
332
333#[allow(clippy::module_name_repetitions)]
334#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
335/// The bitflag representation of all states an object may have.
336pub struct StateSet(BitFlags<State>);
337
338impl StateSet {
339 /// Create a new `StateSet`.
340 ///
341 ///## Example
342 ///```Rust
343 /// let states = State::Focusable | State::Sensitive | State::Active;
344 /// let set = StateSet::new(states);
345 ///
346 /// assert!(set.contains(State::Active));
347 /// assert!(!set.contains(State::Busy));
348 /// ```
349 pub fn new<B: Into<BitFlags<State>>>(value: B) -> Self {
350 Self(value.into())
351 }
352
353 /// Returns the `StateSet` that corresponds to the provided `u64`s bit pattern.
354 ///# Errors
355 /// When the argument encodes an undefined [`State`].
356 pub fn from_bits(bits: u64) -> Result<StateSet, FromBitsError<State>> {
357 Ok(StateSet(BitFlags::from_bits(bits)?))
358 }
359
360 #[must_use]
361 /// Create an empty `StateSet`
362 pub fn empty() -> StateSet {
363 StateSet(State::empty())
364 }
365
366 #[must_use]
367 /// Returns the state as represented by a u64.
368 pub fn bits(&self) -> u64 {
369 self.0.bits()
370 }
371
372 /// Whether the `StateSet` contains a [`State`].
373 pub fn contains<B: Into<BitFlags<State>>>(self, other: B) -> bool {
374 self.0.contains(other)
375 }
376
377 /// Removes a [`State`] (optionally) previously contained in the `StateSet`.
378 pub fn remove<B: Into<BitFlags<State>>>(&mut self, other: B) {
379 self.0.remove(other);
380 }
381
382 /// Inserts a [`State`] in the `StateSet`.
383 pub fn insert<B: Into<BitFlags<State>>>(&mut self, other: B) {
384 self.0.insert(other);
385 }
386
387 /// Returns an iterator that yields each set [`State`].
388 pub fn iter(self) -> impl Iterator<Item = State> {
389 self.0.iter()
390 }
391
392 #[must_use]
393 /// Checks if all states are unset.
394 pub fn is_empty(self) -> bool {
395 self.0.is_empty()
396 }
397
398 /// Returns true if at least one flag is shared.
399 pub fn intersects<B: Into<BitFlags<State>>>(self, other: B) -> bool {
400 self.0.intersects(other)
401 }
402
403 /// Toggles the matching bits.
404 pub fn toggle<B: Into<BitFlags<State>>>(&mut self, other: B) {
405 self.0.toggle(other);
406 }
407}
408
409impl<'de> Deserialize<'de> for StateSet {
410 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
411 where
412 D: Deserializer<'de>,
413 {
414 struct StateSetVisitor;
415
416 impl<'de> Visitor<'de> for StateSetVisitor {
417 type Value = StateSet;
418
419 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
420 formatter
421 .write_str("a sequence comprised of two u32 that represents a valid StateSet")
422 }
423
424 fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
425 where
426 D: Deserializer<'de>,
427 {
428 match <Vec<u32> as Deserialize>::deserialize(deserializer) {
429 Ok(states) if states.len() == 2 => {
430 let mut bits = u64::from(states[0]);
431 bits |= (u64::from(states[1])) << 32;
432 StateSet::from_bits(bits).map_err(|_| de::Error::custom("invalid state"))
433 }
434 Ok(states) => Err(de::Error::invalid_length(states.len(), &"array of size 2")),
435 Err(e) => Err(e),
436 }
437 }
438 }
439
440 deserializer.deserialize_newtype_struct("StateSet", StateSetVisitor)
441 }
442}
443
444impl Serialize for StateSet {
445 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
446 where
447 S: Serializer,
448 {
449 let mut seq: ::SerializeSeq = serializer.serialize_seq(len:Some(2))?;
450 let bits: u64 = self.bits();
451
452 // This cast is safe and truncation is intentional.
453 //The shift is sound provided that `State` is `#[repr(u64)]`
454 #[allow(clippy::cast_possible_truncation)]
455 seq.serialize_element(&(bits as u32))?;
456 seq.serialize_element(&((bits >> 32) as u32))?;
457 seq.end()
458 }
459}
460
461impl Type for StateSet {
462 fn signature() -> Signature<'static> {
463 <Vec<u32> as Type>::signature()
464 }
465}
466
467impl From<State> for StateSet {
468 fn from(value: State) -> Self {
469 Self(value.into())
470 }
471}
472
473impl std::ops::BitXor for StateSet {
474 type Output = StateSet;
475
476 fn bitxor(self, other: Self) -> Self::Output {
477 StateSet(self.0 ^ other.0)
478 }
479}
480impl std::ops::BitXorAssign for StateSet {
481 fn bitxor_assign(&mut self, other: Self) {
482 self.0 = self.0 ^ other.0;
483 }
484}
485impl std::ops::BitOr for StateSet {
486 type Output = StateSet;
487
488 fn bitor(self, other: Self) -> Self::Output {
489 StateSet(self.0 | other.0)
490 }
491}
492impl std::ops::BitOrAssign for StateSet {
493 fn bitor_assign(&mut self, other: Self) {
494 self.0 = self.0 | other.0;
495 }
496}
497impl std::ops::BitAnd for StateSet {
498 type Output = StateSet;
499
500 fn bitand(self, other: Self) -> Self::Output {
501 StateSet(self.0 & other.0)
502 }
503}
504impl std::ops::BitAndAssign for StateSet {
505 fn bitand_assign(&mut self, other: Self) {
506 self.0 = self.0 & other.0;
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513 use byteorder::LE;
514 use serde_plain;
515 use zbus::zvariant::{from_slice, to_bytes, EncodingContext as Context};
516
517 #[test]
518 fn serialize_empty_state_set() {
519 let ctxt = Context::<LE>::new_dbus(0);
520 let encoded = to_bytes(ctxt, &StateSet::empty()).unwrap();
521 assert_eq!(encoded, &[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
522 }
523
524 #[test]
525 fn deserialize_empty_state_set() {
526 let ctxt = Context::<LE>::new_dbus(0);
527 let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ctxt).unwrap();
528 assert_eq!(decoded, StateSet::empty());
529 }
530
531 #[test]
532 fn serialize_state_set_invalid() {
533 let ctxt = Context::<LE>::new_dbus(0);
534 let encoded = to_bytes(ctxt, &StateSet::new(State::Invalid)).unwrap();
535 assert_eq!(encoded, &[8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]);
536 }
537
538 #[test]
539 fn deserialize_state_set_invalid() {
540 let ctxt = Context::<LE>::new_dbus(0);
541 let decoded: StateSet = from_slice(&[8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], ctxt).unwrap();
542 assert_eq!(decoded, StateSet::new(State::Invalid));
543 }
544
545 #[test]
546 fn serialize_state_set_manages_descendants() {
547 let ctxt = Context::<LE>::new_dbus(0);
548 let encoded = to_bytes(ctxt, &StateSet::new(State::ManagesDescendants)).unwrap();
549 assert_eq!(encoded, &[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0]);
550 }
551
552 #[test]
553 fn deserialize_state_set_manages_descendants() {
554 let ctxt = Context::<LE>::new_dbus(0);
555 let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0], ctxt).unwrap();
556 assert_eq!(decoded, StateSet::new(State::ManagesDescendants));
557 }
558
559 #[test]
560 fn serialize_state_set_indeterminate() {
561 let ctxt = Context::<LE>::new_dbus(0);
562 let encoded = to_bytes(ctxt, &StateSet::new(State::Indeterminate)).unwrap();
563 assert_eq!(encoded, &[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]);
564 }
565
566 #[test]
567 fn deserialize_state_set_indeterminate() {
568 let ctxt = Context::<LE>::new_dbus(0);
569 let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], ctxt).unwrap();
570 assert_eq!(decoded, StateSet::new(State::Indeterminate));
571 }
572
573 #[test]
574 fn serialize_state_set_focusable_focused() {
575 let ctxt = Context::<LE>::new_dbus(0);
576 let encoded = to_bytes(ctxt, &StateSet::new(State::Focusable | State::Focused)).unwrap();
577 assert_eq!(encoded, &[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0]);
578 }
579
580 #[test]
581 fn deserialize_state_set_focusable_focused() {
582 let ctxt = Context::<LE>::new_dbus(0);
583 let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0], ctxt).unwrap();
584 assert_eq!(decoded, StateSet::new(State::Focusable | State::Focused));
585 }
586
587 #[test]
588 fn cannot_deserialize_state_set_invalid_length() {
589 let ctxt = Context::<LE>::new_dbus(0);
590 let decoded = from_slice::<_, StateSet>(&[4, 0, 0, 0, 0, 0, 0, 0], ctxt);
591 assert!(decoded.is_err());
592 }
593
594 #[test]
595 fn cannot_deserialize_state_set_invalid_flag() {
596 let ctxt = Context::<LE>::new_dbus(0);
597 let decoded = from_slice::<_, StateSet>(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32], ctxt);
598 assert!(decoded.is_err());
599 }
600
601 #[test]
602 fn convert_state_direct_string() {
603 for state in StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)
604 .unwrap()
605 .iter()
606 {
607 let state_str: String = state.into();
608 let state_two: State = state_str.clone().into();
609 assert_eq!(
610 state, state_two,
611 "The {state:?} was serialized as {state_str}, which deserializes to {state_two:?}"
612 );
613 }
614 }
615 #[test]
616 fn convert_state_direct_string_is_equal_to_serde_output() {
617 for state in StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)
618 .unwrap()
619 .iter()
620 {
621 let serde_state_str: String = serde_plain::to_string(&state).unwrap();
622 let state_str: String = state.into();
623 assert_eq!(serde_state_str, state_str);
624 let state_two: State = serde_plain::from_str(&state_str).unwrap();
625 assert_eq!(state, state_two, "The {state:?} was serialized as {state_str}, which deserializes to {state_two:?} (serde)");
626 }
627 }
628}
629