1use crate::adaptor::AssetLoadAdaptor;
2use crate::animation::{AnimationDirection, Atlas};
3use crate::prelude::*;
4use crate::util::{SlotId, SlotMap};
5use bevy::ecs::system::EntityCommands;
6use bevy::platform::collections::HashMap;
7use bevy::prelude::*;
8use bevy::ui::widget::NodeImageMode;
9
10#[derive(Debug, Default, Reflect)]
11#[reflect]
12pub enum NodeType {
13 #[default]
14 Node,
15 Image,
16 Text,
17 Button,
18 Slot,
19 Template,
20 Property,
21 Custom(String),
22}
23
24#[derive(Debug, Default, Reflect)]
26#[reflect]
27pub struct XNode {
28 pub uuid: u64,
29 pub src: Option<String>,
30 pub styles: Vec<StyleAttr>,
31 pub target: Option<String>,
32 pub watch: Option<String>,
33 pub id: Option<String>,
34 pub name: Option<String>,
35 pub uncompiled: Vec<AttrTokens>,
36 pub tags: HashMap<String, String>,
37 pub defs: HashMap<String, String>,
38 pub event_listener: Vec<Action>,
39 pub content_id: SlotId,
40 pub node_type: NodeType,
41 #[reflect(ignore)]
42 pub children: Vec<XNode>,
43}
44
45#[derive(Debug, Asset, Reflect)]
48#[reflect]
49pub struct HtmlTemplate {
50 pub name: Option<String>,
51 pub properties: HashMap<String, String>,
52 pub root: Vec<XNode>,
53 pub content: SlotMap<String>,
54}
55
56#[derive(Debug, Clone, Reflect)]
59#[reflect]
60pub enum Attribute {
61 Style(StyleAttr),
62 PropertyDefinition(String, String),
63 Name(String),
64 Uncompiled(AttrTokens),
65 Action(Action),
66 Path(String),
67 Target(String),
68 Id(String),
69 Watch(String),
70 Tag(String, String),
71}
72
73#[derive(Debug, Reflect, PartialEq, Clone)]
75#[reflect]
76pub struct AttrTokens {
77 pub prefix: Option<String>,
78 pub ident: String,
79 pub key: String,
80}
81
82impl AttrTokens {
83 pub fn compile(&self, props: &TemplateProperties, loader: &mut impl AssetLoadAdaptor) -> Option<Attribute> {
84 let Some(prop_val) = props.get(&self.key) else {
85 return None;
86 };
87
88 let (_, attr) = match crate::parse::attribute_from_parts::<nom::error::Error<&[u8]>>(
89 self.prefix.as_ref().map(|s| s.as_bytes()),
90 self.ident.as_bytes(),
91 prop_val.as_bytes(),
92 loader
93 ) {
94 Ok(val) => val,
95 Err(_) => (
96 "".as_bytes(),
97 Attribute::PropertyDefinition(self.ident.to_owned(), prop_val.to_owned()),
98 ),
99 };
100
101 if let Attribute::Uncompiled(attr) = attr {
103 return attr.compile(props, loader);
104 };
105
106 Some(attr)
107 }
108}
109
110#[derive(Debug, Reflect, PartialEq, Clone)]
111#[reflect]
112pub enum Action {
113 OnPress(Vec<String>),
114 OnEnter(Vec<String>),
115 OnExit(Vec<String>),
116 OnSpawn(Vec<String>),
117 OnChange(Vec<String>),
118}
119
120impl Action {
121 pub fn self_insert(self, mut cmd: EntityCommands) {
122 match self {
123 Action::OnPress(fn_id) => {
124 cmd.insert(crate::prelude::OnUiPress(fn_id));
125 }
126 Action::OnEnter(fn_id) => {
127 cmd.insert(crate::prelude::OnUiEnter(fn_id));
128 }
129 Action::OnExit(fn_id) => {
130 cmd.insert(crate::prelude::OnUiExit(fn_id));
131 }
132 Action::OnSpawn(fn_id) => {
133 cmd.insert(crate::prelude::OnUiSpawn(fn_id));
134 }
135 Action::OnChange(fn_id) => {
136 cmd.insert(crate::prelude::OnUiChange(fn_id));
137 }
138 }
139 }
140}
141
142#[derive(Debug, Clone, Reflect)]
143#[reflect]
144pub enum FontReference {
145 Handle(Handle<Font>),
146 Path(String),
147}
148
149#[derive(Debug, Clone, Reflect)]
150#[reflect]
151pub enum StyleAttr {
152 Display(Display),
153 Position(PositionType),
154 Overflow(Overflow),
155 OverflowClipMargin(OverflowClipMargin),
156 FrameTime(Val),
157 Left(Val),
158 Right(Val),
159 Top(Val),
160 Bottom(Val),
161 Width(Val),
162 Height(Val),
163 MinWidth(Val),
164 MinHeight(Val),
165 MaxWidth(Val),
166 MaxHeight(Val),
167 AspectRatio(f32),
168 AlignItems(AlignItems),
169 JustifyItems(JustifyItems),
170 AlignSelf(AlignSelf),
171 JustifySelf(JustifySelf),
172 AlignContent(AlignContent),
173 JustifyContent(JustifyContent),
174 Margin(UiRect),
175 Padding(UiRect),
176 Zindex(ZIndex),
177 GlobalZIndex(GlobalZIndex),
178
179 Border(UiRect),
182 BorderColor(Color),
183 BorderRadius(UiRect),
184 Outline(Outline),
185
186 FlexDirection(FlexDirection),
189 FlexWrap(FlexWrap),
190 FlexGrow(f32),
191 FlexShrink(f32),
192 FlexBasis(Val),
193 RowGap(Val),
194 ColumnGap(Val),
195
196 GridAutoFlow(GridAutoFlow),
199 GridTemplateRows(Vec<RepeatedGridTrack>),
200 GridTemplateColumns(Vec<RepeatedGridTrack>),
201 GridAutoRows(Vec<GridTrack>),
202 GridAutoColumns(Vec<GridTrack>),
203 GridRow(GridPlacement),
204 GridColumn(GridPlacement),
205
206 FontSize(f32),
209 Font(FontReference),
210 FontColor(Color),
211 TextLayout(TextLayout),
212
213 Background(Color),
216 ShadowColor(Color),
217 ShadowOffset(Val, Val),
218 ShadowSpread(Val),
219 ShadowBlur(Val),
220 TextShadow(TextShadow),
221
222 Hover(#[reflect(ignore)] Box<StyleAttr>),
224 Pressed(#[reflect(ignore)] Box<StyleAttr>),
225 Active(#[reflect(ignore)] Box<StyleAttr>),
226
227 Delay(f32),
230 Easing(EaseFunction),
231 Atlas(Option<Atlas>),
232 Duration(f32),
233 Iterations(i64),
234 Direction(AnimationDirection),
235 FPS(i64),
236 Frames(Vec<i64>),
237
238 ImageColor(Color),
241 ImageScaleMode(NodeImageMode),
242 ImageRegion(Rect),
243
244 #[cfg(feature = "picking")]
247 Pickable((bool, bool)),
248}
249
250impl Default for StyleAttr {
251 fn default() -> Self {
252 StyleAttr::Display(Display::None)
253 }
254}