shellshot/
window_decoration.rs1use crate::{
2 image_renderer::{ImageRendererError, canvas::Canvas, render_size::Size},
3 window_decoration::no_decoration::NoDecoration,
4};
5
6mod classic;
7pub mod common;
8mod no_decoration;
9
10use ab_glyph::FontArc;
11use clap::ValueEnum;
12pub use classic::Classic;
13use image::Rgba;
14use termwiz::cell::Cell;
15
16#[derive(Clone, Debug, ValueEnum)]
18pub enum WindowDecorationType {
19 Classic,
21}
22
23#[derive(Clone, Debug)]
24pub struct WindowMetrics {
25 pub padding: u32,
26 pub border_width: u32,
27 pub title_bar_height: u32,
28}
29
30#[derive(Debug, Clone)]
31pub struct Fonts {
32 pub regular: FontArc,
33 pub bold: FontArc,
34 pub italic: FontArc,
35 pub bold_italic: FontArc,
36}
37
38pub trait WindowDecoration: std::fmt::Debug {
39 fn build_command_line(&self, command: &str) -> Vec<Cell>;
40
41 fn compute_metrics(&self, char_size: Size) -> WindowMetrics;
42
43 fn get_color_palette(&self) -> [Rgba<u8>; 256];
44
45 fn font(&self) -> Result<Fonts, ImageRendererError>;
46
47 fn draw_window(
48 &self,
49 canvas: &mut Canvas,
50 metrics: &WindowMetrics,
51 ) -> Result<(), ImageRendererError>;
52}
53
54pub fn create_window_decoration(
55 decoration_type: Option<&WindowDecorationType>,
56) -> Box<dyn WindowDecoration> {
57 match decoration_type {
58 Some(WindowDecorationType::Classic) => Box::new(Classic),
59 None => Box::new(NoDecoration),
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use ab_glyph::PxScale;
66
67 use crate::image_renderer::render_size::calculate_char_size;
68
69 use super::*;
70
71 fn all_window_decorations() -> Vec<Option<WindowDecorationType>> {
72 let mut types = WindowDecorationType::value_variants()
73 .iter()
74 .cloned()
75 .map(Some)
76 .collect::<Vec<_>>();
77 types.push(None);
78 types
79 }
80
81 #[test]
82 fn test_all_window_decorations_command_line() {
83 for decoration_type in all_window_decorations() {
84 let window_decoration = create_window_decoration(decoration_type.as_ref());
85 let command_line = window_decoration.build_command_line("echo test");
86
87 assert!(
88 !command_line.is_empty(),
89 "Unexpected number of cells for {decoration_type:?}",
90 );
91 }
92 }
93
94 #[test]
95 fn test_all_window_decorations_colors() {
96 for decoration_type in all_window_decorations() {
97 let window_decoration = create_window_decoration(decoration_type.as_ref());
98
99 let fg = window_decoration.get_color_palette()[7];
100 assert!(fg.0[3] > 0, "Alpha channel must be > 0");
101
102 let palette = window_decoration.get_color_palette();
103 assert_eq!(palette.len(), 256, "Palette must have 256 colors");
104 }
105 }
106
107 #[test]
108 fn test_all_window_decorations_draw() {
109 let canvas_width = 200;
110 let canvas_height = 100;
111 let scale = PxScale::from(1.0);
112
113 for decoration_type in all_window_decorations() {
114 let window_decoration = create_window_decoration(decoration_type.as_ref());
115
116 let font = window_decoration.font().expect("Font should be available");
117
118 let char_size = calculate_char_size(&font.regular, scale);
119 let metrics = window_decoration.compute_metrics(char_size);
120
121 let mut canvas = Canvas::new(canvas_width, canvas_height, font.clone(), scale)
122 .expect("Failed to create Canvas");
123
124 let result = window_decoration.draw_window(&mut canvas, &metrics);
125 assert!(
126 result.is_ok(),
127 "draw_window failed for {decoration_type:?}: {result:?}",
128 );
129 }
130 }
131}