tuifw_screen_base/
lib.rs

1#![feature(allocator_api)]
2#![feature(generic_arg_infer)]
3#![feature(iter_advance_by)]
4#![feature(stmt_expr_attributes)]
5#![feature(trusted_len)]
6
7#![deny(warnings)]
8#![doc(test(attr(deny(warnings))))]
9#![doc(test(attr(allow(dead_code))))]
10#![doc(test(attr(allow(unused_variables))))]
11#![allow(clippy::collapsible_else_if)]
12#![allow(clippy::collapsible_if)]
13#![allow(clippy::manual_map)]
14#![allow(clippy::many_single_char_names)]
15#![allow(clippy::too_many_arguments)]
16
17#![no_std]
18
19extern crate alloc;
20
21use alloc::boxed::Box;
22use core::alloc::Allocator;
23use core::fmt::{self, Debug, Display, Formatter};
24use core::num::NonZeroU16;
25use core::ops::Range;
26use enum_derive_2018::{EnumDisplay, EnumFromStr, IterVariants};
27use macro_attr_2018::macro_attr;
28use unicode_width::UnicodeWidthChar;
29
30pub use int_vec_2d::*;
31
32pub fn char_width(c: char) -> i16 {
33    if c == '\0' { 0 } else { c.width().map_or(0, |x| i16::try_from(x).unwrap()) }
34}
35
36pub fn text_width(s: &str) -> i16 {
37    s.chars().map(char_width).fold(0, |s, c| s.wrapping_add(c))
38}
39
40pub fn is_text_fit_in(w: i16, s: &str) -> bool {
41    let mut w = w as u16;
42    for c in s.chars() {
43        if let Some(new_w) = w.checked_sub(char_width(c) as u16) {
44            w = new_w;
45        } else {
46            return false;
47        }
48    }
49    true
50}
51
52macro_attr! {
53    #[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
54    #[derive(EnumDisplay!, EnumFromStr!, IterVariants!(BgVariants))]
55    pub enum Bg {
56        None,
57        Black,
58        Red,
59        Green,
60        Brown,
61        Blue,
62        Magenta,
63        Cyan,
64        LightGray
65    }
66}
67
68macro_attr! {
69    #[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
70    #[derive(EnumDisplay!, EnumFromStr!, IterVariants!(FgVariants))]
71    pub enum Fg {
72        Black,
73        Red,
74        Green,
75        Brown,
76        Blue,
77        Magenta,
78        Cyan,
79        LightGray,
80        DarkGray,
81        BrightRed,
82        BrightGreen,
83        Yellow,
84        BrightBlue,
85        BrightMagenta,
86        BrightCyan,
87        White
88    }
89}
90
91#[derive(Debug)]
92pub struct TryFromBgError;
93
94impl TryFrom<Bg> for Fg {
95    type Error = TryFromBgError;
96
97    fn try_from(bg: Bg) -> Result<Fg, Self::Error> {
98        match bg {
99            Bg::None => Err(TryFromBgError),
100            Bg::Black => Ok(Fg::Black),
101            Bg::Red => Ok(Fg::Red),
102            Bg::Green => Ok(Fg::Green),
103            Bg::Brown => Ok(Fg::Brown),
104            Bg::Blue => Ok(Fg::Blue),
105            Bg::Magenta => Ok(Fg::Magenta),
106            Bg::Cyan => Ok(Fg::Cyan),
107            Bg::LightGray => Ok(Fg::LightGray),
108        }
109    }
110}
111
112#[derive(Debug)]
113pub struct TryFromFgError;
114
115impl TryFrom<Fg> for Bg {
116    type Error = TryFromFgError;
117
118    fn try_from(fg: Fg) -> Result<Bg, Self::Error> {
119        match fg {
120            Fg::Black => Ok(Bg::Black),
121            Fg::Red => Ok(Bg::Red),
122            Fg::Green => Ok(Bg::Green),
123            Fg::Brown => Ok(Bg::Brown),
124            Fg::Blue => Ok(Bg::Blue),
125            Fg::Magenta => Ok(Bg::Magenta),
126            Fg::Cyan => Ok(Bg::Cyan),
127            Fg::LightGray => Ok(Bg::LightGray),
128            _ => Err(TryFromFgError),
129        }
130    }
131}
132
133#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
134pub enum Ctrl {
135    At, A, B, C, D, E, F, G, J, K, L, N,
136    O, P, Q, R, S, T, U, V, W, X, Y, Z,
137    Backslash, Bracket, Caret, Underscore
138}
139
140#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
141#[non_exhaustive]
142pub enum Key {
143    Char(char),
144    Alt(char),
145    Ctrl(Ctrl),
146    Enter,
147    Escape,
148    Down,
149    Up,
150    Left,
151    Right,
152    Home,
153    End,
154    Backspace,
155    Delete,
156    Insert,
157    PageDown,
158    PageUp,
159    Tab,
160    F1,
161    F2,
162    F3,
163    F4,
164    F5,
165    F6,
166    F7,
167    F8,
168    F9,
169    F10,
170    F11,
171    F12,
172}
173
174#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy, Ord, PartialOrd)]
175#[non_exhaustive]
176pub enum Event {
177    Resize,
178    Key(NonZeroU16, Key),
179}
180
181pub enum Error {
182    Oom,
183    System(Box<dyn Display, &'static dyn Allocator>),
184}
185
186impl Display for Error {
187    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
188        match self {
189            Error::Oom => write!(f, "out of memory"),
190            Error::System(msg) => write!(f, "{msg}")
191        }
192    }
193}
194
195impl Debug for Error {
196    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
197        Display::fmt(self, f)
198    }
199}
200
201pub trait Screen {
202    fn size(&self) -> Vector;
203
204    fn out(
205        &mut self,
206        p: Point,
207        fg: Fg,
208        bg: Bg,
209        text: &str,
210        hard: Range<i16>,
211        soft: Range<i16>,
212    ) -> Range<i16>;
213
214    fn update(&mut self, cursor: Option<Point>, wait: bool) -> Result<Option<Event>, Error>;
215
216    fn line_invalidated_range(&self, line: i16) -> &Range<i16>;
217
218    fn line_invalidated_range_mut(&mut self, line: i16) -> &mut Range<i16>;
219}