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};
use crate::ecs::ui::types::{Anchor, Rect};
use crate::ecs::ui::units::{Ab, Rl};
use crate::render::wgpu::passes::geometry::UiLayer;
impl<'a> UiTreeBuilder<'a> {
pub fn add_floating_panel(&mut self, title: &str, rect: Rect) -> 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 border_color = theme.border_color;
let title_slot = self.world_mut().resources.text_cache.add_text(title);
let collapse_slot = self.world_mut().resources.text_cache.add_text("-");
let focus_order = self.world_mut().resources.retained_ui.next_focus_order;
self.world_mut().resources.retained_ui.next_focus_order += 1;
let mut header_entity = freecs::Entity::default();
let mut content_entity = freecs::Entity::default();
let mut collapse_button_entity = freecs::Entity::default();
let panel_entity = self
.add_node()
.window(
Ab(Vec2::new(rect.min.x, rect.min.y)),
Ab(Vec2::new(rect.width(), rect.height())),
Anchor::TopLeft,
)
.with_rect(corner_radius, 1.0, border_color)
.with_theme_border_color(ThemeColor::Border)
.with_theme_color::<UiBase>(ThemeColor::Background)
.with_layer(UiLayer::FloatingPanels)
.with_clip()
.with_depth(crate::ecs::ui::components::UiDepthMode::Set(
20.0 + focus_order as f32 * 10.0,
))
.with_children(|tree| {
header_entity = tree
.add_node()
.boundary(
Rl(Vec2::new(0.0, 0.0)),
Ab(Vec2::new(0.0, 32.0)) + Rl(Vec2::new(100.0, 0.0)),
)
.with_rect(corner_radius, 0.0, Vec4::new(0.0, 0.0, 0.0, 0.0))
.with_theme_color::<UiBase>(ThemeColor::BackgroundActive)
.with_clip()
.with_interaction()
.with_cursor_icon(winit::window::CursorIcon::Grab)
.with_children(|tree| {
tree.add_node()
.window(
Ab(Vec2::new(8.0, 16.0)),
Ab(Vec2::new(200.0, 20.0)),
Anchor::CenterLeft,
)
.with_text_slot(title_slot, font_size * 0.85)
.with_text_alignment(TextAlignment::Left, VerticalAlignment::Middle)
.with_theme_color::<UiBase>(ThemeColor::Accent)
.without_pointer_events()
.done();
collapse_button_entity = tree
.add_node()
.window(
Rl(Vec2::new(100.0, 50.0)) + Ab(Vec2::new(-8.0, 0.0)),
Ab(Vec2::new(20.0, 20.0)),
Anchor::CenterRight,
)
.with_rect(2.0, 0.0, Vec4::new(0.0, 0.0, 0.0, 0.0))
.with_theme_color::<UiBase>(ThemeColor::BackgroundHover)
.with_theme_color::<UiHover>(ThemeColor::Accent)
.with_interaction()
.with_transition::<UiHover>(8.0, 6.0)
.with_cursor_icon(winit::window::CursorIcon::Pointer)
.with_children(|tree| {
tree.add_node()
.window(
Rl(Vec2::new(50.0, 50.0)),
Ab(Vec2::new(20.0, 20.0)),
Anchor::Center,
)
.with_text_slot(collapse_slot, font_size * 0.8)
.with_text_alignment(
TextAlignment::Center,
VerticalAlignment::Middle,
)
.with_theme_color::<UiBase>(ThemeColor::Text)
.without_pointer_events()
.done();
})
.done();
})
.done();
content_entity = tree
.add_node()
.boundary(Ab(Vec2::new(0.0, 32.0)), Rl(Vec2::new(100.0, 100.0)))
.flow(FlowDirection::Vertical, 8.0, 4.0)
.with_clip()
.without_pointer_events()
.entity();
})
.done();
self.world_mut().ui.set_ui_widget_state(
panel_entity,
UiWidgetState::Panel(UiPanelData {
title: title.to_string(),
title_text_slot: title_slot,
content_entity,
header_entity,
collapsed: false,
panel_kind: UiPanelKind::Floating,
focus_order,
pinned: false,
min_size: Vec2::new(150.0, 100.0),
drag_offset: None,
resize_edge: None,
resize_start_rect: None,
resize_start_mouse: None,
undocked_rect: None,
default_dock_size: 300.0,
collapse_button_entity: Some(collapse_button_entity),
collapse_button_text_slot: Some(collapse_slot),
header_visible: true,
resizable: true,
}),
);
panel_entity
}
fn build_docked_panel(
&mut self,
title: &str,
panel_kind: UiPanelKind,
default_size: f32,
) -> freecs::Entity {
let theme = self
.world_mut()
.resources
.retained_ui
.theme_state
.active_theme();
let font_size = theme.font_size;
let border_color = theme.border_color;
let title_slot = self.world_mut().resources.text_cache.add_text(title);
let focus_order = self.world_mut().resources.retained_ui.next_focus_order;
self.world_mut().resources.retained_ui.next_focus_order += 1;
let mut header_entity = freecs::Entity::default();
let mut content_entity = freecs::Entity::default();
let panel_entity = self
.add_node()
.window(
Ab(Vec2::new(0.0, 0.0)),
Ab(Vec2::new(default_size, default_size)),
Anchor::TopLeft,
)
.with_rect(0.0, 1.0, border_color)
.with_theme_border_color(ThemeColor::Border)
.with_theme_color::<UiBase>(ThemeColor::Background)
.with_layer(UiLayer::DockedPanels)
.with_clip()
.with_depth(crate::ecs::ui::components::UiDepthMode::Set(
10.0 + focus_order as f32 * 10.0,
))
.with_children(|tree| {
header_entity = tree
.add_node()
.boundary(
Rl(Vec2::new(0.0, 0.0)),
Ab(Vec2::new(0.0, 32.0)) + Rl(Vec2::new(100.0, 0.0)),
)
.with_rect(0.0, 0.0, Vec4::new(0.0, 0.0, 0.0, 0.0))
.with_theme_color::<UiBase>(ThemeColor::BackgroundActive)
.with_clip()
.with_interaction()
.with_cursor_icon(winit::window::CursorIcon::Grab)
.with_children(|tree| {
tree.add_node()
.window(
Ab(Vec2::new(8.0, 16.0)),
Ab(Vec2::new(200.0, 20.0)),
Anchor::CenterLeft,
)
.with_text_slot(title_slot, font_size * 0.85)
.with_text_alignment(TextAlignment::Left, VerticalAlignment::Middle)
.with_theme_color::<UiBase>(ThemeColor::Accent)
.without_pointer_events()
.done();
})
.done();
content_entity = tree
.add_node()
.boundary(Ab(Vec2::new(0.0, 32.0)), Rl(Vec2::new(100.0, 100.0)))
.flow(FlowDirection::Vertical, 8.0, 4.0)
.with_clip()
.without_pointer_events()
.entity();
})
.done();
self.world_mut().ui.set_ui_widget_state(
panel_entity,
UiWidgetState::Panel(UiPanelData {
title: title.to_string(),
title_text_slot: title_slot,
content_entity,
header_entity,
collapsed: false,
panel_kind,
focus_order,
pinned: false,
min_size: Vec2::new(100.0, 60.0),
drag_offset: None,
resize_edge: None,
resize_start_rect: None,
resize_start_mouse: None,
undocked_rect: None,
default_dock_size: default_size,
collapse_button_entity: None,
collapse_button_text_slot: None,
header_visible: true,
resizable: true,
}),
);
panel_entity
}
pub fn add_docked_panel_left(&mut self, title: &str, default_width: f32) -> freecs::Entity {
self.build_docked_panel(title, UiPanelKind::DockedLeft, default_width)
}
pub fn add_docked_panel_right(&mut self, title: &str, default_width: f32) -> freecs::Entity {
self.build_docked_panel(title, UiPanelKind::DockedRight, default_width)
}
pub fn add_docked_panel_top(&mut self, title: &str, default_height: f32) -> freecs::Entity {
self.build_docked_panel(title, UiPanelKind::DockedTop, default_height)
}
pub fn add_docked_panel_bottom(&mut self, title: &str, default_height: f32) -> freecs::Entity {
self.build_docked_panel(title, UiPanelKind::DockedBottom, default_height)
}
pub fn add_color_picker(&mut self, initial_color: Vec4) -> freecs::Entity {
self.add_color_picker_with_mode(
initial_color,
crate::ecs::ui::components::ColorPickerMode::Rgb,
)
}
pub fn add_color_picker_hsv(&mut self, initial_color: Vec4) -> freecs::Entity {
self.add_color_picker_with_mode(
initial_color,
crate::ecs::ui::components::ColorPickerMode::Hsv,
)
}
pub fn add_color_picker_with_mode(
&mut self,
initial_color: Vec4,
mode: crate::ecs::ui::components::ColorPickerMode,
) -> freecs::Entity {
let theme = self
.world_mut()
.resources
.retained_ui
.theme_state
.active_theme();
let corner_radius = theme.corner_radius;
let root_entity = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, 0.0)))
.flow(FlowDirection::Horizontal, 4.0, 8.0)
.entity();
self.push_parent(root_entity);
let swatch_entity = self
.add_node()
.flow_child(Ab(Vec2::new(48.0, 48.0)))
.with_rect(corner_radius, 0.0, Vec4::new(0.0, 0.0, 0.0, 0.0))
.with_color::<UiBase>(initial_color)
.without_pointer_events()
.done();
let sliders_container = self
.add_node()
.flow_child(Ab(Vec2::new(0.0, 0.0)))
.flex_grow(1.0)
.flow(FlowDirection::Vertical, 0.0, 4.0)
.entity();
self.push_parent(sliders_container);
let slider_entities = match mode {
crate::ecs::ui::components::ColorPickerMode::Rgb => {
let slider_r = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 1.0, initial_color.x)
.precision(2)
.prefix("R: "),
);
let slider_g = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 1.0, initial_color.y)
.precision(2)
.prefix("G: "),
);
let slider_b = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 1.0, initial_color.z)
.precision(2)
.prefix("B: "),
);
let slider_a = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 1.0, initial_color.w)
.precision(2)
.prefix("A: "),
);
[slider_r, slider_g, slider_b, slider_a]
}
crate::ecs::ui::components::ColorPickerMode::Hsv => {
let hsv = crate::ecs::ui::color::Hsva::from_rgba(initial_color);
let slider_h = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 360.0, hsv.hue)
.precision(0)
.prefix("H: ")
.suffix("\u{00b0}"),
);
let slider_s = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 1.0, hsv.saturation)
.precision(2)
.prefix("S: "),
);
let slider_v = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 1.0, hsv.value)
.precision(2)
.prefix("V: "),
);
let slider_a = self.add_slider_configured(
crate::ecs::ui::components::SliderConfig::new(0.0, 1.0, initial_color.w)
.precision(2)
.prefix("A: "),
);
[slider_h, slider_s, slider_v, slider_a]
}
};
self.pop_parent();
self.pop_parent();
self.world_mut().ui.set_ui_widget_state(
root_entity,
UiWidgetState::ColorPicker(UiColorPickerData {
color: initial_color,
changed: false,
swatch_entity,
slider_entities,
mode,
}),
);
root_entity
}
}