#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub const fn rgb(r: u8, g: u8, b: u8) -> Color {
Color { r, g, b, a: 255 }
}
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
Color { r, g, b, a }
}
pub const fn hex(hex: &str) -> Color {
let bytes = hex.as_bytes();
if bytes.is_empty() || bytes[0] != b'#' || (bytes.len() != 7 && bytes.len() != 9) {
panic!("Color::hex takes \"#rrggbb\" or \"#rrggbbaa\"");
}
let a = if bytes.len() == 9 {
hex_byte(bytes[7], bytes[8])
} else {
255
};
Color {
r: hex_byte(bytes[1], bytes[2]),
g: hex_byte(bytes[3], bytes[4]),
b: hex_byte(bytes[5], bytes[6]),
a,
}
}
}
const fn hex_byte(high: u8, low: u8) -> u8 {
hex_digit(high) * 16 + hex_digit(low)
}
const fn hex_digit(digit: u8) -> u8 {
match digit {
b'0'..=b'9' => digit - b'0',
b'a'..=b'f' => digit - b'a' + 10,
b'A'..=b'F' => digit - b'A' + 10,
_ => panic!("Color::hex found a character that isn't a hex digit"),
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Font {
pub family: &'static str,
pub size: f32,
pub weight: u16,
}
impl Font {
pub const DEFAULT: Font = Font::new("sans-serif", 13.0);
pub const fn new(family: &'static str, size: f32) -> Font {
Font {
family,
size,
weight: 400,
}
}
pub const fn weight(self, weight: u16) -> Font {
Font { weight, ..self }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Border {
pub width: f32,
pub color: Color,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Padding {
pub vertical: u32,
pub horizontal: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Style {
pub background: Option<Color>,
pub color: Option<Color>,
pub font: Option<Font>,
pub padding: Option<Padding>,
pub radius: Option<f32>,
pub border: Option<Border>,
pub opacity: Option<f32>,
}
impl Style {
pub const fn new() -> Style {
Style {
background: None,
color: None,
font: None,
padding: None,
radius: None,
border: None,
opacity: None,
}
}
pub const fn background(self, color: Color) -> Style {
Style {
background: Some(color),
..self
}
}
pub const fn color(self, color: Color) -> Style {
Style {
color: Some(color),
..self
}
}
pub const fn font(self, font: Font) -> Style {
Style {
font: Some(font),
..self
}
}
pub const fn padding(self, vertical: u32, horizontal: u32) -> Style {
Style {
padding: Some(Padding {
vertical,
horizontal,
}),
..self
}
}
pub const fn radius(self, radius: f32) -> Style {
Style {
radius: Some(radius),
..self
}
}
pub const fn border(self, width: f32, color: Color) -> Style {
Style {
border: Some(Border { width, color }),
..self
}
}
pub const fn opacity(self, opacity: f32) -> Style {
Style {
opacity: Some(opacity),
..self
}
}
pub const fn merge(self, over: Style) -> Style {
Style {
background: or(over.background, self.background),
color: or(over.color, self.color),
font: or(over.font, self.font),
padding: or(over.padding, self.padding),
radius: or(over.radius, self.radius),
border: or(over.border, self.border),
opacity: or(over.opacity, self.opacity),
}
}
pub(crate) fn same_layout(&self, other: &Style) -> bool {
self.font == other.font && self.padding == other.padding
}
}
const fn or<T: Copy>(first: Option<T>, second: Option<T>) -> Option<T> {
match first {
Some(value) => Some(value),
None => second,
}
}