7#[derive(Debug, Clone, Copy, PartialEq)]
16 /// Hidden cursor, can set cursor position with this to let IME have correct cursor position.
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
27/// area they are supposed to render to.
28#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
36impl Default for Rect {
37 fn default() -> Rect {
48 /// Creates a new rect, with width and height limited to keep the area under max u16.
49 /// If clipped, aspect ratio will be preserved.
50 pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
51 let max_area = u16::max_value();
52 let (clipped_width, clipped_height) =
53 if u32::from(width) * u32::from(height) > u32::from(max_area) {
54 let aspect_ratio = f64::from(width) / f64::from(height);
55 let max_area_f = f64::from(max_area);
56 let height_f = (max_area_f / aspect_ratio).sqrt();
57 let width_f = height_f * aspect_ratio;
58 (width_f as u16, height_f as u16)
66 height: clipped_height,
71 pub fn area(self) -> u16 {
72 self.width * self.height
76 pub fn left(self) -> u16 {
81 pub fn right(self) -> u16 {
82 self.x.saturating_add(self.width)
86 pub fn top(self) -> u16 {
91 pub fn bottom(self) -> u16 {
92 self.y.saturating_add(self.height)
95 pub fn inner(self, margin: &Margin) -> Rect {
96 if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
100 x: self.x + margin.horizontal,
101 y: self.y + margin.vertical,
102 width: self.width - 2 * margin.horizontal,
103 height: self.height - 2 * margin.vertical,
108 /// Calculate the union between two [`Rect`]s.
109 pub fn union(self, other: Rect) -> Rect {
112 // If `Rect` A is positioned at `(0, 0)` with a width and height of `5`,
113 // and `Rect` B is positioned at `(5, 0)` with a width and height of `2`,
114 // then this is the resulting union:
116 // x1 = min(0, 5) => x1 = 0
117 // y1 = min(0, 0) => y1 = 0
118 // x2 = max(0 + 5, 5 + 2) => x2 = 7
119 // y2 = max(0 + 5, 0 + 2) => y2 = 5
120 let x1 = min(self.x, other.x);
121 let y1 = min(self.y, other.y);
122 let x2 = max(self.x + self.width, other.x + other.width);
123 let y2 = max(self.y + self.height, other.y + other.height);
132 /// Calculate the intersection between two [`Rect`]s.
133 pub fn intersection(self, other: Rect) -> Rect {
136 // If `Rect` A is positioned at `(0, 0)` with a width and height of `5`,
137 // and `Rect` B is positioned at `(5, 0)` with a width and height of `2`,
138 // then this is the resulting intersection:
140 // x1 = max(0, 5) => x1 = 5
141 // y1 = max(0, 0) => y1 = 0
142 // x2 = min(0 + 5, 5 + 2) => x2 = 5
143 // y2 = min(0 + 5, 0 + 2) => y2 = 2
144 let x1 = max(self.x, other.x);
145 let y1 = max(self.y, other.y);
146 let x2 = min(self.x + self.width, other.x + other.width);
147 let y2 = min(self.y + self.height, other.y + other.height);
156 pub fn intersects(self, other: Rect) -> bool {
157 self.x < other.x + other.width
158 && self.x + self.width > other.x
159 && self.y < other.y + other.height
160 && self.y + self.height > other.y
164#[derive(Debug, Clone, Copy, PartialEq)]
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
188#[cfg(feature = "term")]
189impl From<Color> for crossterm::style::Color {
190 fn from(color: Color) -> Self {
191 use crossterm::style::Color as CColor;
194 Color::Reset => CColor::Reset,
195 Color::Black => CColor::Black,
196 Color::Red => CColor::DarkRed,
197 Color::Green => CColor::DarkGreen,
198 Color::Yellow => CColor::DarkYellow,
199 Color::Blue => CColor::DarkBlue,
200 Color::Magenta => CColor::DarkMagenta,
201 Color::Cyan => CColor::DarkCyan,
202 Color::Gray => CColor::Grey,
203 Color::DarkGray => CColor::DarkGrey,
204 Color::LightRed => CColor::Red,
205 Color::LightGreen => CColor::Green,
206 Color::LightBlue => CColor::Blue,
207 Color::LightYellow => CColor::Yellow,
208 Color::LightMagenta => CColor::Magenta,
209 Color::LightCyan => CColor::Cyan,
210 Color::White => CColor::White,
211 Color::Indexed(i) => CColor::AnsiValue(i),
212 Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
218 /// Modifier changes the way a piece of text is displayed.
220 /// They are bitflags so they can easily be composed.
225 /// # use helix_view::graphics::Modifier;
227 /// let m = Modifier::BOLD | Modifier::ITALIC;
229 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
230 pub struct Modifier: u16 {
231 const BOLD = 0b0000_0000_0001;
232 const DIM = 0b0000_0000_0010;
233 const ITALIC = 0b0000_0000_0100;
234 const UNDERLINED = 0b0000_0000_1000;
235 const SLOW_BLINK = 0b0000_0001_0000;
236 const RAPID_BLINK = 0b0000_0010_0000;
237 const REVERSED = 0b0000_0100_0000;
238 const HIDDEN = 0b0000_1000_0000;
239 const CROSSED_OUT = 0b0001_0000_0000;
243impl FromStr for Modifier {
244 type Err = &'static str;
246 fn from_str(modifier: &str) -> Result<Self, Self::Err> {
248 "bold" => Ok(Self::BOLD),
249 "dim" => Ok(Self::DIM),
250 "italic" => Ok(Self::ITALIC),
251 "underlined" => Ok(Self::UNDERLINED),
252 "slow_blink" => Ok(Self::SLOW_BLINK),
253 "rapid_blink" => Ok(Self::RAPID_BLINK),
254 "reversed" => Ok(Self::REVERSED),
255 "hidden" => Ok(Self::HIDDEN),
256 "crossed_out" => Ok(Self::CROSSED_OUT),
257 _ => Err("Invalid modifier"),
262/// Style let you control the main characteristics of the displayed elements.
265/// # use helix_view::graphics::{Color, Modifier, Style};
269/// .add_modifier(Modifier::ITALIC | Modifier::BOLD);
272/// It represents an incremental change. If you apply the styles S1, S2, S3 to a cell of the
273/// terminal buffer, the style of this cell will be the result of the merge of S1, S2 and S3, not
277/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
278/// # use helix_tui::buffer::Buffer;
280/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
281/// Style::default().bg(Color::Red),
282/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
284/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
285/// for style in &styles {
286/// buffer.get_mut(0, 0).set_style(*style);
290/// fg: Some(Color::Yellow),
291/// bg: Some(Color::Red),
292/// add_modifier: Modifier::BOLD,
293/// sub_modifier: Modifier::empty(),
295/// buffer.get(0, 0).style(),
299/// The default implementation returns a `Style` that does not modify anything. If you wish to
300/// reset all properties until that point use [`Style::reset`].
303/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
304/// # use helix_tui::buffer::Buffer;
306/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
307/// Style::reset().fg(Color::Yellow),
309/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
310/// for style in &styles {
311/// buffer.get_mut(0, 0).set_style(*style);
315/// fg: Some(Color::Yellow),
316/// bg: Some(Color::Reset),
317/// add_modifier: Modifier::empty(),
318/// sub_modifier: Modifier::empty(),
320/// buffer.get(0, 0).style(),
323#[derive(Debug, Clone, Copy, PartialEq)]
324#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
326 pub fg: Option<Color>,
327 pub bg: Option<Color>,
328 pub add_modifier: Modifier,
329 pub sub_modifier: Modifier,
332impl Default for Style {
333 fn default() -> Style {
337 add_modifier: Modifier::empty(),
338 sub_modifier: Modifier::empty(),
344 /// Returns a `Style` resetting all properties.
345 pub fn reset() -> Style {
347 fg: Some(Color::Reset),
348 bg: Some(Color::Reset),
349 add_modifier: Modifier::empty(),
350 sub_modifier: Modifier::all(),
354 /// Changes the foreground color.
359 /// # use helix_view::graphics::{Color, Style};
360 /// let style = Style::default().fg(Color::Blue);
361 /// let diff = Style::default().fg(Color::Red);
362 /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red));
364 pub fn fg(mut self, color: Color) -> Style {
365 self.fg = Some(color);
369 /// Changes the background color.
374 /// # use helix_view::graphics::{Color, Style};
375 /// let style = Style::default().bg(Color::Blue);
376 /// let diff = Style::default().bg(Color::Red);
377 /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red));
379 pub fn bg(mut self, color: Color) -> Style {
380 self.bg = Some(color);
384 /// Changes the text emphasis.
386 /// When applied, it adds the given modifier to the `Style` modifiers.
391 /// # use helix_view::graphics::{Color, Modifier, Style};
392 /// let style = Style::default().add_modifier(Modifier::BOLD);
393 /// let diff = Style::default().add_modifier(Modifier::ITALIC);
394 /// let patched = style.patch(diff);
395 /// assert_eq!(patched.add_modifier, Modifier::BOLD | Modifier::ITALIC);
396 /// assert_eq!(patched.sub_modifier, Modifier::empty());
398 pub fn add_modifier(mut self, modifier: Modifier) -> Style {
399 self.sub_modifier.remove(modifier);
400 self.add_modifier.insert(modifier);
404 /// Changes the text emphasis.
406 /// When applied, it removes the given modifier from the `Style` modifiers.
411 /// # use helix_view::graphics::{Color, Modifier, Style};
412 /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
413 /// let diff = Style::default().remove_modifier(Modifier::ITALIC);
414 /// let patched = style.patch(diff);
415 /// assert_eq!(patched.add_modifier, Modifier::BOLD);
416 /// assert_eq!(patched.sub_modifier, Modifier::ITALIC);
418 pub fn remove_modifier(mut self, modifier: Modifier) -> Style {
419 self.add_modifier.remove(modifier);
420 self.sub_modifier.insert(modifier);
424 /// Results in a combined style that is equivalent to applying the two individual styles to
425 /// a style one after the other.
429 /// # use helix_view::graphics::{Color, Modifier, Style};
430 /// let style_1 = Style::default().fg(Color::Yellow);
431 /// let style_2 = Style::default().bg(Color::Red);
432 /// let combined = style_1.patch(style_2);
434 /// Style::default().patch(style_1).patch(style_2),
435 /// Style::default().patch(combined));
437 pub fn patch(mut self, other: Style) -> Style {
438 self.fg = other.fg.or(self.fg);
439 self.bg = other.bg.or(self.bg);
441 self.add_modifier.remove(other.sub_modifier);
442 self.add_modifier.insert(other.add_modifier);
443 self.sub_modifier.remove(other.add_modifier);
444 self.sub_modifier.insert(other.sub_modifier);
455 fn test_rect_size_truncation() {
456 for width in 256u16..300u16 {
457 for height in 256u16..300u16 {
458 let rect = Rect::new(0, 0, width, height);
459 rect.area(); // Should not panic.
460 assert!(rect.width < width || rect.height < height);
461 // The target dimensions are rounded down so the math will not be too precise
462 // but let's make sure the ratios don't diverge crazily.
464 (f64::from(rect.width) / f64::from(rect.height)
465 - f64::from(width) / f64::from(height))
472 // One dimension below 255, one above. Area above max u16.
475 let rect = Rect::new(0, 0, width, height);
476 assert_ne!(rect.width, 900);
477 assert_ne!(rect.height, 100);
478 assert!(rect.width < width || rect.height < height);
482 fn test_rect_size_preservation() {
483 for width in 0..256u16 {
484 for height in 0..256u16 {
485 let rect = Rect::new(0, 0, width, height);
486 rect.area(); // Should not panic.
487 assert_eq!(rect.width, width);
488 assert_eq!(rect.height, height);
492 // One dimension below 255, one above. Area below max u16.
493 let rect = Rect::new(0, 0, 300, 100);
494 assert_eq!(rect.width, 300);
495 assert_eq!(rect.height, 100);
498 fn styles() -> Vec<Style> {
501 Style::default().fg(Color::Yellow),
502 Style::default().bg(Color::Yellow),
503 Style::default().add_modifier(Modifier::BOLD),
504 Style::default().remove_modifier(Modifier::BOLD),
505 Style::default().add_modifier(Modifier::ITALIC),
506 Style::default().remove_modifier(Modifier::ITALIC),
507 Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
508 Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
513 fn combined_patch_gives_same_result_as_individual_patch() {
514 let styles = styles();
519 let combined = a.patch(b.patch(c.patch(d)));
522 Style::default().patch(a).patch(b).patch(c).patch(d),
523 Style::default().patch(combined)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list