diff --git a/rfcs/ui-building-blocks.md b/rfcs/ui-building-blocks.md new file mode 100644 index 00000000..8a880198 --- /dev/null +++ b/rfcs/ui-building-blocks.md @@ -0,0 +1,472 @@ +# Feature Name: `ui_building_blocks` + +## Summary + +This RFC establishes a common vocabulary and basic data structures for UI. +It is combined with a design pattern to manipulate UI styles ergonomically using Bevy's ECS, to tangibly demonstrate the feasibility of the approach. + +## Motivation + +UI is the next big challenge for Bevy, with a complex set of data structures that need to be stored and manipulated. +In particular, the ability to style elements in a reusable fashion is central to a polished, easy-to UI, but there's no immediately obvious approach to handling this. +What makes a "style" in Bevy? How do we represent the individual attributes? How are widgets represented? How are styles composed? How are they applied? + +None of these questions are terribly challenging to implement on their own, but taking the first-seen path is particularly dangerous here, due to the risk of proliferation of non-local configuration and similar-but-not-identical attributes, as seen in CSS. + +We need to settle on a common standard, in order to enable interop between UI crates and establish the basic building blocks that more complex UI decisions can build off of. + +## Guide-level explanation + +At heart, user interfaces (UI) in Bevy are "just another part of the game". +It's built out of the same core primitives that you'll see when building your game logic: entities, components, systems, scenes, events and so on. +That however doesn't mean it's a complete free-for-all: `bevy_ui` defines several built-in components, systems and design patterns +to make working with UI ergonomic, maintainable and interoperable without making it any harder to extend or hook into. + +Commonly, UI is defined by three core components, each controlling a certain type of behavior: + +1. `ScreenSpace`, which dictates that an entity's transform should be drawn relative to the player's screen, rather than the world. +Entities with the `ScreenSpace` component are drawn by the UI camera; all others are drawn with ordinary cameras. +2. `Layout`, which tells Bevy's built-in layout systems to control its positioning relative to other `Layout` entities. +The values of this component's fields determine exactly how this is done. +3. `Widget`, a general purpose marker component that designates an entity as "part of the UI". +All entities that make up the UI should have this marker, whether it's visual (like a button) or abstract (like a layout box). +This has no innate behavior; instead it serves as an easy hook for user code and an identifier for the editor. + +Each of these can be added separately, although every entity that makes up a classical UI will have all three. +For example, if you wanted to have a world-space UI as commonly seen in XR applications, you'd remove the `ScreenSpace` marker component while keeping `Layout` and `Widget`. + +User interfaces in Bevy are made out of **widgets**, which are modified by the **styles** that are applied to them, and the aesthetic and functional behavior of is ultimately implemented through **UI systems**. + +A **widget** is a distinct part of the UI that needs its own data: be that its position, local state or any attributes. +Widgets in Bevy are represented by entities with a `Widget` marker component. + +Each widget has an associated `Styles` component, which contains a `Vec` which points to some (possibly 0) number of **styles**, represented as entities with a `Style` marker component. + +Each style in that vector is applied, in order, to the widget, overriding the **attributes** that already exist. +attributes are stored as components, and serve to control some element of its presentation, such as the font, background color or text alignment. +attributes can be reused across disparate widget types, with functionality achieved through systems that query for the relevant components. +The type of the attribute components determines which behavior it controls, +while the value returned by `.get()` controls the final behavior of the widget. + +Every attribute has both a `MyStyle` and a `MyStyle` variant, stored together on each entity. +When creating a new attribute, you must ensure that it implements `StyleParam`, typically achieved with `#[derive(StyleParam)]`. + +Styles can be modified directly, by modifying the `Styles` component of the widgets you wish to modify. +When spawning entities, you may find it convenient to work with the relevant `EntityCommands` methods instead +to reduce the number of function parameters your systems need. +Pass in the type of your resource(s) that implements the `StyleResource` trait as type parameters, and the next time commands are processed, +the entity stored in that resource will be added to (or removed from) the `Styles` component of your widget entity. + +When working on complex games or applications, you're likely to want to group your styles into **themes**, +automatically applying them to large groups of widgets at once. +In Bevy, themes are applied by adding a generic system that corresponds to that theme to your app's schedule. +Select a marker component type `W`, then add a theme system to your app with `W` as a type parameter: +`app.add_startup_system(solarized_theme::