iced_core/widget/operation/
focusable.rs
1use crate::id::IdEq;
3use crate::widget::operation::{self, Operation, Outcome};
4use crate::widget::Id;
5use crate::Rectangle;
6
7pub trait Focusable {
9 fn is_focused(&self) -> bool;
11
12 fn focus(&mut self);
14
15 fn unfocus(&mut self);
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub struct Count {
22 pub focused: Option<usize>,
24
25 pub total: usize,
27}
28
29pub fn focus<T>(target: Id) -> impl Operation<T> {
31 struct Focus {
32 target: Id,
33 }
34
35 impl<T> Operation<T> for Focus {
36 fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
37 match id {
38 Some(id) if IdEq::eq(&id.0, &self.target.0) => {
39 state.focus();
40 }
41 _ => {
42 state.unfocus();
43 }
44 }
45 }
46
47 fn container(
48 &mut self,
49 _id: Option<&Id>,
50 _bounds: Rectangle,
51 operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
52 ) {
53 operate_on_children(self);
54 }
55 }
56
57 Focus { target }
58}
59
60pub fn count() -> impl Operation<Count> {
63 struct CountFocusable {
64 count: Count,
65 }
66
67 impl Operation<Count> for CountFocusable {
68 fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) {
69 if state.is_focused() {
70 self.count.focused = Some(self.count.total);
71 }
72
73 self.count.total += 1;
74 }
75
76 fn container(
77 &mut self,
78 _id: Option<&Id>,
79 _bounds: Rectangle,
80 operate_on_children: &mut dyn FnMut(&mut dyn Operation<Count>),
81 ) {
82 operate_on_children(self);
83 }
84
85 fn finish(&self) -> Outcome<Count> {
86 Outcome::Some(self.count)
87 }
88 }
89
90 CountFocusable {
91 count: Count::default(),
92 }
93}
94
95pub fn focus_previous<T>() -> impl Operation<T>
99where
100 T: Send + 'static,
101{
102 struct FocusPrevious {
103 count: Count,
104 current: usize,
105 }
106
107 impl<T> Operation<T> for FocusPrevious {
108 fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) {
109 if self.count.total == 0 {
110 return;
111 }
112
113 match self.count.focused {
114 None if self.current == self.count.total - 1 => state.focus(),
115 Some(0) if self.current == 0 => state.unfocus(),
116 Some(0) => {}
117 Some(focused) if focused == self.current => state.unfocus(),
118 Some(focused) if focused - 1 == self.current => state.focus(),
119 _ => {}
120 }
121
122 self.current += 1;
123 }
124
125 fn container(
126 &mut self,
127 _id: Option<&Id>,
128 _bounds: Rectangle,
129 operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
130 ) {
131 operate_on_children(self);
132 }
133 }
134
135 operation::then(count(), |count| FocusPrevious { count, current: 0 })
136}
137
138pub fn focus_next<T>() -> impl Operation<T>
142where
143 T: Send + 'static,
144{
145 struct FocusNext {
146 count: Count,
147 current: usize,
148 }
149
150 impl<T> Operation<T> for FocusNext {
151 fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) {
152 match self.count.focused {
153 None if self.current == 0 => state.focus(),
154 Some(focused) if focused == self.current => state.unfocus(),
155 Some(focused) if focused + 1 == self.current => state.focus(),
156 _ => {}
157 }
158
159 self.current += 1;
160 }
161
162 fn container(
163 &mut self,
164 _id: Option<&Id>,
165 _bounds: Rectangle,
166 operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
167 ) {
168 operate_on_children(self);
169 }
170 }
171
172 operation::then(count(), |count| FocusNext { count, current: 0 })
173}
174
175pub fn find_focused() -> impl Operation<Id> {
178 struct FindFocused {
179 focused: Option<Id>,
180 }
181
182 impl Operation<Id> for FindFocused {
183 fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
184 if state.is_focused() && id.is_some() {
185 self.focused = id.cloned();
186 }
187 }
188
189 fn container(
190 &mut self,
191 _id: Option<&Id>,
192 _bounds: Rectangle,
193 operate_on_children: &mut dyn FnMut(&mut dyn Operation<Id>),
194 ) {
195 operate_on_children(self);
196 }
197
198 fn finish(&self) -> Outcome<Id> {
199 if let Some(id) = &self.focused {
200 Outcome::Some(id.clone())
201 } else {
202 Outcome::None
203 }
204 }
205 }
206
207 FindFocused { focused: None }
208}