Skip to main content

fission_core/ui/widgets/
builder.rs

1use crate::build::{self, BuildCtxHandle, ViewHandle};
2use crate::ui::{Container, Widget};
3use crate::{BoxConstraints, GlobalState, WidgetId};
4use std::sync::Arc;
5
6/// A closure-backed widget for small inline composition.
7///
8/// `Builder` is not a replacement for named components. Use
9/// `impl From<MyComponent> for Widget` for reusable application widgets. Use
10/// `Builder` when a short local closure is clearer than introducing a named
11/// component, for example inside tests or small conditional sections.
12#[derive(Clone)]
13pub struct Builder<S: GlobalState> {
14    builder: Arc<dyn Fn(BuildCtxHandle<S>, ViewHandle<S>) -> Widget + Send + Sync>,
15}
16
17impl<S: GlobalState> Builder<S> {
18    pub fn new<F>(builder: F) -> Self
19    where
20        F: Fn(BuildCtxHandle<S>, ViewHandle<S>) -> Widget + Send + Sync + 'static,
21    {
22        Self {
23            builder: Arc::new(builder),
24        }
25    }
26}
27
28impl<S: GlobalState> From<Builder<S>> for Widget {
29    fn from(component: Builder<S>) -> Self {
30        let (ctx, view) = build::current::<S>();
31        (component.builder)(ctx, view)
32    }
33}
34
35/// A closure-backed widget that receives its parent's last known constraints.
36///
37/// The constraint value comes from the previous layout pass when an explicit
38/// `id` is supplied. Otherwise it falls back to a loose constraint derived from
39/// the current viewport. Prefer normal responsive widgets first; use
40/// `LayoutBuilder` when the child tree itself must change with available size.
41#[derive(Clone)]
42pub struct LayoutBuilder<S: GlobalState> {
43    /// Optional stable identity for constraint look-up across frames.
44    pub id: Option<WidgetId>,
45    /// Flex grow factor applied to the wrapper when `id` is set.
46    pub flex_grow: f32,
47    /// Flex shrink factor applied to the wrapper when `id` is set.
48    pub flex_shrink: f32,
49    builder: Arc<dyn Fn(BuildCtxHandle<S>, ViewHandle<S>, BoxConstraints) -> Widget + Send + Sync>,
50}
51
52impl<S: GlobalState> LayoutBuilder<S> {
53    pub fn new<F>(builder: F) -> Self
54    where
55        F: Fn(BuildCtxHandle<S>, ViewHandle<S>, BoxConstraints) -> Widget + Send + Sync + 'static,
56    {
57        Self {
58            id: None,
59            flex_grow: 0.0,
60            flex_shrink: 1.0,
61            builder: Arc::new(builder),
62        }
63    }
64
65    pub fn flex_grow(mut self, grow: f32) -> Self {
66        self.flex_grow = grow;
67        self
68    }
69
70    pub fn flex_shrink(mut self, shrink: f32) -> Self {
71        self.flex_shrink = shrink;
72        self
73    }
74}
75
76impl<S: GlobalState> From<LayoutBuilder<S>> for Widget {
77    fn from(component: LayoutBuilder<S>) -> Self {
78        let (ctx, view) = build::current::<S>();
79        let viewport = view.viewport_size();
80        let mut max_w = viewport.width;
81        let mut max_h = viewport.height;
82        if !max_w.is_finite() || max_w <= 0.0 {
83            max_w = f32::INFINITY;
84        }
85        if !max_h.is_finite() || max_h <= 0.0 {
86            max_h = f32::INFINITY;
87        }
88        let fallback = BoxConstraints::loose(max_w, max_h);
89        let id = build::current_widget_id().or(component.id);
90        let constraints = id
91            .and_then(|id| view.get_constraints(id))
92            .unwrap_or(fallback);
93        let child = (component.builder)(ctx, view, constraints);
94
95        if let Some(id) = id {
96            let mut container = Container::new(child)
97                .flex_grow(component.flex_grow)
98                .flex_shrink(component.flex_shrink);
99            container.id = Some(id);
100            container.into()
101        } else {
102            child
103        }
104    }
105}