use nalgebra_glm::{Vec2, Vec4};
use crate::ecs::text::components::{TextAlignment, VerticalAlignment};
use crate::ecs::ui::builder::UiTreeBuilder;
use crate::ecs::ui::components::*;
use crate::ecs::ui::layout_types::FlowDirection;
use crate::ecs::ui::state::{UiBase, UiHover, UiPressed, UiStateTrait};
use crate::ecs::ui::units::{Ab, Rl};
impl<'a> UiTreeBuilder<'a> {
pub fn add_button(&mut self, text: &str) -> freecs::Entity {
let theme = self
.world_mut()
.resources
.retained_ui
.theme_state
.active_theme();
let font_size = theme.font_size;
let button_height = theme.button_height;
let corner_radius = theme.corner_radius;
let border_width = theme.border_width;
let border_color = theme.border_color;
let text_slot = self.world_mut().resources.text_cache.add_text(text);
let button_entity = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, button_height)))
.with_rect(corner_radius, border_width, border_color)
.with_theme_border_color(ThemeColor::Border)
.with_theme_color::<UiBase>(ThemeColor::Background)
.with_theme_color::<UiHover>(ThemeColor::BackgroundHover)
.with_theme_color::<UiPressed>(ThemeColor::BackgroundActive)
.with_interaction()
.with_transition::<UiHover>(8.0, 6.0)
.with_transition::<UiPressed>(12.0, 8.0)
.with_cursor_icon(winit::window::CursorIcon::Pointer)
.with_children(|tree| {
tree.add_node()
.with_text_slot(text_slot, font_size)
.with_theme_color::<UiBase>(ThemeColor::Text)
.without_pointer_events()
.done();
})
.done();
self.world_mut().ui.set_ui_widget_state(
button_entity,
UiWidgetState::Button(UiButtonData {
clicked: false,
text_slot,
}),
);
if let Some(interaction) = self
.world_mut()
.ui
.get_ui_node_interaction_mut(button_entity)
{
interaction.accessible_role = Some(AccessibleRole::Button);
}
self.assign_tab_index(button_entity);
button_entity
}
pub fn add_button_colored(&mut self, text: &str, color: Vec4) -> freecs::Entity {
let theme = self
.world_mut()
.resources
.retained_ui
.theme_state
.active_theme();
let font_size = theme.font_size;
let button_height = theme.button_height;
let corner_radius = theme.corner_radius;
let border_width = theme.border_width;
let border_color = theme.border_color;
let hover_color = Vec4::new(
(color.x + 0.1).min(1.0),
(color.y + 0.1).min(1.0),
(color.z + 0.1).min(1.0),
color.w,
);
let active_color = Vec4::new(
(color.x - 0.1).max(0.0),
(color.y - 0.1).max(0.0),
(color.z - 0.1).max(0.0),
color.w,
);
let text_slot = self.world_mut().resources.text_cache.add_text(text);
let button_entity = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, button_height)))
.with_rect(corner_radius, border_width, border_color)
.with_theme_border_color(ThemeColor::Border)
.with_color::<UiBase>(color)
.with_color::<UiHover>(hover_color)
.with_color::<UiPressed>(active_color)
.with_interaction()
.with_transition::<UiHover>(8.0, 6.0)
.with_transition::<UiPressed>(12.0, 8.0)
.with_cursor_icon(winit::window::CursorIcon::Pointer)
.with_children(|tree| {
tree.add_node()
.with_text_slot(text_slot, font_size)
.with_theme_color::<UiBase>(ThemeColor::Text)
.without_pointer_events()
.done();
})
.done();
self.world_mut().ui.set_ui_widget_state(
button_entity,
UiWidgetState::Button(UiButtonData {
clicked: false,
text_slot,
}),
);
self.assign_tab_index(button_entity);
button_entity
}
pub fn add_icon_button(
&mut self,
texture_index: u32,
icon_size: Vec2,
text: &str,
) -> freecs::Entity {
let theme = self
.world_mut()
.resources
.retained_ui
.theme_state
.active_theme();
let font_size = theme.font_size;
let button_height = theme.button_height;
let corner_radius = theme.corner_radius;
let border_width = theme.border_width;
let border_color = theme.border_color;
let text_slot = self.world_mut().resources.text_cache.add_text(text);
let button_entity = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, button_height)))
.with_rect(corner_radius, border_width, border_color)
.with_theme_border_color(ThemeColor::Border)
.with_theme_color::<UiBase>(ThemeColor::Background)
.with_theme_color::<UiHover>(ThemeColor::BackgroundHover)
.with_theme_color::<UiPressed>(ThemeColor::BackgroundActive)
.with_interaction()
.with_transition::<UiHover>(8.0, 6.0)
.with_transition::<UiPressed>(12.0, 8.0)
.with_cursor_icon(winit::window::CursorIcon::Pointer)
.flow(FlowDirection::Horizontal, 8.0, 8.0)
.with_children(|tree| {
tree.add_node()
.flow_child(Ab(icon_size))
.with_image(texture_index)
.with_theme_color::<UiBase>(ThemeColor::Text)
.without_pointer_events()
.done();
tree.add_node()
.flow_child(Ab(Vec2::new(0.0, button_height)))
.flex_grow(1.0)
.with_text_slot(text_slot, font_size)
.with_text_alignment(TextAlignment::Left, VerticalAlignment::Middle)
.with_theme_color::<UiBase>(ThemeColor::Text)
.without_pointer_events()
.done();
})
.done();
self.world_mut().ui.set_ui_widget_state(
button_entity,
UiWidgetState::Button(UiButtonData {
clicked: false,
text_slot,
}),
);
self.assign_tab_index(button_entity);
button_entity
}
pub fn add_button_rich(&mut self, spans: &[TextSpan]) -> freecs::Entity {
let theme = self
.world_mut()
.resources
.retained_ui
.theme_state
.active_theme();
let font_size = theme.font_size;
let button_height = theme.button_height;
let corner_radius = theme.corner_radius;
let border_width = theme.border_width;
let border_color = theme.border_color;
let text_color = theme.text_color;
let button_entity = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, button_height)))
.with_rect(corner_radius, border_width, border_color)
.with_theme_border_color(ThemeColor::Border)
.with_theme_color::<UiBase>(ThemeColor::Background)
.with_theme_color::<UiHover>(ThemeColor::BackgroundHover)
.with_theme_color::<UiPressed>(ThemeColor::BackgroundActive)
.with_interaction()
.with_transition::<UiHover>(8.0, 6.0)
.with_transition::<UiPressed>(12.0, 8.0)
.with_cursor_icon(winit::window::CursorIcon::Pointer)
.entity();
self.push_parent(button_entity);
let span_container = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, button_height)))
.flow(FlowDirection::Horizontal, 0.0, 8.0)
.flow_wrap()
.without_pointer_events()
.entity();
self.push_parent(span_container);
for span in spans {
let text_slot = self.world_mut().resources.text_cache.add_text(&span.text);
let mut span_font_size = span.font_size_override.unwrap_or(font_size);
let span_color = span.color.unwrap_or(text_color);
let span_font_index = span.font_index.unwrap_or(0);
if span.bold {
span_font_size *= 1.05;
}
let outline_width = if span.bold { 0.4 } else { 0.0 };
let outline_color = if span.bold {
span_color
} else {
Vec4::new(0.0, 0.0, 0.0, 0.0)
};
let span_wrapper = self
.add_node()
.flow_child(Ab(Vec2::new(0.0, button_height)))
.auto_size(crate::ecs::ui::components::AutoSizeMode::Width)
.flow(FlowDirection::Vertical, 0.0, 0.0)
.without_pointer_events()
.entity();
self.push_parent(span_wrapper);
let entity = self.add_node().done();
if let Some(content) = self.world_mut().ui.get_ui_node_content_mut(entity) {
*content = crate::ecs::ui::components::UiNodeContent::Text {
text_slot,
font_index: span_font_index,
font_size_override: Some(span_font_size),
outline_color,
outline_width,
alignment: TextAlignment::Left,
vertical_alignment: VerticalAlignment::Middle,
overflow: crate::ecs::ui::components::TextOverflow::default(),
monospace_width: None,
};
}
if let Some(node) = self.world_mut().ui.get_ui_layout_node_mut(entity) {
node.flow_child_size = Some(Ab(Vec2::new(0.0, button_height)).into());
node.auto_size = crate::ecs::ui::components::AutoSizeMode::Width;
node.pointer_events = false;
}
if let Some(color) = self.world_mut().ui.get_ui_node_color_mut(entity) {
color.colors[UiBase::INDEX] = Some(span_color);
}
if span.color.is_none() {
let mut binding = crate::ecs::ui::components::UiThemeBinding::default();
binding.color_roles[UiBase::INDEX] = Some(ThemeColor::Text);
self.world_mut().ui.set_ui_theme_binding(entity, binding);
}
self.pop_parent();
}
self.pop_parent();
self.pop_parent();
self.world_mut().ui.set_ui_widget_state(
button_entity,
UiWidgetState::Button(UiButtonData {
clicked: false,
text_slot: 0,
}),
);
self.assign_tab_index(button_entity);
button_entity
}
pub fn add_image_node(&mut self, texture_index: u32, size: Vec2) -> freecs::Entity {
self.add_node()
.flow_child(Ab(size))
.with_image(texture_index)
.with_color::<UiBase>(Vec4::new(1.0, 1.0, 1.0, 1.0))
.without_pointer_events()
.done()
}
pub fn add_selectable_label(&mut self, text: &str, group_id: Option<u32>) -> freecs::Entity {
let theme = self
.world_mut()
.resources
.retained_ui
.theme_state
.active_theme();
let font_size = theme.font_size;
let corner_radius = theme.corner_radius;
let text_slot = self.world_mut().resources.text_cache.add_text(text);
let label_entity = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, font_size * 1.5)))
.with_rect(corner_radius, 0.0, Vec4::new(0.0, 0.0, 0.0, 0.0))
.with_color::<UiBase>(Vec4::new(0.0, 0.0, 0.0, 0.0))
.with_theme_color::<UiHover>(ThemeColor::BackgroundHover)
.with_interaction()
.with_transition::<UiHover>(8.0, 6.0)
.with_cursor_icon(winit::window::CursorIcon::Pointer)
.with_children(|tree| {
tree.add_node()
.boundary(Ab(Vec2::new(8.0, 0.0)), Rl(Vec2::new(100.0, 100.0)))
.with_text_slot(text_slot, font_size)
.with_text_alignment(TextAlignment::Left, VerticalAlignment::Middle)
.with_theme_color::<UiBase>(ThemeColor::Text)
.without_pointer_events()
.done();
})
.done();
self.world_mut().ui.set_ui_widget_state(
label_entity,
UiWidgetState::SelectableLabel(UiSelectableLabelData {
selected: false,
changed: false,
text_slot,
group_id,
}),
);
if let Some(gid) = group_id {
self.world_mut()
.resources
.retained_ui
.selectable_label_groups
.entry(gid)
.or_default()
.push(label_entity);
}
label_entity
}
}