focusable.rs

  1use crate::{
  2    Bounds, DispatchPhase, Element, FocusEvent, FocusHandle, MouseDownEvent, Pixels, Style,
  3    StyleRefinement, ViewContext, WindowContext,
  4};
  5use refineable::Refineable;
  6use smallvec::SmallVec;
  7
  8pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
  9
 10pub type FocusListener<V> =
 11    Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
 12
 13pub trait Focusable<V: 'static>: Element<V> {
 14    fn focus_listeners(&mut self) -> &mut FocusListeners<V>;
 15    fn set_focus_style(&mut self, style: StyleRefinement);
 16    fn set_focus_in_style(&mut self, style: StyleRefinement);
 17    fn set_in_focus_style(&mut self, style: StyleRefinement);
 18
 19    fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 20    where
 21        Self: Sized,
 22    {
 23        self.set_focus_style(f(StyleRefinement::default()));
 24        self
 25    }
 26
 27    fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 28    where
 29        Self: Sized,
 30    {
 31        self.set_focus_in_style(f(StyleRefinement::default()));
 32        self
 33    }
 34
 35    fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
 36    where
 37        Self: Sized,
 38    {
 39        self.set_in_focus_style(f(StyleRefinement::default()));
 40        self
 41    }
 42
 43    fn on_focus(
 44        mut self,
 45        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
 46    ) -> Self
 47    where
 48        Self: Sized,
 49    {
 50        self.focus_listeners()
 51            .push(Box::new(move |view, focus_handle, event, cx| {
 52                if event.focused.as_ref() == Some(focus_handle) {
 53                    listener(view, event, cx)
 54                }
 55            }));
 56        self
 57    }
 58
 59    fn on_blur(
 60        mut self,
 61        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
 62    ) -> Self
 63    where
 64        Self: Sized,
 65    {
 66        self.focus_listeners()
 67            .push(Box::new(move |view, focus_handle, event, cx| {
 68                if event.blurred.as_ref() == Some(focus_handle) {
 69                    listener(view, event, cx)
 70                }
 71            }));
 72        self
 73    }
 74
 75    fn on_focus_in(
 76        mut self,
 77        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
 78    ) -> Self
 79    where
 80        Self: Sized,
 81    {
 82        self.focus_listeners()
 83            .push(Box::new(move |view, focus_handle, event, cx| {
 84                let descendant_blurred = event
 85                    .blurred
 86                    .as_ref()
 87                    .map_or(false, |blurred| focus_handle.contains(blurred, cx));
 88                let descendant_focused = event
 89                    .focused
 90                    .as_ref()
 91                    .map_or(false, |focused| focus_handle.contains(focused, cx));
 92
 93                if !descendant_blurred && descendant_focused {
 94                    listener(view, event, cx)
 95                }
 96            }));
 97        self
 98    }
 99
100    fn on_focus_out(
101        mut self,
102        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
103    ) -> Self
104    where
105        Self: Sized,
106    {
107        self.focus_listeners()
108            .push(Box::new(move |view, focus_handle, event, cx| {
109                let descendant_blurred = event
110                    .blurred
111                    .as_ref()
112                    .map_or(false, |blurred| focus_handle.contains(blurred, cx));
113                let descendant_focused = event
114                    .focused
115                    .as_ref()
116                    .map_or(false, |focused| focus_handle.contains(focused, cx));
117                if descendant_blurred && !descendant_focused {
118                    listener(view, event, cx)
119                }
120            }));
121        self
122    }
123}
124
125pub trait ElementFocus<V: 'static>: 'static {
126    fn as_focusable(&self) -> Option<&FocusEnabled<V>>;
127    fn as_focusable_mut(&mut self) -> Option<&mut FocusEnabled<V>>;
128
129    fn initialize<R>(
130        &mut self,
131        focus_handle: Option<FocusHandle>,
132        cx: &mut ViewContext<V>,
133        f: impl FnOnce(Option<FocusHandle>, &mut ViewContext<V>) -> R,
134    ) -> R {
135        if let Some(focusable) = self.as_focusable_mut() {
136            let focus_handle = focusable
137                .focus_handle
138                .get_or_insert_with(|| focus_handle.unwrap_or_else(|| cx.focus_handle()))
139                .clone();
140            for listener in focusable.focus_listeners.drain(..) {
141                let focus_handle = focus_handle.clone();
142                cx.on_focus_changed(move |view, event, cx| {
143                    listener(view, &focus_handle, event, cx)
144                });
145            }
146            cx.with_focus(focus_handle.clone(), |cx| f(Some(focus_handle), cx))
147        } else {
148            f(None, cx)
149        }
150    }
151
152    fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
153        if let Some(focusable) = self.as_focusable() {
154            let focus_handle = focusable
155                .focus_handle
156                .as_ref()
157                .expect("must call initialize before refine_style");
158            if focus_handle.contains_focused(cx) {
159                style.refine(&focusable.focus_in_style);
160            }
161
162            if focus_handle.within_focused(cx) {
163                style.refine(&focusable.in_focus_style);
164            }
165
166            if focus_handle.is_focused(cx) {
167                style.refine(&focusable.focus_style);
168            }
169        }
170    }
171
172    fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
173        if let Some(focusable) = self.as_focusable() {
174            let focus_handle = focusable
175                .focus_handle
176                .clone()
177                .expect("must call initialize before paint");
178            cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
179                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
180                    if !cx.default_prevented() {
181                        cx.focus(&focus_handle);
182                        cx.prevent_default();
183                    }
184                }
185            })
186        }
187    }
188}
189
190pub struct FocusEnabled<V> {
191    pub focus_handle: Option<FocusHandle>,
192    pub focus_listeners: FocusListeners<V>,
193    pub focus_style: StyleRefinement,
194    pub focus_in_style: StyleRefinement,
195    pub in_focus_style: StyleRefinement,
196}
197
198impl<V> FocusEnabled<V> {
199    pub fn new() -> Self {
200        Self {
201            focus_handle: None,
202            focus_listeners: FocusListeners::default(),
203            focus_style: StyleRefinement::default(),
204            focus_in_style: StyleRefinement::default(),
205            in_focus_style: StyleRefinement::default(),
206        }
207    }
208
209    pub fn tracked(handle: &FocusHandle) -> Self {
210        Self {
211            focus_handle: Some(handle.clone()),
212            focus_listeners: FocusListeners::default(),
213            focus_style: StyleRefinement::default(),
214            focus_in_style: StyleRefinement::default(),
215            in_focus_style: StyleRefinement::default(),
216        }
217    }
218}
219
220impl<V: 'static> ElementFocus<V> for FocusEnabled<V> {
221    fn as_focusable(&self) -> Option<&FocusEnabled<V>> {
222        Some(self)
223    }
224
225    fn as_focusable_mut(&mut self) -> Option<&mut FocusEnabled<V>> {
226        Some(self)
227    }
228}
229
230impl<V> From<FocusHandle> for FocusEnabled<V> {
231    fn from(value: FocusHandle) -> Self {
232        Self {
233            focus_handle: Some(value),
234            focus_listeners: FocusListeners::default(),
235            focus_style: StyleRefinement::default(),
236            focus_in_style: StyleRefinement::default(),
237            in_focus_style: StyleRefinement::default(),
238        }
239    }
240}
241
242pub struct FocusDisabled;
243
244impl<V: 'static> ElementFocus<V> for FocusDisabled {
245    fn as_focusable(&self) -> Option<&FocusEnabled<V>> {
246        None
247    }
248
249    fn as_focusable_mut(&mut self) -> Option<&mut FocusEnabled<V>> {
250        None
251    }
252}