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(&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            cx.with_focus(focusable.focus_handle.clone(), |cx| f(cx))
156        } else {
157            f(cx)
158        }
159    }
160
161    fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
162        if let Some(focusable) = self.as_focusable() {
163            if focusable.focus_handle.contains_focused(cx) {
164                style.refine(&focusable.focus_in_style);
165            }
166
167            if focusable.focus_handle.within_focused(cx) {
168                style.refine(&focusable.in_focus_style);
169            }
170
171            if focusable.focus_handle.is_focused(cx) {
172                style.refine(&focusable.focus_style);
173            }
174        }
175    }
176
177    fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
178        if let Some(focusable) = self.as_focusable() {
179            let focus_handle = focusable.focus_handle.clone();
180            cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
181                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
182                    if !cx.default_prevented() {
183                        cx.focus(&focus_handle);
184                        cx.prevent_default();
185                    }
186                }
187            })
188        }
189    }
190}
191
192pub struct FocusEnabled<V: 'static + Send + Sync> {
193    pub focus_handle: FocusHandle,
194    pub focus_listeners: FocusListeners<V>,
195    pub focus_style: StyleRefinement,
196    pub focus_in_style: StyleRefinement,
197    pub in_focus_style: StyleRefinement,
198}
199
200impl<V> ElementFocus<V> for FocusEnabled<V>
201where
202    V: 'static + Send + Sync,
203{
204    fn as_focusable(&self) -> Option<&FocusEnabled<V>> {
205        Some(self)
206    }
207}
208
209impl<V> From<FocusHandle> for FocusEnabled<V>
210where
211    V: 'static + Send + Sync,
212{
213    fn from(value: FocusHandle) -> Self {
214        Self {
215            focus_handle: value,
216            focus_listeners: FocusListeners::default(),
217            focus_style: StyleRefinement::default(),
218            focus_in_style: StyleRefinement::default(),
219            in_focus_style: StyleRefinement::default(),
220        }
221    }
222}
223
224pub struct FocusDisabled;
225
226impl<V> ElementFocus<V> for FocusDisabled
227where
228    V: 'static + Send + Sync,
229{
230    fn as_focusable(&self) -> Option<&FocusEnabled<V>> {
231        None
232    }
233}