codecraft 0.1.1

A minimalist 3D game engine built on parts of Bevy (ECS, color) with wgpu and winit: OpenPBR materials, clustered lighting, an immediate-mode UI, audio and gamepad haptics
Documentation
use bevy_ecs::prelude::*;

use super::color::Color;
use super::rect::Rect;

/// Top-left corner of a UI entity, in screen (pixel) space.
#[derive(Component, Clone, Copy, Debug, Default)]
pub struct Position {
    pub x: f32,
    pub y: f32,
}

/// Size of a UI entity, in pixels.
#[derive(Component, Clone, Copy, Debug)]
pub struct Size {
    pub width: f32,
    pub height: f32,
}

impl Position {
    pub fn rect(&self, size: &Size) -> Rect {
        Rect::new(self.x, self.y, size.width, size.height)
    }
}

impl From<Rect> for (Position, Size) {
    fn from(rect: Rect) -> Self {
        (
            Position {
                x: rect.x,
                y: rect.y,
            },
            Size {
                width: rect.width,
                height: rect.height,
            },
        )
    }
}

/// Background fill color, used by [`super::Panel`] entities.
#[derive(Component, Clone, Copy, Debug)]
pub struct Background(pub Color);

/// Text drawn centered over a UI entity (see [`super::Button`]).
#[derive(Component, Clone, Debug)]
pub struct Label(pub String);

/// How big a [`Label`] is drawn; the default size when absent.
#[derive(Component, Clone, Copy, Debug)]
pub struct LabelSize(pub f32);

/// Color the [`Label`] is drawn in; defaults to white when absent.
#[derive(Component, Clone, Copy, Debug)]
pub struct TextColor(pub Color);

impl Default for TextColor {
    fn default() -> Self {
        Self(Color::WHITE)
    }
}

/// An icon drawn over a UI entity, from [`super::icons`].
///
/// The path has to be one of the icons compiled in; anything else logs once
/// and draws nothing, which is better than drawing the wrong thing.
#[derive(Component, Clone, Copy, Debug)]
pub struct Icon {
    pub path: &'static str,
    /// What to tint the mask. The icons are white masks, so this *is* the
    /// icon's colour rather than a modulation of it.
    pub color: Color,
}

impl Icon {
    pub fn new(path: &'static str) -> Self {
        Self {
            path,
            color: Color::WHITE,
        }
    }

    pub fn tinted(path: &'static str, color: Color) -> Self {
        Self { path, color }
    }
}

/// Where a [`Label`] sits inside its rect.
///
/// A button centres its label because a button is a target you aim at. A menu
/// row does not: a column of centred names is unreadable, and a menu is a
/// list you read down.
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum LabelAlign {
    #[default]
    Center,
    Left,
}

/// The icons a button wears either side of its label.
///
/// Separate from [`Icon`], which covers a whole entity -- these are inset into
/// a button that also has words in it. The leading one says what the entry
/// *is*; the trailing one says what it does when you press it, which for a
/// menu means which way it opens.
#[derive(Component, Clone, Copy, Debug, Default)]
pub struct ButtonIcons {
    pub leading: Option<&'static str>,
    pub trailing: Option<&'static str>,
}

impl ButtonIcons {
    pub fn leading(icon: &'static str) -> Self {
        Self {
            leading: Some(icon),
            trailing: None,
        }
    }
}