beuvy-runtime 0.1.0

A low-level Bevy UI kit with reusable controls and utility-class styling.
Documentation
use super::{AddLink, DisabledLink, Link, LinkLabel, state};
use crate::build_pending::UiBuildPending;
use crate::focus::{UiFocusOutlineOnFocusOnly, UiFocusable, hidden_outline};
use crate::interaction_style::UiDisabled;
use crate::style::{
    apply_utility_patch, resolve_classes_with_fallback, root_visual_styles_from_patch,
    text_primary_color,
};
use crate::text::{AddText, typography_from_patch};
use bevy::picking::Pickable;
use bevy::prelude::*;

const DEFAULT_LINK_CLASS: &str = "link-root";
const DEFAULT_LINK_LABEL_CLASS: &str = "link-label";

pub(super) fn add_link(mut commands: Commands, query: Query<(Entity, &AddLink)>) {
    for (entity, add_link) in &query {
        let add_link = add_link.clone();
        let root_patch = resolve_classes_with_fallback(
            DEFAULT_LINK_CLASS,
            add_link.class.as_deref(),
            "link root",
        );
        let label_patch = resolve_classes_with_fallback(
            DEFAULT_LINK_LABEL_CLASS,
            add_link.label_class.as_deref(),
            "link label",
        );
        let root_styles = root_visual_styles_from_patch(&root_patch);

        let mut node = Node::default();
        apply_utility_patch(&mut node, &root_patch);

        commands
            .entity(entity)
            .queue_silenced(move |mut entity_commands: EntityWorldMut| {
                entity_commands.insert((
                    Link {
                        name: add_link.name.clone(),
                        href: add_link.href.clone(),
                    },
                    Name::new(add_link.name.clone()),
                    node,
                    Visibility::Visible,
                    Pickable::default(),
                    hidden_outline(),
                    UiFocusOutlineOnFocusOnly,
                ));

                if let Some(styles) = root_styles.clone() {
                    entity_commands.insert(styles);
                }

                let label = entity_commands.world_scope(|world| {
                    let mut label_node = Node::default();
                    apply_utility_patch(&mut label_node, &label_patch);
                    let typography =
                        typography_from_patch(&label_patch, add_link.label_typography.clone());
                    world
                        .spawn((
                            label_node,
                            AddText {
                                text: add_link.text.clone(),
                                size: typography.font_size,
                                color: label_patch
                                    .visual
                                    .text_color
                                    .as_deref()
                                    .and_then(crate::style::resolve_color_value)
                                    .unwrap_or_else(text_primary_color),
                                ..default()
                            }
                            .typography(typography),
                        ))
                        .id()
                });
                entity_commands.add_child(label);
                entity_commands.insert(LinkLabel { entity: label });

                if add_link.disabled {
                    entity_commands.insert((DisabledLink, UiDisabled));
                } else {
                    entity_commands.insert(UiFocusable);
                }

                entity_commands
                    .observe(state::link_click)
                    .remove::<AddLink>()
                    .remove::<UiBuildPending>();
            });
    }
}