use bevy::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum IconWeight {
Thin,
ExtraLight,
Light,
#[default]
Regular,
Medium,
SemiBold,
Bold,
}
impl IconWeight {
pub fn value(&self) -> u16 {
match self {
IconWeight::Thin => 100,
IconWeight::ExtraLight => 200,
IconWeight::Light => 300,
IconWeight::Regular => 400,
IconWeight::Medium => 500,
IconWeight::SemiBold => 600,
IconWeight::Bold => 700,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum IconGrade {
Low,
#[default]
Normal,
High,
}
impl IconGrade {
pub fn value(&self) -> i16 {
match self {
IconGrade::Low => -25,
IconGrade::Normal => 0,
IconGrade::High => 200,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum IconOpticalSize {
Small,
#[default]
Default,
Large,
ExtraLarge,
}
impl IconOpticalSize {
pub fn value(&self) -> u8 {
match self {
IconOpticalSize::Small => 20,
IconOpticalSize::Default => 24,
IconOpticalSize::Large => 40,
IconOpticalSize::ExtraLarge => 48,
}
}
pub fn size_px(&self) -> f32 {
self.value() as f32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Component)]
pub struct IconStyle {
pub filled: bool,
pub weight: IconWeight,
pub grade: IconGrade,
pub optical_size: IconOpticalSize,
pub color: Option<Color>,
pub size: Option<f32>,
}
impl IconStyle {
pub fn outlined() -> Self {
Self::default()
}
pub fn filled() -> Self {
Self {
filled: true,
..default()
}
}
pub fn with_fill(mut self, filled: bool) -> Self {
self.filled = filled;
self
}
pub fn with_weight(mut self, weight: IconWeight) -> Self {
self.weight = weight;
self
}
pub fn with_grade(mut self, grade: IconGrade) -> Self {
self.grade = grade;
self
}
pub fn with_optical_size(mut self, size: IconOpticalSize) -> Self {
self.optical_size = size;
self
}
pub fn with_color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
pub fn with_size(mut self, size: f32) -> Self {
self.size = Some(size);
self
}
pub fn effective_size(&self) -> f32 {
self.size.unwrap_or_else(|| self.optical_size.size_px())
}
pub fn fill_value(&self) -> f32 {
if self.filled {
1.0
} else {
0.0
}
}
pub fn small() -> Self {
Self {
optical_size: IconOpticalSize::Small,
..default()
}
}
pub fn large() -> Self {
Self {
optical_size: IconOpticalSize::Large,
..default()
}
}
pub fn extra_large() -> Self {
Self {
optical_size: IconOpticalSize::ExtraLarge,
..default()
}
}
pub fn bold() -> Self {
Self {
weight: IconWeight::Bold,
..default()
}
}
pub fn light() -> Self {
Self {
weight: IconWeight::Light,
..default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_icon_weight_values() {
assert_eq!(IconWeight::Thin.value(), 100);
assert_eq!(IconWeight::Regular.value(), 400);
assert_eq!(IconWeight::Bold.value(), 700);
}
#[test]
fn test_icon_grade_values() {
assert_eq!(IconGrade::Low.value(), -25);
assert_eq!(IconGrade::Normal.value(), 0);
assert_eq!(IconGrade::High.value(), 200);
}
#[test]
fn test_icon_optical_size_values() {
assert_eq!(IconOpticalSize::Small.value(), 20);
assert_eq!(IconOpticalSize::Default.value(), 24);
assert_eq!(IconOpticalSize::Large.value(), 40);
assert_eq!(IconOpticalSize::ExtraLarge.value(), 48);
}
#[test]
fn test_icon_style_defaults() {
let style = IconStyle::default();
assert!(!style.filled);
assert_eq!(style.weight, IconWeight::Regular);
assert_eq!(style.grade, IconGrade::Normal);
assert_eq!(style.optical_size, IconOpticalSize::Default);
}
#[test]
fn test_icon_style_builder() {
let style = IconStyle::outlined()
.with_fill(true)
.with_weight(IconWeight::Bold)
.with_size(32.0);
assert!(style.filled);
assert_eq!(style.weight, IconWeight::Bold);
assert_eq!(style.effective_size(), 32.0);
}
#[test]
fn test_icon_style_presets() {
assert!(IconStyle::filled().filled);
assert!(!IconStyle::outlined().filled);
assert_eq!(IconStyle::small().optical_size, IconOpticalSize::Small);
assert_eq!(IconStyle::large().optical_size, IconOpticalSize::Large);
assert_eq!(IconStyle::bold().weight, IconWeight::Bold);
assert_eq!(IconStyle::light().weight, IconWeight::Light);
}
}