cotis-layout 0.1.0-alpha.1

Flexbox-style layout engine for Cotis
Documentation
//! Layout output types consumed by pipes and renderers.
//!
//! The primary public types are [`RenderCommandOutput`] and its variants, plus
//! [`TextDrawPayload`] for text fragments attached to element commands.
//!
//! Pipes such as `CotisLayoutToRenderListPipeForGenerics` in `cotis-pipes` walk a
//! `RenderCommandOutput` stream and decode each element's `custom` payload into concrete draw
//! commands.
//!
//! # Internal types
//!
//! The layout engine also defines tree and state types (`LayoutElement`, `LayoutElementConfig`,
//! `LayoutElementInfo`, `ChildWrapping`, etc.) in crate-private submodules. These appear in the
//! return type of [`CotisLayoutManager::get_cache_element`](crate::preamble::CotisLayoutManager::get_cache_element)
//! for post-frame inspection but are **not stable public API** — their visibility is under review.
//!
//! Note: layout-internal `ChildWrapping` (`None` / `Text`) is partially
//! unrelated to the experimental `child_wrapping` field on `cotis-defaults` layout styles.

use cotis::utils::ElementId;
use cotis_utils::math::BoundingBox;

pub(crate) mod info_types;

pub(crate) mod layout_tree;

pub(crate) mod layout_states;

/// Opens a clip region for subsequent [`RenderCommandOutput::Element`] commands.
///
/// Paired with [`RenderCommandClipEnd`] for the same element `id`. Pipes push and pop clip
/// stacks based on these markers.
#[derive(Debug, Clone)]
#[cfg_attr(
    feature = "serialization",
    derive(serde::Serialize, serde::Deserialize)
)]
pub struct RenderCommandClipStart {
    /// Clip bounds in screen space.
    pub bounds: BoundingBox,
    /// Element that owns this clip container.
    pub id: ElementId,
    /// Draw order relative to siblings (higher draws on top).
    pub z_index: i32,
}

/// Closes the clip region opened by the matching [`RenderCommandClipStart`].
#[derive(Debug, Clone)]
#[cfg_attr(
    feature = "serialization",
    derive(serde::Serialize, serde::Deserialize)
)]
pub struct RenderCommandClipEnd {
    /// Clip bounds in screen space (same as the matching start).
    pub bounds: BoundingBox,
    /// Element that owns this clip container.
    pub id: ElementId,
    /// Draw order relative to siblings.
    pub z_index: i32,
}

/// One step in the layout-to-renderer command stream.
///
/// Emitted in depth-first tree order with z-index sorting applied per subtree root. Pipes
/// translate each variant into renderer draw calls.
///
/// # Examples
///
/// ```
/// use cotis::utils::ElementIdConfig;
/// use cotis_layout::layout_struct::{
///     RenderCommandClipEnd, RenderCommandClipStart, RenderCommandOutput,
///     RenderCommandOutputElement,
/// };
/// use cotis_utils::math::BoundingBox;
///
/// let id = ElementIdConfig::new_empty().get_handle();
/// let bounds = BoundingBox { x: 0.0, y: 0.0, width: 100.0, height: 50.0 };
///
/// let element: RenderCommandOutput<&str> = RenderCommandOutput::Element(RenderCommandOutputElement {
///     bounds,
///     z_index: 0,
///     text: None,
///     custom: "my-payload",
/// });
///
/// let clip_start: RenderCommandOutput<()> = RenderCommandOutput::ClipStart(RenderCommandClipStart {
///     bounds,
///     id: id.clone(),
///     z_index: 1,
/// });
///
/// let clip_end: RenderCommandOutput<()> = RenderCommandOutput::ClipEnd(RenderCommandClipEnd {
///     bounds,
///     id,
///     z_index: 1,
/// });
///
/// assert!(matches!(element, RenderCommandOutput::Element(_)));
/// assert!(matches!(clip_start, RenderCommandOutput::ClipStart(_)));
/// assert!(matches!(clip_end, RenderCommandOutput::ClipEnd(_)));
/// ```
#[derive(Debug, Clone)]
#[cfg_attr(
    feature = "serialization",
    derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
    feature = "serialization",
    serde(bound(deserialize = "
    Custom: serde::de::DeserializeOwned,
"))
)]
pub enum RenderCommandOutput<Custom> {
    /// A sized and positioned element ready for rendering.
    Element(RenderCommandOutputElement<Custom>),
    /// Begin clip scissor for a scroll/clip container.
    ClipStart(RenderCommandClipStart),
    /// End clip scissor for a scroll/clip container.
    ClipEnd(RenderCommandClipEnd),
}

mod text_draw_payload;
pub use text_draw_payload::TextDrawPayload;

/// Render data for one layout element.
///
/// The `custom` field carries the user's config type (`T` from `RenderCommandOutput<T>`).
/// When the element is a text leaf, `text` holds the substring and typography to draw.
#[derive(Debug, Clone)]
#[cfg_attr(
    feature = "serialization",
    derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
    feature = "serialization",
    serde(bound(deserialize = "
    Custom: serde::de::DeserializeOwned,
"))
)]
pub struct RenderCommandOutputElement<Custom> {
    /// Final axis-aligned bounds in screen space.
    pub bounds: BoundingBox,
    /// Draw order relative to siblings (higher draws on top).
    pub z_index: i32,
    /// Text fragment when this element is a synthetic text leaf; `None` for non-text nodes.
    pub text: Option<TextDrawPayload>,
    /// User config payload (e.g. [`ElementConfig`](cotis_defaults::element_configs::ElementConfig)).
    pub custom: Custom,
}