Skip to main content

waterui_layout/containers/
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/// ```rust
23/// use waterui::layout::spacer::spacer_min;
24/// use waterui::prelude::*;
25///
26/// // Push button to trailing edge
27/// hstack((
28///     text("Title"),
29///     spacer(),
30///     button("Done").action(|| {}),
31/// ));
32///
33/// // Center content with equal spacing
34/// hstack((spacer(), text("Centered"), spacer()));
35///
36/// // Spacer with minimum length (never shrinks below 20pt)
37/// spacer_min(20.0);
38/// ```
39//
40// ═══════════════════════════════════════════════════════════════════════════
41// INTERNAL: Layout Contract for Backend Implementers
42// ═══════════════════════════════════════════════════════════════════════════
43//
44
45// Measurement: Returns (minLength, minLength) as intrinsic size
46// Layout: Expands to fill remaining surplus space during place() phase
47// Overflow: Collapses to minLength when space is insufficient
48//
49// ═══════════════════════════════════════════════════════════════════════════
50//
51#[derive(Debug, Clone, PartialEq)]
52pub struct Spacer {
53    min_length: f32,
54}
55
56impl Spacer {
57    /// Creates a new spacer with the specified minimum length.
58    #[must_use]
59    pub const fn new(min_length: f32) -> Self {
60        Self { min_length }
61    }
62
63    /// Creates a spacer with zero minimum length.
64    #[must_use]
65    pub const fn flexible() -> Self {
66        Self { min_length: 0.0 }
67    }
68}
69
70/// Layout implementation for a single spacer.
71///
72/// Spacers are greedy and will expand to fill all available space
73/// in the direction they are placed, respecting their minimum length.
74#[derive(Debug, Clone)]
75pub struct SpacerLayout {
76    min_length: f32,
77}
78
79impl Layout for SpacerLayout {
80    fn size_that_fits(&self, _proposal: ProposalSize, _children: &[&dyn SubView]) -> Size {
81        // Spacer reports its minimum length as intrinsic size (like SwiftUI)
82        // The parent stack will expand it to fill remaining space during place()
83        Size::new(self.min_length, self.min_length)
84    }
85
86    fn place(&self, _bounds: Rect, _children: &[&dyn SubView]) -> Vec<Rect> {
87        // Spacer has no children to place
88        Vec::new()
89    }
90}
91
92impl From<Spacer> for SpacerLayout {
93    fn from(spacer: Spacer) -> Self {
94        Self {
95            min_length: spacer.min_length,
96        }
97    }
98}
99
100raw_view!(Spacer, StretchAxis::MainAxis);
101
102/// Creates a flexible spacer with zero minimum length.
103///
104/// This spacer will expand to fill all available space in layouts.
105#[must_use]
106pub const fn spacer() -> Spacer {
107    Spacer::flexible()
108}
109
110/// Creates a spacer with a specific minimum length.
111///
112/// This spacer will expand to fill available space but never shrink below the minimum.
113#[must_use]
114pub const fn spacer_min(min_length: f32) -> Spacer {
115    Spacer::new(min_length)
116}