waterui_layout/
spacer.rs

1//! Flexible layout gaps used by stacks and other containers.
2
3use alloc::vec::Vec;
4use waterui_core::raw_view;
5
6use crate::{Layout, ProposalSize, Rect, Size, StretchAxis, SubView};
7
8/// A flexible space that expands to push views apart.
9///
10/// Spacer adapts to its parent container: in `HStack` it expands horizontally,
11/// in `VStack` it expands vertically. Use it to push views to opposite edges
12/// or distribute space evenly.
13///
14/// # Layout Behavior
15///
16/// - **In `HStack`:** Expands horizontally only
17/// - **In `VStack`:** Expands vertically only
18/// - **In `ZStack`:** No expansion (falls back to minimum length)
19///
20/// # Examples
21///
22/// ```ignore
23/// // Push button to trailing edge
24/// hstack((
25///     text("Title"),
26///     spacer(),
27///     button("Done", || {}),
28/// ))
29///
30/// // Center content with equal spacing
31/// hstack((spacer(), text("Centered"), spacer()))
32///
33/// // Spacer with minimum length (never shrinks below 20pt)
34/// spacer_min(20.0)
35/// ```
36//
37// ═══════════════════════════════════════════════════════════════════════════
38// INTERNAL: Layout Contract for Backend Implementers
39// ═══════════════════════════════════════════════════════════════════════════
40//
41
42// Measurement: Returns (minLength, minLength) as intrinsic size
43// Layout: Expands to fill remaining surplus space during place() phase
44// Overflow: Collapses to minLength when space is insufficient
45//
46// ═══════════════════════════════════════════════════════════════════════════
47//
48#[derive(Debug, Clone, PartialEq)]
49pub struct Spacer {
50    min_length: f32,
51}
52
53impl Spacer {
54    /// Creates a new spacer with the specified minimum length.
55    #[must_use]
56    pub const fn new(min_length: f32) -> Self {
57        Self { min_length }
58    }
59
60    /// Creates a spacer with zero minimum length.
61    #[must_use]
62    pub const fn flexible() -> Self {
63        Self { min_length: 0.0 }
64    }
65}
66
67/// Layout implementation for a single spacer.
68///
69/// Spacers are greedy and will expand to fill all available space
70/// in the direction they are placed, respecting their minimum length.
71#[derive(Debug, Clone)]
72pub struct SpacerLayout {
73    min_length: f32,
74}
75
76impl Layout for SpacerLayout {
77    fn size_that_fits(&self, _proposal: ProposalSize, _children: &[&dyn SubView]) -> Size {
78        // Spacer reports its minimum length as intrinsic size (like SwiftUI)
79        // The parent stack will expand it to fill remaining space during place()
80        Size::new(self.min_length, self.min_length)
81    }
82
83    fn place(&self, _bounds: Rect, _children: &[&dyn SubView]) -> Vec<Rect> {
84        // Spacer has no children to place
85        Vec::new()
86    }
87}
88
89impl From<Spacer> for SpacerLayout {
90    fn from(spacer: Spacer) -> Self {
91        Self {
92            min_length: spacer.min_length,
93        }
94    }
95}
96
97raw_view!(Spacer, StretchAxis::MainAxis);
98
99/// Creates a flexible spacer with zero minimum length.
100///
101/// This spacer will expand to fill all available space in layouts.
102#[must_use]
103pub const fn spacer() -> Spacer {
104    Spacer::flexible()
105}
106
107/// Creates a spacer with a specific minimum length.
108///
109/// This spacer will expand to fill available space but never shrink below the minimum.
110#[must_use]
111pub const fn spacer_min(min_length: f32) -> Spacer {
112    Spacer::new(min_length)
113}