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}