Skip to main content

iced_selector/
target.rs

1use crate::core::widget::Id;
2use crate::core::widget::operation::accessible::{Accessible, Role};
3use crate::core::widget::operation::{Focusable, Scrollable, TextInput};
4use crate::core::{Rectangle, Vector};
5
6use std::any::Any;
7
8/// A generic widget match produced during selection.
9#[allow(missing_docs)]
10#[derive(Debug, Clone, PartialEq)]
11pub enum Target {
12    Container {
13        id: Option<Id>,
14        bounds: Rectangle,
15        visible_bounds: Option<Rectangle>,
16    },
17    Focusable {
18        id: Option<Id>,
19        bounds: Rectangle,
20        visible_bounds: Option<Rectangle>,
21    },
22    Scrollable {
23        id: Option<Id>,
24        bounds: Rectangle,
25        visible_bounds: Option<Rectangle>,
26        content_bounds: Rectangle,
27        translation: Vector,
28    },
29    TextInput {
30        id: Option<Id>,
31        bounds: Rectangle,
32        visible_bounds: Option<Rectangle>,
33        content: String,
34    },
35    Text {
36        id: Option<Id>,
37        bounds: Rectangle,
38        visible_bounds: Option<Rectangle>,
39        content: String,
40    },
41    Custom {
42        id: Option<Id>,
43        bounds: Rectangle,
44        visible_bounds: Option<Rectangle>,
45    },
46    Accessible {
47        id: Option<Id>,
48        bounds: Rectangle,
49        visible_bounds: Option<Rectangle>,
50        role: Role,
51        label: Option<String>,
52        description: Option<String>,
53    },
54}
55
56impl Target {
57    /// Returns the layout bounds of the [`Target`].
58    pub fn bounds(&self) -> Rectangle {
59        match self {
60            Target::Container { bounds, .. }
61            | Target::Focusable { bounds, .. }
62            | Target::Scrollable { bounds, .. }
63            | Target::TextInput { bounds, .. }
64            | Target::Text { bounds, .. }
65            | Target::Custom { bounds, .. }
66            | Target::Accessible { bounds, .. } => *bounds,
67        }
68    }
69
70    /// Returns the visible bounds of the [`Target`], in screen coordinates.
71    pub fn visible_bounds(&self) -> Option<Rectangle> {
72        match self {
73            Target::Container { visible_bounds, .. }
74            | Target::Focusable { visible_bounds, .. }
75            | Target::Scrollable { visible_bounds, .. }
76            | Target::TextInput { visible_bounds, .. }
77            | Target::Text { visible_bounds, .. }
78            | Target::Custom { visible_bounds, .. }
79            | Target::Accessible { visible_bounds, .. } => *visible_bounds,
80        }
81    }
82}
83
84impl From<Candidate<'_>> for Target {
85    fn from(candidate: Candidate<'_>) -> Self {
86        match candidate {
87            Candidate::Container {
88                id,
89                bounds,
90                visible_bounds,
91            } => Self::Container {
92                id: id.cloned(),
93                bounds,
94                visible_bounds,
95            },
96            Candidate::Focusable {
97                id,
98                bounds,
99                visible_bounds,
100                ..
101            } => Self::Focusable {
102                id: id.cloned(),
103                bounds,
104                visible_bounds,
105            },
106            Candidate::Scrollable {
107                id,
108                bounds,
109                visible_bounds,
110                content_bounds,
111                translation,
112                ..
113            } => Self::Scrollable {
114                id: id.cloned(),
115                bounds,
116                visible_bounds,
117                content_bounds,
118                translation,
119            },
120            Candidate::TextInput {
121                id,
122                bounds,
123                visible_bounds,
124                state,
125            } => Self::TextInput {
126                id: id.cloned(),
127                bounds,
128                visible_bounds,
129                content: state.text().to_owned(),
130            },
131            Candidate::Text {
132                id,
133                bounds,
134                visible_bounds,
135                content,
136            } => Self::Text {
137                id: id.cloned(),
138                bounds,
139                visible_bounds,
140                content: content.to_owned(),
141            },
142            Candidate::Custom {
143                id,
144                bounds,
145                visible_bounds,
146                ..
147            } => Self::Custom {
148                id: id.cloned(),
149                bounds,
150                visible_bounds,
151            },
152            Candidate::Accessible {
153                id,
154                bounds,
155                visible_bounds,
156                accessible,
157            } => Self::Accessible {
158                id: id.cloned(),
159                bounds,
160                visible_bounds,
161                role: accessible.role,
162                label: accessible.label.map(str::to_owned),
163                description: accessible.description.map(str::to_owned),
164            },
165        }
166    }
167}
168
169impl Bounded for Target {
170    fn bounds(&self) -> Rectangle {
171        self.bounds()
172    }
173
174    fn visible_bounds(&self) -> Option<Rectangle> {
175        self.visible_bounds()
176    }
177}
178
179/// A selection candidate.
180///
181/// This is provided to [`Selector::select`](crate::Selector::select).
182#[allow(missing_docs)]
183#[derive(Clone)]
184pub enum Candidate<'a> {
185    Container {
186        id: Option<&'a Id>,
187        bounds: Rectangle,
188        visible_bounds: Option<Rectangle>,
189    },
190    Focusable {
191        id: Option<&'a Id>,
192        bounds: Rectangle,
193        visible_bounds: Option<Rectangle>,
194        state: &'a dyn Focusable,
195    },
196    Scrollable {
197        id: Option<&'a Id>,
198        bounds: Rectangle,
199        content_bounds: Rectangle,
200        visible_bounds: Option<Rectangle>,
201        translation: Vector,
202        state: &'a dyn Scrollable,
203    },
204    TextInput {
205        id: Option<&'a Id>,
206        bounds: Rectangle,
207        visible_bounds: Option<Rectangle>,
208        state: &'a dyn TextInput,
209    },
210    Text {
211        id: Option<&'a Id>,
212        bounds: Rectangle,
213        visible_bounds: Option<Rectangle>,
214        content: &'a str,
215    },
216    Custom {
217        id: Option<&'a Id>,
218        bounds: Rectangle,
219        visible_bounds: Option<Rectangle>,
220        state: &'a dyn Any,
221    },
222    Accessible {
223        id: Option<&'a Id>,
224        bounds: Rectangle,
225        visible_bounds: Option<Rectangle>,
226        accessible: &'a Accessible<'a>,
227    },
228}
229
230impl<'a> Candidate<'a> {
231    /// Returns the widget [`Id`] of the [`Candidate`].
232    pub fn id(&self) -> Option<&'a Id> {
233        match self {
234            Candidate::Container { id, .. }
235            | Candidate::Focusable { id, .. }
236            | Candidate::Scrollable { id, .. }
237            | Candidate::TextInput { id, .. }
238            | Candidate::Text { id, .. }
239            | Candidate::Custom { id, .. }
240            | Candidate::Accessible { id, .. } => *id,
241        }
242    }
243
244    /// Returns the layout bounds of the [`Candidate`].
245    pub fn bounds(&self) -> Rectangle {
246        match self {
247            Candidate::Container { bounds, .. }
248            | Candidate::Focusable { bounds, .. }
249            | Candidate::Scrollable { bounds, .. }
250            | Candidate::TextInput { bounds, .. }
251            | Candidate::Text { bounds, .. }
252            | Candidate::Custom { bounds, .. }
253            | Candidate::Accessible { bounds, .. } => *bounds,
254        }
255    }
256
257    /// Returns the visible bounds of the [`Candidate`], in screen coordinates.
258    pub fn visible_bounds(&self) -> Option<Rectangle> {
259        match self {
260            Candidate::Container { visible_bounds, .. }
261            | Candidate::Focusable { visible_bounds, .. }
262            | Candidate::Scrollable { visible_bounds, .. }
263            | Candidate::TextInput { visible_bounds, .. }
264            | Candidate::Text { visible_bounds, .. }
265            | Candidate::Custom { visible_bounds, .. }
266            | Candidate::Accessible { visible_bounds, .. } => *visible_bounds,
267        }
268    }
269}
270
271/// A bounded type has both layout bounds and visible bounds.
272///
273/// This trait lets us write generic code over the [`Output`](crate::Selector::Output)
274/// of a [`Selector`](crate::Selector).
275pub trait Bounded: std::fmt::Debug {
276    /// Returns the layout bounds.
277    fn bounds(&self) -> Rectangle;
278
279    /// Returns the visible bounds, in screen coordinates.
280    fn visible_bounds(&self) -> Option<Rectangle>;
281}
282
283/// A text match.
284#[allow(missing_docs)]
285#[derive(Debug, Clone, PartialEq)]
286pub enum Text {
287    Raw {
288        id: Option<Id>,
289        bounds: Rectangle,
290        visible_bounds: Option<Rectangle>,
291    },
292    Input {
293        id: Option<Id>,
294        bounds: Rectangle,
295        visible_bounds: Option<Rectangle>,
296    },
297}
298
299impl Text {
300    /// Returns the layout bounds of the [`Text`].
301    pub fn bounds(&self) -> Rectangle {
302        match self {
303            Text::Raw { bounds, .. } | Text::Input { bounds, .. } => *bounds,
304        }
305    }
306
307    /// Returns the visible bounds of the [`Text`], in screen coordinates.
308    pub fn visible_bounds(&self) -> Option<Rectangle> {
309        match self {
310            Text::Raw { visible_bounds, .. } | Text::Input { visible_bounds, .. } => {
311                *visible_bounds
312            }
313        }
314    }
315}
316
317impl Bounded for Text {
318    fn bounds(&self) -> Rectangle {
319        self.bounds()
320    }
321
322    fn visible_bounds(&self) -> Option<Rectangle> {
323        self.visible_bounds()
324    }
325}
326
327/// A widget match produced by an accessibility selector.
328#[derive(Debug, Clone, PartialEq)]
329pub struct AccessibleMatch {
330    /// The widget [`Id`], if any.
331    pub id: Option<Id>,
332    /// The layout bounds of the matched widget.
333    pub bounds: Rectangle,
334    /// The visible bounds of the matched widget, in screen coordinates.
335    pub visible_bounds: Option<Rectangle>,
336    /// The accessible [`Role`] of the matched widget.
337    pub role: Role,
338    /// The accessible label of the matched widget, if any.
339    pub label: Option<String>,
340    /// The accessible description of the matched widget, if any.
341    pub description: Option<String>,
342}
343
344impl AccessibleMatch {
345    /// Returns the layout bounds of the [`AccessibleMatch`].
346    pub fn bounds(&self) -> Rectangle {
347        self.bounds
348    }
349
350    /// Returns the visible bounds of the [`AccessibleMatch`], in screen coordinates.
351    pub fn visible_bounds(&self) -> Option<Rectangle> {
352        self.visible_bounds
353    }
354}
355
356impl Bounded for AccessibleMatch {
357    fn bounds(&self) -> Rectangle {
358        self.bounds()
359    }
360
361    fn visible_bounds(&self) -> Option<Rectangle> {
362        self.visible_bounds()
363    }
364}