use crate::{Fill, Paint, Stroke, Transform2D};
use astrelis_render::Color;
#[derive(Debug, Clone, PartialEq)]
pub struct Style {
pub fill: Option<Fill>,
pub stroke: Option<Stroke>,
pub transform: Transform2D,
}
impl Style {
pub fn new() -> Self {
Self {
fill: None,
stroke: None,
transform: Transform2D::IDENTITY,
}
}
pub fn fill(paint: impl Into<Paint>) -> Self {
Self {
fill: Some(Fill::from_paint(paint.into())),
stroke: None,
transform: Transform2D::IDENTITY,
}
}
pub fn fill_color(color: Color) -> Self {
Self::fill(Paint::solid(color))
}
pub fn stroke(paint: impl Into<Paint>, width: f32) -> Self {
Self {
fill: None,
stroke: Some(Stroke::from_paint(paint.into(), width)),
transform: Transform2D::IDENTITY,
}
}
pub fn stroke_color(color: Color, width: f32) -> Self {
Self::stroke(Paint::solid(color), width)
}
pub fn fill_and_stroke(
fill_paint: impl Into<Paint>,
stroke_paint: impl Into<Paint>,
stroke_width: f32,
) -> Self {
Self {
fill: Some(Fill::from_paint(fill_paint.into())),
stroke: Some(Stroke::from_paint(stroke_paint.into(), stroke_width)),
transform: Transform2D::IDENTITY,
}
}
pub fn with_fill(mut self, fill: Fill) -> Self {
self.fill = Some(fill);
self
}
pub fn with_fill_paint(mut self, paint: impl Into<Paint>) -> Self {
self.fill = Some(Fill::from_paint(paint.into()));
self
}
pub fn with_fill_color(mut self, color: Color) -> Self {
self.fill = Some(Fill::solid(color));
self
}
pub fn with_stroke(mut self, stroke: Stroke) -> Self {
self.stroke = Some(stroke);
self
}
pub fn with_stroke_paint(mut self, paint: impl Into<Paint>, width: f32) -> Self {
self.stroke = Some(Stroke::from_paint(paint.into(), width));
self
}
pub fn with_stroke_color(mut self, color: Color, width: f32) -> Self {
self.stroke = Some(Stroke::solid(color, width));
self
}
pub fn with_transform(mut self, transform: Transform2D) -> Self {
self.transform = transform;
self
}
pub fn has_fill(&self) -> bool {
self.fill.as_ref().is_some_and(|f| f.opacity > 0.0)
}
pub fn has_stroke(&self) -> bool {
self.stroke.as_ref().is_some_and(|s| s.is_visible())
}
pub fn is_visible(&self) -> bool {
self.has_fill() || self.has_stroke()
}
pub fn get_fill_color(&self) -> Option<Color> {
self.fill.as_ref().and_then(|f| f.effective_color())
}
pub fn get_stroke_color(&self) -> Option<Color> {
self.stroke.as_ref().and_then(|s| s.effective_color())
}
}
impl Default for Style {
fn default() -> Self {
Self::new()
}
}
pub mod presets {
use super::*;
pub fn red_fill() -> Style {
Style::fill_color(Color::RED)
}
pub fn green_fill() -> Style {
Style::fill_color(Color::GREEN)
}
pub fn blue_fill() -> Style {
Style::fill_color(Color::BLUE)
}
pub fn black_stroke() -> Style {
Style::stroke_color(Color::BLACK, 1.0)
}
pub fn white_stroke() -> Style {
Style::stroke_color(Color::WHITE, 1.0)
}
pub fn outline() -> Style {
Style::stroke_color(Color::BLACK, 1.0)
}
pub fn debug() -> Style {
Style::fill_and_stroke(Paint::solid(Color::RED), Paint::solid(Color::BLUE), 2.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_style() {
let style = Style::new();
assert!(!style.is_visible());
assert!(!style.has_fill());
assert!(!style.has_stroke());
}
#[test]
fn test_fill_only() {
let style = Style::fill_color(Color::RED);
assert!(style.is_visible());
assert!(style.has_fill());
assert!(!style.has_stroke());
}
#[test]
fn test_stroke_only() {
let style = Style::stroke_color(Color::BLUE, 2.0);
assert!(style.is_visible());
assert!(!style.has_fill());
assert!(style.has_stroke());
}
#[test]
fn test_fill_and_stroke() {
let style =
Style::fill_and_stroke(Paint::solid(Color::RED), Paint::solid(Color::BLACK), 1.0);
assert!(style.has_fill());
assert!(style.has_stroke());
}
}