@@ -1,281 +1,481 @@
1−//! `style` contains the primitives used to control how your user interface will look.
1+use bitflags::bitflags;
2−
2+use std::cmp::{max, min};
3−use bitflags::bitflags;
3+
13+ /// Hidden cursor, can set cursor position with this to let IME have correct cursor position.
17+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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.
5−#[derive(Debug, Clone, Copy, PartialEq)]
25+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
6−#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26+pub struct Rect {
7−pub enum Color {
27+ pub x: u16,
9− Black,
29+ pub width: u16,
10− Red,
30+ pub height: u16,
13− Blue,
33+impl Default for Rect {
14− Magenta,
34+ fn default() -> Rect {
18− LightRed,
38+ width: 0,
19− LightGreen,
39+ height: 0,
25− Rgb(u8, u8, u8),
45+ /// Creates a new rect, with width and height limited to keep the area under max u16.
26− Indexed(u8),
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)
62+ width: clipped_width,
63+ height: clipped_height,
67+ pub fn area(self) -> u16 {
68+ self.width * self.height
71+ pub fn left(self) -> u16 {
75+ pub fn right(self) -> u16 {
76+ self.x.saturating_add(self.width)
79+ pub fn top(self) -> u16 {
83+ pub fn bottom(self) -> u16 {
84+ self.y.saturating_add(self.height)
87+ pub fn inner(self, margin: &Margin) -> Rect {
88+ if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
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,
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);
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);
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
134+#[derive(Debug, Clone, Copy, PartialEq)]
135+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
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;
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 },
29−bitflags! {
187+bitflags! {
30− /// Modifier changes the way a piece of text is displayed.
188+ /// Modifier changes the way a piece of text is displayed.
32− /// They are bitflags so they can easily be composed.
190+ /// They are bitflags so they can easily be composed.
34− /// ## Examples
192+ /// ## Examples
36− /// ```rust
194+ /// ```rust
37− /// # use helix_tui::style::Modifier;
195+ /// # use helix_view::graphics::Modifier;
39− /// let m = Modifier::BOLD | Modifier::ITALIC;
197+ /// let m = Modifier::BOLD | Modifier::ITALIC;
41− #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
199+ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42− pub struct Modifier: u16 {
200+ pub struct Modifier: u16 {
43− const BOLD = 0b0000_0000_0001;
201+ const BOLD = 0b0000_0000_0001;
44− const DIM = 0b0000_0000_0010;
202+ const DIM = 0b0000_0000_0010;
45− const ITALIC = 0b0000_0000_0100;
203+ const ITALIC = 0b0000_0000_0100;
46− const UNDERLINED = 0b0000_0000_1000;
204+ const UNDERLINED = 0b0000_0000_1000;
47− const SLOW_BLINK = 0b0000_0001_0000;
205+ const SLOW_BLINK = 0b0000_0001_0000;
48− const RAPID_BLINK = 0b0000_0010_0000;
206+ const RAPID_BLINK = 0b0000_0010_0000;
49− const REVERSED = 0b0000_0100_0000;
207+ const REVERSED = 0b0000_0100_0000;
50− const HIDDEN = 0b0000_1000_0000;
208+ const HIDDEN = 0b0000_1000_0000;
51− const CROSSED_OUT = 0b0001_0000_0000;
209+ const CROSSED_OUT = 0b0001_0000_0000;
55−/// Style let you control the main characteristics of the displayed elements.
213+/// Style let you control the main characteristics of the displayed elements.
57−/// ```rust
215+/// ```rust
58−/// # use helix_tui::style::{Color, Modifier, Style};
216+/// # use helix_view::graphics::{Color, Modifier, Style};
59−/// Style::default()
217+/// Style::default()
60−/// .fg(Color::Black)
218+/// .fg(Color::Black)
61−/// .bg(Color::Green)
219+/// .bg(Color::Green)
62−/// .add_modifier(Modifier::ITALIC | Modifier::BOLD);
220+/// .add_modifier(Modifier::ITALIC | Modifier::BOLD);
65−/// It represents an incremental change. If you apply the styles S1, S2, S3 to a cell of the
223+/// 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
224+/// terminal buffer, the style of this cell will be the result of the merge of S1, S2 and S3, not
67−/// just S3.
225+/// just S3.
69−/// ```rust
227+/// ```rust
70−/// # use helix_tui::style::{Color, Modifier, Style};
228+/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
71−/// # use helix_tui::buffer::Buffer;
229+/// # use helix_tui::buffer::Buffer;
72−/// # use helix_tui::layout::Rect;
73−/// let styles = [
230+/// let styles = [
74−/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
231+/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
75−/// Style::default().bg(Color::Red),
232+/// Style::default().bg(Color::Red),
76−/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
233+/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
78−/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
235+/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
79−/// for style in &styles {
236+/// for style in &styles {
80−/// buffer.get_mut(0, 0).set_style(*style);
237+/// buffer.get_mut(0, 0).set_style(*style);
82−/// assert_eq!(
239+/// assert_eq!(
83−/// Style {
240+/// Style {
84−/// fg: Some(Color::Yellow),
241+/// fg: Some(Color::Yellow),
85−/// bg: Some(Color::Red),
242+/// bg: Some(Color::Red),
86−/// add_modifier: Modifier::BOLD,
243+/// add_modifier: Modifier::BOLD,
87−/// sub_modifier: Modifier::empty(),
244+/// sub_modifier: Modifier::empty(),
89−/// buffer.get(0, 0).style(),
246+/// buffer.get(0, 0).style(),
93−/// The default implementation returns a `Style` that does not modify anything. If you wish to
250+/// 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`].
251+/// reset all properties until that point use [`Style::reset`].
97−/// # use helix_tui::style::{Color, Modifier, Style};
254+/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
98−/// # use helix_tui::buffer::Buffer;
255+/// # use helix_tui::buffer::Buffer;
99−/// # use helix_tui::layout::Rect;
100−/// let styles = [
256+/// let styles = [
101−/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
257+/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
102−/// Style::reset().fg(Color::Yellow),
258+/// Style::reset().fg(Color::Yellow),
104−/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
260+/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
105−/// for style in &styles {
261+/// for style in &styles {
106−/// buffer.get_mut(0, 0).set_style(*style);
262+/// buffer.get_mut(0, 0).set_style(*style);
108−/// assert_eq!(
264+/// assert_eq!(
109−/// Style {
265+/// Style {
110−/// fg: Some(Color::Yellow),
266+/// fg: Some(Color::Yellow),
111−/// bg: Some(Color::Reset),
267+/// bg: Some(Color::Reset),
112−/// add_modifier: Modifier::empty(),
268+/// add_modifier: Modifier::empty(),
113−/// sub_modifier: Modifier::empty(),
269+/// sub_modifier: Modifier::empty(),
115−/// buffer.get(0, 0).style(),
271+/// buffer.get(0, 0).style(),
118−#[derive(Debug, Clone, Copy, PartialEq)]
274+#[derive(Debug, Clone, Copy, PartialEq)]
119−#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
275+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
120−pub struct Style {
276+pub struct Style {
121− pub fg: Option<Color>,
277+ pub fg: Option<Color>,
122− pub bg: Option<Color>,
278+ pub bg: Option<Color>,
123− pub add_modifier: Modifier,
279+ pub add_modifier: Modifier,
124− pub sub_modifier: Modifier,
280+ pub sub_modifier: Modifier,
127−impl Default for Style {
283+impl Default for Style {
128− fn default() -> Style {
284+ fn default() -> Style {
130− fg: None,
286+ fg: None,
131− bg: None,
287+ bg: None,
132− add_modifier: Modifier::empty(),
288+ add_modifier: Modifier::empty(),
133− sub_modifier: Modifier::empty(),
289+ sub_modifier: Modifier::empty(),
138−impl Style {
294+impl Style {
139− /// Returns a `Style` resetting all properties.
295+ /// Returns a `Style` resetting all properties.
140− pub fn reset() -> Style {
296+ pub fn reset() -> Style {
142− fg: Some(Color::Reset),
298+ fg: Some(Color::Reset),
143− bg: Some(Color::Reset),
299+ bg: Some(Color::Reset),
144− add_modifier: Modifier::empty(),
300+ add_modifier: Modifier::empty(),
145− sub_modifier: Modifier::all(),
301+ sub_modifier: Modifier::all(),
149− /// Changes the foreground color.
305+ /// Changes the foreground color.
151− /// ## Examples
307+ /// ## Examples
153− /// ```rust
309+ /// ```rust
154− /// # use helix_tui::style::{Color, Style};
310+ /// # use helix_view::graphics::{Color, Style};
155− /// let style = Style::default().fg(Color::Blue);
311+ /// let style = Style::default().fg(Color::Blue);
156− /// let diff = Style::default().fg(Color::Red);
312+ /// let diff = Style::default().fg(Color::Red);
157− /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red));
313+ /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red));
159− pub fn fg(mut self, color: Color) -> Style {
315+ pub fn fg(mut self, color: Color) -> Style {
160− self.fg = Some(color);
316+ self.fg = Some(color);
164− /// Changes the background color.
320+ /// Changes the background color.
166− /// ## Examples
322+ /// ## Examples
168− /// ```rust
324+ /// ```rust
169− /// # use helix_tui::style::{Color, Style};
325+ /// # use helix_view::graphics::{Color, Style};
170− /// let style = Style::default().bg(Color::Blue);
326+ /// let style = Style::default().bg(Color::Blue);
171− /// let diff = Style::default().bg(Color::Red);
327+ /// let diff = Style::default().bg(Color::Red);
172− /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red));
328+ /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red));
174− pub fn bg(mut self, color: Color) -> Style {
330+ pub fn bg(mut self, color: Color) -> Style {
175− self.bg = Some(color);
331+ self.bg = Some(color);
179− /// Changes the text emphasis.
335+ /// Changes the text emphasis.
181− /// When applied, it adds the given modifier to the `Style` modifiers.
337+ /// When applied, it adds the given modifier to the `Style` modifiers.
183− /// ## Examples
339+ /// ## Examples
185− /// ```rust
341+ /// ```rust
186− /// # use helix_tui::style::{Color, Modifier, Style};
342+ /// # use helix_view::graphics::{Color, Modifier, Style};
187− /// let style = Style::default().add_modifier(Modifier::BOLD);
343+ /// let style = Style::default().add_modifier(Modifier::BOLD);
188− /// let diff = Style::default().add_modifier(Modifier::ITALIC);
344+ /// let diff = Style::default().add_modifier(Modifier::ITALIC);
189− /// let patched = style.patch(diff);
345+ /// let patched = style.patch(diff);
190− /// assert_eq!(patched.add_modifier, Modifier::BOLD | Modifier::ITALIC);
346+ /// assert_eq!(patched.add_modifier, Modifier::BOLD | Modifier::ITALIC);
191− /// assert_eq!(patched.sub_modifier, Modifier::empty());
347+ /// assert_eq!(patched.sub_modifier, Modifier::empty());
193− pub fn add_modifier(mut self, modifier: Modifier) -> Style {
349+ pub fn add_modifier(mut self, modifier: Modifier) -> Style {
194− self.sub_modifier.remove(modifier);
350+ self.sub_modifier.remove(modifier);
195− self.add_modifier.insert(modifier);
351+ self.add_modifier.insert(modifier);
199− /// Changes the text emphasis.
355+ /// Changes the text emphasis.
201− /// When applied, it removes the given modifier from the `Style` modifiers.
357+ /// When applied, it removes the given modifier from the `Style` modifiers.
203− /// ## Examples
359+ /// ## Examples
205− /// ```rust
361+ /// ```rust
206− /// # use helix_tui::style::{Color, Modifier, Style};
362+ /// # use helix_view::graphics::{Color, Modifier, Style};
207− /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
363+ /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
208− /// let diff = Style::default().remove_modifier(Modifier::ITALIC);
364+ /// let diff = Style::default().remove_modifier(Modifier::ITALIC);
209− /// let patched = style.patch(diff);
365+ /// let patched = style.patch(diff);
210− /// assert_eq!(patched.add_modifier, Modifier::BOLD);
366+ /// assert_eq!(patched.add_modifier, Modifier::BOLD);
211− /// assert_eq!(patched.sub_modifier, Modifier::ITALIC);
367+ /// assert_eq!(patched.sub_modifier, Modifier::ITALIC);
213− pub fn remove_modifier(mut self, modifier: Modifier) -> Style {
369+ pub fn remove_modifier(mut self, modifier: Modifier) -> Style {
214− self.add_modifier.remove(modifier);
370+ self.add_modifier.remove(modifier);
215− self.sub_modifier.insert(modifier);
371+ self.sub_modifier.insert(modifier);
219− /// Results in a combined style that is equivalent to applying the two individual styles to
375+ /// Results in a combined style that is equivalent to applying the two individual styles to
220− /// a style one after the other.
376+ /// a style one after the other.
222− /// ## Examples
378+ /// ## Examples
224− /// # use helix_tui::style::{Color, Modifier, Style};
380+ /// # use helix_view::graphics::{Color, Modifier, Style};
225− /// let style_1 = Style::default().fg(Color::Yellow);
381+ /// let style_1 = Style::default().fg(Color::Yellow);
226− /// let style_2 = Style::default().bg(Color::Red);
382+ /// let style_2 = Style::default().bg(Color::Red);
227− /// let combined = style_1.patch(style_2);
383+ /// let combined = style_1.patch(style_2);
228− /// assert_eq!(
384+ /// assert_eq!(
229− /// Style::default().patch(style_1).patch(style_2),
385+ /// Style::default().patch(style_1).patch(style_2),
230− /// Style::default().patch(combined));
386+ /// Style::default().patch(combined));
232− pub fn patch(mut self, other: Style) -> Style {
388+ pub fn patch(mut self, other: Style) -> Style {
233− self.fg = other.fg.or(self.fg);
389+ self.fg = other.fg.or(self.fg);
234− self.bg = other.bg.or(self.bg);
390+ self.bg = other.bg.or(self.bg);
236− self.add_modifier.remove(other.sub_modifier);
392+ self.add_modifier.remove(other.sub_modifier);
237− self.add_modifier.insert(other.add_modifier);
393+ self.add_modifier.insert(other.add_modifier);
238− self.sub_modifier.remove(other.add_modifier);
394+ self.sub_modifier.remove(other.add_modifier);
239− self.sub_modifier.insert(other.sub_modifier);
395+ self.sub_modifier.insert(other.sub_modifier);
245−#[cfg(test)]
401+#[cfg(test)]
246−mod tests {
402+mod tests {
247− use super::*;
403+ use super::*;
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.
415+ (f64::from(rect.width) / f64::from(rect.height)
416+ - f64::from(width) / f64::from(height))
423+ // One dimension below 255, one above. Area above max u16.
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);
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);
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);
249− fn styles() -> Vec<Style> {
449+ fn styles() -> Vec<Style> {
251− Style::default(),
451+ Style::default(),
252− Style::default().fg(Color::Yellow),
452+ Style::default().fg(Color::Yellow),
253− Style::default().bg(Color::Yellow),
453+ Style::default().bg(Color::Yellow),
254− Style::default().add_modifier(Modifier::BOLD),
454+ Style::default().add_modifier(Modifier::BOLD),
255− Style::default().remove_modifier(Modifier::BOLD),
455+ Style::default().remove_modifier(Modifier::BOLD),
256− Style::default().add_modifier(Modifier::ITALIC),
456+ Style::default().add_modifier(Modifier::ITALIC),
257− Style::default().remove_modifier(Modifier::ITALIC),
457+ Style::default().remove_modifier(Modifier::ITALIC),
258− Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
458+ Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
259− Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
459+ Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
264− fn combined_patch_gives_same_result_as_individual_patch() {
464+ fn combined_patch_gives_same_result_as_individual_patch() {
265− let styles = styles();
465+ let styles = styles();
266− for &a in &styles {
466+ for &a in &styles {
267− for &b in &styles {
467+ for &b in &styles {
268− for &c in &styles {
468+ for &c in &styles {
269− for &d in &styles {
469+ for &d in &styles {
270− let combined = a.patch(b.patch(c.patch(d)));
470+ let combined = a.patch(b.patch(c.patch(d)));
272− assert_eq!(
472+ assert_eq!(
273− Style::default().patch(a).patch(b).patch(c).patch(d),
473+ Style::default().patch(a).patch(b).patch(c).patch(d),
274− Style::default().patch(combined)
474+ Style::default().patch(combined)