use std::borrow::Cow;
use super::{
align::{AlignabilityFacade, Alignable, Aligner, ChildAlignable},
raw::{RawElWrapper, RawElement, RawHaalkaEl},
};
use bevy_ecs::{component::*, lifecycle::HookContext, prelude::*, system::RunSystemOnce, world::DeferredWorld};
use bevy_log::warn;
use bevy_picking::prelude::*;
use futures_signals::signal::{Signal, SignalExt};
pub trait Element: RawElement + Alignable + ChildAlignable {}
impl<E: RawElement + Alignable + ChildAlignable> Element for E {}
pub trait IntoElement {
type EL: Element;
fn into_element(self) -> Self::EL;
}
impl<T: Element> IntoElement for T {
type EL = T;
fn into_element(self) -> Self::EL {
self
}
}
pub trait IntoOptionElement {
type EL: Element;
fn into_option_element(self) -> Option<Self::EL>;
}
impl<E: Element, IE: IntoElement<EL = E>> IntoOptionElement for Option<IE> {
type EL = E;
fn into_option_element(self) -> Option<Self::EL> {
self.map(|into_element| into_element.into_element())
}
}
impl<E: Element, IE: IntoElement<EL = E>> IntoOptionElement for IE {
type EL = E;
fn into_option_element(self) -> Option<Self::EL> {
Some(self.into_element())
}
}
pub trait ElementWrapper: Sized {
type EL: Element + Default;
fn element_mut(&mut self) -> &mut Self::EL;
fn into_el(mut self) -> Self::EL {
std::mem::take(self.element_mut())
}
}
impl<EW: ElementWrapper> RawElWrapper for EW {
fn raw_el_mut(&mut self) -> &mut RawHaalkaEl {
self.element_mut().raw_el_mut()
}
fn into_raw_el(self) -> RawHaalkaEl {
self.into_el().into_raw()
}
}
pub trait TypeEraseable {
fn type_erase(self) -> AlignabilityFacade;
}
impl<T: Alignable> TypeEraseable for T {
fn type_erase(mut self) -> AlignabilityFacade {
let aligner = self.aligner().unwrap_or(Aligner::El);
let (align_option, raw_el) = (self.align_mut().take(), self.into_raw());
AlignabilityFacade::new(raw_el, align_option, aligner)
}
}
fn warn_non_orphan_ui_root(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
world.commands().queue(move |world: &mut World| {
let _ = world.run_system_once(move |child_ofs: Query<&ChildOf>| {
if child_ofs.iter_ancestors(entity).count() > 0 {
warn!(
"entity {:?} is registered as a UiRoot but is not an orphan (has a parent); this may lead to unexpected behavior",
entity
);
}
});
})
}
#[derive(Component)]
#[component(on_add = warn_non_orphan_ui_root)]
pub struct UiRoot;
pub trait UiRootable: RawElWrapper {
fn ui_root(self) -> Self {
self.update_raw_el(|raw_el| raw_el.insert(UiRoot).insert(Pickable::default()))
}
}
pub trait Nameable: RawElWrapper {
fn name<T: Into<Cow<'static, str>>>(mut self, name_option: impl Into<Option<T>>) -> Self {
if let Some(name) = name_option.into() {
self = self.update_raw_el(|raw_el| raw_el.insert(Name::new(name)));
}
self
}
fn name_signal<T: Into<Cow<'static, str>> + 'static, S: Signal<Item = Option<T>> + Send + 'static>(
mut self,
name_option_signal_option: impl Into<Option<S>>,
) -> Self {
if let Some(name_option_signal) = name_option_signal_option.into() {
self = self.update_raw_el(|raw_el| {
raw_el.component_signal::<Name, _>(name_option_signal.map(|name_option| name_option.map(Name::new)))
});
}
self
}
}