1#![allow(clippy::question_mark)]
2#![allow(clippy::type_complexity)]
3use rat_event::{ConsumedEvent, HandleEvent, Outcome};
4use rat_salsa::{Control, SalsaContext};
5use ratatui::buffer::Buffer;
6use ratatui::layout::Rect;
7use std::any::{Any, TypeId, type_name};
8use std::cell::{Cell, RefCell};
9use std::cmp::max;
10use std::fmt::{Debug, Formatter};
11use std::mem;
12use std::rc::Rc;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
16#[must_use]
17#[non_exhaustive]
18pub enum DialogControl<Event> {
19 Continue,
22 Unchanged,
25 Changed,
28 Event(Event),
30 Close(Option<Event>),
32 ToFront,
34 Quit,
36}
37
38impl<Event> ConsumedEvent for DialogControl<Event> {
39 fn is_consumed(&self) -> bool {
40 !matches!(self, DialogControl::Continue)
41 }
42}
43
44impl<Event, T: Into<Outcome>> From<T> for DialogControl<Event> {
45 fn from(value: T) -> Self {
46 let r = value.into();
47 match r {
48 Outcome::Continue => DialogControl::Continue,
49 Outcome::Unchanged => DialogControl::Unchanged,
50 Outcome::Changed => DialogControl::Changed,
51 }
52 }
53}
54
55pub struct DialogStack<Event, Context, Error> {
67 core: Rc<DialogStackCore<Event, Context, Error>>,
68}
69
70struct DialogStackCore<Event, Context, Error> {
71 len: Cell<usize>,
72 render: RefCell<Vec<Box<dyn Fn(Rect, &mut Buffer, &mut dyn Any, &mut Context) + 'static>>>,
73 event: RefCell<
74 Vec<
75 Box<
76 dyn Fn(&Event, &mut dyn Any, &mut Context) -> Result<DialogControl<Event>, Error>
77 + 'static,
78 >,
79 >,
80 >,
81 type_id: RefCell<Vec<TypeId>>,
82 state: RefCell<Vec<Option<Box<dyn Any>>>>,
83}
84
85impl<Event, Context, Error> Clone for DialogStack<Event, Context, Error> {
86 fn clone(&self) -> Self {
87 Self {
88 core: self.core.clone(),
89 }
90 }
91}
92
93impl<Event, Context, Error> Default for DialogStack<Event, Context, Error> {
94 fn default() -> Self {
95 Self {
96 core: Rc::new(DialogStackCore {
97 len: Cell::new(0),
98 render: Default::default(),
99 event: Default::default(),
100 type_id: Default::default(),
101 state: Default::default(),
102 }),
103 }
104 }
105}
106
107impl<Event, Context, Error> Debug for DialogStack<Event, Context, Error> {
108 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
109 let state = self.core.state.borrow();
110 let is_proxy = state.iter().map(|v| v.is_none()).collect::<Vec<_>>();
111 let type_id = self.core.type_id.borrow();
112
113 f.debug_struct("DialogStackCore")
114 .field("len", &self.core.len.get())
115 .field("type_id", &type_id)
116 .field("is_proxy", &is_proxy)
117 .finish()
118 }
119}
120
121impl<Event, Context, Error> DialogStack<Event, Context, Error> {
122 pub fn render(self, area: Rect, buf: &mut Buffer, ctx: &mut Context) {
124 for n in 0..self.core.len.get() {
125 let Some(mut state) = self.core.state.borrow_mut()[n].take() else {
126 panic!("state is gone");
127 };
128 let render_fn = mem::replace(
129 &mut self.core.render.borrow_mut()[n],
130 Box::new(|_, _, _, _| {}),
131 );
132
133 render_fn(area, buf, state.as_mut(), ctx);
134
135 self.core.render.borrow_mut()[n] = render_fn;
136 self.core.state.borrow_mut()[n] = Some(state);
137 }
138 }
139}
140
141impl<Event, Context, Error> DialogStack<Event, Context, Error> {
142 pub fn new() -> Self {
143 Self::default()
144 }
145
146 pub fn push(
153 &self,
154 render: impl Fn(Rect, &mut Buffer, &mut dyn Any, &'_ mut Context) + 'static,
155 event: impl Fn(&Event, &mut dyn Any, &'_ mut Context) -> Result<DialogControl<Event>, Error>
156 + 'static,
157 state: impl Any,
158 ) {
159 self.core.len.update(|v| v + 1);
160 self.core.type_id.borrow_mut().push(state.type_id());
161 self.core.state.borrow_mut().push(Some(Box::new(state)));
162 self.core.event.borrow_mut().push(Box::new(event));
163 self.core.render.borrow_mut().push(Box::new(render));
164 }
165
166 pub fn pop(&self) -> Option<Box<dyn Any>> {
176 self.core.len.update(|v| v - 1);
177 self.core.type_id.borrow_mut().pop();
178 self.core.event.borrow_mut().pop();
179 self.core.render.borrow_mut().pop();
180 let Some(s) = self.core.state.borrow_mut().pop() else {
181 return None;
182 };
183 if s.is_none() {
184 panic!("state is gone");
185 }
186 s
187 }
188
189 pub fn remove(&self, n: usize) -> Box<dyn Any> {
197 for s in self.core.state.borrow().iter() {
198 if s.is_none() {
199 panic!("state is gone");
200 }
201 }
202
203 self.core.len.update(|v| v - 1);
204 self.core.type_id.borrow_mut().remove(n);
205 _ = self.core.event.borrow_mut().remove(n);
206 _ = self.core.render.borrow_mut().remove(n);
207
208 self.core
209 .state
210 .borrow_mut()
211 .remove(n)
212 .expect("state exists")
213 }
214
215 pub fn to_front(&self, n: usize) {
225 for s in self.core.state.borrow().iter() {
226 if s.is_none() {
227 panic!("state is gone");
228 }
229 }
230
231 let type_id = self.core.type_id.borrow_mut().remove(n);
232 let state = self.core.state.borrow_mut().remove(n);
233 let event = self.core.event.borrow_mut().remove(n);
234 let render = self.core.render.borrow_mut().remove(n);
235
236 self.core.type_id.borrow_mut().push(type_id);
237 self.core.state.borrow_mut().push(state);
238 self.core.event.borrow_mut().push(event);
239 self.core.render.borrow_mut().push(render);
240 }
241
242 pub fn is_empty(&self) -> bool {
244 self.core.type_id.borrow().is_empty()
245 }
246
247 pub fn len(&self) -> usize {
249 self.core.len.get()
250 }
251
252 pub fn state_is<S: 'static>(&self, n: usize) -> bool {
254 self.core.type_id.borrow()[n] == TypeId::of::<S>()
255 }
256
257 #[allow(clippy::manual_find)]
259 pub fn first<S: 'static>(&self) -> Option<usize> {
260 for n in (0..self.core.len.get()).rev() {
261 if self.core.type_id.borrow()[n] == TypeId::of::<S>() {
262 return Some(n);
263 }
264 }
265 None
266 }
267
268 pub fn find<S: 'static>(&self) -> Vec<usize> {
270 self.core
271 .type_id
272 .borrow()
273 .iter()
274 .enumerate()
275 .rev()
276 .filter_map(|(n, v)| {
277 if *v == TypeId::of::<S>() {
278 Some(n)
279 } else {
280 None
281 }
282 })
283 .collect()
284 }
285
286 pub fn apply<S: 'static, R>(&self, n: usize, f: impl Fn(&S) -> R) -> R {
295 let Some(state) = self.core.state.borrow_mut()[n].take() else {
296 panic!("state is gone");
297 };
298
299 let r = if let Some(state) = state.as_ref().downcast_ref::<S>() {
300 f(state)
301 } else {
302 self.core.state.borrow_mut()[n] = Some(state);
303 panic!("state is not {:?}", type_name::<S>());
304 };
305
306 self.core.state.borrow_mut()[n] = Some(state);
307 r
308 }
309
310 pub fn apply_mut<S: 'static, R>(&mut self, n: usize, f: impl Fn(&mut S) -> R) -> R {
319 let Some(mut state) = self.core.state.borrow_mut()[n].take() else {
320 panic!("state is gone");
321 };
322
323 let r = if let Some(state) = state.as_mut().downcast_mut::<S>() {
324 f(state)
325 } else {
326 self.core.state.borrow_mut()[n] = Some(state);
327 panic!("state is not {:?}", type_name::<S>());
328 };
329
330 self.core.state.borrow_mut()[n] = Some(state);
331 r
332 }
333}
334
335impl<Event, Context, Error> HandleEvent<Event, &mut Context, Result<Control<Event>, Error>>
341 for DialogStack<Event, Context, Error>
342where
343 Context: SalsaContext<Event, Error>,
344 Error: 'static,
345 Event: 'static,
346{
347 fn handle(&mut self, event: &Event, ctx: &mut Context) -> Result<Control<Event>, Error> {
348 let mut rr = Control::Continue;
349
350 for n in (0..self.core.len.get()).rev() {
351 let Some(mut state) = self.core.state.borrow_mut()[n].take() else {
352 panic!("state is gone");
353 };
354
355 let event_fn = mem::replace(
356 &mut self.core.event.borrow_mut()[n],
357 Box::new(|_, _, _| Ok(DialogControl::Continue)),
358 );
359
360 let r = event_fn(event, state.as_mut(), ctx);
361
362 self.core.event.borrow_mut()[n] = event_fn;
363 self.core.state.borrow_mut()[n] = Some(state);
364
365 match r {
366 Ok(r) => match r {
367 DialogControl::Close(event) => {
368 self.remove(n);
369 if let Some(event) = event {
370 ctx.queue_event(event);
371 }
372 rr = max(rr, Control::Changed);
373 }
374 DialogControl::Event(event) => {
375 ctx.queue_event(event);
376 rr = max(rr, Control::Continue);
377 }
378 DialogControl::ToFront => {
379 self.to_front(n);
380 rr = max(rr, Control::Changed);
381 }
382 DialogControl::Continue => {
383 rr = max(rr, Control::Continue);
384 }
385 DialogControl::Unchanged => {
386 rr = max(rr, Control::Unchanged);
387 }
388 DialogControl::Changed => {
389 rr = max(rr, Control::Changed);
390 }
391 DialogControl::Quit => {
392 rr = max(rr, Control::Quit);
393 }
394 },
395 Err(e) => return Err(e),
396 }
397 }
398
399 Ok(rr)
400 }
401}