use bevy_ecs::prelude::*;
use bevy_picking::prelude::*;
use bevy_ui::prelude::*;
use jonmo::{
signal::{Signal, SignalExt},
signal_vec::{SignalVec, SignalVecExt},
};
use super::{
align::{Alignable, LayoutDirection},
element::{BuilderPassThrough, BuilderWrapper, IntoOptionElement, Nameable, UiRootable},
global_event_aware::GlobalEventAware,
mouse_wheel_scrollable::MouseWheelScrollable,
pointer_event_aware::{Cursorable, PointerEventAware},
viewport_mutable::ViewportMutable,
};
use crate::{clone_semantics_doc, impl_element_clone};
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Hash)]
pub enum Direction {
#[default]
Column,
Row,
}
impl From<Direction> for FlexDirection {
fn from(direction: Direction) -> Self {
match direction {
Direction::Column => FlexDirection::Column,
Direction::Row => FlexDirection::Row,
}
}
}
impl From<Direction> for LayoutDirection {
fn from(direction: Direction) -> Self {
match direction {
Direction::Column => LayoutDirection::Column,
Direction::Row => LayoutDirection::Row,
}
}
}
#[doc = clone_semantics_doc!("Stripe")]
#[derive(Default)]
pub struct Stripe<NodeType> {
builder: jonmo::Builder,
_node_type: std::marker::PhantomData<NodeType>,
}
impl_element_clone!("Stripe", Stripe<NodeType>, my_stripe, ".item(El::new().name(label))");
impl<NodeType: Bundle> From<jonmo::Builder> for Stripe<NodeType> {
fn from(builder: jonmo::Builder) -> Self {
Self {
builder: builder
.with_component::<Node>(|mut node| {
node.display = Display::Flex;
node.flex_direction = FlexDirection::Column;
})
.insert((LayoutDirection::Column, Pickable::IGNORE)),
_node_type: std::marker::PhantomData,
}
}
}
impl<NodeType: Bundle + Default> Stripe<NodeType> {
pub fn new() -> Self {
Self::from(jonmo::Builder::from(NodeType::default()))
}
}
impl<NodeType: Bundle> BuilderWrapper for Stripe<NodeType> {
fn builder_mut(&mut self) -> &mut jonmo::Builder {
&mut self.builder
}
}
impl<NodeType: Bundle> Alignable for Stripe<NodeType> {}
impl<NodeType: Bundle> Cursorable for Stripe<NodeType> {}
impl<NodeType: Bundle> GlobalEventAware for Stripe<NodeType> {}
impl<NodeType: Bundle> Nameable for Stripe<NodeType> {}
impl<NodeType: Bundle> PointerEventAware for Stripe<NodeType> {}
impl<NodeType: Bundle> MouseWheelScrollable for Stripe<NodeType> {}
impl<NodeType: Bundle> UiRootable for Stripe<NodeType> {}
impl<NodeType: Bundle> ViewportMutable for Stripe<NodeType> {}
impl<NodeType: Bundle> BuilderPassThrough for Stripe<NodeType> {}
impl<NodeType: Bundle> Stripe<NodeType> {
pub fn item<IOE: IntoOptionElement>(self, item_option: IOE) -> Self {
if let Some(item) = item_option.into_option_element() {
self.with_builder(|builder| builder.child(item.into_builder()))
} else {
self
}
}
pub fn item_signal<IOE, S>(self, item_option_signal_option: impl Into<Option<S>>) -> Self
where
IOE: IntoOptionElement + 'static,
S: Signal<Item = IOE> + Send + Sync + 'static,
{
if let Some(item_option_signal) = item_option_signal_option.into() {
self.with_builder(|builder| {
builder.child_signal(
item_option_signal
.map_in(move |item_option: IOE| item_option.into_option_element().map(|el| el.into_builder())),
)
})
} else {
self
}
}
pub fn items<IOE: IntoOptionElement + 'static, I: IntoIterator<Item = IOE>>(
self,
items_options_option: impl Into<Option<I>>,
) -> Self
where
I::IntoIter: Send + 'static,
{
if let Some(items_options) = items_options_option.into() {
self.with_builder(|builder| {
builder.children(
items_options
.into_iter()
.filter_map(|item_option| item_option.into_option_element())
.map(|el| el.into_builder()),
)
})
} else {
self
}
}
pub fn items_signal_vec<IOE, S>(self, items_options_signal_vec_option: impl Into<Option<S>>) -> Self
where
IOE: IntoOptionElement + Clone + Send + Sync + 'static,
S: SignalVec<Item = IOE> + Send + Sync + 'static,
{
if let Some(items_options_signal_vec) = items_options_signal_vec_option.into() {
self.with_builder(|builder| {
builder.children_signal_vec(items_options_signal_vec.filter_map(|In(item_option): In<IOE>| {
item_option.into_option_element().map(|el| el.into_builder())
}))
})
} else {
self
}
}
pub fn direction(self, direction: Direction) -> Self {
let flex_direction: FlexDirection = direction.into();
self.with_builder(|builder| {
builder
.with_component::<Node>(move |mut node| {
node.flex_direction = flex_direction;
node.align_items = if direction == Direction::Row {
AlignItems::Center
} else {
AlignItems::Default
};
})
.insert(LayoutDirection::from(direction))
})
}
pub fn direction_signal<S>(self, direction_signal: S) -> Self
where
S: Signal<Item = Direction> + Send + Sync + 'static,
{
self.with_builder(|builder| {
builder.on_signal_with_entity(direction_signal.dedupe(), |mut entity_world_mut, direction| {
if let Some(mut node) = entity_world_mut.get_mut::<Node>() {
node.flex_direction = direction.into();
node.align_items = if direction == Direction::Row {
AlignItems::Center
} else {
AlignItems::Default
};
}
entity_world_mut.insert(LayoutDirection::from(direction));
})
})
}
pub fn multiline_row(self) -> Self {
self.with_builder(|builder| {
builder.with_component::<Node>(|mut node| {
if node.flex_direction == FlexDirection::Row {
node.flex_wrap = FlexWrap::Wrap;
node.flex_basis = Val::Px(0.);
node.flex_grow = 1.;
}
})
})
}
pub fn multiline_row_signal<S>(self, multiline_signal: S) -> Self
where
S: Signal<Item = bool> + Send + Sync + 'static,
{
self.with_builder(|builder| {
builder.on_signal_with_component::<_, Node>(multiline_signal.dedupe(), |mut node, multiline| {
if node.flex_direction == FlexDirection::Row && multiline {
node.flex_wrap = FlexWrap::Wrap;
node.flex_basis = Val::Px(0.);
node.flex_grow = 1.;
} else {
node.flex_wrap = FlexWrap::NoWrap;
node.flex_basis = Val::Auto;
node.flex_grow = 0.;
}
})
})
}
}