use bevy_ecs::prelude::*;
use bevy_picking::prelude::*;
use bevy_ui::prelude::*;
use bevy_utils::prelude::*;
use futures_signals::{
signal::{Signal, SignalExt},
signal_vec::{SignalVec, SignalVecExt},
};
use super::{
align::{AddRemove, AlignHolder, Alignable, Aligner, Alignment, ChildAlignable},
element::{IntoOptionElement, Nameable, UiRootable},
global_event_aware::GlobalEventAware,
mouse_wheel_scrollable::MouseWheelScrollable,
pointer_event_aware::{CursorOnHoverable, PointerEventAware},
raw::{RawElWrapper, RawHaalkaEl},
stack::Stack,
viewport_mutable::ViewportMutable,
};
#[derive(Default)]
pub struct Grid<NodeType> {
raw_el: RawHaalkaEl,
align: Option<AlignHolder>,
_node_type: std::marker::PhantomData<NodeType>,
}
impl<NodeType: Bundle> From<RawHaalkaEl> for Grid<NodeType> {
fn from(value: RawHaalkaEl) -> Self {
Self {
raw_el: value
.with_component::<Node>(|mut node| {
node.display = Display::Grid;
})
.insert(Pickable::IGNORE),
align: None,
_node_type: std::marker::PhantomData,
}
}
}
impl<NodeType: Bundle> From<NodeType> for Grid<NodeType> {
fn from(node_bundle: NodeType) -> Self {
RawHaalkaEl::from(node_bundle).into()
}
}
impl<NodeType: Bundle + Default> Grid<NodeType> {
pub fn new() -> Self {
Self::from(NodeType::default())
}
}
impl<NodeType: Bundle> RawElWrapper for Grid<NodeType> {
fn raw_el_mut(&mut self) -> &mut RawHaalkaEl {
&mut self.raw_el
}
fn into_raw_el(self) -> RawHaalkaEl {
RawHaalkaEl::from(Node {
display: Display::Grid,
..default()
})
.child(self.raw_el)
}
}
impl<NodeType: Bundle> CursorOnHoverable 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> {}
pub const GRID_TRACK_FLOAT_PRECISION_SLACK: f32 = 0.001;
impl<NodeType: Bundle> Grid<NodeType> {
pub fn row_wrap_cell_width(mut self, cell_width_option: impl Into<Option<f32>>) -> Self {
if let Some(cell_width) = cell_width_option.into() {
self.raw_el = self.raw_el.with_component::<Node>(move |mut node| {
node.grid_template_columns = RepeatedGridTrack::px(GridTrackRepetition::AutoFill, cell_width);
});
}
self
}
pub fn row_wrap_cell_width_signal<S: Signal<Item = f32> + Send + 'static>(
mut self,
cell_width_signal_option: impl Into<Option<S>>,
) -> Self {
if let Some(cell_width_signal) = cell_width_signal_option.into() {
self.raw_el =
self.raw_el
.on_signal_with_component::<f32, Node>(cell_width_signal, |mut node, cell_width| {
node.grid_template_columns = RepeatedGridTrack::px(GridTrackRepetition::AutoFill, cell_width)
});
}
self
}
pub fn cell<IOE: IntoOptionElement>(mut self, cell_option: IOE) -> Self {
let apply_alignment = self.apply_alignment_wrapper();
self.raw_el = self.raw_el.child(
cell_option
.into_option_element()
.map(|cell| Self::align_child(cell, apply_alignment)),
);
self
}
pub fn cell_signal<IOE: IntoOptionElement + 'static, S: Signal<Item = IOE> + Send + 'static>(
mut self,
cell_option_signal_option: impl Into<Option<S>>,
) -> Self {
if let Some(cell_option_signal) = cell_option_signal_option.into() {
let apply_alignment = self.apply_alignment_wrapper();
self.raw_el = self.raw_el.child_signal(cell_option_signal.map(move |cell_option| {
cell_option
.into_option_element()
.map(|cell| Self::align_child(cell, apply_alignment))
}));
}
self
}
pub fn cells<IOE: IntoOptionElement + 'static, I: IntoIterator<Item = IOE>>(
mut self,
cells_options_option: impl Into<Option<I>>,
) -> Self
where
I::IntoIter: Send + 'static,
{
if let Some(cells_options) = cells_options_option.into() {
let apply_alignment = self.apply_alignment_wrapper();
self.raw_el = self.raw_el.children(cells_options.into_iter().map(move |cell_option| {
cell_option
.into_option_element()
.map(|cell| Self::align_child(cell, apply_alignment))
}));
}
self
}
pub fn cells_signal_vec<IOE: IntoOptionElement + 'static, S: SignalVec<Item = IOE> + Send + 'static>(
mut self,
cells_options_signal_vec_option: impl Into<Option<S>>,
) -> Self {
if let Some(cells_options_signal_vec) = cells_options_signal_vec_option.into() {
let apply_alignment = self.apply_alignment_wrapper();
self.raw_el = self
.raw_el
.children_signal_vec(cells_options_signal_vec.map(move |cell_option| {
cell_option
.into_option_element()
.map(|cell| Self::align_child(cell, apply_alignment))
}));
}
self
}
}
impl<NodeType: Bundle> Alignable for Grid<NodeType> {
fn aligner(&mut self) -> Option<Aligner> {
Some(Aligner::Grid)
}
fn align_mut(&mut self) -> &mut Option<AlignHolder> {
&mut self.align
}
fn apply_content_alignment(node: &mut Node, alignment: Alignment, action: AddRemove) {
Stack::<NodeType>::apply_content_alignment(node, alignment, action);
}
}
impl<NodeType: Bundle> ChildAlignable for Grid<NodeType> {
fn update_node(mut node: Mut<Node>) {
node.display = Display::Grid;
}
fn apply_alignment(node: &mut Node, alignment: Alignment, action: AddRemove) {
Stack::<NodeType>::apply_alignment(node, alignment, action);
}
}