1 use {
2     super::{
3         Screen,
4         WIDE_STATUS,
5     },
6     crate::{
7         app::Panel,
8         errors::ProgramError,
9     },
10     termimad::Area,
11 };
12 
13 /// the areas of the various parts of a panel. It's
14 /// also where a state usually checks how many panels
15 /// there are, and their respective positions
16 #[derive(Debug, Clone)]
17 pub struct Areas {
18     pub state: Area,
19     pub status: Area,
20     pub input: Area,
21     pub purpose: Option<Area>,
22     pub pos_idx: usize, // from left to right
23     pub nb_pos: usize,  // number of displayed panels
24 }
25 
26 const MINIMAL_PANEL_HEIGHT: u16 = 10;
27 const MINIMAL_PANEL_WIDTH: u16 = 20;
28 
29 enum Slot<'a> {
30     Panel(usize),
31     New(&'a mut Areas),
32 }
33 
34 impl Areas {
35 
36     /// compute an area for a new panel which will be inserted
create( present_panels: &mut [Panel], mut insertion_idx: usize, screen: Screen, with_preview: bool, ) -> Result<Self, ProgramError>37     pub fn create(
38         present_panels: &mut [Panel],
39         mut insertion_idx: usize,
40         screen: Screen,
41         with_preview: bool, // slightly larger last panel
42     ) -> Result<Self, ProgramError> {
43         if insertion_idx > present_panels.len() {
44             insertion_idx = present_panels.len();
45         }
46         let mut areas = Areas {
47             state: Area::uninitialized(),
48             status: Area::uninitialized(),
49             input: Area::uninitialized(),
50             purpose: None,
51             pos_idx: 0,
52             nb_pos: 1,
53         };
54         let mut slots = Vec::with_capacity(present_panels.len() + 1);
55         for i in 0..insertion_idx {
56             slots.push(Slot::Panel(i));
57         }
58         slots.push(Slot::New(&mut areas));
59         for i in insertion_idx..present_panels.len() {
60             slots.push(Slot::Panel(i));
61         }
62         Self::compute_areas(present_panels, &mut slots, screen, with_preview)?;
63         Ok(areas)
64     }
65 
resize_all( panels: &mut [Panel], screen: Screen, with_preview: bool, ) -> Result<(), ProgramError>66     pub fn resize_all(
67         panels: &mut [Panel],
68         screen: Screen,
69         with_preview: bool, // slightly larger last panel
70     ) -> Result<(), ProgramError> {
71         let mut slots = Vec::new();
72         for i in 0..panels.len() {
73             slots.push(Slot::Panel(i));
74         }
75         Self::compute_areas(panels, &mut slots, screen, with_preview)
76     }
77 
compute_areas( panels: &mut [Panel], slots: &mut Vec<Slot>, screen: Screen, with_preview: bool, ) -> Result<(), ProgramError>78     fn compute_areas(
79         panels: &mut [Panel],
80         slots: &mut Vec<Slot>,
81         screen: Screen,
82         with_preview: bool, // slightly larger last panel
83     ) -> Result<(), ProgramError> {
84         if screen.height < MINIMAL_PANEL_HEIGHT {
85             return Err(ProgramError::TerminalTooSmallError);
86         }
87         let n = slots.len() as u16;
88         let mut panel_width = if with_preview {
89             3 * screen.width / (3 * n + 1)
90         } else {
91             screen.width / n
92         };
93         if panel_width < MINIMAL_PANEL_WIDTH {
94             return Err(ProgramError::TerminalTooSmallError);
95         }
96         let mut x = 0;
97         let nb_pos = slots.len();
98         #[allow(clippy::needless_range_loop)]
99         for slot_idx in 0..nb_pos {
100             if slot_idx == nb_pos - 1 {
101                 panel_width = screen.width - x;
102             }
103             let areas: &mut Areas = match &mut slots[slot_idx] {
104                 Slot::Panel(panel_idx) => &mut panels[*panel_idx].areas,
105                 Slot::New(areas) => areas,
106             };
107             let y = screen.height - 2;
108             areas.state = Area::new(x, 0, panel_width, y);
109             areas.status = if WIDE_STATUS {
110                 Area::new(0, y, screen.width, 1)
111             } else {
112                 Area::new(x, y, panel_width, 1)
113             };
114             let y = y + 1;
115             areas.input = Area::new(x, y, panel_width, 1);
116             if slot_idx == nb_pos - 1 {
117                 // the char at the bottom right of the terminal should not be touched
118                 // (it makes some terminals flicker) so the input area is one char shorter
119                 areas.input.width -= 1;
120             }
121             areas.purpose = if slot_idx > 0 {
122                 let area_width = panel_width / 2;
123                 Some(Area::new(x - area_width, y, area_width, 1))
124             } else {
125                 None
126             };
127             areas.pos_idx = slot_idx;
128             areas.nb_pos = nb_pos;
129             x += panel_width;
130         }
131         Ok(())
132     }
133 
is_first(&self) -> bool134     pub fn is_first(&self) -> bool {
135         self.pos_idx == 0
136     }
is_last(&self) -> bool137     pub fn is_last(&self) -> bool {
138         self.pos_idx + 1 == self.nb_pos
139     }
140 }
141