use std::collections::{HashMap, HashSet};
use nalgebra_glm::{Vec2, Vec4};
use crate::ecs::ui::types::Rect;
use super::data_types::{
CanvasCommand, CharStyle, ColorPickerMode, CommandEntry, ContextMenuItem, ContextMenuItemDef,
DataGridColumn, DataGridPoolRow, InputMask, RangeSliderThumb, ResizeEdge, RichTextSnapshot,
SplitDirection, TextSnapshot, TextSpan, UiPanelKind, UndoStack, VirtualListPoolItem,
};
#[derive(Clone, Debug, Default)]
pub struct UiButtonData {
pub clicked: bool,
pub text_slot: usize,
}
#[derive(Clone, Debug, Default)]
pub struct UiSliderData {
pub min: f32,
pub max: f32,
pub value: f32,
pub changed: bool,
pub fill_entity: freecs::Entity,
pub text_slot: usize,
pub logarithmic: bool,
pub precision: usize,
pub prefix: String,
pub suffix: String,
}
#[derive(Clone, Debug, Default)]
pub struct UiRangeSliderData {
pub min: f32,
pub max: f32,
pub low_value: f32,
pub high_value: f32,
pub changed: bool,
pub fill_entity: freecs::Entity,
pub low_thumb_entity: freecs::Entity,
pub high_thumb_entity: freecs::Entity,
pub text_slot: usize,
pub active_thumb: Option<RangeSliderThumb>,
pub precision: usize,
pub thumb_half_size: f32,
}
#[derive(Clone, Debug, Default)]
pub struct UiToggleData {
pub value: bool,
pub changed: bool,
pub animated_position: f32,
pub knob_entity: freecs::Entity,
}
#[derive(Clone, Debug, Default)]
pub struct UiCheckboxData {
pub value: bool,
pub changed: bool,
pub inner_entity: freecs::Entity,
}
#[derive(Clone, Debug, Default)]
pub struct UiRadioData {
pub group_id: u32,
pub option_index: usize,
pub selected: bool,
pub changed: bool,
pub inner_entity: freecs::Entity,
}
#[derive(Clone, Debug, Default)]
pub struct UiProgressBarData {
pub value: f32,
pub fill_entity: freecs::Entity,
}
#[derive(Clone, Debug, Default)]
pub struct UiCollapsingHeaderData {
pub open: bool,
pub changed: bool,
pub content_entity: freecs::Entity,
pub arrow_text_slot: usize,
}
#[derive(Clone, Debug, Default)]
pub struct UiScrollAreaData {
pub scroll_offset: f32,
pub content_entity: freecs::Entity,
pub content_height: f32,
pub visible_height: f32,
pub thumb_entity: freecs::Entity,
pub track_entity: freecs::Entity,
pub thumb_dragging: bool,
pub thumb_drag_start_offset: f32,
pub snap_interval: Option<f32>,
}
#[derive(Clone, Debug, Default)]
pub struct UiTabBarData {
pub selected_tab: usize,
pub changed: bool,
pub tab_entities: Vec<freecs::Entity>,
pub tab_text_slots: Vec<usize>,
}
#[derive(Clone, Debug, Default)]
pub struct UiBreadcrumbData {
pub segments: Vec<String>,
pub clicked_segment: Option<usize>,
pub changed: bool,
pub segment_entities: Vec<freecs::Entity>,
pub segment_text_slots: Vec<usize>,
}
#[derive(Clone, Debug, Default)]
pub struct UiSplitterData {
pub direction: SplitDirection,
pub ratio: f32,
pub changed: bool,
pub first_pane: freecs::Entity,
pub second_pane: freecs::Entity,
pub divider_entity: freecs::Entity,
pub min_ratio: f32,
pub max_ratio: f32,
}
#[derive(Clone, Debug, Default)]
pub struct UiSpinnerData {
pub dots: Vec<freecs::Entity>,
pub rotation_speed: f32,
pub phase: f32,
}
#[derive(Clone, Debug, Default)]
pub struct UiTextInputData {
pub text: String,
pub cursor_position: usize,
pub selection_start: Option<usize>,
pub changed: bool,
pub text_slot: usize,
pub cursor_entity: freecs::Entity,
pub selection_entity: freecs::Entity,
pub scroll_offset: f32,
pub cursor_blink_timer: f64,
pub placeholder_entity: Option<freecs::Entity>,
pub undo_stack: std::sync::Arc<UndoStack<TextSnapshot>>,
pub input_mask: InputMask,
pub max_length: Option<usize>,
pub password: bool,
}
#[derive(Clone, Debug, Default)]
pub struct UiDropdownData {
pub options: Vec<String>,
pub selected_index: usize,
pub changed: bool,
pub open: bool,
pub header_text_slot: usize,
pub popup_entities: Vec<freecs::Entity>,
pub popup_container_entity: freecs::Entity,
pub hovered_index: Option<usize>,
pub is_theme_dropdown: bool,
pub searchable: bool,
pub filter_text: String,
pub filter_input_entity: Option<freecs::Entity>,
pub filtered_indices: Vec<usize>,
}
#[derive(Clone, Debug, Default)]
pub struct UiMultiSelectData {
pub options: Vec<String>,
pub selected_indices: HashSet<usize>,
pub changed: bool,
pub open: bool,
pub header_text_slot: usize,
pub popup_entities: Vec<freecs::Entity>,
pub popup_container_entity: freecs::Entity,
pub hovered_index: Option<usize>,
pub check_entities: Vec<freecs::Entity>,
}
#[derive(Clone, Debug, Default)]
pub struct UiDatePickerData {
pub year: i32,
pub month: u32,
pub day: u32,
pub changed: bool,
pub open: bool,
pub header_text_slot: usize,
pub popup_entity: freecs::Entity,
pub day_entities: Vec<freecs::Entity>,
pub day_text_slots: Vec<usize>,
pub month_label_slot: usize,
pub prev_month_entity: freecs::Entity,
pub next_month_entity: freecs::Entity,
pub selected_day_entity: Option<freecs::Entity>,
}
#[derive(Clone, Debug, Default)]
pub struct UiMenuData {
pub items: Vec<String>,
pub clicked_item: Option<usize>,
pub open: bool,
pub label_text_slot: usize,
pub popup_entities: Vec<freecs::Entity>,
pub popup_container_entity: freecs::Entity,
}
#[derive(Clone, Debug, Default)]
pub struct UiPanelData {
pub id: String,
pub title: String,
pub title_text_slot: usize,
pub content_entity: freecs::Entity,
pub header_entity: freecs::Entity,
pub collapsed: bool,
pub panel_kind: UiPanelKind,
pub focus_order: i32,
pub pinned: bool,
pub min_size: Vec2,
pub drag_offset: Option<Vec2>,
pub resize_edge: Option<ResizeEdge>,
pub resize_start_rect: Option<Rect>,
pub resize_start_mouse: Option<Vec2>,
pub undocked_rect: Option<Rect>,
pub default_dock_size: f32,
pub collapse_button_entity: Option<freecs::Entity>,
pub collapse_button_text_slot: Option<usize>,
pub header_visible: bool,
pub resizable: bool,
}
#[derive(Clone, Debug, Default)]
pub struct UiColorPickerData {
pub color: Vec4,
pub changed: bool,
pub swatch_entity: freecs::Entity,
pub slider_entities: [freecs::Entity; 4],
pub mode: ColorPickerMode,
pub popup_entity: freecs::Entity,
pub popup_wheel_entity: freecs::Entity,
pub popup_open: bool,
}
#[derive(Clone, Debug, Default)]
pub struct UiColorWheelData {
pub color: Vec4,
pub hue: f32,
pub saturation: f32,
pub value: f32,
pub alpha: f32,
pub changed: bool,
pub wheel_entity: freecs::Entity,
pub dot_entity: freecs::Entity,
pub swatch_entity: freecs::Entity,
pub value_slider_entity: freecs::Entity,
pub alpha_slider_entity: freecs::Entity,
}
#[derive(Clone, Debug, Default)]
pub struct UiSelectableLabelData {
pub selected: bool,
pub changed: bool,
pub text_slot: usize,
pub group_id: Option<u32>,
}
#[derive(Clone, Debug, Default)]
pub struct UiDragValueData {
pub value: f32,
pub min: f32,
pub max: f32,
pub speed: f32,
pub precision: usize,
pub prefix: String,
pub suffix: String,
pub changed: bool,
pub text_slot: usize,
pub editing: bool,
pub cursor_entity: freecs::Entity,
pub selection_entity: freecs::Entity,
pub edit_text: String,
pub cursor_position: usize,
pub selection_start: Option<usize>,
pub cursor_blink_timer: f64,
pub scroll_offset: f32,
pub drag_start_value: f32,
pub undo_stack: UndoStack<TextSnapshot>,
pub up_entity: Option<freecs::Entity>,
pub down_entity: Option<freecs::Entity>,
pub step: f32,
}
#[derive(Clone, Debug, Default)]
pub struct UiContextMenuData {
pub items: Vec<ContextMenuItem>,
pub open: bool,
pub clicked_item: Option<usize>,
pub popup_entity: freecs::Entity,
pub item_entities: Vec<freecs::Entity>,
pub item_defs: Vec<ContextMenuItemDef>,
}
#[derive(Clone, Debug, Default)]
pub struct UiTreeNodeData {
pub label: String,
pub text_slot: usize,
pub depth: usize,
pub expanded: bool,
pub selected: bool,
pub row_entity: freecs::Entity,
pub arrow_entity: freecs::Entity,
pub arrow_text_slot: usize,
pub children_container: freecs::Entity,
pub user_data: u64,
pub parent_node: Option<freecs::Entity>,
pub wrapper_entity: freecs::Entity,
pub lazy: bool,
pub lazy_loaded: bool,
}
#[derive(Clone, Debug, Default)]
pub struct UiTreeViewData {
pub selected_nodes: Vec<freecs::Entity>,
pub multi_select: bool,
pub node_entities: Vec<freecs::Entity>,
pub changed: bool,
pub content_entity: freecs::Entity,
pub context_menu_node: Option<freecs::Entity>,
pub filter_text: String,
pub filter_active: bool,
pub pre_filter_expanded: HashMap<freecs::Entity, bool>,
pub scroll_offset: f32,
pub thumb_entity: freecs::Entity,
pub track_entity: freecs::Entity,
pub thumb_dragging: bool,
pub thumb_drag_start_offset: f32,
}
#[derive(Clone, Debug, Default)]
pub struct UiModalDialogData {
pub title_text_slot: usize,
pub content_entity: freecs::Entity,
pub backdrop_entity: freecs::Entity,
pub ok_button: Option<freecs::Entity>,
pub cancel_button: Option<freecs::Entity>,
pub result: Option<bool>,
}
#[derive(Clone, Debug, Default)]
pub struct UiRichTextData {
pub span_entities: Vec<freecs::Entity>,
pub span_text_slots: Vec<usize>,
}
#[derive(Clone, Debug, Default)]
pub struct UiDataGridData {
pub columns: Vec<DataGridColumn>,
pub row_height: f32,
pub pool_size: usize,
pub total_rows: usize,
pub scroll_entity: freecs::Entity,
pub body_entity: freecs::Entity,
pub header_entities: Vec<freecs::Entity>,
pub header_text_slots: Vec<usize>,
pub top_spacer: freecs::Entity,
pub bottom_spacer: freecs::Entity,
pub pool_rows: Vec<DataGridPoolRow>,
pub visible_start: usize,
pub sort_column: Option<usize>,
pub sort_ascending: bool,
pub sort_changed: bool,
pub selected_rows: HashSet<usize>,
pub selection_anchor: Option<usize>,
pub selection_changed: bool,
pub focused: bool,
pub resize_column: Option<usize>,
pub resize_start_x: f32,
pub resize_start_width: f32,
pub filtered_indices: Option<Vec<usize>>,
pub header_divider_entities: Vec<freecs::Entity>,
pub filter_row_entity: Option<freecs::Entity>,
pub filter_input_entities: Vec<freecs::Entity>,
pub filter_texts: Vec<String>,
pub editing_cell: Option<(usize, usize)>,
pub editing_input_entity: Option<freecs::Entity>,
}
#[derive(Clone, Debug, Default)]
pub struct UiPropertyGridData {
pub label_width: f32,
pub row_entities: Vec<freecs::Entity>,
pub label_entities: Vec<freecs::Entity>,
pub resize_active: bool,
pub resize_start_x: f32,
pub resize_start_width: f32,
}
#[derive(Clone, Debug, Default)]
pub struct UiCommandPaletteData {
pub commands: Vec<CommandEntry>,
pub filter_text: String,
pub filtered_indices: Vec<usize>,
pub selected_index: usize,
pub open: bool,
pub executed_command: Option<usize>,
pub text_input_entity: freecs::Entity,
pub result_entities: Vec<freecs::Entity>,
pub result_text_slots: Vec<usize>,
pub backdrop_entity: freecs::Entity,
pub dialog_entity: freecs::Entity,
pub pool_size: usize,
pub scroll_entity: freecs::Entity,
}
#[derive(Clone, Debug, Default)]
pub struct UiVirtualListData {
pub item_height: f32,
pub pool_size: usize,
pub total_items: usize,
pub visible_start: usize,
pub scroll_entity: freecs::Entity,
pub body_entity: freecs::Entity,
pub top_spacer: freecs::Entity,
pub bottom_spacer: freecs::Entity,
pub pool_items: Vec<VirtualListPoolItem>,
pub selection: Option<usize>,
pub selection_changed: bool,
}
#[derive(Clone, Debug, Default)]
pub struct UiCanvasData {
pub commands: Vec<CanvasCommand>,
pub hit_test_enabled: bool,
pub command_bounds: Vec<(u32, crate::ecs::ui::types::Rect)>,
}
#[derive(Clone, Debug, Default)]
pub struct UiTextAreaData {
pub text: String,
pub cursor_position: usize,
pub selection_start: Option<usize>,
pub changed: bool,
pub text_slot: usize,
pub cursor_entity: freecs::Entity,
pub selection_pool: Vec<freecs::Entity>,
pub scroll_offset_y: f32,
pub cursor_blink_timer: f64,
pub placeholder_entity: Option<freecs::Entity>,
pub line_height: f32,
pub visible_rows: usize,
pub undo_stack: UndoStack<TextSnapshot>,
pub max_length: Option<usize>,
}
#[derive(Clone, Debug, Default)]
pub struct UiRichTextEditorData {
pub text: String,
pub char_styles: Vec<CharStyle>,
pub current_style: CharStyle,
pub cursor_position: usize,
pub selection_start: Option<usize>,
pub changed: bool,
pub text_slot: usize,
pub cursor_entity: freecs::Entity,
pub selection_pool: Vec<freecs::Entity>,
pub scroll_offset_y: f32,
pub cursor_blink_timer: f64,
pub line_height: f32,
pub visible_rows: usize,
pub placeholder_entity: Option<freecs::Entity>,
pub undo_stack: UndoStack<RichTextSnapshot>,
}
impl UiRichTextEditorData {
pub fn spans(&self) -> Vec<TextSpan> {
if self.text.is_empty() {
return Vec::new();
}
let chars: Vec<char> = self.text.chars().collect();
let mut spans = Vec::new();
let mut run_start = 0;
let default_style = CharStyle::default();
for index in 1..=chars.len() {
let current_style = self.char_styles.get(index).unwrap_or(&default_style);
let prev_style = self.char_styles.get(run_start).unwrap_or(&default_style);
if index == chars.len() || current_style != prev_style {
let run_text: String = chars[run_start..index].iter().collect();
let style = self.char_styles.get(run_start).cloned().unwrap_or_default();
let mut span = TextSpan::new(&run_text);
span.bold = style.bold;
span.italic = style.italic;
span.underline = style.underline;
span.color = style.color;
spans.push(span);
run_start = index;
}
}
spans
}
}
pub trait WidgetData: Sized {
fn get(world: &crate::ecs::world::World, entity: freecs::Entity) -> Option<&Self>;
fn get_mut(world: &mut crate::ecs::world::World, entity: freecs::Entity) -> Option<&mut Self>;
fn set(world: &mut crate::ecs::world::World, entity: freecs::Entity, data: Self);
}
macro_rules! impl_widget_data {
($data:ty, $get:ident, $get_mut:ident, $set:ident) => {
impl WidgetData for $data {
fn get(world: &crate::ecs::world::World, entity: freecs::Entity) -> Option<&Self> {
world.ui.$get(entity)
}
fn get_mut(
world: &mut crate::ecs::world::World,
entity: freecs::Entity,
) -> Option<&mut Self> {
world.ui.$get_mut(entity)
}
fn set(world: &mut crate::ecs::world::World, entity: freecs::Entity, data: Self) {
world.ui.$set(entity, data);
}
}
};
}
impl_widget_data!(
UiButtonData,
get_ui_button,
get_ui_button_mut,
set_ui_button
);
impl_widget_data!(
UiSliderData,
get_ui_slider,
get_ui_slider_mut,
set_ui_slider
);
impl_widget_data!(
UiToggleData,
get_ui_toggle,
get_ui_toggle_mut,
set_ui_toggle
);
impl_widget_data!(
UiCheckboxData,
get_ui_checkbox,
get_ui_checkbox_mut,
set_ui_checkbox
);
impl_widget_data!(UiRadioData, get_ui_radio, get_ui_radio_mut, set_ui_radio);
impl_widget_data!(
UiProgressBarData,
get_ui_progress_bar,
get_ui_progress_bar_mut,
set_ui_progress_bar
);
impl_widget_data!(
UiCollapsingHeaderData,
get_ui_collapsing_header,
get_ui_collapsing_header_mut,
set_ui_collapsing_header
);
impl_widget_data!(
UiScrollAreaData,
get_ui_scroll_area,
get_ui_scroll_area_mut,
set_ui_scroll_area
);
impl_widget_data!(
UiTabBarData,
get_ui_tab_bar,
get_ui_tab_bar_mut,
set_ui_tab_bar
);
impl_widget_data!(
UiTextInputData,
get_ui_text_input,
get_ui_text_input_mut,
set_ui_text_input
);
impl_widget_data!(
UiDropdownData,
get_ui_dropdown,
get_ui_dropdown_mut,
set_ui_dropdown
);
impl_widget_data!(UiMenuData, get_ui_menu, get_ui_menu_mut, set_ui_menu);
impl_widget_data!(UiPanelData, get_ui_panel, get_ui_panel_mut, set_ui_panel);
impl_widget_data!(
UiColorPickerData,
get_ui_color_picker,
get_ui_color_picker_mut,
set_ui_color_picker
);
impl_widget_data!(
UiColorWheelData,
get_ui_color_wheel,
get_ui_color_wheel_mut,
set_ui_color_wheel
);
impl_widget_data!(
UiSelectableLabelData,
get_ui_selectable_label,
get_ui_selectable_label_mut,
set_ui_selectable_label
);
impl_widget_data!(
UiDragValueData,
get_ui_drag_value,
get_ui_drag_value_mut,
set_ui_drag_value
);
impl_widget_data!(
UiContextMenuData,
get_ui_context_menu,
get_ui_context_menu_mut,
set_ui_context_menu
);
impl_widget_data!(
UiTreeViewData,
get_ui_tree_view,
get_ui_tree_view_mut,
set_ui_tree_view
);
impl_widget_data!(
UiTreeNodeData,
get_ui_tree_node,
get_ui_tree_node_mut,
set_ui_tree_node
);
impl_widget_data!(
UiModalDialogData,
get_ui_modal_dialog,
get_ui_modal_dialog_mut,
set_ui_modal_dialog
);
impl_widget_data!(
UiRichTextData,
get_ui_rich_text,
get_ui_rich_text_mut,
set_ui_rich_text
);
impl_widget_data!(
UiDataGridData,
get_ui_data_grid,
get_ui_data_grid_mut,
set_ui_data_grid
);
impl_widget_data!(
UiPropertyGridData,
get_ui_property_grid,
get_ui_property_grid_mut,
set_ui_property_grid
);
impl_widget_data!(
UiCommandPaletteData,
get_ui_command_palette,
get_ui_command_palette_mut,
set_ui_command_palette
);
impl_widget_data!(
UiCanvasData,
get_ui_canvas,
get_ui_canvas_mut,
set_ui_canvas
);
impl_widget_data!(
UiTextAreaData,
get_ui_text_area,
get_ui_text_area_mut,
set_ui_text_area
);
impl_widget_data!(
super::tiles::UiTileContainerData,
get_ui_tile_container,
get_ui_tile_container_mut,
set_ui_tile_container
);
impl_widget_data!(
UiVirtualListData,
get_ui_virtual_list,
get_ui_virtual_list_mut,
set_ui_virtual_list
);
impl_widget_data!(
UiRichTextEditorData,
get_ui_rich_text_editor,
get_ui_rich_text_editor_mut,
set_ui_rich_text_editor
);
impl_widget_data!(
UiRangeSliderData,
get_ui_range_slider,
get_ui_range_slider_mut,
set_ui_range_slider
);
impl_widget_data!(
UiBreadcrumbData,
get_ui_breadcrumb,
get_ui_breadcrumb_mut,
set_ui_breadcrumb
);
impl_widget_data!(
UiSplitterData,
get_ui_splitter,
get_ui_splitter_mut,
set_ui_splitter
);
impl_widget_data!(
UiMultiSelectData,
get_ui_multi_select,
get_ui_multi_select_mut,
set_ui_multi_select
);
impl_widget_data!(
UiDatePickerData,
get_ui_date_picker,
get_ui_date_picker_mut,
set_ui_date_picker
);
impl_widget_data!(
UiSpinnerData,
get_ui_spinner,
get_ui_spinner_mut,
set_ui_spinner
);