@@ -1,281 +1,481 @@
1−//! `style` contains the primitives used to control how your user interface will look.
2−
3−use bitflags::bitflags;
4−
5−#[derive(Debug, Clone, Copy, PartialEq)]
6−#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7−pub enum Color {
8− Reset,
9− Black,
10− Red,
11− Green,
12− Yellow,
13− Blue,
14− Magenta,
15− Cyan,
16− Gray,
17− DarkGray,
18− LightRed,
19− LightGreen,
20− LightYellow,
21− LightBlue,
22− LightMagenta,
23− LightCyan,
24− White,
25− Rgb(u8, u8, u8),
26− Indexed(u8),
27−}
28−
29−bitflags! {
30− /// Modifier changes the way a piece of text is displayed.
31− ///
32− /// They are bitflags so they can easily be composed.
33− ///
34− /// ## Examples
35− ///
36− /// ```rust
37− /// # use helix_tui::style::Modifier;
38− ///
39− /// let m = Modifier::BOLD | Modifier::ITALIC;
40− /// ```
41− #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42− pub struct Modifier: u16 {
43− const BOLD = 0b0000_0000_0001;
44− const DIM = 0b0000_0000_0010;
45− const ITALIC = 0b0000_0000_0100;
46− const UNDERLINED = 0b0000_0000_1000;
47− const SLOW_BLINK = 0b0000_0001_0000;
48− const RAPID_BLINK = 0b0000_0010_0000;
49− const REVERSED = 0b0000_0100_0000;
50− const HIDDEN = 0b0000_1000_0000;
51− const CROSSED_OUT = 0b0001_0000_0000;
52− }
53−}
54−
55−/// Style let you control the main characteristics of the displayed elements.
56−///
57−/// ```rust
58−/// # use helix_tui::style::{Color, Modifier, Style};
59−/// Style::default()
60−/// .fg(Color::Black)
61−/// .bg(Color::Green)
62−/// .add_modifier(Modifier::ITALIC | Modifier::BOLD);
63−/// ```
64−///
65−/// It represents an incremental change. If you apply the styles S1, S2, S3 to a cell of the
66−/// terminal buffer, the style of this cell will be the result of the merge of S1, S2 and S3, not
67−/// just S3.
68−///
69−/// ```rust
70−/// # use helix_tui::style::{Color, Modifier, Style};
71−/// # use helix_tui::buffer::Buffer;
72−/// # use helix_tui::layout::Rect;
73−/// let styles = [
74−/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
75−/// Style::default().bg(Color::Red),
76−/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
77−/// ];
78−/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
79−/// for style in &styles {
80−/// buffer.get_mut(0, 0).set_style(*style);
81−/// }
82−/// assert_eq!(
83−/// Style {
84−/// fg: Some(Color::Yellow),
85−/// bg: Some(Color::Red),
86−/// add_modifier: Modifier::BOLD,
87−/// sub_modifier: Modifier::empty(),
88−/// },
89−/// buffer.get(0, 0).style(),
90−/// );
91−/// ```
92−///
93−/// The default implementation returns a `Style` that does not modify anything. If you wish to
94−/// reset all properties until that point use [`Style::reset`].
95−///
96−/// ```
97−/// # use helix_tui::style::{Color, Modifier, Style};
98−/// # use helix_tui::buffer::Buffer;
99−/// # use helix_tui::layout::Rect;
100−/// let styles = [
101−/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
102−/// Style::reset().fg(Color::Yellow),
103−/// ];
104−/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
105−/// for style in &styles {
106−/// buffer.get_mut(0, 0).set_style(*style);
107−/// }
108−/// assert_eq!(
109−/// Style {
110−/// fg: Some(Color::Yellow),
111−/// bg: Some(Color::Reset),
112−/// add_modifier: Modifier::empty(),
113−/// sub_modifier: Modifier::empty(),
114−/// },
115−/// buffer.get(0, 0).style(),
116−/// );
117−/// ```
118−#[derive(Debug, Clone, Copy, PartialEq)]
119−#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
120−pub struct Style {
121− pub fg: Option<Color>,
122− pub bg: Option<Color>,
123− pub add_modifier: Modifier,
124− pub sub_modifier: Modifier,
125−}
126−
127−impl Default for Style {
128− fn default() -> Style {
129− Style {
130− fg: None,
131− bg: None,
132− add_modifier: Modifier::empty(),
133− sub_modifier: Modifier::empty(),
134− }
135− }
136−}
137−
138−impl Style {
139− /// Returns a `Style` resetting all properties.
140− pub fn reset() -> Style {
141− Style {
142− fg: Some(Color::Reset),
143− bg: Some(Color::Reset),
144− add_modifier: Modifier::empty(),
145− sub_modifier: Modifier::all(),
146− }
147− }
148−
149− /// Changes the foreground color.
150− ///
151− /// ## Examples
152− ///
153− /// ```rust
154− /// # use helix_tui::style::{Color, Style};
155− /// let style = Style::default().fg(Color::Blue);
156− /// let diff = Style::default().fg(Color::Red);
157− /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red));
158− /// ```
159− pub fn fg(mut self, color: Color) -> Style {
160− self.fg = Some(color);
161− self
162− }
163−
164− /// Changes the background color.
165− ///
166− /// ## Examples
167− ///
168− /// ```rust
169− /// # use helix_tui::style::{Color, Style};
170− /// let style = Style::default().bg(Color::Blue);
171− /// let diff = Style::default().bg(Color::Red);
172− /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red));
173− /// ```
174− pub fn bg(mut self, color: Color) -> Style {
175− self.bg = Some(color);
176− self
177− }
178−
179− /// Changes the text emphasis.
180− ///
181− /// When applied, it adds the given modifier to the `Style` modifiers.
182− ///
183− /// ## Examples
184− ///
185− /// ```rust
186− /// # use helix_tui::style::{Color, Modifier, Style};
187− /// let style = Style::default().add_modifier(Modifier::BOLD);
188− /// let diff = Style::default().add_modifier(Modifier::ITALIC);
189− /// let patched = style.patch(diff);
190− /// assert_eq!(patched.add_modifier, Modifier::BOLD | Modifier::ITALIC);
191− /// assert_eq!(patched.sub_modifier, Modifier::empty());
192− /// ```
193− pub fn add_modifier(mut self, modifier: Modifier) -> Style {
194− self.sub_modifier.remove(modifier);
195− self.add_modifier.insert(modifier);
196− self
197− }
198−
199− /// Changes the text emphasis.
200− ///
201− /// When applied, it removes the given modifier from the `Style` modifiers.
202− ///
203− /// ## Examples
204− ///
205− /// ```rust
206− /// # use helix_tui::style::{Color, Modifier, Style};
207− /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
208− /// let diff = Style::default().remove_modifier(Modifier::ITALIC);
209− /// let patched = style.patch(diff);
210− /// assert_eq!(patched.add_modifier, Modifier::BOLD);
211− /// assert_eq!(patched.sub_modifier, Modifier::ITALIC);
212− /// ```
213− pub fn remove_modifier(mut self, modifier: Modifier) -> Style {
214− self.add_modifier.remove(modifier);
215− self.sub_modifier.insert(modifier);
216− self
217− }
218−
219− /// Results in a combined style that is equivalent to applying the two individual styles to
220− /// a style one after the other.
221− ///
222− /// ## Examples
223− /// ```
224− /// # use helix_tui::style::{Color, Modifier, Style};
225− /// let style_1 = Style::default().fg(Color::Yellow);
226− /// let style_2 = Style::default().bg(Color::Red);
227− /// let combined = style_1.patch(style_2);
228− /// assert_eq!(
229− /// Style::default().patch(style_1).patch(style_2),
230− /// Style::default().patch(combined));
231− /// ```
232− pub fn patch(mut self, other: Style) -> Style {
233− self.fg = other.fg.or(self.fg);
234− self.bg = other.bg.or(self.bg);
235−
236− self.add_modifier.remove(other.sub_modifier);
237− self.add_modifier.insert(other.add_modifier);
238− self.sub_modifier.remove(other.add_modifier);
239− self.sub_modifier.insert(other.sub_modifier);
240−
241− self
242− }
243−}
244−
245−#[cfg(test)]
246−mod tests {
247− use super::*;
248−
249− fn styles() -> Vec<Style> {
250− vec![
251− Style::default(),
252− Style::default().fg(Color::Yellow),
253− Style::default().bg(Color::Yellow),
254− Style::default().add_modifier(Modifier::BOLD),
255− Style::default().remove_modifier(Modifier::BOLD),
256− Style::default().add_modifier(Modifier::ITALIC),
257− Style::default().remove_modifier(Modifier::ITALIC),
258− Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
259− Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
260− ]
261− }
262−
263− #[test]
264− fn combined_patch_gives_same_result_as_individual_patch() {
265− let styles = styles();
266− for &a in &styles {
267− for &b in &styles {
268− for &c in &styles {
269− for &d in &styles {
270− let combined = a.patch(b.patch(c.patch(d)));
271−
272− assert_eq!(
273− Style::default().patch(a).patch(b).patch(c).patch(d),
274− Style::default().patch(combined)
275− );
276− }
277− }
278− }
279− }
280− }
281−}
1+use bitflags::bitflags;
2+use std::cmp::{max, min};
3+
4+#[derive(Debug)]
5+/// UNSTABLE
6+pub enum CursorKind {
7+ /// █
8+ Block,
9+ /// |
10+ Bar,
11+ /// _
12+ Underline,
13+ /// Hidden cursor, can set cursor position with this to let IME have correct cursor position.
14+ Hidden,
15+}
16+
17+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18+pub struct Margin {
19+ pub vertical: u16,
20+ pub horizontal: u16,
21+}
22+
23+/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
24+/// area they are supposed to render to.
25+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
26+pub struct Rect {
27+ pub x: u16,
28+ pub y: u16,
29+ pub width: u16,
30+ pub height: u16,
31+}
32+
33+impl Default for Rect {
34+ fn default() -> Rect {
35+ Rect {
36+ x: 0,
37+ y: 0,
38+ width: 0,
39+ height: 0,
40+ }
41+ }
42+}
43+
44+impl Rect {
45+ /// Creates a new rect, with width and height limited to keep the area under max u16.
46+ /// If clipped, aspect ratio will be preserved.
47+ pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
48+ let max_area = u16::max_value();
49+ let (clipped_width, clipped_height) =
50+ if u32::from(width) * u32::from(height) > u32::from(max_area) {
51+ let aspect_ratio = f64::from(width) / f64::from(height);
52+ let max_area_f = f64::from(max_area);
53+ let height_f = (max_area_f / aspect_ratio).sqrt();
54+ let width_f = height_f * aspect_ratio;
55+ (width_f as u16, height_f as u16)
56+ } else {
57+ (width, height)
58+ };
59+ Rect {
60+ x,
61+ y,
62+ width: clipped_width,
63+ height: clipped_height,
64+ }
65+ }
66+
67+ pub fn area(self) -> u16 {
68+ self.width * self.height
69+ }
70+
71+ pub fn left(self) -> u16 {
72+ self.x
73+ }
74+
75+ pub fn right(self) -> u16 {
76+ self.x.saturating_add(self.width)
77+ }
78+
79+ pub fn top(self) -> u16 {
80+ self.y
81+ }
82+
83+ pub fn bottom(self) -> u16 {
84+ self.y.saturating_add(self.height)
85+ }
86+
87+ pub fn inner(self, margin: &Margin) -> Rect {
88+ if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
89+ Rect::default()
90+ } else {
91+ Rect {
92+ x: self.x + margin.horizontal,
93+ y: self.y + margin.vertical,
94+ width: self.width - 2 * margin.horizontal,
95+ height: self.height - 2 * margin.vertical,
96+ }
97+ }
98+ }
99+
100+ pub fn union(self, other: Rect) -> Rect {
101+ let x1 = min(self.x, other.x);
102+ let y1 = min(self.y, other.y);
103+ let x2 = max(self.x + self.width, other.x + other.width);
104+ let y2 = max(self.y + self.height, other.y + other.height);
105+ Rect {
106+ x: x1,
107+ y: y1,
108+ width: x2 - x1,
109+ height: y2 - y1,
110+ }
111+ }
112+
113+ pub fn intersection(self, other: Rect) -> Rect {
114+ let x1 = max(self.x, other.x);
115+ let y1 = max(self.y, other.y);
116+ let x2 = min(self.x + self.width, other.x + other.width);
117+ let y2 = min(self.y + self.height, other.y + other.height);
118+ Rect {
119+ x: x1,
120+ y: y1,
121+ width: x2 - x1,
122+ height: y2 - y1,
123+ }
124+ }
125+
126+ pub fn intersects(self, other: Rect) -> bool {
127+ self.x < other.x + other.width
128+ && self.x + self.width > other.x
129+ && self.y < other.y + other.height
130+ && self.y + self.height > other.y
131+ }
132+}
133+
134+#[derive(Debug, Clone, Copy, PartialEq)]
135+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136+pub enum Color {
137+ Reset,
138+ Black,
139+ Red,
140+ Green,
141+ Yellow,
142+ Blue,
143+ Magenta,
144+ Cyan,
145+ Gray,
146+ DarkGray,
147+ LightRed,
148+ LightGreen,
149+ LightYellow,
150+ LightBlue,
151+ LightMagenta,
152+ LightCyan,
153+ White,
154+ Rgb(u8, u8, u8),
155+ Indexed(u8),
156+}
157+
158+#[cfg(feature = "term")]
159+impl From<Color> for crossterm::style::Color {
160+ fn from(color: Color) -> Self {
161+ use crossterm::style::Color as CColor;
162+
163+ match color {
164+ Color::Reset => CColor::Reset,
165+ Color::Black => CColor::Black,
166+ Color::Red => CColor::DarkRed,
167+ Color::Green => CColor::DarkGreen,
168+ Color::Yellow => CColor::DarkYellow,
169+ Color::Blue => CColor::DarkBlue,
170+ Color::Magenta => CColor::DarkMagenta,
171+ Color::Cyan => CColor::DarkCyan,
172+ Color::Gray => CColor::Grey,
173+ Color::DarkGray => CColor::DarkGrey,
174+ Color::LightRed => CColor::Red,
175+ Color::LightGreen => CColor::Green,
176+ Color::LightBlue => CColor::Blue,
177+ Color::LightYellow => CColor::Yellow,
178+ Color::LightMagenta => CColor::Magenta,
179+ Color::LightCyan => CColor::Cyan,
180+ Color::White => CColor::White,
181+ Color::Indexed(i) => CColor::AnsiValue(i),
182+ Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
183+ }
184+ }
185+}
186+
187+bitflags! {
188+ /// Modifier changes the way a piece of text is displayed.
189+ ///
190+ /// They are bitflags so they can easily be composed.
191+ ///
192+ /// ## Examples
193+ ///
194+ /// ```rust
195+ /// # use helix_view::graphics::Modifier;
196+ ///
197+ /// let m = Modifier::BOLD | Modifier::ITALIC;
198+ /// ```
199+ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
200+ pub struct Modifier: u16 {
201+ const BOLD = 0b0000_0000_0001;
202+ const DIM = 0b0000_0000_0010;
203+ const ITALIC = 0b0000_0000_0100;
204+ const UNDERLINED = 0b0000_0000_1000;
205+ const SLOW_BLINK = 0b0000_0001_0000;
206+ const RAPID_BLINK = 0b0000_0010_0000;
207+ const REVERSED = 0b0000_0100_0000;
208+ const HIDDEN = 0b0000_1000_0000;
209+ const CROSSED_OUT = 0b0001_0000_0000;
210+ }
211+}
212+
213+/// Style let you control the main characteristics of the displayed elements.
214+///
215+/// ```rust
216+/// # use helix_view::graphics::{Color, Modifier, Style};
217+/// Style::default()
218+/// .fg(Color::Black)
219+/// .bg(Color::Green)
220+/// .add_modifier(Modifier::ITALIC | Modifier::BOLD);
221+/// ```
222+///
223+/// It represents an incremental change. If you apply the styles S1, S2, S3 to a cell of the
224+/// terminal buffer, the style of this cell will be the result of the merge of S1, S2 and S3, not
225+/// just S3.
226+///
227+/// ```rust
228+/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
229+/// # use helix_tui::buffer::Buffer;
230+/// let styles = [
231+/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
232+/// Style::default().bg(Color::Red),
233+/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
234+/// ];
235+/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
236+/// for style in &styles {
237+/// buffer.get_mut(0, 0).set_style(*style);
238+/// }
239+/// assert_eq!(
240+/// Style {
241+/// fg: Some(Color::Yellow),
242+/// bg: Some(Color::Red),
243+/// add_modifier: Modifier::BOLD,
244+/// sub_modifier: Modifier::empty(),
245+/// },
246+/// buffer.get(0, 0).style(),
247+/// );
248+/// ```
249+///
250+/// The default implementation returns a `Style` that does not modify anything. If you wish to
251+/// reset all properties until that point use [`Style::reset`].
252+///
253+/// ```
254+/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
255+/// # use helix_tui::buffer::Buffer;
256+/// let styles = [
257+/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
258+/// Style::reset().fg(Color::Yellow),
259+/// ];
260+/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
261+/// for style in &styles {
262+/// buffer.get_mut(0, 0).set_style(*style);
263+/// }
264+/// assert_eq!(
265+/// Style {
266+/// fg: Some(Color::Yellow),
267+/// bg: Some(Color::Reset),
268+/// add_modifier: Modifier::empty(),
269+/// sub_modifier: Modifier::empty(),
270+/// },
271+/// buffer.get(0, 0).style(),
272+/// );
273+/// ```
274+#[derive(Debug, Clone, Copy, PartialEq)]
275+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
276+pub struct Style {
277+ pub fg: Option<Color>,
278+ pub bg: Option<Color>,
279+ pub add_modifier: Modifier,
280+ pub sub_modifier: Modifier,
281+}
282+
283+impl Default for Style {
284+ fn default() -> Style {
285+ Style {
286+ fg: None,
287+ bg: None,
288+ add_modifier: Modifier::empty(),
289+ sub_modifier: Modifier::empty(),
290+ }
291+ }
292+}
293+
294+impl Style {
295+ /// Returns a `Style` resetting all properties.
296+ pub fn reset() -> Style {
297+ Style {
298+ fg: Some(Color::Reset),
299+ bg: Some(Color::Reset),
300+ add_modifier: Modifier::empty(),
301+ sub_modifier: Modifier::all(),
302+ }
303+ }
304+
305+ /// Changes the foreground color.
306+ ///
307+ /// ## Examples
308+ ///
309+ /// ```rust
310+ /// # use helix_view::graphics::{Color, Style};
311+ /// let style = Style::default().fg(Color::Blue);
312+ /// let diff = Style::default().fg(Color::Red);
313+ /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red));
314+ /// ```
315+ pub fn fg(mut self, color: Color) -> Style {
316+ self.fg = Some(color);
317+ self
318+ }
319+
320+ /// Changes the background color.
321+ ///
322+ /// ## Examples
323+ ///
324+ /// ```rust
325+ /// # use helix_view::graphics::{Color, Style};
326+ /// let style = Style::default().bg(Color::Blue);
327+ /// let diff = Style::default().bg(Color::Red);
328+ /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red));
329+ /// ```
330+ pub fn bg(mut self, color: Color) -> Style {
331+ self.bg = Some(color);
332+ self
333+ }
334+
335+ /// Changes the text emphasis.
336+ ///
337+ /// When applied, it adds the given modifier to the `Style` modifiers.
338+ ///
339+ /// ## Examples
340+ ///
341+ /// ```rust
342+ /// # use helix_view::graphics::{Color, Modifier, Style};
343+ /// let style = Style::default().add_modifier(Modifier::BOLD);
344+ /// let diff = Style::default().add_modifier(Modifier::ITALIC);
345+ /// let patched = style.patch(diff);
346+ /// assert_eq!(patched.add_modifier, Modifier::BOLD | Modifier::ITALIC);
347+ /// assert_eq!(patched.sub_modifier, Modifier::empty());
348+ /// ```
349+ pub fn add_modifier(mut self, modifier: Modifier) -> Style {
350+ self.sub_modifier.remove(modifier);
351+ self.add_modifier.insert(modifier);
352+ self
353+ }
354+
355+ /// Changes the text emphasis.
356+ ///
357+ /// When applied, it removes the given modifier from the `Style` modifiers.
358+ ///
359+ /// ## Examples
360+ ///
361+ /// ```rust
362+ /// # use helix_view::graphics::{Color, Modifier, Style};
363+ /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
364+ /// let diff = Style::default().remove_modifier(Modifier::ITALIC);
365+ /// let patched = style.patch(diff);
366+ /// assert_eq!(patched.add_modifier, Modifier::BOLD);
367+ /// assert_eq!(patched.sub_modifier, Modifier::ITALIC);
368+ /// ```
369+ pub fn remove_modifier(mut self, modifier: Modifier) -> Style {
370+ self.add_modifier.remove(modifier);
371+ self.sub_modifier.insert(modifier);
372+ self
373+ }
374+
375+ /// Results in a combined style that is equivalent to applying the two individual styles to
376+ /// a style one after the other.
377+ ///
378+ /// ## Examples
379+ /// ```
380+ /// # use helix_view::graphics::{Color, Modifier, Style};
381+ /// let style_1 = Style::default().fg(Color::Yellow);
382+ /// let style_2 = Style::default().bg(Color::Red);
383+ /// let combined = style_1.patch(style_2);
384+ /// assert_eq!(
385+ /// Style::default().patch(style_1).patch(style_2),
386+ /// Style::default().patch(combined));
387+ /// ```
388+ pub fn patch(mut self, other: Style) -> Style {
389+ self.fg = other.fg.or(self.fg);
390+ self.bg = other.bg.or(self.bg);
391+
392+ self.add_modifier.remove(other.sub_modifier);
393+ self.add_modifier.insert(other.add_modifier);
394+ self.sub_modifier.remove(other.add_modifier);
395+ self.sub_modifier.insert(other.sub_modifier);
396+
397+ self
398+ }
399+}
400+
401+#[cfg(test)]
402+mod tests {
403+ use super::*;
404+
405+ #[test]
406+ fn test_rect_size_truncation() {
407+ for width in 256u16..300u16 {
408+ for height in 256u16..300u16 {
409+ let rect = Rect::new(0, 0, width, height);
410+ rect.area(); // Should not panic.
411+ assert!(rect.width < width || rect.height < height);
412+ // The target dimensions are rounded down so the math will not be too precise
413+ // but let's make sure the ratios don't diverge crazily.
414+ assert!(
415+ (f64::from(rect.width) / f64::from(rect.height)
416+ - f64::from(width) / f64::from(height))
417+ .abs()
418+ < 1.0
419+ )
420+ }
421+ }
422+
423+ // One dimension below 255, one above. Area above max u16.
424+ let width = 900;
425+ let height = 100;
426+ let rect = Rect::new(0, 0, width, height);
427+ assert_ne!(rect.width, 900);
428+ assert_ne!(rect.height, 100);
429+ assert!(rect.width < width || rect.height < height);
430+ }
431+
432+ #[test]
433+ fn test_rect_size_preservation() {
434+ for width in 0..256u16 {
435+ for height in 0..256u16 {
436+ let rect = Rect::new(0, 0, width, height);
437+ rect.area(); // Should not panic.
438+ assert_eq!(rect.width, width);
439+ assert_eq!(rect.height, height);
440+ }
441+ }
442+
443+ // One dimension below 255, one above. Area below max u16.
444+ let rect = Rect::new(0, 0, 300, 100);
445+ assert_eq!(rect.width, 300);
446+ assert_eq!(rect.height, 100);
447+ }
448+
449+ fn styles() -> Vec<Style> {
450+ vec![
451+ Style::default(),
452+ Style::default().fg(Color::Yellow),
453+ Style::default().bg(Color::Yellow),
454+ Style::default().add_modifier(Modifier::BOLD),
455+ Style::default().remove_modifier(Modifier::BOLD),
456+ Style::default().add_modifier(Modifier::ITALIC),
457+ Style::default().remove_modifier(Modifier::ITALIC),
458+ Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
459+ Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
460+ ]
461+ }
462+
463+ #[test]
464+ fn combined_patch_gives_same_result_as_individual_patch() {
465+ let styles = styles();
466+ for &a in &styles {
467+ for &b in &styles {
468+ for &c in &styles {
469+ for &d in &styles {
470+ let combined = a.patch(b.patch(c.patch(d)));
471+
472+ assert_eq!(
473+ Style::default().patch(a).patch(b).patch(c).patch(d),
474+ Style::default().patch(combined)
475+ );
476+ }
477+ }
478+ }
479+ }
480+ }
481+}