Skip to main content

dais_ui/presenter/
layout.rs

1//! Presenter console layout manager.
2//!
3//! Splits the presenter window into:
4//! - Left panel (60%): current slide
5//! - Right top (40% width, 50% height): next preview
6//! - Right bottom (40% width, 50% height): notes panel
7//! - Bottom bar (fixed ~40px): status/timer
8
9/// Computed layout rectangles for the presenter console.
10pub struct PresenterLayout {
11    /// Current slide panel (left 60%).
12    pub current_slide: egui::Rect,
13    /// Next preview area (right top).
14    pub next_preview: egui::Rect,
15    /// Notes panel (right bottom).
16    pub notes_panel: egui::Rect,
17    /// Status bar at the bottom.
18    pub status_bar: egui::Rect,
19    /// Vertical splitter between the current slide and right column.
20    pub vertical_splitter: egui::Rect,
21    /// Horizontal splitter between next preview and notes.
22    pub horizontal_splitter: egui::Rect,
23}
24
25/// Height of the status bar in logical pixels.
26const STATUS_BAR_HEIGHT: f32 = 40.0;
27/// Thickness of draggable splitters.
28const SPLITTER_SIZE: f32 = 8.0;
29
30impl PresenterLayout {
31    /// Compute layout rects from the available content area.
32    pub fn compute(available: egui::Rect, left_fraction: f32, top_fraction: f32) -> Self {
33        let total_w = available.width();
34        let total_h = available.height();
35
36        let status_bar = egui::Rect::from_min_size(
37            egui::pos2(available.min.x, available.max.y - STATUS_BAR_HEIGHT),
38            egui::vec2(total_w, STATUS_BAR_HEIGHT),
39        );
40
41        let content_h = (total_h - STATUS_BAR_HEIGHT).max(0.0);
42        let splitter_half = SPLITTER_SIZE * 0.5;
43        let left_w = (total_w * left_fraction - splitter_half).max(0.0);
44        let right_w = (total_w - left_w - SPLITTER_SIZE).max(0.0);
45
46        let current_slide = egui::Rect::from_min_size(available.min, egui::vec2(left_w, content_h));
47        let vertical_splitter = egui::Rect::from_min_max(
48            egui::pos2(current_slide.max.x, available.min.y),
49            egui::pos2(current_slide.max.x + SPLITTER_SIZE, available.min.y + content_h),
50        );
51
52        let right_top = egui::pos2(vertical_splitter.max.x, available.min.y);
53        let top_h = (content_h * top_fraction - splitter_half).max(0.0);
54        let bottom_h = (content_h - top_h - SPLITTER_SIZE).max(0.0);
55
56        let next_preview = egui::Rect::from_min_size(right_top, egui::vec2(right_w, top_h));
57        let horizontal_splitter = egui::Rect::from_min_max(
58            egui::pos2(right_top.x, next_preview.max.y),
59            egui::pos2(right_top.x + right_w, next_preview.max.y + SPLITTER_SIZE),
60        );
61
62        let notes_panel = egui::Rect::from_min_size(
63            egui::pos2(right_top.x, horizontal_splitter.max.y),
64            egui::vec2(right_w, bottom_h),
65        );
66
67        Self {
68            current_slide,
69            next_preview,
70            notes_panel,
71            status_bar,
72            vertical_splitter,
73            horizontal_splitter,
74        }
75    }
76}