use nalgebra_glm::{Vec2, Vec4};
use crate::ecs::ui::builder::UiTreeBuilder;
use crate::ecs::ui::components::{DataGridColumn, DragValueConfig, SliderConfig, TextSpan};
use crate::ecs::ui::theme::UiTheme;
use crate::ecs::ui::types::Rect;
use crate::prelude::*;
pub struct Ui<'t, 'w> {
tree: &'t mut UiTreeBuilder<'w>,
parent: freecs::Entity,
}
impl<'t, 'w> Ui<'t, 'w> {
fn scoped<R>(&mut self, f: impl FnOnce(&mut UiTreeBuilder<'w>) -> R) -> R {
let needs_push = self.tree.current_parent() != self.parent;
if needs_push {
self.tree.push_parent(self.parent);
}
let result = f(&mut *self.tree);
if needs_push {
self.tree.pop_parent();
}
result
}
fn container(
&mut self,
add_fn: impl FnOnce(&mut UiTreeBuilder<'w>) -> freecs::Entity,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
let entity = self.scoped(add_fn);
let content = ui_widget_content(self.tree.world_mut(), entity).unwrap_or(entity);
let mut child_ui = Ui {
tree: &mut *self.tree,
parent: content,
};
f(&mut child_ui);
entity
}
pub fn tree(&mut self) -> &mut UiTreeBuilder<'w> {
self.tree
}
pub fn within<R>(&mut self, parent: freecs::Entity, f: impl FnOnce(&mut Ui<'_, 'w>) -> R) -> R {
let mut child = Ui {
tree: &mut *self.tree,
parent,
};
f(&mut child)
}
pub fn parent(&self) -> freecs::Entity {
self.parent
}
pub fn world_mut(&mut self) -> &mut crate::ecs::world::World {
self.tree.world_mut()
}
pub fn theme(&self) -> &UiTheme {
self.tree.active_theme()
}
pub fn set_test_id(&mut self, entity: freecs::Entity, test_id: &str) {
ui_set_test_id(self.tree.world_mut(), entity, test_id);
}
pub fn set_flex_grow(&mut self, entity: freecs::Entity, weight: f32) {
if let Some(node) = self.tree.world_mut().ui.get_ui_layout_node_mut(entity) {
node.flex_grow = Some(weight);
}
}
pub fn set_visible(&mut self, entity: freecs::Entity, visible: bool) {
ui_set_visible(self.tree.world_mut(), entity, visible);
}
pub fn set_disabled(&mut self, entity: freecs::Entity, disabled: bool) {
ui_set_disabled(self.tree.world_mut(), entity, disabled);
}
pub fn set_disabled_recursive(&mut self, entity: freecs::Entity, disabled: bool) {
ui_set_disabled_recursive(self.tree.world_mut(), entity, disabled);
}
pub fn label(&mut self, text: &str) -> freecs::Entity {
self.scoped(|tree| tree.add_label(text))
}
pub fn label_with_slot(&mut self, slot: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_label_with_slot(slot))
}
pub fn label_colored(&mut self, text: &str, color: Vec4) -> freecs::Entity {
self.scoped(|tree| tree.add_label_colored(text, color))
}
pub fn heading(&mut self, text: &str) -> freecs::Entity {
self.scoped(|tree| tree.add_heading(text))
}
pub fn separator(&mut self) -> freecs::Entity {
self.scoped(|tree| tree.add_separator())
}
pub fn spacing(&mut self, amount: f32) -> freecs::Entity {
self.scoped(|tree| tree.add_spacing(amount))
}
pub fn spring(&mut self) -> freecs::Entity {
self.scoped(|tree| tree.add_spring())
}
pub fn button(&mut self, text: &str) -> freecs::Entity {
self.scoped(|tree| tree.add_button(text))
}
pub fn button_colored(&mut self, text: &str, color: Vec4) -> freecs::Entity {
self.scoped(|tree| tree.add_button_colored(text, color))
}
pub fn icon_button(
&mut self,
texture_index: u32,
icon_size: Vec2,
text: &str,
) -> freecs::Entity {
self.scoped(|tree| tree.add_icon_button(texture_index, icon_size, text))
}
pub fn button_rich(&mut self, spans: &[TextSpan]) -> freecs::Entity {
self.scoped(|tree| tree.add_button_rich(spans))
}
pub fn slider(&mut self, min: f32, max: f32, initial: f32) -> freecs::Entity {
self.scoped(|tree| tree.add_slider(min, max, initial))
}
pub fn slider_logarithmic(&mut self, min: f32, max: f32, initial: f32) -> freecs::Entity {
self.scoped(|tree| tree.add_slider_logarithmic(min, max, initial))
}
pub fn slider_configured(&mut self, config: SliderConfig<'_>) -> freecs::Entity {
self.scoped(|tree| tree.add_slider_configured(config))
}
pub fn canvas(&mut self, size: Vec2) -> freecs::Entity {
self.scoped(|tree| tree.add_canvas(size))
}
pub fn toggle(&mut self, initial: bool) -> freecs::Entity {
self.scoped(|tree| tree.add_toggle(initial))
}
pub fn checkbox(&mut self, label: &str, initial: bool) -> freecs::Entity {
self.scoped(|tree| tree.add_checkbox(label, initial))
}
pub fn radio(&mut self, label: &str, group_id: u32, option_index: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_radio(label, group_id, option_index))
}
pub fn progress_bar(&mut self, initial: f32) -> freecs::Entity {
self.scoped(|tree| tree.add_progress_bar(initial))
}
pub fn label_id(&mut self, text: &str, test_id: &str) -> freecs::Entity {
let entity = self.label(text);
self.set_test_id(entity, test_id);
entity
}
pub fn button_id(&mut self, text: &str, test_id: &str) -> freecs::Entity {
let entity = self.button(text);
self.set_test_id(entity, test_id);
entity
}
pub fn slider_id(&mut self, min: f32, max: f32, initial: f32, test_id: &str) -> freecs::Entity {
let entity = self.slider(min, max, initial);
self.set_test_id(entity, test_id);
entity
}
pub fn toggle_id(&mut self, initial: bool, test_id: &str) -> freecs::Entity {
let entity = self.toggle(initial);
self.set_test_id(entity, test_id);
entity
}
pub fn checkbox_id(&mut self, label: &str, initial: bool, test_id: &str) -> freecs::Entity {
let entity = self.checkbox(label, initial);
self.set_test_id(entity, test_id);
entity
}
pub fn text_input_id(&mut self, placeholder: &str, test_id: &str) -> freecs::Entity {
let entity = self.text_input(placeholder);
self.set_test_id(entity, test_id);
entity
}
pub fn dropdown_id(
&mut self,
options: &[&str],
initial: usize,
test_id: &str,
) -> freecs::Entity {
let entity = self.dropdown(options, initial);
self.set_test_id(entity, test_id);
entity
}
pub fn drag_value_id(
&mut self,
min: f32,
max: f32,
initial: f32,
test_id: &str,
) -> freecs::Entity {
let entity = self.drag_value(min, max, initial);
self.set_test_id(entity, test_id);
entity
}
pub fn heading_id(&mut self, text: &str, test_id: &str) -> freecs::Entity {
let entity = self.heading(text);
self.set_test_id(entity, test_id);
entity
}
pub fn progress_bar_id(&mut self, initial: f32, test_id: &str) -> freecs::Entity {
let entity = self.progress_bar(initial);
self.set_test_id(entity, test_id);
entity
}
pub fn radio_id(
&mut self,
label: &str,
group_id: u32,
option_index: usize,
test_id: &str,
) -> freecs::Entity {
let entity = self.radio(label, group_id, option_index);
self.set_test_id(entity, test_id);
entity
}
pub fn tab_bar_id(&mut self, labels: &[&str], initial: usize, test_id: &str) -> freecs::Entity {
let entity = self.tab_bar(labels, initial);
self.set_test_id(entity, test_id);
entity
}
pub fn color_picker_id(&mut self, initial_color: Vec4, test_id: &str) -> freecs::Entity {
let entity = self.color_picker(initial_color);
self.set_test_id(entity, test_id);
entity
}
pub fn color_picker_hsv_id(&mut self, initial_color: Vec4, test_id: &str) -> freecs::Entity {
let entity = self.color_picker_hsv(initial_color);
self.set_test_id(entity, test_id);
entity
}
pub fn range_slider_id(
&mut self,
min: f32,
max: f32,
initial_low: f32,
initial_high: f32,
test_id: &str,
) -> freecs::Entity {
let entity = self.range_slider(min, max, initial_low, initial_high);
self.set_test_id(entity, test_id);
entity
}
pub fn selectable_label_id(
&mut self,
text: &str,
group_id: Option<u32>,
test_id: &str,
) -> freecs::Entity {
let entity = self.selectable_label(text, group_id);
self.set_test_id(entity, test_id);
entity
}
pub fn multi_select_id(&mut self, options: &[&str], test_id: &str) -> freecs::Entity {
let entity = self.multi_select(options);
self.set_test_id(entity, test_id);
entity
}
pub fn date_picker_id(
&mut self,
year: i32,
month: u32,
day: u32,
test_id: &str,
) -> freecs::Entity {
let entity = self.date_picker(year, month, day);
self.set_test_id(entity, test_id);
entity
}
pub fn menu_id(&mut self, label: &str, items: &[&str], test_id: &str) -> freecs::Entity {
let entity = self.menu(label, items);
self.set_test_id(entity, test_id);
entity
}
pub fn splitter_id(
&mut self,
direction: crate::ecs::ui::components::SplitDirection,
initial_ratio: f32,
test_id: &str,
) -> freecs::Entity {
let entity = self.splitter(direction, initial_ratio);
self.set_test_id(entity, test_id);
entity
}
pub fn text_area_id(
&mut self,
placeholder: &str,
rows: usize,
test_id: &str,
) -> freecs::Entity {
let entity = self.text_area(placeholder, rows);
self.set_test_id(entity, test_id);
entity
}
pub fn text_area_with_value_id(
&mut self,
placeholder: &str,
rows: usize,
initial_text: &str,
test_id: &str,
) -> freecs::Entity {
let entity = self.text_area_with_value(placeholder, rows, initial_text);
self.set_test_id(entity, test_id);
entity
}
pub fn text_input_max_length_id(
&mut self,
placeholder: &str,
max_length: usize,
test_id: &str,
) -> freecs::Entity {
let entity = self.text_input_max_length(placeholder, max_length);
self.set_test_id(entity, test_id);
entity
}
pub fn slider_logarithmic_id(
&mut self,
min: f32,
max: f32,
initial: f32,
test_id: &str,
) -> freecs::Entity {
let entity = self.slider_logarithmic(min, max, initial);
self.set_test_id(entity, test_id);
entity
}
pub fn dropdown_searchable_id(
&mut self,
options: &[&str],
initial: usize,
test_id: &str,
) -> freecs::Entity {
let entity = self.dropdown_searchable(options, initial);
self.set_test_id(entity, test_id);
entity
}
pub fn canvas_id(&mut self, size: Vec2, test_id: &str) -> freecs::Entity {
let entity = self.canvas(size);
self.set_test_id(entity, test_id);
entity
}
pub fn image_node_id(
&mut self,
texture_index: u32,
size: Vec2,
test_id: &str,
) -> freecs::Entity {
let entity = self.image_node(texture_index, size);
self.set_test_id(entity, test_id);
entity
}
pub fn confirm_dialog_id(
&mut self,
title: &str,
message: &str,
test_id: &str,
) -> freecs::Entity {
let entity = self.confirm_dialog(title, message);
self.set_test_id(entity, test_id);
entity
}
pub fn data_grid_id(
&mut self,
columns: &[DataGridColumn],
pool_size: usize,
test_id: &str,
) -> freecs::Entity {
let entity = self.data_grid(columns, pool_size);
self.set_test_id(entity, test_id);
entity
}
pub fn table_id(&mut self, headers: &[&str], widths: &[f32], test_id: &str) -> freecs::Entity {
let entity = self.table(headers, widths);
self.set_test_id(entity, test_id);
entity
}
pub fn command_palette_id(&mut self, pool_size: usize, test_id: &str) -> freecs::Entity {
let entity = self.command_palette(pool_size);
self.set_test_id(entity, test_id);
entity
}
pub fn tile_container_id(&mut self, size: Vec2, test_id: &str) -> freecs::Entity {
let entity = self.tile_container(size);
self.set_test_id(entity, test_id);
entity
}
pub fn text_input(&mut self, placeholder: &str) -> freecs::Entity {
self.scoped(|tree| tree.add_text_input(placeholder))
}
pub fn text_input_max_length(
&mut self,
placeholder: &str,
max_length: usize,
) -> freecs::Entity {
self.scoped(|tree| tree.add_text_input_max_length(placeholder, max_length))
}
pub fn text_area(&mut self, placeholder: &str, rows: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_text_area(placeholder, rows))
}
pub fn text_area_with_value(
&mut self,
placeholder: &str,
rows: usize,
initial_text: &str,
) -> freecs::Entity {
self.scoped(|tree| tree.add_text_area_with_value(placeholder, rows, initial_text))
}
pub fn dropdown(&mut self, options: &[&str], initial: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_dropdown(options, initial))
}
pub fn dropdown_searchable(&mut self, options: &[&str], initial: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_dropdown_searchable(options, initial))
}
pub fn multi_select(&mut self, options: &[&str]) -> freecs::Entity {
self.scoped(|tree| tree.add_multi_select(options))
}
pub fn date_picker(&mut self, year: i32, month: u32, day: u32) -> freecs::Entity {
self.scoped(|tree| tree.add_date_picker(year, month, day))
}
pub fn menu(&mut self, label: &str, items: &[&str]) -> freecs::Entity {
self.scoped(|tree| tree.add_menu(label, items))
}
pub fn color_picker(&mut self, initial_color: Vec4) -> freecs::Entity {
self.scoped(|tree| tree.add_color_picker(initial_color))
}
pub fn color_picker_hsv(&mut self, initial_color: Vec4) -> freecs::Entity {
self.scoped(|tree| tree.add_color_picker_hsv(initial_color))
}
pub fn color_wheel(&mut self, initial_color: Vec4) -> freecs::Entity {
self.scoped(|tree| tree.add_color_wheel(initial_color))
}
pub fn splitter(
&mut self,
direction: crate::ecs::ui::components::SplitDirection,
initial_ratio: f32,
) -> freecs::Entity {
self.scoped(|tree| tree.add_splitter(direction, initial_ratio))
}
pub fn range_slider(
&mut self,
min: f32,
max: f32,
initial_low: f32,
initial_high: f32,
) -> freecs::Entity {
self.scoped(|tree| tree.add_range_slider(min, max, initial_low, initial_high))
}
pub fn selectable_label(&mut self, text: &str, group_id: Option<u32>) -> freecs::Entity {
self.scoped(|tree| tree.add_selectable_label(text, group_id))
}
pub fn drag_value(&mut self, min: f32, max: f32, initial: f32) -> freecs::Entity {
self.scoped(|tree| tree.add_drag_value(min, max, initial))
}
pub fn drag_value_configured(&mut self, config: DragValueConfig<'_>) -> freecs::Entity {
self.scoped(|tree| tree.add_drag_value_configured(config))
}
pub fn context_menu(&mut self, items: &[(&str, Option<&str>)]) -> freecs::Entity {
self.scoped(|tree| tree.add_context_menu(items))
}
pub fn context_menu_from_builder(
&mut self,
builder: crate::ecs::ui::widgets::ContextMenuBuilder,
) -> freecs::Entity {
self.scoped(|tree| tree.add_context_menu_from_builder(builder))
}
pub fn tab_bar(&mut self, labels: &[&str], initial: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_tab_bar(labels, initial))
}
pub fn image_node(&mut self, texture_index: u32, size: Vec2) -> freecs::Entity {
self.scoped(|tree| tree.add_image_node(texture_index, size))
}
pub fn theme_dropdown(&mut self) -> freecs::Entity {
self.scoped(|tree| tree.add_theme_dropdown())
}
pub fn spinner(&mut self) -> freecs::Entity {
self.scoped(|tree| tree.add_spinner())
}
pub fn breadcrumb(&mut self, segments: &[&str]) -> freecs::Entity {
self.scoped(|tree| tree.add_breadcrumb(segments))
}
pub fn virtual_list(&mut self, item_height: f32, pool_size: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_virtual_list(item_height, pool_size))
}
pub fn rich_text_editor(&mut self, placeholder: &str, rows: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_rich_text_editor(placeholder, rows))
}
pub fn rich_text(&mut self, spans: &[TextSpan]) -> freecs::Entity {
self.scoped(|tree| tree.add_rich_text(spans))
}
pub fn data_grid(&mut self, columns: &[DataGridColumn], pool_size: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_data_grid(columns, pool_size))
}
pub fn table(&mut self, headers: &[&str], widths: &[f32]) -> freecs::Entity {
self.scoped(|tree| tree.add_table(headers, widths))
}
pub fn confirm_dialog(&mut self, title: &str, message: &str) -> freecs::Entity {
self.scoped(|tree| tree.add_confirm_dialog(title, message))
}
pub fn command_palette(&mut self, pool_size: usize) -> freecs::Entity {
self.scoped(|tree| tree.add_command_palette(pool_size))
}
pub fn property_grid(&mut self, label_width: f32) -> freecs::Entity {
self.scoped(|tree| tree.add_property_grid(label_width))
}
pub fn property_row(
&mut self,
grid: freecs::Entity,
parent: freecs::Entity,
label: &str,
) -> freecs::Entity {
self.tree.add_property_row(grid, parent, label)
}
pub fn property_section(&mut self, parent: freecs::Entity, label: &str) -> freecs::Entity {
self.tree.add_property_section(parent, label)
}
pub fn tree_node(
&mut self,
tree_entity: freecs::Entity,
parent_container: freecs::Entity,
label: &str,
depth: usize,
user_data: u64,
) -> freecs::Entity {
self.tree
.add_tree_node(tree_entity, parent_container, label, depth, user_data)
}
pub fn tree_node_lazy(
&mut self,
tree_entity: freecs::Entity,
parent_container: freecs::Entity,
label: &str,
depth: usize,
user_data: u64,
) -> freecs::Entity {
self.tree
.add_tree_node_lazy(tree_entity, parent_container, label, depth, user_data)
}
pub fn row(&mut self, f: impl FnOnce(&mut Ui<'_, 'w>)) -> freecs::Entity {
self.container(|tree| tree.add_row(), f)
}
pub fn column(&mut self, f: impl FnOnce(&mut Ui<'_, 'w>)) -> freecs::Entity {
self.container(|tree| tree.add_column(), f)
}
pub fn collapsing_header(
&mut self,
label: &str,
default_open: bool,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
self.container(|tree| tree.add_collapsing_header(label, default_open), f)
}
pub fn scroll_area(&mut self, size: Vec2, f: impl FnOnce(&mut Ui<'_, 'w>)) -> freecs::Entity {
self.container(|tree| tree.add_scroll_area(size), f)
}
pub fn scroll_area_fill(
&mut self,
padding: f32,
spacing: f32,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
self.container(|tree| tree.add_scroll_area_fill(padding, spacing), f)
}
pub fn floating_panel(
&mut self,
id: &str,
title: &str,
rect: Rect,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
let id = id.to_string();
let title = title.to_string();
self.container(move |tree| tree.add_floating_panel(&id, &title, rect), f)
}
pub fn docked_panel_left(
&mut self,
id: &str,
title: &str,
default_width: f32,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
let id = id.to_string();
let title = title.to_string();
self.container(
move |tree| tree.add_docked_panel_left(&id, &title, default_width),
f,
)
}
pub fn docked_panel_right(
&mut self,
id: &str,
title: &str,
default_width: f32,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
let id = id.to_string();
let title = title.to_string();
self.container(
move |tree| tree.add_docked_panel_right(&id, &title, default_width),
f,
)
}
pub fn docked_panel_top(
&mut self,
id: &str,
title: &str,
default_height: f32,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
let id = id.to_string();
let title = title.to_string();
self.container(
move |tree| tree.add_docked_panel_top(&id, &title, default_height),
f,
)
}
pub fn docked_panel_bottom(
&mut self,
id: &str,
title: &str,
default_height: f32,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
let id = id.to_string();
let title = title.to_string();
self.container(
move |tree| tree.add_docked_panel_bottom(&id, &title, default_height),
f,
)
}
pub fn modal_dialog(
&mut self,
title: &str,
width: f32,
height: f32,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
self.container(|tree| tree.add_modal_dialog(title, width, height), f)
}
pub fn tree_view(
&mut self,
multi_select: bool,
f: impl FnOnce(&mut Ui<'_, 'w>),
) -> freecs::Entity {
self.container(|tree| tree.add_tree_view(multi_select), f)
}
pub fn tile_container(&mut self, size: Vec2) -> freecs::Entity {
self.scoped(|tree| tree.add_tile_container(size))
}
pub fn build_tiles(
&mut self,
container: freecs::Entity,
f: impl FnOnce(&mut crate::ecs::ui::widgets::TileBuilder<'_, 'w>),
) {
self.tree.build_tiles(container, f);
}
pub fn enabled(&mut self, enabled: bool, f: impl FnOnce(&mut Ui<'_, 'w>)) -> freecs::Entity {
let container = self.scoped(|tree| tree.add_column());
let content = ui_widget_content(self.tree.world_mut(), container).unwrap_or(container);
let mut child_ui = Ui {
tree: &mut *self.tree,
parent: content,
};
f(&mut child_ui);
if !enabled {
ui_set_disabled_recursive(self.tree.world_mut(), container, true);
}
container
}
}
impl<'w> UiTreeBuilder<'w> {
pub fn build_ui(&mut self, parent: freecs::Entity, f: impl FnOnce(&mut Ui<'_, 'w>)) {
let needs_push = self.current_parent() != parent;
if needs_push {
self.push_parent(parent);
}
let mut ui = Ui { tree: self, parent };
f(&mut ui);
if needs_push {
ui.tree.pop_parent();
}
}
}