floem/views/
stack.rs

1use taffy::style::FlexDirection;
2
3use crate::{
4    context::UpdateCx,
5    style::{Style, StyleClassRef},
6    view::ViewId,
7    view::{IntoView, IntoViewIter, View, ViewTuple},
8};
9
10/// A collection of static views. See [`stack`] and [`stack_from_iter`].
11///
12/// The children of a stack can still get reactive updates.
13pub struct Stack {
14    id: ViewId,
15    direction: Option<FlexDirection>,
16}
17
18pub(crate) fn create_stack(
19    children: Vec<Box<dyn View>>,
20    direction: Option<FlexDirection>,
21) -> Stack {
22    create_stack_with_id(ViewId::new(), children, direction)
23}
24
25pub(crate) fn create_stack_with_id(
26    id: ViewId,
27    children: Vec<Box<dyn View>>,
28    direction: Option<FlexDirection>,
29) -> Stack {
30    id.set_children_vec(children);
31    Stack { id, direction }
32}
33
34/// A basic stack that is built from a tuple of views which remains static and always contains the same elements in the same order.
35///
36/// The children of a stack can still get reactive updates.
37/// See also [`v_stack`] and [`h_stack`].
38///
39/// ## Example
40/// ```rust
41/// use floem::views::*;
42/// stack((
43///    text("first element"),
44///     stack((
45///        text("new stack"),
46///        empty(),
47///     )),
48/// ));
49/// ```
50#[deprecated(since = "0.2.0", note = "Use Stack::new() instead")]
51pub fn stack<VT: ViewTuple + 'static>(children: VT) -> Stack {
52    create_stack(children.into_views(), None)
53}
54
55/// A stack which defaults to `FlexDirection::Row`. See also [`v_stack`].
56#[deprecated(since = "0.2.0", note = "Use Stack::horizontal() instead")]
57pub fn h_stack<VT: ViewTuple + 'static>(children: VT) -> Stack {
58    create_stack(children.into_views(), Some(FlexDirection::Row))
59}
60
61/// A stack which defaults to `FlexDirection::Column`. See also [`h_stack`].
62#[deprecated(since = "0.2.0", note = "Use Stack::vertical() instead")]
63pub fn v_stack<VT: ViewTuple + 'static>(children: VT) -> Stack {
64    create_stack(children.into_views(), Some(FlexDirection::Column))
65}
66
67fn from_iter<V>(iterator: impl IntoIterator<Item = V>, direction: Option<FlexDirection>) -> Stack
68where
69    V: IntoView + 'static,
70{
71    create_stack(
72        iterator
73            .into_iter()
74            .map(|v| -> Box<dyn View> { v.into_any() })
75            .collect(),
76        direction,
77    )
78}
79
80pub(crate) fn from_iter_with_id<V>(
81    id: ViewId,
82    iterator: impl IntoIterator<Item = V>,
83    direction: Option<FlexDirection>,
84) -> Stack
85where
86    V: IntoView + 'static,
87{
88    create_stack_with_id(
89        id,
90        iterator
91            .into_iter()
92            .map(|v| -> Box<dyn View> { v.into_any() })
93            .collect(),
94        direction,
95    )
96}
97
98/// Creates a stack from an iterator of views. See also [`v_stack_from_iter`] and [`h_stack_from_iter`].
99///
100/// ## Example
101/// ```rust
102/// use floem::views::*;
103/// stack_from_iter(vec![1,1,2,2,3,4,5,6,7,8,9].iter().map(|val| text(val)));
104/// ```
105#[deprecated(since = "0.2.0", note = "Use Stack::from_iter() instead")]
106pub fn stack_from_iter<V>(iterator: impl IntoIterator<Item = V>) -> Stack
107where
108    V: IntoView + 'static,
109{
110    from_iter(iterator, None)
111}
112
113/// Creates a stack from an iterator of views. It defaults to `FlexDirection::Row`. See also [`v_stack_from_iter`].
114#[deprecated(since = "0.2.0", note = "Use Stack::horizontal_from_iter() instead")]
115pub fn h_stack_from_iter<V>(iterator: impl IntoIterator<Item = V>) -> Stack
116where
117    V: IntoView + 'static,
118{
119    from_iter(iterator, Some(FlexDirection::Row))
120}
121
122/// Creates a stack from an iterator of views. It defaults to `FlexDirection::Column`. See also [`h_stack_from_iter`].
123#[deprecated(since = "0.2.0", note = "Use Stack::vertical_from_iter() instead")]
124pub fn v_stack_from_iter<V>(iterator: impl IntoIterator<Item = V>) -> Stack
125where
126    V: IntoView + 'static,
127{
128    from_iter(iterator, Some(FlexDirection::Column))
129}
130
131impl View for Stack {
132    fn id(&self) -> ViewId {
133        self.id
134    }
135
136    fn view_style(&self) -> Option<crate::style::Style> {
137        self.direction
138            .map(|direction| Style::new().flex_direction(direction))
139    }
140
141    fn debug_name(&self) -> std::borrow::Cow<'static, str> {
142        match self.direction {
143            Some(FlexDirection::Column) => "Vertical Stack".into(),
144            Some(FlexDirection::Row) => "Horizontal Stack".into(),
145            _ => "Stack".into(),
146        }
147    }
148
149    fn update(&mut self, _cx: &mut UpdateCx, state: Box<dyn std::any::Any>) {
150        if let Ok(state) = state.downcast::<Vec<Box<dyn View>>>() {
151            self.id.set_children_vec(*state);
152            self.id.request_all();
153        }
154    }
155}
156
157impl Stack {
158    /// Creates a new stack from any type that implements [`IntoViewIter`].
159    ///
160    /// This accepts arrays, tuples, vectors, slices, and iterators of views.
161    ///
162    /// ## Example
163    /// ```rust,no_run
164    /// use floem::views::*;
165    ///
166    /// // From array
167    /// Stack::new([text("child 1"), text("child 2")]);
168    ///
169    /// // From tuple (heterogeneous types)
170    /// Stack::new((text("label"), button("click")));
171    ///
172    /// // From vec
173    /// Stack::new(vec![text("a"), text("b"), text("c")]);
174    ///
175    /// // From iterator
176    /// Stack::new((0..5).map(|i| text(i)).collect::<Vec<_>>());
177    /// ```
178    pub fn new(children: impl IntoViewIter) -> Self {
179        let id = ViewId::new();
180        id.set_children_iter(children.into_view_iter());
181        Stack {
182            id,
183            direction: None,
184        }
185    }
186
187    /// Creates a new stack with a specific ViewId from a tuple of views.
188    ///
189    /// This is useful for lazy view construction where the `ViewId` is created
190    /// before the view itself.
191    ///
192    /// ## Example
193    /// ```rust
194    /// use floem::{ViewId, views::Stack};
195    ///
196    /// let id = ViewId::new();
197    /// Stack::with_id(id, ("child 1", "child 2"));
198    /// ```
199    pub fn with_id(id: ViewId, children: impl IntoViewIter) -> Self {
200        id.set_children_iter(children.into_view_iter());
201        Stack {
202            id,
203            direction: None,
204        }
205    }
206
207    /// Creates a new stack from an iterator of views.
208    ///
209    /// ## Example
210    /// ```rust,no_run
211    /// use floem::views::*;
212    ///
213    /// Stack::from_iter((0..5).map(|i| text(i)));
214    /// ```
215    #[allow(clippy::should_implement_trait)]
216    pub fn from_iter<V: IntoView + 'static>(children: impl IntoIterator<Item = V>) -> Self {
217        let id = ViewId::new();
218        id.set_children_vec(
219            children
220                .into_iter()
221                .map(|v| -> Box<dyn View> { v.into_any() })
222                .collect(),
223        );
224        Stack {
225            id,
226            direction: None,
227        }
228    }
229
230    /// Creates a new stack with a specific ViewId from an iterator of views.
231    ///
232    /// This is useful for lazy view construction where the `ViewId` is created
233    /// before the view itself. Unlike `with_id`, this method pre-converts all
234    /// views before setting children, avoiding potential RefCell borrow conflicts.
235    ///
236    /// ## Example
237    /// ```rust,no_run
238    /// use floem::{ViewId, views::*};
239    ///
240    /// let id = ViewId::new();
241    /// Stack::from_iter_with_id(id, (0..5).map(|i| text(i)), None);
242    /// ```
243    pub fn from_iter_with_id<V: IntoView + 'static>(
244        id: ViewId,
245        children: impl IntoIterator<Item = V>,
246        direction: Option<FlexDirection>,
247    ) -> Self {
248        id.set_children_vec(
249            children
250                .into_iter()
251                .map(|v| -> Box<dyn View> { v.into_any() })
252                .collect(),
253        );
254        Stack { id, direction }
255    }
256
257    /// Creates a new horizontal stack (row direction).
258    ///
259    /// ## Example
260    /// ```rust,no_run
261    /// use floem::views::*;
262    ///
263    /// Stack::horizontal((text("left"), text("right")));
264    /// ```
265    pub fn horizontal(children: impl IntoViewIter) -> Self {
266        let id = ViewId::new();
267        id.set_children_iter(children.into_view_iter());
268        Stack {
269            id,
270            direction: Some(FlexDirection::Row),
271        }
272    }
273
274    /// Creates a new vertical stack (column direction).
275    ///
276    /// ## Example
277    /// ```rust,no_run
278    /// use floem::views::*;
279    ///
280    /// Stack::vertical((text("top"), text("bottom")));
281    /// ```
282    pub fn vertical(children: impl IntoViewIter) -> Self {
283        let id = ViewId::new();
284        id.set_children_iter(children.into_view_iter());
285        Stack {
286            id,
287            direction: Some(FlexDirection::Column),
288        }
289    }
290
291    /// Creates a new horizontal stack from an iterator of views.
292    ///
293    /// ## Example
294    /// ```rust,no_run
295    /// use floem::views::*;
296    ///
297    /// Stack::horizontal_from_iter((0..5).map(|i| text(i)));
298    /// ```
299    pub fn horizontal_from_iter<V: IntoView + 'static>(
300        children: impl IntoIterator<Item = V>,
301    ) -> Self {
302        let id = ViewId::new();
303        id.set_children_vec(
304            children
305                .into_iter()
306                .map(|v| -> Box<dyn View> { v.into_any() })
307                .collect(),
308        );
309        Stack {
310            id,
311            direction: Some(FlexDirection::Row),
312        }
313    }
314
315    /// Creates a new vertical stack from an iterator of views.
316    ///
317    /// ## Example
318    /// ```rust,no_run
319    /// use floem::views::*;
320    ///
321    /// Stack::vertical_from_iter((0..5).map(|i| text(i)));
322    /// ```
323    pub fn vertical_from_iter<V: IntoView + 'static>(
324        children: impl IntoIterator<Item = V>,
325    ) -> Self {
326        let id = ViewId::new();
327        id.set_children_vec(
328            children
329                .into_iter()
330                .map(|v| -> Box<dyn View> { v.into_any() })
331                .collect(),
332        );
333        Stack {
334            id,
335            direction: Some(FlexDirection::Column),
336        }
337    }
338
339    pub fn add_class_by_idx(self, class: impl Fn(usize) -> StyleClassRef) -> Self {
340        for (index, child) in self.id.children().into_iter().enumerate() {
341            let style_class = class(index);
342            child.add_class(style_class);
343        }
344        self
345    }
346}
347
348pub trait StackExt {
349    fn stack(self, direction: FlexDirection) -> Stack;
350    fn v_stack(self) -> Stack
351    where
352        Self: Sized,
353    {
354        StackExt::stack(self, FlexDirection::Column)
355    }
356    fn h_stack(self) -> Stack
357    where
358        Self: Sized,
359    {
360        StackExt::stack(self, FlexDirection::Row)
361    }
362}
363impl<V: IntoView + 'static, T: IntoIterator<Item = V> + 'static> StackExt for T {
364    fn stack(self, direction: FlexDirection) -> Stack {
365        from_iter(self, Some(direction))
366    }
367}