use std::marker::PhantomData;
use masonry::core::Axis;
use masonry::properties::types::{AsUnit, Length};
use masonry::widgets::{self, ceil_length};
use crate::core::{MessageContext, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
use crate::{Pod, ViewCtx, WidgetView};
pub fn split<State, Action, ChildA, ChildB>(
child1: ChildA,
child2: ChildB,
) -> Split<ChildA, ChildB, State, Action>
where
ChildA: WidgetView<State, Action>,
ChildB: WidgetView<State, Action>,
{
Split {
split_axis: Axis::Horizontal,
split_point: 0.5,
min_size: (Length::ZERO, Length::ZERO),
bar_size: 6.px(),
min_bar_area: 6.px(),
solid_bar: false,
draggable: true,
child1,
child2,
phantom: PhantomData,
}
}
#[must_use = "View values do nothing unless provided to Xilem."]
pub struct Split<ChildA, ChildB, State, Action = ()> {
split_axis: Axis,
split_point: f64,
min_size: (Length, Length), bar_size: Length, min_bar_area: Length, solid_bar: bool,
draggable: bool,
child1: ChildA,
child2: ChildB,
phantom: PhantomData<fn() -> (State, Action)>,
}
impl<ChildA, ChildB, State, Action> Split<ChildA, ChildB, State, Action> {
pub fn split_axis(mut self, axis: Axis) -> Self {
self.split_axis = axis;
self
}
#[track_caller]
pub fn split_point(mut self, split_point: f64) -> Self {
assert!(
(0.0..=1.0).contains(&split_point),
"split_point must be in the range [0.0, 1.0], got {split_point}"
);
self.split_point = split_point;
self
}
pub fn min_size(mut self, first: Length, second: Length) -> Self {
self.min_size = (ceil_length(first), ceil_length(second));
self
}
#[track_caller]
pub fn bar_size(mut self, bar_size: Length) -> Self {
self.bar_size = ceil_length(bar_size);
self
}
#[track_caller]
pub fn min_bar_area(mut self, min_bar_area: Length) -> Self {
self.min_bar_area = ceil_length(min_bar_area);
self
}
pub fn draggable(mut self, draggable: bool) -> Self {
self.draggable = draggable;
self
}
pub fn solid_bar(mut self, solid: bool) -> Self {
self.solid_bar = solid;
self
}
}
const CHILD1_VIEW_ID: ViewId = ViewId::new(0);
const CHILD2_VIEW_ID: ViewId = ViewId::new(1);
impl<ChildA, ChildB, State, Action> ViewMarker for Split<ChildA, ChildB, State, Action> {}
impl<ChildA, ChildB, State, Action> View<State, Action, ViewCtx>
for Split<ChildA, ChildB, State, Action>
where
State: 'static,
Action: 'static,
ChildA: WidgetView<State, Action>,
ChildB: WidgetView<State, Action>,
{
type Element = Pod<widgets::Split<ChildA::Widget, ChildB::Widget>>;
type ViewState = (ChildA::ViewState, ChildB::ViewState);
fn build(&self, ctx: &mut ViewCtx, app_state: &mut State) -> (Self::Element, Self::ViewState) {
let (child1, child1_state) =
ctx.with_id(CHILD1_VIEW_ID, |ctx| self.child1.build(ctx, app_state));
let (child2, child2_state) =
ctx.with_id(CHILD2_VIEW_ID, |ctx| self.child2.build(ctx, app_state));
let widget_pod = ctx.create_pod(
widgets::Split::new(child1.new_widget, child2.new_widget)
.split_axis(self.split_axis)
.split_point(self.split_point)
.min_size(self.min_size.0, self.min_size.1)
.bar_size(self.bar_size)
.min_bar_area(self.min_bar_area)
.draggable(self.draggable)
.solid_bar(self.solid_bar),
);
(widget_pod, (child1_state, child2_state))
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
if prev.split_axis != self.split_axis {
widgets::Split::set_split_axis(&mut element, self.split_axis);
}
if prev.split_point != self.split_point {
widgets::Split::set_split_point(&mut element, self.split_point);
}
if prev.min_size != self.min_size {
widgets::Split::set_min_size(&mut element, self.min_size.0, self.min_size.1);
}
if prev.bar_size != self.bar_size {
widgets::Split::set_bar_size(&mut element, self.bar_size);
}
if prev.min_bar_area != self.min_bar_area {
widgets::Split::set_min_bar_area(&mut element, self.min_bar_area);
}
if prev.draggable != self.draggable {
widgets::Split::set_draggable(&mut element, self.draggable);
}
if prev.solid_bar != self.solid_bar {
widgets::Split::set_bar_solid(&mut element, self.solid_bar);
}
ctx.with_id(CHILD1_VIEW_ID, |ctx| {
let child1_element = widgets::Split::child1_mut(&mut element);
self.child1.rebuild(
&prev.child1,
&mut view_state.0,
ctx,
child1_element,
app_state,
);
});
ctx.with_id(CHILD2_VIEW_ID, |ctx| {
let child2_element = widgets::Split::child2_mut(&mut element);
self.child2.rebuild(
&prev.child2,
&mut view_state.1,
ctx,
child2_element,
app_state,
);
});
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut ViewCtx,
mut element: Mut<'_, Self::Element>,
) {
let child1_element = widgets::Split::child1_mut(&mut element);
self.child1.teardown(&mut view_state.0, ctx, child1_element);
let child2_element = widgets::Split::child2_mut(&mut element);
self.child2.teardown(&mut view_state.1, ctx, child2_element);
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
mut element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action> {
match message.take_first() {
Some(CHILD1_VIEW_ID) => {
let child1_element = widgets::Split::child1_mut(&mut element);
self.child1
.message(&mut view_state.0, message, child1_element, app_state)
}
Some(CHILD2_VIEW_ID) => {
let child2_element = widgets::Split::child2_mut(&mut element);
self.child2
.message(&mut view_state.1, message, child2_element, app_state)
}
view_id => {
tracing::error!(
?message,
"Invalid message arrived in Split::message, expected {:?} or {:?}, got {:?}. This is a bug.",
CHILD1_VIEW_ID,
CHILD2_VIEW_ID,
view_id
);
MessageResult::Stale
}
}
}
}