Skip to main content

rat_focus/
flag.rs

1use crate::{FocusBuilder, HasFocus, Navigation, ratatui};
2use std::cell::{Cell, RefCell};
3use std::fmt::{Debug, Display, Formatter};
4use std::hash::{Hash, Hasher};
5use std::ptr;
6use std::rc::Rc;
7
8/// Holds the flags for the focus.
9///
10/// Add this to the widget state and implement [HasFocus] to
11/// manage your widgets focus state.
12///
13/// __Note__
14///
15/// This struct is intended to be cloned and uses a Rc internally
16/// to share the state.
17///
18/// __Note__
19///
20/// Equality and Hash and the id() function use the memory address of the
21/// FocusFlag behind the internal Rc<>.
22///
23/// __See__
24/// [HasFocus], [on_gained!](crate::on_gained!) and
25/// [on_lost!](crate::on_lost!).
26///
27#[derive(Clone, Default)]
28pub struct FocusFlag(Rc<FocusFlagCore>);
29
30/// Equality for FocusFlag means pointer equality of the underlying
31/// Rc using Rc::ptr_eq.
32impl PartialEq for FocusFlag {
33    fn eq(&self, other: &Self) -> bool {
34        Rc::ptr_eq(&self.0, &other.0)
35    }
36}
37
38impl Eq for FocusFlag {}
39
40impl Hash for FocusFlag {
41    fn hash<H: Hasher>(&self, state: &mut H) {
42        ptr::hash(Rc::as_ptr(&self.0), state);
43    }
44}
45
46impl Display for FocusFlag {
47    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48        let name = self.0.name.borrow();
49        if let Some(name) = &*name {
50            write!(f, "|{}|", name)
51        } else {
52            write!(f, "")
53        }
54    }
55}
56
57impl HasFocus for FocusFlag {
58    fn build(&self, builder: &mut FocusBuilder) {
59        builder.leaf_widget(self);
60    }
61
62    fn focus(&self) -> FocusFlag {
63        self.clone()
64    }
65
66    fn area(&self) -> ratatui::layout::Rect {
67        ratatui::layout::Rect::default()
68    }
69
70    fn area_z(&self) -> u16 {
71        0
72    }
73
74    fn navigable(&self) -> Navigation {
75        Navigation::Regular
76    }
77}
78
79struct FocusFlagCore {
80    /// Field name for debugging purposes.
81    name: RefCell<Option<Box<str>>>,
82    /// Does this widget have the focus.
83    /// Or, if the flag is used for a container, does any of
84    /// widget inside the container have the focus.
85    ///
86    /// This flag is set by [Focus::handle].
87    focus: Cell<bool>,
88    /// This widget just gained the focus. This flag is set by [Focus::handle]
89    /// if there is a focus transfer, and will be reset by the next
90    /// call to [Focus::handle].
91    ///
92    /// See [on_gained!](crate::on_gained!)
93    gained: Cell<bool>,
94    /// Callback for set of gained.
95    ///
96    /// A widget can set this callback and will be notified
97    /// by Focus whenever it gains the focus.
98    ///
99    /// It's a bit crude, as you have set up any widget-state
100    /// you want to change as shared state with the callback-closure.
101    /// But it's still preferable to relying on the fact that
102    /// the `handle` event for a widget will be called while
103    /// the gained flag is still set.
104    on_gained: RefCell<Option<Box<dyn Fn()>>>,
105    /// This widget just lost the focus. This flag is set by [Focus::handle]
106    /// if there is a focus transfer, and will be reset by the next
107    /// call to [Focus::handle].
108    ///
109    /// See [on_lost!](crate::on_lost!)
110    lost: Cell<bool>,
111    /// Callback for set of lost.
112    ///
113    /// A widget can set this callback and will be notified
114    /// by Focus whenever it looses the focus.
115    ///
116    /// It's a bit crude, as you have set up any widget-state
117    /// you want to change as shared state with the callback-closure.
118    /// But it's still preferable to relying on the fact that
119    /// the `handle` event for a widget will be called while
120    /// the lost flag is still set.
121    on_lost: RefCell<Option<Box<dyn Fn()>>>,
122    /// Hit-tests all widgets for the given position.
123    /// Obscured is set to false if the widget-area is hit
124    /// and there are no containers/widgets with a *higher*
125    /// z-index.
126    ///
127    /// This flag is set by [Focus::handle] for every mouse-event.
128    ///
129    /// The purpose is to disable mouse-event handling where a
130    /// popup is covering some widget.
131    obscured: Cell<bool>,
132}
133
134impl Debug for FocusFlag {
135    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
136        f.debug_struct("FocusFlag")
137            .field("name", &self.0.name)
138            .field("focus", &self.0.focus.get())
139            .field("widget_id", &self.widget_id())
140            .field("gained", &self.0.gained.get())
141            .field("on_gained", &self.0.on_gained.borrow().is_some())
142            .field("lost", &self.0.lost.get())
143            .field("on_lost", &self.0.on_lost.borrow().is_some())
144            .finish()
145    }
146}
147
148impl FocusFlag {
149    /// Create a default flag.
150    pub fn new() -> Self {
151        Self::default()
152    }
153
154    /// Create a deep copy of the FocusFlag.
155    ///
156    /// Caution
157    ///
158    /// It will lose the on_gained() and on_lost() callbacks.
159    /// Those can not be replicated/cloned as they
160    /// might hold some Rc's to somewhere.
161    /// You need to set them anew.
162    pub fn new_instance(&self) -> Self {
163        Self(Rc::new(self.0.new_instance()))
164    }
165
166    /// Return an identity value.
167    ///
168    /// This uses the memory address of the backing Rc so it will
169    /// be unique during the runtime but will be different for each
170    /// run.
171    pub fn widget_id(&self) -> usize {
172        Rc::as_ptr(&self.0) as usize
173    }
174
175    /// Create a named flag.
176    ///
177    /// The name is only used for debugging.
178    #[deprecated(
179        since = "1.4.0",
180        note = "to dangerous, use FocusFlag::new().with_name(..) or FocusFlag::fake_clone(..) for a clone."
181    )]
182    pub fn named(name: impl AsRef<str>) -> Self {
183        Self(Rc::new(FocusFlagCore::default().named(name.as_ref())))
184    }
185
186    /// Set a name for a FocusFlag.
187    pub fn with_name(self, name: &str) -> Self {
188        self.set_name(name);
189        self
190    }
191
192    /// Has the focus.
193    #[inline]
194    pub fn get(&self) -> bool {
195        self.0.focus.get()
196    }
197
198    /// Set the focus.
199    #[inline]
200    pub fn set(&self, focus: bool) {
201        self.0.focus.set(focus);
202    }
203
204    /// Get the field-name.
205    #[inline]
206    pub fn name(&self) -> Box<str> {
207        self.0.name.borrow().clone().unwrap_or_default()
208    }
209
210    /// Set the field-name.
211    #[inline]
212    pub fn set_name(&self, name: &str) {
213        *self.0.name.borrow_mut() = Some(Box::from(name))
214    }
215
216    /// Just lost the focus.
217    #[inline]
218    pub fn lost(&self) -> bool {
219        self.0.lost.get()
220    }
221
222    /// Set the lost-flag.
223    ///
224    /// This doesn't call the on_lost callback.
225    #[inline]
226    pub fn set_lost(&self, lost: bool) {
227        self.0.lost.set(lost);
228    }
229
230    /// Set an on_lost callback. The intention is that widget-creators
231    /// can use this to get guaranteed notifications on focus-changes.
232    ///
233    /// This is not an api for widget *users.
234    #[inline]
235    pub fn on_lost(&self, on_lost: impl Fn() + 'static) {
236        *(self.0.on_lost.borrow_mut()) = Some(Box::new(on_lost));
237    }
238
239    /// Notify an on_lost() tragedy.
240    #[inline]
241    pub fn call_on_lost(&self) -> bool {
242        let borrow = self.0.on_lost.borrow();
243        if let Some(f) = borrow.as_ref() {
244            f();
245            true
246        } else {
247            false
248        }
249    }
250
251    /// Just gained the focus.
252    #[inline]
253    pub fn gained(&self) -> bool {
254        self.0.gained.get()
255    }
256
257    /// Set the gained-flag.
258    ///
259    /// This doesn't call the on_gained callback.
260    #[inline]
261    pub fn set_gained(&self, gained: bool) {
262        self.0.gained.set(gained);
263    }
264
265    /// Set an on_gained callback. The intention is that widget-creators
266    /// can use this to get guaranteed notifications on focus-changes.
267    ///
268    /// This is not an api for widget *users.
269    #[inline]
270    pub fn on_gained(&self, on_gained: impl Fn() + 'static) {
271        *(self.0.on_gained.borrow_mut()) = Some(Box::new(on_gained));
272    }
273
274    /// Notify an on_gained() comedy.
275    #[inline]
276    pub fn call_on_gained(&self) -> bool {
277        let borrow = self.0.on_gained.borrow();
278        if let Some(f) = borrow.as_ref() {
279            f();
280            true
281        } else {
282            false
283        }
284    }
285
286    /// Set the obscured flag.
287    #[inline]
288    pub fn set_obscured(&self, mf: bool) {
289        self.0.obscured.set(mf);
290    }
291
292    /// Is the widget area hit by the current mouse-position
293    /// and is there no other widget/container with a
294    /// higher z-index. Then this widget is *not* obscured.
295    ///
296    /// This flag is set when handling a mouse-event.
297    ///
298    /// See [HasFocus::is_obscured()]
299    #[inline]
300    pub fn obscured(&self) -> bool {
301        self.0.obscured.get()
302    }
303
304    /// Reset all flags to false.
305    #[inline]
306    pub fn clear(&self) {
307        self.0.focus.set(false);
308        self.0.lost.set(false);
309        self.0.gained.set(false);
310        self.0.obscured.set(false);
311    }
312}
313
314impl Default for FocusFlagCore {
315    fn default() -> Self {
316        Self {
317            name: RefCell::new(None),
318            focus: Cell::new(false),
319            gained: Cell::new(false),
320            on_gained: RefCell::new(None),
321            lost: Cell::new(false),
322            on_lost: RefCell::new(None),
323            obscured: Cell::new(false),
324        }
325    }
326}
327
328impl FocusFlagCore {
329    #[inline(always)]
330    pub(crate) fn named(self, name: &str) -> Self {
331        *self.name.borrow_mut() = Some(Box::from(name));
332        self
333    }
334
335    /// Create a deep clone.
336    pub(crate) fn new_instance(&self) -> Self {
337        Self {
338            name: self.name.clone(),
339            focus: Cell::new(self.focus.get()),
340            gained: Cell::new(self.gained.get()),
341            on_gained: RefCell::new(None),
342            lost: Cell::new(self.lost.get()),
343            on_lost: RefCell::new(None),
344            obscured: Cell::new(self.obscured.get()),
345        }
346    }
347}