bevy_ui_dsl/
lib.rs

1//! This crate simplifies the process of creating widgets in bevy using a simple extensible DSL.
2
3#[cfg(feature = "class_helpers")]
4pub mod class_helpers;
5mod widgets;
6
7pub use widgets::*;
8use bevy_asset::AssetServer;
9use bevy_ecs::bundle::Bundle;
10use bevy_ecs::entity::Entity;
11use bevy_ecs::system::EntityCommands;
12use bevy_hierarchy::{BuildChildren, ChildBuilder};
13use bevy_text::TextStyle;
14use bevy_ui::node_bundles::{ButtonBundle, ImageBundle, NodeBundle, TextBundle};
15
16
17/// Wrapper for [`ChildBuilder`] that also propogates an [`AssetServer`] for the children that need it.
18pub struct UiChildBuilder<'a, 'b> {
19    builder: &'a mut ChildBuilder<'b>,
20    assets: &'a AssetServer,
21}
22
23impl<'a, 'b> UiChildBuilder<'a, 'b> {
24
25    /// Create a new [`UiChildBuilder`] for adding to children of a node.
26    pub fn new(builder: &'a mut ChildBuilder<'b>, assets: &'a AssetServer) -> Self {
27        Self { builder, assets }
28    }
29
30    pub fn spawn<'c>(&'c mut self, bundle: impl Bundle) -> UiEntityCommands<'a, 'c> {
31        let commands: EntityCommands<'c> = self.builder.spawn(bundle);
32        UiEntityCommands {
33            assets: self.assets,
34            commands,
35        }
36    }
37    pub fn assets(&self) -> &AssetServer {
38        self.assets
39    }
40}
41
42/// Wrapper for [`EntityCommands`] that also propagates an [`AssetServer`] for the children that need it.
43pub struct UiEntityCommands<'a, 'b> {
44    commands: EntityCommands<'b>,
45    assets: &'a AssetServer
46}
47
48impl<'a, 'b> UiEntityCommands<'a, 'b> {
49    pub fn id(&self) -> Entity {
50        self.commands.id()
51    }
52    pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
53        self.commands.insert(bundle);
54        self
55    }
56    pub fn with_children(mut self, spawn_children: impl FnOnce(&mut UiChildBuilder)) -> Self {
57        self.commands.with_children(move |builder| {
58            let mut ui_builder = UiChildBuilder {
59                assets: self.assets,
60                builder,
61            };
62            spawn_children(&mut ui_builder);
63        });
64        self
65    }
66}
67
68/// Something that can overwrite a value, typically a node bundle.
69pub trait Class<B> {
70    fn apply(self, b: &mut B);
71}
72
73impl<T> Class<T> for () {
74    fn apply(self, _b: &mut T) {}
75}
76
77impl<F, B> Class<B> for F
78where
79    F: FnOnce(&mut B),
80{
81    fn apply(self, b: &mut B) {
82        self(b);
83    }
84}
85
86impl<F1, F2, B> Class<B> for (F1, F2)
87where
88    F1: Class<B>,
89    F2: Class<B>,
90{
91    fn apply(self, b: &mut B) {
92        self.0.apply(b);
93        self.1.apply(b);
94    }
95}
96
97impl<F1, F2, F3, B> Class<B> for (F1, F2, F3)
98where
99    F1: Class<B>,
100    F2: Class<B>,
101    F3: Class<B>,
102{
103    fn apply(self, b: &mut B) {
104        self.0.apply(b);
105        self.1.apply(b);
106        self.2.apply(b);
107    }
108}
109
110impl<F1, F2, F3, F4, B> Class<B> for (F1, F2, F3, F4)
111where
112    F1: Class<B>,
113    F2: Class<B>,
114    F3: Class<B>,
115    F4: Class<B>,
116{
117    fn apply(self, b: &mut B) {
118        self.0.apply(b);
119        self.1.apply(b);
120        self.2.apply(b);
121        self.3.apply(b);
122    }
123}
124
125impl Class<NodeBundle> for NodeBundle {
126    fn apply(self, b: &mut NodeBundle) {
127        *b = self;
128    }
129}
130
131impl Class<ImageBundle> for ImageBundle {
132    fn apply(self, b: &mut ImageBundle) {
133        *b = self;
134    }
135}
136
137/// Something that can overwrite a value, typically a node bundle.
138/// Depends on an [`AssetServer`], unlike [`Class`].
139pub trait AssetClass<B> {
140    fn apply(self, assets: &AssetServer, b: &mut B);
141}
142
143impl<T> AssetClass<T> for () {
144    fn apply(self, _a: &AssetServer, _b: &mut T) {}
145}
146
147impl<F, B> AssetClass<B> for F
148where
149    F: FnOnce(&AssetServer, &mut B),
150{
151    fn apply(self, a: &AssetServer, b: &mut B) {
152        self(a, b);
153    }
154}
155
156impl<F1, F2, B> AssetClass<B> for (F1, F2)
157where
158    F1: AssetClass<B>,
159    F2: AssetClass<B>,
160{
161    fn apply(self, a: &AssetServer, b: &mut B) {
162        self.0.apply(a, b);
163        self.1.apply(a, b);
164    }
165}
166
167impl<F1, F2, F3, B> AssetClass<B> for (F1, F2, F3)
168where
169    F1: AssetClass<B>,
170    F2: AssetClass<B>,
171    F3: AssetClass<B>,
172{
173    fn apply(self, a: &AssetServer, b: &mut B) {
174        self.0.apply(a, b);
175        self.1.apply(a, b);
176        self.2.apply(a, b);
177    }
178}
179
180impl<F1, F2, F3, F4, B> AssetClass<B> for (F1, F2, F3, F4)
181where
182    F1: AssetClass<B>,
183    F2: AssetClass<B>,
184    F3: AssetClass<B>,
185    F4: AssetClass<B>,
186{
187    fn apply(self, a: &AssetServer, b: &mut B) {
188        self.0.apply(a, b);
189        self.1.apply(a, b);
190        self.2.apply(a, b);
191        self.3.apply(a, b);
192    }
193}
194
195impl AssetClass<ButtonBundle> for ButtonBundle {
196    fn apply(self, _a: &AssetServer, b: &mut ButtonBundle) {
197        *b = self;
198    }
199}
200
201impl AssetClass<TextBundle> for TextBundle {
202    fn apply(self, _a: &AssetServer, b: &mut TextBundle) {
203        *b = self;
204    }
205}
206
207impl AssetClass<TextStyle> for TextStyle {
208    fn apply(self, _a: &AssetServer, b: &mut TextStyle) {
209        *b = self;
210    }
211}
212
213/// Adds a helper method to [`Entity`] that allows it to be sent to an [`Option`][`Entity`]
214/// ergonomically.
215pub trait EntityWriter {
216    fn set(self, entity: &mut Option<Entity>);
217    fn push(self, destination: &mut Vec<Entity>);
218}
219
220impl EntityWriter for Entity {
221    /// Copies this entity into an Option.
222    fn set(self, entity: &mut Option<Entity>) {
223        *entity = Some(self);
224    }
225    /// Pushes a copy of this Entity into a Vec.
226    fn push(self, entities: &mut Vec<Entity>) {
227        entities.push(self);
228    }
229}