bevy/crates/bevy_ui/src/lib.rs
Carter Anderson dcc03724a5 Base Sets (#7466)
# Objective

NOTE: This depends on #7267 and should not be merged until #7267 is merged. If you are reviewing this before that is merged, I highly recommend viewing the Base Sets commit instead of trying to find my changes amongst those from #7267.

"Default sets" as described by the [Stageless RFC](https://github.com/bevyengine/rfcs/pull/45) have some [unfortunate consequences](https://github.com/bevyengine/bevy/discussions/7365).

## Solution

This adds "base sets" as a variant of `SystemSet`:

A set is a "base set" if `SystemSet::is_base` returns `true`. Typically this will be opted-in to using the `SystemSet` derive:

```rust
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
#[system_set(base)]
enum MyBaseSet {
  A,
  B,
}
``` 

**Base sets are exclusive**: a system can belong to at most one "base set". Adding a system to more than one will result in an error. When possible we fail immediately during system-config-time with a nice file + line number. For the more nested graph-ey cases, this will fail at the final schedule build. 

**Base sets cannot belong to other sets**: this is where the word "base" comes from

Systems and Sets can only be added to base sets using `in_base_set`. Calling `in_set` with a base set will fail. As will calling `in_base_set` with a normal set.

```rust
app.add_system(foo.in_base_set(MyBaseSet::A))
       // X must be a normal set ... base sets cannot be added to base sets
       .configure_set(X.in_base_set(MyBaseSet::A))
```

Base sets can still be configured like normal sets:

```rust
app.add_system(MyBaseSet::B.after(MyBaseSet::Ap))
``` 

The primary use case for base sets is enabling a "default base set":

```rust
schedule.set_default_base_set(CoreSet::Update)
  // this will belong to CoreSet::Update by default
  .add_system(foo)
  // this will override the default base set with PostUpdate
  .add_system(bar.in_base_set(CoreSet::PostUpdate))
```

This allows us to build apis that work by default in the standard Bevy style. This is a rough analog to the "default stage" model, but it use the new "stageless sets" model instead, with all of the ordering flexibility (including exclusive systems) that it provides.

---

## Changelog

- Added "base sets" and ported CoreSet to use them.

## Migration Guide

TODO
2023-02-06 03:10:08 +00:00

151 lines
5.6 KiB
Rust

//! This crate contains Bevy's UI system, which can be used to create UI for both 2D and 3D games
//! # Basic usage
//! Spawn UI elements with [`node_bundles::ButtonBundle`], [`node_bundles::ImageBundle`], [`node_bundles::TextBundle`] and [`node_bundles::NodeBundle`]
//! This UI is laid out with the Flexbox paradigm (see <https://cssreference.io/flexbox/>)
mod flex;
mod focus;
mod geometry;
mod render;
mod stack;
mod ui_node;
pub mod camera_config;
pub mod node_bundles;
pub mod update;
pub mod widget;
use bevy_render::{camera::CameraUpdateSystem, extract_component::ExtractComponentPlugin};
pub use flex::*;
pub use focus::*;
pub use geometry::*;
pub use render::*;
pub use ui_node::*;
#[doc(hidden)]
pub mod prelude {
#[doc(hidden)]
pub use crate::{
camera_config::*, geometry::*, node_bundles::*, ui_node::*, widget::Button, Interaction,
UiScale,
};
}
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_input::InputSystem;
use bevy_transform::TransformSystem;
use bevy_window::ModifiesWindows;
use stack::ui_stack_system;
pub use stack::UiStack;
use update::update_clipping_system;
use crate::prelude::UiCameraConfig;
/// The basic plugin for Bevy UI
#[derive(Default)]
pub struct UiPlugin;
/// The label enum labeling the types of systems in the Bevy UI
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub enum UiSystem {
/// After this label, the ui flex state has been updated
Flex,
/// After this label, input interactions with UI entities have been updated for this frame
Focus,
/// After this label, the [`UiStack`] resource has been updated
Stack,
}
/// The current scale of the UI.
///
/// A multiplier to fixed-sized ui values.
/// **Note:** This will only affect fixed ui values like [`Val::Px`]
#[derive(Debug, Resource)]
pub struct UiScale {
/// The scale to be applied.
pub scale: f64,
}
impl Default for UiScale {
fn default() -> Self {
Self { scale: 1.0 }
}
}
impl Plugin for UiPlugin {
fn build(&self, app: &mut App) {
app.add_plugin(ExtractComponentPlugin::<UiCameraConfig>::default())
.init_resource::<FlexSurface>()
.init_resource::<UiScale>()
.init_resource::<UiStack>()
.register_type::<AlignContent>()
.register_type::<AlignItems>()
.register_type::<AlignSelf>()
.register_type::<CalculatedSize>()
.register_type::<Direction>()
.register_type::<Display>()
.register_type::<FlexDirection>()
.register_type::<FlexWrap>()
.register_type::<FocusPolicy>()
.register_type::<Interaction>()
.register_type::<JustifyContent>()
.register_type::<Node>()
// NOTE: used by Style::aspect_ratio
.register_type::<Option<f32>>()
.register_type::<Overflow>()
.register_type::<PositionType>()
.register_type::<Size>()
.register_type::<UiRect>()
.register_type::<Style>()
.register_type::<BackgroundColor>()
.register_type::<UiImage>()
.register_type::<Val>()
.register_type::<widget::Button>()
.configure_set(UiSystem::Focus.in_base_set(CoreSet::PreUpdate))
.configure_set(UiSystem::Flex.in_base_set(CoreSet::PostUpdate))
.configure_set(UiSystem::Stack.in_base_set(CoreSet::PostUpdate))
.add_system(ui_focus_system.in_set(UiSystem::Focus).after(InputSystem))
// add these systems to front because these must run before transform update systems
.add_system(
widget::text_system
.in_base_set(CoreSet::PostUpdate)
.before(UiSystem::Flex)
.after(ModifiesWindows)
// Potential conflict: `Assets<Image>`
// In practice, they run independently since `bevy_render::camera_update_system`
// will only ever observe its own render target, and `widget::text_system`
// will never modify a pre-existing `Image` asset.
.ambiguous_with(CameraUpdateSystem)
// Potential conflict: `Assets<Image>`
// Since both systems will only ever insert new [`Image`] assets,
// they will never observe each other's effects.
.ambiguous_with(bevy_text::update_text2d_layout),
)
.add_system(
widget::update_image_calculated_size_system
.in_base_set(CoreSet::PostUpdate)
.before(UiSystem::Flex)
// Potential conflicts: `Assets<Image>`
// They run independently since `widget::image_node_system` will only ever observe
// its own UiImage, and `widget::text_system` & `bevy_text::update_text2d_layout`
// will never modify a pre-existing `Image` asset.
.ambiguous_with(bevy_text::update_text2d_layout)
.ambiguous_with(widget::text_system),
)
.add_system(
flex_node_system
.in_set(UiSystem::Flex)
.before(TransformSystem::TransformPropagate)
.after(ModifiesWindows),
)
.add_system(ui_stack_system.in_set(UiSystem::Stack))
.add_system(
update_clipping_system
.after(TransformSystem::TransformPropagate)
.in_base_set(CoreSet::PostUpdate),
);
crate::render::build_ui_render(app);
}
}