1use std::{marker::PhantomData, sync::Arc};
6
7use elgato_streamdeck::AsyncStreamDeck;
8use generic_array::ArrayLength;
9use tokio::sync::{mpsc, RwLock};
10
11use crate::{
12 button::{render_button, RenderConfig},
13 navigation::NavigationEntry,
14 theme::Theme,
15};
16
17use super::{button::ButtonState, matrix::ButtonMatrix, View};
18
19pub struct DisplayManager<N: NavigationEntry<W, H, C>, W, H, C>
24where
25 W: ArrayLength,
26 H: ArrayLength,
27 C: Send + Clone + Sync + 'static,
28{
29 pub(crate) config: RenderConfig,
31 pub(crate) theme: Theme,
33 pub(crate) deck: Arc<AsyncStreamDeck>,
35 pub(crate) view: RwLock<Box<dyn View<W, H, C, N>>>,
37 pub(crate) _navigation: PhantomData<N>,
39 pub(crate) _width: PhantomData<W>,
41 pub(crate) _height: PhantomData<H>,
43 pub(crate) navigation_sender: Arc<mpsc::Sender<N>>,
45 pub(crate) context: C,
47 pub(crate) current_navigation: RwLock<N>,
49}
50
51impl<N: NavigationEntry<W, H, C>, W, H, C> DisplayManager<N, W, H, C>
52where
53 W: ArrayLength,
54 H: ArrayLength,
55 C: Send + Clone + Sync + 'static,
56{
57 pub async fn new(
62 deck: Arc<AsyncStreamDeck>,
63 config: RenderConfig,
64 theme: Theme,
65 context: C,
66 ) -> Result<(Self, mpsc::Receiver<N>), Box<dyn std::error::Error>> {
67 let (sender, receiver) = mpsc::channel(1);
68 let sender = Arc::new(sender);
69 Ok((
70 Self {
71 config,
72 theme,
73 deck,
74 view: RwLock::new(N::default().get_view(context.clone()).await?),
75 _navigation: PhantomData,
76 _width: PhantomData,
77 _height: PhantomData,
78 navigation_sender: sender.clone(),
79 context,
80 current_navigation: RwLock::new(N::default()),
81 },
82 receiver,
83 ))
84 }
85
86 pub async fn navigate_to(&self, navigation_entry: N) -> Result<(), Box<dyn std::error::Error>> {
91 let mut view = self.view.write().await;
92 let mut current_navigation = self.current_navigation.write().await;
93 *view = navigation_entry.get_view(self.context.clone()).await?;
94 *current_navigation = navigation_entry.clone();
95 Ok(())
96 }
97
98 pub async fn get_current_navigation(
102 &self,
103 ) -> Result<N, Box<dyn std::error::Error>> {
104 let current_navigation = self.current_navigation.read().await;
105 Ok(current_navigation.clone())
106 }
107
108 pub async fn render(&self) -> Result<(), Box<dyn std::error::Error>> {
112 let view = self.view.read().await;
113 let button_matrix = view.render().await?;
114 self.render_matrix(&button_matrix).await?;
115 Ok(())
116 }
117
118 pub async fn fetch_all(&self) -> Result<(), Box<dyn std::error::Error>> {
122 let view = self.view.read().await;
123 let result = view.fetch_all(&self.context).await;
124 if let Err(e) = result {
125 eprintln!("Error fetching view state: {}", e);
126 }
127 Ok(())
128 }
129
130 async fn render_matrix(
134 &self,
135 button_matrix: &ButtonMatrix<W, H>,
136 ) -> Result<(), Box<dyn std::error::Error>> {
137 for x in 0..W::to_usize() {
138 for y in 0..H::to_usize() {
139 let button = &button_matrix.buttons[y][x];
140 let button_index = (y * W::to_usize() + x) as u8;
141 let theme = button.theme.as_ref().unwrap_or(&self.theme);
142 let background_color = match button.state {
143 ButtonState::Default => theme.background,
144 ButtonState::Active => theme.active_background,
145 ButtonState::Inactive => theme.inactive_background,
146 ButtonState::Error => theme.error_background,
147 ButtonState::Pressed => theme.pressed_background,
148 };
149 let foreground_color = match button.state {
150 ButtonState::Default => theme.foreground_color,
151 ButtonState::Active => theme.active_foreground_color,
152 ButtonState::Inactive => theme.foreground_color,
153 ButtonState::Error => theme.foreground_color,
154 ButtonState::Pressed => theme.active_foreground_color,
155 };
156 let raw_button = match button.icon {
157 Some(icon) => crate::button::Button::IconWithText {
158 svg_data: icon,
159 text: button.text.to_string(),
160 background: background_color,
161 foreground: foreground_color,
162 },
163 None => crate::button::Button::Text {
164 text: button.text.to_string(),
165 background: background_color,
166 foreground: foreground_color,
167 },
168 };
169 let image = render_button(&raw_button, &self.config)?;
170 self.deck.set_button_image(button_index, image).await?;
171 }
172 self.deck.flush().await?;
173 }
174 Ok(())
175 }
176
177 pub async fn on_press(&self, button: u8) -> Result<(), Box<dyn std::error::Error>> {
182 let view = self.view.read().await;
183 let mut button_matrix = view.render().await?;
184 let button_index = button as usize;
185 let button = button_matrix
186 .get_button_by_index(button_index)
187 .ok_or("Button not found")?;
188 let new_button = button.updated_state(ButtonState::Pressed);
189 button_matrix.set_button_by_index(button_index, new_button)?;
190 self.render_matrix(&button_matrix).await?;
191 Ok(())
192 }
193
194 pub async fn on_release(&self, button: u8) -> Result<(), Box<dyn std::error::Error>> {
199 let view = self.view.read().await;
200 let result = view
201 .on_click(&self.context, button, self.navigation_sender.clone())
202 .await;
203 if let Err(e) = result {
204 eprintln!("Error handling button click: {}", e);
205 }
206 self.render().await?;
207 Ok(())
208 }
209}