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};
#[doc = clone_semantics_doc!("Grid")]
#[derive(Default)]
pub struct Grid<NodeType> {
builder: jonmo::Builder,
_node_type: std::marker::PhantomData<NodeType>,
}
impl_element_clone!("Grid", Grid<NodeType>, my_grid, ".cell(El::new().name(label))");
impl<NodeType: Bundle> From<jonmo::Builder> for Grid<NodeType> {
fn from(builder: jonmo::Builder) -> Self {
Self {
builder: builder
.with_component::<Node>(|mut node| {
node.display = Display::Grid;
})
.insert((LayoutDirection::Grid, Pickable::IGNORE)),
_node_type: std::marker::PhantomData,
}
}
}
impl<NodeType: Bundle + Default> Grid<NodeType> {
pub fn new() -> Self {
Self::from(jonmo::Builder::from(NodeType::default()))
}
}
impl<NodeType> BuilderWrapper for Grid<NodeType> {
fn builder_mut(&mut self) -> &mut jonmo::Builder {
&mut self.builder
}
}
impl<NodeType: Bundle> Alignable for Grid<NodeType> {}
impl<NodeType: Bundle> Cursorable for Grid<NodeType> {}
impl<NodeType: Bundle> GlobalEventAware for Grid<NodeType> {}
impl<NodeType: Bundle> Nameable for Grid<NodeType> {}
impl<NodeType: Bundle> PointerEventAware for Grid<NodeType> {}
impl<NodeType: Bundle> MouseWheelScrollable for Grid<NodeType> {}
impl<NodeType: Bundle> UiRootable for Grid<NodeType> {}
impl<NodeType: Bundle> ViewportMutable for Grid<NodeType> {}
impl<NodeType: Bundle> BuilderPassThrough for Grid<NodeType> {}
pub const GRID_TRACK_FLOAT_PRECISION_SLACK: f32 = 0.001;
impl<NodeType: Bundle> Grid<NodeType> {
pub fn row_wrap_cell_width(self, cell_width_option: impl Into<Option<f32>>) -> Self {
if let Some(cell_width) = cell_width_option.into() {
self.with_builder(|builder| {
builder.with_component::<Node>(move |mut node| {
node.grid_template_columns = RepeatedGridTrack::px(GridTrackRepetition::AutoFill, cell_width);
})
})
} else {
self
}
}
pub fn row_wrap_cell_width_signal<S: Signal<Item = f32> + Send + Sync + 'static>(
self,
cell_width_signal_option: impl Into<Option<S>>,
) -> Self {
if let Some(cell_width_signal) = cell_width_signal_option.into() {
self.with_builder(|builder| {
builder.on_signal_with_component::<_, Node>(cell_width_signal, |mut node, cell_width| {
node.grid_template_columns = RepeatedGridTrack::px(GridTrackRepetition::AutoFill, cell_width)
})
})
} else {
self
}
}
pub fn cell<IOE: IntoOptionElement>(self, cell_option: IOE) -> Self {
if let Some(cell) = cell_option.into_option_element() {
self.with_builder(|builder| builder.child(cell.into_builder()))
} else {
self
}
}
pub fn cell_signal<IOE: IntoOptionElement + 'static, S: Signal<Item = IOE> + Send + Sync + 'static>(
self,
cell_option_signal_option: impl Into<Option<S>>,
) -> Self {
if let Some(cell_option_signal) = cell_option_signal_option.into() {
self.with_builder(|builder| {
builder.child_signal(
cell_option_signal
.map_in(move |cell_option: IOE| cell_option.into_option_element().map(|el| el.into_builder())),
)
})
} else {
self
}
}
pub fn cells<IOE: IntoOptionElement + 'static, I: IntoIterator<Item = IOE>>(
self,
cells_options_option: impl Into<Option<I>>,
) -> Self
where
I::IntoIter: Send + 'static,
{
if let Some(cells_options) = cells_options_option.into() {
self.with_builder(|builder| {
builder.children(
cells_options
.into_iter()
.filter_map(move |cell_option| cell_option.into_option_element().map(|el| el.into_builder())),
)
})
} else {
self
}
}
pub fn cells_signal_vec<
IOE: IntoOptionElement + Clone + Send + Sync + 'static,
S: SignalVec<Item = IOE> + Send + Sync + 'static,
>(
self,
cells_options_signal_vec_option: impl Into<Option<S>>,
) -> Self {
if let Some(cells_options_signal_vec) = cells_options_signal_vec_option.into() {
self.with_builder(|builder| {
builder.children_signal_vec(cells_options_signal_vec.filter_map(move |In(cell_option): In<IOE>| {
cell_option.into_option_element().map(|el| el.into_builder())
}))
})
} else {
self
}
}
}