use crate::ecs::ui::state::UiStateTrait as _;
use nalgebra_glm::Vec4;
use winit::keyboard::KeyCode;
use crate::ecs::ui::components::UiWidgetState;
use crate::ecs::world::World;
use super::snapshot_interaction;
pub(super) fn handle_tree_view(
world: &mut World,
entity: freecs::Entity,
data: &crate::ecs::ui::components::UiTreeViewData,
ctrl_held: bool,
_shift_held: bool,
frame_keys: &[(KeyCode, bool)],
focused_entity: Option<freecs::Entity>,
) {
if let Some(UiWidgetState::TreeView(widget_data)) = world.ui.get_ui_widget_state_mut(entity) {
widget_data.changed = false;
widget_data.context_menu_node = None;
}
let node_entities = data.node_entities.clone();
let multi_select = data.multi_select;
let tree_entity = entity;
for node_entity in &node_entities {
let node_data = world.ui.get_ui_widget_state(*node_entity).cloned();
let Some(UiWidgetState::TreeNode(tree_node)) = node_data else {
continue;
};
let row_interaction = snapshot_interaction(world, tree_node.row_entity);
let arrow_clicked = row_interaction.clicked
&& world
.ui
.get_ui_layout_node(tree_node.arrow_entity)
.map(|node| {
node.computed_rect
.contains(world.resources.input.mouse.position)
})
.unwrap_or(false);
if row_interaction.clicked && !arrow_clicked {
if !multi_select || !ctrl_held {
let previously_selected = data.selected_nodes.clone();
for prev in &previously_selected {
if *prev != *node_entity {
let prev_data = world.ui.get_ui_widget_state(*prev).cloned();
if let Some(UiWidgetState::TreeNode(prev_node)) = prev_data {
if let Some(color) =
world.ui.get_ui_node_color_mut(prev_node.row_entity)
{
color.colors[crate::ecs::ui::state::UiSelected::INDEX] = None;
}
if let Some(weights) =
world.ui.get_ui_state_weights_mut(prev_node.row_entity)
{
weights.weights[crate::ecs::ui::state::UiSelected::INDEX] = 0.0;
}
if let Some(UiWidgetState::TreeNode(wd)) =
world.ui.get_ui_widget_state_mut(*prev)
{
wd.selected = false;
}
}
}
}
}
let new_selected = if ctrl_held && multi_select {
!tree_node.selected
} else {
true
};
let accent = world
.resources
.retained_ui
.theme_state
.active_theme()
.accent_color;
let selected_bg = Vec4::new(accent.x, accent.y, accent.z, 0.3);
if let Some(color) = world.ui.get_ui_node_color_mut(tree_node.row_entity) {
color.colors[crate::ecs::ui::state::UiSelected::INDEX] = if new_selected {
Some(selected_bg)
} else {
None
};
}
if let Some(weights) = world.ui.get_ui_state_weights_mut(tree_node.row_entity) {
weights.weights[crate::ecs::ui::state::UiSelected::INDEX] =
if new_selected { 1.0 } else { 0.0 };
}
if let Some(UiWidgetState::TreeNode(wd)) =
world.ui.get_ui_widget_state_mut(*node_entity)
{
wd.selected = new_selected;
}
let mut new_selection: Vec<freecs::Entity> = Vec::new();
for ne in &node_entities {
if let Some(UiWidgetState::TreeNode(nd)) = world.ui.get_ui_widget_state(*ne)
&& nd.selected
{
new_selection.push(*ne);
}
}
if let Some(UiWidgetState::TreeView(wd)) = world.ui.get_ui_widget_state_mut(tree_entity)
{
wd.selected_nodes = new_selection;
wd.changed = true;
}
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeSelected {
tree: tree_entity,
node: *node_entity,
selected: new_selected,
},
);
}
if arrow_clicked || row_interaction.double_clicked {
let new_expanded = !tree_node.expanded;
if new_expanded && tree_node.lazy && !tree_node.lazy_loaded {
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeExpandRequested {
tree: tree_entity,
node: *node_entity,
user_data: tree_node.user_data,
},
);
} else {
world.resources.text_cache.set_text(
tree_node.arrow_text_slot,
if new_expanded { "\u{25BC}" } else { "\u{25B6}" },
);
if let Some(node) = world
.ui
.get_ui_layout_node_mut(tree_node.children_container)
{
node.visible = new_expanded;
}
if let Some(UiWidgetState::TreeNode(wd)) =
world.ui.get_ui_widget_state_mut(*node_entity)
{
wd.expanded = new_expanded;
}
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeToggled {
tree: tree_entity,
node: *node_entity,
expanded: new_expanded,
},
);
}
}
if row_interaction.right_clicked {
let mouse_position = world.resources.input.mouse.position;
if let Some(UiWidgetState::TreeView(wd)) = world.ui.get_ui_widget_state_mut(tree_entity)
{
wd.context_menu_node = Some(*node_entity);
}
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeContextMenu {
tree: tree_entity,
node: *node_entity,
position: mouse_position,
},
);
}
}
if focused_entity == Some(entity) || focused_entity == Some(data.content_entity) {
let visible_nodes: Vec<freecs::Entity> = node_entities
.iter()
.filter(|ne| {
let node_data = world.ui.get_ui_widget_state(**ne).cloned();
if let Some(UiWidgetState::TreeNode(tn)) = &node_data {
let mut current_parent = tn.parent_node;
while let Some(parent_entity) = current_parent {
if let Some(UiWidgetState::TreeNode(pn)) =
world.ui.get_ui_widget_state(parent_entity)
{
if !pn.expanded {
return false;
}
current_parent = pn.parent_node;
} else {
break;
}
}
true
} else {
false
}
})
.copied()
.collect();
let current_focused = data
.selected_nodes
.first()
.copied()
.or_else(|| visible_nodes.first().copied());
let current_index =
current_focused.and_then(|cf| visible_nodes.iter().position(|n| *n == cf));
for &(key, pressed) in frame_keys {
if !pressed {
continue;
}
let new_focus = match key {
KeyCode::ArrowDown => current_index
.map(|idx| (idx + 1).min(visible_nodes.len().saturating_sub(1)))
.or(Some(0)),
KeyCode::ArrowUp => current_index.map(|idx| idx.saturating_sub(1)).or(Some(0)),
KeyCode::ArrowRight => {
if let Some(focus) = current_focused
&& let Some(UiWidgetState::TreeNode(tn)) =
world.ui.get_ui_widget_state(focus).cloned()
&& !tn.expanded
{
world
.resources
.text_cache
.set_text(tn.arrow_text_slot, "\u{25BC}");
if let Some(node) = world.ui.get_ui_layout_node_mut(tn.children_container) {
node.visible = true;
}
if let Some(UiWidgetState::TreeNode(wd)) =
world.ui.get_ui_widget_state_mut(focus)
{
wd.expanded = true;
}
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeToggled {
tree: tree_entity,
node: focus,
expanded: true,
},
);
}
None
}
KeyCode::ArrowLeft => {
if let Some(focus) = current_focused
&& let Some(UiWidgetState::TreeNode(tn)) =
world.ui.get_ui_widget_state(focus).cloned()
{
if tn.expanded {
world
.resources
.text_cache
.set_text(tn.arrow_text_slot, "\u{25B6}");
if let Some(node) =
world.ui.get_ui_layout_node_mut(tn.children_container)
{
node.visible = false;
}
if let Some(UiWidgetState::TreeNode(wd)) =
world.ui.get_ui_widget_state_mut(focus)
{
wd.expanded = false;
}
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeToggled {
tree: tree_entity,
node: focus,
expanded: false,
},
);
None
} else if let Some(parent) = tn.parent_node {
visible_nodes.iter().position(|n| *n == parent)
} else {
None
}
} else {
None
}
}
KeyCode::Enter | KeyCode::Space => {
if let Some(focus) = current_focused
&& let Some(UiWidgetState::TreeNode(tn)) =
world.ui.get_ui_widget_state(focus).cloned()
{
let new_expanded = !tn.expanded;
world.resources.text_cache.set_text(
tn.arrow_text_slot,
if new_expanded { "\u{25BC}" } else { "\u{25B6}" },
);
if let Some(node) = world.ui.get_ui_layout_node_mut(tn.children_container) {
node.visible = new_expanded;
}
if let Some(UiWidgetState::TreeNode(wd)) =
world.ui.get_ui_widget_state_mut(focus)
{
wd.expanded = new_expanded;
}
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeToggled {
tree: tree_entity,
node: focus,
expanded: new_expanded,
},
);
}
None
}
_ => None,
};
if let Some(new_idx) = new_focus
&& new_idx < visible_nodes.len()
{
let new_node = visible_nodes[new_idx];
if let Some(old) = current_focused
&& let Some(UiWidgetState::TreeNode(old_tn)) =
world.ui.get_ui_widget_state(old).cloned()
{
if let Some(color) = world.ui.get_ui_node_color_mut(old_tn.row_entity) {
color.colors[crate::ecs::ui::state::UiSelected::INDEX] = None;
}
if let Some(weights) = world.ui.get_ui_state_weights_mut(old_tn.row_entity) {
weights.weights[crate::ecs::ui::state::UiSelected::INDEX] = 0.0;
}
if let Some(UiWidgetState::TreeNode(wd)) = world.ui.get_ui_widget_state_mut(old)
{
wd.selected = false;
}
}
let accent = world
.resources
.retained_ui
.theme_state
.active_theme()
.accent_color;
let selected_bg = Vec4::new(accent.x, accent.y, accent.z, 0.3);
if let Some(UiWidgetState::TreeNode(new_tn)) =
world.ui.get_ui_widget_state(new_node).cloned()
{
if let Some(color) = world.ui.get_ui_node_color_mut(new_tn.row_entity) {
color.colors[crate::ecs::ui::state::UiSelected::INDEX] = Some(selected_bg);
}
if let Some(weights) = world.ui.get_ui_state_weights_mut(new_tn.row_entity) {
weights.weights[crate::ecs::ui::state::UiSelected::INDEX] = 1.0;
}
}
if let Some(UiWidgetState::TreeNode(wd)) =
world.ui.get_ui_widget_state_mut(new_node)
{
wd.selected = true;
}
if let Some(UiWidgetState::TreeView(wd)) =
world.ui.get_ui_widget_state_mut(tree_entity)
{
wd.selected_nodes = vec![new_node];
wd.changed = true;
}
world.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::TreeNodeSelected {
tree: tree_entity,
node: new_node,
selected: true,
},
);
}
}
}
}
pub(super) fn handle_modal_dialog(
world: &mut World,
entity: freecs::Entity,
data: &crate::ecs::ui::components::UiModalDialogData,
frame_keys: &[(KeyCode, bool)],
) {
if !world
.ui
.get_ui_layout_node(entity)
.is_some_and(|n| n.visible)
{
if let Some(UiWidgetState::ModalDialog(widget_data)) =
world.ui.get_ui_widget_state_mut(entity)
{
widget_data.result = None;
}
return;
}
let mut result = None;
if let Some(ok) = data.ok_button
&& world
.ui
.get_ui_node_interaction(ok)
.is_some_and(|i| i.clicked)
{
result = Some(true);
}
if let Some(cancel) = data.cancel_button
&& world
.ui
.get_ui_node_interaction(cancel)
.is_some_and(|i| i.clicked)
{
result = Some(false);
}
for (key, is_pressed) in frame_keys {
if *is_pressed && *key == KeyCode::Escape {
result = Some(false);
}
}
if let Some(confirmed) = result {
if let Some(node) = world.ui.get_ui_layout_node_mut(data.backdrop_entity) {
node.visible = false;
}
if let Some(node) = world.ui.get_ui_layout_node_mut(entity) {
node.visible = false;
}
if let Some(UiWidgetState::ModalDialog(widget_data)) =
world.ui.get_ui_widget_state_mut(entity)
{
widget_data.result = Some(confirmed);
}
world.resources.retained_ui.active_modal = None;
world
.resources
.retained_ui
.frame_events
.push(crate::ecs::ui::resources::UiEvent::ModalClosed { entity, confirmed });
}
}