use iced::theme::Palette;
use iced::{Color, Task, Theme};
use oriel_core::data::DataSource;
use crate::grid::{Message, RowContext, VirtualGrid};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GridTheme {
pub name: &'static str,
pub row_height: f32,
pub header_height: f32,
pub cell_padding: f32,
pub background: Color,
pub text: Color,
pub header_background: Color,
pub header_text: Color,
pub border: Color,
pub row_divider: Option<Color>,
pub column_divider: Option<Color>,
pub row_hover: Color,
pub row_selected: Color,
pub row_stripe: Option<Color>,
pub accent: Color,
}
fn rgb(hex: u32) -> Color {
Color::from_rgb8((hex >> 16) as u8, (hex >> 8) as u8, hex as u8)
}
fn mix(a: Color, b: Color, t: f32) -> Color {
Color {
r: a.r + (b.r - a.r) * t,
g: a.g + (b.g - a.g) * t,
b: a.b + (b.b - a.b) * t,
a: 1.0,
}
}
impl GridTheme {
pub fn apply<S: DataSource>(&self, grid: &mut VirtualGrid<S>) -> Task<Message<S::Row>> {
let task = grid.set_row_height(self.row_height);
grid.set_header_height(self.header_height);
grid.set_footer_height(self.header_height);
grid.set_cell_padding(self.cell_padding);
grid.set_row_divider(self.row_divider);
grid.set_column_divider(self.column_divider);
grid.set_rule_color(Some(self.border));
grid.set_cell_style(crate::grid::default_cell_style);
let theme = *self;
grid.set_row_style(move |_, ctx| theme.row_style(ctx));
let theme = *self;
grid.set_header_style(move |_| theme.header_style());
let theme = *self;
grid.set_footer_style(move |_| theme.header_style());
grid.auto_size_all();
task
}
#[must_use]
pub fn row_style<Row>(&self, ctx: &RowContext<'_, Row>) -> iced::widget::container::Style {
let background = if ctx.is_selected {
Some(self.row_selected)
} else if ctx.is_focused {
Some(mix(self.background, self.row_selected, 0.55))
} else if ctx.is_hovered {
Some(self.row_hover)
} else if ctx.index % 2 == 1 {
self.row_stripe
} else {
None
};
iced::widget::container::Style {
text_color: Some(self.text),
background: background.map(Into::into),
..iced::widget::container::Style::default()
}
}
#[must_use]
pub fn header_style(&self) -> iced::widget::container::Style {
iced::widget::container::Style {
text_color: Some(self.header_text),
background: Some(self.header_background.into()),
..iced::widget::container::Style::default()
}
}
#[must_use]
pub fn iced_theme(&self) -> Theme {
let luma =
0.299 * self.background.r + 0.587 * self.background.g + 0.114 * self.background.b;
let base = if luma < 0.5 { Palette::DARK } else { Palette::LIGHT };
Theme::custom(
self.name,
Palette {
background: self.background,
text: self.text,
primary: self.accent,
..base
},
)
}
#[must_use]
pub fn presets() -> [GridTheme; 10] {
[
Self::quartz(),
Self::quartz_dark(),
Self::alpine(),
Self::alpine_dark(),
Self::balham(),
Self::balham_dark(),
Self::material(),
Self::spreadsheet(),
Self::bootstrap(),
Self::bootstrap_dark(),
]
}
#[must_use]
pub fn quartz() -> Self {
Self {
name: "Quartz",
row_height: 42.0,
header_height: 48.0,
cell_padding: 16.0,
background: rgb(0xFFFFFF),
text: rgb(0x181D1F),
header_background: rgb(0xFAFAFB),
header_text: rgb(0x181D1F),
border: rgb(0xDCDDDD),
row_divider: Some(rgb(0xDCDDDD)),
column_divider: None,
row_hover: rgb(0xE4F2FE),
row_selected: rgb(0xEDF7FE),
row_stripe: None,
accent: rgb(0x2196F3),
}
}
#[must_use]
pub fn quartz_dark() -> Self {
Self {
name: "Quartz Dark",
background: rgb(0x1F2936),
text: rgb(0xFFFFFF),
header_background: rgb(0x28313E),
header_text: rgb(0xFFFFFF),
border: rgb(0x434B56),
row_divider: Some(rgb(0x434B56)),
row_hover: rgb(0x1F3F5C),
row_selected: rgb(0x1F3F5C),
..Self::quartz()
}
}
#[must_use]
pub fn alpine() -> Self {
Self {
name: "Alpine",
row_height: 42.0,
header_height: 48.0,
cell_padding: 18.0,
background: rgb(0xFFFFFF),
text: rgb(0x181D1F),
header_background: rgb(0xF8F8F8),
header_text: rgb(0x181D1F),
border: rgb(0xBABFC7),
row_divider: Some(rgb(0xDDE2EB)),
column_divider: None,
row_hover: rgb(0xE9F4FE),
row_selected: rgb(0xBCE0FB),
row_stripe: Some(rgb(0xFCFCFC)),
accent: rgb(0x2196F3),
}
}
#[must_use]
pub fn alpine_dark() -> Self {
Self {
name: "Alpine Dark",
background: rgb(0x181D1F),
text: rgb(0xFFFFFF),
header_background: rgb(0x222628),
header_text: rgb(0xFFFFFF),
border: rgb(0x68686E),
row_divider: Some(rgb(0x383A39)),
row_hover: rgb(0x192934),
row_selected: rgb(0x1B415F),
row_stripe: Some(rgb(0x222628)),
..Self::alpine()
}
}
#[must_use]
pub fn balham() -> Self {
Self {
name: "Balham",
row_height: 28.0,
header_height: 32.0,
cell_padding: 12.0,
background: rgb(0xFFFFFF),
text: rgb(0x000000),
header_background: rgb(0xF5F7F7),
header_text: rgb(0x717272),
border: rgb(0xBDC3C7),
row_divider: Some(rgb(0xD9DCDE)),
column_divider: None,
row_hover: rgb(0xECF0F1),
row_selected: rgb(0xB8E0F9),
row_stripe: Some(rgb(0xFCFDFE)),
accent: rgb(0x0091EA),
}
}
#[must_use]
pub fn balham_dark() -> Self {
Self {
name: "Balham Dark",
background: rgb(0x2D3436),
text: rgb(0xF5F5F5),
header_background: rgb(0x1C1C1C),
header_text: rgb(0xA7A7A7),
border: rgb(0x424242),
row_divider: Some(rgb(0x5C5C5C)),
row_hover: rgb(0x3D4749),
row_selected: rgb(0x20576E),
row_stripe: Some(rgb(0x262C2E)),
accent: rgb(0x00B0FF),
..Self::balham()
}
}
#[must_use]
pub fn material() -> Self {
Self {
name: "Material",
row_height: 48.0,
header_height: 56.0,
cell_padding: 24.0,
background: rgb(0xFFFFFF),
text: rgb(0x212121),
header_background: rgb(0xFFFFFF),
header_text: rgb(0x757575),
border: rgb(0xE2E2E2),
row_divider: Some(rgb(0xE2E2E2)),
column_divider: None,
row_hover: rgb(0xFAFAFA),
row_selected: rgb(0xBCE0FB),
row_stripe: None,
accent: rgb(0xFF4081),
}
}
#[must_use]
pub fn spreadsheet() -> Self {
Self {
name: "Spreadsheet",
row_height: 22.0,
header_height: 24.0,
cell_padding: 4.0,
background: rgb(0xFFFFFF),
text: rgb(0x000000),
header_background: rgb(0xF3F3F3),
header_text: rgb(0x444444),
border: rgb(0xCCCCCC),
row_divider: Some(rgb(0xCCCCCC)),
column_divider: Some(rgb(0xCCCCCC)),
row_hover: rgb(0xFFFFFF),
row_selected: rgb(0xE5EFFF),
row_stripe: None,
accent: rgb(0x1A73E8),
}
}
#[must_use]
pub fn bootstrap() -> Self {
Self {
name: "Bootstrap",
row_height: 42.0,
header_height: 42.0,
cell_padding: 8.0,
background: rgb(0xFFFFFF),
text: rgb(0x000000),
header_background: rgb(0xFFFFFF),
header_text: rgb(0x000000),
border: rgb(0xDEE2E6),
row_divider: Some(rgb(0xDEE2E6)),
column_divider: None,
row_hover: rgb(0xECECEC),
row_selected: rgb(0xE5E5E5),
row_stripe: Some(rgb(0xF2F2F2)),
accent: rgb(0x0D6EFD),
}
}
#[must_use]
pub fn bootstrap_dark() -> Self {
Self {
name: "Bootstrap Dark",
background: rgb(0x212529),
text: rgb(0xFFFFFF),
header_background: rgb(0x212529),
header_text: rgb(0xFFFFFF),
border: rgb(0x495057),
row_divider: Some(rgb(0x495057)),
row_hover: rgb(0x323539),
row_selected: rgb(0x373B3E),
row_stripe: Some(rgb(0x2C3034)),
..Self::bootstrap()
}
}
}