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