use crate::ecs::ui::components::UiWidgetState;
use crate::ecs::world::World;
pub fn ui_property_sync_system(world: &mut World) {
let property_ids: Vec<crate::ecs::ui::resources::PropertyId> = world
.resources
.retained_ui
.bound_properties
.keys()
.copied()
.collect();
for id in property_ids {
let (entity, dirty_from_code, value_clone) = {
let Some(prop) = world.resources.retained_ui.bound_properties.get(&id) else {
continue;
};
(
prop.entity,
prop.dirty_from_code,
match &prop.value {
crate::ecs::ui::resources::PropertyValue::F32(v) => {
crate::ecs::ui::resources::PropertyValue::F32(*v)
}
crate::ecs::ui::resources::PropertyValue::Bool(v) => {
crate::ecs::ui::resources::PropertyValue::Bool(*v)
}
crate::ecs::ui::resources::PropertyValue::Usize(v) => {
crate::ecs::ui::resources::PropertyValue::Usize(*v)
}
crate::ecs::ui::resources::PropertyValue::String(v) => {
crate::ecs::ui::resources::PropertyValue::String(v.clone())
}
crate::ecs::ui::resources::PropertyValue::Vec4(v) => {
crate::ecs::ui::resources::PropertyValue::Vec4(*v)
}
},
)
};
let widget_changed = if let Some(state) = world.ui.get_ui_widget_state(entity) {
match state {
UiWidgetState::Slider(data) => data.changed,
UiWidgetState::DragValue(data) => data.changed,
UiWidgetState::Toggle(data) => data.changed,
UiWidgetState::Checkbox(data) => data.changed,
UiWidgetState::TextInput(data) => data.changed,
UiWidgetState::Dropdown(data) => data.changed,
UiWidgetState::TextArea(data) => data.changed,
UiWidgetState::RichTextEditor(data) => data.changed,
UiWidgetState::ColorPicker(data) => data.changed,
UiWidgetState::TabBar(data) => data.changed,
UiWidgetState::SelectableLabel(data) => data.changed,
UiWidgetState::CollapsingHeader(data) => data.changed,
UiWidgetState::Splitter(data) => data.changed,
UiWidgetState::RangeSlider(data) => data.changed,
UiWidgetState::Radio(data) => {
let group_id = data.group_id;
let members = world
.resources
.retained_ui
.radio_groups
.get(&group_id)
.cloned()
.unwrap_or_default();
members.iter().any(|&member| {
matches!(
world.ui.get_ui_widget_state(member),
Some(UiWidgetState::Radio(r)) if r.changed
)
})
}
_ => false,
}
} else {
false
};
if widget_changed {
if let Some(state) = world.ui.get_ui_widget_state(entity) {
let new_value = match state {
UiWidgetState::Slider(data) => {
Some(crate::ecs::ui::resources::PropertyValue::F32(data.value))
}
UiWidgetState::DragValue(data) => {
Some(crate::ecs::ui::resources::PropertyValue::F32(data.value))
}
UiWidgetState::Toggle(data) => {
Some(crate::ecs::ui::resources::PropertyValue::Bool(data.value))
}
UiWidgetState::Checkbox(data) => {
Some(crate::ecs::ui::resources::PropertyValue::Bool(data.value))
}
UiWidgetState::TextInput(data) => Some(
crate::ecs::ui::resources::PropertyValue::String(data.text.clone()),
),
UiWidgetState::Dropdown(data) => Some(
crate::ecs::ui::resources::PropertyValue::Usize(data.selected_index),
),
UiWidgetState::TextArea(data) => Some(
crate::ecs::ui::resources::PropertyValue::String(data.text.clone()),
),
UiWidgetState::RichTextEditor(data) => Some(
crate::ecs::ui::resources::PropertyValue::String(data.text.clone()),
),
UiWidgetState::ColorPicker(data) => {
Some(crate::ecs::ui::resources::PropertyValue::Vec4(data.color))
}
UiWidgetState::TabBar(data) => Some(
crate::ecs::ui::resources::PropertyValue::Usize(data.selected_tab),
),
UiWidgetState::SelectableLabel(data) => Some(
crate::ecs::ui::resources::PropertyValue::Bool(data.selected),
),
UiWidgetState::CollapsingHeader(data) => {
Some(crate::ecs::ui::resources::PropertyValue::Bool(data.open))
}
UiWidgetState::Radio(data) => {
let group_id = data.group_id;
world
.ui_radio_group_value(group_id)
.map(crate::ecs::ui::resources::PropertyValue::Usize)
}
UiWidgetState::Splitter(data) => {
Some(crate::ecs::ui::resources::PropertyValue::F32(data.ratio))
}
UiWidgetState::RangeSlider(data) => {
Some(crate::ecs::ui::resources::PropertyValue::Vec4(
nalgebra_glm::Vec4::new(data.low_value, data.high_value, 0.0, 0.0),
))
}
_ => None,
};
if let Some(val) = new_value
&& let Some(prop) = world.resources.retained_ui.bound_properties.get_mut(&id)
{
prop.value = val;
prop.dirty_from_widget = true;
}
}
} else if dirty_from_code && let Some(state) = world.ui.get_ui_widget_state(entity) {
match (state, &value_clone) {
(UiWidgetState::Slider(_), crate::ecs::ui::resources::PropertyValue::F32(v)) => {
let v = *v;
world.ui_slider_set_value(entity, v);
}
(UiWidgetState::DragValue(_), crate::ecs::ui::resources::PropertyValue::F32(v)) => {
let v = *v;
world.ui_drag_value_set_value(entity, v);
}
(UiWidgetState::Toggle(_), crate::ecs::ui::resources::PropertyValue::Bool(v)) => {
let v = *v;
world.ui_toggle_set_value(entity, v);
}
(UiWidgetState::Checkbox(_), crate::ecs::ui::resources::PropertyValue::Bool(v)) => {
if let Some(UiWidgetState::Checkbox(data)) =
world.ui.get_ui_widget_state_mut(entity)
{
data.value = *v;
}
}
(
UiWidgetState::SelectableLabel(_),
crate::ecs::ui::resources::PropertyValue::Bool(v),
) => {
let v = *v;
world.ui_set_selected(entity, v);
}
(
UiWidgetState::CollapsingHeader(header_data),
crate::ecs::ui::resources::PropertyValue::Bool(v),
) => {
let new_open = *v;
let content_entity = header_data.content_entity;
let arrow_text_slot = header_data.arrow_text_slot;
if let Some(UiWidgetState::CollapsingHeader(data)) =
world.ui.get_ui_widget_state_mut(entity)
{
data.open = new_open;
}
if let Some(node) = world.ui.get_ui_layout_node_mut(content_entity) {
node.visible = new_open;
}
world.resources.text_cache.set_text(
arrow_text_slot,
if new_open { "\u{25BC}" } else { "\u{25B6}" },
);
}
(
UiWidgetState::Radio(radio_data),
crate::ecs::ui::resources::PropertyValue::Usize(v),
) => {
let target_index = *v;
let group_id = radio_data.group_id;
let members = world
.resources
.retained_ui
.radio_groups
.get(&group_id)
.cloned()
.unwrap_or_default();
for member in members {
if let Some(UiWidgetState::Radio(member_data)) =
world.ui.get_ui_widget_state(member).cloned().as_ref()
{
let should_select = member_data.option_index == target_index;
if let Some(node) =
world.ui.get_ui_layout_node_mut(member_data.inner_entity)
{
node.visible = should_select;
}
if let Some(UiWidgetState::Radio(wd)) =
world.ui.get_ui_widget_state_mut(member)
{
wd.selected = should_select;
}
}
}
}
(
UiWidgetState::TextInput(_),
crate::ecs::ui::resources::PropertyValue::String(v),
) => {
world.ui_text_input_set_value(entity, v);
}
(
UiWidgetState::TextArea(_),
crate::ecs::ui::resources::PropertyValue::String(v),
) => {
world.ui_text_area_set_value(entity, v);
}
(
UiWidgetState::RichTextEditor(_),
crate::ecs::ui::resources::PropertyValue::String(v),
) => {
world.ui_rich_text_editor_set_value(entity, v);
}
(
UiWidgetState::Dropdown(_),
crate::ecs::ui::resources::PropertyValue::Usize(v),
) => {
let v = *v;
world.ui_dropdown_set_value(entity, v);
}
(UiWidgetState::TabBar(_), crate::ecs::ui::resources::PropertyValue::Usize(v)) => {
let v = *v;
world.ui_tab_bar_set_value(entity, v);
}
(
UiWidgetState::ColorPicker(_),
crate::ecs::ui::resources::PropertyValue::Vec4(v),
) => {
let v = *v;
world.ui_color_picker_set_value(entity, v);
}
(UiWidgetState::Splitter(_), crate::ecs::ui::resources::PropertyValue::F32(v)) => {
if let Some(UiWidgetState::Splitter(data)) =
world.ui.get_ui_widget_state_mut(entity)
{
data.ratio = *v;
}
}
(
UiWidgetState::RangeSlider(_),
crate::ecs::ui::resources::PropertyValue::Vec4(v),
) => {
world.ui_range_slider_set_values(entity, v.x, v.y);
}
_ => {}
}
}
}
for _iteration in 0..8 {
let mut dirty_props: Vec<(String, crate::ecs::ui::resources::PropertyValue)> = Vec::new();
for (name, property_id) in &world.resources.retained_ui.named_properties {
if let Some(bp) = world
.resources
.retained_ui
.bound_properties
.get(property_id)
&& (bp.dirty_from_widget || bp.dirty_from_code)
{
dirty_props.push((name.clone(), bp.value.clone()));
}
}
if dirty_props.is_empty() {
break;
}
for (name, _) in &dirty_props {
if let Some(property_id) = world
.resources
.retained_ui
.named_properties
.get(name)
.copied()
&& let Some(bp) = world
.resources
.retained_ui
.bound_properties
.get_mut(&property_id)
{
bp.dirty_from_widget = false;
bp.dirty_from_code = false;
}
}
let mut reactions = std::mem::take(&mut world.resources.retained_ui.property_reactions);
for (name, value) in &dirty_props {
if let Some(reaction_list) = reactions.get_mut(name) {
for reaction in reaction_list.iter_mut() {
reaction(value, world);
}
}
}
let newly_registered = std::mem::take(&mut world.resources.retained_ui.property_reactions);
for (name, mut new_reactions) in newly_registered {
reactions
.entry(name)
.or_default()
.append(&mut new_reactions);
}
world.resources.retained_ui.property_reactions = reactions;
}
}