magical markdown slides
at 9f8b184e4eefac55aaae12c0697e8ee9e6e1eebf 119 lines 3.5 kB view raw
1use ratatui::layout::{Constraint, Direction, Layout, Rect}; 2 3/// Layout manager for slide presentation 4/// 5/// Calculates screen layout with main slide area, optional notes panel, and status bar. 6pub struct SlideLayout { 7 show_notes: bool, 8} 9 10impl SlideLayout { 11 pub fn new(show_notes: bool) -> Self { 12 Self { show_notes } 13 } 14 15 /// Calculate layout areas for the slide viewer 16 /// 17 /// Returns (main_area, notes_area, status_area) where notes_area is None if notes are hidden. 18 pub fn calculate(&self, area: Rect) -> (Rect, Option<Rect>, Rect) { 19 let vertical_chunks = Layout::default() 20 .direction(Direction::Vertical) 21 .constraints([Constraint::Min(3), Constraint::Length(1)]) 22 .split(area); 23 24 let content_area = vertical_chunks[0]; 25 let status_area = vertical_chunks[1]; 26 27 if self.show_notes { 28 let horizontal_chunks = Layout::default() 29 .direction(Direction::Horizontal) 30 .constraints([Constraint::Percentage(60), Constraint::Percentage(40)]) 31 .split(content_area); 32 33 (horizontal_chunks[0], Some(horizontal_chunks[1]), status_area) 34 } else { 35 (content_area, None, status_area) 36 } 37 } 38 39 /// Update notes visibility 40 pub fn set_show_notes(&mut self, show: bool) { 41 self.show_notes = show; 42 } 43 44 /// Check if notes are visible 45 pub fn is_showing_notes(&self) -> bool { 46 self.show_notes 47 } 48} 49 50impl Default for SlideLayout { 51 fn default() -> Self { 52 Self { show_notes: false } 53 } 54} 55 56#[cfg(test)] 57mod tests { 58 use super::*; 59 60 #[test] 61 fn layout_without_notes() { 62 let layout = SlideLayout::new(false); 63 let area = Rect::new(0, 0, 100, 50); 64 let (main, notes, status) = layout.calculate(area); 65 66 assert!(notes.is_none()); 67 assert_eq!(status.height, 1); 68 assert!(main.height > status.height); 69 } 70 71 #[test] 72 fn layout_with_notes() { 73 let layout = SlideLayout::new(true); 74 let area = Rect::new(0, 0, 100, 50); 75 let (main, notes, status) = layout.calculate(area); 76 77 assert!(notes.is_some()); 78 let notes_area = notes.unwrap(); 79 assert!(main.width > notes_area.width); 80 assert_eq!(main.height, notes_area.height); 81 assert_eq!(status.height, 1); 82 } 83 84 #[test] 85 fn layout_toggle_notes() { 86 let mut layout = SlideLayout::default(); 87 assert!(!layout.is_showing_notes()); 88 89 layout.set_show_notes(true); 90 assert!(layout.is_showing_notes()); 91 92 layout.set_show_notes(false); 93 assert!(!layout.is_showing_notes()); 94 } 95 96 #[test] 97 fn layout_small_terminal() { 98 let layout = SlideLayout::new(false); 99 let area = Rect::new(0, 0, 20, 10); 100 let (main, _notes, status) = layout.calculate(area); 101 102 assert_eq!(status.height, 1); 103 assert!(main.height >= 3); 104 } 105 106 #[test] 107 fn layout_proportions_with_notes() { 108 let layout = SlideLayout::new(true); 109 let area = Rect::new(0, 0, 100, 50); 110 let (main, notes, _status) = layout.calculate(area); 111 112 let notes_area = notes.unwrap(); 113 let main_percentage = (main.width as f32 / area.width as f32) * 100.0; 114 let notes_percentage = (notes_area.width as f32 / area.width as f32) * 100.0; 115 116 assert!(main_percentage >= 55.0 && main_percentage <= 65.0); 117 assert!(notes_percentage >= 35.0 && notes_percentage <= 45.0); 118 } 119}