focusable.rs

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