use std::{collections::BTreeSet, ops::Not};
use bevy_ecs::prelude::*;
use bevy_ui::prelude::*;
use futures_signals::signal::{BoxSignal, Signal, SignalExt};
use super::{
column::Column,
el::El,
element::ElementWrapper,
grid::Grid,
raw::{RawElWrapper, RawHaalkaEl},
row::Row,
stack::Stack,
};
#[derive(Clone, Default)]
pub struct Align {
alignments: BTreeSet<Alignment>,
}
#[allow(missing_docs)]
impl Align {
pub fn new() -> Self {
Self::default()
}
pub fn center() -> Self {
Self::default().center_x().center_y()
}
pub fn center_x(mut self) -> Self {
self.alignments.insert(Alignment::CenterX);
self.alignments.remove(&Alignment::Left);
self.alignments.remove(&Alignment::Right);
self
}
pub fn center_y(mut self) -> Self {
self.alignments.insert(Alignment::CenterY);
self.alignments.remove(&Alignment::Top);
self.alignments.remove(&Alignment::Bottom);
self
}
pub fn top(mut self) -> Self {
self.alignments.insert(Alignment::Top);
self.alignments.remove(&Alignment::CenterY);
self.alignments.remove(&Alignment::Bottom);
self
}
pub fn bottom(mut self) -> Self {
self.alignments.insert(Alignment::Bottom);
self.alignments.remove(&Alignment::CenterY);
self.alignments.remove(&Alignment::Top);
self
}
pub fn left(mut self) -> Self {
self.alignments.insert(Alignment::Left);
self.alignments.remove(&Alignment::CenterX);
self.alignments.remove(&Alignment::Right);
self
}
pub fn right(mut self) -> Self {
self.alignments.insert(Alignment::Right);
self.alignments.remove(&Alignment::CenterX);
self.alignments.remove(&Alignment::Left);
self
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Alignment {
Top,
Bottom,
Left,
Right,
CenterX,
CenterY,
}
pub enum AlignHolder {
Align(Align),
AlignSignal(BoxSignal<'static, Option<Align>>),
}
#[allow(missing_docs)]
pub enum AddRemove {
Add,
Remove,
}
fn register_align_signal<REW: RawElWrapper>(
element: REW,
align_signal: impl Signal<Item = Option<Vec<Alignment>>> + Send + 'static,
apply_alignment: fn(&mut Node, Alignment, AddRemove),
) -> REW {
let mut last_alignments_option: Option<Vec<Alignment>> = None;
element.update_raw_el(|raw_el| {
raw_el.on_signal_with_component::<Option<Vec<Alignment>>, Node>(align_signal, move |mut node, aligns_option| {
if let Some(alignments) = aligns_option {
if let Some(mut last_alignments) = last_alignments_option.take() {
last_alignments.retain(|align| !alignments.contains(align));
for alignment in last_alignments {
apply_alignment(&mut node, alignment, AddRemove::Remove)
}
}
for alignment in &alignments {
apply_alignment(&mut node, *alignment, AddRemove::Add)
}
last_alignments_option = alignments.is_empty().not().then_some(alignments);
} else if let Some(last_aligns) = last_alignments_option.take() {
for align in last_aligns {
apply_alignment(&mut node, align, AddRemove::Remove)
}
}
})
})
}
pub trait Alignable: RawElWrapper {
fn aligner(&mut self) -> Option<Aligner> {
None
}
fn align_mut(&mut self) -> &mut Option<AlignHolder>;
fn align(mut self, align_option: impl Into<Option<Align>>) -> Self
where
Self: Sized,
{
if let Some(align) = align_option.into() {
*self.align_mut() = Some(AlignHolder::Align(align));
}
self
}
fn align_signal<S: Signal<Item = Option<Align>> + Send + Sync + 'static>(
mut self,
align_option_signal_option: impl Into<Option<S>>,
) -> Self
where
Self: Sized,
{
if let Some(align_option_signal) = align_option_signal_option.into() {
*self.align_mut() = Some(AlignHolder::AlignSignal(align_option_signal.boxed()));
}
self
}
fn apply_content_alignment_wrapper(&self) -> fn(&mut Node, Alignment, AddRemove) {
Self::apply_content_alignment
}
fn apply_content_alignment(node: &mut Node, alignment: Alignment, action: AddRemove);
fn align_content(mut self, align_option: impl Into<Option<Align>>) -> Self {
if let Some(align) = align_option.into() {
let apply_content_alignment = self.apply_content_alignment_wrapper();
self = self.update_raw_el(move |raw_el| {
raw_el.with_component::<Node>(move |mut node| {
for alignment in align.alignments {
apply_content_alignment(&mut node, alignment, AddRemove::Add);
}
})
});
}
self
}
fn align_content_signal<S: Signal<Item = Option<Align>> + Send + 'static>(
mut self,
align_option_signal_option: impl Into<Option<S>>,
) -> Self {
if let Some(align_option_signal) = align_option_signal_option.into() {
let apply_content_alignment = self.apply_content_alignment_wrapper();
self = register_align_signal(
self,
align_option_signal
.map(|align_option| align_option.map(|align| align.alignments.into_iter().collect())),
apply_content_alignment,
);
}
self
}
}
pub trait ChildAlignable
where
Self: 'static,
{
fn update_node(_node: Mut<Node>) {}
fn apply_alignment_wrapper(&self) -> fn(&mut Node, Alignment, AddRemove) {
Self::apply_alignment
}
fn apply_alignment(node: &mut Node, align: Alignment, action: AddRemove);
fn align_child<Child: RawElWrapper + Alignable>(
mut child: Child,
apply_alignment: fn(&mut Node, Alignment, AddRemove),
) -> Child {
child = child.update_raw_el(|raw_el| raw_el.with_component::<Node>(Self::update_node));
if let Some(align) = child.align_mut().take() {
match align {
AlignHolder::Align(align) => {
child = child.update_raw_el(|raw_el| {
raw_el.with_component::<Node>(move |mut node| {
for align in align.alignments {
apply_alignment(&mut node, align, AddRemove::Add)
}
})
})
}
AlignHolder::AlignSignal(align_option_signal) => {
child = register_align_signal(
child,
{
align_option_signal
.map(|align_option| align_option.map(|align| align.alignments.into_iter().collect()))
},
apply_alignment,
)
}
}
}
child
}
}
impl<EW: ElementWrapper> Alignable for EW {
fn aligner(&mut self) -> Option<Aligner> {
self.element_mut().aligner()
}
fn align_mut(&mut self) -> &mut Option<AlignHolder> {
self.element_mut().align_mut()
}
fn apply_content_alignment(node: &mut Node, alignment: Alignment, action: AddRemove) {
EW::EL::apply_content_alignment(node, alignment, action);
}
}
impl<EW: ElementWrapper + 'static> ChildAlignable for EW {
fn update_node(node: Mut<Node>) {
EW::EL::update_node(node);
}
fn apply_alignment(node: &mut Node, align: Alignment, action: AddRemove) {
EW::EL::apply_alignment(node, align, action);
}
}
#[derive(Clone, Copy)]
pub enum Aligner {
El,
Column,
Row,
Stack,
Grid,
}
pub struct AlignabilityFacade {
raw_el: RawHaalkaEl,
align: Option<AlignHolder>,
aligner: Aligner,
}
impl<NodeType: Bundle> From<NodeType> for AlignabilityFacade {
fn from(node_bundle: NodeType) -> Self {
AlignabilityFacade::new(RawHaalkaEl::from(node_bundle), None, Aligner::El)
}
}
impl AlignabilityFacade {
pub(crate) fn new(raw_el: RawHaalkaEl, align: Option<AlignHolder>, aligner: Aligner) -> Self {
Self { raw_el, align, aligner }
}
}
impl RawElWrapper for AlignabilityFacade {
fn raw_el_mut(&mut self) -> &mut RawHaalkaEl {
&mut self.raw_el
}
}
impl Alignable for AlignabilityFacade {
fn aligner(&mut self) -> Option<Aligner> {
Some(self.aligner)
}
fn align_mut(&mut self) -> &mut Option<AlignHolder> {
&mut self.align
}
fn apply_content_alignment_wrapper(&self) -> fn(&mut Node, Alignment, AddRemove) {
match self.aligner {
Aligner::El => El::<Node>::apply_content_alignment,
Aligner::Column => Column::<Node>::apply_content_alignment,
Aligner::Row => Row::<Node>::apply_content_alignment,
Aligner::Stack => Stack::<Node>::apply_content_alignment,
Aligner::Grid => Grid::<Node>::apply_content_alignment,
}
}
fn apply_content_alignment(_node: &mut Node, _alignment: Alignment, _action: AddRemove) {}
}
impl ChildAlignable for AlignabilityFacade {
fn apply_alignment_wrapper(&self) -> fn(&mut Node, Alignment, AddRemove) {
match self.aligner {
Aligner::El => El::<Node>::apply_alignment,
Aligner::Column => Column::<Node>::apply_alignment,
Aligner::Row => Row::<Node>::apply_alignment,
Aligner::Stack => Stack::<Node>::apply_alignment,
Aligner::Grid => Grid::<Node>::apply_alignment,
}
}
fn apply_alignment(_node: &mut Node, _align: Alignment, _action: AddRemove) {}
}