use crate::text::ScreenAnchor;
use crate::text::ui_root;
use nightshade::ecs::ui::layout_types::FlowDirection;
use nightshade::ecs::ui::units::UiValue;
use nightshade::prelude::*;
pub fn spawn_panel(world: &mut World, anchor: ScreenAnchor, width: f32, height: f32) -> Entity {
let root = ui_root(world);
let (position, anchor_kind) = panel_anchor(anchor);
let panel = {
let mut tree = UiTreeBuilder::from_parent(world, root);
tree.add_node()
.window(position, Ab(vec2(width, height)), anchor_kind)
.with_rect(8.0, 1.0, Vec4::new(1.0, 1.0, 1.0, 0.1))
.color_raw::<UiBase>(Vec4::new(0.05, 0.05, 0.08, 0.85))
.flow(FlowDirection::Vertical, 12.0, 8.0)
.entity()
};
ui_mark_render_dirty(world);
panel
}
pub fn panel_button(world: &mut World, panel: Entity, text: &str) -> Entity {
let button = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_button(text)
};
ui_mark_render_dirty(world);
button
}
pub fn panel_label(world: &mut World, panel: Entity, text: &str) -> Entity {
let label = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_node()
.flow_child(Rl(vec2(100.0, 0.0)) + Ab(vec2(0.0, 24.0)))
.with_text(text, 18.0)
.color_raw::<UiBase>(Vec4::new(1.0, 1.0, 1.0, 1.0))
.entity()
};
ui_mark_render_dirty(world);
label
}
pub fn panel_checkbox(world: &mut World, panel: Entity, label: &str, initial: bool) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_checkbox(label, initial)
};
ui_mark_render_dirty(world);
entity
}
#[inline]
pub fn checkbox_value(world: &World, checkbox: Entity) -> bool {
ui_checkbox_value(world, checkbox).unwrap_or(false)
}
pub fn panel_slider(world: &mut World, panel: Entity, min: f32, max: f32, initial: f32) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_slider(min, max, initial)
};
ui_mark_render_dirty(world);
entity
}
#[inline]
pub fn slider_value(world: &World, slider: Entity) -> f32 {
ui_slider_value(world, slider).unwrap_or(0.0)
}
pub fn set_slider_value(world: &mut World, slider: Entity, value: f32) {
ui_slider_set_value(world, slider, value);
ui_mark_render_dirty(world);
}
pub fn panel_text_input(world: &mut World, panel: Entity, placeholder: &str) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_text_input(placeholder)
};
ui_mark_render_dirty(world);
entity
}
#[inline]
pub fn text_input_changed(world: &World, input: Entity) -> Option<String> {
ui_text_input_changed(world, input).map(str::to_string)
}
pub fn panel_dropdown(
world: &mut World,
panel: Entity,
options: &[&str],
initial: usize,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_dropdown(options, initial)
};
ui_mark_render_dirty(world);
entity
}
#[inline]
pub fn dropdown_selected(world: &World, dropdown: Entity) -> Option<usize> {
ui_dropdown_selected_changed(world, dropdown)
}
pub fn panel_progress_bar(world: &mut World, panel: Entity, initial: f32) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_progress_bar(initial)
};
ui_mark_render_dirty(world);
entity
}
pub fn set_progress(world: &mut World, bar: Entity, value: f32) {
ui_progress_bar_set_value(world, bar, value);
ui_mark_render_dirty(world);
}
pub fn panel_toggle(world: &mut World, panel: Entity, initial: bool) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_toggle(initial)
};
ui_mark_render_dirty(world);
entity
}
#[inline]
pub fn toggle_value(world: &World, toggle: Entity) -> bool {
ui_toggle_value(world, toggle).unwrap_or(false)
}
pub fn panel_radio(
world: &mut World,
panel: Entity,
label: &str,
group_id: u32,
option_index: usize,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_radio(label, group_id, option_index)
};
ui_mark_render_dirty(world);
entity
}
#[inline]
pub fn radio_selected(world: &World, group_id: u32) -> Option<usize> {
ui_radio_group_value(world, group_id)
}
pub fn panel_range_slider(
world: &mut World,
panel: Entity,
min: f32,
max: f32,
low: f32,
high: f32,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_range_slider(min, max, low, high)
};
ui_mark_render_dirty(world);
entity
}
pub fn set_range(world: &mut World, slider: Entity, low: f32, high: f32) {
ui_range_slider_set_values(world, slider, low, high);
ui_mark_render_dirty(world);
}
pub fn panel_tabs(world: &mut World, panel: Entity, labels: &[&str], initial: usize) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_tab_bar(labels, initial)
};
ui_mark_render_dirty(world);
entity
}
pub fn set_tab(world: &mut World, tabs: Entity, index: usize) {
ui_tab_bar_set_value(world, tabs, index);
ui_mark_render_dirty(world);
}
pub fn panel_collapsing(world: &mut World, panel: Entity, label: &str, open: bool) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_collapsing_header(label, open)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_color_picker(world: &mut World, panel: Entity, initial: [f32; 4]) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_color_picker(Vec4::new(initial[0], initial[1], initial[2], initial[3]))
};
ui_mark_render_dirty(world);
entity
}
pub fn color_value(world: &World, picker: Entity) -> [f32; 4] {
ui_color_picker_color(world, picker)
.map(|color| [color.x, color.y, color.z, color.w])
.unwrap_or([1.0, 1.0, 1.0, 1.0])
}
#[inline]
pub fn button_clicked(world: &World, button: Entity) -> bool {
ui_clicked(world, button)
}
#[inline]
pub fn button_hovered(world: &World, button: Entity) -> bool {
world
.ui
.get_ui_node_interaction(button)
.is_some_and(|interaction| interaction.hovered)
}
#[inline]
pub fn despawn_panel(world: &mut World, panel: Entity) {
despawn_recursive_immediate(world, panel);
ui_mark_render_dirty(world);
}
pub fn panel_row(world: &mut World, panel: Entity, height: f32) -> Entity {
let row = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_node()
.flow_child(Rl(vec2(100.0, 0.0)) + Ab(vec2(0.0, height)))
.flow(FlowDirection::Horizontal, 0.0, 8.0)
.entity()
};
ui_mark_render_dirty(world);
row
}
pub fn panel_grid(
world: &mut World,
panel: Entity,
columns: usize,
row_height: f32,
height: f32,
) -> Entity {
let grid = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_node()
.flow_child(Rl(vec2(100.0, 0.0)) + Ab(vec2(0.0, height)))
.grid(columns, row_height, 0.0, 8.0, 8.0)
.entity()
};
ui_mark_render_dirty(world);
grid
}
pub fn panel_scroll(world: &mut World, panel: Entity, height: f32) -> Entity {
let content = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
let area = tree.add_scroll_area(vec2(0.0, height));
widget::<UiScrollAreaData>(tree.world_mut(), area)
.map(|data| data.content_entity)
.unwrap_or(area)
};
ui_mark_render_dirty(world);
content
}
pub fn set_scroll_offset(world: &mut World, scroll_area: Entity, offset: f32) {
ui_scroll_area_set_offset(world, scroll_area, offset);
ui_mark_render_dirty(world);
}
pub fn set_focus_order(world: &mut World, entity: Entity, order: i32) {
if let Some(interaction) = world.ui.get_ui_node_interaction_mut(entity) {
interaction.tab_index = Some(order);
}
}
pub fn focus_widget(world: &mut World, entity: Entity) {
world.resources.retained_ui.interaction.focused_entity = Some(entity);
}
pub fn panel_text_area(world: &mut World, panel: Entity, placeholder: &str, rows: usize) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_text_area(placeholder, rows)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_text_area_with_value(
world: &mut World,
panel: Entity,
placeholder: &str,
rows: usize,
initial: &str,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_text_area_with_value(placeholder, rows, initial)
};
ui_mark_render_dirty(world);
entity
}
pub fn set_text_area(world: &mut World, area: Entity, text: &str) {
ui_text_area_set_value(world, area, text);
ui_mark_render_dirty(world);
}
pub fn panel_multi_select(world: &mut World, panel: Entity, options: &[&str]) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_multi_select(options)
};
ui_mark_render_dirty(world);
entity
}
pub fn set_multi_select(world: &mut World, widget: Entity, indices: &[usize]) {
ui_multi_select_set_selected(world, widget, indices);
ui_mark_render_dirty(world);
}
pub fn panel_date_picker(
world: &mut World,
panel: Entity,
year: i32,
month: u32,
day: u32,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_date_picker(year, month, day)
};
ui_mark_render_dirty(world);
entity
}
pub fn set_date(world: &mut World, picker: Entity, year: i32, month: u32, day: u32) {
ui_date_picker_set_value(world, picker, year, month, day);
ui_mark_render_dirty(world);
}
pub fn panel_menu(world: &mut World, panel: Entity, label: &str, items: &[&str]) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_menu(label, items)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_color_picker_hsv(world: &mut World, panel: Entity, initial: [f32; 4]) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_color_picker_hsv(rgba(initial))
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_splitter(
world: &mut World,
panel: Entity,
direction: SplitDirection,
initial_ratio: f32,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_splitter(direction, initial_ratio)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_breadcrumb(world: &mut World, panel: Entity, segments: &[&str]) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_breadcrumb(segments)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_virtual_list(
world: &mut World,
panel: Entity,
item_height: f32,
pool_size: usize,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_virtual_list(item_height, pool_size)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_table(world: &mut World, panel: Entity, headers: &[&str], widths: &[f32]) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_table(headers, widths)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_data_grid(
world: &mut World,
panel: Entity,
columns: &[(&str, f32)],
pool_size: usize,
) -> Entity {
let grid_columns: Vec<DataGridColumn> = columns
.iter()
.map(|(header, width)| DataGridColumn::new(header, *width))
.collect();
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_data_grid(&grid_columns, pool_size)
};
ui_mark_render_dirty(world);
entity
}
pub fn set_data_grid_rows(world: &mut World, grid: Entity, count: usize) {
ui_data_grid_set_row_count(world, grid, count);
}
pub fn set_data_grid_cell(world: &mut World, grid: Entity, row: usize, column: usize, text: &str) {
ui_data_grid_set_cell(world, grid, row, column, text);
}
#[inline]
pub fn data_grid_selection_changed(world: &World, grid: Entity) -> bool {
ui_data_grid_selection_changed(world, grid)
}
pub fn panel_command_palette(world: &mut World, panel: Entity, pool_size: usize) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_command_palette(pool_size)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_property_grid(world: &mut World, panel: Entity, label_width: f32) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_property_grid(label_width)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_property_row(world: &mut World, grid: Entity, label: &str) -> Entity {
let row = {
let mut tree = UiTreeBuilder::from_parent(world, grid);
tree.add_property_row(grid, grid, label)
};
ui_mark_render_dirty(world);
row
}
pub fn panel_tree_view(world: &mut World, panel: Entity, multi_select: bool) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_tree_view(multi_select)
};
ui_mark_render_dirty(world);
entity
}
pub fn tree_content(world: &World, tree_view: Entity) -> Entity {
widget::<UiTreeViewData>(world, tree_view)
.map(|data| data.content_entity)
.unwrap_or(tree_view)
}
pub fn tree_node(
world: &mut World,
tree_view: Entity,
parent_container: Entity,
label: &str,
depth: usize,
user_data: u64,
) -> Entity {
let node = {
let mut tree = UiTreeBuilder::from_parent(world, parent_container);
tree.add_tree_node(tree_view, parent_container, label, depth, user_data)
};
ui_mark_render_dirty(world);
node
}
pub fn tree_node_children(world: &World, node: Entity) -> Entity {
widget::<UiTreeNodeData>(world, node)
.map(|data| data.children_container)
.unwrap_or(node)
}
pub fn set_tree_node_expanded(world: &mut World, node: Entity, expanded: bool) {
ui_tree_node_set_expanded(world, node, expanded);
ui_mark_render_dirty(world);
}
#[inline]
pub fn tree_view_selected(world: &World, tree_view: Entity) -> Vec<Entity> {
ui_tree_view_selected(world, tree_view)
}
pub fn panel_drag_value(
world: &mut World,
panel: Entity,
min: f32,
max: f32,
initial: f32,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_drag_value(min, max, initial)
};
ui_mark_render_dirty(world);
entity
}
#[inline]
pub fn drag_value(world: &World, widget: Entity) -> f32 {
ui_drag_value(world, widget).unwrap_or(0.0)
}
pub fn panel_selectable(
world: &mut World,
panel: Entity,
text: &str,
group: Option<u32>,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_selectable_label(text, group)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_modal(
world: &mut World,
panel: Entity,
title: &str,
width: f32,
height: f32,
) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_modal_dialog(title, width, height)
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_spinner(world: &mut World, panel: Entity) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_spinner()
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_separator(world: &mut World, panel: Entity) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_separator()
};
ui_mark_render_dirty(world);
entity
}
pub fn panel_heading(world: &mut World, panel: Entity, text: &str) -> Entity {
let entity = {
let mut tree = UiTreeBuilder::from_parent(world, panel);
tree.add_heading(text)
};
ui_mark_render_dirty(world);
entity
}
fn panel_anchor(anchor: ScreenAnchor) -> (UiValue<Vec2>, Anchor) {
match anchor {
ScreenAnchor::TopLeft => (Ab(vec2(20.0, 20.0)).into(), Anchor::TopLeft),
ScreenAnchor::TopCenter => (Rl(vec2(50.0, 0.0)) + Ab(vec2(0.0, 20.0)), Anchor::TopCenter),
ScreenAnchor::TopRight => (
Rl(vec2(100.0, 0.0)) + Ab(vec2(-20.0, 20.0)),
Anchor::TopRight,
),
ScreenAnchor::BottomLeft => (
Rl(vec2(0.0, 100.0)) + Ab(vec2(20.0, -20.0)),
Anchor::BottomLeft,
),
ScreenAnchor::BottomCenter => (
Rl(vec2(50.0, 100.0)) + Ab(vec2(0.0, -20.0)),
Anchor::BottomCenter,
),
ScreenAnchor::BottomRight => (
Rl(vec2(100.0, 100.0)) + Ab(vec2(-20.0, -20.0)),
Anchor::BottomRight,
),
ScreenAnchor::Center => (Rl(vec2(50.0, 50.0)).into(), Anchor::Center),
}
}
fn anchor_base(anchor: ScreenAnchor) -> (Vec2, Anchor) {
match anchor {
ScreenAnchor::TopLeft => (vec2(0.0, 0.0), Anchor::TopLeft),
ScreenAnchor::TopCenter => (vec2(50.0, 0.0), Anchor::TopCenter),
ScreenAnchor::TopRight => (vec2(100.0, 0.0), Anchor::TopRight),
ScreenAnchor::BottomLeft => (vec2(0.0, 100.0), Anchor::BottomLeft),
ScreenAnchor::BottomCenter => (vec2(50.0, 100.0), Anchor::BottomCenter),
ScreenAnchor::BottomRight => (vec2(100.0, 100.0), Anchor::BottomRight),
ScreenAnchor::Center => (vec2(50.0, 50.0), Anchor::Center),
}
}
fn rgba(color: [f32; 4]) -> Vec4 {
Vec4::new(color[0], color[1], color[2], color[3])
}
const PANEL_BORDER: Vec4 = Vec4::new(0.12, 0.14, 0.2, 0.7);
const PANEL_SHADOW: Vec4 = Vec4::new(0.0, 0.0, 0.0, 0.55);
pub fn spawn_panel_at(
world: &mut World,
anchor: ScreenAnchor,
offset: Vec2,
size: Vec2,
color: [f32; 4],
) -> Entity {
let root = ui_root(world);
let (base, anchor_kind) = anchor_base(anchor);
let panel = {
let mut tree = UiTreeBuilder::from_parent(world, root);
tree.add_node()
.window(Rl(base) + Ab(offset), Ab(size), anchor_kind)
.with_rect(10.0, 1.5, PANEL_BORDER)
.color_raw::<UiBase>(rgba(color))
.with_shadow(PANEL_SHADOW, vec2(0.0, 6.0), 18.0, 0.0)
.entity()
};
ui_mark_render_dirty(world);
panel
}
pub fn panel_text(
world: &mut World,
parent: Entity,
text: &str,
rect: [f32; 4],
font_size: f32,
color: [f32; 4],
align: TextAlignment,
) -> Entity {
let label = {
let mut tree = UiTreeBuilder::from_parent(world, parent);
tree.add_node()
.window(
Ab(vec2(rect[0], rect[1])),
Ab(vec2(rect[2], rect[3])),
Anchor::TopLeft,
)
.with_text(text, font_size)
.with_text_alignment(align, VerticalAlignment::Middle)
.color_raw::<UiBase>(rgba(color))
.without_pointer_events()
.entity()
};
ui_mark_render_dirty(world);
label
}
pub fn panel_box(
world: &mut World,
parent: Entity,
offset: Vec2,
size: Vec2,
color: [f32; 4],
) -> Entity {
let box_entity = {
let mut tree = UiTreeBuilder::from_parent(world, parent);
tree.add_node()
.window(Ab(offset), Ab(size), Anchor::TopLeft)
.with_rect(6.0, 0.0, Vec4::new(0.0, 0.0, 0.0, 0.0))
.color_raw::<UiBase>(rgba(color))
.without_pointer_events()
.entity()
};
ui_mark_render_dirty(world);
box_entity
}
pub fn panel_button_at(
world: &mut World,
parent: Entity,
label: &str,
offset: Vec2,
size: Vec2,
) -> Entity {
let button = {
let mut tree = UiTreeBuilder::from_parent(world, parent);
tree.add_node()
.window(Ab(offset), Ab(size), Anchor::TopLeft)
.with_rect(8.0, 1.5, PANEL_BORDER)
.color_raw::<UiBase>(Vec4::new(0.05, 0.06, 0.09, 0.92))
.color_raw::<UiHover>(Vec4::new(0.09, 0.11, 0.17, 0.95))
.color_raw::<UiPressed>(Vec4::new(0.04, 0.05, 0.08, 1.0))
.with_transition::<UiHover>(12.0, 6.0)
.with_transition::<UiPressed>(18.0, 10.0)
.with_transition::<UiSelected>(10.0, 5.0)
.with_interaction()
.entity()
};
if !label.is_empty() {
let mut tree = UiTreeBuilder::from_parent(world, button);
tree.add_node()
.window(Rl(vec2(50.0, 50.0)), Ab(size), Anchor::Center)
.with_text(label, 14.0)
.with_text_alignment(TextAlignment::Center, VerticalAlignment::Middle)
.color_raw::<UiBase>(Vec4::new(0.92, 0.94, 1.0, 1.0))
.without_pointer_events()
.entity();
}
ui_mark_render_dirty(world);
button
}
pub fn set_panel_rect(world: &mut World, node: Entity, offset: Vec2, size: Vec2) {
if let Some(layout) = world.ui.get_ui_layout_node_mut(node) {
layout.base_layout = Some(UiLayoutType::Window(WindowLayout {
position: Ab(offset).into(),
size: Ab(size).into(),
anchor: Anchor::TopLeft,
}));
}
ui_mark_render_dirty(world);
}
pub fn set_panel_color(world: &mut World, node: Entity, color: [f32; 4]) {
if let Some(node_color) = world.ui.get_ui_node_color_mut(node) {
node_color.colors[UiBase::INDEX] = Some(rgba(color));
}
ui_mark_render_dirty(world);
}
pub fn set_panel_text(world: &mut World, label: Entity, text: &str) {
ui_set_text(world, label, text);
}
pub fn set_panel_text_color(world: &mut World, label: Entity, color: [f32; 4]) {
set_panel_color(world, label, color);
}
pub fn set_panel_selected(world: &mut World, button: Entity, selected: bool, accent: [f32; 4]) {
if let Some(node_color) = world.ui.get_ui_node_color_mut(button) {
node_color.colors[UiSelected::INDEX] = Some(rgba(accent));
}
ui_set_selected(world, button, selected);
ui_mark_render_dirty(world);
}
pub fn set_panel_visible(world: &mut World, node: Entity, visible: bool) {
ui_set_visible(world, node, visible);
ui_mark_render_dirty(world);
}