1pub mod display;
6pub mod overlays;
7
8use std::sync::{Arc, RwLock};
9
10use dais_core::state::PresentationState;
11use dais_document::cache::PageCache;
12use dais_document::page::RenderSize;
13use dais_document::typst_renderer::TextBoxRenderCache;
14
15use self::display::AudienceDisplay;
16use crate::widgets::TextBoxTextureCache;
17
18pub struct AudienceWindow {
20 display: AudienceDisplay,
21 tb_cache: TextBoxRenderCache,
22 tb_texture_cache: TextBoxTextureCache,
23}
24
25impl AudienceWindow {
26 pub fn new() -> Self {
27 Self {
28 display: AudienceDisplay::new(),
29 tb_cache: TextBoxRenderCache::new(),
30 tb_texture_cache: TextBoxTextureCache::default(),
31 }
32 }
33
34 pub fn show(
36 &mut self,
37 ctx: &egui::Context,
38 shared_state: &Arc<RwLock<PresentationState>>,
39 cache: &mut PageCache,
40 render_size: RenderSize,
41 ) {
42 let state = shared_state.read().map_or_else(
43 |e| {
44 tracing::error!("Failed to read state for audience window: {e}");
45 PresentationState::new(0, Vec::new())
46 },
47 |s| s.clone(),
48 );
49
50 let audience_page = state.audience_page();
51
52 if let Some(page) = cache.get(audience_page, render_size) {
53 let page = page.clone();
54 self.display.update(ctx, &page, audience_page);
55 }
56
57 let zoom_region = if state.zoom_active {
59 state.zoom_region.as_ref().map(|r| (r.center, r.factor))
60 } else {
61 None
62 };
63
64 egui::CentralPanel::default().frame(egui::Frame::new().fill(egui::Color32::BLACK)).show(
65 ctx,
66 |ui| {
67 let viewport_rect = ui.max_rect();
68 let image_rect = self.display.show(ui, zoom_region);
69 overlays::draw_overlays(
70 ui,
71 viewport_rect,
72 image_rect,
73 &state,
74 &mut self.tb_cache,
75 &mut self.tb_texture_cache,
76 true,
77 );
78 },
79 );
80 }
81}
82
83impl Default for AudienceWindow {
84 fn default() -> Self {
85 Self::new()
86 }
87}