use crate::prelude::*;
mod events;
mod handle_basic;
mod handle_context_menu;
mod handle_data_grid;
mod handle_drag_value;
mod handle_input;
mod handle_navigation;
mod handle_panel;
mod handle_rich_text;
mod handle_scroll;
mod handle_slider;
mod handle_text_area;
mod handle_tiles;
mod handle_tree;
mod input_sync;
mod pool_window;
pub mod text_cursor;
pub use events::{ui_emit_click_events_system, ui_event_bubble_system};
pub use input_sync::ui_retained_input_sync_system;
pub use text_cursor::measure_text_width;
use nalgebra_glm::Vec2;
use winit::keyboard::KeyCode;
use crate::ecs::world::World;
use handle_basic::{
handle_checkbox, handle_collapsing_header, handle_radio, handle_spinner, handle_toggle,
};
use handle_context_menu::{close_submenu_defs_state, close_submenu_popups, handle_context_menu};
use handle_data_grid::{
DataGridContext, handle_command_palette, handle_data_grid, handle_property_grid,
};
use handle_drag_value::handle_drag_value;
use handle_input::{handle_selectable_label, handle_text_input};
use handle_navigation::{
handle_date_picker, handle_dropdown, handle_menu, handle_multi_select, handle_tab_bar,
};
use handle_panel::{PanelContext, handle_color_picker, handle_color_wheel, handle_panel};
use handle_rich_text::{RichTextEditorContext, handle_rich_text_editor};
use handle_scroll::{handle_scroll_area, handle_virtual_list};
use handle_slider::{handle_breadcrumb, handle_range_slider, handle_slider, handle_splitter};
use handle_text_area::handle_text_area;
use handle_tiles::{TileContainerContext, handle_tile_container};
use handle_tree::{TreeViewContext, handle_modal_dialog, handle_tree_view};
struct TextEditContext<'a> {
focused_entity: Option<freecs::Entity>,
frame_chars: &'a [char],
frame_keys: &'a [(KeyCode, bool)],
ctrl_held: bool,
shift_held: bool,
mouse_position: Vec2,
current_time: f64,
dpi_scale: f32,
}
struct InteractionSnapshot {
clicked: bool,
pressed: bool,
dragging: bool,
hovered: bool,
double_clicked: bool,
right_clicked: bool,
drag_start: Option<Vec2>,
}
fn snapshot_interaction(world: &World, entity: freecs::Entity) -> InteractionSnapshot {
world
.ui
.get_ui_node_interaction(entity)
.map(|interaction| InteractionSnapshot {
clicked: interaction.clicked,
pressed: interaction.pressed,
dragging: interaction.dragging,
hovered: interaction.hovered,
double_clicked: interaction.double_clicked,
right_clicked: interaction.right_clicked,
drag_start: interaction.drag_start,
})
.unwrap_or(InteractionSnapshot {
clicked: false,
pressed: false,
dragging: false,
hovered: false,
double_clicked: false,
right_clicked: false,
drag_start: None,
})
}
pub fn ui_widget_interaction_system(world: &mut World) {
if !world.resources.retained_ui.enabled {
return;
}
let delta_time = world.resources.retained_ui.timing.delta_time;
let current_time = world.resources.retained_ui.timing.current_time;
let mouse_position = crate::ecs::input::access::mouse_for_active(world).position;
let frame_chars = std::mem::take(&mut world.resources.retained_ui.input.frame_chars);
let frame_keys = std::mem::take(&mut world.resources.retained_ui.input.frame_keys);
let ctrl_held = world.resources.retained_ui.input.ctrl_held;
let shift_held = world.resources.retained_ui.input.shift_held;
let scroll_delta = world.resources.retained_ui.input.scroll_delta;
let focused_entity = world
.resources
.retained_ui
.interaction_for_active_mut()
.focused_entity;
let mouse_state = crate::ecs::input::access::mouse_for_active(world).state;
let mouse_just_pressed =
mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
let mouse_down = mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_CLICKED);
let dpi_scale = crate::ecs::window::resources::window_scale_factor(world);
let viewport_size = crate::ecs::window::resources::window_viewport_size(world)
.map(|(width, height)| Vec2::new(width as f32, height as f32))
.unwrap_or(Vec2::new(800.0, 600.0));
let has_text_focus = focused_entity.is_some_and(|fe| {
world.ui.get_ui_text_input(fe).is_some()
|| world.ui.get_ui_text_area(fe).is_some()
|| world.ui.get_ui_drag_value(fe).is_some()
});
if !has_text_focus {
let bindings = world.resources.retained_ui.groups.shortcuts.clone();
let alt_held = world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::AltLeft)
|| world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::AltRight);
for &(ref key, pressed) in &frame_keys {
if !pressed {
continue;
}
for (binding, command_index) in &bindings {
if binding.key == *key
&& binding.ctrl == ctrl_held
&& binding.shift == shift_held
&& binding.alt == alt_held
{
world.resources.retained_ui.events_for_active_mut().push(
crate::ecs::ui::resources::UiEvent::ShortcutTriggered {
command_index: *command_index,
},
);
}
}
}
let chrome_shortcuts = world.resources.retained_ui.window_chrome.shortcuts.clone();
for &(ref key, pressed) in &frame_keys {
if !pressed {
continue;
}
for shortcut in &chrome_shortcuts {
if shortcut.binding.key == *key
&& shortcut.binding.ctrl == ctrl_held
&& shortcut.binding.shift == shift_held
&& shortcut.binding.alt == alt_held
{
world.resources.retained_ui.events_for_active_mut().push(
crate::ecs::ui::resources::UiEvent::ShortcutTriggered {
command_index: shortcut.command_index,
},
);
}
}
}
}
let mut panel_rects: Vec<(freecs::Entity, crate::ecs::ui::types::Rect, f32)> = Vec::new();
let panel_entities: Vec<freecs::Entity> = world
.ui
.query_entities(crate::ecs::world::UI_PANEL)
.collect();
for entity in &panel_entities {
if world.ui.get_ui_panel(*entity).is_some()
&& let Some(node) = world.ui.get_ui_layout_node(*entity)
&& node.visible
{
panel_rects.push((*entity, node.computed_rect, node.computed_depth));
}
}
let text_edit_ctx = TextEditContext {
focused_entity,
frame_chars: &frame_chars,
frame_keys: &frame_keys,
ctrl_held,
shift_held,
mouse_position,
current_time,
dpi_scale,
};
for entity in world
.ui
.query_entities(crate::ecs::world::UI_BUTTON)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
if let Some(data) = world.ui.get_ui_button_mut(entity) {
data.clicked = interaction.clicked;
}
if interaction.clicked {
world
.resources
.retained_ui
.frame
.events
.push(crate::ecs::ui::resources::UiEvent::ButtonClicked(entity));
}
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_SLIDER)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_slider(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_slider(
world,
entity,
&interaction,
&data,
mouse_position,
focused_entity,
&frame_keys,
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_TOGGLE)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_toggle(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_toggle(world, entity, &interaction, &data, delta_time);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_CHECKBOX)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_checkbox(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_checkbox(world, entity, &interaction, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_RADIO)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_radio(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_radio(world, entity, &interaction, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_SPINNER)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_spinner(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_spinner(world, entity, &data, delta_time);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_COLLAPSING_HEADER)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_collapsing_header(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_collapsing_header(world, entity, &interaction, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_SCROLL_AREA)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_scroll_area(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_scroll_area(
world,
entity,
&data,
scroll_delta,
mouse_position,
dpi_scale,
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_TAB_BAR)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_tab_bar(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_tab_bar(world, entity, &data, &frame_keys, focused_entity);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_TEXT_INPUT)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_text_input(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_text_input(world, entity, &interaction, &data, &text_edit_ctx);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_DROPDOWN)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_dropdown(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_dropdown(
world,
entity,
&interaction,
&data,
&frame_keys,
focused_entity,
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_MENU)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_menu(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_menu(world, entity, &interaction, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_PANEL)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_panel(entity) {
Some(d) => d.clone(),
None => continue,
};
let panel_mouse = crate::ecs::input::access::mouse_for_active(world);
let panel_mouse_position = panel_mouse.position;
let panel_mouse_state_bits = panel_mouse.state;
let panel_mouse_just_pressed = panel_mouse_state_bits
.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
let panel_mouse_just_released = panel_mouse_state_bits
.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_RELEASED);
let panel_mouse_down =
panel_mouse_state_bits.contains(crate::ecs::input::resources::MouseState::LEFT_CLICKED);
let panel_depth = world
.ui
.get_ui_layout_node(entity)
.map(|n| n.computed_depth)
.unwrap_or(0.0);
let mouse_occluded = panel_rects
.iter()
.any(|(other_entity, other_rect, other_depth)| {
*other_entity != entity
&& *other_depth > panel_depth
&& other_rect.contains(panel_mouse_position)
});
handle_panel(
world,
entity,
&data,
&PanelContext {
mouse_position: panel_mouse_position,
mouse_just_pressed: panel_mouse_just_pressed,
mouse_just_released: panel_mouse_just_released,
mouse_down: panel_mouse_down,
dpi_scale,
viewport_size,
mouse_occluded,
},
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_COLOR_PICKER)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_color_picker(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_color_picker(world, entity, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_COLOR_WHEEL)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_color_wheel(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_color_wheel(world, entity, &data, mouse_position);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_SELECTABLE_LABEL)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_selectable_label(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_selectable_label(world, entity, &interaction, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_DRAG_VALUE)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_drag_value(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_drag_value(world, entity, &interaction, &data, &text_edit_ctx);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_CONTEXT_MENU)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_context_menu(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_context_menu(world, entity, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_TREE_VIEW)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_tree_view(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_tree_view(
world,
entity,
&data,
&TreeViewContext {
ctrl_held,
frame_keys: &frame_keys,
focused_entity,
scroll_delta,
mouse_position,
dpi_scale,
},
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_MODAL_DIALOG)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_modal_dialog(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_modal_dialog(world, entity, &data, &frame_keys);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_DATA_GRID)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_data_grid(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_data_grid(
world,
entity,
&data,
&DataGridContext {
ctrl_held,
shift_held,
frame_keys: &frame_keys,
mouse_position,
mouse_just_pressed,
mouse_down,
},
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_PROPERTY_GRID)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_property_grid(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_property_grid(
world,
entity,
&data,
mouse_position,
mouse_just_pressed,
mouse_down,
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_COMMAND_PALETTE)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_command_palette(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_command_palette(world, entity, &data, &frame_keys);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_CANVAS)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_canvas(entity) {
Some(d) => d.clone(),
None => continue,
};
if interaction.clicked && data.hit_test_enabled {
let mouse_pos = world.resources.input.mouse.position;
if let Some(node) = world.ui.get_ui_layout_node(entity) {
let local_pos = mouse_pos - node.computed_rect.min;
for &(command_id, ref rect) in data.command_bounds.iter().rev() {
if rect.contains(local_pos) {
world.resources.retained_ui.events_for_active_mut().push(
crate::ecs::ui::resources::UiEvent::CanvasClicked {
entity,
command_id,
position: local_pos,
},
);
break;
}
}
}
}
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_TEXT_AREA)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_text_area(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_text_area(world, entity, &interaction, &data, &text_edit_ctx);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_TILE_CONTAINER)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_tile_container(entity) {
Some(d) => d.clone(),
None => continue,
};
let tile_mouse = crate::ecs::input::access::mouse_for_active(world);
let tile_mouse_position = tile_mouse.position;
let tile_mouse_state = tile_mouse.state;
let tile_mouse_just_pressed =
tile_mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
let tile_mouse_just_released =
tile_mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_RELEASED);
let tile_mouse_down =
tile_mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_CLICKED);
let hovered_entity = world
.resources
.retained_ui
.interaction_for_active()
.hovered_entity;
handle_tile_container(
world,
entity,
&data,
&TileContainerContext {
mouse_position: tile_mouse_position,
mouse_just_pressed: tile_mouse_just_pressed,
mouse_just_released: tile_mouse_just_released,
mouse_down: tile_mouse_down,
hovered_entity,
},
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_VIRTUAL_LIST)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_virtual_list(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_virtual_list(world, entity, &data, dpi_scale, &frame_keys, focused_entity);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_RANGE_SLIDER)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_range_slider(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_range_slider(
world,
entity,
&interaction,
&data,
mouse_position,
focused_entity,
&frame_keys,
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_BREADCRUMB)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_breadcrumb(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_breadcrumb(world, entity, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_SPLITTER)
.collect::<Vec<_>>()
{
let data = match world.ui.get_ui_splitter(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_splitter(world, entity, &data, mouse_position);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_RICH_TEXT_EDITOR)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_rich_text_editor(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_rich_text_editor(
world,
RichTextEditorContext {
entity,
interaction: &interaction,
data: &data,
focused_entity,
frame_chars: &frame_chars,
frame_keys: &frame_keys,
ctrl_held,
shift_held,
mouse_position,
current_time,
dpi_scale,
},
);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_MULTI_SELECT)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_multi_select(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_multi_select(world, entity, &interaction, &data);
}
for entity in world
.ui
.query_entities(crate::ecs::world::UI_DATE_PICKER)
.collect::<Vec<_>>()
{
let interaction = snapshot_interaction(world, entity);
let data = match world.ui.get_ui_date_picker(entity) {
Some(d) => d.clone(),
None => continue,
};
handle_date_picker(world, entity, &interaction, &data);
}
if scroll_delta.y.abs() > f32::EPSILON && !is_mouse_over_popup(world, mouse_position) {
close_open_popups(world);
}
let error_color = world
.resources
.retained_ui
.theme_state
.active_theme()
.error_color;
let error_entities: Vec<(freecs::Entity, bool)> = world
.ui
.query_entities(crate::ecs::world::UI_NODE_INTERACTION)
.map(|entity| {
let has_error = world
.ui
.get_ui_node_interaction(entity)
.is_some_and(|interaction| interaction.error_text.is_some());
(entity, has_error)
})
.filter(|(entity, _)| {
world.ui.get_ui_node_content(*entity).is_some_and(|c| {
matches!(c, crate::ecs::ui::components::UiNodeContent::Rect { .. })
})
})
.collect();
for (entity, has_error) in error_entities {
if has_error
&& let Some(crate::ecs::ui::components::UiNodeContent::Rect {
border_color,
border_width,
..
}) = world.ui.get_ui_node_content_mut(entity)
{
*border_color = error_color;
if *border_width < 1.0 {
*border_width = 1.0;
}
}
}
world.resources.retained_ui.input.frame_chars = frame_chars;
world.resources.retained_ui.input.frame_keys = frame_keys;
}
fn is_mouse_over_popup(world: &World, mouse_position: Vec2) -> bool {
let mut popup_containers: Vec<freecs::Entity> = Vec::new();
world
.ui
.query()
.with(crate::ecs::world::UI_DROPDOWN)
.iter(|_, table, idx| {
let data = &table.ui_dropdown[idx];
if data.open {
popup_containers.push(data.popup_container_entity);
}
});
world
.ui
.query()
.with(crate::ecs::world::UI_MENU)
.iter(|_, table, idx| {
let data = &table.ui_menu[idx];
if data.open {
popup_containers.push(data.popup_container_entity);
}
});
world
.ui
.query()
.with(crate::ecs::world::UI_CONTEXT_MENU)
.iter(|_, table, idx| {
let data = &table.ui_context_menu[idx];
if data.open {
popup_containers.push(data.popup_entity);
}
});
for popup in popup_containers {
if let Some(node) = world.ui.get_ui_layout_node(popup)
&& node.computed_rect.contains(mouse_position)
{
return true;
}
}
false
}
fn close_open_popups(world: &mut World) {
let mut open_dropdowns: Vec<(freecs::Entity, freecs::Entity)> = Vec::new();
world
.ui
.query()
.with(crate::ecs::world::UI_DROPDOWN)
.iter(|entity, table, idx| {
let data = &table.ui_dropdown[idx];
if data.open {
open_dropdowns.push((entity, data.popup_container_entity));
}
});
for (entity, popup_container) in open_dropdowns {
if world
.ui
.get_ui_layout_node(popup_container)
.is_some_and(|node| node.visible)
{
ui_mark_layout_dirty(world);
}
if let Some(node) = world.ui.get_ui_layout_node_mut(popup_container) {
node.visible = false;
}
if let Some(widget_data) = world.ui.get_ui_dropdown_mut(entity) {
widget_data.open = false;
}
}
let mut open_menus: Vec<(freecs::Entity, freecs::Entity)> = Vec::new();
world
.ui
.query()
.with(crate::ecs::world::UI_MENU)
.iter(|entity, table, idx| {
let data = &table.ui_menu[idx];
if data.open {
open_menus.push((entity, data.popup_container_entity));
}
});
for (entity, popup_container) in open_menus {
if world
.ui
.get_ui_layout_node(popup_container)
.is_some_and(|node| node.visible)
{
ui_mark_layout_dirty(world);
}
if let Some(node) = world.ui.get_ui_layout_node_mut(popup_container) {
node.visible = false;
}
if let Some(widget_data) = world.ui.get_ui_menu_mut(entity) {
widget_data.open = false;
}
}
let mut open_context_menus: Vec<(
freecs::Entity,
crate::ecs::ui::components::UiContextMenuData,
)> = Vec::new();
world
.ui
.query()
.with(crate::ecs::world::UI_CONTEXT_MENU)
.iter(|entity, table, idx| {
let data = &table.ui_context_menu[idx];
if data.open {
open_context_menus.push((entity, data.clone()));
}
});
for (entity, data) in open_context_menus {
if let Some(node) = world.ui.get_ui_layout_node_mut(data.popup_entity) {
node.visible = false;
}
close_submenu_popups(world, &data.item_defs);
if let Some(widget_data) = world.ui.get_ui_context_menu_mut(entity) {
widget_data.open = false;
close_submenu_defs_state(&mut widget_data.item_defs);
}
world.resources.retained_ui.overlays.active_context_menu = None;
}
}