use teksilo_core::build_context::BuildContext;
use teksilo_core::color_prop::ColorProp;
use teksilo_core::signal::Signal;
use teksilo_core::styles::{
SortDirection, TableGridRecipe, TableHeaderCellConfig, TableRowConfig, TableStyle,
};
use teksilo_core::widget_id::WidgetId;
use teksilo_tokens::{CornerRadius, SurfaceRole};
use crate::primitives::{RectWidget, ZStack};
pub const ROW_HEIGHT: f32 = 28.0;
pub const HEADER_HEIGHT: f32 = 32.0;
pub const CELL_PADDING_HORIZONTAL: f32 = 8.0;
pub const CELL_PADDING_VERTICAL: f32 = 4.0;
pub const RESIZE_HANDLE_WIDTH: f32 = 4.0;
pub const COLUMN_RESIZE_STEP: f32 = 8.0;
pub const GRID_LINE_THICKNESS: f32 = 1.0;
pub const CORNER_RADIUS: f32 = 4.0;
pub const SORT_INDICATOR_SIZE: f32 = 10.0;
pub const FILTER_INDICATOR_SIZE: f32 = 12.0;
pub const HEADER_INTER_CELL_SPACING: f32 = 0.0;
pub const FOCUS_RING_INSET: f32 = 1.0;
pub const MIN_COLUMN_WIDTH_DEFAULT: f32 = 32.0;
pub const TREE_INDENT_PER_LEVEL: f32 = 16.0;
pub const TREE_TWIST_SIZE: f32 = 12.0;
pub const TREE_TWIST_LABEL_GAP: f32 = 4.0;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TableRecipe {
pub row_height: f32,
pub header_height: f32,
pub cell_padding_horizontal: f32,
pub cell_padding_vertical: f32,
pub resize_handle_width: f32,
pub grid_line_thickness: f32,
pub corner_radius: f32,
pub sort_indicator_size: f32,
pub filter_indicator_size: f32,
pub header_inter_cell_spacing: f32,
pub focus_ring_inset: f32,
pub min_column_width_default: f32,
pub tree_indent_per_level: f32,
pub tree_twist_size: f32,
pub tree_twist_label_gap: f32,
}
impl Default for TableRecipe {
fn default() -> Self {
Self {
row_height: ROW_HEIGHT,
header_height: HEADER_HEIGHT,
cell_padding_horizontal: CELL_PADDING_HORIZONTAL,
cell_padding_vertical: CELL_PADDING_VERTICAL,
resize_handle_width: RESIZE_HANDLE_WIDTH,
grid_line_thickness: GRID_LINE_THICKNESS,
corner_radius: CORNER_RADIUS,
sort_indicator_size: SORT_INDICATOR_SIZE,
filter_indicator_size: FILTER_INDICATOR_SIZE,
header_inter_cell_spacing: HEADER_INTER_CELL_SPACING,
focus_ring_inset: FOCUS_RING_INSET,
min_column_width_default: MIN_COLUMN_WIDTH_DEFAULT,
tree_indent_per_level: TREE_INDENT_PER_LEVEL,
tree_twist_size: TREE_TWIST_SIZE,
tree_twist_label_gap: TREE_TWIST_LABEL_GAP,
}
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct RecipeTableStyle {
pub recipe: TableRecipe,
}
impl RecipeTableStyle {
pub fn new(recipe: TableRecipe) -> Self {
Self { recipe }
}
}
impl TableStyle for RecipeTableStyle {
fn make_header_cell(&self, cfg: &TableHeaderCellConfig, ctx: &mut BuildContext) -> WidgetId {
let hovered = cfg.is_hovered.clone();
let resizing = cfg.is_resizing.clone();
let role: Signal<SurfaceRole> = hovered.zip(&resizing).map(|(h, r)| {
if *r {
SurfaceRole::Pressed
} else if *h {
SurfaceRole::Hover
} else {
SurfaceRole::Transparent
}
});
let bg = ctx.add(
RectWidget::new()
.background(ColorProp::DynamicSurfaceRole(role))
.corner_radius(CornerRadius::uniform(self.recipe.corner_radius)),
);
ctx.add(ZStack::new().add_child(bg).add_child(cfg.label))
}
fn make_sort_indicator(&self, _direction: SortDirection, ctx: &mut BuildContext) -> WidgetId {
ctx.add(crate::primitives::Spacer::new())
}
fn make_row_background(&self, cfg: &TableRowConfig, ctx: &mut BuildContext) -> WidgetId {
let alt = cfg.is_alt;
let effective_focus: Option<Signal<bool>> = match (&cfg.is_focused, &cfg.is_window_active) {
(Some(f), Some(wa)) => Some(f.and(wa)),
(Some(f), None) => Some(f.clone()),
(None, Some(wa)) => Some(wa.clone()),
(None, None) => None,
};
let role: Signal<SurfaceRole> = match effective_focus {
Some(focus) => cfg
.is_selected
.clone()
.zip(&cfg.is_hovered)
.zip(&focus)
.map(move |((sel, hov), foc)| {
if *sel {
if *foc {
SurfaceRole::Selected
} else {
SurfaceRole::SelectedInactive
}
} else if *hov {
SurfaceRole::Hover
} else if alt {
SurfaceRole::AltRow
} else {
SurfaceRole::Transparent
}
}),
None => cfg
.is_selected
.clone()
.zip(&cfg.is_hovered)
.map(move |(sel, hov)| {
if *sel {
SurfaceRole::Selected
} else if *hov {
SurfaceRole::Hover
} else if alt {
SurfaceRole::AltRow
} else {
SurfaceRole::Transparent
}
}),
};
ctx.add(RectWidget::new().background(ColorProp::DynamicSurfaceRole(role)))
}
fn grid(&self) -> TableGridRecipe {
TableGridRecipe::default()
}
}