raui_core/widget/component/containers/
content_box.rs

1//! A generic container for content with optional clipping and transforms
2
3use crate::{
4    PropsData, make_widget, pre_hooks,
5    widget::{
6        component::interactive::navigation::{
7            NavContainerActive, NavItemActive, NavJumpActive, use_nav_container_active,
8            use_nav_item, use_nav_jump_direction_active,
9        },
10        context::WidgetContext,
11        node::WidgetNode,
12        unit::content::{ContentBoxItemLayout, ContentBoxItemNode, ContentBoxNode},
13        utils::Transform,
14    },
15};
16use serde::{Deserialize, Serialize};
17
18/// The properties of a [`content_box`] component
19#[derive(PropsData, Debug, Default, Clone, Serialize, Deserialize)]
20#[props_data(crate::props::PropsData)]
21#[prefab(crate::Prefab)]
22pub struct ContentBoxProps {
23    /// Whether or not to clip the parts of items that overflow outside of the box bounds
24    #[serde(default)]
25    pub clipping: bool,
26    /// The transform to apply to the box and it's contents
27    #[serde(default)]
28    pub transform: Transform,
29}
30
31#[pre_hooks(use_nav_container_active, use_nav_jump_direction_active, use_nav_item)]
32pub fn nav_content_box(mut context: WidgetContext) -> WidgetNode {
33    let WidgetContext {
34        key,
35        props,
36        listed_slots,
37        ..
38    } = context;
39
40    let props = props
41        .clone()
42        .without::<NavContainerActive>()
43        .without::<NavJumpActive>()
44        .without::<NavItemActive>();
45
46    make_widget!(content_box)
47        .key(key)
48        .merge_props(props)
49        .listed_slots(listed_slots)
50        .into()
51}
52
53/// A generic container for other widgets
54///
55/// [`content_box`]'s serve two basic purposes: allowing you to apply transformations and clipping
56/// to all contained widgets and giving contained widgets more control over their layout inside of
57/// the box.
58///
59/// # Transform & Clipping
60///
61/// The transformation and clipping options on the [`content_box`] can be set by setting the
62/// [`ContentBoxProps`] on the component.
63///
64/// # Child Widget Layout
65///
66/// With a [`content_box`] you can get more control over the layout of it's children by adding the
67/// [`ContentBoxItemLayout`] properties to any of it's children.
68pub fn content_box(context: WidgetContext) -> WidgetNode {
69    let WidgetContext {
70        id,
71        props,
72        listed_slots,
73        ..
74    } = context;
75
76    let ContentBoxProps {
77        clipping,
78        transform,
79    } = props.read_cloned_or_default();
80
81    let items = listed_slots
82        .into_iter()
83        .filter_map(|slot| {
84            if let Some(props) = slot.props() {
85                let layout = props.read_cloned_or_default::<ContentBoxItemLayout>();
86                Some(ContentBoxItemNode { slot, layout })
87            } else {
88                None
89            }
90        })
91        .collect::<Vec<_>>();
92
93    ContentBoxNode {
94        id: id.to_owned(),
95        props: props.clone(),
96        items,
97        clipping,
98        transform,
99    }
100    .into()
101}