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::*;

/// Latest cursor position in screen (pixel) space, updated by the host app
/// from `winit`'s `CursorMoved` event.
#[derive(Resource, Clone, Copy, Debug, Default)]
pub struct CursorPosition {
    pub x: f32,
    pub y: f32,
}

/// Mouse button and wheel state, updated by the host app from `winit`'s
/// `MouseInput` and `MouseWheel` events. The single-frame parts —
/// `just_pressed`, `just_released`, `scroll` — are cleared automatically at
/// the end of every [`crate::ecs::App::update`] tick.
#[derive(Resource, Clone, Copy, Debug, Default)]
pub struct MouseInput {
    pub left_down: bool,
    pub just_pressed: bool,
    /// True for the single frame the button came up — where a drag ends.
    pub just_released: bool,
    /// Whether the right button is held — what an orbit camera drags with.
    pub right_down: bool,
    /// Wheel clicks since the last frame, positive away from the user. Every
    /// event between two frames is summed, so a fast flick is not thrown away.
    pub scroll: f32,
}

/// Current window size in pixels, updated by the host app on resize.
#[derive(Resource, Clone, Copy, Debug)]
pub struct ScreenSize {
    pub width: f32,
    pub height: f32,
}

impl Default for ScreenSize {
    fn default() -> Self {
        Self {
            width: 800.0,
            height: 600.0,
        }
    }
}

/// The flat-colored quads (panel/button backgrounds, bitmap-font glyphs)
/// produced this frame by [`super::systems::collect_quads_system`], ready to
/// hand to [`super::UiRenderer::render`].
#[derive(Resource, Default)]
pub struct UiDrawList(pub Vec<super::QuadInstance>);

/// The entities [`super::Panel::spawn`] made, kept on the panel entity so
/// [`super::systems::relayout_menu_system`] can reposition them whenever
/// [`ScreenSize`] changes — and so several panels can coexist, each with its
/// own anchor.
#[derive(Component, Clone, Debug)]
pub struct MenuLayout {
    pub panel: Entity,
    pub buttons: Vec<Entity>,
    pub anchor: super::layout::Anchor,
    /// How big this panel draws its buttons, already sized to fit the longest
    /// label it was built with.
    pub metrics: super::layout::Metrics,
    /// How far it was nudged off its anchor, so a resize can nudge it again.
    ///
    /// Without this a panel placed clear of something else in its corner --
    /// the dev menu above its badge -- lands back on top of it the first time
    /// the window changes size.
    pub offset: (f32, f32),
    /// The title bar, its close button, and how tall the bar is.
    ///
    /// The bar grows the panel upwards and pushes the buttons down, so a
    /// relayout that forgets it puts the buttons under the bar.
    pub bar: Option<Entity>,
    pub close: Option<Entity>,
    pub bar_height: f32,
    /// The panels this one's rows open, spawned with it and hidden until one
    /// is hovered.
    ///
    /// Held so that [`entities`](Self::entities) can answer for the whole
    /// tree. Nothing else knows a submenu is there -- it is not a child of
    /// anything in the ECS sense -- so a caller taking a menu down had to walk
    /// the `Submenu` components to find them, and could quietly miss one.
    pub submenus: Vec<SubmenuLayout>,
}

/// A panel one of a menu's rows opens, and the triangle pointing back at that
/// row.
///
/// The pointer is here rather than inside the layout because the row it points
/// *from* is the parent's: the child panel would have no way to know it was
/// opened by anything.
#[derive(Clone, Debug)]
pub struct SubmenuLayout {
    pub layout: MenuLayout,
    pub pointer: Entity,
}

impl MenuLayout {
    /// Every entity the panel owns, submenus and all, for taking it down as a
    /// unit.
    ///
    /// A `Vec` rather than an iterator because it recurses: a submenu is a
    /// menu, and one of its rows can open another.
    pub fn entities(&self) -> Vec<Entity> {
        let mut entities = vec![self.panel];
        entities.extend(self.bar);
        entities.extend(self.close);
        entities.extend(self.buttons.iter().copied());
        for submenu in &self.submenus {
            entities.push(submenu.pointer);
            entities.extend(submenu.layout.entities());
        }
        entities
    }
}

/// What the pointer is over, and therefore who gets to act on it.
///
/// A wheel turned over a panel scrolls the panel; the same wheel turned over
/// the world zooms the camera. Without somewhere to say which, both happen --
/// the list scrolls and the camera lurches, which is the bug this exists to
/// stop.
///
/// Filled every frame by [`super::systems::update_pointer_capture_system`],
/// before anything reads the mouse.
#[derive(Resource, Clone, Copy, Debug, Default)]
pub struct PointerCapture {
    /// The pointer is inside a visible panel.
    pub over_panel: bool,
}

impl PointerCapture {
    /// Whether the world -- a camera rig, a piece being dragged -- should
    /// ignore the mouse this frame.
    pub fn taken(self) -> bool {
        self.over_panel
    }
}