use crate::{use_window_logical_resolution, UIBase, UIExt};
use ambient_cb::Cb;
use ambient_color::Color;
use ambient_element::{
define_el_function_for_vec_element_newtype, element_component, use_frame, use_state, Element,
ElementComponent, ElementComponentExt, Hooks,
};
use ambient_guest_bridge::core::{
hierarchy::components::children,
transform::components::{local_to_parent, local_to_world, translation},
};
use glam::{vec2, vec3, Mat4, Vec2, Vec3, Vec3Swizzles};
use itertools::Itertools;
pub use ambient_guest_bridge::core::layout::{
components::*,
types::{Align, Docking, Fit, Layout, Orientation},
};
#[derive(Debug, Clone)]
pub struct WindowSized(pub Vec<Element>);
define_el_function_for_vec_element_newtype!(WindowSized);
impl ElementComponent for WindowSized {
fn render(self: Box<Self>, hooks: &mut Hooks) -> Element {
let res = use_window_logical_resolution(hooks);
Dock(self.0)
.el()
.with(width(), res.x as _)
.with(height(), res.y as _)
.remove(local_to_parent())
}
}
#[derive(Debug, Clone)]
pub struct Dock(pub Vec<Element>);
define_el_function_for_vec_element_newtype!(Dock);
impl ElementComponent for Dock {
fn render(self: Box<Self>, _: &mut Hooks) -> Element {
Element::from(UIBase)
.init(layout(), Layout::Dock)
.init_default(children())
.children(self.0)
}
}
#[derive(Debug, Clone)]
pub struct Flow(pub Vec<Element>);
define_el_function_for_vec_element_newtype!(Flow);
impl ElementComponent for Flow {
fn render(self: Box<Self>, _: &mut Hooks) -> Element {
Element::from(UIBase)
.init(layout(), Layout::Flow)
.init_default(children())
.children(self.0)
}
}
#[derive(Debug, Clone)]
pub struct Bookcase(pub Vec<BookFile>);
impl ElementComponent for Bookcase {
fn render(self: Box<Self>, _: &mut Hooks) -> Element {
Element::from(UIBase)
.init(layout(), Layout::Bookcase)
.init_default(children())
.children(self.0.into_iter().map(|x| x.el()).collect())
}
}
#[derive(Debug, Clone)]
pub struct BookFile {
container: Element,
book: Element,
}
impl ElementComponent for BookFile {
fn render(self: Box<Self>, _: &mut Hooks) -> Element {
Element::from(UIBase)
.init_default(is_book_file())
.children(vec![self.container, self.book])
}
}
#[derive(Debug, Clone)]
pub struct FlowColumn(pub Vec<Element>);
define_el_function_for_vec_element_newtype!(FlowColumn);
impl ElementComponent for FlowColumn {
fn render(self: Box<Self>, _: &mut Hooks) -> Element {
Flow(self.0)
.el()
.with(orientation(), Orientation::Vertical)
.with(align_horizontal(), Align::Begin)
.with(align_vertical(), Align::Begin)
.with(fit_horizontal(), Fit::Children)
.with(fit_vertical(), Fit::Children)
}
}
#[derive(Debug, Clone)]
pub struct FlowRow(pub Vec<Element>);
define_el_function_for_vec_element_newtype!(FlowRow);
impl ElementComponent for FlowRow {
fn render(self: Box<Self>, _: &mut Hooks) -> Element {
Flow(self.0)
.el()
.with(orientation(), Orientation::Horizontal)
.with(align_horizontal(), Align::Begin)
.with(align_vertical(), Align::Begin)
.with(fit_horizontal(), Fit::Children)
.with(fit_vertical(), Fit::Children)
}
}
#[derive(Debug, Clone)]
pub struct Centered(pub Vec<Element>);
define_el_function_for_vec_element_newtype!(Centered);
impl ElementComponent for Centered {
fn render(self: Box<Self>, _: &mut Hooks) -> Element {
Flow(self.0)
.el()
.with(orientation(), Orientation::Vertical)
.with(align_horizontal(), Align::Center)
.with(align_vertical(), Align::Center)
.with(fit_horizontal(), Fit::None)
.with(fit_vertical(), Fit::None)
}
}
#[element_component]
pub fn FixedGrid(
_: &mut Hooks,
items: Vec<Element>,
item_stride: Vec2,
items_horizontal: usize,
) -> Element {
UIBase.el().children(
items
.into_iter()
.enumerate()
.map(|(i, item)| {
let x = i % items_horizontal;
let y = i / items_horizontal;
item.with(
translation(),
vec3(x as f32 * item_stride.x, y as f32 * item_stride.y, 0.),
)
})
.collect_vec(),
)
}
#[element_component]
pub fn MeasureSize(
hooks: &mut Hooks,
inner: Element,
on_change: Cb<dyn Fn(Vec2) + Sync + Send + 'static>,
) -> Element {
let (id, set_id) = use_state(hooks, None);
let (current, set_current) = use_state(hooks, Vec2::ZERO);
use_frame(hooks, move |world| {
if let Some(id) = id {
let width = world.get(id, width()).unwrap_or(0.);
let height = world.get(id, height()).unwrap_or(0.);
let next = vec2(width, height);
if current != next {
on_change(next);
set_current(next);
}
}
});
inner.on_spawned(move |_, id, _| set_id(Some(id)))
}
#[element_component]
pub fn MeasureAbsolutePosition(
hooks: &mut Hooks,
inner: Element,
on_change: Cb<dyn Fn(Vec3) + Sync + Send + 'static>,
) -> Element {
let (id, set_id) = use_state(hooks, None);
let (current, set_current) = use_state(hooks, Vec3::ZERO);
use_frame(hooks, move |world| {
if let Some(id) = id {
let ltw = world.get(id, local_to_world()).unwrap();
let (_, _, abs_pos) = Mat4::to_scale_rotation_translation(<w);
if current != abs_pos {
on_change(abs_pos);
set_current(abs_pos);
}
}
});
inner.on_spawned(move |_, id, _| set_id(Some(id)))
}
#[element_component]
pub fn LayoutFreeCenter(
hooks: &mut Hooks,
inner: Element,
center_x: bool,
center_y: bool,
) -> Element {
#[derive(Default, Clone, PartialEq, Debug)]
struct State {
width: f32,
height: f32,
scale: Vec2,
}
let (id, set_id) = use_state(hooks, None);
let (current, set_current) = use_state(hooks, State::default());
let mut offset = Vec3::ZERO;
if center_x {
offset.x -= (current.width / 2.0) * current.scale.x;
}
if center_y {
offset.y -= (current.height / 2.0) * current.scale.y;
}
use_frame(hooks, move |world| {
if let Some(id) = id {
let width = world.get(id, width()).unwrap_or(0.);
let height = world.get(id, height()).unwrap_or(0.);
let ltw = world.get(id, local_to_world()).unwrap_or_default();
let (scale, _, _) = ltw.to_scale_rotation_translation();
let state = State {
width,
height,
scale: scale.xy(),
};
if current != state {
set_current(state);
}
}
});
inner
.on_spawned(move |_, id, _| set_id(Some(id)))
.with(translation(), offset)
}
#[element_component]
pub fn Separator(
_hooks: &mut Hooks,
vertical: bool,
) -> Element {
let el = Flow(vec![])
.el()
.with_background(Color::rgba(0., 0., 0., 0.8).into());
if vertical {
el.with(width(), 1.)
.with(fit_horizontal(), Fit::None)
.with(fit_vertical(), Fit::Parent)
} else {
el.with(height(), 1.)
.with(fit_horizontal(), Fit::Parent)
.with(fit_vertical(), Fit::None)
}
}