on master help sign in
Fix LF line-endings (#3316)
commit4b6c4ae6ee5d13be147c27e5e3d53e465968dd2f authorCharlie Groves <c@sevorg.org> dateWed 3 Aug 2022, 12:53 (4 years ago) parent6b244e2
2 files changed+818−818
helix-view/src/graphics.rs+665−665 helix-view/src/keyboard.rs+153−153
helix-view/src/graphics.rs+665−665
@@ -1,665 +1,665 @@
1−use bitflags::bitflags;
2−use serde::{Deserialize, Serialize};
3−use std::{
4− cmp::{max, min},
5− str::FromStr,
6−};
7−
8−#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
9−#[serde(rename_all = "lowercase")]
10−/// UNSTABLE
11−pub enum CursorKind {
12− /// █
13− Block,
14− /// |
15− Bar,
16− /// _
17− Underline,
18− /// Hidden cursor, can set cursor position with this to let IME have correct cursor position.
19− Hidden,
20−}
21−
22−impl Default for CursorKind {
23− fn default() -> Self {
24− Self::Block
25− }
26−}
27−
28−#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29−pub struct Margin {
30− pub left: u16,
31− pub right: u16,
32− pub top: u16,
33− pub bottom: u16,
34−}
35−
36−impl Margin {
37− pub fn none() -> Self {
38− Self {
39− left: 0,
40− right: 0,
41− top: 0,
42− bottom: 0,
43− }
44− }
45−
46− /// Set uniform margin for all sides.
47− pub fn all(value: u16) -> Self {
48− Self {
49− left: value,
50− right: value,
51− top: value,
52− bottom: value,
53− }
54− }
55−
56− /// Set the margin of left and right sides to specified value.
57− pub fn horizontal(value: u16) -> Self {
58− Self {
59− left: value,
60− right: value,
61− top: 0,
62− bottom: 0,
63− }
64− }
65−
66− /// Set the margin of top and bottom sides to specified value.
67− pub fn vertical(value: u16) -> Self {
68− Self {
69− left: 0,
70− right: 0,
71− top: value,
72− bottom: value,
73− }
74− }
75−
76− /// Get the total width of the margin (left + right)
77− pub fn width(&self) -> u16 {
78− self.left + self.right
79− }
80−
81− /// Get the total height of the margin (top + bottom)
82− pub fn height(&self) -> u16 {
83− self.top + self.bottom
84− }
85−}
86−
87−/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
88−/// area they are supposed to render to. (x, y) = (0, 0) is at the top left corner of the screen.
89−#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
90−pub struct Rect {
91− pub x: u16,
92− pub y: u16,
93− pub width: u16,
94− pub height: u16,
95−}
96−
97−impl Rect {
98− /// Creates a new rect, with width and height limited to keep the area under max u16.
99− /// If clipped, aspect ratio will be preserved.
100− pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
101− let max_area = u16::max_value();
102− let (clipped_width, clipped_height) =
103− if u32::from(width) * u32::from(height) > u32::from(max_area) {
104− let aspect_ratio = f64::from(width) / f64::from(height);
105− let max_area_f = f64::from(max_area);
106− let height_f = (max_area_f / aspect_ratio).sqrt();
107− let width_f = height_f * aspect_ratio;
108− (width_f as u16, height_f as u16)
109− } else {
110− (width, height)
111− };
112− Rect {
113− x,
114− y,
115− width: clipped_width,
116− height: clipped_height,
117− }
118− }
119−
120− #[inline]
121− pub fn area(self) -> u16 {
122− self.width * self.height
123− }
124−
125− #[inline]
126− pub fn left(self) -> u16 {
127− self.x
128− }
129−
130− #[inline]
131− pub fn right(self) -> u16 {
132− self.x.saturating_add(self.width)
133− }
134−
135− #[inline]
136− pub fn top(self) -> u16 {
137− self.y
138− }
139−
140− #[inline]
141− pub fn bottom(self) -> u16 {
142− self.y.saturating_add(self.height)
143− }
144−
145− // Returns a new Rect with width reduced from the left side.
146− // This changes the `x` coordinate and clamps it to the right
147− // edge of the original Rect.
148− pub fn clip_left(self, width: u16) -> Rect {
149− let width = std::cmp::min(width, self.width);
150− Rect {
151− x: self.x.saturating_add(width),
152− width: self.width.saturating_sub(width),
153− ..self
154− }
155− }
156−
157− // Returns a new Rect with width reduced from the right side.
158− // This does _not_ change the `x` coordinate.
159− pub fn clip_right(self, width: u16) -> Rect {
160− Rect {
161− width: self.width.saturating_sub(width),
162− ..self
163− }
164− }
165−
166− // Returns a new Rect with height reduced from the top.
167− // This changes the `y` coordinate and clamps it to the bottom
168− // edge of the original Rect.
169− pub fn clip_top(self, height: u16) -> Rect {
170− let height = std::cmp::min(height, self.height);
171− Rect {
172− y: self.y.saturating_add(height),
173− height: self.height.saturating_sub(height),
174− ..self
175− }
176− }
177−
178− // Returns a new Rect with height reduced from the bottom.
179− // This does _not_ change the `y` coordinate.
180− pub fn clip_bottom(self, height: u16) -> Rect {
181− Rect {
182− height: self.height.saturating_sub(height),
183− ..self
184− }
185− }
186−
187− pub fn with_height(self, height: u16) -> Rect {
188− // new height may make area > u16::max_value, so use new()
189− Self::new(self.x, self.y, self.width, height)
190− }
191−
192− pub fn with_width(self, width: u16) -> Rect {
193− Self::new(self.x, self.y, width, self.height)
194− }
195−
196− pub fn inner(self, margin: &Margin) -> Rect {
197− if self.width < margin.width() || self.height < margin.height() {
198− Rect::default()
199− } else {
200− Rect {
201− x: self.x + margin.left,
202− y: self.y + margin.top,
203− width: self.width - margin.width(),
204− height: self.height - margin.height(),
205− }
206− }
207− }
208−
209− /// Calculate the union between two [`Rect`]s.
210− pub fn union(self, other: Rect) -> Rect {
211− // Example:
212− //
213− // If `Rect` A is positioned at `(0, 0)` with a width and height of `5`,
214− // and `Rect` B is positioned at `(5, 0)` with a width and height of `2`,
215− // then this is the resulting union:
216− //
217− // x1 = min(0, 5) => x1 = 0
218− // y1 = min(0, 0) => y1 = 0
219− // x2 = max(0 + 5, 5 + 2) => x2 = 7
220− // y2 = max(0 + 5, 0 + 2) => y2 = 5
221− let x1 = min(self.x, other.x);
222− let y1 = min(self.y, other.y);
223− let x2 = max(self.x + self.width, other.x + other.width);
224− let y2 = max(self.y + self.height, other.y + other.height);
225− Rect {
226− x: x1,
227− y: y1,
228− width: x2 - x1,
229− height: y2 - y1,
230− }
231− }
232−
233− /// Calculate the intersection between two [`Rect`]s.
234− pub fn intersection(self, other: Rect) -> Rect {
235− // Example:
236− //
237− // If `Rect` A is positioned at `(0, 0)` with a width and height of `5`,
238− // and `Rect` B is positioned at `(5, 0)` with a width and height of `2`,
239− // then this is the resulting intersection:
240− //
241− // x1 = max(0, 5) => x1 = 5
242− // y1 = max(0, 0) => y1 = 0
243− // x2 = min(0 + 5, 5 + 2) => x2 = 5
244− // y2 = min(0 + 5, 0 + 2) => y2 = 2
245− let x1 = max(self.x, other.x);
246− let y1 = max(self.y, other.y);
247− let x2 = min(self.x + self.width, other.x + other.width);
248− let y2 = min(self.y + self.height, other.y + other.height);
249− Rect {
250− x: x1,
251− y: y1,
252− width: x2 - x1,
253− height: y2 - y1,
254− }
255− }
256−
257− pub fn intersects(self, other: Rect) -> bool {
258− self.x < other.x + other.width
259− && self.x + self.width > other.x
260− && self.y < other.y + other.height
261− && self.y + self.height > other.y
262− }
263−}
264−
265−#[derive(Debug, Clone, Copy, PartialEq)]
266−#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
267−pub enum Color {
268− Reset,
269− Black,
270− Red,
271− Green,
272− Yellow,
273− Blue,
274− Magenta,
275− Cyan,
276− Gray,
277− LightRed,
278− LightGreen,
279− LightYellow,
280− LightBlue,
281− LightMagenta,
282− LightCyan,
283− LightGray,
284− White,
285− Rgb(u8, u8, u8),
286− Indexed(u8),
287−}
288−
289−#[cfg(feature = "term")]
290−impl From<Color> for crossterm::style::Color {
291− fn from(color: Color) -> Self {
292− use crossterm::style::Color as CColor;
293−
294− match color {
295− Color::Reset => CColor::Reset,
296− Color::Black => CColor::Black,
297− Color::Red => CColor::DarkRed,
298− Color::Green => CColor::DarkGreen,
299− Color::Yellow => CColor::DarkYellow,
300− Color::Blue => CColor::DarkBlue,
301− Color::Magenta => CColor::DarkMagenta,
302− Color::Cyan => CColor::DarkCyan,
303− Color::Gray => CColor::DarkGrey,
304− Color::LightRed => CColor::Red,
305− Color::LightGreen => CColor::Green,
306− Color::LightBlue => CColor::Blue,
307− Color::LightYellow => CColor::Yellow,
308− Color::LightMagenta => CColor::Magenta,
309− Color::LightCyan => CColor::Cyan,
310− Color::LightGray => CColor::Grey,
311− Color::White => CColor::White,
312− Color::Indexed(i) => CColor::AnsiValue(i),
313− Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
314− }
315− }
316−}
317−
318−bitflags! {
319− /// Modifier changes the way a piece of text is displayed.
320− ///
321− /// They are bitflags so they can easily be composed.
322− ///
323− /// ## Examples
324− ///
325− /// ```rust
326− /// # use helix_view::graphics::Modifier;
327− ///
328− /// let m = Modifier::BOLD | Modifier::ITALIC;
329− /// ```
330− #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
331− pub struct Modifier: u16 {
332− const BOLD = 0b0000_0000_0001;
333− const DIM = 0b0000_0000_0010;
334− const ITALIC = 0b0000_0000_0100;
335− const UNDERLINED = 0b0000_0000_1000;
336− const SLOW_BLINK = 0b0000_0001_0000;
337− const RAPID_BLINK = 0b0000_0010_0000;
338− const REVERSED = 0b0000_0100_0000;
339− const HIDDEN = 0b0000_1000_0000;
340− const CROSSED_OUT = 0b0001_0000_0000;
341− }
342−}
343−
344−impl FromStr for Modifier {
345− type Err = &'static str;
346−
347− fn from_str(modifier: &str) -> Result<Self, Self::Err> {
348− match modifier {
349− "bold" => Ok(Self::BOLD),
350− "dim" => Ok(Self::DIM),
351− "italic" => Ok(Self::ITALIC),
352− "underlined" => Ok(Self::UNDERLINED),
353− "slow_blink" => Ok(Self::SLOW_BLINK),
354− "rapid_blink" => Ok(Self::RAPID_BLINK),
355− "reversed" => Ok(Self::REVERSED),
356− "hidden" => Ok(Self::HIDDEN),
357− "crossed_out" => Ok(Self::CROSSED_OUT),
358− _ => Err("Invalid modifier"),
359− }
360− }
361−}
362−
363−/// Style let you control the main characteristics of the displayed elements.
364−///
365−/// ```rust
366−/// # use helix_view::graphics::{Color, Modifier, Style};
367−/// Style::default()
368−/// .fg(Color::Black)
369−/// .bg(Color::Green)
370−/// .add_modifier(Modifier::ITALIC | Modifier::BOLD);
371−/// ```
372−///
373−/// It represents an incremental change. If you apply the styles S1, S2, S3 to a cell of the
374−/// terminal buffer, the style of this cell will be the result of the merge of S1, S2 and S3, not
375−/// just S3.
376−///
377−/// ```rust
378−/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
379−/// # use helix_tui::buffer::Buffer;
380−/// let styles = [
381−/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
382−/// Style::default().bg(Color::Red),
383−/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
384−/// ];
385−/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
386−/// for style in &styles {
387−/// buffer[(0, 0)].set_style(*style);
388−/// }
389−/// assert_eq!(
390−/// Style {
391−/// fg: Some(Color::Yellow),
392−/// bg: Some(Color::Red),
393−/// add_modifier: Modifier::BOLD,
394−/// sub_modifier: Modifier::empty(),
395−/// },
396−/// buffer[(0, 0)].style(),
397−/// );
398−/// ```
399−///
400−/// The default implementation returns a `Style` that does not modify anything. If you wish to
401−/// reset all properties until that point use [`Style::reset`].
402−///
403−/// ```
404−/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
405−/// # use helix_tui::buffer::Buffer;
406−/// let styles = [
407−/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
408−/// Style::reset().fg(Color::Yellow),
409−/// ];
410−/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
411−/// for style in &styles {
412−/// buffer[(0, 0)].set_style(*style);
413−/// }
414−/// assert_eq!(
415−/// Style {
416−/// fg: Some(Color::Yellow),
417−/// bg: Some(Color::Reset),
418−/// add_modifier: Modifier::empty(),
419−/// sub_modifier: Modifier::empty(),
420−/// },
421−/// buffer[(0, 0)].style(),
422−/// );
423−/// ```
424−#[derive(Debug, Clone, Copy, PartialEq)]
425−#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
426−pub struct Style {
427− pub fg: Option<Color>,
428− pub bg: Option<Color>,
429− pub add_modifier: Modifier,
430− pub sub_modifier: Modifier,
431−}
432−
433−impl Default for Style {
434− fn default() -> Style {
435− Style {
436− fg: None,
437− bg: None,
438− add_modifier: Modifier::empty(),
439− sub_modifier: Modifier::empty(),
440− }
441− }
442−}
443−
444−impl Style {
445− /// Returns a `Style` resetting all properties.
446− pub fn reset() -> Style {
447− Style {
448− fg: Some(Color::Reset),
449− bg: Some(Color::Reset),
450− add_modifier: Modifier::empty(),
451− sub_modifier: Modifier::all(),
452− }
453− }
454−
455− /// Changes the foreground color.
456− ///
457− /// ## Examples
458− ///
459− /// ```rust
460− /// # use helix_view::graphics::{Color, Style};
461− /// let style = Style::default().fg(Color::Blue);
462− /// let diff = Style::default().fg(Color::Red);
463− /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red));
464− /// ```
465− pub fn fg(mut self, color: Color) -> Style {
466− self.fg = Some(color);
467− self
468− }
469−
470− /// Changes the background color.
471− ///
472− /// ## Examples
473− ///
474− /// ```rust
475− /// # use helix_view::graphics::{Color, Style};
476− /// let style = Style::default().bg(Color::Blue);
477− /// let diff = Style::default().bg(Color::Red);
478− /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red));
479− /// ```
480− pub fn bg(mut self, color: Color) -> Style {
481− self.bg = Some(color);
482− self
483− }
484−
485− /// Changes the text emphasis.
486− ///
487− /// When applied, it adds the given modifier to the `Style` modifiers.
488− ///
489− /// ## Examples
490− ///
491− /// ```rust
492− /// # use helix_view::graphics::{Color, Modifier, Style};
493− /// let style = Style::default().add_modifier(Modifier::BOLD);
494− /// let diff = Style::default().add_modifier(Modifier::ITALIC);
495− /// let patched = style.patch(diff);
496− /// assert_eq!(patched.add_modifier, Modifier::BOLD | Modifier::ITALIC);
497− /// assert_eq!(patched.sub_modifier, Modifier::empty());
498− /// ```
499− pub fn add_modifier(mut self, modifier: Modifier) -> Style {
500− self.sub_modifier.remove(modifier);
501− self.add_modifier.insert(modifier);
502− self
503− }
504−
505− /// Changes the text emphasis.
506− ///
507− /// When applied, it removes the given modifier from the `Style` modifiers.
508− ///
509− /// ## Examples
510− ///
511− /// ```rust
512− /// # use helix_view::graphics::{Color, Modifier, Style};
513− /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
514− /// let diff = Style::default().remove_modifier(Modifier::ITALIC);
515− /// let patched = style.patch(diff);
516− /// assert_eq!(patched.add_modifier, Modifier::BOLD);
517− /// assert_eq!(patched.sub_modifier, Modifier::ITALIC);
518− /// ```
519− pub fn remove_modifier(mut self, modifier: Modifier) -> Style {
520− self.add_modifier.remove(modifier);
521− self.sub_modifier.insert(modifier);
522− self
523− }
524−
525− /// Results in a combined style that is equivalent to applying the two individual styles to
526− /// a style one after the other.
527− ///
528− /// ## Examples
529− /// ```
530− /// # use helix_view::graphics::{Color, Modifier, Style};
531− /// let style_1 = Style::default().fg(Color::Yellow);
532− /// let style_2 = Style::default().bg(Color::Red);
533− /// let combined = style_1.patch(style_2);
534− /// assert_eq!(
535− /// Style::default().patch(style_1).patch(style_2),
536− /// Style::default().patch(combined));
537− /// ```
538− pub fn patch(mut self, other: Style) -> Style {
539− self.fg = other.fg.or(self.fg);
540− self.bg = other.bg.or(self.bg);
541−
542− self.add_modifier.remove(other.sub_modifier);
543− self.add_modifier.insert(other.add_modifier);
544− self.sub_modifier.remove(other.add_modifier);
545− self.sub_modifier.insert(other.sub_modifier);
546−
547− self
548− }
549−}
550−
551−#[cfg(test)]
552−mod tests {
553− use super::*;
554−
555− #[test]
556− fn test_rect_size_truncation() {
557− for width in 256u16..300u16 {
558− for height in 256u16..300u16 {
559− let rect = Rect::new(0, 0, width, height);
560− rect.area(); // Should not panic.
561− assert!(rect.width < width || rect.height < height);
562− // The target dimensions are rounded down so the math will not be too precise
563− // but let's make sure the ratios don't diverge crazily.
564− assert!(
565− (f64::from(rect.width) / f64::from(rect.height)
566− - f64::from(width) / f64::from(height))
567− .abs()
568− < 1.0
569− )
570− }
571− }
572−
573− // One dimension below 255, one above. Area above max u16.
574− let width = 900;
575− let height = 100;
576− let rect = Rect::new(0, 0, width, height);
577− assert_ne!(rect.width, 900);
578− assert_ne!(rect.height, 100);
579− assert!(rect.width < width || rect.height < height);
580− }
581−
582− #[test]
583− fn test_rect_size_preservation() {
584− for width in 0..256u16 {
585− for height in 0..256u16 {
586− let rect = Rect::new(0, 0, width, height);
587− rect.area(); // Should not panic.
588− assert_eq!(rect.width, width);
589− assert_eq!(rect.height, height);
590− }
591− }
592−
593− // One dimension below 255, one above. Area below max u16.
594− let rect = Rect::new(0, 0, 300, 100);
595− assert_eq!(rect.width, 300);
596− assert_eq!(rect.height, 100);
597− }
598−
599− #[test]
600− fn test_rect_chop_from_left() {
601− let rect = Rect::new(0, 0, 20, 30);
602− assert_eq!(Rect::new(10, 0, 10, 30), rect.clip_left(10));
603− assert_eq!(
604− Rect::new(20, 0, 0, 30),
605− rect.clip_left(40),
606− "x should be clamped to original width if new width is bigger"
607− );
608− }
609−
610− #[test]
611− fn test_rect_chop_from_right() {
612− let rect = Rect::new(0, 0, 20, 30);
613− assert_eq!(Rect::new(0, 0, 10, 30), rect.clip_right(10));
614− }
615−
616− #[test]
617− fn test_rect_chop_from_top() {
618− let rect = Rect::new(0, 0, 20, 30);
619− assert_eq!(Rect::new(0, 10, 20, 20), rect.clip_top(10));
620− assert_eq!(
621− Rect::new(0, 30, 20, 0),
622− rect.clip_top(50),
623− "y should be clamped to original height if new height is bigger"
624− );
625− }
626−
627− #[test]
628− fn test_rect_chop_from_bottom() {
629− let rect = Rect::new(0, 0, 20, 30);
630− assert_eq!(Rect::new(0, 0, 20, 20), rect.clip_bottom(10));
631− }
632−
633− fn styles() -> Vec<Style> {
634− vec![
635− Style::default(),
636− Style::default().fg(Color::Yellow),
637− Style::default().bg(Color::Yellow),
638− Style::default().add_modifier(Modifier::BOLD),
639− Style::default().remove_modifier(Modifier::BOLD),
640− Style::default().add_modifier(Modifier::ITALIC),
641− Style::default().remove_modifier(Modifier::ITALIC),
642− Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
643− Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
644− ]
645− }
646−
647− #[test]
648− fn combined_patch_gives_same_result_as_individual_patch() {
649− let styles = styles();
650− for &a in &styles {
651− for &b in &styles {
652− for &c in &styles {
653− for &d in &styles {
654− let combined = a.patch(b.patch(c.patch(d)));
655−
656− assert_eq!(
657− Style::default().patch(a).patch(b).patch(c).patch(d),
658− Style::default().patch(combined)
659− );
660− }
661− }
662− }
663− }
664− }
665−}
1+use bitflags::bitflags;
2+use serde::{Deserialize, Serialize};
3+use std::{
4+ cmp::{max, min},
5+ str::FromStr,
6+};
7+
8+#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
9+#[serde(rename_all = "lowercase")]
10+/// UNSTABLE
11+pub enum CursorKind {
12+ /// █
13+ Block,
14+ /// |
15+ Bar,
16+ /// _
17+ Underline,
18+ /// Hidden cursor, can set cursor position with this to let IME have correct cursor position.
19+ Hidden,
20+}
21+
22+impl Default for CursorKind {
23+ fn default() -> Self {
24+ Self::Block
25+ }
26+}
27+
28+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29+pub struct Margin {
30+ pub left: u16,
31+ pub right: u16,
32+ pub top: u16,
33+ pub bottom: u16,
34+}
35+
36+impl Margin {
37+ pub fn none() -> Self {
38+ Self {
39+ left: 0,
40+ right: 0,
41+ top: 0,
42+ bottom: 0,
43+ }
44+ }
45+
46+ /// Set uniform margin for all sides.
47+ pub fn all(value: u16) -> Self {
48+ Self {
49+ left: value,
50+ right: value,
51+ top: value,
52+ bottom: value,
53+ }
54+ }
55+
56+ /// Set the margin of left and right sides to specified value.
57+ pub fn horizontal(value: u16) -> Self {
58+ Self {
59+ left: value,
60+ right: value,
61+ top: 0,
62+ bottom: 0,
63+ }
64+ }
65+
66+ /// Set the margin of top and bottom sides to specified value.
67+ pub fn vertical(value: u16) -> Self {
68+ Self {
69+ left: 0,
70+ right: 0,
71+ top: value,
72+ bottom: value,
73+ }
74+ }
75+
76+ /// Get the total width of the margin (left + right)
77+ pub fn width(&self) -> u16 {
78+ self.left + self.right
79+ }
80+
81+ /// Get the total height of the margin (top + bottom)
82+ pub fn height(&self) -> u16 {
83+ self.top + self.bottom
84+ }
85+}
86+
87+/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
88+/// area they are supposed to render to. (x, y) = (0, 0) is at the top left corner of the screen.
89+#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
90+pub struct Rect {
91+ pub x: u16,
92+ pub y: u16,
93+ pub width: u16,
94+ pub height: u16,
95+}
96+
97+impl Rect {
98+ /// Creates a new rect, with width and height limited to keep the area under max u16.
99+ /// If clipped, aspect ratio will be preserved.
100+ pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
101+ let max_area = u16::max_value();
102+ let (clipped_width, clipped_height) =
103+ if u32::from(width) * u32::from(height) > u32::from(max_area) {
104+ let aspect_ratio = f64::from(width) / f64::from(height);
105+ let max_area_f = f64::from(max_area);
106+ let height_f = (max_area_f / aspect_ratio).sqrt();
107+ let width_f = height_f * aspect_ratio;
108+ (width_f as u16, height_f as u16)
109+ } else {
110+ (width, height)
111+ };
112+ Rect {
113+ x,
114+ y,
115+ width: clipped_width,
116+ height: clipped_height,
117+ }
118+ }
119+
120+ #[inline]
121+ pub fn area(self) -> u16 {
122+ self.width * self.height
123+ }
124+
125+ #[inline]
126+ pub fn left(self) -> u16 {
127+ self.x
128+ }
129+
130+ #[inline]
131+ pub fn right(self) -> u16 {
132+ self.x.saturating_add(self.width)
133+ }
134+
135+ #[inline]
136+ pub fn top(self) -> u16 {
137+ self.y
138+ }
139+
140+ #[inline]
141+ pub fn bottom(self) -> u16 {
142+ self.y.saturating_add(self.height)
143+ }
144+
145+ // Returns a new Rect with width reduced from the left side.
146+ // This changes the `x` coordinate and clamps it to the right
147+ // edge of the original Rect.
148+ pub fn clip_left(self, width: u16) -> Rect {
149+ let width = std::cmp::min(width, self.width);
150+ Rect {
151+ x: self.x.saturating_add(width),
152+ width: self.width.saturating_sub(width),
153+ ..self
154+ }
155+ }
156+
157+ // Returns a new Rect with width reduced from the right side.
158+ // This does _not_ change the `x` coordinate.
159+ pub fn clip_right(self, width: u16) -> Rect {
160+ Rect {
161+ width: self.width.saturating_sub(width),
162+ ..self
163+ }
164+ }
165+
166+ // Returns a new Rect with height reduced from the top.
167+ // This changes the `y` coordinate and clamps it to the bottom
168+ // edge of the original Rect.
169+ pub fn clip_top(self, height: u16) -> Rect {
170+ let height = std::cmp::min(height, self.height);
171+ Rect {
172+ y: self.y.saturating_add(height),
173+ height: self.height.saturating_sub(height),
174+ ..self
175+ }
176+ }
177+
178+ // Returns a new Rect with height reduced from the bottom.
179+ // This does _not_ change the `y` coordinate.
180+ pub fn clip_bottom(self, height: u16) -> Rect {
181+ Rect {
182+ height: self.height.saturating_sub(height),
183+ ..self
184+ }
185+ }
186+
187+ pub fn with_height(self, height: u16) -> Rect {
188+ // new height may make area > u16::max_value, so use new()
189+ Self::new(self.x, self.y, self.width, height)
190+ }
191+
192+ pub fn with_width(self, width: u16) -> Rect {
193+ Self::new(self.x, self.y, width, self.height)
194+ }
195+
196+ pub fn inner(self, margin: &Margin) -> Rect {
197+ if self.width < margin.width() || self.height < margin.height() {
198+ Rect::default()
199+ } else {
200+ Rect {
201+ x: self.x + margin.left,
202+ y: self.y + margin.top,
203+ width: self.width - margin.width(),
204+ height: self.height - margin.height(),
205+ }
206+ }
207+ }
208+
209+ /// Calculate the union between two [`Rect`]s.
210+ pub fn union(self, other: Rect) -> Rect {
211+ // Example:
212+ //
213+ // If `Rect` A is positioned at `(0, 0)` with a width and height of `5`,
214+ // and `Rect` B is positioned at `(5, 0)` with a width and height of `2`,
215+ // then this is the resulting union:
216+ //
217+ // x1 = min(0, 5) => x1 = 0
218+ // y1 = min(0, 0) => y1 = 0
219+ // x2 = max(0 + 5, 5 + 2) => x2 = 7
220+ // y2 = max(0 + 5, 0 + 2) => y2 = 5
221+ let x1 = min(self.x, other.x);
222+ let y1 = min(self.y, other.y);
223+ let x2 = max(self.x + self.width, other.x + other.width);
224+ let y2 = max(self.y + self.height, other.y + other.height);
225+ Rect {
226+ x: x1,
227+ y: y1,
228+ width: x2 - x1,
229+ height: y2 - y1,
230+ }
231+ }
232+
233+ /// Calculate the intersection between two [`Rect`]s.
234+ pub fn intersection(self, other: Rect) -> Rect {
235+ // Example:
236+ //
237+ // If `Rect` A is positioned at `(0, 0)` with a width and height of `5`,
238+ // and `Rect` B is positioned at `(5, 0)` with a width and height of `2`,
239+ // then this is the resulting intersection:
240+ //
241+ // x1 = max(0, 5) => x1 = 5
242+ // y1 = max(0, 0) => y1 = 0
243+ // x2 = min(0 + 5, 5 + 2) => x2 = 5
244+ // y2 = min(0 + 5, 0 + 2) => y2 = 2
245+ let x1 = max(self.x, other.x);
246+ let y1 = max(self.y, other.y);
247+ let x2 = min(self.x + self.width, other.x + other.width);
248+ let y2 = min(self.y + self.height, other.y + other.height);
249+ Rect {
250+ x: x1,
251+ y: y1,
252+ width: x2 - x1,
253+ height: y2 - y1,
254+ }
255+ }
256+
257+ pub fn intersects(self, other: Rect) -> bool {
258+ self.x < other.x + other.width
259+ && self.x + self.width > other.x
260+ && self.y < other.y + other.height
261+ && self.y + self.height > other.y
262+ }
263+}
264+
265+#[derive(Debug, Clone, Copy, PartialEq)]
266+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
267+pub enum Color {
268+ Reset,
269+ Black,
270+ Red,
271+ Green,
272+ Yellow,
273+ Blue,
274+ Magenta,
275+ Cyan,
276+ Gray,
277+ LightRed,
278+ LightGreen,
279+ LightYellow,
280+ LightBlue,
281+ LightMagenta,
282+ LightCyan,
283+ LightGray,
284+ White,
285+ Rgb(u8, u8, u8),
286+ Indexed(u8),
287+}
288+
289+#[cfg(feature = "term")]
290+impl From<Color> for crossterm::style::Color {
291+ fn from(color: Color) -> Self {
292+ use crossterm::style::Color as CColor;
293+
294+ match color {
295+ Color::Reset => CColor::Reset,
296+ Color::Black => CColor::Black,
297+ Color::Red => CColor::DarkRed,
298+ Color::Green => CColor::DarkGreen,
299+ Color::Yellow => CColor::DarkYellow,
300+ Color::Blue => CColor::DarkBlue,
301+ Color::Magenta => CColor::DarkMagenta,
302+ Color::Cyan => CColor::DarkCyan,
303+ Color::Gray => CColor::DarkGrey,
304+ Color::LightRed => CColor::Red,
305+ Color::LightGreen => CColor::Green,
306+ Color::LightBlue => CColor::Blue,
307+ Color::LightYellow => CColor::Yellow,
308+ Color::LightMagenta => CColor::Magenta,
309+ Color::LightCyan => CColor::Cyan,
310+ Color::LightGray => CColor::Grey,
311+ Color::White => CColor::White,
312+ Color::Indexed(i) => CColor::AnsiValue(i),
313+ Color::Rgb(r, g, b) => CColor::Rgb { r, g, b },
314+ }
315+ }
316+}
317+
318+bitflags! {
319+ /// Modifier changes the way a piece of text is displayed.
320+ ///
321+ /// They are bitflags so they can easily be composed.
322+ ///
323+ /// ## Examples
324+ ///
325+ /// ```rust
326+ /// # use helix_view::graphics::Modifier;
327+ ///
328+ /// let m = Modifier::BOLD | Modifier::ITALIC;
329+ /// ```
330+ #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
331+ pub struct Modifier: u16 {
332+ const BOLD = 0b0000_0000_0001;
333+ const DIM = 0b0000_0000_0010;
334+ const ITALIC = 0b0000_0000_0100;
335+ const UNDERLINED = 0b0000_0000_1000;
336+ const SLOW_BLINK = 0b0000_0001_0000;
337+ const RAPID_BLINK = 0b0000_0010_0000;
338+ const REVERSED = 0b0000_0100_0000;
339+ const HIDDEN = 0b0000_1000_0000;
340+ const CROSSED_OUT = 0b0001_0000_0000;
341+ }
342+}
343+
344+impl FromStr for Modifier {
345+ type Err = &'static str;
346+
347+ fn from_str(modifier: &str) -> Result<Self, Self::Err> {
348+ match modifier {
349+ "bold" => Ok(Self::BOLD),
350+ "dim" => Ok(Self::DIM),
351+ "italic" => Ok(Self::ITALIC),
352+ "underlined" => Ok(Self::UNDERLINED),
353+ "slow_blink" => Ok(Self::SLOW_BLINK),
354+ "rapid_blink" => Ok(Self::RAPID_BLINK),
355+ "reversed" => Ok(Self::REVERSED),
356+ "hidden" => Ok(Self::HIDDEN),
357+ "crossed_out" => Ok(Self::CROSSED_OUT),
358+ _ => Err("Invalid modifier"),
359+ }
360+ }
361+}
362+
363+/// Style let you control the main characteristics of the displayed elements.
364+///
365+/// ```rust
366+/// # use helix_view::graphics::{Color, Modifier, Style};
367+/// Style::default()
368+/// .fg(Color::Black)
369+/// .bg(Color::Green)
370+/// .add_modifier(Modifier::ITALIC | Modifier::BOLD);
371+/// ```
372+///
373+/// It represents an incremental change. If you apply the styles S1, S2, S3 to a cell of the
374+/// terminal buffer, the style of this cell will be the result of the merge of S1, S2 and S3, not
375+/// just S3.
376+///
377+/// ```rust
378+/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
379+/// # use helix_tui::buffer::Buffer;
380+/// let styles = [
381+/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
382+/// Style::default().bg(Color::Red),
383+/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
384+/// ];
385+/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
386+/// for style in &styles {
387+/// buffer[(0, 0)].set_style(*style);
388+/// }
389+/// assert_eq!(
390+/// Style {
391+/// fg: Some(Color::Yellow),
392+/// bg: Some(Color::Red),
393+/// add_modifier: Modifier::BOLD,
394+/// sub_modifier: Modifier::empty(),
395+/// },
396+/// buffer[(0, 0)].style(),
397+/// );
398+/// ```
399+///
400+/// The default implementation returns a `Style` that does not modify anything. If you wish to
401+/// reset all properties until that point use [`Style::reset`].
402+///
403+/// ```
404+/// # use helix_view::graphics::{Rect, Color, Modifier, Style};
405+/// # use helix_tui::buffer::Buffer;
406+/// let styles = [
407+/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
408+/// Style::reset().fg(Color::Yellow),
409+/// ];
410+/// let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
411+/// for style in &styles {
412+/// buffer[(0, 0)].set_style(*style);
413+/// }
414+/// assert_eq!(
415+/// Style {
416+/// fg: Some(Color::Yellow),
417+/// bg: Some(Color::Reset),
418+/// add_modifier: Modifier::empty(),
419+/// sub_modifier: Modifier::empty(),
420+/// },
421+/// buffer[(0, 0)].style(),
422+/// );
423+/// ```
424+#[derive(Debug, Clone, Copy, PartialEq)]
425+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
426+pub struct Style {
427+ pub fg: Option<Color>,
428+ pub bg: Option<Color>,
429+ pub add_modifier: Modifier,
430+ pub sub_modifier: Modifier,
431+}
432+
433+impl Default for Style {
434+ fn default() -> Style {
435+ Style {
436+ fg: None,
437+ bg: None,
438+ add_modifier: Modifier::empty(),
439+ sub_modifier: Modifier::empty(),
440+ }
441+ }
442+}
443+
444+impl Style {
445+ /// Returns a `Style` resetting all properties.
446+ pub fn reset() -> Style {
447+ Style {
448+ fg: Some(Color::Reset),
449+ bg: Some(Color::Reset),
450+ add_modifier: Modifier::empty(),
451+ sub_modifier: Modifier::all(),
452+ }
453+ }
454+
455+ /// Changes the foreground color.
456+ ///
457+ /// ## Examples
458+ ///
459+ /// ```rust
460+ /// # use helix_view::graphics::{Color, Style};
461+ /// let style = Style::default().fg(Color::Blue);
462+ /// let diff = Style::default().fg(Color::Red);
463+ /// assert_eq!(style.patch(diff), Style::default().fg(Color::Red));
464+ /// ```
465+ pub fn fg(mut self, color: Color) -> Style {
466+ self.fg = Some(color);
467+ self
468+ }
469+
470+ /// Changes the background color.
471+ ///
472+ /// ## Examples
473+ ///
474+ /// ```rust
475+ /// # use helix_view::graphics::{Color, Style};
476+ /// let style = Style::default().bg(Color::Blue);
477+ /// let diff = Style::default().bg(Color::Red);
478+ /// assert_eq!(style.patch(diff), Style::default().bg(Color::Red));
479+ /// ```
480+ pub fn bg(mut self, color: Color) -> Style {
481+ self.bg = Some(color);
482+ self
483+ }
484+
485+ /// Changes the text emphasis.
486+ ///
487+ /// When applied, it adds the given modifier to the `Style` modifiers.
488+ ///
489+ /// ## Examples
490+ ///
491+ /// ```rust
492+ /// # use helix_view::graphics::{Color, Modifier, Style};
493+ /// let style = Style::default().add_modifier(Modifier::BOLD);
494+ /// let diff = Style::default().add_modifier(Modifier::ITALIC);
495+ /// let patched = style.patch(diff);
496+ /// assert_eq!(patched.add_modifier, Modifier::BOLD | Modifier::ITALIC);
497+ /// assert_eq!(patched.sub_modifier, Modifier::empty());
498+ /// ```
499+ pub fn add_modifier(mut self, modifier: Modifier) -> Style {
500+ self.sub_modifier.remove(modifier);
501+ self.add_modifier.insert(modifier);
502+ self
503+ }
504+
505+ /// Changes the text emphasis.
506+ ///
507+ /// When applied, it removes the given modifier from the `Style` modifiers.
508+ ///
509+ /// ## Examples
510+ ///
511+ /// ```rust
512+ /// # use helix_view::graphics::{Color, Modifier, Style};
513+ /// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
514+ /// let diff = Style::default().remove_modifier(Modifier::ITALIC);
515+ /// let patched = style.patch(diff);
516+ /// assert_eq!(patched.add_modifier, Modifier::BOLD);
517+ /// assert_eq!(patched.sub_modifier, Modifier::ITALIC);
518+ /// ```
519+ pub fn remove_modifier(mut self, modifier: Modifier) -> Style {
520+ self.add_modifier.remove(modifier);
521+ self.sub_modifier.insert(modifier);
522+ self
523+ }
524+
525+ /// Results in a combined style that is equivalent to applying the two individual styles to
526+ /// a style one after the other.
527+ ///
528+ /// ## Examples
529+ /// ```
530+ /// # use helix_view::graphics::{Color, Modifier, Style};
531+ /// let style_1 = Style::default().fg(Color::Yellow);
532+ /// let style_2 = Style::default().bg(Color::Red);
533+ /// let combined = style_1.patch(style_2);
534+ /// assert_eq!(
535+ /// Style::default().patch(style_1).patch(style_2),
536+ /// Style::default().patch(combined));
537+ /// ```
538+ pub fn patch(mut self, other: Style) -> Style {
539+ self.fg = other.fg.or(self.fg);
540+ self.bg = other.bg.or(self.bg);
541+
542+ self.add_modifier.remove(other.sub_modifier);
543+ self.add_modifier.insert(other.add_modifier);
544+ self.sub_modifier.remove(other.add_modifier);
545+ self.sub_modifier.insert(other.sub_modifier);
546+
547+ self
548+ }
549+}
550+
551+#[cfg(test)]
552+mod tests {
553+ use super::*;
554+
555+ #[test]
556+ fn test_rect_size_truncation() {
557+ for width in 256u16..300u16 {
558+ for height in 256u16..300u16 {
559+ let rect = Rect::new(0, 0, width, height);
560+ rect.area(); // Should not panic.
561+ assert!(rect.width < width || rect.height < height);
562+ // The target dimensions are rounded down so the math will not be too precise
563+ // but let's make sure the ratios don't diverge crazily.
564+ assert!(
565+ (f64::from(rect.width) / f64::from(rect.height)
566+ - f64::from(width) / f64::from(height))
567+ .abs()
568+ < 1.0
569+ )
570+ }
571+ }
572+
573+ // One dimension below 255, one above. Area above max u16.
574+ let width = 900;
575+ let height = 100;
576+ let rect = Rect::new(0, 0, width, height);
577+ assert_ne!(rect.width, 900);
578+ assert_ne!(rect.height, 100);
579+ assert!(rect.width < width || rect.height < height);
580+ }
581+
582+ #[test]
583+ fn test_rect_size_preservation() {
584+ for width in 0..256u16 {
585+ for height in 0..256u16 {
586+ let rect = Rect::new(0, 0, width, height);
587+ rect.area(); // Should not panic.
588+ assert_eq!(rect.width, width);
589+ assert_eq!(rect.height, height);
590+ }
591+ }
592+
593+ // One dimension below 255, one above. Area below max u16.
594+ let rect = Rect::new(0, 0, 300, 100);
595+ assert_eq!(rect.width, 300);
596+ assert_eq!(rect.height, 100);
597+ }
598+
599+ #[test]
600+ fn test_rect_chop_from_left() {
601+ let rect = Rect::new(0, 0, 20, 30);
602+ assert_eq!(Rect::new(10, 0, 10, 30), rect.clip_left(10));
603+ assert_eq!(
604+ Rect::new(20, 0, 0, 30),
605+ rect.clip_left(40),
606+ "x should be clamped to original width if new width is bigger"
607+ );
608+ }
609+
610+ #[test]
611+ fn test_rect_chop_from_right() {
612+ let rect = Rect::new(0, 0, 20, 30);
613+ assert_eq!(Rect::new(0, 0, 10, 30), rect.clip_right(10));
614+ }
615+
616+ #[test]
617+ fn test_rect_chop_from_top() {
618+ let rect = Rect::new(0, 0, 20, 30);
619+ assert_eq!(Rect::new(0, 10, 20, 20), rect.clip_top(10));
620+ assert_eq!(
621+ Rect::new(0, 30, 20, 0),
622+ rect.clip_top(50),
623+ "y should be clamped to original height if new height is bigger"
624+ );
625+ }
626+
627+ #[test]
628+ fn test_rect_chop_from_bottom() {
629+ let rect = Rect::new(0, 0, 20, 30);
630+ assert_eq!(Rect::new(0, 0, 20, 20), rect.clip_bottom(10));
631+ }
632+
633+ fn styles() -> Vec<Style> {
634+ vec![
635+ Style::default(),
636+ Style::default().fg(Color::Yellow),
637+ Style::default().bg(Color::Yellow),
638+ Style::default().add_modifier(Modifier::BOLD),
639+ Style::default().remove_modifier(Modifier::BOLD),
640+ Style::default().add_modifier(Modifier::ITALIC),
641+ Style::default().remove_modifier(Modifier::ITALIC),
642+ Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
643+ Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
644+ ]
645+ }
646+
647+ #[test]
648+ fn combined_patch_gives_same_result_as_individual_patch() {
649+ let styles = styles();
650+ for &a in &styles {
651+ for &b in &styles {
652+ for &c in &styles {
653+ for &d in &styles {
654+ let combined = a.patch(b.patch(c.patch(d)));
655+
656+ assert_eq!(
657+ Style::default().patch(a).patch(b).patch(c).patch(d),
658+ Style::default().patch(combined)
659+ );
660+ }
661+ }
662+ }
663+ }
664+ }
665+}
helix-view/src/keyboard.rs+153−153
@@ -1,153 +1,153 @@
1−use bitflags::bitflags;
2−
3−bitflags! {
4− /// Represents key modifiers (shift, control, alt).
5− #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6− pub struct KeyModifiers: u8 {
7− const SHIFT = 0b0000_0001;
8− const CONTROL = 0b0000_0010;
9− const ALT = 0b0000_0100;
10− const NONE = 0b0000_0000;
11− }
12−}
13−
14−#[cfg(feature = "term")]
15−impl From<KeyModifiers> for crossterm::event::KeyModifiers {
16− fn from(key_modifiers: KeyModifiers) -> Self {
17− use crossterm::event::KeyModifiers as CKeyModifiers;
18−
19− let mut result = CKeyModifiers::NONE;
20−
21− if key_modifiers.contains(KeyModifiers::SHIFT) {
22− result.insert(CKeyModifiers::SHIFT);
23− }
24− if key_modifiers.contains(KeyModifiers::CONTROL) {
25− result.insert(CKeyModifiers::CONTROL);
26− }
27− if key_modifiers.contains(KeyModifiers::ALT) {
28− result.insert(CKeyModifiers::ALT);
29− }
30−
31− result
32− }
33−}
34−
35−#[cfg(feature = "term")]
36−impl From<crossterm::event::KeyModifiers> for KeyModifiers {
37− fn from(val: crossterm::event::KeyModifiers) -> Self {
38− use crossterm::event::KeyModifiers as CKeyModifiers;
39−
40− let mut result = KeyModifiers::NONE;
41−
42− if val.contains(CKeyModifiers::SHIFT) {
43− result.insert(KeyModifiers::SHIFT);
44− }
45− if val.contains(CKeyModifiers::CONTROL) {
46− result.insert(KeyModifiers::CONTROL);
47− }
48− if val.contains(CKeyModifiers::ALT) {
49− result.insert(KeyModifiers::ALT);
50− }
51−
52− result
53− }
54−}
55−
56−/// Represents a key.
57−#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
58−#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59−pub enum KeyCode {
60− /// Backspace key.
61− Backspace,
62− /// Enter key.
63− Enter,
64− /// Left arrow key.
65− Left,
66− /// Right arrow key.
67− Right,
68− /// Up arrow key.
69− Up,
70− /// Down arrow key.
71− Down,
72− /// Home key.
73− Home,
74− /// End key.
75− End,
76− /// Page up key.
77− PageUp,
78− /// Page down key.
79− PageDown,
80− /// Tab key.
81− Tab,
82− /// Delete key.
83− Delete,
84− /// Insert key.
85− Insert,
86− /// F key.
87− ///
88− /// `KeyCode::F(1)` represents F1 key, etc.
89− F(u8),
90− /// A character.
91− ///
92− /// `KeyCode::Char('c')` represents `c` character, etc.
93− Char(char),
94− /// Null.
95− Null,
96− /// Escape key.
97− Esc,
98−}
99−
100−#[cfg(feature = "term")]
101−impl From<KeyCode> for crossterm::event::KeyCode {
102− fn from(key_code: KeyCode) -> Self {
103− use crossterm::event::KeyCode as CKeyCode;
104−
105− match key_code {
106− KeyCode::Backspace => CKeyCode::Backspace,
107− KeyCode::Enter => CKeyCode::Enter,
108− KeyCode::Left => CKeyCode::Left,
109− KeyCode::Right => CKeyCode::Right,
110− KeyCode::Up => CKeyCode::Up,
111− KeyCode::Down => CKeyCode::Down,
112− KeyCode::Home => CKeyCode::Home,
113− KeyCode::End => CKeyCode::End,
114− KeyCode::PageUp => CKeyCode::PageUp,
115− KeyCode::PageDown => CKeyCode::PageDown,
116− KeyCode::Tab => CKeyCode::Tab,
117− KeyCode::Delete => CKeyCode::Delete,
118− KeyCode::Insert => CKeyCode::Insert,
119− KeyCode::F(f_number) => CKeyCode::F(f_number),
120− KeyCode::Char(character) => CKeyCode::Char(character),
121− KeyCode::Null => CKeyCode::Null,
122− KeyCode::Esc => CKeyCode::Esc,
123− }
124− }
125−}
126−
127−#[cfg(feature = "term")]
128−impl From<crossterm::event::KeyCode> for KeyCode {
129− fn from(val: crossterm::event::KeyCode) -> Self {
130− use crossterm::event::KeyCode as CKeyCode;
131−
132− match val {
133− CKeyCode::Backspace => KeyCode::Backspace,
134− CKeyCode::Enter => KeyCode::Enter,
135− CKeyCode::Left => KeyCode::Left,
136− CKeyCode::Right => KeyCode::Right,
137− CKeyCode::Up => KeyCode::Up,
138− CKeyCode::Down => KeyCode::Down,
139− CKeyCode::Home => KeyCode::Home,
140− CKeyCode::End => KeyCode::End,
141− CKeyCode::PageUp => KeyCode::PageUp,
142− CKeyCode::PageDown => KeyCode::PageDown,
143− CKeyCode::Tab => KeyCode::Tab,
144− CKeyCode::BackTab => unreachable!("BackTab should have been handled on KeyEvent level"),
145− CKeyCode::Delete => KeyCode::Delete,
146− CKeyCode::Insert => KeyCode::Insert,
147− CKeyCode::F(f_number) => KeyCode::F(f_number),
148− CKeyCode::Char(character) => KeyCode::Char(character),
149− CKeyCode::Null => KeyCode::Null,
150− CKeyCode::Esc => KeyCode::Esc,
151− }
152− }
153−}
1+use bitflags::bitflags;
2+
3+bitflags! {
4+ /// Represents key modifiers (shift, control, alt).
5+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6+ pub struct KeyModifiers: u8 {
7+ const SHIFT = 0b0000_0001;
8+ const CONTROL = 0b0000_0010;
9+ const ALT = 0b0000_0100;
10+ const NONE = 0b0000_0000;
11+ }
12+}
13+
14+#[cfg(feature = "term")]
15+impl From<KeyModifiers> for crossterm::event::KeyModifiers {
16+ fn from(key_modifiers: KeyModifiers) -> Self {
17+ use crossterm::event::KeyModifiers as CKeyModifiers;
18+
19+ let mut result = CKeyModifiers::NONE;
20+
21+ if key_modifiers.contains(KeyModifiers::SHIFT) {
22+ result.insert(CKeyModifiers::SHIFT);
23+ }
24+ if key_modifiers.contains(KeyModifiers::CONTROL) {
25+ result.insert(CKeyModifiers::CONTROL);
26+ }
27+ if key_modifiers.contains(KeyModifiers::ALT) {
28+ result.insert(CKeyModifiers::ALT);
29+ }
30+
31+ result
32+ }
33+}
34+
35+#[cfg(feature = "term")]
36+impl From<crossterm::event::KeyModifiers> for KeyModifiers {
37+ fn from(val: crossterm::event::KeyModifiers) -> Self {
38+ use crossterm::event::KeyModifiers as CKeyModifiers;
39+
40+ let mut result = KeyModifiers::NONE;
41+
42+ if val.contains(CKeyModifiers::SHIFT) {
43+ result.insert(KeyModifiers::SHIFT);
44+ }
45+ if val.contains(CKeyModifiers::CONTROL) {
46+ result.insert(KeyModifiers::CONTROL);
47+ }
48+ if val.contains(CKeyModifiers::ALT) {
49+ result.insert(KeyModifiers::ALT);
50+ }
51+
52+ result
53+ }
54+}
55+
56+/// Represents a key.
57+#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy, Hash)]
58+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59+pub enum KeyCode {
60+ /// Backspace key.
61+ Backspace,
62+ /// Enter key.
63+ Enter,
64+ /// Left arrow key.
65+ Left,
66+ /// Right arrow key.
67+ Right,
68+ /// Up arrow key.
69+ Up,
70+ /// Down arrow key.
71+ Down,
72+ /// Home key.
73+ Home,
74+ /// End key.
75+ End,
76+ /// Page up key.
77+ PageUp,
78+ /// Page down key.
79+ PageDown,
80+ /// Tab key.
81+ Tab,
82+ /// Delete key.
83+ Delete,
84+ /// Insert key.
85+ Insert,
86+ /// F key.
87+ ///
88+ /// `KeyCode::F(1)` represents F1 key, etc.
89+ F(u8),
90+ /// A character.
91+ ///
92+ /// `KeyCode::Char('c')` represents `c` character, etc.
93+ Char(char),
94+ /// Null.
95+ Null,
96+ /// Escape key.
97+ Esc,
98+}
99+
100+#[cfg(feature = "term")]
101+impl From<KeyCode> for crossterm::event::KeyCode {
102+ fn from(key_code: KeyCode) -> Self {
103+ use crossterm::event::KeyCode as CKeyCode;
104+
105+ match key_code {
106+ KeyCode::Backspace => CKeyCode::Backspace,
107+ KeyCode::Enter => CKeyCode::Enter,
108+ KeyCode::Left => CKeyCode::Left,
109+ KeyCode::Right => CKeyCode::Right,
110+ KeyCode::Up => CKeyCode::Up,
111+ KeyCode::Down => CKeyCode::Down,
112+ KeyCode::Home => CKeyCode::Home,
113+ KeyCode::End => CKeyCode::End,
114+ KeyCode::PageUp => CKeyCode::PageUp,
115+ KeyCode::PageDown => CKeyCode::PageDown,
116+ KeyCode::Tab => CKeyCode::Tab,
117+ KeyCode::Delete => CKeyCode::Delete,
118+ KeyCode::Insert => CKeyCode::Insert,
119+ KeyCode::F(f_number) => CKeyCode::F(f_number),
120+ KeyCode::Char(character) => CKeyCode::Char(character),
121+ KeyCode::Null => CKeyCode::Null,
122+ KeyCode::Esc => CKeyCode::Esc,
123+ }
124+ }
125+}
126+
127+#[cfg(feature = "term")]
128+impl From<crossterm::event::KeyCode> for KeyCode {
129+ fn from(val: crossterm::event::KeyCode) -> Self {
130+ use crossterm::event::KeyCode as CKeyCode;
131+
132+ match val {
133+ CKeyCode::Backspace => KeyCode::Backspace,
134+ CKeyCode::Enter => KeyCode::Enter,
135+ CKeyCode::Left => KeyCode::Left,
136+ CKeyCode::Right => KeyCode::Right,
137+ CKeyCode::Up => KeyCode::Up,
138+ CKeyCode::Down => KeyCode::Down,
139+ CKeyCode::Home => KeyCode::Home,
140+ CKeyCode::End => KeyCode::End,
141+ CKeyCode::PageUp => KeyCode::PageUp,
142+ CKeyCode::PageDown => KeyCode::PageDown,
143+ CKeyCode::Tab => KeyCode::Tab,
144+ CKeyCode::BackTab => unreachable!("BackTab should have been handled on KeyEvent level"),
145+ CKeyCode::Delete => KeyCode::Delete,
146+ CKeyCode::Insert => KeyCode::Insert,
147+ CKeyCode::F(f_number) => KeyCode::F(f_number),
148+ CKeyCode::Char(character) => KeyCode::Char(character),
149+ CKeyCode::Null => KeyCode::Null,
150+ CKeyCode::Esc => KeyCode::Esc,
151+ }
152+ }
153+}