use nalgebra_glm::{Vec2, Vec4};
use winit::keyboard::KeyCode;

use crate::ecs::text::resources::FontAtlasData;
use crate::ecs::ui::components::{
    DropPreview, DropZone, SplitDirection, TileId, TileNode, UiPanelKind, UiTileContainerData,
    UiWidgetState,
};
use crate::ecs::ui::state::UiStateTrait as _;
use crate::ecs::world::World;

fn validate_widget_text(world: &mut World, entity: freecs::Entity, text: &str) {
    let rules: Vec<crate::ecs::ui::components::ValidationRule> = world
        .get_ui_node_interaction(entity)
        .map(|interaction| interaction.validation_rules.clone())
        .unwrap_or_default();

    if rules.is_empty() {
        return;
    }

    let mut error: Option<String> = None;
    for rule in &rules {
        if let Err(message) = rule.validate(text) {
            error = Some(message);
            break;
        }
    }

    if let Some(interaction) = world.get_ui_node_interaction_mut(entity) {
        interaction.error_text = error.clone();
        interaction.tooltip_text = error;
    }
}

pub fn ui_retained_input_sync_system(world: &mut World) {
    if !world.resources.retained_ui.enabled {
        return;
    }

    world.resources.retained_ui.frame_events.clear();
    world.resources.retained_ui.delta_time = world.resources.window.timing.delta_time;
    world.resources.retained_ui.current_time =
        world.resources.window.timing.uptime_milliseconds as f64 / 1000.0;

    let focused_secondary = world
        .resources
        .secondary_windows
        .focused_index
        .and_then(|idx| {
            world
                .resources
                .secondary_windows
                .states
                .iter()
                .find(|s| s.index == idx)
                .map(|s| {
                    let keystates = &s.input.keyboard_keystates;
                    let ctrl = matches!(
                        keystates.get(&KeyCode::ControlLeft),
                        Some(winit::event::ElementState::Pressed)
                    ) || matches!(
                        keystates.get(&KeyCode::ControlRight),
                        Some(winit::event::ElementState::Pressed)
                    );
                    let shift = matches!(
                        keystates.get(&KeyCode::ShiftLeft),
                        Some(winit::event::ElementState::Pressed)
                    ) || matches!(
                        keystates.get(&KeyCode::ShiftRight),
                        Some(winit::event::ElementState::Pressed)
                    );
                    (
                        s.input.mouse_wheel_delta,
                        ctrl,
                        shift,
                        s.input.frame_keys.clone(),
                        s.input.frame_chars.clone(),
                    )
                })
        });

    if let Some((scroll, ctrl, shift, frame_keys, frame_chars)) = focused_secondary {
        world.resources.retained_ui.frame_chars = frame_chars;
        world.resources.retained_ui.frame_keys = frame_keys;
        world.resources.retained_ui.scroll_delta = scroll;
        world.resources.retained_ui.ctrl_held = ctrl;
        world.resources.retained_ui.shift_held = shift;
    } else {
        world.resources.retained_ui.frame_chars =
            world.resources.input.keyboard.frame_chars.clone();
        world.resources.retained_ui.frame_keys = world.resources.input.keyboard.frame_keys.clone();
        world.resources.retained_ui.scroll_delta = world.resources.input.mouse.wheel_delta;

        let ctrl_held = world
            .resources
            .input
            .keyboard
            .is_key_pressed(KeyCode::ControlLeft)
            || world
                .resources
                .input
                .keyboard
                .is_key_pressed(KeyCode::ControlRight);
        let shift_held = world
            .resources
            .input
            .keyboard
            .is_key_pressed(KeyCode::ShiftLeft)
            || world
                .resources
                .input
                .keyboard
                .is_key_pressed(KeyCode::ShiftRight);

        world.resources.retained_ui.ctrl_held = ctrl_held;
        world.resources.retained_ui.shift_held = shift_held;
    }

    if world.resources.retained_ui.scroll_delta != Vec2::zeros()
        || !world.resources.retained_ui.frame_chars.is_empty()
    {
        world.resources.retained_ui.layout_dirty = true;
    }

    world.resources.retained_ui.cleanup_stale_animations();
}

pub fn measure_text_width(atlas: &FontAtlasData, text: &str, font_size: f32) -> f32 {
    let scale = font_size / atlas.font_size;
    let mut width = 0.0f32;
    let mut prev_char: Option<char> = None;
    for character in text.chars() {
        if let Some(glyph) = atlas.glyphs.get(&character) {
            if let Some(previous) = prev_char
                && let Some(&kern) = atlas.kerning.get(&(previous, character))
            {
                width += kern * scale;
            }
            width += glyph.advance * scale;
        }
        prev_char = Some(character);
    }
    width
}

pub fn byte_index_at_x(atlas: &FontAtlasData, text: &str, font_size: f32, target_x: f32) -> usize {
    let scale = font_size / atlas.font_size;
    let mut cursor_x = 0.0f32;
    let mut prev_char: Option<char> = None;
    for (index, character) in text.chars().enumerate() {
        if let Some(glyph) = atlas.glyphs.get(&character) {
            if let Some(previous) = prev_char
                && let Some(&kern) = atlas.kerning.get(&(previous, character))
            {
                cursor_x += kern * scale;
            }
            let advance = glyph.advance * scale;
            if target_x < cursor_x + advance * 0.5 {
                return index;
            }
            cursor_x += advance;
        }
        prev_char = Some(character);
    }
    text.chars().count()
}

pub fn prev_word_boundary(text: &str, position: usize) -> usize {
    let chars: Vec<char> = text.chars().collect();
    if position == 0 {
        return 0;
    }
    let mut index = position - 1;
    while index > 0 && chars[index].is_whitespace() {
        index -= 1;
    }
    while index > 0 && !chars[index - 1].is_whitespace() {
        index -= 1;
    }
    index
}

pub fn next_word_boundary(text: &str, position: usize) -> usize {
    let chars: Vec<char> = text.chars().collect();
    let len = chars.len();
    if position >= len {
        return len;
    }
    let mut index = position;
    while index < len && !chars[index].is_whitespace() {
        index += 1;
    }
    while index < len && chars[index].is_whitespace() {
        index += 1;
    }
    index
}

fn line_col_from_char_position(text: &str, position: usize) -> (usize, usize) {
    let mut line = 0;
    let mut col = 0;
    for (index, character) in text.chars().enumerate() {
        if index == position {
            return (line, col);
        }
        if character == '\n' {
            line += 1;
            col = 0;
        } else {
            col += 1;
        }
    }
    (line, col)
}

fn char_position_from_line_col(text: &str, target_line: usize, target_col: usize) -> usize {
    let mut line = 0;
    let mut col = 0;
    for (index, character) in text.chars().enumerate() {
        if line == target_line && col == target_col {
            return index;
        }
        if line > target_line {
            return index.saturating_sub(1);
        }
        if character == '\n' {
            if line == target_line {
                return index;
            }
            line += 1;
            col = 0;
        } else {
            col += 1;
        }
    }
    text.chars().count()
}

fn line_count(text: &str) -> usize {
    text.chars().filter(|c| *c == '\n').count() + 1
}

fn line_start_char_index(text: &str, target_line: usize) -> usize {
    let mut line = 0;
    for (index, character) in text.chars().enumerate() {
        if line == target_line {
            return index;
        }
        if character == '\n' {
            line += 1;
        }
    }
    text.chars().count()
}

fn line_text(text: &str, target_line: usize) -> &str {
    text.split('\n').nth(target_line).unwrap_or("")
}

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
        .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.delta_time;
    let current_time = world.resources.retained_ui.current_time;
    let mouse_position = world.resources.input.mouse.position;
    let frame_chars = std::mem::take(&mut world.resources.retained_ui.frame_chars);
    let frame_keys = std::mem::take(&mut world.resources.retained_ui.frame_keys);
    let ctrl_held = world.resources.retained_ui.ctrl_held;
    let shift_held = world.resources.retained_ui.shift_held;
    let scroll_delta = world.resources.retained_ui.scroll_delta;
    let focused_entity = world.resources.retained_ui.focused_entity;
    let mouse_state = world.resources.input.mouse.state;
    let mouse_just_pressed =
        mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
    let mouse_just_released =
        mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_RELEASED);
    let mouse_down = mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_CLICKED);
    let dpi_scale = world.resources.window.cached_scale_factor;
    let viewport_size = world
        .resources
        .window
        .cached_viewport_size
        .map(|(width, height)| Vec2::new(width as f32, height as f32))
        .unwrap_or(Vec2::new(800.0, 600.0));

    let entities: Vec<freecs::Entity> = world
        .query_entities(crate::ecs::world::UI_WIDGET_STATE)
        .collect();

    let has_text_focus = focused_entity.is_some_and(|fe| {
        matches!(
            world.get_ui_widget_state(fe),
            Some(UiWidgetState::TextInput(_))
                | Some(UiWidgetState::TextArea(_))
                | Some(UiWidgetState::DragValue(_))
        )
    });
    if !has_text_focus {
        let bindings = world.resources.retained_ui.shortcut_bindings.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.frame_events.push(
                        crate::ecs::ui::resources::UiEvent::ShortcutTriggered {
                            command_index: *command_index,
                        },
                    );
                }
            }
        }
    }

    let mut panel_rects: Vec<(freecs::Entity, crate::ecs::ui::types::Rect, f32)> = Vec::new();
    for entity in &entities {
        if let Some(UiWidgetState::Panel(_)) = world.get_ui_widget_state(*entity)
            && let Some(node) = world.get_ui_layout_node(*entity)
            && node.visible
        {
            panel_rects.push((*entity, node.computed_rect, node.computed_depth));
        }
    }

    for entity in entities {
        let interaction = snapshot_interaction(world, entity);

        let widget_clone = match world.get_ui_widget_state(entity) {
            Some(widget) => widget.clone(),
            None => continue,
        };

        match widget_clone {
            UiWidgetState::Button(_) => {
                if let Some(UiWidgetState::Button(data)) = world.get_ui_widget_state_mut(entity) {
                    data.clicked = interaction.clicked;
                }
                if interaction.clicked {
                    world
                        .resources
                        .retained_ui
                        .frame_events
                        .push(crate::ecs::ui::resources::UiEvent::ButtonClicked(entity));
                }
            }
            UiWidgetState::Slider(data) => {
                handle_slider(
                    world,
                    entity,
                    &interaction,
                    &data,
                    mouse_position,
                    focused_entity,
                    &frame_keys,
                );
            }
            UiWidgetState::Toggle(data) => {
                handle_toggle(world, entity, &interaction, &data, delta_time);
            }
            UiWidgetState::Checkbox(data) => {
                handle_checkbox(world, entity, &interaction, &data);
            }
            UiWidgetState::Radio(data) => {
                handle_radio(world, entity, &interaction, &data);
            }
            UiWidgetState::ProgressBar(_) => {}
            UiWidgetState::CollapsingHeader(data) => {
                handle_collapsing_header(world, entity, &interaction, &data);
            }
            UiWidgetState::ScrollArea(data) => {
                handle_scroll_area(
                    world,
                    entity,
                    &interaction,
                    &data,
                    scroll_delta,
                    mouse_position,
                    dpi_scale,
                );
            }
            UiWidgetState::TabBar(data) => {
                handle_tab_bar(world, entity, &data, &frame_keys, focused_entity);
            }
            UiWidgetState::TextInput(data) => {
                handle_text_input(
                    world,
                    entity,
                    &interaction,
                    &data,
                    focused_entity,
                    &frame_chars,
                    &frame_keys,
                    ctrl_held,
                    shift_held,
                    mouse_position,
                    current_time,
                    dpi_scale,
                );
            }
            UiWidgetState::Dropdown(data) => {
                handle_dropdown(
                    world,
                    entity,
                    &interaction,
                    &data,
                    &frame_keys,
                    focused_entity,
                );
            }
            UiWidgetState::Menu(data) => {
                handle_menu(world, entity, &interaction, &data);
            }
            UiWidgetState::Panel(data) => {
                let panel_depth = world
                    .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(mouse_position)
                        });
                handle_panel(
                    world,
                    entity,
                    &data,
                    &PanelContext {
                        mouse_position,
                        mouse_just_pressed,
                        mouse_just_released,
                        mouse_down,
                        dpi_scale,
                        viewport_size,
                        mouse_occluded,
                    },
                );
            }
            UiWidgetState::ColorPicker(data) => {
                handle_color_picker(world, entity, &data);
            }
            UiWidgetState::SelectableLabel(data) => {
                handle_selectable_label(world, entity, &interaction, &data);
            }
            UiWidgetState::DragValue(data) => {
                handle_drag_value(
                    world,
                    entity,
                    &interaction,
                    &data,
                    focused_entity,
                    &frame_chars,
                    &frame_keys,
                    ctrl_held,
                    shift_held,
                    mouse_position,
                    current_time,
                    dpi_scale,
                );
            }
            UiWidgetState::ContextMenu(data) => {
                handle_context_menu(world, entity, &data);
            }
            UiWidgetState::TreeView(data) => {
                handle_tree_view(
                    world,
                    entity,
                    &data,
                    ctrl_held,
                    shift_held,
                    &frame_keys,
                    focused_entity,
                );
            }
            UiWidgetState::TreeNode(_) => {}
            UiWidgetState::ModalDialog(data) => {
                handle_modal_dialog(world, entity, &data, &frame_keys);
            }
            UiWidgetState::RichText(_) => {}
            UiWidgetState::DataGrid(data) => {
                handle_data_grid(
                    world,
                    entity,
                    &data,
                    &DataGridContext {
                        ctrl_held,
                        shift_held,
                        frame_keys: &frame_keys,
                        mouse_position,
                        mouse_just_pressed,
                        mouse_down,
                    },
                );
            }
            UiWidgetState::PropertyGrid(data) => {
                handle_property_grid(
                    world,
                    entity,
                    &data,
                    mouse_position,
                    mouse_just_pressed,
                    mouse_down,
                );
            }
            UiWidgetState::CommandPalette(data) => {
                handle_command_palette(world, entity, &data, &frame_keys);
            }
            UiWidgetState::Canvas(data) => {
                if interaction.clicked && data.hit_test_enabled {
                    let mouse_pos = world.resources.input.mouse.position;
                    if let Some(node) = world.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.frame_events.push(
                                    crate::ecs::ui::resources::UiEvent::CanvasClicked {
                                        entity,
                                        command_id,
                                        position: local_pos,
                                    },
                                );
                                break;
                            }
                        }
                    }
                }
            }
            UiWidgetState::TextArea(data) => {
                handle_text_area(
                    world,
                    entity,
                    &interaction,
                    &data,
                    focused_entity,
                    &frame_chars,
                    &frame_keys,
                    ctrl_held,
                    shift_held,
                    mouse_position,
                    current_time,
                    dpi_scale,
                );
            }
            UiWidgetState::TileContainer(data) => {
                handle_tile_container(
                    world,
                    entity,
                    &data,
                    &TileContainerContext {
                        mouse_position,
                        mouse_just_pressed,
                        mouse_just_released,
                        mouse_down,
                    },
                );
            }
            UiWidgetState::VirtualList(data) => {
                handle_virtual_list(world, entity, &data, dpi_scale, &frame_keys, focused_entity);
            }
            UiWidgetState::RangeSlider(data) => {
                handle_range_slider(
                    world,
                    entity,
                    &interaction,
                    &data,
                    mouse_position,
                    focused_entity,
                    &frame_keys,
                );
            }
            UiWidgetState::Breadcrumb(data) => {
                handle_breadcrumb(world, entity, &data);
            }
            UiWidgetState::Splitter(data) => {
                handle_splitter(world, entity, &data, mouse_position);
            }
            UiWidgetState::RichTextEditor(data) => {
                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,
                    },
                );
            }
            UiWidgetState::MultiSelect(data) => {
                handle_multi_select(world, entity, &interaction, &data);
            }
            UiWidgetState::DatePicker(data) => {
                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
        .query_entities(crate::ecs::world::UI_NODE_INTERACTION)
        .map(|entity| {
            let has_error = world
                .get_ui_node_interaction(entity)
                .is_some_and(|interaction| interaction.error_text.is_some());
            (entity, has_error)
        })
        .filter(|(entity, _)| {
            world.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.get_ui_node_content_mut(entity)
        {
            *border_color = error_color;
            if *border_width < 1.0 {
                *border_width = 1.0;
            }
        }
    }

    world.resources.retained_ui.frame_chars = frame_chars;
    world.resources.retained_ui.frame_keys = frame_keys;
}

fn is_mouse_over_popup(world: &World, mouse_position: Vec2) -> bool {
    for entity in world.query_entities(crate::ecs::world::UI_WIDGET_STATE) {
        let popup_container = match world.get_ui_widget_state(entity) {
            Some(UiWidgetState::Dropdown(data)) if data.open => Some(data.popup_container_entity),
            Some(UiWidgetState::Menu(data)) if data.open => Some(data.popup_container_entity),
            Some(UiWidgetState::ContextMenu(data)) if data.open => Some(data.popup_entity),
            _ => None,
        };
        if let Some(popup) = popup_container
            && let Some(node) = world.get_ui_layout_node(popup)
            && node.computed_rect.contains(mouse_position)
        {
            return true;
        }
    }
    false
}

fn close_open_popups(world: &mut World) {
    let entities: Vec<freecs::Entity> = world
        .query_entities(crate::ecs::world::UI_WIDGET_STATE)
        .collect();

    for entity in entities {
        let widget = match world.get_ui_widget_state(entity) {
            Some(widget) => widget.clone(),
            None => continue,
        };

        match widget {
            UiWidgetState::Dropdown(data) if data.open => {
                if let Some(node) = world.get_ui_layout_node_mut(data.popup_container_entity) {
                    node.visible = false;
                }

                if let Some(UiWidgetState::Dropdown(widget_data)) =
                    world.get_ui_widget_state_mut(entity)
                {
                    widget_data.open = false;
                }
            }
            UiWidgetState::Menu(data) if data.open => {
                if let Some(node) = world.get_ui_layout_node_mut(data.popup_container_entity) {
                    node.visible = false;
                }

                if let Some(UiWidgetState::Menu(widget_data)) =
                    world.get_ui_widget_state_mut(entity)
                {
                    widget_data.open = false;
                }
            }
            UiWidgetState::ContextMenu(data) if data.open => {
                if let Some(node) = world.get_ui_layout_node_mut(data.popup_entity) {
                    node.visible = false;
                }
                close_submenu_popups(world, &data.item_defs);

                if let Some(UiWidgetState::ContextMenu(widget_data)) =
                    world.get_ui_widget_state_mut(entity)
                {
                    widget_data.open = false;
                    close_submenu_defs_state(&mut widget_data.item_defs);
                }
                world.resources.retained_ui.active_context_menu = None;
            }
            _ => {}
        }
    }
}

fn handle_slider(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiSliderData,
    mouse_position: Vec2,
    focused_entity: Option<freecs::Entity>,
    frame_keys: &[(KeyCode, bool)],
) {
    let mut new_value = data.value;
    let mut changed = false;

    if focused_entity == Some(entity) {
        let range = data.max - data.min;
        let step = if data.logarithmic {
            range * 0.01
        } else {
            let natural_step = 10.0_f32.powf((range.log10() - 1.0).floor());
            natural_step.max(0.01)
        };
        for (key, is_pressed) in frame_keys {
            if !is_pressed {
                continue;
            }
            match key {
                KeyCode::ArrowRight | KeyCode::ArrowUp => {
                    new_value = (new_value + step).min(data.max);
                    changed = true;
                }
                KeyCode::ArrowLeft | KeyCode::ArrowDown => {
                    new_value = (new_value - step).max(data.min);
                    changed = true;
                }
                KeyCode::Home => {
                    new_value = data.min;
                    changed = true;
                }
                KeyCode::End => {
                    new_value = data.max;
                    changed = true;
                }
                _ => {}
            }
        }
    }

    if interaction.pressed || interaction.dragging {
        let track_rect = world
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = track_rect {
            let normalized = ((mouse_position.x - rect.min.x) / rect.width()).clamp(0.0, 1.0);
            new_value = if data.logarithmic && data.min > 0.0 && data.max > data.min {
                data.min * (data.max / data.min).powf(normalized)
            } else {
                data.min + normalized * (data.max - data.min)
            };
            if (new_value - data.value).abs() > f32::EPSILON {
                changed = true;
            }

            let fill_percent = normalized * 100.0;
            if let Some(fill_node) = world.get_ui_layout_node_mut(data.fill_entity)
                && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
                    fill_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
            {
                boundary.position_2 =
                    crate::ecs::ui::units::Rl(Vec2::new(fill_percent, 100.0)).into();
            }

            world.resources.text_cache.set_text(
                data.text_slot,
                format!(
                    "{}{:.prec$}{}",
                    data.prefix,
                    new_value,
                    data.suffix,
                    prec = data.precision
                ),
            );
        }
    }

    if changed && !(interaction.pressed || interaction.dragging) {
        let range = data.max - data.min;
        let normalized = if data.logarithmic && data.min > 0.0 && range > 0.0 {
            ((new_value / data.min).ln() / (data.max / data.min).ln()).clamp(0.0, 1.0)
        } else if range.abs() > f32::EPSILON {
            ((new_value - data.min) / range).clamp(0.0, 1.0)
        } else {
            0.0
        };
        let fill_percent = normalized * 100.0;
        if let Some(fill_node) = world.get_ui_layout_node_mut(data.fill_entity)
            && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
                fill_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
        {
            boundary.position_2 = crate::ecs::ui::units::Rl(Vec2::new(fill_percent, 100.0)).into();
        }
        world.resources.text_cache.set_text(
            data.text_slot,
            format!(
                "{}{:.prec$}{}",
                data.prefix,
                new_value,
                data.suffix,
                prec = data.precision
            ),
        );
    }

    if let Some(UiWidgetState::Slider(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.value = new_value;
        widget_data.changed = changed;
    }
    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::SliderChanged {
                entity,
                value: new_value,
            },
        );
    }
}

fn handle_splitter(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiSplitterData,
    mouse_position: Vec2,
) {
    use crate::ecs::ui::components::SplitDirection;

    let divider_dragging = world
        .get_ui_node_interaction(data.divider_entity)
        .map(|interaction| interaction.dragging)
        .unwrap_or(false);

    if !divider_dragging {
        if let Some(crate::ecs::ui::components::UiWidgetState::Splitter(splitter_data)) =
            world.get_ui_widget_state_mut(entity)
        {
            splitter_data.changed = false;
        }
        return;
    }

    let container_rect = world
        .get_ui_layout_node(entity)
        .map(|node| node.computed_rect);
    let Some(container_rect) = container_rect else {
        return;
    };

    let new_ratio = match data.direction {
        SplitDirection::Horizontal => {
            let width = container_rect.width();
            if width > 0.0 {
                ((mouse_position.x - container_rect.min.x) / width)
                    .clamp(data.min_ratio, data.max_ratio)
            } else {
                data.ratio
            }
        }
        SplitDirection::Vertical => {
            let height = container_rect.height();
            if height > 0.0 {
                ((mouse_position.y - container_rect.min.y) / height)
                    .clamp(data.min_ratio, data.max_ratio)
            } else {
                data.ratio
            }
        }
    };

    if (new_ratio - data.ratio).abs() < 0.001 {
        return;
    }

    let divider_thickness = 4.0;
    let (p1_1, p1_2, d1, d2, p2_1, p2_2) = match data.direction {
        SplitDirection::Horizontal => {
            let half = divider_thickness * 0.5;
            (
                crate::ecs::ui::units::Rl(Vec2::new(0.0, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(-half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 0.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(-half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(new_ratio * 100.0, 0.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(half, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, 100.0)),
            )
        }
        SplitDirection::Vertical => {
            let half = divider_thickness * 0.5;
            (
                crate::ecs::ui::units::Rl(Vec2::new(0.0, 0.0)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, -half)),
                crate::ecs::ui::units::Rl(Vec2::new(0.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, -half)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, half)),
                crate::ecs::ui::units::Rl(Vec2::new(0.0, new_ratio * 100.0))
                    + crate::ecs::ui::units::Ab(Vec2::new(0.0, half)),
                crate::ecs::ui::units::Rl(Vec2::new(100.0, 100.0)),
            )
        }
    };

    let first_pane = data.first_pane;
    let second_pane = data.second_pane;
    let divider_entity = data.divider_entity;

    if let Some(node) = world.get_ui_layout_node_mut(first_pane)
        && let Some(Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary))) =
            node.layouts.get_mut(0)
    {
        boundary.position_1 = p1_1.into();
        boundary.position_2 = p1_2;
    }
    if let Some(node) = world.get_ui_layout_node_mut(divider_entity)
        && let Some(Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary))) =
            node.layouts.get_mut(0)
    {
        boundary.position_1 = d1;
        boundary.position_2 = d2;
    }
    if let Some(node) = world.get_ui_layout_node_mut(second_pane)
        && let Some(Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary))) =
            node.layouts.get_mut(0)
    {
        boundary.position_1 = p2_1;
        boundary.position_2 = p2_2.into();
    }

    if let Some(crate::ecs::ui::components::UiWidgetState::Splitter(splitter_data)) =
        world.get_ui_widget_state_mut(entity)
    {
        splitter_data.ratio = new_ratio;
        splitter_data.changed = true;
    }

    world.resources.retained_ui.frame_events.push(
        crate::ecs::ui::resources::UiEvent::SplitterChanged {
            entity,
            ratio: new_ratio,
        },
    );
}

fn handle_breadcrumb(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiBreadcrumbData,
) {
    let mut clicked_segment = None;
    for (index, segment_entity) in data.segment_entities.iter().enumerate() {
        let clicked = world
            .get_ui_node_interaction(*segment_entity)
            .map(|interaction| interaction.clicked)
            .unwrap_or(false);
        if clicked {
            clicked_segment = Some(index);
            break;
        }
    }

    if let Some(index) = clicked_segment {
        if let Some(UiWidgetState::Breadcrumb(breadcrumb_data)) =
            world.get_ui_widget_state_mut(entity)
        {
            breadcrumb_data.clicked_segment = Some(index);
            breadcrumb_data.changed = true;
        }
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::BreadcrumbClicked {
                entity,
                segment_index: index,
            },
        );
    } else if let Some(UiWidgetState::Breadcrumb(breadcrumb_data)) =
        world.get_ui_widget_state_mut(entity)
    {
        breadcrumb_data.clicked_segment = None;
        breadcrumb_data.changed = false;
    }
}

fn handle_range_slider(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiRangeSliderData,
    mouse_position: Vec2,
    focused_entity: Option<freecs::Entity>,
    frame_keys: &[(KeyCode, bool)],
) {
    let mut low_value = data.low_value;
    let mut high_value = data.high_value;
    let mut changed = false;
    let mut active_thumb = data.active_thumb;

    let range = data.max - data.min;
    if range.abs() < f32::EPSILON {
        if let Some(UiWidgetState::RangeSlider(widget_data)) = world.get_ui_widget_state_mut(entity)
        {
            widget_data.changed = false;
        }
        return;
    }

    if interaction.pressed {
        let track_rect = world
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = track_rect {
            let normalized = ((mouse_position.x - rect.min.x) / rect.width()).clamp(0.0, 1.0);
            let mouse_value = data.min + normalized * range;

            let dist_to_low = (mouse_value - low_value).abs();
            let dist_to_high = (mouse_value - high_value).abs();
            active_thumb = Some(if dist_to_low <= dist_to_high {
                crate::ecs::ui::components::RangeSliderThumb::Low
            } else {
                crate::ecs::ui::components::RangeSliderThumb::High
            });
        }
    }

    if (interaction.pressed || interaction.dragging)
        && let Some(thumb) = active_thumb
    {
        let track_rect = world
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = track_rect {
            let normalized = ((mouse_position.x - rect.min.x) / rect.width()).clamp(0.0, 1.0);
            let new_value = data.min + normalized * range;

            match thumb {
                crate::ecs::ui::components::RangeSliderThumb::Low => {
                    low_value = new_value.min(high_value);
                }
                crate::ecs::ui::components::RangeSliderThumb::High => {
                    high_value = new_value.max(low_value);
                }
            }

            if (low_value - data.low_value).abs() > f32::EPSILON
                || (high_value - data.high_value).abs() > f32::EPSILON
            {
                changed = true;
            }

            let low_n = ((low_value - data.min) / range).clamp(0.0, 1.0);
            let high_n = ((high_value - data.min) / range).clamp(0.0, 1.0);

            crate::ecs::ui::widgets::update_range_slider_visuals(
                world,
                &crate::ecs::ui::widgets::RangeSliderVisualUpdate {
                    fill_entity: data.fill_entity,
                    low_thumb: data.low_thumb_entity,
                    high_thumb: data.high_thumb_entity,
                    text_slot: data.text_slot,
                    precision: data.precision,
                    low_normalized: low_n,
                    high_normalized: high_n,
                    low_value,
                    high_value,
                    thumb_half: data.thumb_half_size,
                },
            );
        }
    }

    if !interaction.pressed && !interaction.dragging {
        active_thumb = None;
    }

    if focused_entity == Some(entity) {
        let step = range * 0.01;
        for &(key, pressed) in frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::Tab => {
                    active_thumb = match active_thumb {
                        Some(crate::ecs::ui::components::RangeSliderThumb::Low) => {
                            Some(crate::ecs::ui::components::RangeSliderThumb::High)
                        }
                        _ => Some(crate::ecs::ui::components::RangeSliderThumb::Low),
                    };
                }
                KeyCode::ArrowLeft | KeyCode::ArrowDown => {
                    let thumb =
                        active_thumb.unwrap_or(crate::ecs::ui::components::RangeSliderThumb::Low);
                    match thumb {
                        crate::ecs::ui::components::RangeSliderThumb::Low => {
                            low_value = (low_value - step).max(data.min).min(high_value);
                        }
                        crate::ecs::ui::components::RangeSliderThumb::High => {
                            high_value = (high_value - step).max(low_value).max(data.min);
                        }
                    }
                    if active_thumb.is_none() {
                        active_thumb = Some(thumb);
                    }
                    changed = true;
                }
                KeyCode::ArrowRight | KeyCode::ArrowUp => {
                    let thumb =
                        active_thumb.unwrap_or(crate::ecs::ui::components::RangeSliderThumb::High);
                    match thumb {
                        crate::ecs::ui::components::RangeSliderThumb::Low => {
                            low_value = (low_value + step).min(high_value).min(data.max);
                        }
                        crate::ecs::ui::components::RangeSliderThumb::High => {
                            high_value = (high_value + step).min(data.max);
                        }
                    }
                    if active_thumb.is_none() {
                        active_thumb = Some(thumb);
                    }
                    changed = true;
                }
                _ => {}
            }
        }

        if changed && !interaction.pressed && !interaction.dragging {
            let low_n = ((low_value - data.min) / range).clamp(0.0, 1.0);
            let high_n = ((high_value - data.min) / range).clamp(0.0, 1.0);
            crate::ecs::ui::widgets::update_range_slider_visuals(
                world,
                &crate::ecs::ui::widgets::RangeSliderVisualUpdate {
                    fill_entity: data.fill_entity,
                    low_thumb: data.low_thumb_entity,
                    high_thumb: data.high_thumb_entity,
                    text_slot: data.text_slot,
                    precision: data.precision,
                    low_normalized: low_n,
                    high_normalized: high_n,
                    low_value,
                    high_value,
                    thumb_half: data.thumb_half_size,
                },
            );
        }
    }

    if let Some(UiWidgetState::RangeSlider(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.low_value = low_value;
        widget_data.high_value = high_value;
        widget_data.changed = changed;
        widget_data.active_thumb = active_thumb;
    }

    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::RangeSliderChanged {
                entity,
                low: low_value,
                high: high_value,
            },
        );
    }
}

fn handle_toggle(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiToggleData,
    delta_time: f32,
) {
    let mut value = data.value;
    let mut changed = false;
    let mut animated_position = data.animated_position;

    if interaction.clicked {
        value = !value;
        changed = true;
    }

    let target = if value { 1.0 } else { 0.0 };
    let speed = 8.0;
    let diff = target - animated_position;
    if diff.abs() > 0.001 {
        animated_position += diff.signum() * speed * delta_time;
        animated_position = animated_position.clamp(0.0, 1.0);
    } else {
        animated_position = target;
    }

    let theme = world.resources.retained_ui.theme_state.active_theme();
    let toggle_height = theme.toggle_height;
    let toggle_width = theme.toggle_width;
    let on_color = theme.accent_color;
    let off_color = theme.background_color;
    let knob_padding = 2.0;
    let knob_size = toggle_height - knob_padding * 2.0;
    let knob_travel = toggle_width - knob_size - knob_padding * 2.0;
    let knob_x = knob_padding + animated_position * knob_travel;
    if let Some(knob_node) = world.get_ui_layout_node_mut(data.knob_entity)
        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
            knob_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
    {
        window.position = crate::ecs::ui::units::Ab(Vec2::new(knob_x, knob_padding)).into();
    }
    let blended = off_color + (on_color - off_color) * animated_position;
    if let Some(color) = world.get_ui_node_color_mut(entity) {
        color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(blended);
    }

    if let Some(UiWidgetState::Toggle(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.value = value;
        widget_data.changed = changed;
        widget_data.animated_position = animated_position;
    }
    if changed {
        world
            .resources
            .retained_ui
            .frame_events
            .push(crate::ecs::ui::resources::UiEvent::ToggleChanged { entity, value });
    }
}

fn handle_checkbox(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiCheckboxData,
) {
    if interaction.clicked {
        let new_value = !data.value;
        if let Some(node) = world.get_ui_layout_node_mut(data.inner_entity) {
            node.visible = new_value;
        }
        if let Some(UiWidgetState::Checkbox(widget_data)) = world.get_ui_widget_state_mut(entity) {
            widget_data.value = new_value;
            widget_data.changed = true;
        }
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::CheckboxChanged {
                entity,
                value: new_value,
            },
        );
    } else if let Some(UiWidgetState::Checkbox(widget_data)) = world.get_ui_widget_state_mut(entity)
    {
        widget_data.changed = false;
    }
}

fn handle_radio(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiRadioData,
) {
    if let Some(UiWidgetState::Radio(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.changed = false;
    }

    if interaction.clicked && !data.selected {
        if let Some(node) = world.get_ui_layout_node_mut(data.inner_entity) {
            node.visible = true;
        }

        let siblings = world
            .resources
            .retained_ui
            .radio_groups
            .get(&data.group_id)
            .cloned()
            .unwrap_or_default();

        for other_entity in siblings {
            if other_entity == entity {
                continue;
            }
            let other_data_clone = world.get_ui_widget_state(other_entity).cloned();
            if let Some(UiWidgetState::Radio(other_radio)) = other_data_clone
                && other_radio.selected
            {
                if let Some(node) = world.get_ui_layout_node_mut(other_radio.inner_entity) {
                    node.visible = false;
                }
                if let Some(UiWidgetState::Radio(wd)) = world.get_ui_widget_state_mut(other_entity)
                {
                    wd.selected = false;
                }
            }
        }

        if let Some(UiWidgetState::Radio(widget_data)) = world.get_ui_widget_state_mut(entity) {
            widget_data.selected = true;
            widget_data.changed = true;
        }
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::RadioChanged {
                entity,
                group_id: data.group_id,
                option_index: data.option_index,
            },
        );
    }
}

fn handle_collapsing_header(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiCollapsingHeaderData,
) {
    if let Some(UiWidgetState::CollapsingHeader(widget_data)) =
        world.get_ui_widget_state_mut(entity)
    {
        widget_data.changed = false;
    }
    if interaction.clicked {
        let new_open = !data.open;
        if let Some(node) = world.get_ui_layout_node_mut(data.content_entity) {
            node.visible = new_open;
        }
        world.resources.text_cache.set_text(
            data.arrow_text_slot,
            if new_open { "\u{25BC}" } else { "\u{25B6}" },
        );
        if let Some(UiWidgetState::CollapsingHeader(widget_data)) =
            world.get_ui_widget_state_mut(entity)
        {
            widget_data.open = new_open;
            widget_data.changed = true;
        }
    }
}

fn handle_scroll_area(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiScrollAreaData,
    scroll_delta: Vec2,
    mouse_position: Vec2,
    dpi_scale: f32,
) {
    let visible_height = world
        .get_ui_layout_node(entity)
        .map(|node| node.computed_rect.height())
        .unwrap_or(0.0);

    let children: Vec<freecs::Entity> = world
        .resources
        .children_cache
        .get(&data.content_entity)
        .map(|v| v.to_vec())
        .unwrap_or_default();

    let content_flow = world
        .get_ui_layout_node(data.content_entity)
        .and_then(|node| node.flow_layout);

    let mut total_content_height = 0.0f32;
    let mut visible_count = 0usize;
    for child in &children {
        if let Some(node) = world.get_ui_layout_node(*child)
            && node.visible
        {
            total_content_height += node.computed_rect.height();
            visible_count += 1;
        }
    }

    if let Some(flow) = content_flow {
        if visible_count > 1 {
            total_content_height += flow.spacing * dpi_scale * (visible_count - 1) as f32;
        }
        total_content_height += flow.padding * dpi_scale * 2.0;
    }

    let max_scroll = (total_content_height - visible_height).max(0.0) / dpi_scale;
    let mut scroll_offset = data.scroll_offset;
    let mut thumb_dragging = data.thumb_dragging;
    let mut thumb_drag_start_offset = data.thumb_drag_start_offset;

    if interaction.hovered && scroll_delta.y.abs() > 0.0 {
        scroll_offset -= scroll_delta.y * 40.0;
        scroll_offset = scroll_offset.clamp(0.0, max_scroll);
    }

    let thumb_interaction = snapshot_interaction(world, data.thumb_entity);

    if thumb_interaction.pressed && !thumb_dragging {
        thumb_dragging = true;
        thumb_drag_start_offset = scroll_offset;
    }

    if thumb_dragging
        && thumb_interaction.pressed
        && let Some(drag_start) = thumb_interaction.drag_start
    {
        let track_rect = world
            .get_ui_layout_node(data.track_entity)
            .map(|n| n.computed_rect);
        if let Some(track) = track_rect {
            let track_height = track.height();
            let thumb_ratio = if total_content_height > 0.0 {
                visible_height / total_content_height
            } else {
                1.0
            };
            let scrollable_track = track_height * (1.0 - thumb_ratio);
            if scrollable_track > 0.0 {
                let mouse_delta = mouse_position.y - drag_start.y;
                let scroll_per_pixel = max_scroll / scrollable_track;
                scroll_offset = (thumb_drag_start_offset + mouse_delta * scroll_per_pixel)
                    .clamp(0.0, max_scroll);
            }
        }
    }

    if !thumb_interaction.pressed {
        thumb_dragging = false;
    }

    if let Some(snap) = data.snap_interval
        && snap > 0.0
        && !thumb_dragging
        && scroll_delta.y.abs() < f32::EPSILON
    {
        let target = (scroll_offset / snap).round() * snap;
        let target = target.clamp(0.0, max_scroll);
        let delta_time = world.resources.retained_ui.delta_time;
        let diff = target - scroll_offset;
        if diff.abs() > 0.1 {
            scroll_offset += diff * (10.0 * delta_time).min(1.0);
        } else {
            scroll_offset = target;
        }
    }

    if let Some(content_node) = world.get_ui_layout_node_mut(data.content_entity)
        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Boundary(boundary)) =
            content_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
    {
        boundary.position_1 = crate::ecs::ui::units::Ab(Vec2::new(0.0, -scroll_offset)).into();
    }

    let show_scrollbar = total_content_height > visible_height;
    if let Some(track_node) = world.get_ui_layout_node_mut(data.track_entity) {
        track_node.visible = show_scrollbar;
    }

    if show_scrollbar {
        let thumb_ratio = (visible_height / total_content_height).clamp(0.05, 1.0);
        let track_rect = world
            .get_ui_layout_node(data.track_entity)
            .map(|n| n.computed_rect);
        if let Some(track) = track_rect {
            let track_height = track.height();
            let thumb_height_physical = (track_height * thumb_ratio).max(20.0 * dpi_scale);
            let scrollable_track = track_height - thumb_height_physical;
            let scroll_ratio = if max_scroll > 0.0 {
                scroll_offset / max_scroll
            } else {
                0.0
            };
            let thumb_y = scroll_ratio * scrollable_track / dpi_scale;
            let thumb_height = thumb_height_physical / dpi_scale;

            if let Some(thumb_node) = world.get_ui_layout_node_mut(data.thumb_entity)
                && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                    thumb_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
            {
                window.position = crate::ecs::ui::units::Ab(Vec2::new(0.0, thumb_y)).into();
                window.size = crate::ecs::ui::units::Ab(Vec2::new(8.0, thumb_height)).into();
            }
        }
    }

    if let Some(UiWidgetState::ScrollArea(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.scroll_offset = scroll_offset;
        widget_data.content_height = total_content_height;
        widget_data.visible_height = visible_height;
        widget_data.thumb_dragging = thumb_dragging;
        widget_data.thumb_drag_start_offset = thumb_drag_start_offset;
    }
}

fn handle_virtual_list(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiVirtualListData,
    dpi_scale: f32,
    frame_keys: &[(KeyCode, bool)],
    focused_entity: Option<freecs::Entity>,
) {
    let scroll_offset = if let Some(UiWidgetState::ScrollArea(scroll_data)) =
        world.get_ui_widget_state(data.scroll_entity)
    {
        scroll_data.scroll_offset
    } else {
        0.0
    };

    let visible_start = if data.item_height > 0.0 {
        (scroll_offset / data.item_height).floor() as usize
    } else {
        0
    };
    let visible_start = visible_start.min(data.total_items.saturating_sub(data.pool_size));
    let visible_end = data.total_items.min(visible_start + data.pool_size);

    let top_height = visible_start as f32 * data.item_height;
    let bottom_height = data.total_items.saturating_sub(visible_end) as f32 * data.item_height;

    if let Some(top_node) = world.get_ui_layout_node_mut(data.top_spacer) {
        top_node.flow_child_size = Some(
            crate::ecs::ui::units::Rl(Vec2::new(100.0, 0.0))
                + crate::ecs::ui::units::Ab(Vec2::new(0.0, top_height)),
        );
    }
    if let Some(bottom_node) = world.get_ui_layout_node_mut(data.bottom_spacer) {
        bottom_node.flow_child_size = Some(
            crate::ecs::ui::units::Rl(Vec2::new(100.0, 0.0))
                + crate::ecs::ui::units::Ab(Vec2::new(0.0, bottom_height)),
        );
    }

    let mut selection = data.selection;
    let mut selection_changed = false;

    for (pool_index, item) in data.pool_items.iter().enumerate() {
        let item_index = visible_start + pool_index;
        let is_visible = item_index < data.total_items;

        if let Some(node) = world.get_ui_layout_node_mut(item.container_entity) {
            node.visible = is_visible;
        }

        if is_visible {
            let interaction = snapshot_interaction(world, item.container_entity);
            if interaction.clicked {
                selection = Some(item_index);
                selection_changed = true;
                world.resources.retained_ui.frame_events.push(
                    crate::ecs::ui::resources::UiEvent::VirtualListItemClicked {
                        entity,
                        item_index,
                    },
                );
            }

            let accent_color = world
                .resources
                .retained_ui
                .theme_state
                .active_theme()
                .accent_color;
            let is_selected = selection == Some(item_index);
            if let Some(crate::ecs::ui::components::UiNodeContent::Rect {
                border_width,
                border_color,
                ..
            }) = world.get_ui_node_content_mut(item.container_entity)
            {
                if is_selected {
                    *border_width = 1.0 / dpi_scale;
                    *border_color = accent_color;
                } else {
                    *border_width = 0.0;
                }
            }
        }
    }

    let is_focused = focused_entity == Some(entity)
        || data
            .pool_items
            .iter()
            .any(|item| focused_entity == Some(item.container_entity));
    if is_focused && data.total_items > 0 {
        for &(key, pressed) in frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::ArrowUp => {
                    let current = selection.unwrap_or(0);
                    if current > 0 {
                        selection = Some(current - 1);
                        selection_changed = true;
                    }
                }
                KeyCode::ArrowDown => {
                    let current = selection.map_or(0, |s| s + 1);
                    if current < data.total_items {
                        selection = Some(current);
                        selection_changed = true;
                    }
                }
                KeyCode::Home => {
                    selection = Some(0);
                    selection_changed = true;
                }
                KeyCode::End => {
                    selection = Some(data.total_items - 1);
                    selection_changed = true;
                }
                _ => {}
            }
        }
    }

    if let Some(UiWidgetState::VirtualList(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.visible_start = visible_start;
        widget_data.selection = selection;
        widget_data.selection_changed = selection_changed;
    }
}

fn handle_tab_bar(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiTabBarData,
    frame_keys: &[(KeyCode, bool)],
    focused_entity: Option<freecs::Entity>,
) {
    let mut new_selected = data.selected_tab;
    let mut changed = false;

    for (index, tab_entity) in data.tab_entities.iter().enumerate() {
        let tab_clicked = world
            .get_ui_node_interaction(*tab_entity)
            .map(|i| i.clicked)
            .unwrap_or(false);
        if tab_clicked && data.selected_tab != index {
            new_selected = index;
            changed = true;
            break;
        }
    }

    let any_tab_focused = data
        .tab_entities
        .iter()
        .any(|tab| focused_entity == Some(*tab));
    if any_tab_focused {
        let tab_count = data.tab_entities.len();
        for &(key, pressed) in frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::ArrowLeft => {
                    if new_selected > 0 {
                        new_selected -= 1;
                        changed = true;
                    }
                }
                KeyCode::ArrowRight => {
                    if new_selected + 1 < tab_count {
                        new_selected += 1;
                        changed = true;
                    }
                }
                _ => {}
            }
        }
    }

    if changed {
        let theme = world.resources.retained_ui.theme_state.active_theme();
        let active_bg = theme.accent_color;
        let inactive_bg = theme.background_color;

        let old_entity = data.tab_entities[data.selected_tab];
        let new_entity = data.tab_entities[new_selected];

        if let Some(color) = world.get_ui_node_color_mut(old_entity) {
            color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(inactive_bg);
        }
        if let Some(color) = world.get_ui_node_color_mut(new_entity) {
            color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(active_bg);
        }
    }

    if let Some(UiWidgetState::TabBar(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.selected_tab = new_selected;
        widget_data.changed = changed;
    }
    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::TabChanged {
                entity,
                tab_index: new_selected,
            },
        );
    }
}

#[allow(clippy::too_many_arguments)]
fn handle_text_input(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiTextInputData,
    focused_entity: Option<freecs::Entity>,
    frame_chars: &[char],
    frame_keys: &[(KeyCode, bool)],
    ctrl_held: bool,
    shift_held: bool,
    mouse_position: Vec2,
    current_time: f64,
    dpi_scale: f32,
) {
    let is_focused = focused_entity == Some(entity);
    let mut text = data.text.clone();
    let mut cursor_position = data.cursor_position;
    let mut selection_start = data.selection_start;
    let mut changed = false;
    let mut cursor_blink_timer = data.cursor_blink_timer;
    let mut scroll_offset = data.scroll_offset;
    let mut clear_focus = false;
    let mut undo_stack = data.undo_stack.clone();
    let mut needs_snapshot = false;
    let input_mask = &data.input_mask;
    let max_length = data.max_length;

    if is_focused {
        for character in frame_chars {
            if *character >= ' ' && input_mask.accepts(*character) {
                if let Some(max) = max_length {
                    let result_len = if let Some(sel) = selection_start {
                        let selected = sel.max(cursor_position) - sel.min(cursor_position);
                        text.chars().count() - selected + 1
                    } else {
                        text.chars().count() + 1
                    };
                    if result_len > max {
                        continue;
                    }
                }
                if !needs_snapshot {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    needs_snapshot = true;
                }
                if let Some(sel_start) = selection_start {
                    let min = sel_start.min(cursor_position);
                    let max = sel_start.max(cursor_position);
                    let chars: Vec<char> = text.chars().collect();
                    let mut new_text: String = chars[..min].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[max..].iter());
                    text = new_text;
                    cursor_position = min + 1;
                    selection_start = None;
                } else {
                    let chars: Vec<char> = text.chars().collect();
                    let mut new_text: String = chars[..cursor_position].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[cursor_position..].iter());
                    text = new_text;
                    cursor_position += 1;
                }
                changed = true;
                cursor_blink_timer = current_time;
            }
        }

        if needs_snapshot {
            undo_stack.push(
                crate::ecs::ui::components::TextSnapshot {
                    text: text.clone(),
                    cursor_position,
                    selection_start,
                },
                current_time,
            );
        }

        for (key, is_pressed) in frame_keys {
            if !is_pressed {
                continue;
            }
            let chars: Vec<char> = text.chars().collect();
            let len = chars.len();

            match key {
                KeyCode::Backspace => {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    let mut did_change = false;
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        cursor_position = min;
                        selection_start = None;
                        did_change = true;
                    } else if cursor_position > 0 {
                        if ctrl_held {
                            let new_pos = prev_word_boundary(&text, cursor_position);
                            let mut new_text: String = chars[..new_pos].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            text = new_text;
                            cursor_position = new_pos;
                        } else {
                            let mut new_text: String =
                                chars[..cursor_position - 1].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            text = new_text;
                            cursor_position -= 1;
                        }
                        did_change = true;
                    }
                    if did_change {
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                        changed = true;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::Delete => {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    let mut did_change = false;
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        cursor_position = min;
                        selection_start = None;
                        did_change = true;
                    } else if cursor_position < len {
                        if ctrl_held {
                            let end_pos = next_word_boundary(&text, cursor_position);
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[end_pos..].iter());
                            text = new_text;
                        } else {
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[cursor_position + 1..].iter());
                            text = new_text;
                        }
                        did_change = true;
                    }
                    if did_change {
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                        changed = true;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::ArrowLeft => {
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let min = selection_start.unwrap().min(cursor_position);
                        cursor_position = min;
                        selection_start = None;
                        cursor_blink_timer = current_time;
                        continue;
                    }
                    if ctrl_held {
                        cursor_position = prev_word_boundary(&text, cursor_position);
                    } else {
                        cursor_position = cursor_position.saturating_sub(1);
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::ArrowRight => {
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let max = selection_start.unwrap().max(cursor_position);
                        cursor_position = max;
                        selection_start = None;
                        cursor_blink_timer = current_time;
                        continue;
                    }
                    if ctrl_held {
                        cursor_position = next_word_boundary(&text, cursor_position);
                    } else if cursor_position < len {
                        cursor_position += 1;
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::Home => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    cursor_position = 0;
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::End => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    cursor_position = len;
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::KeyA if ctrl_held => {
                    selection_start = Some(0);
                    cursor_position = len;
                    cursor_blink_timer = current_time;
                }
                KeyCode::KeyZ if ctrl_held => {
                    if let Some(snapshot) = undo_stack.undo() {
                        text = snapshot.text.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        changed = true;
                        cursor_blink_timer = current_time;
                    }
                }
                KeyCode::KeyY if ctrl_held => {
                    if let Some(snapshot) = undo_stack.redo() {
                        text = snapshot.text.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        changed = true;
                        cursor_blink_timer = current_time;
                    }
                }
                KeyCode::KeyC if ctrl_held => {
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let selected: String = text.chars().skip(min).take(max - min).collect();
                        world.resources.retained_ui.clipboard_text = selected;
                    }
                }
                KeyCode::KeyX if ctrl_held => {
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let selected: String = text.chars().skip(min).take(max - min).collect();
                        world.resources.retained_ui.clipboard_text = selected;
                        undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                            text: text.clone(),
                            cursor_position,
                            selection_start,
                        });
                        let chars: Vec<char> = text.chars().collect();
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        cursor_position = min;
                        selection_start = None;
                        changed = true;
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                    }
                }
                KeyCode::KeyV if ctrl_held => {
                    let mut paste_text = world.resources.retained_ui.clipboard_text.clone();
                    if !paste_text.is_empty() {
                        if let Some(max) = max_length {
                            let existing = text.chars().count();
                            let removed = selection_start
                                .map(|s| s.max(cursor_position) - s.min(cursor_position))
                                .unwrap_or(0);
                            let available = max.saturating_sub(existing - removed);
                            paste_text = paste_text.chars().take(available).collect();
                        }
                        if !paste_text.is_empty() {
                            undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            });
                            if let Some(sel_start) = selection_start {
                                let min = sel_start.min(cursor_position);
                                let max = sel_start.max(cursor_position);
                                let chars: Vec<char> = text.chars().collect();
                                let mut new_text: String = chars[..min].iter().collect();
                                new_text.push_str(&paste_text);
                                new_text.extend(chars[max..].iter());
                                text = new_text;
                                cursor_position = min + paste_text.chars().count();
                                selection_start = None;
                            } else {
                                let chars: Vec<char> = text.chars().collect();
                                let mut new_text: String =
                                    chars[..cursor_position].iter().collect();
                                new_text.push_str(&paste_text);
                                new_text.extend(chars[cursor_position..].iter());
                                text = new_text;
                                cursor_position += paste_text.chars().count();
                            }
                            changed = true;
                            undo_stack.push(
                                crate::ecs::ui::components::TextSnapshot {
                                    text: text.clone(),
                                    cursor_position,
                                    selection_start,
                                },
                                current_time,
                            );
                        }
                    }
                }
                KeyCode::Escape => {
                    selection_start = None;
                    clear_focus = true;
                }
                KeyCode::Enter => {
                    clear_focus = true;
                }
                _ => {}
            }
        }

        if interaction.clicked {
            let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
            if let Some(rect) = input_rect {
                let font_size = world
                    .resources
                    .retained_ui
                    .theme_state
                    .active_theme()
                    .font_size;
                let best_idx = world
                    .resources
                    .text_cache
                    .font_manager
                    .best_bitmap_font_for_size(font_size);
                let font_arc = world
                    .resources
                    .text_cache
                    .font_manager
                    .get_bitmap_font_arc(best_idx);
                if let Some(atlas) = font_arc {
                    let local_x = (mouse_position.x - rect.min.x) / dpi_scale - 8.0 + scroll_offset;
                    let new_pos = byte_index_at_x(&atlas, &text, font_size, local_x);
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else {
                        selection_start = None;
                    }
                    cursor_position = new_pos;
                    cursor_blink_timer = current_time;
                }
            }
        }

        if interaction.double_clicked {
            let len = text.chars().count();
            let pos = cursor_position.min(len);
            let word_start = prev_word_boundary(&text, pos);
            let word_end = next_word_boundary(&text, pos).min(len);
            selection_start = Some(word_start);
            cursor_position = word_end;
        }
    } else {
        selection_start = None;
    }

    if clear_focus {
        world.resources.retained_ui.focused_entity = None;
    }

    if changed {
        world.resources.text_cache.set_text(data.text_slot, &text);
    }

    let cursor_visible = is_focused && ((current_time - cursor_blink_timer) % 1.0) < 0.5;
    if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity) {
        cursor_node.visible = cursor_visible;
    }

    let has_selection =
        is_focused && selection_start.is_some() && selection_start != Some(cursor_position);
    if let Some(sel_node) = world.get_ui_layout_node_mut(data.selection_entity) {
        sel_node.visible = has_selection;
    }

    let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
    if let Some(rect) = input_rect {
        let font_size = world
            .resources
            .retained_ui
            .theme_state
            .active_theme()
            .font_size;
        let best_idx = world
            .resources
            .text_cache
            .font_manager
            .best_bitmap_font_for_size(font_size);
        let font_arc = world
            .resources
            .text_cache
            .font_manager
            .get_bitmap_font_arc(best_idx);
        if let Some(atlas) = font_arc {
            let text_before_cursor: String = text.chars().take(cursor_position).collect();
            let cursor_x = measure_text_width(&atlas, &text_before_cursor, font_size);

            let visible_width = rect.width() / dpi_scale - 16.0;
            if cursor_x - scroll_offset > visible_width {
                scroll_offset = cursor_x - visible_width;
            } else if cursor_x - scroll_offset < 0.0 {
                scroll_offset = cursor_x;
            }

            let cursor_screen_x = cursor_x - scroll_offset;

            let mut sel_start_x = 0.0f32;
            let mut sel_end_x = 0.0f32;
            if has_selection {
                let sel_start = selection_start.unwrap();
                let sel_min = sel_start.min(cursor_position);
                let sel_max = sel_start.max(cursor_position);
                let text_before_sel: String = text.chars().take(sel_min).collect();
                let text_to_sel_end: String = text.chars().take(sel_max).collect();
                sel_start_x =
                    measure_text_width(&atlas, &text_before_sel, font_size) - scroll_offset;
                sel_end_x = measure_text_width(&atlas, &text_to_sel_end, font_size) - scroll_offset;
            }

            drop(atlas);

            if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity)
                && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                    cursor_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
            {
                window.position =
                    crate::ecs::ui::units::Ab(Vec2::new(8.0 + cursor_screen_x, 4.0)).into();
            }

            if has_selection
                && let Some(sel_node) = world.get_ui_layout_node_mut(data.selection_entity)
                && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                    sel_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
            {
                window.position =
                    crate::ecs::ui::units::Ab(Vec2::new(8.0 + sel_start_x, 4.0)).into();
                window.size = crate::ecs::ui::units::Ab(Vec2::new(
                    sel_end_x - sel_start_x,
                    rect.height() / dpi_scale - 8.0,
                ))
                .into();
            }
        }
    }

    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::TextInputChanged {
                entity,
                text: text.clone(),
            },
        );
        validate_widget_text(world, entity, &text);
    }
    if clear_focus {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::TextInputSubmitted {
                entity,
                text: text.clone(),
            },
        );
    }
    let text_is_empty = text.is_empty();
    let placeholder_entity = if let Some(UiWidgetState::TextInput(widget_data)) =
        world.get_ui_widget_state_mut(entity)
    {
        widget_data.text = text;
        widget_data.cursor_position = cursor_position;
        widget_data.selection_start = selection_start;
        widget_data.changed = changed;
        widget_data.cursor_blink_timer = cursor_blink_timer;
        widget_data.scroll_offset = scroll_offset;
        widget_data.undo_stack = undo_stack;
        widget_data.placeholder_entity
    } else {
        None
    };
    if let Some(ph_entity) = placeholder_entity
        && let Some(node) = world.get_ui_layout_node_mut(ph_entity)
    {
        node.visible = text_is_empty;
    }
}

fn handle_dropdown(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiDropdownData,
    frame_keys: &[(KeyCode, bool)],
    focused_entity: Option<freecs::Entity>,
) {
    let mut selected_index = data.selected_index;
    let mut changed = false;
    let mut open = data.open;
    let mut hovered_index = data.hovered_index;
    let is_theme_dropdown = data.is_theme_dropdown;
    let was_open = data.open;
    let option_count = data.options.len();
    let searchable = data.searchable;
    let filter_input_entity = data.filter_input_entity;
    let mut filtered_indices = data.filtered_indices.clone();
    let mut filter_text = data.filter_text.clone();

    if interaction.clicked {
        open = !open;
    }

    if !open && focused_entity == Some(entity) {
        for &(key, pressed) in frame_keys {
            if pressed
                && (key == KeyCode::ArrowDown || key == KeyCode::Space || key == KeyCode::Enter)
            {
                open = true;
                hovered_index = Some(selected_index);
                break;
            }
        }
    }

    if open && !was_open && searchable {
        if let Some(filter_entity) = filter_input_entity {
            if let Some(UiWidgetState::TextInput(input_data)) =
                world.get_ui_widget_state_mut(filter_entity)
            {
                input_data.text.clear();
                input_data.cursor_position = 0;
                input_data.selection_start = None;
                input_data.changed = false;
            }
            if let Some(UiWidgetState::TextInput(input_data)) =
                world.get_ui_widget_state(filter_entity)
            {
                world
                    .resources
                    .text_cache
                    .set_text(input_data.text_slot, "");
            }
            world.resources.retained_ui.focused_entity = Some(filter_entity);
        }
        filter_text.clear();
        filtered_indices = (0..option_count).collect();
    }

    if open
        && searchable
        && let Some(filter_entity) = filter_input_entity
    {
        let current_filter = world
            .get_ui_widget_state(filter_entity)
            .and_then(|ws| {
                if let UiWidgetState::TextInput(input_data) = ws {
                    Some(input_data.text.clone())
                } else {
                    None
                }
            })
            .unwrap_or_default();

        if current_filter != filter_text {
            filter_text = current_filter;
            let lower_filter = filter_text.to_lowercase();
            filtered_indices = (0..option_count)
                .filter(|idx| {
                    lower_filter.is_empty()
                        || data.options[*idx].to_lowercase().contains(&lower_filter)
                })
                .collect();
            hovered_index = None;

            for (index, popup_entity) in data.popup_entities.iter().enumerate() {
                let visible = filtered_indices.contains(&index);
                if let Some(node) = world.get_ui_layout_node_mut(*popup_entity) {
                    node.visible = visible;
                }
            }
        }
    }

    if open {
        let visible_count = if searchable {
            filtered_indices.len()
        } else {
            option_count
        };

        for &(key, pressed) in frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::ArrowDown => {
                    if visible_count > 0 {
                        let current_visible_pos = hovered_index
                            .and_then(|hi| {
                                if searchable {
                                    filtered_indices.iter().position(|&idx| idx == hi)
                                } else {
                                    Some(hi)
                                }
                            })
                            .unwrap_or(0);
                        let next_pos = if current_visible_pos + 1 >= visible_count {
                            0
                        } else {
                            current_visible_pos + 1
                        };
                        hovered_index = Some(if searchable {
                            filtered_indices[next_pos]
                        } else {
                            next_pos
                        });
                    }
                }
                KeyCode::ArrowUp => {
                    if visible_count > 0 {
                        let current_visible_pos = hovered_index
                            .and_then(|hi| {
                                if searchable {
                                    filtered_indices.iter().position(|&idx| idx == hi)
                                } else {
                                    Some(hi)
                                }
                            })
                            .unwrap_or(0);
                        let prev_pos = if current_visible_pos == 0 {
                            visible_count.saturating_sub(1)
                        } else {
                            current_visible_pos - 1
                        };
                        hovered_index = Some(if searchable {
                            filtered_indices[prev_pos]
                        } else {
                            prev_pos
                        });
                    }
                }
                KeyCode::Enter => {
                    if let Some(idx) = hovered_index {
                        selected_index = idx;
                        changed = true;
                        open = false;
                        let selected_text = data.options[idx].clone();
                        world
                            .resources
                            .text_cache
                            .set_text(data.header_text_slot, selected_text);
                    }
                }
                KeyCode::Escape => {
                    open = false;
                }
                _ => {}
            }
        }

        for (index, popup_entity) in data.popup_entities.iter().enumerate() {
            if searchable && !filtered_indices.contains(&index) {
                continue;
            }
            let popup_interaction = world.get_ui_node_interaction(*popup_entity);
            let popup_clicked = popup_interaction.map(|i| i.clicked).unwrap_or(false);
            let popup_hovered = popup_interaction.map(|i| i.hovered).unwrap_or(false);

            if popup_hovered {
                hovered_index = Some(index);
            }

            if popup_clicked {
                selected_index = index;
                changed = true;
                open = false;

                let selected_text = data.options[index].clone();
                world
                    .resources
                    .text_cache
                    .set_text(data.header_text_slot, selected_text);
                break;
            }
        }

        for (index, popup_entity) in data.popup_entities.iter().enumerate() {
            let is_highlighted = hovered_index == Some(index);
            if let Some(weights) = world.get_ui_state_weights_mut(*popup_entity) {
                weights.weights[crate::ecs::ui::state::UiHover::INDEX] =
                    if is_highlighted { 1.0 } else { 0.0 };
            }
        }

        let mouse_just_pressed = world
            .resources
            .input
            .mouse
            .state
            .contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
        let filter_hovered = filter_input_entity
            .and_then(|fe| world.get_ui_node_interaction(fe))
            .map(|i| i.hovered)
            .unwrap_or(false);
        if mouse_just_pressed && !interaction.hovered && hovered_index.is_none() && !filter_hovered
        {
            open = false;
        }
    }

    if searchable && was_open && !open {
        if let Some(filter_entity) = filter_input_entity
            && world.resources.retained_ui.focused_entity == Some(filter_entity)
        {
            world.resources.retained_ui.focused_entity = None;
        }
        for popup_entity in &data.popup_entities {
            if let Some(node) = world.get_ui_layout_node_mut(*popup_entity) {
                node.visible = true;
            }
        }
    }

    if is_theme_dropdown {
        if open && hovered_index != data.hovered_index {
            world
                .resources
                .retained_ui
                .theme_state
                .set_preview(hovered_index);
        }
        if was_open && !open {
            world.resources.retained_ui.theme_state.clear_preview();
        }
        if changed {
            world
                .resources
                .retained_ui
                .theme_state
                .select_theme(selected_index);
        }
    }

    if let Some(node) = world.get_ui_layout_node_mut(data.popup_container_entity) {
        node.visible = open;
    }

    if let Some(UiWidgetState::Dropdown(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.selected_index = selected_index;
        widget_data.changed = changed;
        widget_data.open = open;
        widget_data.hovered_index = hovered_index;
        widget_data.filter_text = filter_text;
        widget_data.filtered_indices = filtered_indices;
    }
    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::DropdownChanged {
                entity,
                selected_index,
            },
        );
    }
}

fn handle_multi_select(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiMultiSelectData,
) {
    let mut selected_indices = data.selected_indices.clone();
    let mut changed = false;
    let mut open = data.open;
    let mut hovered_index = data.hovered_index;
    let popup_container_entity = data.popup_container_entity;
    let popup_entities = data.popup_entities.clone();
    let check_entities = data.check_entities.clone();

    if interaction.clicked {
        open = !open;
    }

    if open {
        for (index, &popup_entity) in popup_entities.iter().enumerate() {
            if let Some(popup_interaction) = world.get_ui_node_interaction(popup_entity) {
                if popup_interaction.clicked {
                    if selected_indices.contains(&index) {
                        selected_indices.remove(&index);
                    } else {
                        selected_indices.insert(index);
                    }
                    changed = true;
                }
                if popup_interaction.hovered {
                    hovered_index = Some(index);
                }
            }
        }

        let mouse_state = world.resources.input.mouse.state;
        let mouse_just_pressed =
            mouse_state.contains(crate::ecs::input::MouseState::LEFT_JUST_PRESSED);
        if mouse_just_pressed
            && !interaction.hovered
            && !world
                .get_ui_node_interaction(popup_container_entity)
                .is_some_and(|i| i.hovered)
        {
            open = false;
        }
    }

    let header_text = if selected_indices.is_empty() {
        "None selected".to_string()
    } else if selected_indices.len() == 1 {
        let idx = *selected_indices.iter().next().unwrap();
        data.options[idx].clone()
    } else {
        format!("{} selected", selected_indices.len())
    };
    world
        .resources
        .text_cache
        .set_text(data.header_text_slot, &header_text);

    for (index, &check_entity) in check_entities.iter().enumerate() {
        let check_text = if selected_indices.contains(&index) {
            "\u{2713}"
        } else {
            " "
        };
        if let Some(crate::ecs::ui::components::UiNodeContent::Text { text_slot, .. }) =
            world.get_ui_node_content(check_entity)
        {
            let slot = *text_slot;
            world.resources.text_cache.set_text(slot, check_text);
        }
    }

    if let Some(node) = world.get_ui_layout_node_mut(popup_container_entity) {
        node.visible = open;
    }

    if let Some(UiWidgetState::MultiSelect(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.selected_indices = selected_indices.clone();
        widget_data.changed = changed;
        widget_data.open = open;
        widget_data.hovered_index = hovered_index;
    }
    if changed {
        let selected_vec: Vec<usize> = selected_indices.into_iter().collect();
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::MultiSelectChanged {
                entity,
                selected_indices: selected_vec,
            },
        );
    }
}

fn handle_date_picker(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiDatePickerData,
) {
    let mut year = data.year;
    let mut month = data.month;
    let mut day = data.day;
    let mut changed = false;
    let mut open = data.open;
    let popup_entity = data.popup_entity;
    let day_entities = data.day_entities.clone();
    let day_text_slots = data.day_text_slots.clone();
    let prev_month_entity = data.prev_month_entity;
    let next_month_entity = data.next_month_entity;
    let month_label_slot = data.month_label_slot;
    let header_text_slot = data.header_text_slot;

    if interaction.clicked {
        open = !open;
    }

    let mut repopulate = false;

    if open {
        if let Some(prev_interaction) = world.get_ui_node_interaction(prev_month_entity)
            && prev_interaction.clicked
        {
            if month == 1 {
                month = 12;
                year -= 1;
            } else {
                month -= 1;
            }
            repopulate = true;
        }

        if let Some(next_interaction) = world.get_ui_node_interaction(next_month_entity)
            && next_interaction.clicked
        {
            if month == 12 {
                month = 1;
                year += 1;
            } else {
                month += 1;
            }
            repopulate = true;
        }

        let first_dow = crate::ecs::ui::widgets::day_of_week(year, month, 1);
        let days = crate::ecs::ui::widgets::days_in_month(year, month);

        for (cell_index, &day_entity) in day_entities.iter().enumerate() {
            if let Some(day_interaction) = world.get_ui_node_interaction(day_entity)
                && day_interaction.clicked
            {
                let cell_u32 = cell_index as u32;
                if cell_u32 >= first_dow && cell_u32 < first_dow + days {
                    let clicked_day = cell_u32 - first_dow + 1;
                    day = clicked_day;
                    changed = true;
                    open = false;
                }
            }
        }

        let mouse_state = world.resources.input.mouse.state;
        let mouse_just_pressed =
            mouse_state.contains(crate::ecs::input::MouseState::LEFT_JUST_PRESSED);
        if mouse_just_pressed
            && !interaction.hovered
            && !world
                .get_ui_node_interaction(popup_entity)
                .is_some_and(|i| i.hovered)
        {
            open = false;
        }
    }

    if repopulate {
        let accent_color = world
            .resources
            .retained_ui
            .theme_state
            .active_theme()
            .accent_color;
        crate::ecs::ui::widgets::populate_calendar_grid(
            world,
            year,
            month,
            day,
            &day_text_slots,
            &day_entities,
            accent_color,
        );
        world.resources.text_cache.set_text(
            month_label_slot,
            crate::ecs::ui::widgets::format_month_year(year, month),
        );
    }

    if changed {
        let accent_color = world
            .resources
            .retained_ui
            .theme_state
            .active_theme()
            .accent_color;
        crate::ecs::ui::widgets::populate_calendar_grid(
            world,
            year,
            month,
            day,
            &day_text_slots,
            &day_entities,
            accent_color,
        );
        world
            .resources
            .text_cache
            .set_text(header_text_slot, format!("{year:04}-{month:02}-{day:02}"));
    }

    if let Some(node) = world.get_ui_layout_node_mut(popup_entity) {
        node.visible = open;
    }

    if let Some(UiWidgetState::DatePicker(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.year = year;
        widget_data.month = month;
        widget_data.day = day;
        widget_data.changed = changed;
        widget_data.open = open;
    }
    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::DatePickerChanged {
                entity,
                year,
                month,
                day,
            },
        );
    }
}

fn handle_menu(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiMenuData,
) {
    let mut clicked_item = None;
    let mut open = data.open;

    if interaction.clicked {
        open = !open;
    }

    if open {
        let mut any_popup_hovered = false;
        for (index, popup_entity) in data.popup_entities.iter().enumerate() {
            let popup_interaction = world.get_ui_node_interaction(*popup_entity);
            let popup_clicked = popup_interaction.map(|i| i.clicked).unwrap_or(false);
            if popup_interaction.map(|i| i.hovered).unwrap_or(false) {
                any_popup_hovered = true;
            }
            if popup_clicked {
                clicked_item = Some(index);
                open = false;
                break;
            }
        }

        let mouse_just_pressed = world
            .resources
            .input
            .mouse
            .state
            .contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);
        if mouse_just_pressed && !interaction.hovered && !any_popup_hovered {
            open = false;
        }
    }

    if let Some(node) = world.get_ui_layout_node_mut(data.popup_container_entity) {
        node.visible = open;
    }

    if let Some(UiWidgetState::Menu(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.clicked_item = clicked_item;
        widget_data.open = open;
    }
    if let Some(item_index) = clicked_item {
        world
            .resources
            .retained_ui
            .frame_events
            .push(crate::ecs::ui::resources::UiEvent::MenuItemClicked { entity, item_index });
    }
}

struct PanelContext {
    mouse_position: Vec2,
    mouse_just_pressed: bool,
    mouse_just_released: bool,
    mouse_down: bool,
    dpi_scale: f32,
    viewport_size: Vec2,
    mouse_occluded: bool,
}

fn handle_panel(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiPanelData,
    ctx: &PanelContext,
) {
    use crate::ecs::ui::components::ResizeEdge;
    use crate::ecs::ui::units::Ab;

    let mouse_position = ctx.mouse_position;
    let mouse_just_pressed = ctx.mouse_just_pressed;
    let mouse_just_released = ctx.mouse_just_released;
    let mouse_down = ctx.mouse_down;
    let dpi_scale = ctx.dpi_scale;
    let viewport_size = ctx.viewport_size;

    let header_interaction = snapshot_interaction(world, data.header_entity);

    let mut drag_offset = data.drag_offset;
    let mut resize_edge = data.resize_edge;
    let mut resize_start_rect = data.resize_start_rect;
    let mut resize_start_mouse = data.resize_start_mouse;
    let mut panel_kind = data.panel_kind;
    let mut undocked_rect = data.undocked_rect;
    let mut default_dock_size = data.default_dock_size;
    let resizable = data.resizable;

    if let Some(collapse_entity) = data.collapse_button_entity {
        let collapse_interaction = snapshot_interaction(world, collapse_entity);
        if collapse_interaction.clicked {
            if panel_kind == UiPanelKind::Floating {
                if let Some(node) = world.get_ui_layout_node_mut(entity) {
                    node.visible = false;
                }
            } else {
                let new_collapsed = !data.collapsed;
                if let Some(node) = world.get_ui_layout_node_mut(data.content_entity) {
                    node.visible = !new_collapsed;
                }
                if let Some(text_slot) = data.collapse_button_text_slot {
                    world
                        .resources
                        .text_cache
                        .set_text(text_slot, if new_collapsed { "+" } else { "-" });
                }
                if let Some(UiWidgetState::Panel(widget_data)) =
                    world.get_ui_widget_state_mut(entity)
                {
                    widget_data.collapsed = new_collapsed;
                }
            }
        }
    }

    if header_interaction.dragging && panel_kind == UiPanelKind::Floating {
        if drag_offset.is_none() {
            let panel_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
            if let Some(rect) = panel_rect
                && let Some(drag_start) = header_interaction.drag_start
            {
                drag_offset = Some(drag_start - rect.min);
            }
        }

        if let Some(offset) = drag_offset
            && let Some(node) = world.get_ui_layout_node_mut(entity)
            && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
        {
            let current_ab = window.position.absolute.unwrap_or(Vec2::new(0.0, 0.0));
            let parent_min = node.computed_rect.min - current_ab * dpi_scale;
            let new_pos = (mouse_position - offset - parent_min) / dpi_scale;
            let rounded_pos = Vec2::new(new_pos.x.round(), new_pos.y.round());
            window.position = Ab(rounded_pos).into();
        }

        world.resources.retained_ui.dock_indicator_active = true;
        world.resources.retained_ui.dock_indicator_panel = Some(entity);
    }

    if header_interaction.dragging
        && panel_kind != UiPanelKind::Floating
        && !data.pinned
        && let Some(drag_start) = header_interaction.drag_start
    {
        let drag_distance = (mouse_position - drag_start).magnitude();
        if drag_distance > 50.0 {
            panel_kind = UiPanelKind::Floating;

            let restored_size = undocked_rect
                .map(|r| r.size())
                .unwrap_or(Vec2::new(300.0, 400.0));
            let new_pos = mouse_position - Vec2::new(restored_size.x * 0.5, 14.0);

            if let Some(node) = world.get_ui_layout_node_mut(entity) {
                node.layer = Some(crate::render::wgpu::passes::geometry::UiLayer::FloatingPanels);
                if let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                    node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
                {
                    window.position = Ab(new_pos / dpi_scale).into();
                    window.size = Ab(restored_size / dpi_scale).into();
                }
            }

            drag_offset = Some(Vec2::new(restored_size.x * 0.5, 14.0));
            world.resources.retained_ui.dock_indicator_active = true;
            world.resources.retained_ui.dock_indicator_panel = Some(entity);
        }
    }

    if mouse_just_released
        && world.resources.retained_ui.dock_indicator_panel == Some(entity)
        && panel_kind == UiPanelKind::Floating
    {
        let indicator_size = 40.0;
        let indicator_spacing = 4.0;
        let center = viewport_size * 0.5;
        let dock_buttons = [
            (
                crate::ecs::ui::types::Rect::from_center_size(
                    Vec2::new(center.x, center.y - indicator_size - indicator_spacing),
                    Vec2::new(indicator_size, indicator_size),
                ),
                UiPanelKind::DockedTop,
            ),
            (
                crate::ecs::ui::types::Rect::from_center_size(
                    Vec2::new(center.x, center.y + indicator_size + indicator_spacing),
                    Vec2::new(indicator_size, indicator_size),
                ),
                UiPanelKind::DockedBottom,
            ),
            (
                crate::ecs::ui::types::Rect::from_center_size(
                    Vec2::new(center.x - indicator_size - indicator_spacing, center.y),
                    Vec2::new(indicator_size, indicator_size),
                ),
                UiPanelKind::DockedLeft,
            ),
            (
                crate::ecs::ui::types::Rect::from_center_size(
                    Vec2::new(center.x + indicator_size + indicator_spacing, center.y),
                    Vec2::new(indicator_size, indicator_size),
                ),
                UiPanelKind::DockedRight,
            ),
        ];

        let mut target_dock = None;
        for (btn_rect, dock_kind) in &dock_buttons {
            if btn_rect.contains(mouse_position) {
                target_dock = Some(*dock_kind);
                break;
            }
        }

        if let Some(new_kind) = target_dock {
            let panel_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
            if let Some(rect) = panel_rect {
                undocked_rect = Some(rect);
            }
            panel_kind = new_kind;
            default_dock_size = match new_kind {
                UiPanelKind::DockedLeft | UiPanelKind::DockedRight => {
                    undocked_rect.map(|r| r.width()).unwrap_or(300.0) / dpi_scale
                }
                UiPanelKind::DockedTop | UiPanelKind::DockedBottom => {
                    undocked_rect.map(|r| r.height()).unwrap_or(300.0) / dpi_scale
                }
                UiPanelKind::Floating => 300.0,
            };

            if let Some(node) = world.get_ui_layout_node_mut(entity) {
                node.layer = Some(crate::render::wgpu::passes::geometry::UiLayer::DockedPanels);
            }
        }

        world.resources.retained_ui.dock_indicator_active = false;
        world.resources.retained_ui.dock_indicator_panel = None;
    }

    if !header_interaction.pressed && !header_interaction.dragging {
        drag_offset = None;
        if world.resources.retained_ui.dock_indicator_panel == Some(entity) {
            world.resources.retained_ui.dock_indicator_active = false;
            world.resources.retained_ui.dock_indicator_panel = None;
        }
    }

    if !ctx.mouse_occluded && resizable {
        let panel_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);

        if let Some(rect) = panel_rect {
            let edge_margin = 6.0 * dpi_scale;
            let near_left = (mouse_position.x - rect.min.x).abs() < edge_margin;
            let near_right = (mouse_position.x - rect.max.x).abs() < edge_margin;
            let near_top = (mouse_position.y - rect.min.y).abs() < edge_margin;
            let near_bottom = (mouse_position.y - rect.max.y).abs() < edge_margin;
            let inside_x = mouse_position.x >= rect.min.x - edge_margin
                && mouse_position.x <= rect.max.x + edge_margin;
            let inside_y = mouse_position.y >= rect.min.y - edge_margin
                && mouse_position.y <= rect.max.y + edge_margin;

            let detected_edge = match panel_kind {
                UiPanelKind::DockedLeft => {
                    if near_right && inside_y {
                        Some(ResizeEdge::Right)
                    } else {
                        None
                    }
                }
                UiPanelKind::DockedRight => {
                    if near_left && inside_y {
                        Some(ResizeEdge::Left)
                    } else {
                        None
                    }
                }
                UiPanelKind::DockedTop => {
                    if near_bottom && inside_x {
                        Some(ResizeEdge::Bottom)
                    } else {
                        None
                    }
                }
                UiPanelKind::DockedBottom => {
                    if near_top && inside_x {
                        Some(ResizeEdge::Top)
                    } else {
                        None
                    }
                }
                UiPanelKind::Floating => {
                    if near_top && near_left && inside_x && inside_y {
                        Some(ResizeEdge::TopLeft)
                    } else if near_top && near_right && inside_x && inside_y {
                        Some(ResizeEdge::TopRight)
                    } else if near_bottom && near_left && inside_x && inside_y {
                        Some(ResizeEdge::BottomLeft)
                    } else if near_bottom && near_right && inside_x && inside_y {
                        Some(ResizeEdge::BottomRight)
                    } else if near_left && inside_y {
                        Some(ResizeEdge::Left)
                    } else if near_right && inside_y {
                        Some(ResizeEdge::Right)
                    } else if near_top && inside_x {
                        Some(ResizeEdge::Top)
                    } else if near_bottom && inside_x {
                        Some(ResizeEdge::Bottom)
                    } else {
                        None
                    }
                }
            };

            if let Some(edge) = detected_edge {
                world.resources.retained_ui.requested_cursor = Some(match edge {
                    ResizeEdge::Left | ResizeEdge::Right => winit::window::CursorIcon::EwResize,
                    ResizeEdge::Top | ResizeEdge::Bottom => winit::window::CursorIcon::NsResize,
                    ResizeEdge::TopLeft | ResizeEdge::BottomRight => {
                        winit::window::CursorIcon::NwseResize
                    }
                    ResizeEdge::TopRight | ResizeEdge::BottomLeft => {
                        winit::window::CursorIcon::NeswResize
                    }
                });
            }

            if !header_interaction.dragging && mouse_just_pressed && detected_edge.is_some() {
                resize_edge = detected_edge;
                resize_start_rect = Some(rect);
                resize_start_mouse = Some(mouse_position);
            }
        }
    }

    if let Some(edge) = resize_edge {
        world.resources.retained_ui.requested_cursor = Some(match edge {
            ResizeEdge::Left | ResizeEdge::Right => winit::window::CursorIcon::EwResize,
            ResizeEdge::Top | ResizeEdge::Bottom => winit::window::CursorIcon::NsResize,
            ResizeEdge::TopLeft | ResizeEdge::BottomRight => winit::window::CursorIcon::NwseResize,
            ResizeEdge::TopRight | ResizeEdge::BottomLeft => winit::window::CursorIcon::NeswResize,
        });
        if mouse_down {
            if let (Some(start_rect), Some(start_mouse)) = (resize_start_rect, resize_start_mouse) {
                let delta = mouse_position - start_mouse;
                let min_size = data.min_size;
                let mut new_pos = start_rect.min;
                let mut new_size = start_rect.size();

                match edge {
                    ResizeEdge::Right => {
                        new_size.x = (start_rect.width() + delta.x).max(min_size.x);
                    }
                    ResizeEdge::Bottom => {
                        new_size.y = (start_rect.height() + delta.y).max(min_size.y);
                    }
                    ResizeEdge::Left => {
                        let new_width = (start_rect.width() - delta.x).max(min_size.x);
                        new_pos.x = start_rect.max.x - new_width;
                        new_size.x = new_width;
                    }
                    ResizeEdge::Top => {
                        let new_height = (start_rect.height() - delta.y).max(min_size.y);
                        new_pos.y = start_rect.max.y - new_height;
                        new_size.y = new_height;
                    }
                    ResizeEdge::TopLeft => {
                        let new_width = (start_rect.width() - delta.x).max(min_size.x);
                        let new_height = (start_rect.height() - delta.y).max(min_size.y);
                        new_pos.x = start_rect.max.x - new_width;
                        new_pos.y = start_rect.max.y - new_height;
                        new_size.x = new_width;
                        new_size.y = new_height;
                    }
                    ResizeEdge::TopRight => {
                        let new_height = (start_rect.height() - delta.y).max(min_size.y);
                        new_pos.y = start_rect.max.y - new_height;
                        new_size.x = (start_rect.width() + delta.x).max(min_size.x);
                        new_size.y = new_height;
                    }
                    ResizeEdge::BottomLeft => {
                        let new_width = (start_rect.width() - delta.x).max(min_size.x);
                        new_pos.x = start_rect.max.x - new_width;
                        new_size.x = new_width;
                        new_size.y = (start_rect.height() + delta.y).max(min_size.y);
                    }
                    ResizeEdge::BottomRight => {
                        new_size.x = (start_rect.width() + delta.x).max(min_size.x);
                        new_size.y = (start_rect.height() + delta.y).max(min_size.y);
                    }
                }

                if panel_kind != UiPanelKind::Floating {
                    match panel_kind {
                        UiPanelKind::DockedLeft | UiPanelKind::DockedRight => {
                            default_dock_size = new_size.x / dpi_scale;
                        }
                        UiPanelKind::DockedTop | UiPanelKind::DockedBottom => {
                            default_dock_size = new_size.y / dpi_scale;
                        }
                        UiPanelKind::Floating => {}
                    }
                } else if let Some(node) = world.get_ui_layout_node_mut(entity)
                    && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                        node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
                {
                    let current_ab = window.position.absolute.unwrap_or(Vec2::new(0.0, 0.0));
                    let parent_min = node.computed_rect.min - current_ab * dpi_scale;
                    let logical_pos = (new_pos - parent_min) / dpi_scale;
                    let logical_size = new_size / dpi_scale;
                    window.position =
                        Ab(Vec2::new(logical_pos.x.round(), logical_pos.y.round())).into();
                    window.size =
                        Ab(Vec2::new(logical_size.x.round(), logical_size.y.round())).into();
                }
            }
        } else {
            resize_edge = None;
            resize_start_rect = None;
            resize_start_mouse = None;
        }
    }

    if let Some(UiWidgetState::Panel(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.drag_offset = drag_offset;
        widget_data.resize_edge = resize_edge;
        widget_data.resize_start_rect = resize_start_rect;
        widget_data.resize_start_mouse = resize_start_mouse;
        widget_data.panel_kind = panel_kind;
        widget_data.undocked_rect = undocked_rect;
        widget_data.default_dock_size = default_dock_size;
    }
}

fn handle_color_picker(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiColorPickerData,
) {
    let mode = data.mode;
    let mut any_changed = false;

    let mut slider_values = [0.0_f32; 4];
    for (index, slider_entity) in data.slider_entities.iter().enumerate() {
        if let Some(UiWidgetState::Slider(slider_data)) = world.get_ui_widget_state(*slider_entity)
        {
            if slider_data.changed {
                any_changed = true;
            }
            slider_values[index] = slider_data.value;
        }
    }

    let color = match mode {
        crate::ecs::ui::components::ColorPickerMode::Rgb => Vec4::new(
            slider_values[0],
            slider_values[1],
            slider_values[2],
            slider_values[3],
        ),
        crate::ecs::ui::components::ColorPickerMode::Hsv => {
            let hsv = crate::ecs::ui::color::Hsva {
                hue: slider_values[0],
                saturation: slider_values[1],
                value: slider_values[2],
                alpha: slider_values[3],
            };
            hsv.to_rgba()
        }
    };

    if any_changed && let Some(swatch_color) = world.get_ui_node_color_mut(data.swatch_entity) {
        swatch_color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(color);
    }

    if let Some(UiWidgetState::ColorPicker(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.color = color;
        widget_data.changed = any_changed;
    }
    if any_changed {
        world
            .resources
            .retained_ui
            .frame_events
            .push(crate::ecs::ui::resources::UiEvent::ColorPickerChanged { entity, color });
    }
}

fn handle_selectable_label(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiSelectableLabelData,
) {
    if let Some(UiWidgetState::SelectableLabel(widget_data)) = world.get_ui_widget_state_mut(entity)
    {
        widget_data.changed = false;
    }

    if interaction.clicked {
        let new_selected = !data.selected;

        if let Some(group_id) = data.group_id
            && new_selected
        {
            let siblings = world
                .resources
                .retained_ui
                .selectable_label_groups
                .get(&group_id)
                .cloned()
                .unwrap_or_default();
            for other in siblings {
                if other == entity {
                    continue;
                }
                let other_data = world.get_ui_widget_state(other).cloned();
                if let Some(UiWidgetState::SelectableLabel(other_sl)) = other_data
                    && other_sl.selected
                {
                    world.ui_set_selected(other, false);
                    if let Some(UiWidgetState::SelectableLabel(wd)) =
                        world.get_ui_widget_state_mut(other)
                    {
                        wd.changed = true;
                    }
                }
            }
        }

        world.ui_set_selected(entity, new_selected);
        if let Some(UiWidgetState::SelectableLabel(widget_data)) =
            world.get_ui_widget_state_mut(entity)
        {
            widget_data.selected = new_selected;
            widget_data.changed = true;
        }

        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::SelectableLabelClicked {
                entity,
                selected: new_selected,
            },
        );
    }
}

#[allow(clippy::too_many_arguments)]
fn handle_drag_value(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiDragValueData,
    focused_entity: Option<freecs::Entity>,
    frame_chars: &[char],
    frame_keys: &[(KeyCode, bool)],
    ctrl_held: bool,
    shift_held: bool,
    mouse_position: Vec2,
    current_time: f64,
    dpi_scale: f32,
) {
    let is_focused = focused_entity == Some(entity);
    let mut value = data.value;
    let mut changed = false;
    let mut editing = data.editing;
    let mut edit_text = data.edit_text.clone();
    let mut cursor_position = data.cursor_position;
    let mut selection_start = data.selection_start;
    let mut cursor_blink_timer = data.cursor_blink_timer;
    let mut scroll_offset = data.scroll_offset;
    let mut drag_start_value = data.drag_start_value;
    let mut undo_stack = data.undo_stack.clone();
    let mut clear_focus = false;

    if editing && is_focused {
        let mut needs_snapshot = false;
        for character in frame_chars {
            if *character >= ' '
                && (character.is_ascii_digit() || *character == '.' || *character == '-')
            {
                if !needs_snapshot {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: edit_text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    needs_snapshot = true;
                }
                if let Some(sel_start) = selection_start {
                    let min = sel_start.min(cursor_position);
                    let max = sel_start.max(cursor_position);
                    let chars: Vec<char> = edit_text.chars().collect();
                    let mut new_text: String = chars[..min].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[max..].iter());
                    edit_text = new_text;
                    cursor_position = min + 1;
                    selection_start = None;
                } else {
                    let chars: Vec<char> = edit_text.chars().collect();
                    let mut new_text: String = chars[..cursor_position].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[cursor_position..].iter());
                    edit_text = new_text;
                    cursor_position += 1;
                }
                cursor_blink_timer = current_time;
            }
        }
        if needs_snapshot {
            undo_stack.push(
                crate::ecs::ui::components::TextSnapshot {
                    text: edit_text.clone(),
                    cursor_position,
                    selection_start,
                },
                current_time,
            );
        }

        for (key, is_pressed) in frame_keys {
            if !is_pressed {
                continue;
            }
            let chars: Vec<char> = edit_text.chars().collect();
            let len = chars.len();

            match key {
                KeyCode::Backspace => {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: edit_text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        edit_text = new_text;
                        cursor_position = min;
                        selection_start = None;
                    } else if cursor_position > 0 {
                        if ctrl_held {
                            let new_pos = prev_word_boundary(&edit_text, cursor_position);
                            let mut new_text: String = chars[..new_pos].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            edit_text = new_text;
                            cursor_position = new_pos;
                        } else {
                            let mut new_text: String =
                                chars[..cursor_position - 1].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            edit_text = new_text;
                            cursor_position -= 1;
                        }
                    }
                    undo_stack.push(
                        crate::ecs::ui::components::TextSnapshot {
                            text: edit_text.clone(),
                            cursor_position,
                            selection_start,
                        },
                        current_time,
                    );
                    cursor_blink_timer = current_time;
                }
                KeyCode::Delete => {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: edit_text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        edit_text = new_text;
                        cursor_position = min;
                        selection_start = None;
                    } else if cursor_position < len {
                        if ctrl_held {
                            let end_pos = next_word_boundary(&edit_text, cursor_position);
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[end_pos..].iter());
                            edit_text = new_text;
                        } else {
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[cursor_position + 1..].iter());
                            edit_text = new_text;
                        }
                    }
                    undo_stack.push(
                        crate::ecs::ui::components::TextSnapshot {
                            text: edit_text.clone(),
                            cursor_position,
                            selection_start,
                        },
                        current_time,
                    );
                    cursor_blink_timer = current_time;
                }
                KeyCode::ArrowLeft => {
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let min = selection_start.unwrap().min(cursor_position);
                        cursor_position = min;
                        selection_start = None;
                        cursor_blink_timer = current_time;
                        continue;
                    }
                    if ctrl_held {
                        cursor_position = prev_word_boundary(&edit_text, cursor_position);
                    } else {
                        cursor_position = cursor_position.saturating_sub(1);
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::ArrowRight => {
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let max = selection_start.unwrap().max(cursor_position);
                        cursor_position = max;
                        selection_start = None;
                        cursor_blink_timer = current_time;
                        continue;
                    }
                    if ctrl_held {
                        cursor_position = next_word_boundary(&edit_text, cursor_position);
                    } else if cursor_position < len {
                        cursor_position += 1;
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::Home => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    cursor_position = 0;
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::End => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    cursor_position = len;
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::KeyA if ctrl_held => {
                    selection_start = Some(0);
                    cursor_position = len;
                    cursor_blink_timer = current_time;
                }
                KeyCode::KeyZ if ctrl_held => {
                    if let Some(snapshot) = undo_stack.undo() {
                        edit_text = snapshot.text.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        cursor_blink_timer = current_time;
                    }
                }
                KeyCode::KeyY if ctrl_held => {
                    if let Some(snapshot) = undo_stack.redo() {
                        edit_text = snapshot.text.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        cursor_blink_timer = current_time;
                    }
                }
                KeyCode::KeyC if ctrl_held => {
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let selected: String =
                            edit_text.chars().skip(min).take(max - min).collect();
                        world.resources.retained_ui.clipboard_text = selected;
                    }
                }
                KeyCode::KeyX if ctrl_held => {
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let selected: String =
                            edit_text.chars().skip(min).take(max - min).collect();
                        world.resources.retained_ui.clipboard_text = selected;
                        undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                            text: edit_text.clone(),
                            cursor_position,
                            selection_start,
                        });
                        let chars: Vec<char> = edit_text.chars().collect();
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        edit_text = new_text;
                        cursor_position = min;
                        selection_start = None;
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: edit_text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                    }
                }
                KeyCode::KeyV if ctrl_held => {
                    let paste_text = world.resources.retained_ui.clipboard_text.clone();
                    if !paste_text.is_empty() {
                        undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                            text: edit_text.clone(),
                            cursor_position,
                            selection_start,
                        });
                        if let Some(sel_start) = selection_start {
                            let min = sel_start.min(cursor_position);
                            let max = sel_start.max(cursor_position);
                            let chars: Vec<char> = edit_text.chars().collect();
                            let mut new_text: String = chars[..min].iter().collect();
                            new_text.push_str(&paste_text);
                            new_text.extend(chars[max..].iter());
                            edit_text = new_text;
                            cursor_position = min + paste_text.chars().count();
                            selection_start = None;
                        } else {
                            let chars: Vec<char> = edit_text.chars().collect();
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.push_str(&paste_text);
                            new_text.extend(chars[cursor_position..].iter());
                            edit_text = new_text;
                            cursor_position += paste_text.chars().count();
                        }
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: edit_text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                    }
                }
                KeyCode::Escape => {
                    editing = false;
                    selection_start = None;
                    clear_focus = true;
                }
                KeyCode::Enter => {
                    if let Ok(parsed) = edit_text.parse::<f32>() {
                        value = parsed.clamp(data.min, data.max);
                        changed = true;
                    }
                    editing = false;
                    selection_start = None;
                    clear_focus = true;
                }
                _ => {}
            }
        }

        if interaction.clicked {
            let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
            if let Some(rect) = input_rect {
                let font_size = world
                    .resources
                    .retained_ui
                    .theme_state
                    .active_theme()
                    .font_size;
                let best_idx = world
                    .resources
                    .text_cache
                    .font_manager
                    .best_bitmap_font_for_size(font_size);
                let font_arc = world
                    .resources
                    .text_cache
                    .font_manager
                    .get_bitmap_font_arc(best_idx);
                if let Some(atlas) = font_arc {
                    let local_x = (mouse_position.x - rect.min.x) / dpi_scale - 8.0 + scroll_offset;
                    let new_pos = byte_index_at_x(&atlas, &edit_text, font_size, local_x);
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else {
                        selection_start = None;
                    }
                    cursor_position = new_pos;
                    cursor_blink_timer = current_time;
                }
            }
        }

        if interaction.double_clicked {
            let len = edit_text.chars().count();
            let pos = cursor_position.min(len);
            let word_start = prev_word_boundary(&edit_text, pos);
            let word_end = next_word_boundary(&edit_text, pos).min(len);
            selection_start = Some(word_start);
            cursor_position = word_end;
        }

        world
            .resources
            .text_cache
            .set_text(data.text_slot, &edit_text);
    } else if editing && !is_focused {
        if let Ok(parsed) = edit_text.parse::<f32>() {
            value = parsed.clamp(data.min, data.max);
            changed = true;
        }
        editing = false;
        selection_start = None;
    } else {
        if interaction.dragging
            && let Some(drag_start) = interaction.drag_start
        {
            let delta_x = mouse_position.x - drag_start.x;
            let pixel_threshold = 3.0;
            if delta_x.abs() > pixel_threshold {
                let new_value = (drag_start_value + (delta_x / dpi_scale) * data.speed)
                    .clamp(data.min, data.max);
                if (new_value - value).abs() > f32::EPSILON {
                    value = new_value;
                    changed = true;
                }
            }
        }

        if interaction.clicked && !interaction.dragging {
            editing = true;
            edit_text = format!("{:.prec$}", data.value, prec = data.precision);
            cursor_position = edit_text.chars().count();
            selection_start = Some(0);
            cursor_blink_timer = current_time;
            world.resources.retained_ui.focused_entity = Some(entity);
        }

        if interaction.pressed && !interaction.dragging && interaction.drag_start.is_some() {
            drag_start_value = value;
        }
    }

    if clear_focus {
        world.resources.retained_ui.focused_entity = None;
    }

    if !editing {
        let display = format!(
            "{}{:.prec$}{}",
            data.prefix,
            value,
            data.suffix,
            prec = data.precision
        );
        world
            .resources
            .text_cache
            .set_text(data.text_slot, &display);
        scroll_offset = 0.0;
    }

    let cursor_visible = editing && is_focused && ((current_time - cursor_blink_timer) % 1.0) < 0.5;
    if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity) {
        cursor_node.visible = cursor_visible;
    }

    let has_selection = editing
        && is_focused
        && selection_start.is_some()
        && selection_start != Some(cursor_position);
    if let Some(sel_node) = world.get_ui_layout_node_mut(data.selection_entity) {
        sel_node.visible = has_selection;
    }

    if editing {
        let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
        if let Some(rect) = input_rect {
            let font_size = world
                .resources
                .retained_ui
                .theme_state
                .active_theme()
                .font_size;
            let best_idx = world
                .resources
                .text_cache
                .font_manager
                .best_bitmap_font_for_size(font_size);
            let font_arc = world
                .resources
                .text_cache
                .font_manager
                .get_bitmap_font_arc(best_idx);
            if let Some(atlas) = font_arc {
                let text_before_cursor: String = edit_text.chars().take(cursor_position).collect();
                let cursor_x = measure_text_width(&atlas, &text_before_cursor, font_size);

                let visible_width = rect.width() / dpi_scale - 16.0;
                if cursor_x - scroll_offset > visible_width {
                    scroll_offset = cursor_x - visible_width;
                } else if cursor_x - scroll_offset < 0.0 {
                    scroll_offset = cursor_x;
                }

                let cursor_screen_x = cursor_x - scroll_offset;

                let mut sel_start_x = 0.0f32;
                let mut sel_end_x = 0.0f32;
                if has_selection {
                    let sel_start = selection_start.unwrap();
                    let sel_min = sel_start.min(cursor_position);
                    let sel_max = sel_start.max(cursor_position);
                    let text_before_sel: String = edit_text.chars().take(sel_min).collect();
                    let text_to_sel_end: String = edit_text.chars().take(sel_max).collect();
                    sel_start_x =
                        measure_text_width(&atlas, &text_before_sel, font_size) - scroll_offset;
                    sel_end_x =
                        measure_text_width(&atlas, &text_to_sel_end, font_size) - scroll_offset;
                }

                drop(atlas);

                if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity)
                    && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                        cursor_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
                {
                    window.position =
                        crate::ecs::ui::units::Ab(Vec2::new(8.0 + cursor_screen_x, 4.0)).into();
                }

                if has_selection
                    && let Some(sel_node) = world.get_ui_layout_node_mut(data.selection_entity)
                    && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                        sel_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
                {
                    window.position =
                        crate::ecs::ui::units::Ab(Vec2::new(8.0 + sel_start_x, 4.0)).into();
                    window.size = crate::ecs::ui::units::Ab(Vec2::new(
                        sel_end_x - sel_start_x,
                        rect.height() / dpi_scale - 8.0,
                    ))
                    .into();
                }
            }
        }
    }

    if let Some(up) = data.up_entity {
        let up_clicked = world
            .get_ui_node_interaction(up)
            .map(|i| i.clicked)
            .unwrap_or(false);
        if up_clicked {
            value = (value + data.step).min(data.max);
            changed = true;
        }
    }
    if let Some(down) = data.down_entity {
        let down_clicked = world
            .get_ui_node_interaction(down)
            .map(|i| i.clicked)
            .unwrap_or(false);
        if down_clicked {
            value = (value - data.step).max(data.min);
            changed = true;
        }
    }

    if let Some(interaction_comp) = world.get_ui_node_interaction_mut(entity) {
        if editing {
            interaction_comp.cursor_icon = Some(winit::window::CursorIcon::Text);
        } else {
            interaction_comp.cursor_icon = Some(winit::window::CursorIcon::EwResize);
        }
    }

    if let Some(UiWidgetState::DragValue(widget_data)) = world.get_ui_widget_state_mut(entity) {
        widget_data.value = value;
        widget_data.changed = changed;
        widget_data.editing = editing;
        widget_data.edit_text = edit_text;
        widget_data.cursor_position = cursor_position;
        widget_data.selection_start = selection_start;
        widget_data.cursor_blink_timer = cursor_blink_timer;
        widget_data.scroll_offset = scroll_offset;
        widget_data.drag_start_value = drag_start_value;
        widget_data.undo_stack = undo_stack;
    }

    if changed {
        world
            .resources
            .retained_ui
            .frame_events
            .push(crate::ecs::ui::resources::UiEvent::DragValueChanged { entity, value });
    }
}

fn handle_context_menu(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiContextMenuData,
) {
    if !data.open {
        if let Some(UiWidgetState::ContextMenu(widget_data)) = world.get_ui_widget_state_mut(entity)
        {
            widget_data.clicked_item = None;
        }
        return;
    }

    let use_defs = !data.item_defs.is_empty();
    let mut clicked_command = None;
    let mut should_close = false;

    if use_defs {
        clicked_command = check_defs_for_click(world, &data.item_defs);

        handle_submenu_hover(world, entity, &data.item_defs);
    } else {
        for (index, item_entity) in data.item_entities.iter().enumerate() {
            if *item_entity == freecs::Entity::default() {
                continue;
            }
            let item_clicked = world
                .get_ui_node_interaction(*item_entity)
                .map(|i| i.clicked)
                .unwrap_or(false);
            if item_clicked {
                clicked_command = Some(index);
                should_close = true;
                break;
            }
        }
    }

    if clicked_command.is_some() {
        should_close = true;
    }

    let frame_keys = &world.resources.retained_ui.frame_keys;
    for (key, is_pressed) in frame_keys {
        if *is_pressed && *key == KeyCode::Escape {
            should_close = true;
        }
    }

    let hovered = world.resources.retained_ui.hovered_entity;
    let mouse_state = world.resources.input.mouse.state;
    let left_just_pressed =
        mouse_state.contains(crate::ecs::input::resources::MouseState::LEFT_JUST_PRESSED);

    if left_just_pressed && let Some(hovered_ent) = hovered {
        let mut is_inside =
            data.item_entities.contains(&hovered_ent) || hovered_ent == data.popup_entity;
        if !is_inside && use_defs {
            is_inside = is_entity_in_submenu_popups(&data.item_defs, hovered_ent, world);
        }
        if !is_inside {
            should_close = true;
        }
    }

    if should_close {
        if let Some(node) = world.get_ui_layout_node_mut(data.popup_entity) {
            node.visible = false;
        }
        close_submenu_popups(world, &data.item_defs);
        if let Some(UiWidgetState::ContextMenu(widget_data)) = world.get_ui_widget_state_mut(entity)
        {
            widget_data.open = false;
            widget_data.clicked_item = clicked_command;
            close_submenu_defs_state(&mut widget_data.item_defs);
        }
        world.resources.retained_ui.active_context_menu = None;

        if let Some(cmd_id) = clicked_command {
            world.resources.retained_ui.frame_events.push(
                crate::ecs::ui::resources::UiEvent::ContextMenuItemClicked {
                    entity,
                    item_index: cmd_id,
                },
            );
        }
    }
}

fn check_defs_for_click(
    world: &World,
    defs: &[crate::ecs::ui::components::ContextMenuItemDef],
) -> Option<usize> {
    for def in defs {
        if let crate::ecs::ui::components::ContextMenuItemKind::Action = &def.kind
            && let Some(cmd_id) = def.command_id
        {
            let clicked = world
                .get_ui_node_interaction(def.row_entity)
                .map(|i| i.clicked)
                .unwrap_or(false);
            if clicked {
                return Some(cmd_id);
            }
        }
        if let crate::ecs::ui::components::ContextMenuItemKind::Submenu { children, .. } = &def.kind
            && let Some(cmd_id) = check_defs_for_click(world, children)
        {
            return Some(cmd_id);
        }
    }
    None
}

fn handle_submenu_hover(
    world: &mut World,
    menu_entity: freecs::Entity,
    defs: &[crate::ecs::ui::components::ContextMenuItemDef],
) {
    let hovered = world.resources.retained_ui.hovered_entity;

    let mut submenu_indices = Vec::new();
    for (index, def) in defs.iter().enumerate() {
        if let crate::ecs::ui::components::ContextMenuItemKind::Submenu { .. } = &def.kind {
            submenu_indices.push(index);
        }
    }

    for &sub_index in &submenu_indices {
        let def = &defs[sub_index];
        let row_hovered = hovered == Some(def.row_entity);

        if let crate::ecs::ui::components::ContextMenuItemKind::Submenu {
            popup_entity,
            open,
            children,
            ..
        } = &def.kind
        {
            let child_popup = *popup_entity;
            let was_open = *open;

            if row_hovered && !was_open {
                let row_rect = world
                    .get_ui_layout_node(def.row_entity)
                    .map(|n| n.computed_rect);
                if let Some(rect) = row_rect {
                    let dpi_scale = world.resources.window.cached_scale_factor;
                    let pos = Vec2::new(rect.max.x, rect.min.y) / dpi_scale;
                    if let Some(node) = world.get_ui_layout_node_mut(child_popup)
                        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                            node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
                    {
                        window.position = crate::ecs::ui::units::Ab(pos).into();
                        node.visible = true;
                    }
                }
                if let Some(UiWidgetState::ContextMenu(menu_data)) =
                    world.get_ui_widget_state_mut(menu_entity)
                    && let Some(item_def) = menu_data.item_defs.get_mut(sub_index)
                    && let crate::ecs::ui::components::ContextMenuItemKind::Submenu { open, .. } =
                        &mut item_def.kind
                {
                    *open = true;
                }
            } else if !row_hovered && was_open {
                let popup_hovered = hovered == Some(child_popup);
                let child_item_hovered =
                    is_entity_in_submenu_popups(children, hovered.unwrap_or_default(), world)
                        || popup_hovered;

                if !child_item_hovered {
                    if let Some(node) = world.get_ui_layout_node_mut(child_popup) {
                        node.visible = false;
                    }
                    close_submenu_popups(world, children);
                    if let Some(UiWidgetState::ContextMenu(menu_data)) =
                        world.get_ui_widget_state_mut(menu_entity)
                        && let Some(item_def) = menu_data.item_defs.get_mut(sub_index)
                        && let crate::ecs::ui::components::ContextMenuItemKind::Submenu {
                            open,
                            children,
                            ..
                        } = &mut item_def.kind
                    {
                        *open = false;
                        close_submenu_defs_state(children);
                    }
                }
            }

            if was_open {
                let current_defs = if let Some(UiWidgetState::ContextMenu(menu_data)) =
                    world.get_ui_widget_state(menu_entity)
                {
                    if let Some(item_def) = menu_data.item_defs.get(sub_index) {
                        if let crate::ecs::ui::components::ContextMenuItemKind::Submenu {
                            children,
                            ..
                        } = &item_def.kind
                        {
                            Some(children.clone())
                        } else {
                            None
                        }
                    } else {
                        None
                    }
                } else {
                    None
                };
                if let Some(child_defs) = current_defs {
                    handle_submenu_hover(world, menu_entity, &child_defs);
                }
            }
        }
    }
}

fn is_entity_in_submenu_popups(
    defs: &[crate::ecs::ui::components::ContextMenuItemDef],
    target: freecs::Entity,
    world: &World,
) -> bool {
    for def in defs {
        if def.row_entity == target {
            return true;
        }
        if let crate::ecs::ui::components::ContextMenuItemKind::Submenu {
            popup_entity,
            children,
            ..
        } = &def.kind
        {
            if *popup_entity == target {
                return true;
            }
            let popup_rect = world
                .get_ui_layout_node(*popup_entity)
                .map(|n| n.computed_rect);
            if let Some(rect) = popup_rect {
                let mouse_pos = world.resources.input.mouse.position;
                if rect.contains(mouse_pos) {
                    return true;
                }
            }
            if is_entity_in_submenu_popups(children, target, world) {
                return true;
            }
        }
    }
    false
}

fn close_submenu_popups(
    world: &mut World,
    defs: &[crate::ecs::ui::components::ContextMenuItemDef],
) {
    for def in defs {
        if let crate::ecs::ui::components::ContextMenuItemKind::Submenu {
            popup_entity,
            children,
            ..
        } = &def.kind
        {
            if let Some(node) = world.get_ui_layout_node_mut(*popup_entity) {
                node.visible = false;
            }
            close_submenu_popups(world, children);
        }
    }
}

fn close_submenu_defs_state(defs: &mut [crate::ecs::ui::components::ContextMenuItemDef]) {
    for def in defs.iter_mut() {
        if let crate::ecs::ui::components::ContextMenuItemKind::Submenu { open, children, .. } =
            &mut def.kind
        {
            *open = false;
            close_submenu_defs_state(children);
        }
    }
}

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.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.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
                .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.get_ui_widget_state(*prev).cloned();
                        if let Some(UiWidgetState::TreeNode(prev_node)) = prev_data {
                            if let Some(color) = world.get_ui_node_color_mut(prev_node.row_entity) {
                                color.colors[crate::ecs::ui::state::UiSelected::INDEX] = None;
                            }
                            if let Some(weights) =
                                world.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.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.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.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.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.get_ui_widget_state(*ne)
                    && nd.selected
                {
                    new_selection.push(*ne);
                }
            }
            if let Some(UiWidgetState::TreeView(wd)) = world.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.get_ui_layout_node_mut(tree_node.children_container) {
                    node.visible = new_expanded;
                }
                if let Some(UiWidgetState::TreeNode(wd)) =
                    world.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.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.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.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.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.get_ui_layout_node_mut(tn.children_container) {
                            node.visible = true;
                        }
                        if let Some(UiWidgetState::TreeNode(wd)) =
                            world.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.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.get_ui_layout_node_mut(tn.children_container)
                            {
                                node.visible = false;
                            }
                            if let Some(UiWidgetState::TreeNode(wd)) =
                                world.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.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.get_ui_layout_node_mut(tn.children_container) {
                            node.visible = new_expanded;
                        }
                        if let Some(UiWidgetState::TreeNode(wd)) =
                            world.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.get_ui_widget_state(old).cloned()
                {
                    if let Some(color) = world.get_ui_node_color_mut(old_tn.row_entity) {
                        color.colors[crate::ecs::ui::state::UiSelected::INDEX] = None;
                    }
                    if let Some(weights) = world.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.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.get_ui_widget_state(new_node).cloned()
                {
                    if let Some(color) = world.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.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.get_ui_widget_state_mut(new_node) {
                    wd.selected = true;
                }

                if let Some(UiWidgetState::TreeView(wd)) =
                    world.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,
                    },
                );
            }
        }
    }
}

fn handle_modal_dialog(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiModalDialogData,
    frame_keys: &[(KeyCode, bool)],
) {
    if !world.get_ui_layout_node(entity).is_some_and(|n| n.visible) {
        if let Some(UiWidgetState::ModalDialog(widget_data)) = world.get_ui_widget_state_mut(entity)
        {
            widget_data.result = None;
        }
        return;
    }

    let mut result = None;

    if let Some(ok) = data.ok_button
        && world.get_ui_node_interaction(ok).is_some_and(|i| i.clicked)
    {
        result = Some(true);
    }
    if let Some(cancel) = data.cancel_button
        && world
            .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.get_ui_layout_node_mut(data.backdrop_entity) {
            node.visible = false;
        }
        if let Some(node) = world.get_ui_layout_node_mut(entity) {
            node.visible = false;
        }
        if let Some(UiWidgetState::ModalDialog(widget_data)) = world.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 });
    }
}

pub fn ui_event_bubble_system(world: &mut World) {
    if !world.resources.retained_ui.enabled {
        return;
    }

    world.resources.retained_ui.bubbled_events.clear();

    let events = world.resources.retained_ui.frame_events.clone();

    if !world.resources.children_cache_valid {
        world.validate_and_rebuild_children_cache();
    }

    for event in &events {
        if !event.bubbles() {
            continue;
        }
        let target = event.target_entity();
        let mut current = target;

        loop {
            let parent_opt = world.get_parent(current).and_then(|p| p.0);

            let Some(parent) = parent_opt else {
                break;
            };

            world.resources.retained_ui.bubbled_events.push(
                crate::ecs::ui::resources::BubbledUiEvent {
                    event: event.clone(),
                    target,
                    ancestor: parent,
                    stopped: false,
                },
            );

            current = parent;
        }
    }
}

struct DataGridContext<'a> {
    ctrl_held: bool,
    shift_held: bool,
    frame_keys: &'a [(KeyCode, bool)],
    mouse_position: Vec2,
    mouse_just_pressed: bool,
    mouse_down: bool,
}

fn handle_data_grid(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
    ctx: &DataGridContext<'_>,
) {
    if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
        grid.sort_changed = false;
        grid.selection_changed = false;
    }

    let mut clicked_row = false;
    for (pool_index, pool_row) in data.pool_rows.iter().enumerate() {
        let row_interaction = snapshot_interaction(world, pool_row.row_entity);
        if !row_interaction.clicked {
            continue;
        }
        clicked_row = true;

        let visible_row = data.visible_start + pool_index;
        let effective_row_count = data
            .filtered_indices
            .as_ref()
            .map_or(data.total_rows, |indices| indices.len());
        if visible_row >= effective_row_count {
            continue;
        }
        let data_row = visible_row;

        if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
            grid.focused = true;
            if ctx.ctrl_held {
                if grid.selected_rows.contains(&data_row) {
                    grid.selected_rows.remove(&data_row);
                } else {
                    grid.selected_rows.insert(data_row);
                }
                grid.selection_anchor = Some(data_row);
            } else if ctx.shift_held
                && let Some(anchor) = grid.selection_anchor
            {
                let range_start = anchor.min(data_row);
                let range_end = anchor.max(data_row);
                grid.selected_rows.clear();
                for row in range_start..=range_end {
                    grid.selected_rows.insert(row);
                }
            } else {
                grid.selected_rows.clear();
                grid.selected_rows.insert(data_row);
                grid.selection_anchor = Some(data_row);
            }
            grid.selection_changed = true;
        }
        break;
    }

    if ctx.mouse_just_pressed && !clicked_row {
        let grid_rect = world
            .get_ui_layout_node(entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = grid_rect {
            let inside = ctx.mouse_position.x >= rect.min.x
                && ctx.mouse_position.x <= rect.max.x
                && ctx.mouse_position.y >= rect.min.y
                && ctx.mouse_position.y <= rect.max.y;
            if !inside
                && let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity)
            {
                grid.focused = false;
            }
        }
    }

    for (column_index, header_entity) in data.header_entities.iter().enumerate() {
        if column_index < data.columns.len()
            && data.columns[column_index].sortable
            && snapshot_interaction(world, *header_entity).clicked
        {
            if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
                if grid.sort_column == Some(column_index) {
                    if grid.sort_ascending {
                        grid.sort_ascending = false;
                    } else {
                        grid.sort_column = None;
                    }
                } else {
                    grid.sort_column = Some(column_index);
                    grid.sort_ascending = true;
                }
                grid.sort_changed = true;
            }

            data_grid_update_sort_indicators(world, entity, data);
            break;
        }
    }

    for (pool_index, pool_row) in data.pool_rows.iter().enumerate() {
        let row_interaction = snapshot_interaction(world, pool_row.row_entity);
        if !row_interaction.double_clicked {
            continue;
        }

        let visible_row = data.visible_start + pool_index;
        let effective_row_count = data
            .filtered_indices
            .as_ref()
            .map_or(data.total_rows, |indices| indices.len());
        if visible_row >= effective_row_count {
            continue;
        }

        let mouse_x = ctx.mouse_position.x;
        let mut col_found = None;
        for (col_idx, cell_entity) in pool_row.cell_entities.iter().enumerate() {
            if col_idx < data.columns.len()
                && data.columns[col_idx].editable
                && let Some(cell_node) = world.get_ui_layout_node(*cell_entity)
            {
                let cell_rect = cell_node.computed_rect;
                if mouse_x >= cell_rect.min.x && mouse_x <= cell_rect.max.x {
                    col_found = Some(col_idx);
                    break;
                }
            }
        }

        if let Some(column) = col_found {
            world.ui_data_grid_start_edit(entity, visible_row, column);
        }
        break;
    }

    if let Some(editing_input) = data.editing_input_entity
        && data.editing_cell.is_some()
    {
        let focused = world.resources.retained_ui.focused_entity == Some(editing_input);
        let mut commit = false;
        let mut cancel = false;

        for &(key, pressed) in ctx.frame_keys {
            if !pressed {
                continue;
            }
            match key {
                KeyCode::Enter => commit = true,
                KeyCode::Escape => cancel = true,
                _ => {}
            }
        }

        if !focused && !commit && !cancel {
            commit = true;
        }

        if commit {
            world.ui_data_grid_stop_edit(entity, true);
        } else if cancel {
            world.ui_data_grid_stop_edit(entity, false);
        }
    }

    handle_data_grid_column_resize(world, entity, data, ctx);

    let is_focused = if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state(entity)
    {
        grid.focused
    } else {
        false
    };

    if is_focused && data.total_rows > 0 {
        handle_data_grid_keyboard(world, entity, data, ctx);
    }

    update_data_grid_visibility(world, entity, data);
}

fn data_grid_update_sort_indicators(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
) {
    let (sort_column, sort_ascending) =
        if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state(entity) {
            (grid.sort_column, grid.sort_ascending)
        } else {
            return;
        };

    for (index, text_slot) in data.header_text_slots.iter().enumerate() {
        let base_label = &data.columns[index].label;
        let display = match sort_column {
            Some(col) if col == index => {
                let indicator = if sort_ascending {
                    " \u{25B2}"
                } else {
                    " \u{25BC}"
                };
                format!("{base_label}{indicator}")
            }
            _ => base_label.clone(),
        };
        world.resources.text_cache.set_text(*text_slot, &display);
    }
}

fn handle_command_palette(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiCommandPaletteData,
    frame_keys: &[(winit::keyboard::KeyCode, bool)],
) {
    if !data.open {
        if let Some(UiWidgetState::CommandPalette(palette)) = world.get_ui_widget_state_mut(entity)
        {
            palette.executed_command = None;
        }
        return;
    }

    let text_changed = if let Some(UiWidgetState::TextInput(ti)) =
        world.get_ui_widget_state(data.text_input_entity)
    {
        ti.changed
    } else {
        false
    };

    if text_changed {
        let new_text = if let Some(UiWidgetState::TextInput(ti)) =
            world.get_ui_widget_state(data.text_input_entity)
        {
            ti.text.clone()
        } else {
            String::new()
        };

        let filter_lower = new_text.to_lowercase();
        let new_indices: Vec<usize> = data
            .commands
            .iter()
            .enumerate()
            .filter(|(_, cmd)| {
                if filter_lower.is_empty() {
                    return true;
                }
                cmd.label.to_lowercase().contains(&filter_lower)
                    || cmd.category.to_lowercase().contains(&filter_lower)
            })
            .map(|(index, _)| index)
            .collect();

        if let Some(UiWidgetState::CommandPalette(palette)) = world.get_ui_widget_state_mut(entity)
        {
            palette.filter_text = new_text;
            palette.filtered_indices = new_indices;
            palette.selected_index = 0;
        }

        world.ui_command_palette_rebuild_results(entity);
    }

    let filtered_count = data.filtered_indices.len();
    let mut execute_index = None;
    let mut should_close = false;

    for &(key, pressed) in frame_keys {
        if !pressed {
            continue;
        }
        match key {
            KeyCode::ArrowUp => {
                if let Some(UiWidgetState::CommandPalette(palette)) =
                    world.get_ui_widget_state_mut(entity)
                {
                    palette.selected_index = palette.selected_index.saturating_sub(1);
                }
            }
            KeyCode::ArrowDown => {
                if filtered_count > 0
                    && let Some(UiWidgetState::CommandPalette(palette)) =
                        world.get_ui_widget_state_mut(entity)
                {
                    palette.selected_index = (palette.selected_index + 1).min(filtered_count - 1);
                }
            }
            KeyCode::Enter => {
                if filtered_count > 0 {
                    let sel = if let Some(UiWidgetState::CommandPalette(palette)) =
                        world.get_ui_widget_state(entity)
                    {
                        palette.selected_index
                    } else {
                        0
                    };
                    if sel < filtered_count {
                        execute_index = Some(data.filtered_indices[sel]);
                    }
                }
            }
            KeyCode::Escape => {
                should_close = true;
            }
            _ => {}
        }
    }

    for (pool_index, &row_entity) in data.result_entities.iter().enumerate() {
        if pool_index < filtered_count {
            let clicked = world
                .get_ui_node_interaction(row_entity)
                .map(|i| i.clicked)
                .unwrap_or(false);
            if clicked {
                execute_index = Some(data.filtered_indices[pool_index]);
            }
        }
    }

    let selected_index =
        if let Some(UiWidgetState::CommandPalette(palette)) = world.get_ui_widget_state(entity) {
            palette.selected_index
        } else {
            0
        };

    let accent_color = world
        .resources
        .retained_ui
        .theme_state
        .active_theme()
        .accent_color;

    for (pool_index, &row_entity) in data.result_entities.iter().enumerate() {
        if pool_index < filtered_count {
            let is_selected = pool_index == selected_index;
            let bg = if is_selected {
                Vec4::new(accent_color.x, accent_color.y, accent_color.z, 0.3)
            } else {
                Vec4::new(0.0, 0.0, 0.0, 0.0)
            };
            if let Some(color) = world.get_ui_node_color_mut(row_entity) {
                color.colors[crate::ecs::ui::state::UiBase::INDEX] = Some(bg);
            }
        }
    }

    let backdrop_clicked = world
        .get_ui_node_interaction(data.backdrop_entity)
        .map(|i| i.clicked)
        .unwrap_or(false);
    if backdrop_clicked {
        should_close = true;
    }

    if execute_index.is_some() {
        should_close = true;
    }

    if should_close {
        if let Some(node) = world.get_ui_layout_node_mut(data.backdrop_entity) {
            node.visible = false;
        }
        if let Some(node) = world.get_ui_layout_node_mut(entity) {
            node.visible = false;
        }
        if let Some(UiWidgetState::CommandPalette(palette)) = world.get_ui_widget_state_mut(entity)
        {
            palette.open = false;
            palette.executed_command = execute_index;
        }
        if world.resources.retained_ui.focused_entity == Some(data.text_input_entity) {
            world.resources.retained_ui.focused_entity = None;
        }

        if let Some(cmd_index) = execute_index {
            world.resources.retained_ui.frame_events.push(
                crate::ecs::ui::resources::UiEvent::CommandPaletteExecuted {
                    entity,
                    command_index: cmd_index,
                },
            );
        }
    }
}

fn handle_property_grid(
    world: &mut World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiPropertyGridData,
    mouse_position: Vec2,
    mouse_just_pressed: bool,
    mouse_down: bool,
) {
    let currently_resizing =
        if let Some(UiWidgetState::PropertyGrid(grid)) = world.get_ui_widget_state(entity) {
            grid.resize_active
        } else {
            return;
        };

    if currently_resizing {
        world.resources.retained_ui.requested_cursor = Some(winit::window::CursorIcon::ColResize);

        if mouse_down {
            let (start_x, start_width) = if let Some(UiWidgetState::PropertyGrid(grid)) =
                world.get_ui_widget_state(entity)
            {
                (grid.resize_start_x, grid.resize_start_width)
            } else {
                return;
            };

            let grid_rect = world
                .get_ui_layout_node(entity)
                .map(|node| node.computed_rect);
            let grid_width = grid_rect.map(|r| r.width()).unwrap_or(300.0);

            let delta = mouse_position.x - start_x;
            let new_width = (start_width + delta).clamp(30.0, grid_width - 50.0);

            if let Some(UiWidgetState::PropertyGrid(grid)) = world.get_ui_widget_state_mut(entity) {
                grid.label_width = new_width;
            }

            let (viewport_width, viewport_height) = world
                .resources
                .window
                .cached_viewport_size
                .map(|(w, h)| (w as f32, h as f32))
                .unwrap_or((800.0, 600.0));
            let grid_height = grid_rect.map(|r| r.height()).unwrap_or(0.0);

            for label_entity in &data.label_entities {
                if let Some(node) = world.get_ui_layout_node_mut(*label_entity)
                    && let Some(ref mut child_size) = node.flow_child_size
                {
                    let mut evaluated =
                        child_size.evaluate(&crate::ecs::ui::units::UiEvalContext {
                            parent_width: grid_width,
                            parent_height: grid_height,
                            viewport_width,
                            viewport_height,
                            font_size: 16.0,
                            absolute_scale: 1.0,
                        });
                    evaluated.x = new_width;
                    *child_size = crate::ecs::ui::units::Ab(evaluated).into();
                }
            }
        } else if let Some(UiWidgetState::PropertyGrid(grid)) =
            world.get_ui_widget_state_mut(entity)
        {
            grid.resize_active = false;
        }
        return;
    }

    let edge_threshold = 5.0;
    let mut near_edge = false;

    for label_entity in &data.label_entities {
        let label_rect = world
            .get_ui_layout_node(*label_entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = label_rect {
            let near_right_edge = (mouse_position.x - rect.max.x).abs() < edge_threshold
                && mouse_position.y >= rect.min.y
                && mouse_position.y <= rect.max.y;
            if near_right_edge {
                near_edge = true;
                if mouse_just_pressed {
                    if let Some(UiWidgetState::PropertyGrid(grid)) =
                        world.get_ui_widget_state_mut(entity)
                    {
                        grid.resize_active = true;
                        grid.resize_start_x = mouse_position.x;
                        grid.resize_start_width = grid.label_width;
                    }
                    return;
                }
                break;
            }
        }
    }

    if near_edge {
        world.resources.retained_ui.requested_cursor = Some(winit::window::CursorIcon::ColResize);
    }
}

fn handle_data_grid_column_resize(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
    ctx: &DataGridContext<'_>,
) {
    let currently_resizing =
        if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state(entity) {
            grid.resize_column
        } else {
            None
        };

    if let Some(resize_col) = currently_resizing {
        world.resources.retained_ui.requested_cursor = Some(winit::window::CursorIcon::ColResize);

        if ctx.mouse_down {
            let (start_x, start_width) =
                if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state(entity) {
                    (grid.resize_start_x, grid.resize_start_width)
                } else {
                    return;
                };

            let delta = ctx.mouse_position.x - start_x;
            let new_width = (start_width + delta).max(30.0);

            if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
                grid.columns[resize_col].width = new_width;
            }

            let vp = world
                .resources
                .window
                .cached_viewport_size
                .map(|(w, h)| (w as f32, h as f32))
                .unwrap_or((800.0, 600.0));

            if let Some(header_entity) = data.header_entities.get(resize_col)
                && let Some(node) = world.get_ui_layout_node_mut(*header_entity)
                && let Some(ref mut child_size) = node.flow_child_size
            {
                let mut evaluated = child_size.evaluate(&crate::ecs::ui::units::UiEvalContext {
                    parent_width: vp.0,
                    parent_height: vp.1,
                    viewport_width: vp.0,
                    viewport_height: vp.1,
                    font_size: 16.0,
                    absolute_scale: 1.0,
                });
                evaluated.x = new_width;
                *child_size = crate::ecs::ui::units::Ab(evaluated).into();
            }

            for pool_row in &data.pool_rows {
                if let Some(&cell_entity) = pool_row.cell_entities.get(resize_col)
                    && let Some(node) = world.get_ui_layout_node_mut(cell_entity)
                    && let Some(ref mut child_size) = node.flow_child_size
                {
                    let mut evaluated =
                        child_size.evaluate(&crate::ecs::ui::units::UiEvalContext {
                            parent_width: vp.0,
                            parent_height: vp.1,
                            viewport_width: vp.0,
                            viewport_height: vp.1,
                            font_size: 16.0,
                            absolute_scale: 1.0,
                        });
                    evaluated.x = new_width;
                    *child_size = crate::ecs::ui::units::Ab(evaluated).into();
                }
            }
        } else if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
            grid.resize_column = None;
        }
        return;
    }

    let edge_threshold = 5.0;
    let mut near_any_edge = false;

    for (column_index, header_entity) in data.header_entities.iter().enumerate() {
        let header_rect = world
            .get_ui_layout_node(*header_entity)
            .map(|node| node.computed_rect);
        if let Some(rect) = header_rect {
            let near_right_edge = (ctx.mouse_position.x - rect.max.x).abs() < edge_threshold
                && ctx.mouse_position.y >= rect.min.y
                && ctx.mouse_position.y <= rect.max.y;
            if near_right_edge {
                near_any_edge = true;
                if ctx.mouse_just_pressed {
                    if let Some(UiWidgetState::DataGrid(grid)) =
                        world.get_ui_widget_state_mut(entity)
                    {
                        grid.resize_column = Some(column_index);
                        grid.resize_start_x = ctx.mouse_position.x;
                        grid.resize_start_width = data.columns[column_index].width;
                    }
                    return;
                }
                break;
            }
        }
    }

    if near_any_edge {
        world.resources.retained_ui.requested_cursor = Some(winit::window::CursorIcon::ColResize);
    }
}

fn handle_data_grid_keyboard(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
    ctx: &DataGridContext<'_>,
) {
    let total_rows = data
        .filtered_indices
        .as_ref()
        .map_or(data.total_rows, |indices| indices.len());
    if total_rows == 0 {
        return;
    }

    let current_anchor =
        if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state(entity) {
            grid.selection_anchor
        } else {
            return;
        };

    let anchor = current_anchor.unwrap_or(0);
    let mut new_anchor = None;

    for &(key, pressed) in ctx.frame_keys {
        if !pressed {
            continue;
        }
        match key {
            KeyCode::ArrowUp => {
                new_anchor = Some(anchor.saturating_sub(1));
            }
            KeyCode::ArrowDown => {
                new_anchor = Some((anchor + 1).min(total_rows - 1));
            }
            KeyCode::PageUp => {
                new_anchor = Some(anchor.saturating_sub(data.pool_size));
            }
            KeyCode::PageDown => {
                new_anchor = Some((anchor + data.pool_size).min(total_rows - 1));
            }
            KeyCode::Home => {
                new_anchor = Some(0);
            }
            KeyCode::End => {
                new_anchor = Some(total_rows - 1);
            }
            KeyCode::KeyA if ctx.ctrl_held => {
                if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
                    grid.selected_rows.clear();
                    for row in 0..total_rows {
                        grid.selected_rows.insert(row);
                    }
                    grid.selection_changed = true;
                }
                return;
            }
            _ => {}
        }
    }

    if let Some(target) = new_anchor {
        if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
            if ctx.shift_held {
                let range_start = anchor.min(target);
                let range_end = anchor.max(target);
                grid.selected_rows.clear();
                for row in range_start..=range_end {
                    grid.selected_rows.insert(row);
                }
            } else {
                grid.selected_rows.clear();
                grid.selected_rows.insert(target);
                grid.selection_anchor = Some(target);
            }
            grid.selection_changed = true;
        }

        let row_height = data.row_height;
        let target_offset = target as f32 * row_height;
        if let Some(UiWidgetState::ScrollArea(scroll_data)) =
            world.get_ui_widget_state(data.scroll_entity)
        {
            let scroll_offset = scroll_data.scroll_offset;
            let visible_height = scroll_data.visible_height;
            if target_offset < scroll_offset {
                if let Some(UiWidgetState::ScrollArea(scroll_data)) =
                    world.get_ui_widget_state_mut(data.scroll_entity)
                {
                    scroll_data.scroll_offset = target_offset;
                }
            } else if target_offset + row_height > scroll_offset + visible_height
                && let Some(UiWidgetState::ScrollArea(scroll_data)) =
                    world.get_ui_widget_state_mut(data.scroll_entity)
            {
                scroll_data.scroll_offset = target_offset + row_height - visible_height;
            }
        }
    }
}

fn update_data_grid_visibility(
    world: &mut crate::ecs::world::World,
    entity: freecs::Entity,
    data: &crate::ecs::ui::components::UiDataGridData,
) {
    let scroll_offset = if let Some(UiWidgetState::ScrollArea(scroll_data)) =
        world.get_ui_widget_state(data.scroll_entity)
    {
        scroll_data.scroll_offset
    } else {
        0.0
    };

    let effective_row_count = data
        .filtered_indices
        .as_ref()
        .map_or(data.total_rows, |indices| indices.len());

    let row_height = data.row_height;
    let visible_start = if row_height > 0.0 {
        (scroll_offset / row_height).floor() as usize
    } else {
        0
    };
    let visible_start = visible_start.min(effective_row_count.saturating_sub(data.pool_size));

    let top_height = visible_start as f32 * row_height;
    if let Some(node) = world.get_ui_layout_node_mut(data.top_spacer) {
        node.flow_child_size =
            Some(crate::ecs::ui::units::Ab(nalgebra_glm::Vec2::new(0.0, top_height)).into());
    }

    let visible_end = (visible_start + data.pool_size).min(effective_row_count);
    let bottom_rows = effective_row_count.saturating_sub(visible_end);
    let bottom_height = bottom_rows as f32 * row_height;
    if let Some(node) = world.get_ui_layout_node_mut(data.bottom_spacer) {
        node.flow_child_size =
            Some(crate::ecs::ui::units::Ab(nalgebra_glm::Vec2::new(0.0, bottom_height)).into());
    }

    let selected_rows =
        if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state(entity) {
            grid.selected_rows.clone()
        } else {
            std::collections::HashSet::new()
        };

    for (pool_index, pool_row) in data.pool_rows.iter().enumerate() {
        let visible_row = visible_start + pool_index;
        let visible = visible_row < effective_row_count;

        if let Some(node) = world.get_ui_layout_node_mut(pool_row.row_entity) {
            node.visible = visible;
        }

        if visible {
            let is_selected = selected_rows.contains(&visible_row);
            if let Some(weights) = world.get_ui_state_weights_mut(pool_row.row_entity) {
                weights.weights[crate::ecs::ui::state::UiSelected::INDEX] =
                    if is_selected { 1.0 } else { 0.0 };
            }
        }
    }

    if let Some(UiWidgetState::DataGrid(grid)) = world.get_ui_widget_state_mut(entity) {
        grid.visible_start = visible_start;
    }
}

#[allow(clippy::too_many_arguments)]
fn handle_text_area(
    world: &mut World,
    entity: freecs::Entity,
    interaction: &InteractionSnapshot,
    data: &crate::ecs::ui::components::UiTextAreaData,
    focused_entity: Option<freecs::Entity>,
    frame_chars: &[char],
    frame_keys: &[(KeyCode, bool)],
    ctrl_held: bool,
    shift_held: bool,
    mouse_position: Vec2,
    current_time: f64,
    dpi_scale: f32,
) {
    let is_focused = focused_entity == Some(entity);
    let mut text = data.text.clone();
    let mut cursor_position = data.cursor_position;
    let mut selection_start = data.selection_start;
    let mut changed = false;
    let mut cursor_blink_timer = data.cursor_blink_timer;
    let mut scroll_offset_y = data.scroll_offset_y;
    let mut clear_focus = false;
    let line_height = data.line_height;
    let mut undo_stack = data.undo_stack.clone();
    let mut needs_snapshot = false;
    let max_length = data.max_length;

    if is_focused {
        for character in frame_chars {
            if *character >= ' ' {
                if let Some(max) = max_length {
                    let result_len = if let Some(sel) = selection_start {
                        let selected = sel.max(cursor_position) - sel.min(cursor_position);
                        text.chars().count() - selected + 1
                    } else {
                        text.chars().count() + 1
                    };
                    if result_len > max {
                        continue;
                    }
                }
                if !needs_snapshot {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    needs_snapshot = true;
                }
                if let Some(sel_start) = selection_start {
                    let min = sel_start.min(cursor_position);
                    let max = sel_start.max(cursor_position);
                    let chars: Vec<char> = text.chars().collect();
                    let mut new_text: String = chars[..min].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[max..].iter());
                    text = new_text;
                    cursor_position = min + 1;
                    selection_start = None;
                } else {
                    let chars: Vec<char> = text.chars().collect();
                    let mut new_text: String = chars[..cursor_position].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[cursor_position..].iter());
                    text = new_text;
                    cursor_position += 1;
                }
                changed = true;
                cursor_blink_timer = current_time;
            }
        }

        if needs_snapshot {
            undo_stack.push(
                crate::ecs::ui::components::TextSnapshot {
                    text: text.clone(),
                    cursor_position,
                    selection_start,
                },
                current_time,
            );
        }

        for (key, is_pressed) in frame_keys {
            if !is_pressed {
                continue;
            }
            let chars: Vec<char> = text.chars().collect();
            let len = chars.len();

            match key {
                KeyCode::Backspace => {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    let mut did_change = false;
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        cursor_position = min;
                        selection_start = None;
                        did_change = true;
                    } else if cursor_position > 0 {
                        if ctrl_held {
                            let new_pos = prev_word_boundary(&text, cursor_position);
                            let mut new_text: String = chars[..new_pos].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            text = new_text;
                            cursor_position = new_pos;
                        } else {
                            let mut new_text: String =
                                chars[..cursor_position - 1].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            text = new_text;
                            cursor_position -= 1;
                        }
                        did_change = true;
                    }
                    if did_change {
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                        changed = true;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::Delete => {
                    undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                        text: text.clone(),
                        cursor_position,
                        selection_start,
                    });
                    let mut did_change = false;
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        cursor_position = min;
                        selection_start = None;
                        did_change = true;
                    } else if cursor_position < len {
                        if ctrl_held {
                            let end_pos = next_word_boundary(&text, cursor_position);
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[end_pos..].iter());
                            text = new_text;
                        } else {
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[cursor_position + 1..].iter());
                            text = new_text;
                        }
                        did_change = true;
                    }
                    if did_change {
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                        changed = true;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::Enter => {
                    if ctrl_held {
                        clear_focus = true;
                    } else {
                        undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                            text: text.clone(),
                            cursor_position,
                            selection_start,
                        });
                        if let Some(sel_start) = selection_start {
                            let min = sel_start.min(cursor_position);
                            let max = sel_start.max(cursor_position);
                            let chars_vec: Vec<char> = text.chars().collect();
                            let mut new_text: String = chars_vec[..min].iter().collect();
                            new_text.push('\n');
                            new_text.extend(chars_vec[max..].iter());
                            text = new_text;
                            cursor_position = min + 1;
                            selection_start = None;
                        } else {
                            let chars_vec: Vec<char> = text.chars().collect();
                            let mut new_text: String =
                                chars_vec[..cursor_position].iter().collect();
                            new_text.push('\n');
                            new_text.extend(chars_vec[cursor_position..].iter());
                            text = new_text;
                            cursor_position += 1;
                        }
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                        changed = true;
                        cursor_blink_timer = current_time;
                    }
                }
                KeyCode::ArrowLeft => {
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let min = selection_start.unwrap().min(cursor_position);
                        cursor_position = min;
                        selection_start = None;
                        cursor_blink_timer = current_time;
                        continue;
                    }
                    if ctrl_held {
                        cursor_position = prev_word_boundary(&text, cursor_position);
                    } else {
                        cursor_position = cursor_position.saturating_sub(1);
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::ArrowRight => {
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let max = selection_start.unwrap().max(cursor_position);
                        cursor_position = max;
                        selection_start = None;
                        cursor_blink_timer = current_time;
                        continue;
                    }
                    if ctrl_held {
                        cursor_position = next_word_boundary(&text, cursor_position);
                    } else if cursor_position < len {
                        cursor_position += 1;
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::ArrowUp => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, cur_col) = line_col_from_char_position(&text, cursor_position);
                    if cur_line > 0 {
                        cursor_position = char_position_from_line_col(&text, cur_line - 1, cur_col);
                    } else {
                        cursor_position = 0;
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::ArrowDown => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, cur_col) = line_col_from_char_position(&text, cursor_position);
                    let total_lines = line_count(&text);
                    if cur_line + 1 < total_lines {
                        cursor_position = char_position_from_line_col(&text, cur_line + 1, cur_col);
                    } else {
                        cursor_position = len;
                    }
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::Home => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, _) = line_col_from_char_position(&text, cursor_position);
                    cursor_position = line_start_char_index(&text, cur_line);
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::End => {
                    if shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, _) = line_col_from_char_position(&text, cursor_position);
                    let lt = line_text(&text, cur_line);
                    cursor_position = line_start_char_index(&text, cur_line) + lt.chars().count();
                    if !shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = current_time;
                }
                KeyCode::KeyA if ctrl_held => {
                    selection_start = Some(0);
                    cursor_position = len;
                    cursor_blink_timer = current_time;
                }
                KeyCode::KeyZ if ctrl_held => {
                    if let Some(snapshot) = undo_stack.undo() {
                        text = snapshot.text.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        changed = true;
                        cursor_blink_timer = current_time;
                    }
                }
                KeyCode::KeyY if ctrl_held => {
                    if let Some(snapshot) = undo_stack.redo() {
                        text = snapshot.text.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        changed = true;
                        cursor_blink_timer = current_time;
                    }
                }
                KeyCode::KeyC if ctrl_held => {
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let selected: String = text.chars().skip(min).take(max - min).collect();
                        world.resources.retained_ui.clipboard_text = selected;
                    }
                }
                KeyCode::KeyX if ctrl_held => {
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let selected: String = text.chars().skip(min).take(max - min).collect();
                        world.resources.retained_ui.clipboard_text = selected;
                        undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                            text: text.clone(),
                            cursor_position,
                            selection_start,
                        });
                        let chars: Vec<char> = text.chars().collect();
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        cursor_position = min;
                        selection_start = None;
                        changed = true;
                        undo_stack.push(
                            crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            },
                            current_time,
                        );
                    }
                }
                KeyCode::KeyV if ctrl_held => {
                    let mut paste_text = world.resources.retained_ui.clipboard_text.clone();
                    if !paste_text.is_empty() {
                        if let Some(max) = max_length {
                            let existing = text.chars().count();
                            let removed = selection_start
                                .map(|s| s.max(cursor_position) - s.min(cursor_position))
                                .unwrap_or(0);
                            let available = max.saturating_sub(existing - removed);
                            paste_text = paste_text.chars().take(available).collect();
                        }
                        if !paste_text.is_empty() {
                            undo_stack.push_initial(crate::ecs::ui::components::TextSnapshot {
                                text: text.clone(),
                                cursor_position,
                                selection_start,
                            });
                            if let Some(sel_start) = selection_start {
                                let min = sel_start.min(cursor_position);
                                let max = sel_start.max(cursor_position);
                                let chars: Vec<char> = text.chars().collect();
                                let mut new_text: String = chars[..min].iter().collect();
                                new_text.push_str(&paste_text);
                                new_text.extend(chars[max..].iter());
                                text = new_text;
                                cursor_position = min + paste_text.chars().count();
                                selection_start = None;
                            } else {
                                let chars: Vec<char> = text.chars().collect();
                                let mut new_text: String =
                                    chars[..cursor_position].iter().collect();
                                new_text.push_str(&paste_text);
                                new_text.extend(chars[cursor_position..].iter());
                                text = new_text;
                                cursor_position += paste_text.chars().count();
                            }
                            changed = true;
                            undo_stack.push(
                                crate::ecs::ui::components::TextSnapshot {
                                    text: text.clone(),
                                    cursor_position,
                                    selection_start,
                                },
                                current_time,
                            );
                        }
                    }
                }
                KeyCode::Escape => {
                    selection_start = None;
                    clear_focus = true;
                }
                _ => {}
            }
        }

        if interaction.clicked {
            let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
            if let Some(rect) = input_rect {
                let font_size = world
                    .resources
                    .retained_ui
                    .theme_state
                    .active_theme()
                    .font_size;
                let best_idx = world
                    .resources
                    .text_cache
                    .font_manager
                    .best_bitmap_font_for_size(font_size);
                let font_arc = world
                    .resources
                    .text_cache
                    .font_manager
                    .get_bitmap_font_arc(best_idx);
                if let Some(atlas) = font_arc {
                    let local_y =
                        (mouse_position.y - rect.min.y) / dpi_scale - 8.0 + scroll_offset_y;
                    let clicked_line = (local_y / line_height).max(0.0) as usize;
                    let total_lines = line_count(&text);
                    let target_line = clicked_line.min(total_lines.saturating_sub(1));
                    let lt = line_text(&text, target_line);
                    let local_x = (mouse_position.x - rect.min.x) / dpi_scale - 8.0;
                    let col = byte_index_at_x(&atlas, lt, font_size, local_x);
                    let line_start = line_start_char_index(&text, target_line);
                    if shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else {
                        selection_start = None;
                    }
                    cursor_position = line_start + col;
                    cursor_blink_timer = current_time;
                }
            }
        }

        if interaction.double_clicked {
            let len = text.chars().count();
            let pos = cursor_position.min(len);
            let word_start = prev_word_boundary(&text, pos);
            let word_end = next_word_boundary(&text, pos).min(len);
            selection_start = Some(word_start);
            cursor_position = word_end;
        }
    } else {
        selection_start = None;
    }

    if clear_focus {
        world.resources.retained_ui.focused_entity = None;
    }

    if changed {
        world.resources.text_cache.set_text(data.text_slot, &text);

        #[cfg(feature = "syntax_highlighting")]
        if let Some(language) = &data.syntax_language {
            crate::ecs::ui::syntax::highlight_text_area(world, &text, language, data.text_slot);
        }
    }

    let cursor_visible = is_focused && ((current_time - cursor_blink_timer) % 1.0) < 0.5;
    if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity) {
        cursor_node.visible = cursor_visible;
    }

    let (cur_line, cur_col) = line_col_from_char_position(&text, cursor_position);
    let visible_rows = data.visible_rows;
    let cursor_y = cur_line as f32 * line_height;
    let visible_height = visible_rows as f32 * line_height;
    if cursor_y - scroll_offset_y >= visible_height {
        scroll_offset_y = cursor_y - visible_height + line_height;
    } else if cursor_y < scroll_offset_y {
        scroll_offset_y = cursor_y;
    }
    let total_lines = line_count(&text);
    let max_scroll = ((total_lines as f32 * line_height) - visible_height).max(0.0);
    scroll_offset_y = scroll_offset_y.clamp(0.0, max_scroll);

    let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
    if let Some(rect) = input_rect {
        let font_size = world
            .resources
            .retained_ui
            .theme_state
            .active_theme()
            .font_size;
        let best_idx = world
            .resources
            .text_cache
            .font_manager
            .best_bitmap_font_for_size(font_size);
        let font_arc = world
            .resources
            .text_cache
            .font_manager
            .get_bitmap_font_arc(best_idx);
        if let Some(atlas) = font_arc {
            let cur_line_text = line_text(&text, cur_line);
            let text_before_cursor: String = cur_line_text.chars().take(cur_col).collect();
            let cursor_x = measure_text_width(&atlas, &text_before_cursor, font_size);
            let cursor_screen_y = cur_line as f32 * line_height - scroll_offset_y;

            let has_selection =
                is_focused && selection_start.is_some() && selection_start != Some(cursor_position);

            let sel_positions: Vec<(f32, f32, f32)> = if has_selection {
                let sel_start = selection_start.unwrap();
                let sel_min = sel_start.min(cursor_position);
                let sel_max = sel_start.max(cursor_position);
                let (min_line, min_col) = line_col_from_char_position(&text, sel_min);
                let (max_line, max_col) = line_col_from_char_position(&text, sel_max);

                let mut positions = Vec::new();
                for line_idx in min_line..=max_line {
                    let lt = line_text(&text, line_idx);
                    let start_col = if line_idx == min_line { min_col } else { 0 };
                    let end_col = if line_idx == max_line {
                        max_col
                    } else {
                        lt.chars().count()
                    };
                    let start_text: String = lt.chars().take(start_col).collect();
                    let end_text: String = lt.chars().take(end_col).collect();
                    let sx = measure_text_width(&atlas, &start_text, font_size);
                    let ex = measure_text_width(&atlas, &end_text, font_size);
                    let sy = line_idx as f32 * line_height - scroll_offset_y;
                    positions.push((sx, ex - sx, sy));
                }
                positions
            } else {
                Vec::new()
            };

            drop(atlas);

            if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity)
                && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                    cursor_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
            {
                window.position =
                    crate::ecs::ui::units::Ab(Vec2::new(8.0 + cursor_x, 8.0 + cursor_screen_y))
                        .into();
            }

            for (pool_index, sel_entity) in data.selection_pool.iter().enumerate() {
                if pool_index < sel_positions.len() {
                    let (sx, width, sy) = sel_positions[pool_index];
                    if let Some(sel_node) = world.get_ui_layout_node_mut(*sel_entity) {
                        sel_node.visible = true;
                        if let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                            sel_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
                        {
                            window.position =
                                crate::ecs::ui::units::Ab(Vec2::new(8.0 + sx, 8.0 + sy)).into();
                            window.size =
                                crate::ecs::ui::units::Ab(Vec2::new(width.max(2.0), line_height))
                                    .into();
                        }
                    }
                } else if let Some(sel_node) = world.get_ui_layout_node_mut(*sel_entity) {
                    sel_node.visible = false;
                }
            }

            let _ = rect;
        }
    }

    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::TextAreaChanged {
                entity,
                text: text.clone(),
            },
        );
    }
    if clear_focus {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::TextInputSubmitted {
                entity,
                text: text.clone(),
            },
        );
    }
    let text_is_empty = text.is_empty();
    let placeholder_entity =
        if let Some(UiWidgetState::TextArea(widget_data)) = world.get_ui_widget_state_mut(entity) {
            widget_data.text = text;
            widget_data.cursor_position = cursor_position;
            widget_data.selection_start = selection_start;
            widget_data.changed = changed;
            widget_data.cursor_blink_timer = cursor_blink_timer;
            widget_data.scroll_offset_y = scroll_offset_y;
            widget_data.undo_stack = undo_stack;
            widget_data.placeholder_entity
        } else {
            None
        };
    if let Some(ph_entity) = placeholder_entity
        && let Some(node) = world.get_ui_layout_node_mut(ph_entity)
    {
        node.visible = text_is_empty;
    }
}

struct RichTextEditorContext<'a> {
    entity: freecs::Entity,
    interaction: &'a InteractionSnapshot,
    data: &'a crate::ecs::ui::components::UiRichTextEditorData,
    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,
}

fn handle_rich_text_editor(world: &mut World, context: RichTextEditorContext<'_>) {
    let entity = context.entity;
    let is_focused = context.focused_entity == Some(entity);
    let data = context.data;
    let mut text = data.text.clone();
    let mut char_styles = data.char_styles.clone();
    let mut current_style = data.current_style.clone();
    let mut cursor_position = data.cursor_position;
    let mut selection_start = data.selection_start;
    let mut changed = false;
    let mut cursor_blink_timer = data.cursor_blink_timer;
    let mut scroll_offset_y = data.scroll_offset_y;
    let mut clear_focus = false;
    let line_height = data.line_height;
    let mut undo_stack = data.undo_stack.clone();
    let mut needs_snapshot = false;

    if is_focused {
        for character in context.frame_chars {
            if *character >= ' ' {
                if !needs_snapshot {
                    undo_stack.push_initial(crate::ecs::ui::components::RichTextSnapshot {
                        text: text.clone(),
                        char_styles: char_styles.clone(),
                        cursor_position,
                        selection_start,
                    });
                    needs_snapshot = true;
                }
                if let Some(sel_start) = selection_start {
                    let min = sel_start.min(cursor_position);
                    let max = sel_start.max(cursor_position);
                    let chars: Vec<char> = text.chars().collect();
                    let mut new_text: String = chars[..min].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[max..].iter());
                    text = new_text;
                    char_styles.drain(min..max);
                    char_styles.insert(min, current_style.clone());
                    cursor_position = min + 1;
                    selection_start = None;
                } else {
                    let chars: Vec<char> = text.chars().collect();
                    let mut new_text: String = chars[..cursor_position].iter().collect();
                    new_text.push(*character);
                    new_text.extend(chars[cursor_position..].iter());
                    text = new_text;
                    char_styles.insert(cursor_position, current_style.clone());
                    cursor_position += 1;
                }
                changed = true;
                cursor_blink_timer = context.current_time;
            }
        }

        if needs_snapshot {
            undo_stack.push(
                crate::ecs::ui::components::RichTextSnapshot {
                    text: text.clone(),
                    char_styles: char_styles.clone(),
                    cursor_position,
                    selection_start,
                },
                context.current_time,
            );
        }

        for (key, is_pressed) in context.frame_keys {
            if !is_pressed {
                continue;
            }
            let chars: Vec<char> = text.chars().collect();
            let len = chars.len();

            match key {
                KeyCode::Backspace => {
                    undo_stack.push_initial(crate::ecs::ui::components::RichTextSnapshot {
                        text: text.clone(),
                        char_styles: char_styles.clone(),
                        cursor_position,
                        selection_start,
                    });
                    let mut did_change = false;
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        char_styles.drain(min..max);
                        cursor_position = min;
                        selection_start = None;
                        did_change = true;
                    } else if cursor_position > 0 {
                        if context.ctrl_held {
                            let new_pos = prev_word_boundary(&text, cursor_position);
                            let mut new_text: String = chars[..new_pos].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            text = new_text;
                            char_styles.drain(new_pos..cursor_position);
                            cursor_position = new_pos;
                        } else {
                            let mut new_text: String =
                                chars[..cursor_position - 1].iter().collect();
                            new_text.extend(chars[cursor_position..].iter());
                            text = new_text;
                            char_styles.remove(cursor_position - 1);
                            cursor_position -= 1;
                        }
                        did_change = true;
                    }
                    if did_change {
                        undo_stack.push(
                            crate::ecs::ui::components::RichTextSnapshot {
                                text: text.clone(),
                                char_styles: char_styles.clone(),
                                cursor_position,
                                selection_start,
                            },
                            context.current_time,
                        );
                        changed = true;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::Delete => {
                    undo_stack.push_initial(crate::ecs::ui::components::RichTextSnapshot {
                        text: text.clone(),
                        char_styles: char_styles.clone(),
                        cursor_position,
                        selection_start,
                    });
                    let mut did_change = false;
                    if let Some(sel_start) = selection_start {
                        let min = sel_start.min(cursor_position);
                        let max = sel_start.max(cursor_position);
                        let mut new_text: String = chars[..min].iter().collect();
                        new_text.extend(chars[max..].iter());
                        text = new_text;
                        char_styles.drain(min..max);
                        cursor_position = min;
                        selection_start = None;
                        did_change = true;
                    } else if cursor_position < len {
                        if context.ctrl_held {
                            let end_pos = next_word_boundary(&text, cursor_position);
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[end_pos..].iter());
                            text = new_text;
                            char_styles.drain(cursor_position..end_pos);
                        } else {
                            let mut new_text: String = chars[..cursor_position].iter().collect();
                            new_text.extend(chars[cursor_position + 1..].iter());
                            text = new_text;
                            char_styles.remove(cursor_position);
                        }
                        did_change = true;
                    }
                    if did_change {
                        undo_stack.push(
                            crate::ecs::ui::components::RichTextSnapshot {
                                text: text.clone(),
                                char_styles: char_styles.clone(),
                                cursor_position,
                                selection_start,
                            },
                            context.current_time,
                        );
                        changed = true;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::Enter => {
                    if context.ctrl_held {
                        clear_focus = true;
                    } else {
                        undo_stack.push_initial(crate::ecs::ui::components::RichTextSnapshot {
                            text: text.clone(),
                            char_styles: char_styles.clone(),
                            cursor_position,
                            selection_start,
                        });
                        if let Some(sel_start) = selection_start {
                            let min = sel_start.min(cursor_position);
                            let max = sel_start.max(cursor_position);
                            let chars_vec: Vec<char> = text.chars().collect();
                            let mut new_text: String = chars_vec[..min].iter().collect();
                            new_text.push('\n');
                            new_text.extend(chars_vec[max..].iter());
                            text = new_text;
                            char_styles.drain(min..max);
                            char_styles.insert(min, current_style.clone());
                            cursor_position = min + 1;
                            selection_start = None;
                        } else {
                            let chars_vec: Vec<char> = text.chars().collect();
                            let mut new_text: String =
                                chars_vec[..cursor_position].iter().collect();
                            new_text.push('\n');
                            new_text.extend(chars_vec[cursor_position..].iter());
                            text = new_text;
                            char_styles.insert(cursor_position, current_style.clone());
                            cursor_position += 1;
                        }
                        undo_stack.push(
                            crate::ecs::ui::components::RichTextSnapshot {
                                text: text.clone(),
                                char_styles: char_styles.clone(),
                                cursor_position,
                                selection_start,
                            },
                            context.current_time,
                        );
                        changed = true;
                        cursor_blink_timer = context.current_time;
                    }
                }
                KeyCode::ArrowLeft => {
                    if context.shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let min = selection_start.unwrap().min(cursor_position);
                        cursor_position = min;
                        selection_start = None;
                        cursor_blink_timer = context.current_time;
                        continue;
                    }
                    if context.ctrl_held {
                        cursor_position = prev_word_boundary(&text, cursor_position);
                    } else {
                        cursor_position = cursor_position.saturating_sub(1);
                    }
                    if !context.shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::ArrowRight => {
                    if context.shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else if selection_start.is_some() {
                        let max = selection_start.unwrap().max(cursor_position);
                        cursor_position = max;
                        selection_start = None;
                        cursor_blink_timer = context.current_time;
                        continue;
                    }
                    if context.ctrl_held {
                        cursor_position = next_word_boundary(&text, cursor_position);
                    } else if cursor_position < len {
                        cursor_position += 1;
                    }
                    if !context.shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::ArrowUp => {
                    if context.shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, cur_col) = line_col_from_char_position(&text, cursor_position);
                    if cur_line > 0 {
                        cursor_position = char_position_from_line_col(&text, cur_line - 1, cur_col);
                    } else {
                        cursor_position = 0;
                    }
                    if !context.shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::ArrowDown => {
                    if context.shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, cur_col) = line_col_from_char_position(&text, cursor_position);
                    let total_lines = line_count(&text);
                    if cur_line + 1 < total_lines {
                        cursor_position = char_position_from_line_col(&text, cur_line + 1, cur_col);
                    } else {
                        cursor_position = len;
                    }
                    if !context.shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::Home => {
                    if context.shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, _) = line_col_from_char_position(&text, cursor_position);
                    cursor_position = line_start_char_index(&text, cur_line);
                    if !context.shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::End => {
                    if context.shift_held && selection_start.is_none() {
                        selection_start = Some(cursor_position);
                    }
                    let (cur_line, _) = line_col_from_char_position(&text, cursor_position);
                    let lt = line_text(&text, cur_line);
                    cursor_position = line_start_char_index(&text, cur_line) + lt.chars().count();
                    if !context.shift_held {
                        selection_start = None;
                    }
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::KeyA if context.ctrl_held => {
                    selection_start = Some(0);
                    cursor_position = len;
                    cursor_blink_timer = context.current_time;
                }
                KeyCode::KeyB if context.ctrl_held => {
                    toggle_selection_style(
                        &mut char_styles,
                        &mut current_style,
                        selection_start,
                        cursor_position,
                        |s| &mut s.bold,
                    );
                    changed = true;
                }
                KeyCode::KeyI if context.ctrl_held => {
                    toggle_selection_style(
                        &mut char_styles,
                        &mut current_style,
                        selection_start,
                        cursor_position,
                        |s| &mut s.italic,
                    );
                    changed = true;
                }
                KeyCode::KeyU if context.ctrl_held => {
                    toggle_selection_style(
                        &mut char_styles,
                        &mut current_style,
                        selection_start,
                        cursor_position,
                        |s| &mut s.underline,
                    );
                    changed = true;
                }
                KeyCode::KeyZ if context.ctrl_held => {
                    if let Some(snapshot) = undo_stack.undo() {
                        text = snapshot.text.clone();
                        char_styles = snapshot.char_styles.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        changed = true;
                        cursor_blink_timer = context.current_time;
                    }
                }
                KeyCode::KeyY if context.ctrl_held => {
                    if let Some(snapshot) = undo_stack.redo() {
                        text = snapshot.text.clone();
                        char_styles = snapshot.char_styles.clone();
                        cursor_position = snapshot.cursor_position;
                        selection_start = snapshot.selection_start;
                        changed = true;
                        cursor_blink_timer = context.current_time;
                    }
                }
                KeyCode::Escape => {
                    selection_start = None;
                    clear_focus = true;
                }
                _ => {}
            }
        }

        if context.interaction.clicked {
            let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
            if let Some(rect) = input_rect {
                let font_size = world
                    .resources
                    .retained_ui
                    .theme_state
                    .active_theme()
                    .font_size;
                let best_idx = world
                    .resources
                    .text_cache
                    .font_manager
                    .best_bitmap_font_for_size(font_size);
                let font_arc = world
                    .resources
                    .text_cache
                    .font_manager
                    .get_bitmap_font_arc(best_idx);
                if let Some(atlas) = font_arc {
                    let local_y = (context.mouse_position.y - rect.min.y) / context.dpi_scale - 8.0
                        + scroll_offset_y;
                    let clicked_line = (local_y / line_height).max(0.0) as usize;
                    let total_lines = line_count(&text);
                    let target_line = clicked_line.min(total_lines.saturating_sub(1));
                    let lt = line_text(&text, target_line);
                    let local_x = (context.mouse_position.x - rect.min.x) / context.dpi_scale - 8.0;
                    let col = byte_index_at_x(&atlas, lt, font_size, local_x);
                    let line_start = line_start_char_index(&text, target_line);
                    if context.shift_held {
                        if selection_start.is_none() {
                            selection_start = Some(cursor_position);
                        }
                    } else {
                        selection_start = None;
                    }
                    cursor_position = line_start + col;
                    cursor_blink_timer = context.current_time;
                }
            }
        }

        if context.interaction.double_clicked {
            let len = text.chars().count();
            let pos = cursor_position.min(len);
            let word_start = prev_word_boundary(&text, pos);
            let word_end = next_word_boundary(&text, pos).min(len);
            selection_start = Some(word_start);
            cursor_position = word_end;
        }
    } else {
        selection_start = None;
    }

    if clear_focus {
        world.resources.retained_ui.focused_entity = None;
    }

    if changed {
        world.resources.text_cache.set_text(data.text_slot, &text);

        let has_colors = char_styles.iter().any(|s| s.color.is_some());
        if has_colors {
            let colors: Vec<Option<Vec4>> = char_styles.iter().map(|s| s.color).collect();
            world
                .resources
                .retained_ui
                .text_slot_character_colors
                .insert(data.text_slot, colors);
        } else {
            world
                .resources
                .retained_ui
                .text_slot_character_colors
                .remove(&data.text_slot);
        }
    }

    let cursor_visible = is_focused && ((context.current_time - cursor_blink_timer) % 1.0) < 0.5;
    if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity) {
        cursor_node.visible = cursor_visible;
    }

    let (cur_line, cur_col) = line_col_from_char_position(&text, cursor_position);
    let visible_rows = data.visible_rows;
    let cursor_y = cur_line as f32 * line_height;
    let visible_height = visible_rows as f32 * line_height;
    if cursor_y - scroll_offset_y >= visible_height {
        scroll_offset_y = cursor_y - visible_height + line_height;
    } else if cursor_y < scroll_offset_y {
        scroll_offset_y = cursor_y;
    }
    let total_lines = line_count(&text);
    let max_scroll = ((total_lines as f32 * line_height) - visible_height).max(0.0);
    scroll_offset_y = scroll_offset_y.clamp(0.0, max_scroll);

    let input_rect = world.get_ui_layout_node(entity).map(|n| n.computed_rect);
    if let Some(rect) = input_rect {
        let font_size = world
            .resources
            .retained_ui
            .theme_state
            .active_theme()
            .font_size;
        let best_idx = world
            .resources
            .text_cache
            .font_manager
            .best_bitmap_font_for_size(font_size);
        let font_arc = world
            .resources
            .text_cache
            .font_manager
            .get_bitmap_font_arc(best_idx);
        if let Some(atlas) = font_arc {
            let cur_line_text = line_text(&text, cur_line);
            let text_before_cursor: String = cur_line_text.chars().take(cur_col).collect();
            let cursor_x = measure_text_width(&atlas, &text_before_cursor, font_size);
            let cursor_screen_y = cur_line as f32 * line_height - scroll_offset_y;

            let has_selection =
                is_focused && selection_start.is_some() && selection_start != Some(cursor_position);

            let sel_positions: Vec<(f32, f32, f32)> = if has_selection {
                let sel_start = selection_start.unwrap();
                let sel_min = sel_start.min(cursor_position);
                let sel_max = sel_start.max(cursor_position);
                let (min_line, min_col) = line_col_from_char_position(&text, sel_min);
                let (max_line, max_col) = line_col_from_char_position(&text, sel_max);

                let mut positions = Vec::new();
                for line_idx in min_line..=max_line {
                    let lt = line_text(&text, line_idx);
                    let start_col = if line_idx == min_line { min_col } else { 0 };
                    let end_col = if line_idx == max_line {
                        max_col
                    } else {
                        lt.chars().count()
                    };
                    let start_text: String = lt.chars().take(start_col).collect();
                    let end_text: String = lt.chars().take(end_col).collect();
                    let sx = measure_text_width(&atlas, &start_text, font_size);
                    let ex = measure_text_width(&atlas, &end_text, font_size);
                    let sy = line_idx as f32 * line_height - scroll_offset_y;
                    positions.push((sx, ex - sx, sy));
                }
                positions
            } else {
                Vec::new()
            };

            drop(atlas);

            if let Some(cursor_node) = world.get_ui_layout_node_mut(data.cursor_entity)
                && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                    cursor_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
            {
                window.position =
                    crate::ecs::ui::units::Ab(Vec2::new(8.0 + cursor_x, 8.0 + cursor_screen_y))
                        .into();
            }

            for (pool_index, sel_entity) in data.selection_pool.iter().enumerate() {
                if pool_index < sel_positions.len() {
                    let (sx, width, sy) = sel_positions[pool_index];
                    if let Some(sel_node) = world.get_ui_layout_node_mut(*sel_entity) {
                        sel_node.visible = true;
                        if let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                            sel_node.layouts[crate::ecs::ui::state::UiBase::INDEX].as_mut()
                        {
                            window.position =
                                crate::ecs::ui::units::Ab(Vec2::new(8.0 + sx, 8.0 + sy)).into();
                            window.size =
                                crate::ecs::ui::units::Ab(Vec2::new(width.max(2.0), line_height))
                                    .into();
                        }
                    }
                } else if let Some(sel_node) = world.get_ui_layout_node_mut(*sel_entity) {
                    sel_node.visible = false;
                }
            }

            let _ = rect;
        }
    }

    if changed {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::RichTextEditorChanged {
                entity,
                text: text.clone(),
            },
        );
    }
    if clear_focus {
        world.resources.retained_ui.frame_events.push(
            crate::ecs::ui::resources::UiEvent::TextInputSubmitted {
                entity,
                text: text.clone(),
            },
        );
    }
    let placeholder_update = if let Some(UiWidgetState::RichTextEditor(widget_data)) =
        world.get_ui_widget_state_mut(entity)
    {
        let text_is_empty = text.is_empty();
        widget_data.text = text;
        widget_data.char_styles = char_styles;
        widget_data.current_style = current_style;
        widget_data.cursor_position = cursor_position;
        widget_data.selection_start = selection_start;
        widget_data.changed = changed;
        widget_data.cursor_blink_timer = cursor_blink_timer;
        widget_data.scroll_offset_y = scroll_offset_y;
        widget_data.undo_stack = undo_stack;
        widget_data
            .placeholder_entity
            .filter(|_| changed)
            .map(|ph| (ph, text_is_empty))
    } else {
        None
    };

    if let Some((ph_entity, text_is_empty)) = placeholder_update
        && let Some(node) = world.get_ui_layout_node_mut(ph_entity)
    {
        node.visible = text_is_empty;
    }
}

fn toggle_selection_style(
    char_styles: &mut [crate::ecs::ui::components::CharStyle],
    current_style: &mut crate::ecs::ui::components::CharStyle,
    selection_start: Option<usize>,
    cursor_position: usize,
    accessor: fn(&mut crate::ecs::ui::components::CharStyle) -> &mut bool,
) {
    if let Some(sel_start) = selection_start {
        let start = sel_start.min(cursor_position);
        let end = sel_start.max(cursor_position);
        let all_set = (start..end).all(|index| {
            char_styles
                .get(index)
                .map(|s| *accessor(&mut s.clone()))
                .unwrap_or(false)
        });
        let new_value = !all_set;
        for index in start..end {
            if index < char_styles.len() {
                *accessor(&mut char_styles[index]) = new_value;
            }
        }
        *accessor(current_style) = new_value;
    } else {
        let field = accessor(current_style);
        *field = !*field;
    }
}

fn compute_tile_rects(
    data: &mut UiTileContainerData,
    tile_id: TileId,
    available: crate::ecs::ui::types::Rect,
) {
    if tile_id.0 >= data.rects.len() {
        data.rects
            .resize(tile_id.0 + 1, crate::ecs::ui::types::Rect::default());
    }
    data.rects[tile_id.0] = available;

    let Some(node) = data.tiles[tile_id.0].clone() else {
        return;
    };

    match node {
        TileNode::Pane { .. } => {}
        TileNode::Split {
            direction,
            ratio,
            children,
        } => {
            let splitter = data.splitter_width;
            match direction {
                SplitDirection::Horizontal => {
                    let total_width = available.width() - splitter;
                    let left_width = total_width * ratio;

                    let left_rect = crate::ecs::ui::types::Rect {
                        min: available.min,
                        max: Vec2::new(available.min.x + left_width, available.max.y),
                    };
                    let right_rect = crate::ecs::ui::types::Rect {
                        min: Vec2::new(available.min.x + left_width + splitter, available.min.y),
                        max: available.max,
                    };

                    compute_tile_rects(data, children[0], left_rect);
                    compute_tile_rects(data, children[1], right_rect);
                }
                SplitDirection::Vertical => {
                    let total_height = available.height() - splitter;
                    let top_height = total_height * ratio;

                    let top_rect = crate::ecs::ui::types::Rect {
                        min: available.min,
                        max: Vec2::new(available.max.x, available.min.y + top_height),
                    };
                    let bottom_rect = crate::ecs::ui::types::Rect {
                        min: Vec2::new(available.min.x, available.min.y + top_height + splitter),
                        max: available.max,
                    };

                    compute_tile_rects(data, children[0], top_rect);
                    compute_tile_rects(data, children[1], bottom_rect);
                }
            }
        }
        TileNode::Tabs { panes, active } => {
            let tab_bar_h = data.tab_bar_height;
            let content_rect = crate::ecs::ui::types::Rect {
                min: Vec2::new(available.min.x, available.min.y + tab_bar_h),
                max: available.max,
            };
            for (index, &pane_id) in panes.iter().enumerate() {
                if pane_id.0 >= data.rects.len() {
                    data.rects
                        .resize(pane_id.0 + 1, crate::ecs::ui::types::Rect::default());
                }
                if index == active {
                    compute_tile_rects(data, pane_id, content_rect);
                } else {
                    data.rects[pane_id.0] = content_rect;
                }
            }
        }
    }
}

fn tile_hit_test_splitter(data: &UiTileContainerData, mouse: Vec2) -> Option<TileId> {
    for (index, tile) in data.tiles.iter().enumerate() {
        if let Some(TileNode::Split {
            direction,
            children,
            ..
        }) = tile
        {
            let Some(child0_rect) = data.rects.get(children[0].0) else {
                continue;
            };
            let splitter_half = data.splitter_width * 0.5 + 2.0;
            let parent_rect = &data.rects[index];

            let hit = match direction {
                SplitDirection::Horizontal => {
                    let boundary = child0_rect.max.x + data.splitter_width * 0.5;
                    (mouse.x - boundary).abs() < splitter_half
                        && mouse.y >= parent_rect.min.y
                        && mouse.y <= parent_rect.max.y
                }
                SplitDirection::Vertical => {
                    let boundary = child0_rect.max.y + data.splitter_width * 0.5;
                    (mouse.y - boundary).abs() < splitter_half
                        && mouse.x >= parent_rect.min.x
                        && mouse.x <= parent_rect.max.x
                }
            };

            if hit {
                return Some(TileId(index));
            }
        }
    }
    None
}

fn tile_hit_test_tab_bar(data: &UiTileContainerData, mouse: Vec2) -> Option<(TileId, usize)> {
    for (index, tile) in data.tiles.iter().enumerate() {
        if let Some(TileNode::Tabs { panes, .. }) = tile
            && !panes.is_empty()
        {
            let rect = &data.rects[index];
            let tab_bar_rect = crate::ecs::ui::types::Rect {
                min: rect.min,
                max: Vec2::new(rect.max.x, rect.min.y + data.tab_bar_height),
            };

            if tab_bar_rect.contains(mouse) {
                let tab_width = tab_bar_rect.width() / panes.len() as f32;
                let relative_x = mouse.x - tab_bar_rect.min.x;
                let tab_index = (relative_x / tab_width) as usize;
                let tab_index = tab_index.min(panes.len() - 1);
                return Some((TileId(index), tab_index));
            }
        }
    }
    None
}

fn tile_hit_test_tab_close(
    data: &UiTileContainerData,
    tabs_id: TileId,
    tab_index: usize,
    mouse: Vec2,
) -> bool {
    let rect = &data.rects[tabs_id.0];
    if let Some(TileNode::Tabs { panes, .. }) = data.get(tabs_id)
        && !panes.is_empty()
    {
        let close_w = data.tab_close_width();
        let tab_width = rect.width() / panes.len() as f32;
        let tab_x = rect.min.x + tab_index as f32 * tab_width;
        let close_region_left = tab_x + tab_width - close_w;
        let close_region_right = tab_x + tab_width - 1.0;
        let close_region_top = rect.min.y + 1.0;
        let close_region_bottom = rect.min.y + data.tab_bar_height - 1.0;
        mouse.x >= close_region_left
            && mouse.x <= close_region_right
            && mouse.y >= close_region_top
            && mouse.y <= close_region_bottom
    } else {
        false
    }
}

fn tile_hit_test_drop_zone(data: &UiTileContainerData, mouse: Vec2) -> Option<DropPreview> {
    for (index, tile) in data.tiles.iter().enumerate() {
        if !matches!(tile, Some(TileNode::Tabs { .. })) {
            continue;
        }

        let rect = &data.rects[index];
        if !rect.contains(mouse) {
            continue;
        }

        let width = rect.width();
        let height = rect.height();
        let relative = mouse - rect.min;

        let zone_fraction = 0.25;

        let zone = if relative.x < width * zone_fraction {
            DropZone::Left
        } else if relative.x > width * (1.0 - zone_fraction) {
            DropZone::Right
        } else if relative.y < height * zone_fraction {
            DropZone::Top
        } else if relative.y > height * (1.0 - zone_fraction) {
            DropZone::Bottom
        } else {
            DropZone::Center
        };

        return Some(DropPreview {
            target_tile: TileId(index),
            zone,
        });
    }
    None
}

struct TileContainerContext {
    mouse_position: Vec2,
    mouse_just_pressed: bool,
    mouse_just_released: bool,
    mouse_down: bool,
}

fn handle_tile_container(
    world: &mut World,
    entity: freecs::Entity,
    data: &UiTileContainerData,
    ctx: &TileContainerContext,
) {
    if let Some(node) = world.get_ui_layout_node(entity)
        && (!node.visible
            || (node.computed_rect.width() <= 0.0 && node.computed_rect.height() <= 0.0))
    {
        return;
    }

    let mouse_position = ctx.mouse_position;
    let mouse_just_pressed = ctx.mouse_just_pressed;
    let mouse_just_released = ctx.mouse_just_released;
    let mouse_down = ctx.mouse_down;

    let reserved = &world.resources.retained_ui.reserved_areas;
    let viewport_size = world
        .resources
        .window
        .cached_viewport_size
        .map(|(width, height)| Vec2::new(width as f32, height as f32))
        .unwrap_or(Vec2::new(800.0, 600.0));
    let container_rect = crate::ecs::ui::types::Rect {
        min: Vec2::new(reserved.left, reserved.top),
        max: Vec2::new(
            viewport_size.x - reserved.right,
            viewport_size.y - reserved.bottom,
        ),
    };

    if let Some(node) = world.get_ui_layout_node_mut(entity)
        && let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
            &mut node.layouts[crate::ecs::ui::state::UiBase::INDEX]
    {
        window.position = crate::ecs::ui::units::Ab(container_rect.min).into();
        window.size = crate::ecs::ui::units::Ab(container_rect.size()).into();
    }

    let mut data = data.clone();

    let root = data.root;
    compute_tile_rects(&mut data, root, container_rect);

    let total_panes = data
        .tiles
        .iter()
        .filter(|t| matches!(t, Some(TileNode::Pane { .. })))
        .count();

    if let Some((split_id, _start_ratio)) = data.dragging_splitter {
        if mouse_down {
            let parent_rect = data.rects[split_id.0];
            let splitter_w = data.splitter_width;
            if let Some(TileNode::Split {
                direction, ratio, ..
            }) = data.get_mut(split_id)
            {
                let new_ratio = match direction {
                    SplitDirection::Horizontal => {
                        let total = parent_rect.width() - splitter_w;
                        if total > 0.0 {
                            ((mouse_position.x - parent_rect.min.x) / total).clamp(0.1, 0.9)
                        } else {
                            *ratio
                        }
                    }
                    SplitDirection::Vertical => {
                        let total = parent_rect.height() - splitter_w;
                        if total > 0.0 {
                            ((mouse_position.y - parent_rect.min.y) / total).clamp(0.1, 0.9)
                        } else {
                            *ratio
                        }
                    }
                };
                *ratio = new_ratio;
            }

            let root = data.root;
            compute_tile_rects(&mut data, root, container_rect);
        }
        if mouse_just_released || !mouse_down {
            if mouse_just_released && let Some(TileNode::Split { ratio, .. }) = data.get(split_id) {
                world.resources.retained_ui.frame_events.push(
                    crate::ecs::ui::resources::UiEvent::TileSplitterMoved {
                        container: entity,
                        split_id,
                        ratio: *ratio,
                    },
                );
            }
            data.dragging_splitter = None;
        }
    } else if let Some((source_tabs_id, tab_index, start_pos)) = data.pending_tab_drag {
        if mouse_down {
            let distance = nalgebra_glm::length(&(mouse_position - start_pos));
            if distance > 8.0 {
                data.pending_tab_drag = None;
                data.dragging_tab = Some((source_tabs_id, tab_index, mouse_position));
                data.drop_preview = tile_hit_test_drop_zone(&data, mouse_position);
            }
        } else {
            data.pending_tab_drag = None;
        }
    } else if let Some((source_tabs_id, tab_index, _drag_offset)) = data.dragging_tab {
        if mouse_down {
            data.dragging_tab = Some((source_tabs_id, tab_index, mouse_position));
            data.drop_preview = tile_hit_test_drop_zone(&data, mouse_position);
        }
        if mouse_just_released || !mouse_down {
            let preview = data.drop_preview.take();
            data.dragging_tab = None;

            if mouse_just_released
                && let Some(preview) = preview
                && let Some(TileNode::Tabs { panes, .. }) = data.get(source_tabs_id).cloned()
                && tab_index < panes.len()
            {
                let pane_id = panes[tab_index];
                let target = preview.target_tile;

                match preview.zone {
                    DropZone::Center => {
                        if target == source_tabs_id {
                            if let Some((_, drop_index)) =
                                tile_hit_test_tab_bar(&data, mouse_position)
                                    .filter(|(tid, _)| *tid == source_tabs_id)
                                && let Some(TileNode::Tabs { panes, active }) =
                                    data.get_mut(source_tabs_id)
                                && tab_index < panes.len()
                                && drop_index < panes.len()
                                && drop_index != tab_index
                            {
                                let moved = panes.remove(tab_index);
                                panes.insert(drop_index, moved);
                                *active = drop_index;
                            }
                        } else {
                            if let Some(TileNode::Tabs {
                                panes: source_panes,
                                active: source_active,
                            }) = data.get_mut(source_tabs_id)
                            {
                                source_panes.retain(|id| *id != pane_id);
                                if *source_active >= source_panes.len() && !source_panes.is_empty()
                                {
                                    *source_active = source_panes.len() - 1;
                                }
                            }
                            if let Some(TileNode::Tabs {
                                panes: target_panes,
                                active: target_active,
                            }) = data.get_mut(target)
                            {
                                target_panes.push(pane_id);
                                *target_active = target_panes.len() - 1;
                            }
                            world.resources.retained_ui.frame_events.push(
                                crate::ecs::ui::resources::UiEvent::TileTabActivated {
                                    container: entity,
                                    pane_id,
                                },
                            );
                            collapse_empty_tiles(&mut data, source_tabs_id);
                        }
                    }
                    zone => {
                        if target == source_tabs_id && panes.len() == 1 {
                        } else {
                            let (direction, swap) = match zone {
                                DropZone::Left => (SplitDirection::Horizontal, true),
                                DropZone::Right => (SplitDirection::Horizontal, false),
                                DropZone::Top => (SplitDirection::Vertical, true),
                                DropZone::Bottom => (SplitDirection::Vertical, false),
                                DropZone::Center => unreachable!(),
                            };

                            if let Some(TileNode::Tabs {
                                panes: source_panes,
                                active: source_active,
                            }) = data.get_mut(source_tabs_id)
                            {
                                source_panes.retain(|id| *id != pane_id);
                                if *source_active >= source_panes.len() && !source_panes.is_empty()
                                {
                                    *source_active = source_panes.len() - 1;
                                }
                            }

                            let new_tabs = TileNode::Tabs {
                                panes: vec![pane_id],
                                active: 0,
                            };
                            let new_tabs_id = data.alloc(new_tabs);

                            if let Some(old_node) = data.tiles[target.0].take() {
                                let old_id = data.alloc(old_node);

                                let children = if swap {
                                    [new_tabs_id, old_id]
                                } else {
                                    [old_id, new_tabs_id]
                                };
                                data.tiles[target.0] = Some(TileNode::Split {
                                    direction,
                                    ratio: 0.5,
                                    children,
                                });
                            }

                            collapse_empty_tiles(&mut data, source_tabs_id);
                        }
                    }
                }
            }
        }
    } else {
        if mouse_just_pressed && container_rect.contains(mouse_position) {
            if let Some(split_id) = tile_hit_test_splitter(&data, mouse_position) {
                let current_ratio = if let Some(TileNode::Split { ratio, .. }) = data.get(split_id)
                {
                    *ratio
                } else {
                    0.5
                };
                data.dragging_splitter = Some((split_id, current_ratio));
            } else if let Some((tabs_id, tab_index)) = tile_hit_test_tab_bar(&data, mouse_position)
            {
                let close_hit = total_panes >= 2
                    && tile_hit_test_tab_close(&data, tabs_id, tab_index, mouse_position);

                if close_hit {
                    if let Some(TileNode::Tabs { panes, .. }) = data.get(tabs_id)
                        && tab_index < panes.len()
                    {
                        let pane_id = panes[tab_index];

                        let title = if let Some(TileNode::Pane { title, .. }) = data.get(pane_id) {
                            title.clone()
                        } else {
                            String::new()
                        };

                        if let Some(TileNode::Pane { content_entity, .. }) = data.get(pane_id)
                            && let Some(node) = world.get_ui_layout_node_mut(*content_entity)
                        {
                            node.visible = false;
                        }

                        if let Some(TileNode::Tabs { panes, active }) = data.get_mut(tabs_id) {
                            panes.retain(|id| *id != pane_id);
                            if *active >= panes.len() && !panes.is_empty() {
                                *active = panes.len() - 1;
                            }
                        }
                        data.free(pane_id);
                        collapse_empty_tiles(&mut data, tabs_id);

                        world.resources.retained_ui.frame_events.push(
                            crate::ecs::ui::resources::UiEvent::TileTabClosed {
                                container: entity,
                                pane_id,
                                title,
                            },
                        );
                    }
                } else if let Some(TileNode::Tabs { panes, active }) = data.get_mut(tabs_id)
                    && tab_index < panes.len()
                {
                    let pane_id = panes[tab_index];
                    *active = tab_index;
                    data.pending_tab_drag = Some((tabs_id, tab_index, mouse_position));
                    world.resources.retained_ui.frame_events.push(
                        crate::ecs::ui::resources::UiEvent::TileTabActivated {
                            container: entity,
                            pane_id,
                        },
                    );
                }
            }
        }

        data.drop_preview = None;
    }

    let root = data.root;
    compute_tile_rects(&mut data, root, container_rect);

    for (index, tile) in data.tiles.iter().enumerate() {
        if let Some(TileNode::Pane { content_entity, .. }) = tile {
            let rect = data.rects.get(index).copied().unwrap_or_default();

            let mut is_active = true;
            for tabs_tile in &data.tiles {
                if let Some(TileNode::Tabs { panes, active }) = tabs_tile
                    && panes.contains(&TileId(index))
                    && panes.get(*active) != Some(&TileId(index))
                {
                    is_active = false;
                    break;
                }
            }

            if let Some(node) = world.get_ui_layout_node_mut(*content_entity) {
                node.visible = is_active;
                if let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
                    &mut node.layouts[crate::ecs::ui::state::UiBase::INDEX]
                {
                    window.position = crate::ecs::ui::units::Ab(Vec2::new(
                        rect.min.x - container_rect.min.x,
                        rect.min.y - container_rect.min.y,
                    ))
                    .into();
                    window.size =
                        crate::ecs::ui::units::Ab(Vec2::new(rect.width(), rect.height())).into();
                }
            }
        }
    }

    if let Some((split_id, _)) = data.dragging_splitter {
        if let Some(TileNode::Split { direction, .. }) = data.get(split_id) {
            let cursor = match direction {
                SplitDirection::Horizontal => winit::window::CursorIcon::ColResize,
                SplitDirection::Vertical => winit::window::CursorIcon::RowResize,
            };
            world.resources.retained_ui.requested_cursor = Some(cursor);
        }
    } else if container_rect.contains(mouse_position)
        && let Some(split_id) = tile_hit_test_splitter(&data, mouse_position)
        && let Some(TileNode::Split { direction, .. }) = data.get(split_id)
    {
        let cursor = match direction {
            SplitDirection::Horizontal => winit::window::CursorIcon::ColResize,
            SplitDirection::Vertical => winit::window::CursorIcon::RowResize,
        };
        world.resources.retained_ui.requested_cursor = Some(cursor);
    }

    data.hovered_close = if total_panes >= 2
        && data.dragging_tab.is_none()
        && data.dragging_splitter.is_none()
        && container_rect.contains(mouse_position)
    {
        if let Some((tabs_id, tab_index)) = tile_hit_test_tab_bar(&data, mouse_position)
            && tile_hit_test_tab_close(&data, tabs_id, tab_index, mouse_position)
        {
            Some((tabs_id, tab_index))
        } else {
            None
        }
    } else {
        None
    };

    world.resources.retained_ui.layout_dirty = true;
    world.resources.retained_ui.render_dirty = true;

    if let Some(UiWidgetState::TileContainer(widget_data)) = world.get_ui_widget_state_mut(entity) {
        *widget_data = data;
    }
}

fn collapse_empty_tiles(data: &mut UiTileContainerData, tabs_id: TileId) {
    let remaining = if let Some(TileNode::Tabs { panes, .. }) = data.get(tabs_id) {
        panes.len()
    } else {
        return;
    };

    if remaining > 0 {
        return;
    }

    if let Some((parent_split_id, child_index)) = data.find_parent_split(tabs_id) {
        let sibling_index = 1 - child_index;
        let sibling_id = if let Some(TileNode::Split { children, .. }) = data.get(parent_split_id) {
            children[sibling_index]
        } else {
            return;
        };

        let sibling_node = data.tiles[sibling_id.0].take();
        data.tiles[parent_split_id.0] = sibling_node;
        data.free(tabs_id);
        data.free(sibling_id);
    }
}

pub fn ui_event_dispatch_system(world: &mut World) {
    if !world.resources.retained_ui.enabled {
        return;
    }

    let events = world.resources.retained_ui.frame_events.clone();

    if !events.is_empty() {
        let mut handlers = std::mem::take(&mut world.resources.retained_ui.event_handlers);

        for event in &events {
            let target = event.target_entity();
            if let Some(entity_handlers) = handlers.get_mut(&target) {
                for handler in entity_handlers.iter_mut() {
                    handler(world, event);
                }
            }
        }

        let newly_registered = std::mem::take(&mut world.resources.retained_ui.event_handlers);
        for (entity, mut new_handlers) in newly_registered {
            handlers
                .entry(entity)
                .or_default()
                .append(&mut new_handlers);
        }
        world.resources.retained_ui.event_handlers = handlers;
    }

    let mut click_entities: Vec<freecs::Entity> = events
        .iter()
        .filter_map(|event| match event {
            crate::ecs::ui::resources::UiEvent::ButtonClicked(entity)
            | crate::ecs::ui::resources::UiEvent::SelectableLabelClicked { entity, .. } => {
                Some(*entity)
            }
            _ => None,
        })
        .collect();

    for &entity in world.resources.retained_ui.click_reactions.keys() {
        if !click_entities.contains(&entity)
            && world
                .get_ui_node_interaction(entity)
                .is_some_and(|interaction| interaction.clicked)
        {
            click_entities.push(entity);
        }
    }

    if !click_entities.is_empty() {
        let mut click_reactions = std::mem::take(&mut world.resources.retained_ui.click_reactions);
        for entity in &click_entities {
            if let Some(reaction_list) = click_reactions.get_mut(entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(world);
                }
            }
        }
        let newly_registered_clicks =
            std::mem::take(&mut world.resources.retained_ui.click_reactions);
        for (entity, mut new_reactions) in newly_registered_clicks {
            click_reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.click_reactions = click_reactions;
    }

    let submit_events: Vec<(freecs::Entity, String)> = events
        .iter()
        .filter_map(|event| {
            if let crate::ecs::ui::resources::UiEvent::TextInputSubmitted { entity, text } = event {
                Some((*entity, text.clone()))
            } else {
                None
            }
        })
        .collect();
    if !submit_events.is_empty() {
        let mut reactions = std::mem::take(&mut world.resources.retained_ui.submit_reactions);
        for (entity, text) in &submit_events {
            if let Some(reaction_list) = reactions.get_mut(entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(text.clone(), world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.submit_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.submit_reactions = reactions;
    }

    let confirm_events: Vec<(freecs::Entity, bool)> = events
        .iter()
        .filter_map(|event| {
            if let crate::ecs::ui::resources::UiEvent::ModalClosed { entity, confirmed } = event {
                Some((*entity, *confirmed))
            } else {
                None
            }
        })
        .collect();
    if !confirm_events.is_empty() {
        let mut reactions = std::mem::take(&mut world.resources.retained_ui.confirm_reactions);
        for (entity, confirmed) in &confirm_events {
            if let Some(reaction_list) = reactions.get_mut(entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(*confirmed, world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.confirm_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.confirm_reactions = reactions;
    }

    let menu_events: Vec<(freecs::Entity, usize)> = events
        .iter()
        .filter_map(|event| match event {
            crate::ecs::ui::resources::UiEvent::ContextMenuItemClicked { entity, item_index }
            | crate::ecs::ui::resources::UiEvent::MenuItemClicked { entity, item_index } => {
                Some((*entity, *item_index))
            }
            crate::ecs::ui::resources::UiEvent::BreadcrumbClicked {
                entity,
                segment_index,
            } => Some((*entity, *segment_index)),
            _ => None,
        })
        .collect();
    if !menu_events.is_empty() {
        let mut reactions = std::mem::take(&mut world.resources.retained_ui.menu_select_reactions);
        for (entity, index) in &menu_events {
            if let Some(reaction_list) = reactions.get_mut(entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(*index, world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.menu_select_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.menu_select_reactions = reactions;
    }

    let command_events: Vec<(freecs::Entity, usize)> = events
        .iter()
        .filter_map(|event| {
            if let crate::ecs::ui::resources::UiEvent::CommandPaletteExecuted {
                entity,
                command_index,
            } = event
            {
                Some((*entity, *command_index))
            } else {
                None
            }
        })
        .collect();
    if !command_events.is_empty() {
        let mut reactions = std::mem::take(&mut world.resources.retained_ui.command_reactions);
        for (entity, index) in &command_events {
            if let Some(reaction_list) = reactions.get_mut(entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(*index, world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.command_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.command_reactions = reactions;
    }

    let tree_events: Vec<(freecs::Entity, freecs::Entity)> = events
        .iter()
        .filter_map(|event| {
            if let crate::ecs::ui::resources::UiEvent::TreeNodeSelected { tree, node, .. } = event {
                Some((*tree, *node))
            } else {
                None
            }
        })
        .collect();
    if !tree_events.is_empty() {
        let mut reactions = std::mem::take(&mut world.resources.retained_ui.tree_select_reactions);
        for (tree_entity, node) in &tree_events {
            if let Some(reaction_list) = reactions.get_mut(tree_entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(*node, world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.tree_select_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.tree_select_reactions = reactions;
    }

    let tree_context_menu_events: Vec<(freecs::Entity, freecs::Entity, nalgebra_glm::Vec2)> =
        events
            .iter()
            .filter_map(|event| {
                if let crate::ecs::ui::resources::UiEvent::TreeNodeContextMenu {
                    tree,
                    node,
                    position,
                } = event
                {
                    Some((*tree, *node, *position))
                } else {
                    None
                }
            })
            .collect();
    if !tree_context_menu_events.is_empty() {
        let mut reactions =
            std::mem::take(&mut world.resources.retained_ui.tree_context_menu_reactions);
        for (tree_entity, node, position) in &tree_context_menu_events {
            if let Some(reaction_list) = reactions.get_mut(tree_entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(*node, *position, world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.tree_context_menu_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.tree_context_menu_reactions = reactions;
    }

    let multi_select_events: Vec<(freecs::Entity, Vec<usize>)> = events
        .iter()
        .filter_map(|event| {
            if let crate::ecs::ui::resources::UiEvent::MultiSelectChanged {
                entity,
                selected_indices,
            } = event
            {
                Some((*entity, selected_indices.clone()))
            } else {
                None
            }
        })
        .collect();
    if !multi_select_events.is_empty() {
        let mut reactions = std::mem::take(&mut world.resources.retained_ui.multi_select_reactions);
        for (entity, indices) in &multi_select_events {
            if let Some(reaction_list) = reactions.get_mut(entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(indices.clone(), world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.multi_select_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.multi_select_reactions = reactions;
    }

    let date_events: Vec<(freecs::Entity, i32, u32, u32)> = events
        .iter()
        .filter_map(|event| {
            if let crate::ecs::ui::resources::UiEvent::DatePickerChanged {
                entity,
                year,
                month,
                day,
            } = event
            {
                Some((*entity, *year, *month, *day))
            } else {
                None
            }
        })
        .collect();
    if !date_events.is_empty() {
        let mut reactions = std::mem::take(&mut world.resources.retained_ui.date_changed_reactions);
        for (entity, year, month, day) in &date_events {
            if let Some(reaction_list) = reactions.get_mut(entity) {
                for reaction in reaction_list.iter_mut() {
                    reaction(*year, *month, *day, world);
                }
            }
        }
        let newly = std::mem::take(&mut world.resources.retained_ui.date_changed_reactions);
        for (entity, mut new_reactions) in newly {
            reactions
                .entry(entity)
                .or_default()
                .append(&mut new_reactions);
        }
        world.resources.retained_ui.date_changed_reactions = reactions;
    }

    let changed_entity_keys: Vec<freecs::Entity> = world
        .resources
        .retained_ui
        .changed_reactions
        .keys()
        .copied()
        .collect();
    if !changed_entity_keys.is_empty() {
        let changed_entities: Vec<freecs::Entity> = changed_entity_keys
            .into_iter()
            .filter(|entity| world.ui_changed(*entity))
            .collect();
        if !changed_entities.is_empty() {
            let mut reactions = std::mem::take(&mut world.resources.retained_ui.changed_reactions);
            for entity in &changed_entities {
                if let Some(reaction_list) = reactions.get_mut(entity) {
                    for reaction in reaction_list.iter_mut() {
                        reaction(world);
                    }
                }
            }
            let newly = std::mem::take(&mut world.resources.retained_ui.changed_reactions);
            for (entity, mut new_reactions) in newly {
                reactions
                    .entry(entity)
                    .or_default()
                    .append(&mut new_reactions);
            }
            world.resources.retained_ui.changed_reactions = reactions;
        }
    }
}

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.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.get_ui_widget_state(member),
                            Some(UiWidgetState::Radio(r)) if r.changed
                        )
                    })
                }
                _ => false,
            }
        } else {
            false
        };

        if widget_changed {
            if let Some(state) = world.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.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.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.get_ui_widget_state_mut(entity)
                    {
                        data.open = new_open;
                    }
                    if let Some(node) = world.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.get_ui_widget_state(member).cloned().as_ref()
                        {
                            let should_select = member_data.option_index == target_index;
                            if let Some(node) =
                                world.get_ui_layout_node_mut(member_data.inner_entity)
                            {
                                node.visible = should_select;
                            }
                            if let Some(UiWidgetState::Radio(wd)) =
                                world.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.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;
    }
}
