hUI/hui/src/layout.rs
2024-03-07 22:46:01 +01:00

179 lines
4.8 KiB
Rust

//! element layout, alignment and sizing
use glam::{vec2, Vec2};
/// Alignment along a single axis
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, PartialOrd, Ord)]
pub enum Alignment {
/// Put the element at the beginning of the axis\
/// (left for horizontal, top for vertical alignment)
#[default]
Begin = 0,
/// Put the element in the center
Center = 1,
/// Put the element at the end of the axis\
/// (right for horizontal, bottom for vertical alignment)
End = 2,
}
/// Represents alignment in 2D space
///
/// - `horizontal` - alignment *along* x-axis (horizontal)\
/// - `vertical` - alignment *along* y-axis (vertical)
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, PartialOrd, Ord)]
pub struct Alignment2d {
/// Alignment *along* horizontal axis (X)
///
/// ```text
/// ├───────[ ]──────┤
/// ↑↑ ↑↑ ↑↑
/// Begin Center End
/// ```
pub horizontal: Alignment,
/// Alignment *along* vertical axis (Y)
///
/// ```text
/// ┬ ←─ Begin
/// │
/// [ ] ←─ Center
/// │
/// ┴ ←─ End
/// ```
pub vertical: Alignment,
}
impl Alignment2d {
/// Create a new `Alignment2d` with the same alignment for both axes
#[inline]
pub const fn all(alignment: Alignment) -> Self {
Self {
horizontal: alignment,
vertical: alignment,
}
}
}
impl From<(Alignment, Alignment)> for Alignment2d {
#[inline]
fn from((horizontal, vertical): (Alignment, Alignment)) -> Self {
Self { horizontal, vertical }
}
}
impl From<[Alignment; 2]> for Alignment2d {
#[inline]
fn from([horizontal, vertical]: [Alignment; 2]) -> Self {
Self { horizontal, vertical }
}
}
impl From<Alignment> for Alignment2d {
#[inline]
fn from(alignment: Alignment) -> Self {
Self::all(alignment)
}
}
/// Represents a single size dimension of an UI element.\
/// Can be either a static size in pixels, a fraction the parent size or auto-calculated\
/// (Meaning of `auto` is entirely dependent on the element).
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub enum Size {
/// Automatically calculate size based on content
#[default]
Auto,
/// Size as a ratio of parent size\
/// Valid range: 0.0-1.0 (0-100%)
///
/// Out of range values are allowed, but are not guaranteed to work as expected\
/// (especially with negative values)
Fraction(f32),
//TODO FractionRemaining(f32),
/// Static size in pixels
Static(f32),
}
impl From<f32> for Size {
#[inline]
fn from(value: f32) -> Self {
Self::Static(value)
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq)]
pub struct Size2d {
pub width: Size,
pub height: Size,
}
impl From<(Size, Size)> for Size2d {
#[inline]
fn from((width, height): (Size, Size)) -> Self {
Self { width, height }
}
}
//XXX: should this exist?
impl From<Size> for Size2d {
#[inline]
fn from(size: Size) -> Self {
Self {
width: size,
height: size,
}
}
}
/// Represents the direction of the layout\
/// (for example, the direction of a container's children)\
///
/// - `Vertical` - Children are laid out from top to bottom\
/// - `Horizontal` - Children are laid out from left to right
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Direction {
/// Children are laid out from top to bottom
#[default]
Vertical,
/// Children are laid out from left to right
Horizontal,
}
/// Represents the layout information required to measure, layout and render an element.\
/// Includes the position, maximum size, direction of the layout and other information
pub struct LayoutInfo {
/// Screen-space coordinates of the top-left corner of the element.\
/// Use this value during the layout step to render the element
///
/// Not available during the measure step (will be set to zero)
pub position: Vec2,
/// Maximum size the element is allowed to take up
pub max_size: Vec2,
/// Current direction of the layout\
/// (Usually matches direction of the parent container)
pub direction: Direction,
}
/// Helper function to calculate the size of an element based on its layout and size information\
/// Used to help reduce code duplication in the `measure` method of UI elements
pub fn compute_size(layout: &LayoutInfo, size: Size2d, comfy_size: Vec2) -> Vec2 {
let width = match size.width {
Size::Auto => comfy_size.x,
Size::Fraction(fraction) => layout.max_size.x * fraction,
Size::Static(size) => size,
};
let height = match size.height {
Size::Auto => comfy_size.y,
Size::Fraction(fraction) => layout.max_size.y * fraction,
Size::Static(size) => size,
};
vec2(width, height)
}