use crossterm::style::Color;
pub const BUTTON_NORMAL: u8 = 1; pub const BUTTON_DEFAULT: u8 = 2; pub const BUTTON_SELECTED: u8 = 3; pub const BUTTON_DISABLED: u8 = 4; pub const BUTTON_SHORTCUT: u8 = 7; pub const BUTTON_SHADOW: u8 = 8;
pub const INPUT_NORMAL: u8 = 1; pub const INPUT_FOCUSED: u8 = 2; pub const INPUT_SELECTED: u8 = 3; pub const INPUT_ARROWS: u8 = 4;
pub const SCROLLBAR_PAGE: u8 = 1; pub const SCROLLBAR_ARROWS: u8 = 2; pub const SCROLLBAR_INDICATOR: u8 = 3;
pub const LISTBOX_NORMAL: u8 = 1; pub const LISTBOX_FOCUSED: u8 = 2; pub const LISTBOX_SELECTED: u8 = 3; pub const LISTBOX_DIVIDER: u8 = 4;
pub const CLUSTER_NORMAL: u8 = 1; pub const CLUSTER_FOCUSED: u8 = 2; pub const CLUSTER_SHORTCUT: u8 = 3; pub const CLUSTER_SHORTCUT_SELECTED: u8 = 4; pub const CLUSTER_DISABLED: u8 = 5;
pub const LABEL_NORMAL: u8 = 1; pub const LABEL_SELECTED: u8 = 2; pub const LABEL_SHORTCUT: u8 = 3;
pub const STATIC_TEXT_NORMAL: u8 = 1;
pub const PARAM_TEXT_NORMAL: u8 = 1;
pub const STATUSLINE_NORMAL: u8 = 1; pub const STATUSLINE_SHORTCUT: u8 = 2; pub const STATUSLINE_SELECTED: u8 = 3; pub const STATUSLINE_SELECTED_SHORTCUT: u8 = 4; pub const STATUSLINE_DISABLED: u8 = 5;
pub const FRAME_INACTIVE: u8 = 1; pub const FRAME_ACTIVE_BORDER: u8 = 2; pub const FRAME_TITLE: u8 = 2; pub const FRAME_ICON: u8 = 3;
pub const WINDOW_BACKGROUND: u8 = 1; pub const BLUE_WINDOW_BACKGROUND: u8 = 5;
pub const EDITOR_NORMAL: u8 = 1; pub const EDITOR_SELECTED: u8 = 2; pub const EDITOR_CURSOR: u8 = 2;
pub const SYNTAX_NORMAL_IDX: u8 = 3;
pub const SYNTAX_KEYWORD_IDX: u8 = 4;
pub const SYNTAX_STRING_IDX: u8 = 5;
pub const SYNTAX_COMMENT_IDX: u8 = 6;
pub const SYNTAX_NUMBER_IDX: u8 = 7;
pub const SYNTAX_OPERATOR_IDX: u8 = 8;
pub const SYNTAX_IDENTIFIER_IDX: u8 = 9;
pub const SYNTAX_TYPE_IDX: u8 = 10;
pub const SYNTAX_PREPROCESSOR_IDX: u8 = 11;
pub const SYNTAX_FUNCTION_IDX: u8 = 12;
pub const SYNTAX_SPECIAL_IDX: u8 = 13;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TvColor {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
LightMagenta = 13,
Yellow = 14,
White = 15,
Rgb {
r: u8,
g: u8,
b: u8,
} = 16,
}
impl TvColor {
pub fn to_index(self) -> u8 {
match self {
TvColor::Black => 0,
TvColor::Blue => 1,
TvColor::Green => 2,
TvColor::Cyan => 3,
TvColor::Red => 4,
TvColor::Magenta => 5,
TvColor::Brown => 6,
TvColor::LightGray => 7,
TvColor::DarkGray => 8,
TvColor::LightBlue => 9,
TvColor::LightGreen => 10,
TvColor::LightCyan => 11,
TvColor::LightRed => 12,
TvColor::LightMagenta => 13,
TvColor::Yellow => 14,
TvColor::White => 15,
TvColor::Rgb { .. } => 0,
}
}
pub fn to_ansi_code(self) -> u8 {
match self {
TvColor::Black => 0,
TvColor::Blue => 4, TvColor::Green => 2,
TvColor::Cyan => 6, TvColor::Red => 1, TvColor::Magenta => 5,
TvColor::Brown => 3, TvColor::LightGray => 7,
TvColor::DarkGray => 8,
TvColor::LightBlue => 12, TvColor::LightGreen => 10,
TvColor::LightCyan => 14, TvColor::LightRed => 9, TvColor::LightMagenta => 13,
TvColor::Yellow => 11, TvColor::White => 15,
TvColor::Rgb { .. } => 0, }
}
pub fn to_crossterm(self) -> Color {
match self {
TvColor::Black => Color::Rgb { r: 0, g: 0, b: 0 },
TvColor::Blue => Color::Rgb { r: 0, g: 0, b: 170 },
TvColor::Green => Color::Rgb { r: 0, g: 170, b: 0 },
TvColor::Cyan => Color::Rgb {
r: 0,
g: 170,
b: 170,
},
TvColor::Red => Color::Rgb { r: 170, g: 0, b: 0 },
TvColor::Magenta => Color::Rgb {
r: 170,
g: 0,
b: 170,
},
TvColor::Brown => Color::Rgb {
r: 170,
g: 85,
b: 0,
},
TvColor::LightGray => Color::Rgb {
r: 170,
g: 170,
b: 170,
},
TvColor::DarkGray => Color::Rgb {
r: 85,
g: 85,
b: 85,
},
TvColor::LightBlue => Color::Rgb {
r: 85,
g: 85,
b: 255,
},
TvColor::LightGreen => Color::Rgb {
r: 85,
g: 255,
b: 85,
},
TvColor::LightCyan => Color::Rgb {
r: 85,
g: 255,
b: 255,
},
TvColor::LightRed => Color::Rgb {
r: 255,
g: 85,
b: 85,
},
TvColor::LightMagenta => Color::Rgb {
r: 255,
g: 85,
b: 255,
},
TvColor::Yellow => Color::Rgb {
r: 255,
g: 255,
b: 85,
},
TvColor::White => Color::Rgb {
r: 255,
g: 255,
b: 255,
},
TvColor::Rgb { r, g, b } => Color::Rgb { r, g, b },
}
}
pub fn to_rgb(self) -> (u8, u8, u8) {
match self {
TvColor::Black => (0, 0, 0),
TvColor::Blue => (0, 0, 170),
TvColor::Green => (0, 170, 0),
TvColor::Cyan => (0, 170, 170),
TvColor::Red => (170, 0, 0),
TvColor::Magenta => (170, 0, 170),
TvColor::Brown => (170, 85, 0),
TvColor::LightGray => (170, 170, 170),
TvColor::DarkGray => (85, 85, 85),
TvColor::LightBlue => (85, 85, 255),
TvColor::LightGreen => (85, 255, 85),
TvColor::LightCyan => (85, 255, 255),
TvColor::LightRed => (255, 85, 85),
TvColor::LightMagenta => (255, 85, 255),
TvColor::Yellow => (255, 255, 85),
TvColor::White => (255, 255, 255),
TvColor::Rgb { r, g, b } => (r, g, b),
}
}
pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
let all_colors = [
TvColor::Black,
TvColor::Blue,
TvColor::Green,
TvColor::Cyan,
TvColor::Red,
TvColor::Magenta,
TvColor::Brown,
TvColor::LightGray,
TvColor::DarkGray,
TvColor::LightBlue,
TvColor::LightGreen,
TvColor::LightCyan,
TvColor::LightRed,
TvColor::LightMagenta,
TvColor::Yellow,
TvColor::White,
];
let mut best_color = TvColor::Black;
let mut best_distance = u32::MAX;
for &color in &all_colors {
let (cr, cg, cb) = color.to_rgb();
let distance = (r as i32 - cr as i32).pow(2) as u32
+ (g as i32 - cg as i32).pow(2) as u32
+ (b as i32 - cb as i32).pow(2) as u32;
if distance < best_distance {
best_distance = distance;
best_color = color;
}
}
best_color
}
pub fn from_u8(n: u8) -> Self {
match n & 0x0F {
0 => TvColor::Black,
1 => TvColor::Blue,
2 => TvColor::Green,
3 => TvColor::Cyan,
4 => TvColor::Red,
5 => TvColor::Magenta,
6 => TvColor::Brown,
7 => TvColor::LightGray,
8 => TvColor::DarkGray,
9 => TvColor::LightBlue,
10 => TvColor::LightGreen,
11 => TvColor::LightCyan,
12 => TvColor::LightRed,
13 => TvColor::LightMagenta,
14 => TvColor::Yellow,
15 => TvColor::White,
_ => TvColor::LightGray,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Style(u8);
impl Style {
pub const BOLD: Style = Style(1 << 0);
pub const ITALIC: Style = Style(1 << 1);
pub const UNDERLINE: Style = Style(1 << 2);
pub const REVERSE: Style = Style(1 << 3);
pub const DIM: Style = Style(1 << 4);
pub const STRIKETHROUGH: Style = Style(1 << 5);
pub const fn empty() -> Style {
Style(0)
}
pub const fn bits(self) -> u8 {
self.0
}
pub const fn is_empty(self) -> bool {
self.0 == 0
}
pub const fn contains(self, other: Style) -> bool {
(self.0 & other.0) == other.0
}
pub fn insert(&mut self, other: Style) {
self.0 |= other.0;
}
pub fn remove(&mut self, other: Style) {
self.0 &= !other.0;
}
}
impl core::ops::BitOr for Style {
type Output = Style;
fn bitor(self, rhs: Style) -> Style {
Style(self.0 | rhs.0)
}
}
impl core::ops::BitOrAssign for Style {
fn bitor_assign(&mut self, rhs: Style) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Attr {
pub fg: TvColor,
pub bg: TvColor,
pub style: Style,
}
impl Attr {
pub const fn new(fg: TvColor, bg: TvColor) -> Self {
Self {
fg,
bg,
style: Style::empty(),
}
}
pub const fn with_style(self, style: Style) -> Self {
Self {
fg: self.fg,
bg: self.bg,
style,
}
}
const fn add_style(self, flag: Style) -> Self {
Self {
fg: self.fg,
bg: self.bg,
style: Style(self.style.bits() | flag.bits()),
}
}
pub const fn bold(self) -> Self {
self.add_style(Style::BOLD)
}
pub const fn italic(self) -> Self {
self.add_style(Style::ITALIC)
}
pub const fn underline(self) -> Self {
self.add_style(Style::UNDERLINE)
}
pub const fn reverse(self) -> Self {
self.add_style(Style::REVERSE)
}
pub const fn dim(self) -> Self {
self.add_style(Style::DIM)
}
pub const fn strikethrough(self) -> Self {
self.add_style(Style::STRIKETHROUGH)
}
pub fn from_u8(byte: u8) -> Self {
Self {
fg: TvColor::from_u8(byte & 0x0F),
bg: TvColor::from_u8((byte >> 4) & 0x0F),
style: Style::empty(),
}
}
pub fn to_u8(self) -> u8 {
self.fg.to_index() | (self.bg.to_index() << 4)
}
pub fn swap(self) -> Self {
Self {
fg: self.bg,
bg: self.fg,
style: self.style,
}
}
pub fn darken(&self, factor: f32) -> Self {
let darken_color = |color: TvColor| -> TvColor {
let (r, g, b) = color.to_rgb();
let new_r = ((r as f32) * factor).min(255.0) as u8;
let new_g = ((g as f32) * factor).min(255.0) as u8;
let new_b = ((b as f32) * factor).min(255.0) as u8;
TvColor::from_rgb(new_r, new_g, new_b)
};
Self {
fg: darken_color(self.fg),
bg: darken_color(self.bg),
style: self.style,
}
}
}
pub mod colors {
use super::*;
pub const NORMAL: Attr = Attr::new(TvColor::LightGray, TvColor::Blue);
pub const HIGHLIGHTED: Attr = Attr::new(TvColor::Yellow, TvColor::Blue);
pub const SELECTED: Attr = Attr::new(TvColor::White, TvColor::Cyan);
pub const DISABLED: Attr = Attr::new(TvColor::DarkGray, TvColor::Blue);
pub const MENU_NORMAL: Attr = Attr::new(TvColor::Black, TvColor::LightGray);
pub const MENU_SELECTED: Attr = Attr::new(TvColor::White, TvColor::Green);
pub const MENU_DISABLED: Attr = Attr::new(TvColor::DarkGray, TvColor::LightGray);
pub const MENU_SHORTCUT: Attr = Attr::new(TvColor::Red, TvColor::LightGray);
pub const DIALOG_NORMAL: Attr = Attr::new(TvColor::Black, TvColor::LightGray); pub const DIALOG_FRAME: Attr = Attr::new(TvColor::White, TvColor::LightGray); pub const DIALOG_FRAME_ACTIVE: Attr = Attr::new(TvColor::White, TvColor::LightGray); pub const DIALOG_TITLE: Attr = Attr::new(TvColor::White, TvColor::LightGray); pub const DIALOG_SHORTCUT: Attr = Attr::new(TvColor::Red, TvColor::LightGray);
pub const BUTTON_NORMAL: Attr = Attr::new(TvColor::Black, TvColor::Green); pub const BUTTON_DEFAULT: Attr = Attr::new(TvColor::LightGreen, TvColor::Green); pub const BUTTON_SELECTED: Attr = Attr::new(TvColor::White, TvColor::Green); pub const BUTTON_DISABLED: Attr = Attr::new(TvColor::DarkGray, TvColor::Green); pub const BUTTON_SHORTCUT: Attr = Attr::new(TvColor::Yellow, TvColor::Green); pub const BUTTON_SHADOW: Attr = Attr::new(TvColor::LightGray, TvColor::DarkGray);
pub const STATUS_NORMAL: Attr = Attr::new(TvColor::Black, TvColor::LightGray);
pub const STATUS_SHORTCUT: Attr = Attr::new(TvColor::Red, TvColor::LightGray);
pub const STATUS_SELECTED: Attr = Attr::new(TvColor::White, TvColor::Green);
pub const STATUS_SELECTED_SHORTCUT: Attr = Attr::new(TvColor::Yellow, TvColor::Green);
pub const INPUT_NORMAL: Attr = Attr::new(TvColor::Yellow, TvColor::Blue); pub const INPUT_FOCUSED: Attr = Attr::new(TvColor::Yellow, TvColor::Blue); pub const INPUT_SELECTED: Attr = Attr::new(TvColor::Cyan, TvColor::Cyan); pub const INPUT_ARROWS: Attr = Attr::new(TvColor::Red, TvColor::Cyan);
pub const EDITOR_NORMAL: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const EDITOR_SELECTED: Attr = Attr::new(TvColor::Black, TvColor::Cyan);
pub const LISTBOX_NORMAL: Attr = Attr::new(TvColor::Black, TvColor::LightGray);
pub const LISTBOX_FOCUSED: Attr = Attr::new(TvColor::Black, TvColor::White);
pub const LISTBOX_SELECTED: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const LISTBOX_SELECTED_FOCUSED: Attr = Attr::new(TvColor::White, TvColor::Cyan);
pub const SCROLLBAR_PAGE: Attr = Attr::new(TvColor::DarkGray, TvColor::LightGray);
pub const SCROLLBAR_INDICATOR: Attr = Attr::new(TvColor::Blue, TvColor::LightGray);
pub const SCROLLBAR_ARROW: Attr = Attr::new(TvColor::Black, TvColor::LightGray);
pub const SCROLLER_NORMAL: Attr = Attr::new(TvColor::Black, TvColor::LightGray);
pub const SCROLLER_SELECTED: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const DESKTOP: Attr = Attr::new(TvColor::LightGray, TvColor::DarkGray);
pub const SYNTAX_NORMAL: Attr = Attr::new(TvColor::LightGray, TvColor::Blue);
pub const SYNTAX_KEYWORD: Attr = Attr::new(TvColor::Yellow, TvColor::Blue);
pub const SYNTAX_STRING: Attr = Attr::new(TvColor::LightRed, TvColor::Blue);
pub const SYNTAX_COMMENT: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const SYNTAX_NUMBER: Attr = Attr::new(TvColor::LightMagenta, TvColor::Blue);
pub const SYNTAX_OPERATOR: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const SYNTAX_IDENTIFIER: Attr = Attr::new(TvColor::LightGray, TvColor::Blue);
pub const SYNTAX_TYPE: Attr = Attr::new(TvColor::LightGreen, TvColor::Blue);
pub const SYNTAX_PREPROCESSOR: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const SYNTAX_FUNCTION: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const SYNTAX_SPECIAL: Attr = Attr::new(TvColor::White, TvColor::Blue);
pub const HELP_NORMAL: Attr = Attr::new(TvColor::Black, TvColor::LightGray);
pub const HELP_FOCUSED: Attr = Attr::new(TvColor::Black, TvColor::White);
}
#[derive(Debug, Clone)]
pub struct Palette {
data: Vec<u8>,
}
impl Palette {
pub fn new() -> Self {
Self { data: Vec::new() }
}
pub fn from_slice(data: &[u8]) -> Self {
Self {
data: data.to_vec(),
}
}
pub fn get(&self, index: usize) -> u8 {
if index == 0 || index > self.data.len() {
0
} else {
self.data[index - 1]
}
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl Default for Palette {
fn default() -> Self {
Self::new()
}
}
pub mod palettes {
use std::cell::RefCell;
thread_local! {
static CUSTOM_APP_PALETTE: RefCell<Option<Vec<u8>>> = RefCell::new(None);
}
pub fn set_custom_palette(palette: Option<Vec<u8>>) {
CUSTOM_APP_PALETTE.with(|p| {
*p.borrow_mut() = palette;
});
}
pub fn get_app_palette() -> Vec<u8> {
CUSTOM_APP_PALETTE.with(|p| {
if let Some(custom) = p.borrow().as_ref() {
custom.clone()
} else {
CP_APP_COLOR.to_vec()
}
})
}
#[rustfmt::skip]
pub const CP_APP_COLOR: &[u8] = &[
0x71, 0x70, 0x78, 0x74, 0x20, 0x28, 0x24, 0x17, 0x1F, 0x1A, 0x31, 0x31, 0x1E, 0x71, 0x1F, 0x30, 0x3F, 0x3A, 0x13, 0x13, 0x3E, 0x21, 0x3F,
0x70, 0x7F, 0x7A, 0x13, 0x13, 0x70, 0x7F, 0x7E,
0x70, 0x7F, 0x7A, 0x13, 0x13, 0x70, 0x70, 0x7F, 0x7E, 0x20, 0x2B, 0x2F, 0x78, 0x2E, 0x70, 0x30, 0x3F, 0x3E, 0x1F, 0x2F, 0x1A, 0x20, 0x72, 0x31, 0x31, 0x30, 0x2F, 0x3E, 0x31, 0x13, 0x38, 0x00, 0x17, 0x1E, 0x1C, 0x1F, 0x1D, 0x1F, 0x17, 0x1A, 0x1F, 0x1B, 0x1F,
0x37, 0x3E, 0x3C, 0x3F, 0x3D, 0x3F, 0x37, 0x3A, 0x3F, 0x3B, 0x3F,
0x70, 0x7E, 0x7C, 0x78, 0x7D, 0x70, 0x70, 0x72, 0x78, 0x71, 0x74,
0x07, 0x0F, 0x0A, 0x08, 0x08, 0x0F, 0x07, 0x00,
0x17, 0x1F, 0x1A, 0x71, 0x71, 0x1E, 0x17, 0x1F, 0x1E, 0x20, 0x2B, 0x2F, 0x78, 0x2E, 0x10, 0x30, 0x3F, 0x3E, 0x70, 0x2F, 0x7A, 0x20, 0x12, 0x31, 0x31, 0x30, 0x2F, 0x3E, 0x31, 0x13, 0x38, 0x00, ];
#[rustfmt::skip]
pub const CP_BLUE_WINDOW: &[u8] = &[
8, 9, 10, 11, 12, 13, 14, 15, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, ];
#[rustfmt::skip]
pub const CP_CYAN_WINDOW: &[u8] = &[
16, 17, 18, 19, 20, 21, 22, 23, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, ];
#[rustfmt::skip]
pub const CP_GRAY_WINDOW: &[u8] = &[
24, 25, 26, 27, 28, 29, 30, 31, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, ];
#[rustfmt::skip]
pub const CP_GRAY_DIALOG: &[u8] = &[
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, ];
#[rustfmt::skip]
pub const CP_BLUE_DIALOG: &[u8] = &[
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, ];
#[rustfmt::skip]
pub const CP_BUTTON: &[u8] = &[
10, 11, 12, 13, 14, 14, 14, 15, ];
#[rustfmt::skip]
pub const CP_STATIC_TEXT: &[u8] = &[
6, ];
#[rustfmt::skip]
pub const CP_INPUT_LINE: &[u8] = &[
19, 19, 20, 21, ];
#[rustfmt::skip]
pub const CP_LABEL: &[u8] = &[
7, 8, 9, 9, 13, 13, ];
#[rustfmt::skip]
pub const CP_LISTBOX: &[u8] = &[
26, 26, 27, 28, ];
#[rustfmt::skip]
pub const CP_SCROLLBAR: &[u8] = &[
4, 5, 5, ];
#[rustfmt::skip]
pub const CP_SCROLLER: &[u8] = &[
6, 7, ];
#[rustfmt::skip]
pub const CP_CLUSTER: &[u8] = &[
16, 17, 18, 18, 31, ];
#[rustfmt::skip]
pub const CP_STATUSLINE: &[u8] = &[
2, 4, 45, 41, 3,
];
#[rustfmt::skip]
pub const CP_MENU_BAR: &[u8] = &[
2, 5, 3, 4, ];
#[rustfmt::skip]
pub const CP_MEMO: &[u8] = &[
26, 27, ];
#[rustfmt::skip]
pub const CP_INDICATOR: &[u8] = &[
2, 3, ];
#[rustfmt::skip]
pub const CP_HELP_VIEWER: &[u8] = &[
1, 2, 6, 2, 3, 4, ];
#[rustfmt::skip]
pub const CP_EDITOR: &[u8] = &[
6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ];
#[rustfmt::skip]
pub const CP_HISTORY_VIEWER: &[u8] = &[
6, 6, 7, 6, 6, ];
#[rustfmt::skip]
pub const CP_HISTORY: &[u8] = &[
22, 23, ];
#[rustfmt::skip]
pub const CP_PROGRESS_BAR: &[u8] = &[
5, 4, ];
#[rustfmt::skip]
pub const CP_BACKGROUND: &[u8] = &[
1, ];
}
#[cfg(test)]
mod tests {
use super::palettes::*;
use super::*;
fn app(index: u8) -> u8 {
CP_APP_COLOR[index as usize - 1]
}
#[test]
fn app_palette_restored_borland_bytes() {
assert_eq!(app(15), 0x1F);
assert_eq!(app(23), 0x3F);
assert_eq!(app(31), 0x7E);
}
#[test]
fn blue_dialog_maps_to_blue_backgrounds() {
let frame = app(CP_BLUE_DIALOG[0]);
assert_eq!(frame, 0x17);
assert_eq!(Attr::from_u8(frame).bg, TvColor::Blue);
let active = app(CP_BLUE_DIALOG[1]);
assert_eq!(active, 0x1F);
assert_eq!(Attr::from_u8(active).bg, TvColor::Blue);
for &idx in CP_BLUE_DIALOG {
assert!((idx as usize) <= CP_APP_COLOR.len());
}
for &idx in CP_BLUE_DIALOG {
assert!(idx < 64 || idx > 96);
}
}
#[test]
fn blue_dialog_matches_borland_values() {
let borland: [u8; 32] = [
0x17, 0x1F, 0x1A, 0x71, 0x71, 0x1E, 0x17, 0x1F, 0x1E, 0x20, 0x2B, 0x2F, 0x78, 0x2E,
0x10, 0x30, 0x3F, 0x3E, 0x70, 0x2F, 0x7A, 0x20, 0x12, 0x31, 0x31, 0x30, 0x2F, 0x3E,
0x31, 0x13, 0x38, 0x00,
];
for (i, &idx) in CP_BLUE_DIALOG.iter().enumerate() {
assert_eq!(app(idx), borland[i], "blue dialog entry {}", i + 1);
}
}
#[test]
fn cluster_palette_matches_borland() {
assert_eq!(CP_CLUSTER, &[16, 17, 18, 18, 31]);
assert_eq!(app(CP_CLUSTER[CLUSTER_DISABLED as usize - 1]), 0x7E);
let pal = Palette::from_slice(CP_CLUSTER);
assert_eq!(pal.get(6), 0);
}
#[test]
fn test_style_bitset() {
let s = Style::BOLD | Style::ITALIC;
assert!(s.contains(Style::BOLD));
assert!(s.contains(Style::ITALIC));
assert!(!s.contains(Style::UNDERLINE));
assert!(Style::empty().is_empty());
assert!(!s.is_empty());
let mut m = Style::empty();
m.insert(Style::UNDERLINE);
assert!(m.contains(Style::UNDERLINE));
m.remove(Style::UNDERLINE);
assert!(!m.contains(Style::UNDERLINE));
assert!(m.is_empty());
}
#[test]
fn test_attr_style_builders() {
let a = Attr::new(TvColor::White, TvColor::Blue);
assert!(a.style.is_empty());
let b = a.bold().italic();
assert!(b.style.contains(Style::BOLD));
assert!(b.style.contains(Style::ITALIC));
assert_eq!(b.fg, TvColor::White);
assert_eq!(b.bg, TvColor::Blue);
let c = a.with_style(Style::UNDERLINE | Style::REVERSE);
assert!(c.style.contains(Style::UNDERLINE));
assert!(c.style.contains(Style::REVERSE));
assert!(!c.style.contains(Style::BOLD));
}
#[test]
fn test_attr_u8_drops_style() {
let a = Attr::new(TvColor::White, TvColor::Blue).bold();
let round = Attr::from_u8(a.to_u8());
assert_eq!(round.fg, TvColor::White);
assert_eq!(round.bg, TvColor::Blue);
assert!(
round.style.is_empty(),
"style is not representable in the color byte"
);
}
#[test]
fn test_attr_swap_darken_preserve_style() {
let a = Attr::new(TvColor::White, TvColor::Blue).underline();
assert!(a.swap().style.contains(Style::UNDERLINE));
assert!(a.darken(0.5).style.contains(Style::UNDERLINE));
}
}