use std::rc::Rc;
use crate::geometry::Size;
use crate::style::{Align, Direction, Edges, Justify, Length, Overflow, Position, Style};
use crate::theme::{ColorValue, RadiusValue};
use crate::tree::interaction::Interaction;
type Setter = Rc<dyn Fn(&mut Style)>;
pub struct StylePatch {
setters: Vec<Setter>,
}
impl StylePatch {
pub(crate) fn apply(&self, style: &mut Style) {
for setter in &self.setters {
setter(style);
}
}
}
#[derive(Default)]
pub struct PatchBuilder {
setters: Vec<Setter>,
}
impl PatchBuilder {
fn push(mut self, set: impl Fn(&mut Style) + 'static) -> Self {
self.setters.push(Rc::new(set));
self
}
pub fn width(self, width: Length) -> Self {
self.push(move |style| style.layout.width = width)
}
pub fn height(self, height: Length) -> Self {
self.push(move |style| style.layout.height = height)
}
pub fn size(self, size: Size) -> Self {
self.push(move |style| {
style.layout.width = Length::px(size.width);
style.layout.height = Length::px(size.height);
})
}
pub fn min_width(self, width: Length) -> Self {
self.push(move |style| style.layout.min_width = width)
}
pub fn min_height(self, height: Length) -> Self {
self.push(move |style| style.layout.min_height = height)
}
pub fn max_width(self, width: Length) -> Self {
self.push(move |style| style.layout.max_width = width)
}
pub fn max_height(self, height: Length) -> Self {
self.push(move |style| style.layout.max_height = height)
}
pub fn padding(self, padding: Length) -> Self {
self.push(move |style| style.layout.padding = Edges::all(padding))
}
pub fn padding_xy(self, padding: Size) -> Self {
self.push(move |style| {
style.layout.padding = Edges::xy(Length::px(padding.width), Length::px(padding.height));
})
}
pub fn margin(self, margin: Length) -> Self {
self.push(move |style| style.layout.margin = Edges::all(margin))
}
pub fn gap(self, gap: Size) -> Self {
self.push(move |style| style.layout.gap = gap)
}
pub fn direction(self, direction: Direction) -> Self {
self.push(move |style| style.layout.direction = direction)
}
pub fn row(self) -> Self {
self.push(|style| style.layout.direction = Direction::Row)
}
pub fn column(self) -> Self {
self.push(|style| style.layout.direction = Direction::Column)
}
pub fn wrap(self, wrap: bool) -> Self {
self.push(move |style| style.layout.wrap = wrap)
}
pub fn grow(self, grow: f32) -> Self {
self.push(move |style| style.layout.grow = grow)
}
pub fn shrink(self, shrink: f32) -> Self {
self.push(move |style| style.layout.shrink = shrink)
}
pub fn basis(self, basis: Length) -> Self {
self.push(move |style| style.layout.basis = basis)
}
pub fn align_items(self, align: Align) -> Self {
self.push(move |style| style.layout.align_items = Some(align))
}
pub fn justify_content(self, justify: Justify) -> Self {
self.push(move |style| style.layout.justify_content = Some(justify))
}
pub fn absolute(self) -> Self {
self.push(|style| style.layout.position = Position::Absolute)
}
pub fn inset(self, inset: Edges<Length>) -> Self {
self.push(move |style| style.layout.inset = inset)
}
pub fn border(self, width: f32) -> Self {
self.push(move |style| style.layout.border_width = Edges::all(width))
}
pub fn clip(self, clip: bool) -> Self {
self.push(move |style| {
style.layout.overflow = if clip {
Overflow::Clip
} else {
Overflow::Visible
};
})
}
pub fn hidden(self, hidden: bool) -> Self {
self.push(move |style| style.layout.hidden = hidden)
}
pub fn background(self, color: impl Into<ColorValue>) -> Self {
let color = color.into();
self.push(move |style| style.paint.background = Some(color))
}
pub fn border_color(self, color: impl Into<ColorValue>) -> Self {
let color = color.into();
self.push(move |style| style.paint.border_color = Some(color))
}
pub fn text_color(self, color: impl Into<ColorValue>) -> Self {
let color = color.into();
self.push(move |style| style.paint.text_color = Some(color))
}
pub fn corner_radius(self, radius: impl Into<RadiusValue>) -> Self {
let radius = radius.into();
self.push(move |style| style.paint.corner_radius = radius)
}
pub(crate) fn patch(self) -> StylePatch {
StylePatch {
setters: self.setters,
}
}
}
pub struct Variants {
pub hovered: Option<StylePatch>,
pub focused: Option<StylePatch>,
pub pressed: Option<StylePatch>,
pub disabled: Option<StylePatch>,
}
impl Default for Variants {
fn default() -> Variants {
Variants {
hovered: None,
focused: None,
pressed: None,
disabled: None,
}
}
}
impl Variants {
pub(crate) fn is_empty(&self) -> bool {
self.hovered.is_none()
&& self.focused.is_none()
&& self.pressed.is_none()
&& self.disabled.is_none()
}
}
pub(crate) fn resolve(base: &Style, variants: &Variants, interaction: Interaction) -> Style {
let mut style = base.clone();
if interaction.has(Interaction::HOVERED)
&& let Some(patch) = &variants.hovered
{
patch.apply(&mut style);
}
if interaction.has(Interaction::KEYBOARD_FOCUS)
&& let Some(patch) = &variants.focused
{
patch.apply(&mut style);
}
if interaction.has(Interaction::PRESSED)
&& let Some(patch) = &variants.pressed
{
patch.apply(&mut style);
}
if interaction.has(Interaction::DISABLED)
&& let Some(patch) = &variants.disabled
{
patch.apply(&mut style);
}
style
}
#[cfg(test)]
#[path = "variants_tests.rs"]
mod tests;