matchmaker/render/
state_effects.rs

1use super::State;
2use crate::{
3    MAX_EFFECTS, SSS, Selection,
4    message::Event,
5    ui::{PickerUI, PreviewUI, UI},
6};
7
8use arrayvec::ArrayVec;
9use ratatui::text::{Span, Text};
10
11#[derive(Debug, Clone)]
12pub enum Effect {
13    ClearPreviewSet,
14    Header(Text<'static>),
15    Footer(Text<'static>),
16    ClearFooter,
17    ClearHeader,
18    ClearSelections,
19    RevalidateSelectons,
20    /// Reload the nucleo matcher
21    /// Note that the reload interrupt handler is NOT triggered if this is produced from a dynamic handler
22    Reload,
23
24    Prompt(Span<'static>),
25    /// Set the input ui contents and cursor
26    Input((String, u16)),
27    RestoreInputPrefix,
28
29    DisableCursor(bool),
30    SetIndex(u32),
31    TrySync,
32}
33#[derive(Debug, Default)]
34pub struct Effects(ArrayVec<Effect, MAX_EFFECTS>);
35
36#[macro_export]
37macro_rules! efx {
38    ( $( $x:expr ),* $(,)? ) => {
39        {
40            [$($x),*].into_iter().collect::<$crate::render::Effects>()
41        }
42    };
43}
44pub use crate::acs;
45
46impl<S: Selection> State<S> {
47    // note: apparently its important that this is a method on state to satisfy borrow checker
48    pub fn apply_effects<T: SSS>(
49        &mut self,
50        effects: Effects,
51        _ui: &mut UI,
52        picker_ui: &mut PickerUI<T, S>,
53        _preview_ui: &mut Option<PreviewUI>,
54    ) {
55        if !effects.is_empty() {
56            log::debug!("{effects:?}");
57        }
58        for effect in effects {
59            match effect {
60                // ----- preview -------
61                Effect::ClearPreviewSet => {
62                    self.preview_set = None;
63                }
64
65                // ----- displays -------
66                Effect::Header(text) => {
67                    picker_ui.header.set(text);
68                }
69                Effect::Footer(text) => {
70                    picker_ui.footer.set(text);
71                }
72                Effect::ClearHeader => {
73                    picker_ui.header.show = false;
74                }
75                Effect::ClearFooter => {
76                    picker_ui.footer.show = false;
77                }
78
79                // ----- input -------
80                Effect::Input((input, cursor)) => {
81                    picker_ui.input.set(input, cursor);
82                }
83                Effect::Prompt(prompt) => {
84                    picker_ui.input.prompt = prompt;
85                }
86                Effect::RestoreInputPrefix => {
87                    picker_ui.input.prompt = Span::from(picker_ui.input.config.prompt.clone());
88                }
89
90                // ----- results -------
91                Effect::DisableCursor(disabled) => {
92                    picker_ui.results.cursor_disabled = disabled;
93                }
94                Effect::SetIndex(index) => {
95                    log::info!("{:?}", picker_ui.results);
96                    picker_ui.results.cursor_jump(index);
97                }
98
99                // -------- selections ---------
100                Effect::ClearSelections => {
101                    picker_ui.selections.clear();
102                }
103                Effect::RevalidateSelectons => {
104                    picker_ui.selections.revalidate();
105                }
106
107                // ---------- misc -------------
108                // this may not be the best place for these? We're trying to trigger a handler
109                Effect::Reload => {
110                    // the reload handler is not triggered when a handler produces this effect
111                    picker_ui.worker.restart(false);
112                }
113                Effect::TrySync => {
114                    if !picker_ui.results.status.running {
115                        self.insert(Event::Synced);
116                    }
117                }
118            }
119        }
120    }
121}
122
123// ----------------------------------------------------
124
125impl PartialEq for Effect {
126    fn eq(&self, other: &Self) -> bool {
127        std::mem::discriminant(self) == std::mem::discriminant(other)
128    }
129}
130
131impl Eq for Effect {}
132
133impl Effects {
134    pub fn new() -> Self {
135        Self(ArrayVec::new())
136    }
137
138    /// Insert only if not already present
139    pub fn insert(&mut self, effect: Effect) -> bool {
140        if self.0.contains(&effect) {
141            false
142        } else {
143            self.0.push(effect);
144            true
145        }
146    }
147
148    pub fn len(&self) -> usize {
149        self.0.len()
150    }
151
152    pub fn is_empty(&self) -> bool {
153        self.0.is_empty()
154    }
155
156    pub fn append(&mut self, other: Self) {
157        for effect in other {
158            self.insert(effect);
159        }
160    }
161}
162
163impl IntoIterator for Effects {
164    type Item = Effect;
165    type IntoIter = arrayvec::IntoIter<Effect, 12>;
166
167    fn into_iter(self) -> Self::IntoIter {
168        self.0.into_iter()
169    }
170}
171
172impl FromIterator<Effect> for Effects {
173    fn from_iter<I: IntoIterator<Item = Effect>>(iter: I) -> Self {
174        let mut effects = Effects::new();
175        for e in iter {
176            effects.insert(e);
177        }
178        effects
179    }
180}