hephae_ui/
lib.rs

1#![allow(internal_features)]
2#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]
3#![doc = include_str!("../README.md")]
4#![cfg_attr(doc, deny(missing_docs))]
5
6pub mod measure;
7pub mod node;
8pub mod root;
9pub mod style;
10
11use bevy_ecs::prelude::*;
12
13/// Common imports for [`hephae-ui`](crate).
14pub mod prelude {
15    pub use crate::{
16        node::{Border, ComputedUi, UiCaches},
17        root::Camera2dRoot,
18        style::{
19            AlignContent, AlignItems, AlignSelf, BoxSizing, Display, FlexDirection, FlexWrap, JustifyContent, Overflow,
20            Position, Ui, UiBorder, UiSize,
21            Val::{self, *},
22        },
23    };
24}
25
26/// App plugins for [`hephae_ui`](crate).
27pub mod plugin {
28    use std::marker::PhantomData;
29
30    use bevy_app::{PluginGroupBuilder, prelude::*};
31    use bevy_ecs::prelude::*;
32    use bevy_render::camera::CameraUpdateSystem;
33    use bevy_transform::prelude::*;
34    use hephae_utils::derive::plugin_conf;
35
36    use crate::{
37        HephaeUiSystems,
38        measure::{ContentSize, Measure, Measurements, on_measure_inserted},
39        node::compute_ui_tree,
40        root::{Camera2dRoot, UiRoot, UiRootTrns, compute_root_transform},
41        style::ui_changed,
42    };
43
44    plugin_conf! {
45        /// [`Measure`]s you can pass to [`ui_measure`] to conveniently configure them in one go.
46        pub trait MeasureConf for Measure, T => ui_measure::<T>()
47    }
48
49    plugin_conf! {
50        /// [`UiRoot`]s you can pass to [`ui_root`] to conveniently configure them in one go.
51        pub trait RootConf for UiRoot, T => ui_root::<T>()
52    }
53
54    /// Configures Hephae UI in your application. Pass additional user-defined leaf node measurers
55    /// and UI roots as pleased.
56    pub fn ui<M: MeasureConf, R: RootConf>() -> impl PluginGroup {
57        struct UiGroup<M: MeasureConf, R: RootConf>(PhantomData<(M, R)>);
58        impl<M: MeasureConf, R: RootConf> PluginGroup for UiGroup<M, R> {
59            fn build(self) -> PluginGroupBuilder {
60                let mut builder = PluginGroupBuilder::start::<Self>()
61                    .add(|app: &mut App| {
62                        app.init_resource::<Measurements>()
63                            .configure_sets(
64                                PostUpdate,
65                                (
66                                    (
67                                        HephaeUiSystems::ComputeRootTransform.after(CameraUpdateSystem),
68                                        HephaeUiSystems::InvalidateCaches,
69                                    ),
70                                    HephaeUiSystems::ComputeUiLayout,
71                                )
72                                    .chain()
73                                    .before(TransformSystem::TransformPropagate),
74                            )
75                            .add_systems(
76                                PostUpdate,
77                                (
78                                    ui_changed.in_set(HephaeUiSystems::InvalidateCaches),
79                                    compute_ui_tree.in_set(HephaeUiSystems::ComputeUiLayout),
80                                ),
81                            );
82                    })
83                    .add(ui_root::<Camera2dRoot>());
84
85                builder = M::build(builder);
86                R::build(builder)
87            }
88        }
89
90        UiGroup::<M, R>(PhantomData)
91    }
92
93    /// Configures a custom UI leaf node measurer.
94    pub fn ui_measure<T: Measure>() -> impl Plugin {
95        |app: &mut App| {
96            app.register_required_components::<T, ContentSize>()
97                .add_observer(on_measure_inserted::<T>)
98                .world_mut()
99                .resource_scope(|world, mut measurements: Mut<Measurements>| {
100                    measurements.register::<T>(world);
101                });
102        }
103    }
104
105    /// Configures a custom UI root component.
106    pub fn ui_root<T: UiRoot>() -> impl Plugin {
107        |app: &mut App| {
108            app.register_required_components::<T, UiRootTrns>()
109                .register_required_components::<T, Transform>()
110                .add_systems(
111                    PostUpdate,
112                    compute_root_transform::<T>.in_set(HephaeUiSystems::ComputeRootTransform),
113                );
114        }
115    }
116}
117
118/// Labels for systems added by Hephae UI.
119#[derive(SystemSet, Debug, Copy, Clone, PartialEq, Eq, Hash)]
120pub enum HephaeUiSystems {
121    /// System in [`PostUpdate`](bevy_app::PostUpdate) that calculates transform and available size
122    /// for each UI root.
123    ComputeRootTransform,
124    /// System in [`PostUpdate`](bevy_app::PostUpdate) that is responsible over invalidating UI
125    /// layout caches so the pipeline will recompute them.
126    InvalidateCaches,
127    /// System in [`PostUpdate`](bevy_app::PostUpdate) that calculates every UI node layouts
128    /// recursively starting from the root.
129    ComputeUiLayout,
130}