use std::collections::HashMap;
use nalgebra_glm::{Vec2, Vec4};
use crate::ecs::ui::components::*;
use crate::ecs::ui::state::{UiBase, UiStateTrait};
use crate::ecs::ui::units::Ab;
impl crate::ecs::world::World {
pub fn ui_data_grid_set_row_count(&mut self, entity: freecs::Entity, count: usize) {
if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state_mut(entity) {
data.total_rows = count;
data.selected_rows.retain(|&row| row < count);
}
}
pub fn ui_data_grid_set_cell(
&mut self,
entity: freecs::Entity,
data_row: usize,
column: usize,
text: &str,
) {
if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state(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)
{
self.resources.text_cache.set_text(text_slot, text);
}
}
}
pub fn ui_data_grid_sort_changed(&self, entity: freecs::Entity) -> bool {
if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state(entity) {
data.sort_changed
} else {
false
}
}
pub fn ui_data_grid_selection_changed(&self, entity: freecs::Entity) -> bool {
if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state(entity) {
data.selection_changed
} else {
false
}
}
pub fn ui_data_grid_set_filter(&mut self, entity: freecs::Entity, indices: &[usize]) {
if let Some(UiWidgetState::DataGrid(grid)) = self.ui.get_ui_widget_state_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(&mut self, entity: freecs::Entity) {
if let Some(UiWidgetState::DataGrid(grid)) = self.ui.get_ui_widget_state_mut(entity) {
grid.filtered_indices = None;
grid.selected_rows.clear();
grid.selection_anchor = None;
grid.selection_changed = true;
}
}
pub fn ui_data_grid_populate(
&mut self,
entity: freecs::Entity,
source: &dyn crate::ecs::ui::components::DataGridDataSource,
) {
let row_count = source.row_count();
self.ui_data_grid_set_row_count(entity, row_count);
let Some(data) = self.widget::<UiDataGridData>(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);
self.ui_data_grid_set_cell(entity, visible_row, column, &text);
}
}
}
pub fn ui_data_grid_populate_fn(
&mut self,
entity: freecs::Entity,
row_count: usize,
cell_fn: impl Fn(usize, usize) -> String,
) {
self.ui_data_grid_set_row_count(entity, row_count);
let Some(data) = self.widget::<UiDataGridData>(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);
self.ui_data_grid_set_cell(entity, visible_row, column, &text);
}
}
}
pub fn ui_data_grid_start_edit(&mut self, entity: freecs::Entity, row: usize, column: usize) {
let info = if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state(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 = self
.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 {
self.ui_text_input_set_value(input_entity, &cell_text);
if let Some(UiWidgetState::TextInput(input_data)) =
self.ui.get_ui_widget_state_mut(input_entity)
{
input_data.selection_start = Some(0);
}
let root_rect = self
.ui
.get_ui_layout_node(entity)
.map(|n| n.computed_rect)
.unwrap_or_default();
let cell_rect = self
.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) = self.ui.get_ui_layout_node_mut(input_entity) {
node.visible = true;
if let Some(crate::ecs::ui::layout_types::UiLayoutType::Window(window)) =
node.layouts[UiBase::INDEX].as_mut()
{
window.position = Ab(Vec2::new(rel_x, rel_y)).into();
window.size = Ab(Vec2::new(width, height)).into();
}
}
self.resources.retained_ui.focused_entity = Some(input_entity);
if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state_mut(entity) {
data.editing_cell = Some((row, column));
}
}
}
pub fn ui_data_grid_stop_edit(&mut self, entity: freecs::Entity, commit: bool) {
let info = if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state(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 = self
.widget::<UiTextInputData>(input_entity)
.map(|d| d.text.clone())
.unwrap_or_default();
self.resources.retained_ui.frame_events.push(
crate::ecs::ui::resources::UiEvent::DataGridCellEdited {
entity,
row,
column,
text,
},
);
}
if let Some(node) = self.ui.get_ui_layout_node_mut(input_entity) {
node.visible = false;
}
if self.resources.retained_ui.focused_entity == Some(input_entity) {
self.resources.retained_ui.focused_entity = None;
}
if let Some(UiWidgetState::DataGrid(data)) = self.ui.get_ui_widget_state_mut(entity) {
data.editing_cell = None;
}
}
}
pub fn ui_text_area_set_value(&mut self, entity: freecs::Entity, text: &str) {
let extract =
if let Some(UiWidgetState::TextArea(data)) = self.ui.get_ui_widget_state(entity) {
Some((
data.text_slot,
data.placeholder_entity,
data.syntax_language.clone(),
))
} else {
None
};
if let Some((slot, placeholder_entity, syntax_language)) = extract {
self.resources.text_cache.set_text(slot, text);
if let Some(UiWidgetState::TextArea(data)) = self.ui.get_ui_widget_state_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) = self.ui.get_ui_layout_node_mut(ph_entity)
{
node.visible = text.is_empty();
}
#[cfg(feature = "syntax_highlighting")]
if let Some(language) = &syntax_language {
crate::ecs::ui::syntax::highlight_text_area(self, text, language, slot);
}
#[cfg(not(feature = "syntax_highlighting"))]
let _ = syntax_language;
}
}
pub fn ui_text_area_set_syntax(&mut self, entity: freecs::Entity, language: Option<&str>) {
let info = if let Some(UiWidgetState::TextArea(data)) = self.ui.get_ui_widget_state(entity)
{
Some((data.text_slot, data.text.clone()))
} else {
None
};
if let Some(UiWidgetState::TextArea(data)) = self.ui.get_ui_widget_state_mut(entity) {
data.syntax_language = language.map(|s| s.to_string());
}
if language.is_some()
&& let Some((text_slot, text)) = info
{
#[cfg(feature = "syntax_highlighting")]
crate::ecs::ui::syntax::highlight_text_area(self, &text, language.unwrap(), text_slot);
#[cfg(not(feature = "syntax_highlighting"))]
{
let _ = (text_slot, text);
}
} else if let Some((text_slot, _)) = info {
self.resources
.retained_ui
.text_slot_character_colors
.remove(&text_slot);
}
}
pub fn ui_rich_text_editor_set_value(&mut self, entity: freecs::Entity, text: &str) {
let slot = if let Some(UiWidgetState::RichTextEditor(data)) =
self.ui.get_ui_widget_state(entity)
{
Some(data.text_slot)
} else {
None
};
if let Some(slot) = slot {
self.resources.text_cache.set_text(slot, text);
let char_count = text.chars().count();
if let Some(UiWidgetState::RichTextEditor(data)) =
self.ui.get_ui_widget_state_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(&mut self, entity: freecs::Entity) {
self.ui_rich_text_editor_toggle_style(entity, |style| &mut style.bold);
}
pub fn ui_rich_text_editor_toggle_italic(&mut self, entity: freecs::Entity) {
self.ui_rich_text_editor_toggle_style(entity, |style| &mut style.italic);
}
pub fn ui_rich_text_editor_toggle_underline(&mut self, entity: freecs::Entity) {
self.ui_rich_text_editor_toggle_style(entity, |style| &mut style.underline);
}
pub fn ui_rich_text_editor_set_color(&mut self, entity: freecs::Entity, color: Option<Vec4>) {
let update_info = if let Some(UiWidgetState::RichTextEditor(data)) =
self.ui.get_ui_widget_state_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 {
Self::update_rich_text_char_colors(
&char_styles,
text_slot,
&mut self.resources.retained_ui.text_slot_character_colors,
);
}
}
fn ui_rich_text_editor_toggle_style(
&mut self,
entity: freecs::Entity,
accessor: fn(&mut CharStyle) -> &mut bool,
) {
let update_info = if let Some(UiWidgetState::RichTextEditor(data)) =
self.ui.get_ui_widget_state_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 {
Self::update_rich_text_char_colors(
&char_styles,
text_slot,
&mut self.resources.retained_ui.text_slot_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);
}
}
}