promkit_core/
render.rs

1use std::sync::Arc;
2
3use crossbeam_skiplist::SkipMap;
4use tokio::sync::Mutex;
5
6use crate::{Pane, terminal::Terminal};
7
8/// SharedRenderer is a type alias for an Arc-wrapped Renderer, allowing for shared ownership and concurrency.
9pub type SharedRenderer<K> = Arc<Renderer<K>>;
10
11/// Renderer is responsible for managing and rendering multiple panes in a terminal.
12pub struct Renderer<K: Ord + Send + 'static> {
13    terminal: Mutex<Terminal>,
14    panes: SkipMap<K, Pane>,
15}
16
17impl<K: Ord + Send + 'static> Renderer<K> {
18    pub fn try_new() -> anyhow::Result<Self> {
19        Ok(Self {
20            terminal: Mutex::new(Terminal {
21                position: crossterm::cursor::position()?,
22            }),
23            panes: SkipMap::new(),
24        })
25    }
26
27    pub async fn try_new_with_panes<I>(init_panes: I, draw: bool) -> anyhow::Result<Self>
28    where
29        I: IntoIterator<Item = (K, Pane)>,
30    {
31        let renderer = Self::try_new()?;
32        renderer.update(init_panes);
33        if draw {
34            renderer.render().await?;
35        }
36        Ok(renderer)
37    }
38
39    pub fn update<I>(&self, items: I) -> &Self
40    where
41        I: IntoIterator<Item = (K, Pane)>,
42    {
43        items.into_iter().for_each(|(index, pane)| {
44            self.panes.insert(index, pane);
45        });
46        self
47    }
48
49    pub fn remove<I>(&self, items: I) -> &Self
50    where
51        I: IntoIterator<Item = K>,
52    {
53        items.into_iter().for_each(|index| {
54            self.panes.remove(&index);
55        });
56        self
57    }
58
59    // TODO: Implement diff rendering
60    pub async fn render(&self) -> anyhow::Result<()> {
61        let panes: Vec<Pane> = self
62            .panes
63            .iter()
64            .map(|entry| entry.value().clone())
65            .collect();
66        let mut terminal = self.terminal.lock().await;
67        terminal.draw(&panes)
68    }
69}