use crate::ecs::world::World;
use std::collections::HashMap;
use nalgebra_glm::{Vec2, Vec4};
use crate::ecs::ui::components::*;
use crate::ecs::ui::units::Ab;
use crate::prelude::*;
pub fn ui_data_grid_set_row_count(world: &mut World, entity: freecs::Entity, count: usize) {
if let Some(data) = world.ui.get_ui_data_grid_mut(entity) {
data.total_rows = count;
data.selected_rows.retain(|&row| row < count);
}
}
pub fn ui_data_grid_set_cell(
world: &mut World,
entity: freecs::Entity,
data_row: usize,
column: usize,
text: &str,
) {
if let Some(data) = world.ui.get_ui_data_grid(entity) {
if data_row < data.visible_start {
return;
}
let pool_index = data_row - data.visible_start;
if let Some(pool_row) = data.pool_rows.get(pool_index)
&& let Some(&text_slot) = pool_row.cell_text_slots.get(column)
{
world.resources.text.cache.set_text(text_slot, text);
}
}
}
pub fn ui_data_grid_sort_changed(world: &World, entity: freecs::Entity) -> bool {
if let Some(data) = world.ui.get_ui_data_grid(entity) {
data.sort_changed
} else {
false
}
}
pub fn ui_data_grid_selection_changed(world: &World, entity: freecs::Entity) -> bool {
if let Some(data) = world.ui.get_ui_data_grid(entity) {
data.selection_changed
} else {
false
}
}
pub fn ui_data_grid_set_filter(world: &mut World, entity: freecs::Entity, indices: &[usize]) {
if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
grid.filtered_indices = Some(indices.to_vec());
grid.selected_rows.clear();
grid.selection_anchor = None;
grid.selection_changed = true;
}
}
pub fn ui_data_grid_clear_filter(world: &mut World, entity: freecs::Entity) {
if let Some(grid) = world.ui.get_ui_data_grid_mut(entity) {
grid.filtered_indices = None;
grid.selected_rows.clear();
grid.selection_anchor = None;
grid.selection_changed = true;
}
}
pub fn ui_data_grid_populate(
world: &mut World,
entity: freecs::Entity,
source: &dyn crate::ecs::ui::components::DataGridDataSource,
) {
let row_count = source.row_count();
ui_data_grid_set_row_count(world, entity, row_count);
let Some(data) = widget::<UiDataGridData>(world, entity) else {
return;
};
let end = (data.visible_start + data.pool_size).min(data.total_rows);
let range = data.visible_start..end;
let col_count = data.columns.len();
let filtered = data.filtered_indices.clone();
for visible_row in range {
let data_row = filtered
.as_ref()
.and_then(|indices| indices.get(visible_row).copied())
.unwrap_or(visible_row);
for column in 0..col_count {
let text = source.cell_text(data_row, column);
ui_data_grid_set_cell(world, entity, visible_row, column, &text);
}
}
}
pub fn ui_data_grid_populate_fn(
world: &mut World,
entity: freecs::Entity,
row_count: usize,
cell_fn: impl Fn(usize, usize) -> String,
) {
ui_data_grid_set_row_count(world, entity, row_count);
let Some(data) = widget::<UiDataGridData>(world, entity) else {
return;
};
let end = (data.visible_start + data.pool_size).min(data.total_rows);
let range = data.visible_start..end;
let col_count = data.columns.len();
let filtered = data.filtered_indices.clone();
for visible_row in range {
let data_row = filtered
.as_ref()
.and_then(|indices| indices.get(visible_row).copied())
.unwrap_or(visible_row);
for column in 0..col_count {
let text = cell_fn(data_row, column);
ui_data_grid_set_cell(world, entity, visible_row, column, &text);
}
}
}
pub fn ui_data_grid_start_edit(
world: &mut World,
entity: freecs::Entity,
row: usize,
column: usize,
) {
let info = if let Some(data) = world.ui.get_ui_data_grid(entity) {
if column < data.columns.len()
&& data.columns[column].editable
&& let Some(input_entity) = data.editing_input_entity
{
let pool_row_idx = if row >= data.visible_start {
row - data.visible_start
} else {
return;
};
if pool_row_idx >= data.pool_rows.len() {
return;
}
let cell_entity = data.pool_rows[pool_row_idx].cell_entities[column];
let cell_text_slot = data.pool_rows[pool_row_idx].cell_text_slots[column];
let cell_text = world
.resources
.text
.cache
.get_text(cell_text_slot)
.unwrap_or_default()
.to_string();
Some((input_entity, cell_entity, cell_text))
} else {
None
}
} else {
None
};
if let Some((input_entity, cell_entity, cell_text)) = info {
ui_text_input_set_value(world, input_entity, &cell_text);
if let Some(input_data) = world.ui.get_ui_text_input_mut(input_entity) {
input_data.selection_start = Some(0);
}
let root_rect = world
.ui
.get_ui_layout_node(entity)
.map(|n| n.computed_rect)
.unwrap_or_default();
let cell_rect = world
.ui
.get_ui_layout_node(cell_entity)
.map(|n| n.computed_rect)
.unwrap_or_default();
let rel_x = cell_rect.min.x - root_rect.min.x;
let rel_y = cell_rect.min.y - root_rect.min.y;
let width = cell_rect.width();
let height = cell_rect.height();
if let Some(node) = world.ui.get_ui_layout_node_mut(input_entity) {
node.visible = true;
if let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
node.base_layout.as_mut()
{
window.position = Ab(Vec2::new(rel_x, rel_y)).into();
window.size = Ab(Vec2::new(width, height)).into();
}
}
world
.resources
.retained_ui
.interaction_for_active_mut()
.focused_entity = Some(input_entity);
if let Some(data) = world.ui.get_ui_data_grid_mut(entity) {
data.editing_cell = Some((row, column));
}
}
}
pub fn ui_data_grid_stop_edit(world: &mut World, entity: freecs::Entity, commit: bool) {
let info = if let Some(data) = world.ui.get_ui_data_grid(entity) {
if let Some((row, column)) = data.editing_cell
&& let Some(input_entity) = data.editing_input_entity
{
Some((input_entity, row, column))
} else {
None
}
} else {
None
};
if let Some((input_entity, row, column)) = info {
if commit {
let text = widget::<UiTextInputData>(world, input_entity)
.map(|d| d.text.clone())
.unwrap_or_default();
world.resources.retained_ui.frame.events.push(
crate::ecs::ui::resources::UiEvent::DataGridCellEdited {
entity,
row,
column,
text,
},
);
}
if let Some(node) = world.ui.get_ui_layout_node_mut(input_entity) {
node.visible = false;
}
if world
.resources
.retained_ui
.interaction_for_active_mut()
.focused_entity
== Some(input_entity)
{
world
.resources
.retained_ui
.interaction_for_active_mut()
.focused_entity = None;
}
if let Some(data) = world.ui.get_ui_data_grid_mut(entity) {
data.editing_cell = None;
}
}
}
pub fn ui_text_area_set_value(world: &mut World, entity: freecs::Entity, text: &str) {
let extract = world
.ui
.get_ui_text_area(entity)
.map(|data| (data.text_slot, data.placeholder_entity));
if let Some((slot, placeholder_entity)) = extract {
world.resources.text.cache.set_text(slot, text);
if let Some(data) = world.ui.get_ui_text_area_mut(entity) {
data.text = text.to_string();
data.cursor_position = text.chars().count();
data.selection_start = None;
}
if let Some(ph_entity) = placeholder_entity
&& let Some(node) = world.ui.get_ui_layout_node_mut(ph_entity)
{
node.visible = text.is_empty();
}
}
}
pub fn ui_rich_text_editor_set_value(world: &mut World, entity: freecs::Entity, text: &str) {
let slot = world
.ui
.get_ui_rich_text_editor(entity)
.map(|data| data.text_slot);
if let Some(slot) = slot {
world.resources.text.cache.set_text(slot, text);
let char_count = text.chars().count();
if let Some(data) = world.ui.get_ui_rich_text_editor_mut(entity) {
data.text = text.to_string();
data.char_styles = vec![CharStyle::default(); char_count];
data.cursor_position = char_count;
data.selection_start = None;
}
}
}
pub fn ui_rich_text_editor_toggle_bold(world: &mut World, entity: freecs::Entity) {
ui_rich_text_editor_toggle_style(world, entity, |style| &mut style.bold);
}
pub fn ui_rich_text_editor_toggle_italic(world: &mut World, entity: freecs::Entity) {
ui_rich_text_editor_toggle_style(world, entity, |style| &mut style.italic);
}
pub fn ui_rich_text_editor_toggle_underline(world: &mut World, entity: freecs::Entity) {
ui_rich_text_editor_toggle_style(world, entity, |style| &mut style.underline);
}
pub fn ui_rich_text_editor_set_color(
world: &mut World,
entity: freecs::Entity,
color: Option<Vec4>,
) {
let update_info = if let Some(data) = world.ui.get_ui_rich_text_editor_mut(entity) {
if let Some(sel_start) = data.selection_start {
let start = sel_start.min(data.cursor_position);
let end = sel_start.max(data.cursor_position);
for index in start..end {
if index < data.char_styles.len() {
data.char_styles[index].color = color;
}
}
}
data.current_style.color = color;
data.changed = true;
Some((data.char_styles.clone(), data.text_slot))
} else {
None
};
if let Some((char_styles, text_slot)) = update_info {
update_rich_text_char_colors(
&char_styles,
text_slot,
&mut world.resources.retained_ui.text_cache.character_colors,
);
}
}
pub fn ui_rich_text_editor_toggle_style(
world: &mut World,
entity: freecs::Entity,
accessor: fn(&mut CharStyle) -> &mut bool,
) {
let update_info = if let Some(data) = world.ui.get_ui_rich_text_editor_mut(entity) {
if let Some(sel_start) = data.selection_start {
let start = sel_start.min(data.cursor_position);
let end = sel_start.max(data.cursor_position);
let all_set = (start..end).all(|index| {
data.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 < data.char_styles.len() {
*accessor(&mut data.char_styles[index]) = new_value;
}
}
*accessor(&mut data.current_style) = new_value;
} else {
let field = accessor(&mut data.current_style);
*field = !*field;
}
data.changed = true;
Some((data.char_styles.clone(), data.text_slot))
} else {
None
};
if let Some((char_styles, text_slot)) = update_info {
update_rich_text_char_colors(
&char_styles,
text_slot,
&mut world.resources.retained_ui.text_cache.character_colors,
);
}
}
fn update_rich_text_char_colors(
char_styles: &[CharStyle],
text_slot: usize,
slot_colors: &mut HashMap<usize, Vec<Option<Vec4>>>,
) {
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();
slot_colors.insert(text_slot, colors);
} else {
slot_colors.remove(&text_slot);
}
}