1use decorated::Decorated;
2use defaults::*;
3use prototty_render::*;
4
5#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
9#[derive(Debug, Clone)]
10pub struct BorderChars {
11 pub top: char,
12 pub bottom: char,
13 pub left: char,
14 pub right: char,
15 pub top_left: char,
16 pub top_right: char,
17 pub bottom_left: char,
18 pub bottom_right: char,
19 pub before_title: char,
20 pub after_title: char,
21}
22
23impl Default for BorderChars {
24 fn default() -> Self {
25 Self {
26 top: '─',
27 bottom: '─',
28 left: '│',
29 right: '│',
30 top_left: '┌',
31 top_right: '┐',
32 bottom_left: '└',
33 bottom_right: '┘',
34 before_title: '┤',
35 after_title: '├',
36 }
37 }
38}
39
40#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
43#[derive(Default, Debug, Clone)]
44pub struct BorderPadding {
45 pub top: u32,
46 pub bottom: u32,
47 pub left: u32,
48 pub right: u32,
49}
50
51#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
55#[derive(Debug, Clone)]
56pub struct Border {
57 pub title: Option<String>,
58 pub padding: BorderPadding,
59 pub chars: BorderChars,
60 pub foreground_colour: Rgb24,
61 pub background_colour: Rgb24,
62 pub title_colour: Rgb24,
63 pub bold_title: bool,
64 pub underline_title: bool,
65 pub bold_border: bool,
66}
67
68impl<T: ?Sized, V: View<T> + ViewSize<T>> View<T> for Decorated<V, Border> {
69 fn view<G: ViewGrid>(&mut self, data: &T, offset: Coord, depth: i32, grid: &mut G) {
70 self.view
71 .view(data, offset + self.decorator.child_offset(), depth, grid);
72
73 let span = self.decorator.span_offset() + self.view.size(data);
74
75 grid.set_cell(
76 offset,
77 depth,
78 self.decorator.view_cell_info(self.decorator.chars.top_left),
79 );
80 grid.set_cell(
81 offset + Coord::new(span.x, 0),
82 depth,
83 self.decorator
84 .view_cell_info(self.decorator.chars.top_right),
85 );
86 grid.set_cell(
87 offset + Coord::new(0, span.y),
88 depth,
89 self.decorator
90 .view_cell_info(self.decorator.chars.bottom_left),
91 );
92 grid.set_cell(
93 offset + Coord::new(span.x, span.y),
94 depth,
95 self.decorator
96 .view_cell_info(self.decorator.chars.bottom_right),
97 );
98 let title_offset = if let Some(title) = self.decorator.title.as_ref() {
99 let before = offset + Coord::new(1, 0);
100 let after = offset + Coord::new(title.len() as i32 + 2, 0);
101 grid.set_cell(
102 before,
103 depth,
104 self.decorator
105 .view_cell_info(self.decorator.chars.before_title),
106 );
107 grid.set_cell(
108 after,
109 depth,
110 self.decorator
111 .view_cell_info(self.decorator.chars.after_title),
112 );
113 for (index, ch) in title.chars().enumerate() {
114 let coord = offset + Coord::new(index as i32 + 2, 0);
115 grid.set_cell(
116 coord,
117 depth,
118 ViewCell {
119 character: Some(ch),
120 bold: Some(self.decorator.bold_title),
121 underline: Some(self.decorator.underline_title),
122 foreground: Some(self.decorator.title_colour),
123 background: Some(self.decorator.background_colour),
124 },
125 );
126 }
127
128 title.len() as i32 + 2
129 } else {
130 0
131 };
132
133 for i in (1 + title_offset)..span.x {
134 grid.set_cell(
135 offset + Coord::new(i, 0),
136 depth,
137 self.decorator.view_cell_info(self.decorator.chars.top),
138 );
139 }
140 for i in 1..span.x {
141 grid.set_cell(
142 offset + Coord::new(i, span.y),
143 depth,
144 self.decorator.view_cell_info(self.decorator.chars.bottom),
145 );
146 }
147
148 for i in 1..span.y {
149 grid.set_cell(
150 offset + Coord::new(0, i),
151 depth,
152 self.decorator.view_cell_info(self.decorator.chars.left),
153 );
154 grid.set_cell(
155 offset + Coord::new(span.x, i),
156 depth,
157 self.decorator.view_cell_info(self.decorator.chars.right),
158 );
159 }
160 }
161}
162
163impl<T: ?Sized, V: View<T> + ViewSize<T>> ViewSize<T> for Decorated<V, Border> {
164 fn size(&mut self, data: &T) -> Size {
165 self.view.size(data) + Size::new(2, 2)
166 }
167}
168
169impl Border {
170 pub fn new() -> Self {
171 Self {
172 title: None,
173 padding: Default::default(),
174 chars: Default::default(),
175 foreground_colour: DEFAULT_FG,
176 background_colour: DEFAULT_BG,
177 title_colour: DEFAULT_FG,
178 bold_title: false,
179 underline_title: false,
180 bold_border: false,
181 }
182 }
183 pub fn with_title<S: Into<String>>(title: S) -> Self {
184 Self {
185 title: Some(title.into()),
186 padding: Default::default(),
187 chars: Default::default(),
188 foreground_colour: DEFAULT_FG,
189 background_colour: DEFAULT_BG,
190 title_colour: DEFAULT_FG,
191 bold_title: false,
192 underline_title: false,
193 bold_border: false,
194 }
195 }
196 fn child_offset(&self) -> Coord {
197 Coord {
198 x: (self.padding.left + 1) as i32,
199 y: (self.padding.top + 1) as i32,
200 }
201 }
202 fn span_offset(&self) -> Coord {
203 Coord {
204 x: (self.padding.left + self.padding.right + 1) as i32,
205 y: (self.padding.top + self.padding.bottom + 1) as i32,
206 }
207 }
208 fn view_cell_info(&self, character: char) -> ViewCell {
209 ViewCell {
210 character: Some(character),
211 foreground: Some(self.foreground_colour),
212 background: Some(self.background_colour),
213 bold: Some(self.bold_border),
214 underline: Some(false),
215 }
216 }
217}