beuvy-runtime 0.1.0

A low-level Bevy UI kit with reusable controls and utility-class styling.
Documentation
mod animate;

use crate::utility::UtilityTransitionTiming;
use bevy::prelude::*;

pub(crate) use animate::animate_state_visual_styles;

#[derive(Debug, Clone, Copy, PartialEq)]
pub(super) struct UiAnimatedColor {
    pub(super) from: Color,
    pub(super) to: Color,
}

#[derive(Component, Debug, Clone, Copy)]
pub(super) struct UiStateVisualTransition {
    pub(super) background: Option<UiAnimatedColor>,
    pub(super) border: Option<UiAnimatedColor>,
    pub(super) text: Option<UiAnimatedColor>,
    pub(super) outline: Option<UiAnimatedColor>,
    pub(super) duration_secs: f32,
    pub(super) elapsed_secs: f32,
    pub(super) timing: UtilityTransitionTiming,
}

impl UiStateVisualTransition {
    pub(super) fn has_values(&self) -> bool {
        self.background.is_some()
            || self.border.is_some()
            || self.text.is_some()
            || self.outline.is_some()
    }
}

pub(super) fn transition_color(
    current: Option<Color>,
    target: Option<Color>,
) -> Option<UiAnimatedColor> {
    match (current, target) {
        (Some(from), Some(to)) if !color_nearly_equal(from, to) => {
            Some(UiAnimatedColor { from, to })
        }
        _ => None,
    }
}

pub(super) fn eased_progress(value: f32, timing: UtilityTransitionTiming) -> f32 {
    let value = value.clamp(0.0, 1.0);
    match timing {
        UtilityTransitionTiming::Linear => value,
        UtilityTransitionTiming::EaseIn => value * value,
        UtilityTransitionTiming::EaseOut => 1.0 - (1.0 - value) * (1.0 - value),
        UtilityTransitionTiming::EaseInOut => {
            if value < 0.5 {
                2.0 * value * value
            } else {
                1.0 - (-2.0 * value + 2.0).powi(2) / 2.0
            }
        }
    }
}

pub(super) fn color_nearly_equal(lhs: Color, rhs: Color) -> bool {
    let lhs = lhs.to_srgba().to_f32_array();
    let rhs = rhs.to_srgba().to_f32_array();
    lhs.into_iter()
        .zip(rhs)
        .all(|(lhs, rhs)| (lhs - rhs).abs() <= 0.002)
}