use bevy::prelude::*;
use bevy::ui::{FocusPolicy, InteractionDisabled};
use crate::element::{Attribute, Common, Element};
use crate::ext::ElementFlags;
use crate::protocol::animatable::Animatable;
use crate::protocol::units::Length;
use crate::style::props::{FLEX_DIRECTION, FOCUS_POLICY, GLOBAL_Z_INDEX, HEIGHT, WIDTH};
use crate::style::{Invalidation, Style, Writer, owns};
pub static NODE: Element = Element::new("node");
pub static BUTTON: Element = Element {
attrs: &[&DISABLED],
writers: &[&DISABLED_WRITER],
default_style: Some(button_style),
spawn: Some(|ctx| ctx.spawn((bevy::ui_widgets::Button, crate::ReactInteraction::default()))),
..Element::new("button")
};
fn button_style() -> Style {
let mut style = Style::default();
style.set(&FOCUS_POLICY, FocusPolicy::Block);
style
}
pub static DISABLED: Attribute<bool> = Attribute {
invalidate: Invalidation::PAINT,
..Attribute::new("disabled")
};
pub static DISABLED_WRITER: Writer = Writer {
reads: &[],
attrs: &[&DISABLED],
writes: &[owns::<InteractionDisabled>],
apply: |ctx, _style, ec| {
let disabled = ctx.attr(&DISABLED).copied().unwrap_or(false);
if ctx.fresh {
if disabled {
ec.insert(InteractionDisabled);
}
return;
}
ec.queue(move |mut entity: EntityWorldMut| {
if entity.contains::<InteractionDisabled>() == disabled {
return;
}
if disabled {
entity.insert(InteractionDisabled);
} else {
entity.remove::<InteractionDisabled>();
}
});
},
};
#[allow(clippy::type_complexity)]
pub fn label_buttons(
mut commands: Commands,
buttons: Query<
Entity,
(
With<bevy::ui_widgets::Button>,
With<crate::bridge::ReactNode>,
Or<(Added<bevy::ui_widgets::Button>, Changed<Children>)>,
),
>,
children: Query<&Children>,
texts: Query<(), With<Text>>,
mut text_reader: bevy::ui::widget::TextUiReader,
) {
for button in &buttons {
let label = children
.iter_descendants_depth_first(button)
.filter(|e| texts.contains(*e))
.flat_map(|block| {
text_reader
.iter(block)
.filter_map(|(_, _, item)| match item {
bevy::text::TextElement::Text { text, .. } => Some(text.to_string()),
bevy::text::TextElement::Box(_) => None,
})
.collect::<Vec<_>>()
})
.filter(|text| !text.trim().is_empty())
.collect::<Vec<_>>()
.join(" ");
if label.is_empty() {
commands.entity(button).remove::<AccessibleLabel>();
} else {
commands.entity(button).insert(AccessibleLabel::new(label));
}
}
}
pub static ROOT: Element = Element {
flags: ElementFlags {
pick_ignore: true,
..ElementFlags::DETACHED_ROOT
},
common: Common::IDENTITY,
default_style: Some(root_style),
spawn: Some(|ctx| {
ctx.spawn((
crate::bridge::RRoot,
bevy::input_focus::tab_navigation::TabGroup::new(1),
))
}),
..Element::new("root")
};
fn root_style() -> Style {
let mut style = Style::default();
style.set(&WIDTH, Animatable::Static(Length::Vw(100.0)));
style.set(&HEIGHT, Animatable::Static(Length::Vh(100.0)));
style.set(&FLEX_DIRECTION, FlexDirection::Column);
style.set(&GLOBAL_Z_INDEX, 1);
style
}