Skip to main content

ratatui_reactive/core/
focus_manager.rs

1use crate::{Component, Render};
2use ratatui::buffer::Buffer;
3use ratatui::layout::Rect;
4use std::marker::PhantomData;
5use sycamore_reactive::{
6    ReadSignal, Signal, create_child_scope, create_signal, on_cleanup, provide_context,
7};
8
9#[derive(Debug)]
10pub struct FocusManager<R> {
11    focus: Signal<usize>,
12    marker: PhantomData<*mut R>,
13}
14
15impl<R> Clone for FocusManager<R> {
16    #[inline]
17    fn clone(&self) -> Self {
18        *self
19    }
20}
21
22impl<R> Copy for FocusManager<R> {}
23
24#[derive(Debug, Clone, Copy)]
25pub struct Focusable {
26    route: usize,
27    focus: ReadSignal<usize>,
28}
29
30#[inline]
31#[cfg_attr(debug_assertions, track_caller)]
32pub fn provide_focus_manager<R: 'static + Into<usize>>(initial: R) -> FocusManager<R> {
33    let focus = create_signal(initial.into());
34    let focus_manager = FocusManager {
35        focus,
36        marker: PhantomData,
37    };
38    provide_context(focus_manager);
39    focus_manager
40}
41
42impl<F: Into<usize>> FocusManager<F> {
43    #[inline]
44    #[cfg_attr(debug_assertions, track_caller)]
45    pub fn on<R: Render + 'static, C: Component<R>>(self, route: F, component: C) -> impl Render {
46        let scope = create_child_scope(|| {
47            provide_context(Focusable {
48                route: route.into(),
49                focus: *self.focus,
50            })
51        });
52        on_cleanup(move || scope.dispose());
53        let wrapped = scope.run_in(move || component.create());
54        move |area: Rect, buf: &mut Buffer| {
55            self.focus.track();
56            wrapped.render(area, buf);
57        }
58    }
59
60    #[inline]
61    #[cfg_attr(debug_assertions, track_caller)]
62    pub fn focus(&self, route: F) {
63        self.focus.set(route.into());
64    }
65}
66
67impl Focusable {
68    #[inline]
69    #[cfg_attr(debug_assertions, track_caller)]
70    pub fn is_focused(&self) -> bool {
71        self.focus.with_untracked(|r| r == &self.route)
72    }
73}