use std::marker::PhantomData;
use masonry::core::{FromDynWidget, Widget, WidgetMut};
use masonry::properties::types::Length;
use masonry::widgets;
use crate::core::{
AppendVec, ElementSplice, MessageContext, MessageResult, Mut, SuperElement, View, ViewElement,
ViewMarker, ViewSequence,
};
use crate::{Pod, ViewCtx, WidgetView};
pub use masonry::widgets::GridParams;
pub fn grid<State, Action, Seq: GridSequence<State, Action>>(
sequence: Seq,
width: i32,
height: i32,
) -> Grid<Seq, State, Action> {
Grid {
sequence,
spacing: Length::ZERO,
height,
width,
phantom: PhantomData,
}
}
#[must_use = "View values do nothing unless provided to Xilem."]
pub struct Grid<Seq, State, Action = ()> {
sequence: Seq,
spacing: Length,
width: i32,
height: i32,
phantom: PhantomData<fn() -> (State, Action)>,
}
impl<Seq, State, Action> Grid<Seq, State, Action> {
#[track_caller]
pub fn spacing(mut self, spacing: Length) -> Self {
self.spacing = spacing;
self
}
}
mod hidden {
use super::GridElement;
use crate::core::AppendVec;
#[doc(hidden)]
#[expect(
unnameable_types,
reason = "Implementation detail, public because of trait visibility rules"
)]
pub struct GridState<SeqState> {
pub(crate) seq_state: SeqState,
pub(crate) scratch: AppendVec<GridElement>,
}
}
use hidden::GridState;
impl<Seq, State, Action> ViewMarker for Grid<Seq, State, Action> {}
impl<State, Action, Seq> View<State, Action, ViewCtx> for Grid<Seq, State, Action>
where
State: 'static,
Action: 'static,
Seq: GridSequence<State, Action>,
{
type Element = Pod<widgets::Grid>;
type ViewState = GridState<Seq::SeqState>;
fn build(&self, ctx: &mut ViewCtx, app_state: &mut State) -> (Self::Element, Self::ViewState) {
let mut elements = AppendVec::default();
let mut widget = widgets::Grid::with_dimensions(self.width, self.height);
widget = widget.with_spacing(self.spacing);
let seq_state = self.sequence.seq_build(ctx, &mut elements, app_state);
for element in elements.drain() {
widget = widget.with_child(element.child.new_widget, element.params);
}
let pod = ctx.create_pod(widget);
(
pod,
GridState {
seq_state,
scratch: elements,
},
)
}
fn rebuild(
&self,
prev: &Self,
GridState { seq_state, scratch }: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
if prev.height != self.height {
widgets::Grid::set_height(&mut element, self.height);
}
if prev.width != self.width {
widgets::Grid::set_width(&mut element, self.width);
}
if prev.spacing != self.spacing {
widgets::Grid::set_spacing(&mut element, self.spacing);
}
let mut splice = GridSplice::new(element, scratch);
self.sequence
.seq_rebuild(&prev.sequence, seq_state, ctx, &mut splice, app_state);
debug_assert!(scratch.is_empty());
}
fn teardown(
&self,
GridState { seq_state, scratch }: &mut Self::ViewState,
ctx: &mut ViewCtx,
element: Mut<'_, Self::Element>,
) {
let mut splice = GridSplice::new(element, scratch);
self.sequence.seq_teardown(seq_state, ctx, &mut splice);
debug_assert!(scratch.is_empty());
}
fn message(
&self,
GridState { seq_state, scratch }: &mut Self::ViewState,
message: &mut MessageContext,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action> {
let mut splice = GridSplice::new(element, scratch);
let result = self
.sequence
.seq_message(seq_state, message, &mut splice, app_state);
debug_assert!(scratch.is_empty());
result
}
}
impl ViewElement for GridElement {
type Mut<'w> = GridElementMut<'w>;
}
impl SuperElement<Self, ViewCtx> for GridElement {
fn upcast(_ctx: &mut ViewCtx, child: Self) -> Self {
child
}
fn with_downcast_val<R>(
mut this: Mut<'_, Self>,
f: impl FnOnce(Mut<'_, Self>) -> R,
) -> (Self::Mut<'_>, R) {
let r = {
let parent = this.parent.reborrow_mut();
let reborrow = GridElementMut {
idx: this.idx,
parent,
};
f(reborrow)
};
(this, r)
}
}
impl<W: Widget + FromDynWidget + ?Sized> SuperElement<Pod<W>, ViewCtx> for GridElement {
fn upcast(_: &mut ViewCtx, child: Pod<W>) -> Self {
Self {
child: child.erased(),
params: GridParams::new(1, 1, 1, 1),
}
}
fn with_downcast_val<R>(
mut this: Mut<'_, Self>,
f: impl FnOnce(Mut<'_, Pod<W>>) -> R,
) -> (Mut<'_, Self>, R) {
let ret = {
let mut child = widgets::Grid::child_mut(&mut this.parent, this.idx);
let downcast = child.downcast();
f(downcast)
};
(this, ret)
}
}
impl ElementSplice<GridElement> for GridSplice<'_, '_> {
fn with_scratch<R>(&mut self, f: impl FnOnce(&mut AppendVec<GridElement>) -> R) -> R {
let ret = f(self.scratch);
for element in self.scratch.drain() {
widgets::Grid::insert_grid_child_at(
&mut self.element,
self.idx,
element.child.new_widget,
element.params,
);
self.idx += 1;
}
ret
}
fn insert(&mut self, element: GridElement) {
widgets::Grid::insert_grid_child_at(
&mut self.element,
self.idx,
element.child.new_widget,
element.params,
);
self.idx += 1;
}
fn mutate<R>(&mut self, f: impl FnOnce(Mut<'_, GridElement>) -> R) -> R {
let child = GridElementMut {
parent: self.element.reborrow_mut(),
idx: self.idx,
};
let ret = f(child);
self.idx += 1;
ret
}
fn skip(&mut self, n: usize) {
self.idx += n;
}
fn index(&self) -> usize {
self.idx
}
fn delete<R>(&mut self, f: impl FnOnce(Mut<'_, GridElement>) -> R) -> R {
let ret = {
let child = GridElementMut {
parent: self.element.reborrow_mut(),
idx: self.idx,
};
f(child)
};
widgets::Grid::remove_child(&mut self.element, self.idx);
ret
}
}
pub trait GridSequence<State, Action = ()>:
ViewSequence<State, Action, ViewCtx, GridElement>
{
}
impl<Seq, State, Action> GridSequence<State, Action> for Seq where
Seq: ViewSequence<State, Action, ViewCtx, GridElement>
{
}
pub trait GridExt<State, Action>: WidgetView<State, Action> {
fn grid_item(self, params: impl Into<GridParams>) -> GridItem<Self, State, Action>
where
State: 'static,
Action: 'static,
Self: Sized,
{
grid_item(self, params)
}
fn grid_pos(self, x: i32, y: i32) -> GridItem<Self, State, Action>
where
State: 'static,
Action: 'static,
Self: Sized,
{
grid_item(self, GridParams::new(x, y, 1, 1))
}
}
impl<State, Action, V: WidgetView<State, Action>> GridExt<State, Action> for V {}
pub struct GridElement {
child: Pod<dyn Widget>,
params: GridParams,
}
pub struct GridElementMut<'w> {
parent: WidgetMut<'w, widgets::Grid>,
idx: usize,
}
struct GridSplice<'w, 's> {
idx: usize,
element: WidgetMut<'w, widgets::Grid>,
scratch: &'s mut AppendVec<GridElement>,
}
impl<'w, 's> GridSplice<'w, 's> {
fn new(element: WidgetMut<'w, widgets::Grid>, scratch: &'s mut AppendVec<GridElement>) -> Self {
Self {
idx: 0,
element,
scratch,
}
}
}
pub struct GridItem<V, State, Action> {
view: V,
params: GridParams,
phantom: PhantomData<fn() -> (State, Action)>,
}
pub fn grid_item<V, State, Action>(
view: V,
params: impl Into<GridParams>,
) -> GridItem<V, State, Action>
where
State: 'static,
Action: 'static,
V: WidgetView<State, Action>,
{
GridItem {
view,
params: params.into(),
phantom: PhantomData,
}
}
impl<V, State, Action> ViewMarker for GridItem<V, State, Action> {}
impl<State, Action, V> View<State, Action, ViewCtx> for GridItem<V, State, Action>
where
State: 'static,
Action: 'static,
V: WidgetView<State, Action>,
{
type Element = GridElement;
type ViewState = V::ViewState;
fn build(&self, ctx: &mut ViewCtx, app_state: &mut State) -> (Self::Element, Self::ViewState) {
let (pod, state) = self.view.build(ctx, app_state);
(
GridElement {
child: pod.erased(),
params: self.params,
},
state,
)
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
{
if self.params != prev.params {
widgets::Grid::update_child_grid_params(
&mut element.parent,
element.idx,
self.params,
);
}
let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx);
self.view
.rebuild(&prev.view, view_state, ctx, child.downcast(), app_state);
}
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<'_, Self::Element>,
) {
let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx);
self.view.teardown(view_state, ctx, child.downcast());
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
mut element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action> {
let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx);
self.view
.message(view_state, message, child.downcast(), app_state)
}
}