use crate::styles::paint::Colored;
use crate::styles::{CssClass, CssSource, IconPlace, TagName};
use crate::widgets::controls::place_icon_if;
use crate::widgets::{BindToID, Button, UIGenID, UIWidgetState, WidgetId, WidgetKind};
use crate::{CurrentWidgetState, ExtendedUiConfiguration, ImageCache};
use bevy::camera::visibility::RenderLayers;
use bevy::ecs::relationship::RelatedSpawnerCommands;
use bevy::prelude::*;
#[derive(Component)]
struct ButtonBase;
#[derive(Component)]
struct ButtonText;
pub struct ButtonWidget;
impl Plugin for ButtonWidget {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(internal_node_creation_system, update_button_system),
);
}
}
fn internal_node_creation_system(
mut commands: Commands,
query: Query<
(Entity, &UIGenID, &Button, Option<&CssSource>),
(With<Button>, Without<ButtonBase>),
>,
config: Res<ExtendedUiConfiguration>,
asset_server: Res<AssetServer>,
mut image_cache: ResMut<ImageCache>,
mut images: ResMut<Assets<Image>>,
) {
let layer = config.render_layers.first().unwrap_or(&1);
for (entity, id, button, source_opt) in query.iter() {
let mut css_source = CssSource::default();
if let Some(source) = source_opt {
css_source = source.clone();
}
commands
.entity(entity)
.insert((
Name::new(format!("Button-{}", button.entry)),
Node::default(),
WidgetId {
id: button.entry,
kind: WidgetKind::Button,
},
BackgroundColor::default(),
ImageNode::default(),
BorderColor::default(),
BoxShadow::new(
Colored::TRANSPARENT,
Val::Px(0.),
Val::Px(0.),
Val::Px(0.),
Val::Px(0.),
),
ZIndex::default(),
Pickable::default(),
css_source.clone(),
TagName("button".to_string()),
RenderLayers::layer(*layer),
ButtonBase,
))
.with_children(|builder| {
spawn_button_children(
builder,
button,
id,
*layer,
css_source.clone(),
&asset_server,
&mut image_cache,
&mut images,
);
})
.observe(on_internal_click)
.observe(on_internal_cursor_entered)
.observe(on_internal_cursor_leave);
}
}
fn update_button_system(
mut commands: Commands,
query: Query<
(
Entity,
&UIGenID,
&Button,
Option<&CssSource>,
Option<&Children>,
),
(With<Button>, With<ButtonBase>, Changed<Button>),
>,
text_q: Query<(), With<ButtonText>>,
image_q: Query<(), With<crate::widgets::controls::ButtonImage>>,
config: Res<ExtendedUiConfiguration>,
asset_server: Res<AssetServer>,
mut image_cache: ResMut<ImageCache>,
mut images: ResMut<Assets<Image>>,
) {
let layer = config.render_layers.first().unwrap_or(&1);
for (entity, id, button, source_opt, children_opt) in query.iter() {
let mut css_source = CssSource::default();
if let Some(source) = source_opt {
css_source = source.clone();
}
if let Some(children) = children_opt {
for child in children.iter() {
if commands.get_entity(child).is_ok()
&& (text_q.get(child).is_ok() || image_q.get(child).is_ok())
{
commands.entity(child).despawn();
}
}
}
commands.entity(entity).with_children(|builder| {
spawn_button_children(
builder,
button,
id,
*layer,
css_source.clone(),
&asset_server,
&mut image_cache,
&mut images,
);
});
}
}
fn spawn_button_children(
builder: &mut RelatedSpawnerCommands<ChildOf>,
button: &Button,
id: &UIGenID,
layer: usize,
css_source: CssSource,
asset_server: &Res<AssetServer>,
image_cache: &mut ResMut<ImageCache>,
images: &mut ResMut<Assets<Image>>,
) {
place_icon_if(
builder,
button.icon_place,
IconPlace::Left,
&button.icon_path,
button.entry,
asset_server,
image_cache,
images,
vec!["button-text".to_string()],
id.0,
layer,
css_source.clone(),
);
builder.spawn((
Name::new(format!("Button-Text-{}", button.entry)),
Text::new(button.text.clone()),
TextColor::default(),
TextFont::default(),
TextLayout::default(),
css_source.clone(),
UIWidgetState::default(),
ZIndex::default(),
CssClass(vec!["button-text".to_string()]),
Pickable::IGNORE,
BindToID(id.0),
RenderLayers::layer(layer),
ButtonText,
));
place_icon_if(
builder,
button.icon_place,
IconPlace::Right,
&button.icon_path,
button.entry,
asset_server,
image_cache,
images,
vec!["button-text".to_string()],
id.0,
layer,
css_source.clone(),
);
}
fn on_internal_click(
mut trigger: On<Pointer<Click>>,
mut query: Query<(&mut UIWidgetState, &UIGenID), With<Button>>,
mut current_widget_state: ResMut<CurrentWidgetState>,
) {
if let Ok((mut state, gen_id)) = query.get_mut(trigger.entity) {
if state.disabled {
trigger.propagate(false);
return;
}
state.focused = true;
state.checked = true;
current_widget_state.widget_id = gen_id.0;
}
trigger.propagate(false);
}
fn on_internal_cursor_entered(
mut trigger: On<Pointer<Over>>,
mut query: Query<&mut UIWidgetState, With<Button>>,
) {
if let Ok(mut state) = query.get_mut(trigger.entity) {
state.hovered = true;
}
trigger.propagate(false);
}
fn on_internal_cursor_leave(
mut trigger: On<Pointer<Out>>,
mut query: Query<&mut UIWidgetState, With<Button>>,
) {
if let Ok(mut state) = query.get_mut(trigger.entity) {
state.hovered = false;
}
trigger.propagate(false);
}