ratatui_kit/hooks/
use_insert_before.rs1use std::{
2 collections::VecDeque,
3 sync::{Arc, Mutex},
4 task::{Poll, Waker},
5};
6
7use ratatui::{buffer::Buffer, widgets::Widget};
8
9use crate::{Hook, Hooks, Terminal};
10
11mod private {
12 pub trait Sealed {}
13 impl Sealed for crate::hooks::Hooks<'_, '_> {}
14}
15
16type FnBox = Box<dyn FnOnce(&mut Buffer)>;
17
18#[derive(Clone, Default)]
19pub struct InsertBeforeHandler {
20 queue: Arc<Mutex<VecDeque<(u16, FnBox)>>>,
21 waker: Arc<Mutex<Option<Waker>>>,
22}
23
24impl Hook for InsertBeforeHandler {
25 fn poll_change(&mut self, cx: &mut std::task::Context) -> std::task::Poll<()> {
26 let mut waker = self.waker.lock().unwrap();
27 let mut queue = self.queue.lock().unwrap();
28 if queue.is_empty() {
29 waker.replace(cx.waker().clone());
30 Poll::Pending
31 } else {
32 Poll::Ready(())
33 }
34 }
35
36 fn post_component_update(&mut self, updater: &mut crate::ComponentUpdater) {
37 let mut queue = self.queue.lock().unwrap();
38 for (height, callback) in queue.drain(..) {
39 updater.terminal().insert_before(height, callback);
40 }
41 }
42}
43
44impl InsertBeforeHandler {
45 pub fn insert_before<F>(&self, height: u16, callback: F) -> &Self
46 where
47 F: FnOnce(&mut Buffer) + 'static,
48 {
49 let mut queue = self.queue.lock().unwrap();
50 queue.push_back((height, Box::new(callback)));
51 self
52 }
53
54 pub fn render_before<T: Widget + 'static>(&self, widget: T, height: u16) -> &Self {
55 self.insert_before(height, move |buf| {
56 widget.render(buf.area, buf);
57 });
58 self
59 }
60
61 pub fn finish(&self) {
62 if let Some(waker) = self.waker.lock().unwrap().take() {
63 waker.wake();
64 }
65 }
66}
67
68pub trait UseInsertBefore: private::Sealed {
69 fn use_insert_before(&mut self) -> InsertBeforeHandler;
71}
72
73impl UseInsertBefore for Hooks<'_, '_> {
74 fn use_insert_before(&mut self) -> InsertBeforeHandler {
75 self.use_hook(InsertBeforeHandler::default).clone()
76 }
77}