1pub mod button;
4pub mod text;
5pub mod row;
6pub mod column;
7pub mod modal;
8pub mod checkbox;
9pub mod root;
10pub mod radio;
11pub mod dropdown;
12pub mod select;
13pub mod link;
14pub mod slider;
15pub mod progress_bar;
16pub mod circular;
17pub mod tooltip;
18pub mod text_input;
19pub mod text_edit;
20pub mod scroll;
21pub mod image;
22
23pub use button::*;
24pub use text::*;
25pub use row::*;
26pub use column::*;
27pub use modal::*;
28pub use checkbox::*;
29pub use root::*;
30pub use radio::*;
31pub use dropdown::*;
32pub use select::*;
33pub use link::*;
34pub use slider::*;
35pub use progress_bar::*;
36pub use circular::*;
37pub use tooltip::*;
38pub use text_input::*;
39pub use text_edit::*;
40pub use scroll::*;
41pub use image::*;
42
43use bevy::prelude::*;
44use bevy::window::{CursorIcon, SystemCursorIcon};
45use bevy::ecs::query::QueryFilter;
46use bevy::ecs::system::SystemParam;
47use cosmic_text::{FontSystem, SwashCache, Attrs};
48use crate::{events::*, colors::IntoColor};
49
50pub trait Widget {
51 fn build(self) -> impl Bundle;
52}
53
54pub trait SetToolTip: Sized {
56 fn set_tooltip(&mut self) -> &mut TooltipBundle;
57
58 fn tooltip(mut self, text: &str) -> Self {
60 self.set_tooltip().use_tooltip = UseTooltip(true);
61 self.set_tooltip().text = TooltipText(text.to_string());
62 self.set_tooltip().text_bundle.text.0 = text.to_string();
63 self
64 }
65
66 fn tooltip_style(mut self, style: ContainerStyle) -> Self {
68 self.set_tooltip().style = style;
69 self
70 }
71
72 fn tooltip_text_style(mut self, text_style: TextStyle) -> Self {
74 self.set_tooltip().text_bundle.text_style = text_style;
75 self
76 }
77
78 fn tooltip_position(mut self, pos: &str) -> Self {
82 match pos {
83 "left" => self.set_tooltip().position = TooltipPosition::Left,
84 "right" => self.set_tooltip().position = TooltipPosition::Right,
85 "top" => self.set_tooltip().position = TooltipPosition::Top,
86 "bottom" => self.set_tooltip().position = TooltipPosition::Bottom,
87 _ => self.set_tooltip().position = TooltipPosition::Center,
88 }
89 self
90 }
91}
92
93pub trait SetIdAndClass: Sized {
94 fn id_and_class(&mut self) -> &mut IdAndClass;
95
96 fn id(mut self, id: &str) -> Self {
97 self.id_and_class().id.0 = id.to_string();
98 self
99 }
100
101 fn class(mut self, class: &str) -> Self {
102 self.id_and_class().class.0 = class.to_string();
103 self
104 }
105}
106
107pub trait SetContainerStyle: Sized {
108 fn container_style(&mut self) -> &mut ContainerStyle;
109
110 fn node(mut self, node: Node) -> Self {
111 self.container_style().node = node;
112 self
113 }
114
115 fn width(mut self, value: Val) -> Self {
116 self.container_style().node.width = value;
117 self
118 }
119
120 fn height(mut self, value: Val) -> Self {
121 self.container_style().node.height = value;
122 self
123 }
124
125 fn align_items(mut self, ai: AlignItems) -> Self {
126 self.container_style().node.align_items = ai;
127 self
128 }
129
130 fn align_content(mut self, ac: AlignContent) -> Self {
131 self.container_style().node.align_content = ac;
132 self
133 }
134
135 fn justify_content(mut self, jc: JustifyContent) -> Self {
136 self.container_style().node.justify_content = jc;
137 self
138 }
139
140 fn margin(mut self, value: Val) -> Self {
141 self.container_style().node.margin = UiRect::all(value);
142 self
143 }
144
145 fn margin_x(mut self, value: Val) -> Self {
146 self.container_style().node.margin.left = value;
147 self.container_style().node.margin.right = value;
148 self
149 }
150
151 fn margin_y(mut self, value: Val) -> Self {
152 self.container_style().node.margin.top = value;
153 self.container_style().node.margin.bottom = value;
154 self
155 }
156
157 fn margin_top(mut self, value: Val) -> Self {
158 self.container_style().node.margin.top = value;
159 self
160 }
161
162 fn margin_right(mut self, value: Val) -> Self {
163 self.container_style().node.margin.right = value;
164 self
165 }
166
167 fn margin_bottom(mut self, value: Val) -> Self {
168 self.container_style().node.margin.bottom = value;
169 self
170 }
171
172 fn margin_left(mut self, value: Val) -> Self {
173 self.container_style().node.margin.left = value;
174 self
175 }
176
177 fn padding(mut self, value: Val) -> Self {
178 self.container_style().node.padding = UiRect::all(value);
179 self
180 }
181
182 fn padding_x(mut self, value: Val) -> Self {
183 self.container_style().node.padding.left = value;
184 self.container_style().node.padding.right = value;
185 self
186 }
187
188 fn padding_y(mut self, value: Val) -> Self {
189 self.container_style().node.padding.top = value;
190 self.container_style().node.padding.bottom = value;
191 self
192 }
193
194 fn padding_top(mut self, value: Val) -> Self {
195 self.container_style().node.padding.top = value;
196 self
197 }
198
199 fn padding_bottom(mut self, value: Val) -> Self {
200 self.container_style().node.padding.bottom = value;
201 self
202 }
203
204 fn padding_left(mut self, value: Val) -> Self {
205 self.container_style().node.padding.left = value;
206 self
207 }
208
209 fn padding_right(mut self, value: Val) -> Self {
210 self.container_style().node.padding.right = value;
211 self
212 }
213
214 fn border(mut self, value: Val) -> Self {
215 self.container_style().node.border = UiRect::all(value);
216 self
217 }
218
219 fn border_top(mut self, value: Val) -> Self {
220 self.container_style().node.border.top = value;
221 self
222 }
223
224 fn border_bottom(mut self, value: Val) -> Self {
225 self.container_style().node.border.bottom = value;
226 self
227 }
228
229 fn border_left(mut self, value: Val) -> Self {
230 self.container_style().node.border.left = value;
231 self
232 }
233
234 fn border_right(mut self, value: Val) -> Self {
235 self.container_style().node.border.right = value;
236 self
237 }
238
239 fn background_color(mut self, color: impl IntoColor) -> Self {
240 self.container_style().background_color.0 = color.into_color();
241 self
242 }
243
244 fn border_radius(mut self, radius: BorderRadius) -> Self{
245 self.container_style().node.border_radius = radius;
246 self
247 }
248
249 fn border_color(mut self, color: impl IntoColor) -> Self {
250 self.container_style().border_color = BorderColor::all(color.into_color());
251 self
252 }
253
254 fn border_top_color(mut self, color: impl IntoColor) -> Self {
255 self.container_style().border_color.top = color.into_color();
256 self
257 }
258
259 fn border_bottom_color(mut self, color: impl IntoColor) -> Self {
260 self.container_style().border_color.bottom = color.into_color();
261 self
262 }
263
264 fn border_left_color(mut self, color: impl IntoColor) -> Self {
265 self.container_style().border_color.left = color.into_color();
266 self
267 }
268
269 fn border_right_color(mut self, color: impl IntoColor) -> Self {
270 self.container_style().border_color.right = color.into_color();
271 self
272 }
273
274 fn shadow(mut self, shadow: ShadowStyle) -> Self {
275 self.container_style().shadow = BoxShadow(vec![shadow]);
276 self
277 }
278
279 fn no_shadow(mut self) -> Self {
280 self.container_style().shadow = BoxShadow::default();
281 self
282 }
283
284 fn style(mut self, style: ContainerStyle) -> Self {
285 *self.container_style() = style;
286 self
287 }
288}
289
290#[derive(Component, Debug, Default, PartialEq, Eq, Clone)]
292pub struct Id(pub String);
293
294#[derive(Component, Debug, Default, PartialEq, Eq, Clone)]
296pub struct Class(pub String);
297
298impl Class {
299 pub fn add_class(&mut self, class: &str) {
301 let mut classes: Vec<_> = self.0.split_whitespace().collect();
302
303 for new_class in class.split_whitespace() {
304 if !classes.contains(&new_class) {
305 classes.push(new_class);
306 }
307 }
308
309 self.0 = classes.join(" ");
310 }
311
312 pub fn remove_class(&mut self, class: &str) {
314 let to_remove: Vec<_> = class.split_whitespace().collect();
315
316 let classes: Vec<_> = self.0
317 .split_whitespace()
318 .filter(|c| !to_remove.contains(c))
319 .collect();
320
321 self.0 = classes.join(" ");
322 }
323
324 pub fn class_list(&self) -> Vec<String> {
328 self.0
329 .split_whitespace()
330 .map(|s| s.to_string())
331 .collect()
332 }
333
334 pub fn has_class(&self, class: &str) -> bool {
336 self.0.split_whitespace().any(|c| c == class)
337 }
338}
339
340#[derive(Bundle, Clone, Default, Debug)]
341pub struct IdAndClass {
342 pub id: Id,
343 pub class: Class
344}
345
346#[derive(SystemParam)]
348pub struct StyleQuery<'w, 's, F: QueryFilter + 'static = ()> {
349 pub query: Query<'w, 's, (
350 &'static mut Node,
351 &'static mut BackgroundColor,
352 &'static mut BorderColor,
353 &'static mut BoxShadow,
354 &'static mut ZIndex,
355 ), F>,
356}
357
358pub struct ChildText<'a> {
361 pub value: &'a mut Text,
362 pub font: &'a mut TextFont,
363 pub layout: &'a mut TextLayout,
364 pub color: &'a mut TextColor
365}
366
367pub struct WidgetStyle<'a> {
369 pub node: &'a mut Node,
370 pub background_color: &'a mut BackgroundColor,
371 pub border_color: &'a mut BorderColor,
372 pub z_index: &'a mut ZIndex,
373 pub shadow: &'a mut BoxShadow
374}
375
376pub trait WidgetQuery<'w, 's> {
378 type WidgetView<'a> where Self: 'a;
380
381 fn find_by_id<'a>(&'a mut self, target_id: &str) -> Option<Self::WidgetView<'a>>;
383
384 fn find_by_entity<'a>(&'a mut self, entity: Entity) -> Option<Self::WidgetView<'a>>;
386
387 fn find_by_class(&self, target_class: &str) -> Vec<Entity>;
389
390 fn get_by_id<F>(&mut self, id: &str, f: F)
431 where
432 F: for<'a> FnOnce(&mut Self::WidgetView<'a>)
433 {
434 if let Some(mut widget) = self.find_by_id(id) {
435 f(&mut widget);
436 }
437 }
438
439 fn get_by_entity<F>(&mut self, entity: Entity, f: F)
441 where
442 F: for<'a> FnOnce(&mut Self::WidgetView<'a>)
443 {
444 if let Some(mut widget) = self.find_by_entity(entity) {
445 f(&mut widget);
446 }
447 }
448
449 fn get_by_class<F>(&mut self, class: &str, mut f: F)
451 where
452 F: for<'a> FnMut(&mut Self::WidgetView<'a>)
453 {
454 let entities = self.find_by_class(class);
455
456 for entity in entities {
457 if let Some(mut widget) = self.find_by_entity(entity) {
458 f(&mut widget);
459 }
460 }
461 }
462
463 fn get_components<'a>(&'a mut self, entity: Entity) -> Option<Self::WidgetView<'a>>;
465}
466
467pub trait WidgetChildren {
469 fn add_child(&mut self, child_bundle: impl Bundle);
471
472 fn add_children(&mut self, bundles: impl IntoIterator<Item = impl Bundle>);
474
475 fn insert_at(
477 &mut self,
478 index: usize,
479 bundles: impl IntoIterator<Item = impl Bundle>
480 );
481
482 fn insert_first(&mut self, bundles: impl IntoIterator<Item = impl Bundle>);
484
485 fn insert_last(&mut self, bundles: impl IntoIterator<Item = impl Bundle>);
487
488 fn remove_at(&mut self, index: usize);
491
492 fn remove_first(&mut self);
495
496 fn remove_last(&mut self);
499}
500
501#[derive(Component)]
502pub(crate) struct MakaraWidget;
503
504#[derive(Component)]
506pub struct WidgetFocus(pub bool);
507
508#[derive(Bundle, Clone)]
510pub struct ContainerStyle {
511 pub node: Node,
512 pub background_color: BackgroundColor,
513 pub border_color: BorderColor,
514 pub shadow: BoxShadow,
515 pub z_index: ZIndex,
516}
517
518impl Default for ContainerStyle {
519 fn default() -> Self {
520 Self {
521 node: Node::default(),
522 background_color: BackgroundColor::default(),
523 border_color: BorderColor::default(),
524 z_index: ZIndex::default(),
525 shadow: BoxShadow::new(
526 Color::BLACK.with_alpha(0.8),
527 px(0.0),
528 px(1.0),
529 px(1.0),
530 px(2.0),
531 ),
532 }
533 }
534}
535
536#[derive(Default, PartialEq, Eq)]
538pub enum Theme {
539 #[default]
540 Light,
541 Dark
542}
543
544#[derive(Resource, Default)]
546pub struct MakaraTheme {
547 pub theme: Theme
548}
549
550impl MakaraTheme {
551 pub fn change_theme(&mut self, theme: Theme) {
553 self.theme = theme;
554 }
555}
556
557#[derive(Resource)]
559pub struct MakaraTextEditContext {
560 pub font_system: FontSystem,
561 pub swash_cache: SwashCache,
562 pub attrs: Attrs<'static>
563}
564
565impl Default for MakaraTextEditContext {
566 fn default() -> Self {
567 Self {
568 font_system: FontSystem::new(),
569 swash_cache: SwashCache::new(),
570 attrs: Attrs::new()
571 }
572 }
573}
574
575pub(crate) fn on_mouse_out(
577 mut out: On<Pointer<Out>>,
578 mut commands: Commands,
579 mut tooltips: Query<
580 (&mut Node, &ComputedNode, &TooltipPosition, &UseTooltip),
581 With<MakaraTooltip>
582 >,
583 widgets: Query<&Children, With<MakaraWidget>>,
584 window: Single<Entity, With<Window>>,
585) {
586 if let Ok(children) = widgets.get(out.entity) {
587 show_or_hide_tooltip(false, &mut tooltips, None, None, children);
588 }
589
590 commands.entity(*window).insert(CursorIcon::System(SystemCursorIcon::Default));
591 commands.trigger(MouseOut { entity: out.entity });
592 out.propagate(false);
593}