use crate::ecs::ui::builder::UiTreeBuilder;
use crate::ecs::world::World;
use nalgebra_glm::Vec2;
use winit::keyboard::KeyCode;
pub struct UiInteractionContext {
pub mouse_position: Vec2,
pub scroll_delta: Vec2,
pub frame_keys: Vec<(KeyCode, bool)>,
pub frame_chars: Vec<char>,
pub ctrl_held: bool,
pub shift_held: bool,
pub delta_time: f32,
pub focused_entity: Option<freecs::Entity>,
pub dpi_scale: f32,
}
pub trait CompositeWidget: Sized + 'static {
type Value;
fn build(tree: &mut UiTreeBuilder) -> Self;
fn update(&mut self, _world: &mut World) {}
fn interact(
&mut self,
_world: &mut World,
_entity: freecs::Entity,
_ctx: &UiInteractionContext,
) {
}
fn value(&self, _world: &World) -> Self::Value;
}
pub(crate) trait AnyCompositeWidget: std::any::Any {
fn update_any(&mut self, world: &mut World);
fn interact_any(
&mut self,
world: &mut World,
entity: freecs::Entity,
ctx: &UiInteractionContext,
);
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}
impl<C: CompositeWidget> AnyCompositeWidget for C {
fn update_any(&mut self, world: &mut World) {
self.update(world);
}
fn interact_any(
&mut self,
world: &mut World,
entity: freecs::Entity,
ctx: &UiInteractionContext,
) {
self.interact(world, entity, ctx);
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl<'a> UiTreeBuilder<'a> {
pub fn add_composite<T: CompositeWidget>(&mut self) -> freecs::Entity {
use crate::ecs::ui::layout_types::FlowDirection;
use crate::ecs::ui::units::{Ab, Rl};
use nalgebra_glm::Vec2;
let container = self
.add_node()
.flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, 0.0)))
.flow(FlowDirection::Vertical, 0.0, 4.0)
.without_pointer_events()
.entity();
self.push_parent(container);
let widget = T::build(self);
self.pop_parent();
self.world_mut()
.resources
.retained_ui
.composite_widgets
.insert(container, Box::new(widget));
container
}
}
impl World {
pub fn ui_composite<T: CompositeWidget>(&self, entity: freecs::Entity) -> Option<&T> {
self.resources
.retained_ui
.composite_widgets
.get(&entity)
.and_then(|boxed| boxed.as_any().downcast_ref::<T>())
}
pub fn ui_composite_mut<T: CompositeWidget>(
&mut self,
entity: freecs::Entity,
) -> Option<&mut T> {
self.resources
.retained_ui
.composite_widgets
.get_mut(&entity)
.and_then(|boxed| boxed.as_any_mut().downcast_mut::<T>())
}
pub fn ui_composite_value<T: CompositeWidget>(
&self,
entity: freecs::Entity,
) -> Option<T::Value> {
self.ui_composite::<T>(entity)
.map(|composite| composite.value(self))
}
}
pub fn ui_composite_update_system(world: &mut World) {
if !world.resources.retained_ui.enabled {
return;
}
let ctx = UiInteractionContext {
mouse_position: world.resources.input.mouse.position,
scroll_delta: world.resources.retained_ui.scroll_delta,
frame_keys: world.resources.retained_ui.frame_keys.clone(),
frame_chars: world.resources.retained_ui.frame_chars.clone(),
ctrl_held: world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::ControlLeft)
|| world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::ControlRight),
shift_held: world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::ShiftLeft)
|| world
.resources
.input
.keyboard
.is_key_pressed(KeyCode::ShiftRight),
delta_time: world.resources.retained_ui.delta_time,
focused_entity: world.resources.retained_ui.focused_entity,
dpi_scale: world.resources.window.cached_scale_factor,
};
let entities: Vec<freecs::Entity> = world
.resources
.retained_ui
.composite_widgets
.keys()
.copied()
.collect();
for entity in entities {
if world.ui.get_ui_layout_node(entity).is_none() {
world
.resources
.retained_ui
.composite_widgets
.remove(&entity);
continue;
}
if let Some(mut composite) = world
.resources
.retained_ui
.composite_widgets
.remove(&entity)
{
composite.interact_any(world, entity, &ctx);
composite.update_any(world);
world
.resources
.retained_ui
.composite_widgets
.insert(entity, composite);
}
}
}