bevy_pages 0.2.0

A lightweight and elegant framework to upgrade your Bevy UI experience.
Documentation
use crate::element::{ElementProps, ElementWidget};
use crate::parser::color::parse_color;
use crate::parser::values::{parse_attribute, parse_matches};
use crate::widgets::Widget;
use bevy::asset::AssetServer;
use bevy::color::Color;
use bevy::prelude::*;
use roxmltree::Node as XmlNode;

/// The layout orientation of the divider line.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum DividerOrientation {
    /// A horizontal line across the container.
    #[default]
    Horizontal,
    /// A vertical line down the container.
    Vertical,
}

impl DividerOrientation {
    /// Parses the divider orientation from a string.
    pub fn parse(s: &str) -> Result<Self, String> {
        parse_matches(
            s,
            &[
                ("horizontal", || Ok(Self::Horizontal)),
                ("vertical", || Ok(Self::Vertical)),
            ],
        )
    }
}

/// A widget to show a divider line.
///
/// ## XML Usage
///
/// Build a divider widget using the `<Divider />` tag.
///
/// ### Attributes
/// - `orientation = "<horizontal|vertical>"`: The orientation of the line.
/// - `color = "<color>"`: The color of the divider line.
///
/// ## Logic
///
/// This widget does not have any special code logic.
/// Use different element events to implement custom behavior.
#[derive(Component, Clone, Debug, PartialEq, Default)]
pub struct DividerWidget {
    /// Orientation of the line.
    pub orientation: DividerOrientation,
    /// Color of the divider line.
    pub color: Option<Color>,
}

impl Widget for DividerWidget {
    fn spawn(&self, commands: &mut EntityCommands, _: &AssetServer) -> Entity {
        commands.id()
    }

    fn parse(
        node: &XmlNode,
        prefix: Option<&str>,
        base: Option<&ElementWidget>,
    ) -> Result<Self, String>
    where
        Self: Sized,
    {
        let base_div = match base {
            Some(ElementWidget::Divider(div)) => Some(div),
            _ => None,
        };

        let orientation = parse_attribute(node, "orientation", prefix, DividerOrientation::parse)?
            .or_else(|| base_div.map(|b| b.orientation))
            .unwrap_or_default();

        let color = parse_attribute(node, "color", prefix, parse_color)?
            .or_else(|| base_div.and_then(|b| b.color));

        Ok(Self { orientation, color })
    }

    fn apply_defaults(
        node: &XmlNode,
        default: &mut ElementProps,
        hover: &mut ElementProps,
        click: &mut ElementProps,
    ) {
        let is_vertical = node
            .attribute("orientation")
            .map(|s| s.trim() == "vertical")
            .unwrap_or(false);

        let default_color = Color::srgb(0.25, 0.25, 0.28);

        if is_vertical {
            // Vertical default
            if !node.has_attribute("width") {
                default.node.width = Val::Px(1.0);
            }

            if !node.has_attribute("height") {
                default.node.height = Val::Percent(100.0);
            }

            if !node.has_attribute("margin") {
                default.node.margin = UiRect::axes(Val::Px(8.0), Val::Px(0.0));
            }
        } else {
            // Horizontal default
            if !node.has_attribute("width") {
                default.node.width = Val::Percent(100.0);
            }

            if !node.has_attribute("height") {
                default.node.height = Val::Px(1.0);
            }

            if !node.has_attribute("margin") {
                default.node.margin = UiRect::axes(Val::Px(0.0), Val::Px(8.0));
                hover.node.margin = default.node.margin;
                click.node.margin = default.node.margin;
            }
        }

        if default.bg_color.is_none() {
            default.bg_color = Some(default_color);
        }
    }
}