mirror of https://github.com/linebender/xilem
Make the environment and element available in `View::message` (#1220)
See [#xilem > Environment System](https://xi.zulipchat.com/#narrow/channel/354396-xilem/topic/Environment.20System/with/529496085) and [#xilem > Giving `message` access to the Element](https://xi.zulipchat.com/#narrow/channel/354396-xilem/topic/Giving.20.60message.60.20access.20to.20the.20Element/with/529436165) for motivation. This is a massive refactor, to significant improve the capabilities of Xilem. The main difficulty so far has been sequences, as we need to skip the right amount of items. This will either require storing large amounts of item, or restoring the `count` method on sequences. Obviously Xilem and Xilem Web are not yet done in this PR. --------- Co-authored-by: Philipp Mildenberger <philipp@mildenberger.me>
This commit is contained in:
parent
be1c8e87d5
commit
d4d48d80a5
|
@ -11,9 +11,11 @@ use masonry::core::{ErasedAction, NewWidget, Widget, WidgetId};
|
|||
use masonry::peniko::Blob;
|
||||
use masonry_winit::app::{AppDriver, DriverCtx, MasonryState, MasonryUserEvent, WindowId};
|
||||
use winit::window::WindowAttributes;
|
||||
use xilem_core::{AnyViewState, RawProxy, SendMessage, View};
|
||||
|
||||
use crate::core::{DynMessage, MessageResult, ProxyError, ViewId};
|
||||
use crate::core::{
|
||||
AnyViewState, DynMessage, MessageContext, MessageResult, ProxyError, RawProxy, SendMessage,
|
||||
View, ViewId, ViewPathTracker,
|
||||
};
|
||||
use crate::window_view::{CreateWindow, WindowView};
|
||||
use crate::{AnyWidgetView, AppState, ViewCtx, WindowOptions};
|
||||
|
||||
|
@ -25,6 +27,7 @@ pub struct MasonryDriver<State, Logic> {
|
|||
runtime: Arc<tokio::runtime::Runtime>,
|
||||
// Fonts which will be registered on startup.
|
||||
fonts: Vec<Blob<u8>>,
|
||||
scratch_id_path: Vec<ViewId>,
|
||||
}
|
||||
|
||||
struct Window<State> {
|
||||
|
@ -58,6 +61,7 @@ where
|
|||
proxy: Arc::new(MasonryProxy(Box::new(event_sink))),
|
||||
runtime: Arc::new(runtime),
|
||||
fonts,
|
||||
scratch_id_path: Vec::new(),
|
||||
};
|
||||
let windows: Vec<_> = (driver.logic)(&mut driver.state)
|
||||
.map(|(id, attrs, root_widget_view)| {
|
||||
|
@ -249,22 +253,46 @@ where
|
|||
return;
|
||||
};
|
||||
|
||||
let mut id_path = std::mem::take(&mut self.scratch_id_path);
|
||||
id_path.clear();
|
||||
let message_result = if widget_id == ASYNC_MARKER_WIDGET {
|
||||
// If this is not an action from a real widget, dispatch it using the path it contains.
|
||||
let (path, message) = *action.downcast::<MessagePackage>().unwrap();
|
||||
// Handle an async path
|
||||
window.view.message(
|
||||
&mut window.view_state,
|
||||
&path,
|
||||
id_path.extend_from_slice(&path);
|
||||
let mut message_context = MessageContext::new(
|
||||
std::mem::take(window.view_ctx.environment()),
|
||||
id_path,
|
||||
message.into(),
|
||||
&mut self.state,
|
||||
)
|
||||
} else if let Some(id_path) = window.view_ctx.get_id_path(widget_id) {
|
||||
window.view.message(
|
||||
);
|
||||
let res = window.view.message(
|
||||
&mut window.view_state,
|
||||
id_path.as_slice(),
|
||||
DynMessage(action),
|
||||
&mut message_context,
|
||||
masonry_ctx.window_handle_and_render_root(window_id),
|
||||
&mut self.state,
|
||||
)
|
||||
);
|
||||
let (env, id_path, _message) = message_context.finish();
|
||||
*window.view_ctx.environment() = env;
|
||||
self.scratch_id_path = id_path;
|
||||
// TODO: Handle `message` somehow?
|
||||
res
|
||||
} else if let Some(path) = window.view_ctx.get_id_path(widget_id) {
|
||||
id_path.extend_from_slice(path);
|
||||
let mut message_context = MessageContext::new(
|
||||
std::mem::take(window.view_ctx.environment()),
|
||||
id_path,
|
||||
DynMessage(action),
|
||||
);
|
||||
let res = window.view.message(
|
||||
&mut window.view_state,
|
||||
&mut message_context,
|
||||
masonry_ctx.window_handle_and_render_root(window_id),
|
||||
&mut self.state,
|
||||
);
|
||||
let (env, id_path, _message) = message_context.finish();
|
||||
*window.view_ctx.environment() = env;
|
||||
self.scratch_id_path = id_path;
|
||||
// TODO: Handle `message` somehow?
|
||||
res
|
||||
} else {
|
||||
tracing::error!(
|
||||
"Got action {action:?} for unknown widget. Did you forget to use `with_action_widget`?"
|
||||
|
@ -291,7 +319,7 @@ where
|
|||
);
|
||||
}
|
||||
MessageResult::Nop => {}
|
||||
MessageResult::Stale(_) => {
|
||||
MessageResult::Stale => {
|
||||
tracing::info!("Discarding message");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -41,55 +41,82 @@ impl<
|
|||
{
|
||||
type OneOfElement = Pod<OneOfWidget<A, B, C, D, E, F, G, H, I>>;
|
||||
|
||||
fn with_downcast_a(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<A>>)) {
|
||||
fn with_downcast_a<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<A>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::A(a) => f(elem.ctx.get_mut(a)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_b(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<B>>)) {
|
||||
fn with_downcast_b<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<B>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::B(b) => f(elem.ctx.get_mut(b)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_c(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<C>>)) {
|
||||
fn with_downcast_c<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<C>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::C(c) => f(elem.ctx.get_mut(c)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_d(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<D>>)) {
|
||||
fn with_downcast_d<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<D>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::D(d) => f(elem.ctx.get_mut(d)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_e(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<E>>)) {
|
||||
fn with_downcast_e<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<E>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::E(e) => f(elem.ctx.get_mut(e)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_f(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<F>>)) {
|
||||
fn with_downcast_f<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<F>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::F(f_) => f(elem.ctx.get_mut(f_)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_g(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<G>>)) {
|
||||
fn with_downcast_g<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<G>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::G(g) => f(elem.ctx.get_mut(g)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_h(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<H>>)) {
|
||||
fn with_downcast_h<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<H>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::H(h) => f(elem.ctx.get_mut(h)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn with_downcast_i(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<I>>)) {
|
||||
fn with_downcast_i<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<I>>) -> R,
|
||||
) -> R {
|
||||
match elem.widget {
|
||||
OneOfWidget::I(i) => f(elem.ctx.get_mut(i)),
|
||||
_ => unreachable!(),
|
||||
|
|
|
@ -9,9 +9,8 @@ use masonry::properties::{
|
|||
DisabledBackground, HoveredBorderColor, Padding,
|
||||
};
|
||||
use masonry::widgets::{self, ButtonPress};
|
||||
use xilem_core::ViewPathTracker;
|
||||
|
||||
use crate::core::{DynMessage, Mut, View, ViewMarker};
|
||||
use crate::core::{MessageContext, Mut, View, ViewMarker, ViewPathTracker};
|
||||
use crate::property_tuple::PropertyTuple;
|
||||
use crate::style::Style;
|
||||
use crate::view::Label;
|
||||
|
@ -205,25 +204,31 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
match id_path.split_first() {
|
||||
Some((&LABEL_VIEW_ID, rest)) => self.label.message(&mut (), rest, message, app_state),
|
||||
None => match message.downcast::<ButtonPress>() {
|
||||
Ok(press) => (self.callback)(app_state, press.button),
|
||||
Err(message) => {
|
||||
match message.take_first() {
|
||||
Some(LABEL_VIEW_ID) => self.label.message(
|
||||
&mut (),
|
||||
message,
|
||||
widgets::Button::label_mut(&mut element),
|
||||
app_state,
|
||||
),
|
||||
None => match message.take_message::<ButtonPress>() {
|
||||
Some(press) => (self.callback)(app_state, press.button),
|
||||
None => {
|
||||
// TODO: Panic?
|
||||
tracing::error!(
|
||||
"Wrong message type in Button::message: {message:?} expected {}",
|
||||
type_name::<ButtonPress>()
|
||||
);
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
tracing::warn!("Got unexpected id path in Button::message");
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::PropertyTuple as _;
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::core::{MessageContext, Mut, ViewMarker};
|
||||
use crate::style::Style;
|
||||
use crate::{MessageResult, Pod, View, ViewCtx, ViewId};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx};
|
||||
|
||||
use masonry::core::ArcStr;
|
||||
use masonry::properties::*;
|
||||
|
@ -141,19 +141,19 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
debug_assert!(
|
||||
id_path.is_empty(),
|
||||
message.remaining_path().is_empty(),
|
||||
"id path should be empty in Checkbox::message"
|
||||
);
|
||||
match message.downcast::<CheckboxToggled>() {
|
||||
Ok(checked) => MessageResult::Action((self.callback)(app_state, checked.0)),
|
||||
Err(message) => {
|
||||
match message.take_message::<CheckboxToggled>() {
|
||||
Some(checked) => MessageResult::Action((self.callback)(app_state, checked.0)),
|
||||
None => {
|
||||
tracing::error!("Wrong message type in Checkbox::message, got {message:?}.");
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ pub use masonry::widgets::FlexParams;
|
|||
use masonry::widgets::{self};
|
||||
|
||||
use crate::core::{
|
||||
AppendVec, DynMessage, ElementSplice, MessageResult, Mut, SuperElement, View, ViewElement,
|
||||
AppendVec, ElementSplice, MessageContext, MessageResult, Mut, SuperElement, View, ViewElement,
|
||||
ViewId, ViewMarker, ViewPathTracker, ViewSequence,
|
||||
};
|
||||
use crate::{AnyWidgetView, Pod, PropertyTuple as _, ViewCtx, WidgetView};
|
||||
|
@ -252,12 +252,13 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let mut splice = FlexSplice::new(element);
|
||||
self.sequence
|
||||
.seq_message(view_state, id_path, message, app_state)
|
||||
.seq_message(view_state, message, &mut splice, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,6 +409,10 @@ impl ElementSplice<FlexElement> for FlexSplice<'_> {
|
|||
fn skip(&mut self, n: usize) {
|
||||
self.idx += n;
|
||||
}
|
||||
|
||||
fn index(&self) -> usize {
|
||||
self.idx
|
||||
}
|
||||
}
|
||||
|
||||
/// An ordered sequence of views for a [`Flex`] view.
|
||||
|
@ -594,11 +599,14 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.view.message(view_state, id_path, message, app_state)
|
||||
let mut child = widgets::Flex::child_mut(&mut element.parent, element.idx)
|
||||
.expect("FlexWrapper always has a widget child");
|
||||
self.view
|
||||
.message(view_state, message, child.downcast(), app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -664,11 +672,11 @@ impl<State, Action> View<State, Action, ViewCtx> for FlexSpacer {
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
_: &[ViewId],
|
||||
_: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_: Mut<'_, Self::Element>,
|
||||
_: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
unreachable!()
|
||||
unreachable!("FlexSpacer doesn't handle messages but got {message:?}.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -896,16 +904,16 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let (start, rest) = id_path
|
||||
.split_first()
|
||||
let start = message
|
||||
.take_first()
|
||||
.expect("Id path has elements for AnyFlexChild");
|
||||
if start.routing_id() != view_state.generation {
|
||||
// The message was sent to a previous edition of the inner value
|
||||
return MessageResult::Stale(message);
|
||||
return MessageResult::Stale;
|
||||
}
|
||||
let Self::Item(flex_item) = self else {
|
||||
unreachable!(
|
||||
|
@ -913,6 +921,11 @@ where
|
|||
)
|
||||
};
|
||||
|
||||
flex_item.message(view_state.inner.as_mut().unwrap(), rest, message, app_state)
|
||||
flex_item.message(
|
||||
view_state.inner.as_mut().unwrap(),
|
||||
message,
|
||||
element,
|
||||
app_state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ use masonry::properties::{Background, BorderColor, BorderWidth, CornerRadius, Pa
|
|||
use masonry::widgets;
|
||||
|
||||
use crate::core::{
|
||||
AppendVec, DynMessage, ElementSplice, MessageResult, Mut, SuperElement, View, ViewElement,
|
||||
ViewId, ViewMarker, ViewSequence,
|
||||
AppendVec, ElementSplice, MessageContext, MessageResult, Mut, SuperElement, View, ViewElement,
|
||||
ViewMarker, ViewSequence,
|
||||
};
|
||||
use crate::{Pod, PropertyTuple as _, ViewCtx, WidgetView};
|
||||
|
||||
|
@ -179,12 +179,13 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let mut splice = GridSplice::new(element);
|
||||
self.sequence
|
||||
.seq_message(view_state, id_path, message, app_state)
|
||||
.seq_message(view_state, message, &mut splice, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,6 +284,10 @@ impl ElementSplice<GridElement> for GridSplice<'_> {
|
|||
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 {
|
||||
|
@ -479,10 +484,12 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.view.message(view_state, id_path, message, app_state)
|
||||
let mut child = widgets::Grid::child_mut(&mut element.parent, element.idx);
|
||||
self.view
|
||||
.message(view_state, message, child.downcast(), app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
//! The bitmap image widget.
|
||||
|
||||
use masonry::widgets::{self};
|
||||
use masonry::widgets;
|
||||
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx, ViewId};
|
||||
use crate::core::{MessageContext, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx};
|
||||
|
||||
pub use masonry::core::ObjectFit;
|
||||
/// Displays the bitmap `image`.
|
||||
|
@ -84,13 +84,14 @@ impl<State, Action> View<State, Action, ViewCtx> for Image {
|
|||
fn message(
|
||||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
_: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_: Mut<'_, Self::Element>,
|
||||
_: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
tracing::error!(
|
||||
"Message arrived in Image::message, but Image doesn't consume any messages, this is a bug"
|
||||
?message,
|
||||
"Message arrived in Image::message, but Image doesn't consume any messages, this is a bug."
|
||||
);
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::core::{
|
||||
AppendVec, DynMessage, ElementSplice, MessageResult, Mut, SuperElement, View, ViewElement,
|
||||
ViewId, ViewMarker, ViewSequence,
|
||||
AppendVec, ElementSplice, MessageContext, MessageResult, Mut, SuperElement, View, ViewElement,
|
||||
ViewMarker, ViewSequence,
|
||||
};
|
||||
use crate::{Pod, PropertyTuple as _, ViewCtx};
|
||||
use masonry::core::{FromDynWidget, Widget, WidgetMut};
|
||||
|
@ -173,12 +173,13 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let mut splice = IndexedStackSplice::new(element);
|
||||
self.sequence
|
||||
.seq_message(view_state, id_path, message, app_state)
|
||||
.seq_message(view_state, message, &mut splice, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,6 +265,10 @@ impl ElementSplice<IndexedStackElement> for IndexedStackSplice<'_> {
|
|||
self.idx += n;
|
||||
}
|
||||
|
||||
fn index(&self) -> usize {
|
||||
self.idx
|
||||
}
|
||||
|
||||
fn delete<R>(&mut self, f: impl FnOnce(Mut<'_, IndexedStackElement>) -> R) -> R {
|
||||
let ret = {
|
||||
let child = IndexedStackElementMut {
|
||||
|
|
|
@ -7,10 +7,11 @@ use masonry::properties::{DisabledTextColor, TextColor};
|
|||
use masonry::widgets::{
|
||||
LineBreaking, {self},
|
||||
};
|
||||
use xilem_core::MessageContext;
|
||||
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::core::{Mut, ViewMarker};
|
||||
use crate::style::Style;
|
||||
use crate::{MessageResult, Pod, PropertyTuple as _, TextAlign, View, ViewCtx, ViewId};
|
||||
use crate::{MessageResult, Pod, PropertyTuple as _, TextAlign, View, ViewCtx};
|
||||
|
||||
/// A non-interactive text element.
|
||||
/// # Example
|
||||
|
@ -175,13 +176,14 @@ impl<State, Action> View<State, Action, ViewCtx> for Label {
|
|||
fn message(
|
||||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
tracing::error!(
|
||||
?message,
|
||||
"Message arrived in Label::message, but Label doesn't consume any messages, this is a bug"
|
||||
);
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ use std::marker::PhantomData;
|
|||
|
||||
use masonry::widgets;
|
||||
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx, ViewId, WidgetView};
|
||||
use crate::core::{MessageContext, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx, WidgetView};
|
||||
|
||||
/// A view which puts `child` into a scrollable region.
|
||||
///
|
||||
|
@ -74,10 +74,12 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.child.message(view_state, id_path, message, app_state)
|
||||
let child_element = widgets::Portal::child_mut(&mut element);
|
||||
self.child
|
||||
.message(view_state, message, child_element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use masonry::widgets;
|
||||
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx, ViewId};
|
||||
use crate::core::{MessageContext, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx};
|
||||
|
||||
/// A view which displays a progress bar.
|
||||
///
|
||||
|
@ -54,13 +54,14 @@ impl<State, Action> View<State, Action, ViewCtx> for ProgressBar {
|
|||
fn message(
|
||||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
tracing::error!(
|
||||
?message,
|
||||
"Message arrived in ProgressBar::message, but ProgressBar doesn't consume any messages, this is a bug"
|
||||
);
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ use masonry::widgets::{
|
|||
LineBreaking, {self},
|
||||
};
|
||||
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::{Color, MessageResult, Pod, TextAlign, View, ViewCtx, ViewId};
|
||||
use crate::core::{MessageContext, Mut, ViewMarker};
|
||||
use crate::{Color, MessageResult, Pod, TextAlign, View, ViewCtx};
|
||||
|
||||
/// A view which displays selectable text.
|
||||
pub fn prose(content: impl Into<ArcStr>) -> Prose {
|
||||
|
@ -189,13 +189,14 @@ impl<State, Action> View<State, Action, ViewCtx> for Prose {
|
|||
fn message(
|
||||
&self,
|
||||
_view_state: &mut Self::ViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
tracing::error!(
|
||||
?message,
|
||||
"Message arrived in Prose::message, but Prose doesn't consume any messages, this is a bug"
|
||||
);
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::property_tuple::PropertyTuple;
|
|||
use crate::style::Style;
|
||||
use masonry::widgets;
|
||||
|
||||
use crate::core::{DynMessage, Mut, View, ViewId, ViewMarker};
|
||||
use crate::core::{MessageContext, Mut, View, ViewMarker};
|
||||
use crate::{Pod, ViewCtx, WidgetView};
|
||||
|
||||
/// A widget with predefined size.
|
||||
|
@ -186,10 +186,13 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> crate::MessageResult<Action> {
|
||||
self.inner.message(view_state, id_path, message, app_state)
|
||||
let mut child = widgets::SizedBox::child_mut(&mut element)
|
||||
.expect("We only create SizedBox with a child");
|
||||
self.inner
|
||||
.message(view_state, message, child.downcast(), app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
use masonry::peniko::Color;
|
||||
use masonry::widgets;
|
||||
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx, ViewId};
|
||||
use crate::core::{MessageContext, Mut, ViewMarker};
|
||||
use crate::{MessageResult, Pod, View, ViewCtx};
|
||||
|
||||
/// An indefinite spinner.
|
||||
///
|
||||
|
@ -94,13 +94,14 @@ impl<State, Action> View<State, Action, ViewCtx> for Spinner {
|
|||
fn message(
|
||||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
_: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
_: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
tracing::error!(
|
||||
?message,
|
||||
"Message arrived in Spinner::message, but Spinner doesn't consume any messages, this is a bug"
|
||||
);
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use masonry::core::Axis;
|
||||
use masonry::widgets::{self};
|
||||
use xilem_core::{DynMessage, MessageResult, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use masonry::widgets;
|
||||
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{Pod, ViewCtx, WidgetView};
|
||||
|
||||
/// A container containing two other widgets, splitting the area either horizontally or vertically.
|
||||
|
@ -202,7 +202,7 @@ where
|
|||
prev: &Self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
mut element: xilem_core::Mut<'_, Self::Element>,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
if prev.split_axis != self.split_axis {
|
||||
|
@ -260,7 +260,7 @@ where
|
|||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
mut element: xilem_core::Mut<'_, Self::Element>,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
let child1_element = widgets::Split::child1_mut(&mut element);
|
||||
|
@ -275,27 +275,30 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[xilem_core::ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> xilem_core::MessageResult<Action> {
|
||||
match id_path.split_first() {
|
||||
Some((&CHILD1_VIEW_ID, rest)) => {
|
||||
match message.take_first() {
|
||||
Some(CHILD1_VIEW_ID) => {
|
||||
let child1_element = widgets::Split::child1_mut(&mut element);
|
||||
self.child1
|
||||
.message(&mut view_state.0, rest, message, app_state)
|
||||
.message(&mut view_state.0, message, child1_element, app_state)
|
||||
}
|
||||
Some((&CHILD2_VIEW_ID, rest)) => {
|
||||
Some(CHILD2_VIEW_ID) => {
|
||||
let child2_element = widgets::Split::child2_mut(&mut element);
|
||||
self.child2
|
||||
.message(&mut view_state.1, rest, message, app_state)
|
||||
.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(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use tokio::task::JoinHandle;
|
|||
use crate::ViewCtx;
|
||||
use crate::core::anymore::AnyDebug;
|
||||
use crate::core::{
|
||||
DynMessage, MessageProxy, MessageResult, Mut, NoElement, View, ViewId, ViewMarker,
|
||||
MessageContext, MessageProxy, MessageResult, Mut, NoElement, View, ViewId, ViewMarker,
|
||||
ViewPathTracker,
|
||||
};
|
||||
|
||||
|
@ -119,15 +119,15 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
debug_assert!(
|
||||
id_path.is_empty(),
|
||||
message.remaining_path().is_empty(),
|
||||
"id path should be empty in Task::message"
|
||||
);
|
||||
let message = message.downcast::<M>().unwrap();
|
||||
let message = message.take_message::<M>().unwrap();
|
||||
MessageResult::Action((self.on_event)(app_state, *message))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ use masonry::widgets::{self, TextAction};
|
|||
use vello::kurbo::Affine;
|
||||
use vello::peniko::Color;
|
||||
|
||||
use crate::core::{DynMessage, Mut, View, ViewMarker};
|
||||
use crate::core::{MessageContext, Mut, View, ViewMarker};
|
||||
use crate::property_tuple::PropertyTuple;
|
||||
use crate::style::Style;
|
||||
use crate::{InsertNewline, MessageResult, Pod, TextAlign, ViewCtx, ViewId};
|
||||
use crate::{InsertNewline, MessageResult, Pod, TextAlign, ViewCtx};
|
||||
|
||||
// FIXME - A major problem of the current approach (always setting the text_input contents)
|
||||
// is that if the user forgets to hook up the modify the state's contents in the callback,
|
||||
|
@ -225,16 +225,16 @@ impl<State: 'static, Action: 'static> View<State, Action, ViewCtx> for TextInput
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
debug_assert!(
|
||||
id_path.is_empty(),
|
||||
message.remaining_path().is_empty(),
|
||||
"id path should be empty in TextInput::message"
|
||||
);
|
||||
match message.downcast::<TextAction>() {
|
||||
Ok(action) => match *action {
|
||||
match message.take_message::<TextAction>() {
|
||||
Some(action) => match *action {
|
||||
TextAction::Changed(text) => {
|
||||
MessageResult::Action((self.on_changed)(app_state, text))
|
||||
}
|
||||
|
@ -244,12 +244,12 @@ impl<State: 'static, Action: 'static> View<State, Action, ViewCtx> for TextInput
|
|||
|
||||
TextAction::Entered(_) => {
|
||||
tracing::error!("Textbox::message: on_enter is not set");
|
||||
MessageResult::Stale(DynMessage(action))
|
||||
MessageResult::Stale
|
||||
}
|
||||
},
|
||||
Err(message) => {
|
||||
tracing::error!("Wrong message type in TextInput::message: {message:?}.");
|
||||
MessageResult::Stale(message)
|
||||
None => {
|
||||
tracing::error!(?message, "Wrong message type in TextInput::message");
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::core::{DynMessage, View, ViewMarker};
|
||||
use crate::core::{MessageContext, Mut, View, ViewMarker};
|
||||
use crate::{Affine, Pod, ViewCtx, WidgetView};
|
||||
|
||||
/// A view which transforms the widget created by child.
|
||||
|
@ -111,7 +111,7 @@ where
|
|||
prev: &Self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
mut element: xilem_core::Mut<'_, Self::Element>,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
self.child.rebuild(
|
||||
|
@ -143,7 +143,7 @@ where
|
|||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
element: xilem_core::Mut<'_, Self::Element>,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
self.child
|
||||
|
@ -153,11 +153,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[xilem_core::ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> xilem_core::MessageResult<Action> {
|
||||
self.child
|
||||
.message(&mut view_state.child, id_path, message, app_state)
|
||||
.message(&mut view_state.child, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,9 @@ use masonry::core::ArcStr;
|
|||
use masonry::parley::style::{FontStack, FontWeight};
|
||||
use masonry::widgets;
|
||||
use vello::peniko::Color;
|
||||
use xilem_core::ViewPathTracker;
|
||||
|
||||
use super::{Label, label};
|
||||
use crate::core::{DynMessage, Mut, ViewMarker};
|
||||
use crate::core::{MessageContext, Mut, ViewMarker, ViewPathTracker};
|
||||
use crate::style::Style as _;
|
||||
use crate::{MessageResult, Pod, TextAlign, View, ViewCtx, ViewId};
|
||||
|
||||
|
@ -151,18 +150,25 @@ impl<State, Action> View<State, Action, ViewCtx> for VariableLabel {
|
|||
fn message(
|
||||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
if let Some((first, remainder)) = id_path.split_first() {
|
||||
if let Some(first) = message.take_first() {
|
||||
assert_eq!(first.routing_id(), 0);
|
||||
self.label.message(&mut (), remainder, message, app_state)
|
||||
|
||||
self.label.message(
|
||||
&mut (),
|
||||
message,
|
||||
widgets::VariableLabel::label_mut(&mut element),
|
||||
app_state,
|
||||
)
|
||||
} else {
|
||||
tracing::error!(
|
||||
?message,
|
||||
"Message arrived in VariableLabel::message, but VariableLabel doesn't consume any messages, this is a bug"
|
||||
);
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,15 @@ use std::{collections::HashMap, marker::PhantomData, ops::Range};
|
|||
use masonry::core::{FromDynWidget, Widget, WidgetPod};
|
||||
use masonry::widgets::{self, VirtualScrollAction};
|
||||
use private::VirtualScrollState;
|
||||
use xilem_core::{
|
||||
AsyncCtx, DynMessage, MessageResult, SendMessage, View, ViewId, ViewMarker, ViewPathTracker,
|
||||
};
|
||||
|
||||
use crate::core::{
|
||||
AsyncCtx, MessageContext, MessageResult, Mut, SendMessage, View, ViewId, ViewMarker,
|
||||
ViewPathTracker,
|
||||
};
|
||||
use crate::{Pod, ViewCtx, WidgetView};
|
||||
|
||||
// TODO: Refactor this file massively due to new changes in Xilem.
|
||||
|
||||
/// A (vertical) virtual scrolling View, for Masonry's [`VirtualScroll`](widgets::VirtualScroll).
|
||||
///
|
||||
/// Virtual scrolling is a technique to improve performance when scrolling through long lists, by
|
||||
|
@ -174,7 +177,7 @@ where
|
|||
prev: &Self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
mut element: xilem_core::Mut<'_, Self::Element>,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
if self.valid_range != prev.valid_range {
|
||||
|
@ -305,7 +308,7 @@ where
|
|||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
mut element: xilem_core::Mut<'_, Self::Element>,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
for (&idx, child) in &view_state.previous_views {
|
||||
|
@ -325,28 +328,32 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[xilem_core::ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> xilem_core::MessageResult<Action> {
|
||||
if let [first, tail @ ..] = id_path {
|
||||
let child_idx = index_for_view_id(*first);
|
||||
if let Some(first) = message.take_first() {
|
||||
let child_idx = index_for_view_id(first);
|
||||
let target = view_state.previous_views.get(&child_idx);
|
||||
if let Some(target) = target {
|
||||
let state = view_state.view_states.get_mut(&child_idx).unwrap();
|
||||
let result = target.message(&mut state.state, tail, message, app_state);
|
||||
|
||||
let result = target.message(
|
||||
&mut state.state,
|
||||
message,
|
||||
widgets::VirtualScroll::child_mut(&mut element, child_idx),
|
||||
app_state,
|
||||
);
|
||||
if matches!(result, MessageResult::RequestRebuild) {
|
||||
state.requested_rebuild = true;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
tracing::error!("Message sent type in VirtualScroll::message: {message:?}");
|
||||
return MessageResult::Stale(message);
|
||||
return MessageResult::Stale;
|
||||
}
|
||||
}
|
||||
if message.is::<VirtualScrollAction>() {
|
||||
let action = message.downcast::<VirtualScrollAction>().unwrap();
|
||||
|
||||
if let Some(action) = message.take_message::<VirtualScrollAction>() {
|
||||
view_state.current_updated = true;
|
||||
// We know that the `current_views` have not been applied, so we can just brute force overwrite them.
|
||||
view_state.current_views.clear();
|
||||
|
@ -359,7 +366,9 @@ where
|
|||
}
|
||||
view_state.pending_action = Some(*action);
|
||||
MessageResult::RequestRebuild
|
||||
} else if message.is::<UpdateVirtualChildren>() {
|
||||
} else if let Some(UpdateVirtualChildren) =
|
||||
message.take_message::<UpdateVirtualChildren>().as_deref()
|
||||
{
|
||||
view_state.current_updated = true;
|
||||
view_state.current_views.clear();
|
||||
view_state.pending_children_update = false;
|
||||
|
@ -370,8 +379,8 @@ where
|
|||
}
|
||||
MessageResult::RequestRebuild
|
||||
} else {
|
||||
tracing::error!("Wrong message type in VirtualScroll::message: {message:?}");
|
||||
MessageResult::Stale(message)
|
||||
tracing::error!(?message, "Wrong message type in VirtualScroll::message");
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use tokio::task::JoinHandle;
|
|||
use crate::ViewCtx;
|
||||
use crate::core::anymore::AnyDebug;
|
||||
use crate::core::{
|
||||
DynMessage, MessageProxy, MessageResult, Mut, NoElement, View, ViewId, ViewMarker,
|
||||
MessageContext, MessageProxy, MessageResult, Mut, NoElement, View, ViewId, ViewMarker,
|
||||
ViewPathTracker,
|
||||
};
|
||||
|
||||
|
@ -139,15 +139,15 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
debug_assert!(
|
||||
id_path.is_empty(),
|
||||
message.remaining_path().is_empty(),
|
||||
"id path should be empty in Task::message"
|
||||
);
|
||||
let message = message.downcast::<M>().unwrap();
|
||||
let message = message.take_message::<M>().unwrap();
|
||||
MessageResult::Action((self.on_response)(app_state, *message))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,14 @@ use std::marker::PhantomData;
|
|||
use masonry::core::{FromDynWidget, Widget, WidgetMut};
|
||||
use masonry::properties::types::UnitPoint;
|
||||
use masonry::widgets;
|
||||
use xilem_core::{MessageResult, ViewId};
|
||||
pub use masonry::widgets::ChildAlignment;
|
||||
|
||||
use crate::core::{
|
||||
AppendVec, DynMessage, ElementSplice, Mut, SuperElement, View, ViewElement, ViewMarker,
|
||||
ViewSequence,
|
||||
AppendVec, ElementSplice, MessageContext, MessageResult, Mut, SuperElement, View, ViewElement,
|
||||
ViewMarker, ViewSequence,
|
||||
};
|
||||
use crate::{Pod, ViewCtx, WidgetView};
|
||||
|
||||
pub use masonry::widgets::ChildAlignment;
|
||||
|
||||
/// A widget that lays out its children on top of each other.
|
||||
/// The children are laid out back to front.
|
||||
///
|
||||
|
@ -114,12 +112,13 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let mut splice = ZStackSplice::new(element);
|
||||
self.sequence
|
||||
.seq_message(view_state, id_path, message, app_state)
|
||||
.seq_message(view_state, message, &mut splice, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,11 +223,14 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.view.message(view_state, id_path, message, app_state)
|
||||
let mut child = widgets::ZStack::child_mut(&mut element.parent, element.idx)
|
||||
.expect("ZStackWrapper always has a corresponding child");
|
||||
self.view
|
||||
.message(view_state, message, child.downcast(), app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,6 +368,10 @@ impl ElementSplice<ZStackElement> for ZStackSplice<'_> {
|
|||
self.idx += n;
|
||||
}
|
||||
|
||||
fn index(&self) -> usize {
|
||||
self.idx
|
||||
}
|
||||
|
||||
fn delete<R>(&mut self, f: impl FnOnce(Mut<'_, ZStackElement>) -> R) -> R {
|
||||
let ret = {
|
||||
let child = ZStackElementMut {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
use masonry::app::RenderRoot;
|
||||
use masonry::core::{NewWidget, Widget};
|
||||
use winit::window::{Window, WindowAttributes};
|
||||
use xilem_core::{AnyViewState, View, ViewElement, ViewMarker};
|
||||
|
||||
use crate::core::{AnyViewState, MessageContext, Mut, View, ViewElement, ViewMarker};
|
||||
use crate::{AnyWidgetView, ViewCtx, WindowOptions};
|
||||
|
||||
pub(crate) struct WindowView<State> {
|
||||
|
@ -55,7 +55,7 @@ where
|
|||
prev: &Self,
|
||||
root_widget_view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
(window, render_root): xilem_core::Mut<'_, Self::Element>,
|
||||
(window, render_root): Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
self.options.rebuild(&prev.options, window);
|
||||
|
@ -68,7 +68,7 @@ where
|
|||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut ViewCtx,
|
||||
(_, render_root): xilem_core::Mut<'_, Self::Element>,
|
||||
(_, render_root): Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
render_root.edit_root_widget(|mut root| {
|
||||
|
@ -80,12 +80,14 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[xilem_core::ViewId],
|
||||
message: xilem_core::DynMessage,
|
||||
message: &mut MessageContext,
|
||||
(_, render_root): Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> xilem_core::MessageResult<()> {
|
||||
self.root_widget_view
|
||||
.message(view_state, id_path, message, app_state)
|
||||
render_root.edit_root_widget(|mut root| {
|
||||
self.root_widget_view
|
||||
.message(view_state, message, root.downcast(), app_state)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,10 +52,7 @@ The current proposal would split the application into two processes:
|
|||
|
||||
## `no_std` support
|
||||
|
||||
Xilem Core supports running with `#![no_std]`, but does use [`alloc`][] to be available.
|
||||
|
||||
It is plausible that this reactivity pattern could be used without allocation being required, but that is not provided by this package.
|
||||
If you wish to use Xilem Core in environments where an allocator is not available, feel free to bring this up on [Zulip][Zulip].
|
||||
Xilem Core supports running with `#![no_std]`, but does require [`alloc`][] to be available.
|
||||
|
||||
[Xilem]: https://crates.io/crates/xilem
|
||||
[Xilem Web]: https://crates.io/crates/xilem_web
|
||||
|
|
|
@ -7,8 +7,8 @@ use std::io::stdin;
|
|||
use std::path::PathBuf;
|
||||
|
||||
use xilem_core::{
|
||||
AnyElement, AnyView, Environment, Mut, SuperElement, View, ViewElement, ViewId, ViewMarker,
|
||||
ViewPathTracker,
|
||||
AnyElement, AnyView, Environment, MessageContext, Mut, SuperElement, View, ViewElement, ViewId,
|
||||
ViewMarker, ViewPathTracker,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -200,8 +200,8 @@ impl<State, Action> View<State, Action, ViewCtx> for File {
|
|||
fn message(
|
||||
&self,
|
||||
_view_state: &mut Self::ViewState,
|
||||
_id_path: &[ViewId],
|
||||
_message: xilem_core::DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
_app_state: &mut State,
|
||||
) -> xilem_core::MessageResult<Action> {
|
||||
unreachable!()
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
use core::any::Any;
|
||||
|
||||
use xilem_core::{
|
||||
DynMessage, Environment, MessageResult, Mut, SuperElement, View, ViewElement, ViewId,
|
||||
Environment, MessageContext, MessageResult, Mut, SuperElement, View, ViewElement, ViewId,
|
||||
ViewMarker, ViewPathTracker,
|
||||
};
|
||||
|
||||
|
@ -92,8 +92,8 @@ impl<State, Action> View<State, Action, ViewCtx> for Button {
|
|||
fn message(
|
||||
&self,
|
||||
_view_state: &mut Self::ViewState,
|
||||
_id_path: &[ViewId],
|
||||
_message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Nop
|
||||
|
|
|
@ -7,7 +7,7 @@ use alloc::boxed::Box;
|
|||
use core::any::Any;
|
||||
|
||||
use crate::{
|
||||
AnyElement, DynMessage, MessageResult, Mut, View, ViewElement, ViewId, ViewMarker,
|
||||
AnyElement, MessageContext, MessageResult, Mut, View, ViewElement, ViewId, ViewMarker,
|
||||
ViewPathTracker,
|
||||
};
|
||||
|
||||
|
@ -58,8 +58,8 @@ pub trait AnyView<State, Action, Context, Element: ViewElement> {
|
|||
fn dyn_message(
|
||||
&self,
|
||||
dyn_state: &mut AnyViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Element::Mut<'_>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action>;
|
||||
}
|
||||
|
@ -153,22 +153,26 @@ where
|
|||
fn dyn_message(
|
||||
&self,
|
||||
dyn_state: &mut AnyViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: DynamicElement::Mut<'_>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let state = dyn_state
|
||||
.inner_state
|
||||
.downcast_mut()
|
||||
.expect("build or rebuild always set the correct corresponding state type");
|
||||
let Some((first, remainder)) = id_path.split_first() else {
|
||||
let Some(first) = message.take_first() else {
|
||||
// TODO: More info here (i.e. debug print message).
|
||||
unreachable!("Parent view of `AnyView` sent outdated and/or incorrect empty view path");
|
||||
};
|
||||
if first.routing_id() != dyn_state.generation {
|
||||
// Do we want to log something here?
|
||||
return MessageResult::Stale(message);
|
||||
return MessageResult::Stale;
|
||||
}
|
||||
self.message(state, remainder, message, app_state)
|
||||
DynamicElement::with_downcast_val(element, |element| {
|
||||
self.message(state, message, element, app_state)
|
||||
})
|
||||
.1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,11 +227,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.dyn_message(view_state, id_path, message, app_state)
|
||||
self.dyn_message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,11 +282,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.dyn_message(view_state, id_path, message, app_state)
|
||||
self.dyn_message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,11 +335,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.dyn_message(view_state, id_path, message, app_state)
|
||||
self.dyn_message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,10 +388,10 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.dyn_message(view_state, id_path, message, app_state)
|
||||
self.dyn_message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
// Copyright 2025 the Xilem Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{DynMessage, Environment, ViewId};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use anymore::AnyDebug;
|
||||
|
||||
/// The `MessageContext` is used in [`View::message`](crate::View::message).
|
||||
///
|
||||
/// It contains the full current "target" path for message routing, along with
|
||||
/// where we are along that path.
|
||||
/// Additionally, it also provides access to the current [`Environment`],
|
||||
/// allowing the resources for the current view tree location to be accessed.
|
||||
// TODO: Is it OK for this debug to be load bearing? It probably shouldn't be a derive.
|
||||
#[derive(Debug)]
|
||||
pub struct MessageContext {
|
||||
// TODO: Just plain pub?
|
||||
pub(crate) environment: Environment,
|
||||
full_id_path: Vec<ViewId>,
|
||||
id_path_index: usize,
|
||||
message: Option<DynMessage>,
|
||||
}
|
||||
|
||||
impl MessageContext {
|
||||
/// Remove the first element from the id path which this message needs to be routed to.
|
||||
///
|
||||
/// This mirrors [`ViewPathTracker::with_id`](crate::ViewPathTracker::with_id).
|
||||
/// Returns `None` if there are no more elements in the id path (for views
|
||||
/// which follow the usual patterns in that case the calling view would be
|
||||
/// the target view).
|
||||
pub fn take_first(&mut self) -> Option<ViewId> {
|
||||
let ret = self.full_id_path.get(self.id_path_index)?;
|
||||
self.id_path_index += 1;
|
||||
Some(*ret)
|
||||
}
|
||||
|
||||
/// The remaining id path, which should mostly be handled by your children.
|
||||
///
|
||||
/// If this returns an empty slice, then `take_first` will return `None`.
|
||||
pub fn remaining_path(&self) -> &[ViewId] {
|
||||
&self.full_id_path[self.id_path_index..]
|
||||
}
|
||||
|
||||
/// The id path to this view from the root.
|
||||
pub fn current_path(&self) -> &[ViewId] {
|
||||
&self.full_id_path[..self.id_path_index]
|
||||
}
|
||||
|
||||
/// Take the message, downcasting it to the specified type.
|
||||
///
|
||||
/// If the message is not of the specified type, returns `None`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If the message has already been taken.
|
||||
/// - If the message is not fully routed (i.e. the remaining path is not empty)
|
||||
#[track_caller]
|
||||
pub fn take_message<T: AnyDebug>(&mut self) -> Option<Box<T>> {
|
||||
self.maybe_take_message(|_| true)
|
||||
}
|
||||
|
||||
/// Downcast the message to the specified type, taking it if `f` returns true.
|
||||
///
|
||||
/// If the message is not of the specified type, returns `None`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If the message has already been taken.
|
||||
/// - If the message is not fully routed (i.e. the remaining path is not empty)
|
||||
#[track_caller]
|
||||
pub fn maybe_take_message<T: AnyDebug>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&T) -> bool,
|
||||
) -> Option<Box<T>> {
|
||||
debug_assert_eq!(
|
||||
self.full_id_path.len(),
|
||||
self.id_path_index,
|
||||
"Can't take a message that has not reached its target"
|
||||
);
|
||||
if let Some(message) = self.message.take() {
|
||||
if message.is::<T>() {
|
||||
let message = message.downcast().unwrap();
|
||||
if f(&*message) {
|
||||
return Some(message);
|
||||
} else {
|
||||
self.message = Some(DynMessage(message));
|
||||
}
|
||||
} else {
|
||||
self.message = Some(message);
|
||||
}
|
||||
None
|
||||
} else {
|
||||
panic!("The message has already been taken.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the message, or returns `None` if it's already been taken.
|
||||
///
|
||||
/// This method is an escape hatch for [`take_message`](Self::take_message)
|
||||
/// and [`maybe_take_message`](Self::maybe_take_message).
|
||||
/// Almost all views should use those methods instead.
|
||||
#[track_caller]
|
||||
pub fn force_take_message<T: AnyDebug>(&mut self) -> Option<DynMessage> {
|
||||
self.message.take()
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods used by implementations of the Xilem pattern, not directly by View implementations.
|
||||
impl MessageContext {
|
||||
/// Create a new message context.
|
||||
///
|
||||
/// End-users of Xilem do not need to use this function.
|
||||
///
|
||||
/// For driver implementers, the provided environment should your app's global environment.
|
||||
/// This can be recovered by [`finish`](Self::finish).
|
||||
pub fn new(environment: Environment, target_id_path: Vec<ViewId>, message: DynMessage) -> Self {
|
||||
Self {
|
||||
environment,
|
||||
full_id_path: target_id_path,
|
||||
id_path_index: 0,
|
||||
message: Some(message),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap this `MessageContext` into its constituent parts.
|
||||
pub fn finish(self) -> (Environment, Vec<ViewId>, Option<DynMessage>) {
|
||||
let Self {
|
||||
environment,
|
||||
full_id_path,
|
||||
message,
|
||||
..
|
||||
} = self;
|
||||
(environment, full_id_path, message)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::{DynMessage, Environment, MessageContext, ViewId};
|
||||
|
||||
#[test]
|
||||
fn take_path_full_path() {
|
||||
let env = Environment::new();
|
||||
let path = [0, 4, 3, 2, 1, 0]
|
||||
.into_iter()
|
||||
.map(ViewId::new)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut ctx = MessageContext::new(env, path.clone(), DynMessage::new(()));
|
||||
for element in &path {
|
||||
let next = ctx.take_first().unwrap();
|
||||
assert_eq!(next, *element);
|
||||
|
||||
assert!(path.starts_with(ctx.current_path()));
|
||||
assert!(path.ends_with(ctx.remaining_path()));
|
||||
assert_eq!(
|
||||
path.len(),
|
||||
ctx.current_path().len() + ctx.remaining_path().len()
|
||||
);
|
||||
assert_eq!(*ctx.current_path().last().unwrap(), next);
|
||||
}
|
||||
assert!(ctx.take_first().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
not(debug_assertions),
|
||||
ignore = "This test doesn't work without debug assertions (i.e. in release mode)"
|
||||
)]
|
||||
#[should_panic(expected = "Can't take a message that has not reached its target")]
|
||||
fn take_message_nonempty_path() {
|
||||
let env = Environment::new();
|
||||
let path = vec![ViewId::new(1)];
|
||||
|
||||
let mut ctx = MessageContext::new(env, path.clone(), DynMessage::new(()));
|
||||
ctx.take_message::<()>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_message_wrong_type() {
|
||||
let env = Environment::new();
|
||||
let path = vec![];
|
||||
|
||||
let mut ctx = MessageContext::new(env, path.clone(), DynMessage::new(()));
|
||||
let took = ctx.take_message::<u32>();
|
||||
assert!(took.is_none());
|
||||
let () = *ctx.take_message::<()>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The message has already been taken.")]
|
||||
fn take_message_twice() {
|
||||
let env = Environment::new();
|
||||
let path = vec![];
|
||||
|
||||
let mut ctx = MessageContext::new(env, path.clone(), DynMessage::new(()));
|
||||
let () = *ctx.take_message::<()>().unwrap();
|
||||
ctx.take_message::<()>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn maybe_take_message() {
|
||||
let env = Environment::new();
|
||||
let path = vec![];
|
||||
|
||||
let mut ctx = MessageContext::new(env, path.clone(), DynMessage::new(10_u32));
|
||||
ctx.maybe_take_message::<u32>(|x| {
|
||||
assert_eq!(*x, 10);
|
||||
false
|
||||
});
|
||||
let ret = ctx.take_message::<u32>().unwrap();
|
||||
assert_eq!(*ret, 10);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
use anymore::AnyDebug;
|
||||
use hashbrown::{HashMap, hash_map::Entry};
|
||||
|
||||
use crate::{MessageResult, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{MessageContext, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use core::{any::TypeId, marker::PhantomData};
|
||||
|
@ -250,7 +250,7 @@ where
|
|||
prev: &Self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut Ctx,
|
||||
element: crate::Mut<'_, Self::Element>,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
// Use our value in the child rebuild.
|
||||
|
@ -283,7 +283,7 @@ where
|
|||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut Ctx,
|
||||
element: crate::Mut<'_, Self::Element>,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
// Make our value available in the child teardown.
|
||||
|
@ -311,14 +311,33 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: crate::DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> crate::MessageResult<Action> {
|
||||
// Use our value in the child message.
|
||||
let slot =
|
||||
&mut message.environment.slots[usize::try_from(view_state.environment_slot).unwrap()];
|
||||
debug_assert!(
|
||||
view_state.this_state.is_some(),
|
||||
"`Provides` should be providing something."
|
||||
);
|
||||
core::mem::swap(&mut slot.item, &mut view_state.this_state);
|
||||
|
||||
// TODO: Any need for a message directly to this view?
|
||||
// TODO: When the context/environment is available in messages, add the context value here.
|
||||
self.child
|
||||
.message(&mut view_state.child_state, id_path, message, app_state)
|
||||
let ret = self
|
||||
.child
|
||||
.message(&mut view_state.child_state, message, element, app_state);
|
||||
|
||||
let slot =
|
||||
&mut message.environment.slots[usize::try_from(view_state.environment_slot).unwrap()];
|
||||
core::mem::swap(&mut slot.item, &mut view_state.this_state);
|
||||
debug_assert!(
|
||||
view_state.this_state.is_some(),
|
||||
"`Provides` should get its value back."
|
||||
);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,7 +500,7 @@ where
|
|||
_: &Self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut Ctx,
|
||||
element: crate::Mut<'_, Self::Element>,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
ctx.with_id(WITH_CONTEXT_CHILD, |ctx| {
|
||||
|
@ -516,7 +535,7 @@ where
|
|||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
ctx: &mut Ctx,
|
||||
element: crate::Mut<'_, Self::Element>,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
if let Some(_listener_idx) = view_state.listener_index {
|
||||
|
@ -535,28 +554,26 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: crate::DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> crate::MessageResult<Action> {
|
||||
let Some((first, rest)) = id_path.split_first() else {
|
||||
match message.downcast::<Rebuild>() {
|
||||
Ok(_) => return MessageResult::RequestRebuild,
|
||||
Err(message) => {
|
||||
let Some(first) = message.take_first() else {
|
||||
match message.take_message::<Rebuild>() {
|
||||
Some(_) => return MessageResult::RequestRebuild,
|
||||
None => {
|
||||
tracing::warn!("Expected `Rebuild` in WithContext::Message, got {message:?}");
|
||||
return MessageResult::Stale(message);
|
||||
return MessageResult::Stale;
|
||||
}
|
||||
}
|
||||
};
|
||||
debug_assert_eq!(
|
||||
*first, WITH_CONTEXT_CHILD,
|
||||
first, WITH_CONTEXT_CHILD,
|
||||
"Message should have been routed properly."
|
||||
);
|
||||
|
||||
// TODO: Any need for a message directly to this view?
|
||||
// TODO: When the context/environment is available in messages, add the context value here.
|
||||
view_state
|
||||
.prev
|
||||
.message(&mut view_state.child_state, rest, message, app_state)
|
||||
.message(&mut view_state.child_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,7 @@
|
|||
//!
|
||||
//! ## `no_std` support
|
||||
//!
|
||||
//! Xilem Core supports running with `#![no_std]`, but does use [`alloc`][] to be available.
|
||||
//!
|
||||
//! It is plausible that this reactivity pattern could be used without allocation being required, but that is not provided by this package.
|
||||
//! If you wish to use Xilem Core in environments where an allocator is not available, feel free to bring this up on [Zulip][Zulip].
|
||||
//! Xilem Core supports running with `#![no_std]`, but does require [`alloc`][] to be available.
|
||||
//!
|
||||
//! [Xilem]: https://crates.io/crates/xilem
|
||||
//! [Xilem Web]: https://crates.io/crates/xilem_web
|
||||
|
@ -50,12 +47,11 @@
|
|||
#![expect(clippy::allow_attributes_without_reason, reason = "Deferred: Noisy")]
|
||||
extern crate alloc;
|
||||
|
||||
// Used only for ad-hoc debugging of tests
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
pub use anymore;
|
||||
|
||||
mod context;
|
||||
pub use context::MessageContext;
|
||||
|
||||
mod deferred;
|
||||
pub use deferred::{AsyncCtx, MessageProxy, PhantomView, ProxyError, RawProxy};
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ pub enum MessageResult<Action> {
|
|||
/// does not require the element tree to be recreated.
|
||||
Nop,
|
||||
/// The view this message was being routed to no longer exists.
|
||||
Stale(DynMessage),
|
||||
Stale,
|
||||
}
|
||||
|
||||
impl<A> MessageResult<A> {
|
||||
|
@ -36,7 +36,7 @@ impl<A> MessageResult<A> {
|
|||
match self {
|
||||
Self::Action(a) => MessageResult::Action(f(a)),
|
||||
Self::RequestRebuild => MessageResult::RequestRebuild,
|
||||
Self::Stale(message) => MessageResult::Stale(message),
|
||||
Self::Stale => MessageResult::Stale,
|
||||
Self::Nop => MessageResult::Nop,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ use core::sync::atomic::{AtomicBool, Ordering};
|
|||
|
||||
use crate::element::NoElement;
|
||||
use crate::{
|
||||
DynMessage, MessageResult, SuperElement, View, ViewElement, ViewId, ViewMarker, ViewPathTracker,
|
||||
MessageContext, MessageResult, SuperElement, View, ViewElement, ViewId, ViewMarker,
|
||||
ViewPathTracker,
|
||||
};
|
||||
|
||||
/// An append only `Vec`.
|
||||
|
@ -35,6 +36,12 @@ impl<T> AppendVec<T> {
|
|||
pub fn drain(&mut self) -> Drain<'_, T> {
|
||||
self.inner.drain(..)
|
||||
}
|
||||
/// Equivalent to [`ElementSplice::index`].
|
||||
pub fn index(&self) -> usize {
|
||||
// If there are no items, to get here we need to skip 0
|
||||
// if there is one, we need to skip 1
|
||||
self.inner.len()
|
||||
}
|
||||
/// Returns `true` if the vector contains no elements.
|
||||
///
|
||||
/// See [`Vec::is_empty`] for more details
|
||||
|
@ -57,6 +64,8 @@ impl<T> Default for AppendVec<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// --- MARK: Traits
|
||||
|
||||
/// Views for ordered sequences of elements.
|
||||
///
|
||||
/// Generally, a container view will internally contain a `ViewSequence`.
|
||||
|
@ -77,7 +86,6 @@ impl<T> Default for AppendVec<T> {
|
|||
/// - An [`array`] of `ViewSequence` values.
|
||||
/// - Tuples of `ViewSequences` with up to 15 elements.
|
||||
/// These can be nested if an ad-hoc sequence of more than 15 sequences is needed.
|
||||
///
|
||||
pub trait ViewSequence<State, Action, Context, Element>: 'static
|
||||
where
|
||||
Context: ViewPathTracker,
|
||||
|
@ -88,6 +96,11 @@ where
|
|||
/// to incorrect views.
|
||||
/// - To pass on the state of child sequences, or a child View's [`ViewState`].
|
||||
///
|
||||
/// The type used for this associated type cannot be treated as public API; this is
|
||||
/// internal state to the `ViewSequence` implementation.
|
||||
/// That is, `ViewSequence` implementations are permitted to change the type they use for this
|
||||
/// during even a patch release of their crate.
|
||||
///
|
||||
/// [`ViewState`]: View::ViewState
|
||||
type SeqState;
|
||||
|
||||
|
@ -121,13 +134,30 @@ where
|
|||
|
||||
/// Propagate a message.
|
||||
///
|
||||
/// Handle a message, propagating to elements if needed. Here, `id_path` is a slice
|
||||
/// of ids, where the first item identifiers a child element of this sequence, if necessary.
|
||||
/// Handle a message, propagating to child views if needed.
|
||||
/// The context contains both the path of this view, and the remaining
|
||||
/// path that the rest of the implementation needs to go along.
|
||||
/// The first items in the remaining part of thtis path will be those added
|
||||
/// in build and/or rebuild.
|
||||
///
|
||||
/// The provided `elements` must be at the same [`index`](ElementSplice::index)
|
||||
/// it was at when `rebuild` (or `build`) was last called on this sequence.
|
||||
///
|
||||
/// The easiest way to achieve this is to cache the index reached before any child
|
||||
/// sequence's build/rebuild, and skip to that value.
|
||||
/// Note that the amount you will need to skip to reach this value won't be the index
|
||||
/// directly, but instead must be the difference between this index and the value of
|
||||
/// the index at the start of your build/rebuild.
|
||||
// Potential optimisation: Sequence implementations can be grouped into three classes:
|
||||
// 1) Statically known size (e.g. a single element, a tuple with only statically known size)
|
||||
// 2) Linear time known size (e.g. a tuple of linear or better known size, a vec of statically known size, an option)
|
||||
// 3) Dynamically known size (e.g. a vec)
|
||||
// For case 1 and maybe case 2, we don't need to store the indices, and could instead rebuild them dynamically.
|
||||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action>;
|
||||
}
|
||||
|
@ -146,10 +176,19 @@ pub trait ElementSplice<Element: ViewElement> {
|
|||
fn mutate<R>(&mut self, f: impl FnOnce(Element::Mut<'_>) -> R) -> R;
|
||||
/// Don't make any changes to the next n existing elements.
|
||||
fn skip(&mut self, n: usize);
|
||||
/// How many elements you would need to [`skip`](ElementSplice::skip) from when this
|
||||
/// `ElementSplice` was created to get to the current element.
|
||||
///
|
||||
/// Note that in using this function, previous views will have skipped.
|
||||
/// Values obtained from this method may change during any `rebuild`, but will not change
|
||||
/// between `build`/`rebuild` and the next `message`
|
||||
fn index(&self) -> usize;
|
||||
/// Delete the next existing element, after running a function on it.
|
||||
fn delete<R>(&mut self, f: impl FnOnce(Element::Mut<'_>) -> R) -> R;
|
||||
}
|
||||
|
||||
// --- MARK: For V: View
|
||||
|
||||
impl<State, Action, Context, V, Element> ViewSequence<State, Action, Context, Element> for V
|
||||
where
|
||||
Context: ViewPathTracker,
|
||||
|
@ -201,14 +240,21 @@ where
|
|||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.message(seq_state, id_path, message, app_state)
|
||||
elements.mutate(|this_element| {
|
||||
Element::with_downcast_val(this_element, |element| {
|
||||
self.message(seq_state, message, element, app_state)
|
||||
})
|
||||
.1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// --- MARK: for Option<Seq>
|
||||
|
||||
/// The state used to implement `ViewSequence` for `Option<impl ViewSequence>`
|
||||
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
|
||||
#[derive(Debug)]
|
||||
|
@ -343,16 +389,17 @@ where
|
|||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let (start, rest) = id_path
|
||||
.split_first()
|
||||
let start = message
|
||||
.take_first()
|
||||
.expect("Id path has elements for Option<ViewSequence>");
|
||||
|
||||
if start.routing_id() != seq_state.generation {
|
||||
// The message was sent to a previous edition of the inner value
|
||||
return MessageResult::Stale(message);
|
||||
return MessageResult::Stale;
|
||||
}
|
||||
assert_eq!(
|
||||
self.is_some(),
|
||||
|
@ -360,14 +407,16 @@ where
|
|||
"Inconsistent ViewSequence state. Perhaps the parent is mixing up children"
|
||||
);
|
||||
if let Some((seq, inner_state)) = self.as_ref().zip(seq_state.inner.as_mut()) {
|
||||
seq.seq_message(inner_state, rest, message, app_state)
|
||||
seq.seq_message(inner_state, message, elements, app_state)
|
||||
} else {
|
||||
// TODO: this should be unreachable as the generation was increased on the falling edge
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- MARK: for Vec<Seq>
|
||||
|
||||
/// The state used to implement `ViewSequence` for `Vec<impl ViewSequence>`
|
||||
///
|
||||
/// We use a generation arena for vector types, with half of the `ViewId` dedicated
|
||||
|
@ -378,7 +427,11 @@ where
|
|||
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
|
||||
#[derive(Debug)]
|
||||
pub struct VecViewState<InnerState> {
|
||||
inner_states: Vec<InnerState>,
|
||||
// We use two vectors here because the `inner_states` is the
|
||||
// same length as the actual vector, whereas the generations
|
||||
// is the same length as the longest version of this we have seen.
|
||||
/// The fields of the tuple are (number of widgets to skip, `InnerState`).
|
||||
inner_states: Vec<(usize, InnerState)>,
|
||||
|
||||
generations: Vec<u32>,
|
||||
}
|
||||
|
@ -428,6 +481,7 @@ where
|
|||
elements: &mut AppendVec<Element>,
|
||||
app_state: &mut State,
|
||||
) -> Self::SeqState {
|
||||
let start_idx = elements.index();
|
||||
let generations = alloc::vec![0; self.len()];
|
||||
let inner_states = self
|
||||
.iter()
|
||||
|
@ -435,7 +489,9 @@ where
|
|||
.zip(&generations)
|
||||
.map(|((index, seq), generation)| {
|
||||
let id = create_generational_view_id(index, *generation);
|
||||
ctx.with_id(id, |ctx| seq.seq_build(ctx, elements, app_state))
|
||||
let this_skip = elements.index() - start_idx;
|
||||
let inner_state = ctx.with_id(id, |ctx| seq.seq_build(ctx, elements, app_state));
|
||||
(this_skip, inner_state)
|
||||
})
|
||||
.collect();
|
||||
VecViewState {
|
||||
|
@ -453,13 +509,15 @@ where
|
|||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
for (i, (((child, child_prev), child_state), child_generation)) in self
|
||||
let start_idx = elements.index();
|
||||
for (i, (((child, child_prev), (child_skip, child_state)), child_generation)) in self
|
||||
.iter()
|
||||
.zip(prev)
|
||||
.zip(&mut seq_state.inner_states)
|
||||
.zip(&seq_state.generations)
|
||||
.enumerate()
|
||||
{
|
||||
*child_skip = elements.index() - start_idx;
|
||||
// Rebuild the items which are common to both vectors
|
||||
let id = create_generational_view_id(i, *child_generation);
|
||||
ctx.with_id(id, |ctx| {
|
||||
|
@ -475,7 +533,7 @@ where
|
|||
let generations = seq_state.generations[n..].iter_mut();
|
||||
// But remove the old states
|
||||
let states = seq_state.inner_states.drain(n..);
|
||||
for (index, ((old_seq, generation), mut inner_state)) in
|
||||
for (index, ((old_seq, generation), (_, mut inner_state))) in
|
||||
to_teardown.zip(generations).zip(states).enumerate()
|
||||
{
|
||||
let id = create_generational_view_id(index + n, *generation);
|
||||
|
@ -501,9 +559,6 @@ where
|
|||
// We believe this to be superfluous for the default use case, as even with 1000 rebuilds a second, each adding
|
||||
// to the same array, this would take 50 days of the application running continuously.
|
||||
// See also https://github.com/bevyengine/bevy/pull/9907, where they warn in their equivalent case
|
||||
// Note that we have a slightly different strategy to Bevy, where we use a global generation
|
||||
// This theoretically allows some of the memory in `seq_state` to be reclaimed, at the cost of making overflow
|
||||
// more likely here. Note that we don't actually reclaim this memory at the moment.
|
||||
|
||||
// We use 0 to wrap around. It would require extremely unfortunate timing to get an async event
|
||||
// with the correct generation exactly u32::MAX generations late, so wrapping is the best option
|
||||
|
@ -513,6 +568,7 @@ where
|
|||
} else if n > prev_n {
|
||||
// If needed, create new generations
|
||||
seq_state.generations.resize(n, 0);
|
||||
let outer_idx = elements.index();
|
||||
elements.with_scratch(|elements| {
|
||||
seq_state.inner_states.extend(
|
||||
self[prev_n..]
|
||||
|
@ -521,7 +577,10 @@ where
|
|||
.enumerate()
|
||||
.map(|(index, (seq, generation))| {
|
||||
let id = create_generational_view_id(index + prev_n, *generation);
|
||||
ctx.with_id(id, |ctx| seq.seq_build(ctx, elements, app_state))
|
||||
let this_skip = elements.index() + outer_idx - start_idx;
|
||||
let inner_state =
|
||||
ctx.with_id(id, |ctx| seq.seq_build(ctx, elements, app_state));
|
||||
(this_skip, inner_state)
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
@ -536,7 +595,7 @@ where
|
|||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
for (index, ((seq, state), generation)) in self
|
||||
for (index, ((seq, (_, state)), generation)) in self
|
||||
.iter()
|
||||
.zip(&mut seq_state.inner_states)
|
||||
.zip(&seq_state.generations)
|
||||
|
@ -551,25 +610,29 @@ where
|
|||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let (start, rest) = id_path
|
||||
.split_first()
|
||||
let start = message
|
||||
.take_first()
|
||||
.expect("Id path has elements for Vec<ViewSequence>");
|
||||
let (index, generation) = view_id_to_index_generation(*start);
|
||||
let (index, generation) = view_id_to_index_generation(start);
|
||||
let stored_generation = &seq_state.generations[index];
|
||||
if *stored_generation != generation {
|
||||
// The value in the sequence i
|
||||
return MessageResult::Stale(message);
|
||||
return MessageResult::Stale;
|
||||
}
|
||||
// Panics if index is out of bounds, but we know it isn't because this is the same generation
|
||||
let inner_state = &mut seq_state.inner_states[index];
|
||||
self[index].seq_message(inner_state, rest, message, app_state)
|
||||
let (child_skip, inner_state) = &mut seq_state.inner_states[index];
|
||||
|
||||
elements.skip(*child_skip);
|
||||
self[index].seq_message(inner_state, message, elements, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
// --- MARK: for [Seq; N]
|
||||
|
||||
impl<State, Action, Context, Element, Seq, const N: usize>
|
||||
ViewSequence<State, Action, Context, Element> for [Seq; N]
|
||||
where
|
||||
|
@ -577,7 +640,8 @@ where
|
|||
Context: ViewPathTracker,
|
||||
Element: ViewElement,
|
||||
{
|
||||
type SeqState = [Seq::SeqState; N];
|
||||
/// The fields of the tuple are (number of widgets to skip, child `SeqState`).
|
||||
type SeqState = [(usize, Seq::SeqState); N];
|
||||
|
||||
#[doc(hidden)]
|
||||
fn seq_build(
|
||||
|
@ -586,14 +650,16 @@ where
|
|||
elements: &mut AppendVec<Element>,
|
||||
app_state: &mut State,
|
||||
) -> Self::SeqState {
|
||||
let start_idx = elements.index();
|
||||
// there's no enumerate directly on an array
|
||||
let mut idx = 0;
|
||||
self.each_ref().map(|vs| {
|
||||
let this_skip = elements.index() - start_idx;
|
||||
let state = ctx.with_id(ViewId::new(idx), |ctx| {
|
||||
vs.seq_build(ctx, elements, app_state)
|
||||
});
|
||||
idx += 1;
|
||||
state
|
||||
(this_skip, state)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -606,7 +672,11 @@ where
|
|||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
for (idx, ((seq, prev_seq), state)) in self.iter().zip(prev).zip(seq_state).enumerate() {
|
||||
let start_idx = elements.index();
|
||||
for (idx, ((seq, prev_seq), (this_skip, state))) in
|
||||
self.iter().zip(prev).zip(seq_state).enumerate()
|
||||
{
|
||||
*this_skip = elements.index() - start_idx;
|
||||
ctx.with_id(
|
||||
ViewId::new(idx.try_into().expect(
|
||||
"ViewSequence arrays with more than u64::MAX + 1 elements not supported",
|
||||
|
@ -622,18 +692,19 @@ where
|
|||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let (start, rest) = id_path
|
||||
.split_first()
|
||||
let start = message
|
||||
.take_first()
|
||||
.expect("Id path has elements for [ViewSequence; N]");
|
||||
|
||||
let index: usize = start.routing_id().try_into().unwrap();
|
||||
// We know the index is in bounds because it was created from an index into a value of Self
|
||||
let inner_state = &mut seq_state[index];
|
||||
self[index].seq_message(inner_state, rest, message, app_state)
|
||||
let (this_skip, inner_state) = &mut seq_state[index];
|
||||
elements.skip(*this_skip);
|
||||
self[index].seq_message(inner_state, message, elements, app_state)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -644,7 +715,7 @@ where
|
|||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
for (idx, (seq, state)) in self.iter().zip(seq_state).enumerate() {
|
||||
for (idx, (seq, (_, state))) in self.iter().zip(seq_state).enumerate() {
|
||||
ctx.with_id(
|
||||
ViewId::new(idx.try_into().expect(
|
||||
"ViewSequence arrays with more than u64::MAX + 1 elements not supported",
|
||||
|
@ -657,6 +728,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// --- MARK: for ()
|
||||
|
||||
impl<State, Action, Context, Element> ViewSequence<State, Action, Context, Element> for ()
|
||||
where
|
||||
Context: ViewPathTracker,
|
||||
|
@ -694,15 +767,16 @@ where
|
|||
fn seq_message(
|
||||
&self,
|
||||
_: &mut Self::SeqState,
|
||||
_: &[ViewId],
|
||||
_message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_: &mut impl ElementSplice<Element>,
|
||||
_: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
unreachable!("Messages should never be dispatched to an empty tuple");
|
||||
// TODO add Debug trait bound because of this?: , got {message:?}
|
||||
unreachable!("Messages should never be dispatched to an empty tuple {message:?}.");
|
||||
}
|
||||
}
|
||||
|
||||
// --- MARK: for (Seq,)
|
||||
|
||||
impl<State, Action, Context, Element, Seq> ViewSequence<State, Action, Context, Element> for (Seq,)
|
||||
where
|
||||
Seq: ViewSequence<State, Action, Context, Element>,
|
||||
|
@ -745,14 +819,16 @@ where
|
|||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.0.seq_message(seq_state, id_path, message, app_state)
|
||||
self.0.seq_message(seq_state, message, elements, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
// --- MARK: for (Seq, ...)
|
||||
|
||||
macro_rules! impl_view_tuple {
|
||||
(
|
||||
// We could use the ${index} metavariable here once it's stable
|
||||
|
@ -768,7 +844,8 @@ macro_rules! impl_view_tuple {
|
|||
> ViewSequence<State, Action, Context, Element> for ($($seq,)+)
|
||||
|
||||
{
|
||||
type SeqState = ($($seq::SeqState,)+);
|
||||
/// The fields of the inner tuples are (number of widgets to skip, child state).
|
||||
type SeqState = ($((usize, $seq::SeqState),)+);
|
||||
|
||||
fn seq_build(
|
||||
&self,
|
||||
|
@ -776,9 +853,12 @@ macro_rules! impl_view_tuple {
|
|||
elements: &mut AppendVec<Element>,
|
||||
app_state: &mut State,
|
||||
) -> Self::SeqState {
|
||||
let start_idx = elements.index();
|
||||
($(
|
||||
ctx.with_id(ViewId::new($idx), |ctx| {
|
||||
self.$idx.seq_build(ctx, elements, app_state)
|
||||
let this_skip = elements.index() - start_idx;
|
||||
let state = self.$idx.seq_build(ctx, elements, app_state);
|
||||
(this_skip, state)
|
||||
}),
|
||||
)+)
|
||||
}
|
||||
|
@ -791,9 +871,11 @@ macro_rules! impl_view_tuple {
|
|||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) {
|
||||
let start_idx = elements.index();
|
||||
$(
|
||||
ctx.with_id(ViewId::new($idx), |ctx| {
|
||||
self.$idx.seq_rebuild(&prev.$idx, &mut seq_state.$idx, ctx, elements, app_state);
|
||||
seq_state.$idx.0 = elements.index() - start_idx;
|
||||
self.$idx.seq_rebuild(&prev.$idx, &mut seq_state.$idx.1, ctx, elements, app_state);
|
||||
});
|
||||
)+
|
||||
}
|
||||
|
@ -807,7 +889,7 @@ macro_rules! impl_view_tuple {
|
|||
) {
|
||||
$(
|
||||
ctx.with_id(ViewId::new($idx), |ctx| {
|
||||
self.$idx.seq_teardown(&mut seq_state.$idx, ctx, elements, app_state)
|
||||
self.$idx.seq_teardown(&mut seq_state.$idx.1, ctx, elements, app_state)
|
||||
});
|
||||
)+
|
||||
}
|
||||
|
@ -815,20 +897,23 @@ macro_rules! impl_view_tuple {
|
|||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let (start, rest) = id_path
|
||||
.split_first()
|
||||
let start = message
|
||||
.take_first()
|
||||
.expect("Id path has elements for tuple");
|
||||
match start.routing_id() {
|
||||
$(
|
||||
$idx => self.$idx.seq_message(&mut seq_state.$idx, rest, message, app_state),
|
||||
$idx => {
|
||||
elements.skip(seq_state.$idx.0);
|
||||
self.$idx.seq_message(&mut seq_state.$idx.1, message, elements, app_state)
|
||||
},
|
||||
)+
|
||||
// If we have received a message, our parent is (mostly) certain that we requested it
|
||||
// The only time that wouldn't be the case is when a generational index has overflowed?
|
||||
_ => unreachable!("Unexpected id path {start:?} in tuple (wants to be routed via {rest:?})"),
|
||||
_ => unreachable!("Unexpected id path {start:?} in tuple (wants to be routed via {message:?})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -852,10 +937,12 @@ impl_view_tuple!(M0, Seq0, 0; M1, Seq1, 1; M2, Seq2, 2; M3, Seq3, 3; M4, Seq4, 4
|
|||
impl_view_tuple!(M0, Seq0, 0; M1, Seq1, 1; M2, Seq2, 2; M3, Seq3, 3; M4, Seq4, 4; M5, Seq5, 5; M6, Seq6, 6; M7, Seq7, 7; M8, Seq8, 8; M9, Seq9, 9; M10, Seq10, 10; M11, Seq11, 11; M12, Seq12, 12; M13, Seq13, 13; M14, Seq14, 14);
|
||||
impl_view_tuple!(M0, Seq0, 0; M1, Seq1, 1; M2, Seq2, 2; M3, Seq3, 3; M4, Seq4, 4; M5, Seq5, 5; M6, Seq6, 6; M7, Seq7, 7; M8, Seq8, 8; M9, Seq9, 9; M10, Seq10, 10; M11, Seq11, 11; M12, Seq12, 12; M13, Seq13, 13; M14, Seq14, 14; M15, Seq15, 15);
|
||||
|
||||
// --- MARK: NoElements
|
||||
|
||||
/// A stub `ElementSplice` implementation for `NoElement`.
|
||||
///
|
||||
/// It is technically possible for someone to create an implementation of `ViewSequence`
|
||||
/// which uses a `NoElement` `ElementSplice`. But we don't think that sequence could be meaningful.
|
||||
/// which uses a (different) `NoElement` `ElementSplice`. But we don't think that sequence could be meaningful.
|
||||
pub(crate) struct NoElements;
|
||||
|
||||
impl ElementSplice<NoElement> for NoElements {
|
||||
|
@ -872,6 +959,10 @@ impl ElementSplice<NoElement> for NoElements {
|
|||
|
||||
fn skip(&mut self, _: usize) {}
|
||||
|
||||
fn index(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn delete<R>(&mut self, f: impl FnOnce(<NoElement as crate::ViewElement>::Mut<'_>) -> R) -> R {
|
||||
f(())
|
||||
}
|
||||
|
@ -966,10 +1057,11 @@ where
|
|||
fn seq_message(
|
||||
&self,
|
||||
seq_state: &mut Self::SeqState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_elements: &mut impl ElementSplice<Element>,
|
||||
app_state: &mut State,
|
||||
) -> crate::MessageResult<Action> {
|
||||
self.seq.seq_message(seq_state, id_path, message, app_state)
|
||||
self.seq
|
||||
.seq_message(seq_state, message, &mut NoElements, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use core::ops::Deref;
|
|||
|
||||
use crate::environment::Environment;
|
||||
use crate::message::MessageResult;
|
||||
use crate::{DynMessage, Mut, ViewElement};
|
||||
use crate::{MessageContext, Mut, ViewElement};
|
||||
|
||||
/// A type which can be a [`View`]. Imposes no requirements on the underlying type.
|
||||
/// Should be implemented alongside every `View` implementation:
|
||||
|
@ -56,11 +56,6 @@ pub trait ViewMarker {}
|
|||
/// impl<...> ViewMarker for Button<...> {}
|
||||
/// impl<...> View<...> for Button<...> {...}
|
||||
/// ```
|
||||
///
|
||||
/// ## Alloc
|
||||
///
|
||||
/// As it uses owned dynamically typed messages ([`DynMessage`]), this trait requires an
|
||||
/// allocator to be available.
|
||||
pub trait View<State, Action, Context: ViewPathTracker>: ViewMarker + 'static {
|
||||
/// The element type which this view operates on.
|
||||
type Element: ViewElement;
|
||||
|
@ -69,6 +64,11 @@ pub trait View<State, Action, Context: ViewPathTracker>: ViewMarker + 'static {
|
|||
/// This often means routing information for messages to child views or view sequences,
|
||||
/// to avoid sending outdated views.
|
||||
/// This is also used in [`memoize`](crate::memoize) to store the previously constructed view.
|
||||
///
|
||||
/// The type used for this associated type cannot be treated as public API; this is
|
||||
/// internal state to the `View` implementation.
|
||||
/// That is, `View` implementations are permitted to change the type they use for this
|
||||
/// during even a patch release of their crate.
|
||||
type ViewState;
|
||||
|
||||
/// Create the corresponding Element value.
|
||||
|
@ -103,8 +103,8 @@ pub trait View<State, Action, Context: ViewPathTracker>: ViewMarker + 'static {
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action>;
|
||||
|
||||
|
@ -205,12 +205,12 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.deref()
|
||||
.message(view_state, id_path, message, app_state)
|
||||
.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,13 +275,13 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let message_result =
|
||||
self.deref()
|
||||
.message(&mut view_state.view_state, id_path, message, app_state);
|
||||
.message(&mut view_state.view_state, message, element, app_state);
|
||||
if matches!(message_result, MessageResult::RequestRebuild) {
|
||||
view_state.dirty = true;
|
||||
}
|
||||
|
@ -339,13 +339,13 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let message_result =
|
||||
self.deref()
|
||||
.message(&mut view_state.view_state, id_path, message, app_state);
|
||||
.message(&mut view_state.view_state, message, element, app_state);
|
||||
if matches!(message_result, MessageResult::RequestRebuild) {
|
||||
view_state.dirty = true;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
AppendVec, DynMessage, Mut, NoElement, View, ViewId, ViewMarker, ViewPathTracker, ViewSequence,
|
||||
sequence::NoElements,
|
||||
AppendVec, MessageContext, Mut, NoElement, View, ViewId, ViewMarker, ViewPathTracker,
|
||||
ViewSequence, sequence::NoElements,
|
||||
};
|
||||
|
||||
/// Create a view which acts as `active_view`, whilst also running `alongside_view`, without inserting it into the tree.
|
||||
|
@ -92,20 +92,21 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
(active_state, alongside_state): &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> crate::MessageResult<Action> {
|
||||
let (first, id_path) = id_path
|
||||
.split_first()
|
||||
.expect("Id path has elements for Fork");
|
||||
let first = message.take_first().expect("Id path has elements for Fork");
|
||||
match first.routing_id() {
|
||||
0 => self
|
||||
.active_view
|
||||
.message(active_state, id_path, message, app_state),
|
||||
1 => self
|
||||
.alongside_view
|
||||
.seq_message(alongside_state, id_path, message, app_state),
|
||||
.message(active_state, message, element, app_state),
|
||||
1 => self.alongside_view.seq_message(
|
||||
alongside_state,
|
||||
message,
|
||||
&mut NoElements,
|
||||
app_state,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use core::any::type_name;
|
|||
use core::fmt::Debug;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{DynMessage, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{MessageContext, MessageResult, Mut, View, ViewMarker, ViewPathTracker};
|
||||
|
||||
/// The View for [`lens`].
|
||||
///
|
||||
|
@ -144,14 +144,14 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
(child, child_view_state): &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut ParentState,
|
||||
) -> MessageResult<Action> {
|
||||
child.message(
|
||||
child_view_state,
|
||||
id_path,
|
||||
message,
|
||||
element,
|
||||
(self.access_state)(app_state),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use core::fmt::Debug;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{DynMessage, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{MessageContext, MessageResult, Mut, View, ViewMarker, ViewPathTracker};
|
||||
|
||||
/// View type for [`map_message`] and [`map_action`]. Most users will want to use `map_action` (the latter).
|
||||
///
|
||||
|
@ -160,11 +160,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<ParentAction> {
|
||||
let child_result = self.child.message(view_state, id_path, message, app_state);
|
||||
let child_result = self.child.message(view_state, message, element, app_state);
|
||||
(self.map_fn)(app_state, child_result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use core::fmt::Debug;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{DynMessage, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{MessageContext, MessageResult, Mut, View, ViewMarker, ViewPathTracker};
|
||||
|
||||
/// The View for [`map_state`].
|
||||
///
|
||||
|
@ -129,11 +129,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut ParentState,
|
||||
) -> MessageResult<Action> {
|
||||
self.child
|
||||
.message(view_state, id_path, message, (self.map_state)(app_state))
|
||||
.message(view_state, message, element, (self.map_state)(app_state))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use core::fmt::Debug;
|
|||
use core::marker::PhantomData;
|
||||
use core::mem::size_of;
|
||||
|
||||
use crate::{DynMessage, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{MessageContext, MessageResult, Mut, View, ViewMarker, ViewPathTracker};
|
||||
|
||||
/// A view which supports Memoization.
|
||||
///
|
||||
|
@ -134,14 +134,14 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let message_result =
|
||||
view_state
|
||||
.view
|
||||
.message(&mut view_state.view_state, id_path, message, app_state);
|
||||
.message(&mut view_state.view_state, message, element, app_state);
|
||||
if matches!(message_result, MessageResult::RequestRebuild) {
|
||||
view_state.dirty = true;
|
||||
}
|
||||
|
@ -265,14 +265,14 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let message_result =
|
||||
view_state
|
||||
.view
|
||||
.message(&mut view_state.view_state, id_path, message, app_state);
|
||||
.message(&mut view_state.view_state, message, element, app_state);
|
||||
if matches!(message_result, MessageResult::RequestRebuild) {
|
||||
view_state.dirty = true;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
use hidden::OneOfState;
|
||||
|
||||
use crate::{
|
||||
DynMessage, MessageResult, Mut, View, ViewElement, ViewId, ViewMarker, ViewPathTracker,
|
||||
MessageContext, MessageResult, Mut, View, ViewElement, ViewId, ViewMarker, ViewPathTracker,
|
||||
};
|
||||
|
||||
/// This trait allows, specifying a type as `ViewElement`, which should never be constructed or used.
|
||||
|
@ -135,39 +135,66 @@ pub trait OneOfCtx<
|
|||
|
||||
/// Casts the view element `elem` to the `OneOf::A` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_a(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, A>));
|
||||
fn with_downcast_a<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, A>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::B` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_b(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, B>));
|
||||
fn with_downcast_b<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, B>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::C` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_c(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, C>));
|
||||
fn with_downcast_c<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, C>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::D` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_d(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, D>));
|
||||
fn with_downcast_d<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, D>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::E` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_e(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, E>));
|
||||
fn with_downcast_e<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, E>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::F` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_f(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, F>));
|
||||
fn with_downcast_f<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, F>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::G` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_g(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, G>));
|
||||
fn with_downcast_g<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, G>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::H` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_h(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, H>));
|
||||
fn with_downcast_h<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, H>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Casts the view element `elem` to the `OneOf::I` variant.
|
||||
/// `f` needs to be invoked with that inner `ViewElement`
|
||||
fn with_downcast_i(elem: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, I>));
|
||||
fn with_downcast_i<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, I>) -> R,
|
||||
) -> R;
|
||||
|
||||
/// Creates the wrapping element, this is used in `View::build` to wrap the inner view element variant
|
||||
fn upcast_one_of_element(
|
||||
|
@ -526,26 +553,44 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
mut element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let (start, rest) = id_path
|
||||
.split_first()
|
||||
let start = message
|
||||
.take_first()
|
||||
.expect("Id path has elements for OneOf");
|
||||
if start.routing_id() != view_state.generation {
|
||||
return MessageResult::Stale(message);
|
||||
return MessageResult::Stale;
|
||||
}
|
||||
match (self, &mut view_state.inner_state) {
|
||||
(Self::A(v), OneOf::A(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::B(v), OneOf::B(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::C(v), OneOf::C(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::D(v), OneOf::D(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::E(v), OneOf::E(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::F(v), OneOf::F(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::G(v), OneOf::G(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::H(v), OneOf::H(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::I(v), OneOf::I(state)) => v.message(state, rest, message, app_state),
|
||||
(Self::A(v), OneOf::A(state)) => Context::with_downcast_a(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::B(v), OneOf::B(state)) => Context::with_downcast_b(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::C(v), OneOf::C(state)) => Context::with_downcast_c(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::D(v), OneOf::D(state)) => Context::with_downcast_d(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::E(v), OneOf::E(state)) => Context::with_downcast_e(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::F(v), OneOf::F(state)) => Context::with_downcast_f(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::G(v), OneOf::G(state)) => Context::with_downcast_g(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::H(v), OneOf::H(state)) => Context::with_downcast_h(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
(Self::I(v), OneOf::I(state)) => Context::with_downcast_i(&mut element, |element| {
|
||||
v.message(state, message, element, app_state)
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -556,7 +601,7 @@ where
|
|||
#[doc(hidden)]
|
||||
mod hidden {
|
||||
use super::PhantomElementCtx;
|
||||
use crate::{DynMessage, View, ViewMarker};
|
||||
use crate::{MessageContext, Mut, View, ViewMarker};
|
||||
|
||||
#[allow(unnameable_types)] // reason: Implementation detail, public because of trait visibility rules
|
||||
#[derive(Debug)]
|
||||
|
@ -596,8 +641,8 @@ mod hidden {
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
_: &[crate::ViewId],
|
||||
_: DynMessage,
|
||||
_: &mut MessageContext,
|
||||
_: Mut<'_, Self::Element>,
|
||||
_: &mut State,
|
||||
) -> crate::MessageResult<Action> {
|
||||
match *self {}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright 2024 the Xilem Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
DynMessage, MessageResult, Mut, View, ViewElement, ViewId, ViewMarker, ViewPathTracker,
|
||||
};
|
||||
use crate::{MessageContext, MessageResult, Mut, View, ViewElement, ViewMarker, ViewPathTracker};
|
||||
|
||||
/// This trait provides a way to add [`View`] implementations for types that would be restricted otherwise by the orphan rules.
|
||||
///
|
||||
|
@ -44,8 +42,8 @@ pub trait OrphanView<V, State, Action>: ViewPathTracker + Sized {
|
|||
fn orphan_message(
|
||||
view: &V,
|
||||
view_state: &mut Self::OrphanViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::OrphanElement>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action>;
|
||||
}
|
||||
|
@ -94,11 +92,11 @@ macro_rules! impl_orphan_view_for {
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
Context::orphan_message(self, view_state, id_path, message, app_state)
|
||||
Context::orphan_message(self, view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -128,7 +126,7 @@ impl_orphan_view_for!(usize);
|
|||
/// These [`OrphanView`] implementations can e.g. be used in a vector graphics context, as for example seen in `xilem_web` within svg nodes
|
||||
mod kurbo {
|
||||
use super::OrphanView;
|
||||
use crate::{DynMessage, MessageResult, Mut, View, ViewId, ViewMarker};
|
||||
use crate::{MessageContext, MessageResult, Mut, View, ViewMarker};
|
||||
impl_orphan_view_for!(kurbo::PathSeg);
|
||||
impl_orphan_view_for!(kurbo::Arc);
|
||||
impl_orphan_view_for!(kurbo::BezPath);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use crate::{DynMessage, MessageResult, NoElement, View, ViewMarker, ViewPathTracker};
|
||||
use crate::{MessageContext, MessageResult, Mut, NoElement, View, ViewMarker, ViewPathTracker};
|
||||
|
||||
/// A view which executes `once` exactly once.
|
||||
///
|
||||
|
@ -103,7 +103,7 @@ where
|
|||
_: &Self,
|
||||
(): &mut Self::ViewState,
|
||||
_: &mut Context,
|
||||
(): crate::Mut<'_, Self::Element>,
|
||||
(): Mut<'_, Self::Element>,
|
||||
_: &mut State,
|
||||
) {
|
||||
// Nothing to do
|
||||
|
@ -113,7 +113,7 @@ where
|
|||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
_: &mut Context,
|
||||
_: crate::Mut<'_, Self::Element>,
|
||||
_: Mut<'_, Self::Element>,
|
||||
_: &mut State,
|
||||
) {
|
||||
// Nothing to do
|
||||
|
@ -122,11 +122,10 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
(): &mut Self::ViewState,
|
||||
_: &[crate::ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_: Mut<'_, Self::Element>,
|
||||
_: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
// Nothing to do
|
||||
panic!("Message should not have been sent to a `RunOnce` View: {message:?}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,13 @@ type AnyNoopView = dyn AnyView<(), Action, TestCtx, TestElement>;
|
|||
fn messages_to_inner_view() {
|
||||
let view: Box<AnyNoopView> = Box::new(OperationView::<0>(0));
|
||||
let mut ctx = TestCtx::default();
|
||||
let (element, mut state) = view.build(&mut ctx, &mut ());
|
||||
let (mut element, mut state) = view.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
assert_eq!(element.operations, &[Operation::Build(0)]);
|
||||
|
||||
let result = view.message(&mut state, &element.view_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
ctx.with_message_context(element.view_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -38,8 +39,10 @@ fn message_after_rebuild() {
|
|||
&[Operation::Build(0), Operation::Rebuild { from: 0, to: 1 }]
|
||||
);
|
||||
|
||||
let result = view2.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 1);
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -62,8 +65,10 @@ fn no_message_after_stale() {
|
|||
]
|
||||
);
|
||||
|
||||
let result = view2.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -100,6 +105,8 @@ fn no_message_after_stale_then_same_type() {
|
|||
]
|
||||
);
|
||||
|
||||
let result = view3.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view3.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -99,12 +99,14 @@ fn arc_passthrough_teardown() {
|
|||
fn arc_passthrough_message() {
|
||||
let view1 = Arc::new(record_ops(0));
|
||||
let mut ctx = TestCtx::default();
|
||||
let (element, mut state) = view1.build(&mut ctx, &mut ());
|
||||
let (mut element, mut state) = view1.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
assert_eq!(element.operations, &[Operation::Build(0)]);
|
||||
|
||||
let result = view1.message(&mut state, &element.view_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
ctx.with_message_context(element.view_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view1.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
}
|
||||
|
||||
// --- MARK: Box tests
|
||||
|
@ -174,13 +176,15 @@ fn box_passthrough_teardown() {
|
|||
fn box_passthrough_message() {
|
||||
let view1 = Box::new(record_ops(0));
|
||||
let mut ctx = TestCtx::default();
|
||||
let (element, mut state) = view1.build(&mut ctx, &mut ());
|
||||
let (mut element, mut state) = view1.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
assert_eq!(element.operations, &[Operation::Build(0)]);
|
||||
|
||||
let result = view1.message(&mut state, &element.view_path, DynMessage::new(()), &mut ());
|
||||
let MessageResult::Action(inner) = result else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(inner.id, 0);
|
||||
ctx.with_message_context(element.view_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view1.message(&mut state, ctx, &mut element, &mut ());
|
||||
let MessageResult::Action(inner) = result else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(inner.id, 0);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ fn two_element_passthrough() {
|
|||
fn two_element_message() {
|
||||
let view = sequence(2, [record_ops(0), record_ops(1)]);
|
||||
let mut ctx = TestCtx::default();
|
||||
let (element, mut state) = view.build(&mut ctx, &mut ());
|
||||
let (mut element, mut state) = view.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
assert_eq!(element.operations, &[Operation::Build(2)]);
|
||||
assert_eq!(element.view_path, &[]);
|
||||
|
@ -110,9 +110,13 @@ fn two_element_message() {
|
|||
assert_eq!(second_child.operations, &[Operation::Build(1)]);
|
||||
let second_path = second_child.view_path.to_vec();
|
||||
|
||||
let result = view.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
ctx.with_message_context(first_path, DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
|
||||
let result = view.message(&mut state, &second_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 1);
|
||||
ctx.with_message_context(second_path, DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -51,9 +51,11 @@ fn one_element_sequence_passthrough() {
|
|||
&[Operation::Build(0), Operation::Rebuild { from: 0, to: 2 }]
|
||||
);
|
||||
|
||||
let result = view2.message(&mut state, &[], DynMessage::new(()), &mut ());
|
||||
// The message should have been routed to the only child
|
||||
assert_action(result, 2);
|
||||
ctx.with_message_context(Vec::new(), DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
// The message should have been routed to the only child
|
||||
assert_action(result, 2);
|
||||
});
|
||||
|
||||
view2.teardown(&mut state, &mut ctx, &mut element, &mut ());
|
||||
assert_eq!(
|
||||
|
@ -286,7 +288,7 @@ fn option_some_none() {
|
|||
fn option_message_some() {
|
||||
let view = sequence(1, Some(record_ops(0)));
|
||||
let mut ctx = TestCtx::default();
|
||||
let (element, mut state) = view.build(&mut ctx, &mut ());
|
||||
let (mut element, mut state) = view.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
|
||||
let seq_children = element.children.as_ref().unwrap();
|
||||
|
@ -294,8 +296,10 @@ fn option_message_some() {
|
|||
let child = seq_children.active.first().unwrap();
|
||||
let path = child.view_path.to_vec();
|
||||
|
||||
let result = view.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -313,8 +317,10 @@ fn option_message_some_some() {
|
|||
let view2 = sequence(0, Some(record_ops(1)));
|
||||
view2.rebuild(&view, &mut state, &mut ctx, &mut element, &mut ());
|
||||
|
||||
let result = view2.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 1);
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -332,8 +338,10 @@ fn option_message_some_none_stale() {
|
|||
let view2 = sequence(0, None);
|
||||
view2.rebuild(&view, &mut state, &mut ctx, &mut element, &mut ());
|
||||
|
||||
let result = view2.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -354,6 +362,8 @@ fn option_message_some_none_some_stale() {
|
|||
let view3 = sequence(0, Some(record_ops(1)));
|
||||
view3.rebuild(&view2, &mut state, &mut ctx, &mut element, &mut ());
|
||||
|
||||
let result = view2.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -38,6 +38,16 @@ impl TestCtx {
|
|||
"Views should always match push_ids and pop_ids"
|
||||
);
|
||||
}
|
||||
pub(super) fn with_message_context(
|
||||
&mut self,
|
||||
target_id_path: Vec<ViewId>,
|
||||
message: DynMessage,
|
||||
f: impl FnOnce(&mut MessageContext),
|
||||
) {
|
||||
let mut ctx = MessageContext::new(std::mem::take(&mut self.1), target_id_path, message);
|
||||
f(&mut ctx);
|
||||
self.1 = ctx.finish().0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
|
@ -154,12 +164,17 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut (),
|
||||
) -> MessageResult<Action> {
|
||||
let mut elements = SeqTracker {
|
||||
inner: element.children.as_mut().unwrap(),
|
||||
ix: 0,
|
||||
scratch: &mut view_state.1,
|
||||
};
|
||||
self.seq
|
||||
.seq_message(&mut view_state.0, id_path, message, app_state)
|
||||
.seq_message(&mut view_state.0, message, &mut elements, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,8 +224,8 @@ impl<const N: u32> View<(), Action, TestCtx> for OperationView<N> {
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
_: &[ViewId],
|
||||
_: DynMessage,
|
||||
_: &mut MessageContext,
|
||||
_: Mut<'_, Self::Element>,
|
||||
_: &mut (),
|
||||
) -> MessageResult<Action> {
|
||||
// If we get an `Action` value, we know it came from here
|
||||
|
@ -258,6 +273,7 @@ impl AnyElement<Self, TestCtx> for TestElement {
|
|||
#[derive(Clone)]
|
||||
pub(super) struct SeqChildren {
|
||||
pub(super) active: Vec<TestElement>,
|
||||
// The index in the original sequence, and the element.
|
||||
pub(super) deleted: Vec<(usize, TestElement)>,
|
||||
}
|
||||
|
||||
|
@ -294,6 +310,9 @@ impl ElementSplice<TestElement> for SeqTracker<'_> {
|
|||
fn skip(&mut self, n: usize) {
|
||||
self.ix += n;
|
||||
}
|
||||
fn index(&self) -> usize {
|
||||
self.ix
|
||||
}
|
||||
fn delete<R>(&mut self, f: impl FnOnce(Mut<'_, TestElement>) -> R) -> R {
|
||||
let ret = f(&mut self.inner.active[self.ix]);
|
||||
let val = self.inner.active.remove(self.ix);
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2024 the Xilem Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Tests for [`SequenceView`] with vectors.
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
use xilem_core::View;
|
||||
|
||||
fn record_ops(id: u32) -> OperationView<0> {
|
||||
OperationView(id)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_in_tuple() {
|
||||
let view = sequence(3, (record_ops(0), vec![record_ops(1), record_ops(2)]));
|
||||
let mut ctx = TestCtx::default();
|
||||
let (mut element, mut state) = view.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
assert_eq!(element.operations, &[Operation::Build(3)]);
|
||||
assert_eq!(element.view_path, &[]);
|
||||
|
||||
// Children after build have expected shape
|
||||
let seq_children = element.children.as_ref().unwrap();
|
||||
assert!(seq_children.deleted.is_empty());
|
||||
assert_eq!(seq_children.active.len(), 3);
|
||||
|
||||
// The tuple child
|
||||
let child = &seq_children.active[0];
|
||||
assert_eq!(child.operations, &[Operation::Build(0)]);
|
||||
assert_eq!(child.view_path.len(), 1);
|
||||
|
||||
// The Vec children
|
||||
let child = &seq_children.active[1];
|
||||
assert_eq!(child.operations, &[Operation::Build(1)]);
|
||||
// (Tuple then vec)
|
||||
assert_eq!(child.view_path.len(), 2);
|
||||
let child = &seq_children.active[2];
|
||||
assert_eq!(child.operations, &[Operation::Build(2)]);
|
||||
assert_eq!(child.view_path.len(), 2);
|
||||
|
||||
// Rebuild
|
||||
let view2 = sequence(5, (record_ops(4), vec![]));
|
||||
view2.rebuild(&view, &mut state, &mut ctx, &mut element, &mut ());
|
||||
ctx.assert_empty();
|
||||
|
||||
assert_eq!(
|
||||
element.operations,
|
||||
&[Operation::Build(3), Operation::Rebuild { from: 3, to: 5 }]
|
||||
);
|
||||
|
||||
let seq_children = element.children.as_ref().unwrap();
|
||||
// Teardowns from deleted elements in Vec
|
||||
assert_eq!(seq_children.deleted.len(), 2);
|
||||
|
||||
let (child_idx, child) = &seq_children.deleted[0];
|
||||
assert_eq!(*child_idx, 1);
|
||||
assert_eq!(child.view_path.len(), 2);
|
||||
assert_eq!(
|
||||
child.operations,
|
||||
&[Operation::Build(1), Operation::Teardown(1)]
|
||||
);
|
||||
let (child_idx, child) = &seq_children.deleted[1];
|
||||
// N.B. we delete the item with the index we see it with (i.e. after the previous item has been deleted)
|
||||
assert_eq!(*child_idx, 1);
|
||||
assert_eq!(child.view_path.len(), 2);
|
||||
assert_eq!(
|
||||
child.operations,
|
||||
&[Operation::Build(2), Operation::Teardown(2)]
|
||||
);
|
||||
|
||||
assert_eq!(seq_children.active.len(), 1);
|
||||
|
||||
let child = &seq_children.active[0];
|
||||
assert_eq!(
|
||||
child.operations,
|
||||
&[Operation::Build(0), Operation::Rebuild { from: 0, to: 4 }]
|
||||
);
|
||||
assert_eq!(child.view_path.len(), 1);
|
||||
}
|
|
@ -89,67 +89,67 @@ impl
|
|||
}
|
||||
}
|
||||
|
||||
fn with_downcast_a(
|
||||
fn with_downcast_a<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, TestElement>),
|
||||
) {
|
||||
f(elem);
|
||||
f: impl FnOnce(Mut<'_, TestElement>) -> R,
|
||||
) -> R {
|
||||
f(elem)
|
||||
}
|
||||
|
||||
fn with_downcast_b(
|
||||
fn with_downcast_b<R>(
|
||||
elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, TestElement>),
|
||||
) {
|
||||
f(elem);
|
||||
f: impl FnOnce(Mut<'_, TestElement>) -> R,
|
||||
) -> R {
|
||||
f(elem)
|
||||
}
|
||||
|
||||
// when one of the following would be invoked, it would be an error in the impl of `OneOfN`
|
||||
fn with_downcast_c(
|
||||
fn with_downcast_c<R>(
|
||||
_elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>),
|
||||
) {
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>) -> R,
|
||||
) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn with_downcast_d(
|
||||
fn with_downcast_d<R>(
|
||||
_elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>),
|
||||
) {
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>) -> R,
|
||||
) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn with_downcast_e(
|
||||
fn with_downcast_e<R>(
|
||||
_elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>),
|
||||
) {
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>) -> R,
|
||||
) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn with_downcast_f(
|
||||
fn with_downcast_f<R>(
|
||||
_elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>),
|
||||
) {
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>) -> R,
|
||||
) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn with_downcast_g(
|
||||
fn with_downcast_g<R>(
|
||||
_elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>),
|
||||
) {
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>) -> R,
|
||||
) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn with_downcast_h(
|
||||
fn with_downcast_h<R>(
|
||||
_elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>),
|
||||
) {
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>) -> R,
|
||||
) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn with_downcast_i(
|
||||
fn with_downcast_i<R>(
|
||||
_elem: &mut Mut<'_, Self::OneOfElement>,
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>),
|
||||
) {
|
||||
_f: impl FnOnce(Mut<'_, <Self as PhantomElementCtx>::PhantomElement>) -> R,
|
||||
) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -227,12 +227,14 @@ fn one_of_passthrough_teardown() {
|
|||
fn one_of_passthrough_message() {
|
||||
let view1: OneOf2<OperationView<0>, OperationView<1>> = OneOf2::A(record_ops_0(0));
|
||||
let mut ctx = TestCtx::default();
|
||||
let (element, mut state) = view1.build(&mut ctx, &mut ());
|
||||
let (mut element, mut state) = view1.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
assert_eq!(element.operations, &[Operation::Build(0)]);
|
||||
|
||||
let result = view1.message(&mut state, &element.view_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
ctx.with_message_context(element.view_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view1.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -255,8 +257,10 @@ fn one_of_no_message_after_stale() {
|
|||
]
|
||||
);
|
||||
|
||||
let result = view2.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -293,6 +297,8 @@ fn one_of_no_message_after_stale_then_same_type() {
|
|||
]
|
||||
);
|
||||
|
||||
let result = view3.message(&mut state, &path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(path, DynMessage::new(()), |ctx| {
|
||||
let result = view3.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#![expect(clippy::missing_assert_message, reason = "Deferred: Noisy")]
|
||||
|
||||
use xilem_core::{DynMessage, MessageResult, Mut, OrphanView, View, ViewId, ViewPathTracker};
|
||||
use xilem_core::{MessageContext, MessageResult, Mut, OrphanView, View, ViewPathTracker};
|
||||
|
||||
mod common;
|
||||
use common::*;
|
||||
|
@ -71,11 +71,11 @@ impl<State, Action> OrphanView<&'static str, State, Action> for TestCtx {
|
|||
fn orphan_message(
|
||||
_view: &&'static str,
|
||||
_view_state: &mut Self::OrphanViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::OrphanElement>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,9 +58,11 @@ fn one_element_passthrough() {
|
|||
&[Operation::Build(0), Operation::Rebuild { from: 0, to: 2 }]
|
||||
);
|
||||
|
||||
let result = view2.message(&mut state, &[], DynMessage::new(()), &mut ());
|
||||
// The message should have been routed to the only child
|
||||
assert_action(result, 2);
|
||||
ctx.with_message_context(Vec::new(), DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
// The message should have been routed to the only child
|
||||
assert_action(result, 2);
|
||||
});
|
||||
|
||||
view2.teardown(&mut state, &mut ctx, &mut element, &mut ());
|
||||
assert_eq!(
|
||||
|
@ -172,7 +174,7 @@ fn two_element_passthrough() {
|
|||
fn two_element_message() {
|
||||
let view = sequence(2, (record_ops(0), record_ops(1)));
|
||||
let mut ctx = TestCtx::default();
|
||||
let (element, mut state) = view.build(&mut ctx, &mut ());
|
||||
let (mut element, mut state) = view.build(&mut ctx, &mut ());
|
||||
ctx.assert_empty();
|
||||
assert_eq!(element.operations, &[Operation::Build(2)]);
|
||||
assert_eq!(element.view_path, &[]);
|
||||
|
@ -187,11 +189,14 @@ fn two_element_message() {
|
|||
assert_eq!(second_child.operations, &[Operation::Build(1)]);
|
||||
let second_path = second_child.view_path.to_vec();
|
||||
|
||||
let result = view.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
|
||||
let result = view.message(&mut state, &second_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 1);
|
||||
ctx.with_message_context(first_path, DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
ctx.with_message_context(second_path, DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 1);
|
||||
});
|
||||
}
|
||||
|
||||
// We don't test higher tuples, because these all use the same implementation
|
||||
|
|
|
@ -194,19 +194,28 @@ fn normal_messages() {
|
|||
let second_child = &seq_children.active[1];
|
||||
let second_path = second_child.view_path.to_vec();
|
||||
|
||||
let result = view.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
let result = view.message(&mut state, &second_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 1);
|
||||
ctx.with_message_context(first_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
ctx.with_message_context(second_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 1);
|
||||
});
|
||||
|
||||
let view2 = sequence(0, vec![record_ops(2), record_ops(3)]);
|
||||
view2.rebuild(&view, &mut state, &mut ctx, &mut element, &mut ());
|
||||
ctx.assert_empty();
|
||||
|
||||
let result = view2.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 2);
|
||||
let result = view2.message(&mut state, &second_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 3);
|
||||
ctx.with_message_context(first_path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
|
||||
assert_action(result, 2);
|
||||
});
|
||||
ctx.with_message_context(second_path, DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -223,20 +232,27 @@ fn stale_messages() {
|
|||
let first_child = seq_children.active.first().unwrap();
|
||||
let first_path = first_child.view_path.to_vec();
|
||||
|
||||
let result = view.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
assert_action(result, 0);
|
||||
ctx.with_message_context(first_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert_action(result, 0);
|
||||
});
|
||||
// let result = view.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
|
||||
let view2 = sequence(0, vec![]);
|
||||
view2.rebuild(&view, &mut state, &mut ctx, &mut element, &mut ());
|
||||
ctx.assert_empty();
|
||||
|
||||
let result = view2.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(first_path.clone(), DynMessage::new(()), |ctx| {
|
||||
let result = view2.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
|
||||
let view3 = sequence(0, vec![record_ops(1)]);
|
||||
view3.rebuild(&view2, &mut state, &mut ctx, &mut element, &mut ());
|
||||
ctx.assert_empty();
|
||||
|
||||
let result = view3.message(&mut state, &first_path, DynMessage::new(()), &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale(_)));
|
||||
ctx.with_message_context(first_path, DynMessage::new(()), |ctx| {
|
||||
let result = view3.message(&mut state, ctx, &mut element, &mut ());
|
||||
assert!(matches!(result, MessageResult::Stale));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::core::{MessageResult, Mut, View, ViewId, ViewMarker};
|
||||
use crate::{DomNode, DomView, DynMessage, ViewCtx};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewMarker};
|
||||
use crate::{DomNode, DomView, ViewCtx};
|
||||
|
||||
/// Invokes the `callback` after the inner `element` [`DomView`] was created.
|
||||
/// See [`after_build`] for more details.
|
||||
|
@ -148,12 +148,12 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.element
|
||||
.message(view_state, id_path, message, app_state)
|
||||
.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,12 +204,12 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.element
|
||||
.message(view_state, id_path, message, app_state)
|
||||
.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,11 +254,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.element
|
||||
.message(view_state, id_path, message, app_state)
|
||||
.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
|
||||
use crate::core::{AppendVec, MessageResult, ViewId};
|
||||
use crate::core::{AppendVec, MessageContext, MessageResult, ViewId};
|
||||
use crate::elements::DomChildrenSplice;
|
||||
use crate::{AnyPod, DomFragment, DynMessage, ViewCtx};
|
||||
|
||||
|
@ -123,17 +123,34 @@ where
|
|||
let mut inner_guard = self.0.borrow_mut();
|
||||
let inner = &mut *inner_guard;
|
||||
if let Some(fragment) = &mut inner.fragment {
|
||||
let message_result = fragment.seq_message(
|
||||
inner.fragment_state.as_mut().unwrap(),
|
||||
&message.id_path,
|
||||
message.body,
|
||||
&mut inner.data,
|
||||
);
|
||||
let message_result;
|
||||
{
|
||||
let env = std::mem::take(&mut inner.ctx.environment);
|
||||
let mut message_context =
|
||||
MessageContext::new(env, (*message.id_path).into(), message.body);
|
||||
let mut dom_children_splice = DomChildrenSplice::new(
|
||||
&mut inner.fragment_append_scratch,
|
||||
&mut inner.elements,
|
||||
&mut inner.vec_splice_scratch,
|
||||
&inner.root,
|
||||
inner.ctx.fragment.clone(),
|
||||
false,
|
||||
false,
|
||||
);
|
||||
message_result = fragment.seq_message(
|
||||
inner.fragment_state.as_mut().unwrap(),
|
||||
&mut message_context,
|
||||
&mut dom_children_splice,
|
||||
&mut inner.data,
|
||||
);
|
||||
let (env, _path, _message) = message_context.finish();
|
||||
inner.ctx.environment = env;
|
||||
}
|
||||
|
||||
// Each of those results are currently resulting in a rebuild, that may be subject to change
|
||||
match message_result {
|
||||
MessageResult::RequestRebuild | MessageResult::Nop | MessageResult::Action(_) => {}
|
||||
MessageResult::Stale(_) => {
|
||||
MessageResult::Stale => {
|
||||
// TODO perhaps inform the user that a stale request bubbled to the top?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use std::marker::PhantomData;
|
|||
use wasm_bindgen::closure::Closure;
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
|
||||
use crate::core::{MessageResult, Mut, NoElement, View, ViewId, ViewMarker};
|
||||
use crate::{DynMessage, OptionalAction, ViewCtx};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, NoElement, View, ViewMarker};
|
||||
use crate::{OptionalAction, ViewCtx};
|
||||
|
||||
/// Start an interval which invokes `callback` every `ms` milliseconds
|
||||
pub struct Interval<Callback, State, Action> {
|
||||
|
@ -140,12 +140,12 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
debug_assert!(id_path.is_empty());
|
||||
message.downcast::<()>().unwrap_throw();
|
||||
debug_assert!(message.remaining_path().is_empty());
|
||||
message.take_message::<()>().unwrap_throw();
|
||||
match (self.callback)(app_state).action() {
|
||||
Some(action) => MessageResult::Action(action),
|
||||
None => MessageResult::Nop,
|
||||
|
|
|
@ -8,8 +8,10 @@ use wasm_bindgen::closure::Closure;
|
|||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
use crate::core::{MessageResult, Mut, NoElement, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{DynMessage, OptionalAction, ViewCtx};
|
||||
use crate::core::{
|
||||
MessageContext, MessageResult, Mut, NoElement, View, ViewId, ViewMarker, ViewPathTracker,
|
||||
};
|
||||
use crate::{OptionalAction, ViewCtx};
|
||||
|
||||
/// Await a future returned by `init_future` invoked with the argument `data`, `callback` is called with the output of the future. `init_future` will be invoked again, when `data` changes. Use [`memoized_await`] for construction of this [`View`]
|
||||
pub struct MemoizedAwait<State, Action, OA, InitFuture, Data, Callback, F, FOut> {
|
||||
|
@ -245,13 +247,19 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
(): Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
assert_eq!(id_path.len(), 1);
|
||||
if id_path[0].routing_id() == view_state.generation {
|
||||
match *message.downcast().unwrap_throw() {
|
||||
assert_eq!(
|
||||
message.remaining_path().len(),
|
||||
1,
|
||||
"MemoizedAwait doesn't have children but controls a single path element, got {message:?}."
|
||||
);
|
||||
let my_id = message.take_first().unwrap();
|
||||
|
||||
if my_id.routing_id() == view_state.generation {
|
||||
match *message.take_message().unwrap_throw() {
|
||||
MemoizedAwaitMessage::Output(future_output) => {
|
||||
match (self.callback)(app_state, future_output).action() {
|
||||
Some(action) => MessageResult::Action(action),
|
||||
|
@ -265,7 +273,7 @@ where
|
|||
}
|
||||
}
|
||||
} else {
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ use futures::channel::oneshot;
|
|||
use wasm_bindgen::UnwrapThrowExt;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
use crate::ViewCtx;
|
||||
use crate::context::MessageThunk;
|
||||
use crate::core::anymore::AnyDebug;
|
||||
use crate::core::{MessageResult, Mut, NoElement, View, ViewId, ViewMarker};
|
||||
use crate::{DynMessage, ViewCtx};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, NoElement, View, ViewMarker};
|
||||
|
||||
/// Spawn an async task to update state asynchronously
|
||||
///
|
||||
|
@ -182,15 +182,15 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
_: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
debug_assert!(
|
||||
id_path.is_empty(),
|
||||
"id path should be empty in AsyncRepeat::message"
|
||||
message.remaining_path().is_empty(),
|
||||
"id path should be empty in AsyncRepeat::message, got {message:?}"
|
||||
);
|
||||
let message = message.downcast::<M>().unwrap();
|
||||
let message = message.take_message::<M>().unwrap();
|
||||
MessageResult::Action((self.on_event)(app_state, *message))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ pub struct ViewCtx {
|
|||
/// A stack containing modifier count size-hints for each element context, mostly to avoid unnecessary allocations.
|
||||
modifier_size_hints: Vec<VecMap<TypeId, usize>>,
|
||||
modifier_size_hint_stack_idx: usize,
|
||||
environment: Environment,
|
||||
pub(crate) environment: Environment,
|
||||
}
|
||||
|
||||
impl Default for ViewCtx {
|
||||
|
|
|
@ -10,12 +10,10 @@ use std::rc::Rc;
|
|||
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
|
||||
use crate::core::{AppendVec, ElementSplice, MessageResult, Mut, View, ViewId, ViewMarker};
|
||||
use crate::core::{AppendVec, ElementSplice, MessageContext, MessageResult, Mut, View, ViewMarker};
|
||||
use crate::modifiers::Children;
|
||||
use crate::vec_splice::VecSplice;
|
||||
use crate::{
|
||||
AnyPod, DomFragment, DomNode, DynMessage, FromWithContext, HTML_NS, Pod, ViewCtx, document,
|
||||
};
|
||||
use crate::{AnyPod, DomFragment, DomNode, FromWithContext, HTML_NS, Pod, ViewCtx, document};
|
||||
|
||||
// sealed, because this should only cover `ViewSequences` with the blanket impl below
|
||||
/// This is basically a specialized dynamically dispatchable [`ViewSequence`][crate::core::ViewSequence], It's currently not able to change the underlying type unlike [`AnyDomView`](crate::AnyDomView), so it should not be used as `dyn DomViewSequence`.
|
||||
|
@ -59,8 +57,8 @@ pub(crate) trait DomViewSequence<State, Action>: 'static {
|
|||
fn dyn_seq_message(
|
||||
&self,
|
||||
seq_state: &mut Box<dyn Any>,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
elements: &mut DomChildrenSplice<'_, '_, '_, '_>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action>;
|
||||
}
|
||||
|
@ -119,14 +117,14 @@ where
|
|||
fn dyn_seq_message(
|
||||
&self,
|
||||
seq_state: &mut Box<dyn Any>,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: &mut DomChildrenSplice<'_, '_, '_, '_>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.seq_message(
|
||||
seq_state.downcast_mut().unwrap_throw(),
|
||||
id_path,
|
||||
message,
|
||||
element,
|
||||
app_state,
|
||||
)
|
||||
}
|
||||
|
@ -218,6 +216,10 @@ impl ElementSplice<AnyPod> for DomChildrenSplice<'_, '_, '_, '_> {
|
|||
self.ix += n;
|
||||
}
|
||||
|
||||
fn index(&self) -> usize {
|
||||
self.ix
|
||||
}
|
||||
|
||||
fn delete<R>(&mut self, f: impl FnOnce(Mut<'_, AnyPod>) -> R) -> R {
|
||||
let mut child = self.children.delete_next();
|
||||
let child = child.as_mut(self.parent, true);
|
||||
|
@ -335,6 +337,39 @@ pub(crate) fn teardown_element<State, Action, Element>(
|
|||
);
|
||||
}
|
||||
|
||||
pub(crate) fn message_element<State, Action, Element>(
|
||||
children: &dyn DomViewSequence<State, Action>,
|
||||
element: Mut<'_, Pod<Element>>,
|
||||
state: &mut ElementState,
|
||||
ctx: &mut MessageContext,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action>
|
||||
where
|
||||
State: 'static,
|
||||
Action: 'static,
|
||||
Element: 'static,
|
||||
Element: DomNode<Props: AsMut<Children>>,
|
||||
{
|
||||
let mut dom_children_splice = DomChildrenSplice::new(
|
||||
&mut state.append_scratch,
|
||||
element.props.as_mut(),
|
||||
&mut state.vec_splice_scratch,
|
||||
element.node.as_ref(),
|
||||
// HACK: This will never be read, since we're handling a message rather than making any tree changes
|
||||
// As such, for now we just use a dummy value
|
||||
Rc::new(web_sys::DocumentFragment::new().unwrap()),
|
||||
true,
|
||||
// Hydration should only be used within `View::{build,rebuild}`, a message reaching this would indicate that the element is already build and hydrated, so this can safely be false.
|
||||
false,
|
||||
);
|
||||
children.dyn_seq_message(
|
||||
&mut state.seq_state,
|
||||
ctx,
|
||||
&mut dom_children_splice,
|
||||
app_state,
|
||||
)
|
||||
}
|
||||
|
||||
/// An element that can change its tag, it's useful for autonomous custom elements (i.e. web components)
|
||||
pub struct CustomElement<Children, State, Action> {
|
||||
name: Cow<'static, str>,
|
||||
|
@ -420,13 +455,12 @@ where
|
|||
|
||||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
element_state: &mut Self::ViewState,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.children
|
||||
.dyn_seq_message(&mut view_state.seq_state, id_path, message, app_state)
|
||||
message_element(&*self.children, element, element_state, message, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -502,17 +536,12 @@ macro_rules! define_element {
|
|||
|
||||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
element_state: &mut Self::ViewState,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.children.dyn_seq_message(
|
||||
&mut view_state.seq_state,
|
||||
id_path,
|
||||
message,
|
||||
app_state,
|
||||
)
|
||||
message_element(&*self.children, element, element_state, message, app_state)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -521,10 +550,10 @@ macro_rules! define_element {
|
|||
macro_rules! define_elements {
|
||||
($ns:ident, $($element_def:tt,)*) => {
|
||||
use std::marker::PhantomData;
|
||||
use super::{build_element, rebuild_element, teardown_element, DomViewSequence, ElementState};
|
||||
use super::{build_element, rebuild_element, teardown_element, message_element, DomViewSequence, ElementState};
|
||||
use crate::{
|
||||
core::{MessageResult, Mut, ViewId, ViewMarker},
|
||||
DomFragment, DynMessage, Pod, View, ViewCtx,
|
||||
core::{MessageResult, Mut, ViewMarker, MessageContext},
|
||||
DomFragment, Pod, View, ViewCtx,
|
||||
};
|
||||
$(define_element!(crate::$ns, $element_def);)*
|
||||
};
|
||||
|
|
|
@ -9,8 +9,8 @@ use wasm_bindgen::{JsCast, UnwrapThrowExt, throw_str};
|
|||
use web_sys::{AddEventListenerOptions, js_sys};
|
||||
|
||||
use crate::core::anymore::AnyDebug;
|
||||
use crate::core::{MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{DomView, DynMessage, OptionalAction, ViewCtx};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::{DomView, OptionalAction, ViewCtx};
|
||||
|
||||
/// Use a distinctive number here, to be able to catch bugs.
|
||||
/// In case the generational-id view path in `View::Message` lead to a wrong view
|
||||
|
@ -216,8 +216,8 @@ fn teardown_event_listener<State, Action, V>(
|
|||
fn message_event_listener<State, Action, V, Event, OA, Callback>(
|
||||
element_view: &V,
|
||||
state: &mut OnEventState<V::ViewState>,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, V::Element>,
|
||||
app_state: &mut State,
|
||||
handler: &Callback,
|
||||
) -> MessageResult<Action>
|
||||
|
@ -229,20 +229,20 @@ where
|
|||
OA: OptionalAction<Action>,
|
||||
Callback: Fn(&mut State, Event) -> OA + 'static,
|
||||
{
|
||||
let Some((first, remainder)) = id_path.split_first() else {
|
||||
let Some(first) = message.take_first() else {
|
||||
throw_str("Parent view of `OnEvent` sent outdated and/or incorrect empty view path");
|
||||
};
|
||||
if *first != ON_EVENT_VIEW_ID {
|
||||
if first != ON_EVENT_VIEW_ID {
|
||||
throw_str("Parent view of `OnEvent` sent outdated and/or incorrect empty view path");
|
||||
}
|
||||
if remainder.is_empty() {
|
||||
let event = message.downcast::<Event>().unwrap_throw();
|
||||
if message.remaining_path().is_empty() {
|
||||
let event = message.take_message::<Event>().unwrap_throw();
|
||||
match (handler)(app_state, *event).action() {
|
||||
Some(a) => MessageResult::Action(a),
|
||||
None => MessageResult::Nop,
|
||||
}
|
||||
} else {
|
||||
element_view.message(&mut state.child_state, remainder, message, app_state)
|
||||
element_view.message(&mut state.child_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,15 +338,15 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: crate::DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
message_event_listener(
|
||||
&self.dom_view,
|
||||
view_state,
|
||||
id_path,
|
||||
message,
|
||||
element,
|
||||
app_state,
|
||||
&self.handler,
|
||||
)
|
||||
|
@ -460,11 +460,11 @@ macro_rules! event_definitions {
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: crate::DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
message_event_listener(&self.dom_view, view_state, id_path, message, app_state, &self.handler)
|
||||
message_event_listener(&self.dom_view, view_state, message, element, app_state, &self.handler)
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
@ -647,19 +647,19 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let Some((first, remainder)) = id_path.split_first() else {
|
||||
let Some(first) = message.take_first() else {
|
||||
throw_str("Parent view of `OnResize` sent outdated and/or incorrect empty view path");
|
||||
};
|
||||
if *first != ON_EVENT_VIEW_ID {
|
||||
if first != ON_EVENT_VIEW_ID {
|
||||
throw_str("Parent view of `OnResize` sent outdated and/or incorrect empty view path");
|
||||
}
|
||||
if remainder.is_empty() {
|
||||
if message.remaining_path().is_empty() {
|
||||
let event = message
|
||||
.downcast::<web_sys::ResizeObserverEntry>()
|
||||
.take_message::<web_sys::ResizeObserverEntry>()
|
||||
.unwrap_throw();
|
||||
match (self.handler)(app_state, *event).action() {
|
||||
Some(a) => MessageResult::Action(a),
|
||||
|
@ -667,7 +667,7 @@ where
|
|||
}
|
||||
} else {
|
||||
self.dom_view
|
||||
.message(&mut view_state.child_state, remainder, message, app_state)
|
||||
.message(&mut view_state.child_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ use std::marker::PhantomData;
|
|||
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
|
||||
use crate::core::{MessageResult, Mut, View, ViewElement, ViewId, ViewMarker};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewElement, ViewMarker};
|
||||
use crate::modifiers::{Modifier, WithModifier};
|
||||
use crate::vecmap::VecMap;
|
||||
use crate::{AttributeValue, DomView, DynMessage, IntoAttributeValue, ViewCtx};
|
||||
use crate::{AttributeValue, DomView, IntoAttributeValue, ViewCtx};
|
||||
|
||||
type CowStr = std::borrow::Cow<'static, str>;
|
||||
|
||||
|
@ -349,10 +349,10 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.inner.message(view_state, id_path, message, app_state)
|
||||
self.inner.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ use std::marker::PhantomData;
|
|||
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
|
||||
use crate::core::{MessageResult, Mut, View, ViewElement, ViewId, ViewMarker};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewElement, ViewMarker};
|
||||
use crate::diff::{Diff, diff_iters};
|
||||
use crate::modifiers::{Modifier, WithModifier};
|
||||
use crate::vecmap::VecMap;
|
||||
use crate::{DomView, DynMessage, ViewCtx};
|
||||
use crate::{DomView, ViewCtx};
|
||||
|
||||
type CowStr = std::borrow::Cow<'static, str>;
|
||||
|
||||
|
@ -387,10 +387,10 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
(_, view_state): &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.el.message(view_state, id_path, message, app_state)
|
||||
self.el.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -240,11 +240,11 @@ macro_rules! overwrite_bool_modifier_view {
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[$crate::core::ViewId],
|
||||
message: $crate::DynMessage,
|
||||
message: &mut $crate::core::MessageContext,
|
||||
element: $crate::core::Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> $crate::core::MessageResult<Action> {
|
||||
self.inner.message(view_state, id_path, message, app_state)
|
||||
self.inner.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,10 +10,10 @@ use peniko::kurbo::Vec2;
|
|||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
|
||||
use super::{Modifier, WithModifier};
|
||||
use crate::core::{MessageResult, Mut, View, ViewElement, ViewId, ViewMarker};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewElement, ViewMarker};
|
||||
use crate::diff::{Diff, diff_iters};
|
||||
use crate::vecmap::VecMap;
|
||||
use crate::{DomView, DynMessage, ViewCtx};
|
||||
use crate::{DomView, ViewCtx};
|
||||
|
||||
type CowStr = std::borrow::Cow<'static, str>;
|
||||
|
||||
|
@ -508,11 +508,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
(_, view_state): &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.el.message(view_state, id_path, message, app_state)
|
||||
self.el.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,11 +604,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.el.message(view_state, id_path, message, app_state)
|
||||
self.el.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,10 +730,10 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.el.message(view_state, id_path, message, app_state)
|
||||
self.el.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,67 +84,94 @@ where
|
|||
};
|
||||
}
|
||||
|
||||
fn with_downcast_a(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N1>>)) {
|
||||
fn with_downcast_a<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N1>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::A(node), OneOf::A(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_b(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N2>>)) {
|
||||
fn with_downcast_b<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N2>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::B(node), OneOf::B(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_c(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N3>>)) {
|
||||
fn with_downcast_c<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N3>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::C(node), OneOf::C(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_d(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N4>>)) {
|
||||
fn with_downcast_d<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N4>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::D(node), OneOf::D(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_e(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N5>>)) {
|
||||
fn with_downcast_e<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N5>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::E(node), OneOf::E(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_f(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N6>>)) {
|
||||
fn with_downcast_f<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N6>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::F(node), OneOf::F(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_g(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N7>>)) {
|
||||
fn with_downcast_g<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N7>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::G(node), OneOf::G(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_h(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N8>>)) {
|
||||
fn with_downcast_h<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N8>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::H(node), OneOf::H(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
|
||||
fn with_downcast_i(e: &mut Mut<'_, Self::OneOfElement>, f: impl FnOnce(Mut<'_, Pod<N9>>)) {
|
||||
fn with_downcast_i<R>(
|
||||
e: &mut Mut<'_, Self::OneOfElement>,
|
||||
f: impl FnOnce(Mut<'_, Pod<N9>>) -> R,
|
||||
) -> R {
|
||||
let (OneOf::I(node), OneOf::I(props)) = (&mut e.node, &mut e.props) else {
|
||||
unreachable!()
|
||||
};
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed));
|
||||
f(PodMut::new(node, props, e.flags, e.parent, e.was_removed))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ use wasm_bindgen::prelude::Closure;
|
|||
use wasm_bindgen::{JsCast, UnwrapThrowExt, throw_str};
|
||||
use web_sys::PointerEvent;
|
||||
|
||||
use crate::core::{MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker};
|
||||
use crate::interfaces::Element;
|
||||
use crate::{DomView, DynMessage, ViewCtx};
|
||||
use crate::{DomView, ViewCtx};
|
||||
|
||||
/// Use a distinctive number here, to be able to catch bugs.
|
||||
/// In case the generational-id view path in `View::Message` lead to a wrong view
|
||||
|
@ -201,22 +201,22 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
let Some((first, remainder)) = id_path.split_first() else {
|
||||
let Some(first) = message.take_first() else {
|
||||
throw_str("Parent view of `Pointer` sent outdated and/or incorrect empty view path");
|
||||
};
|
||||
if *first != POINTER_VIEW_ID {
|
||||
if first != POINTER_VIEW_ID {
|
||||
throw_str("Parent view of `Pointer` sent outdated and/or incorrect empty view path");
|
||||
}
|
||||
if remainder.is_empty() {
|
||||
let msg = message.downcast().unwrap_throw();
|
||||
if message.remaining_path().is_empty() {
|
||||
let msg = message.take_message().unwrap_throw();
|
||||
MessageResult::Action((self.callback)(app_state, *msg))
|
||||
} else {
|
||||
self.child
|
||||
.message(&mut view_state.child_state, remainder, message, app_state)
|
||||
.message(&mut view_state.child_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ use std::marker::PhantomData;
|
|||
|
||||
use peniko::{Brush, kurbo};
|
||||
|
||||
use crate::core::{MessageResult, Mut, View, ViewElement, ViewId, ViewMarker};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewElement, ViewMarker};
|
||||
use crate::modifiers::{AttributeModifier, Attributes, Modifier, WithModifier};
|
||||
use crate::{DomView, DynMessage, ViewCtx};
|
||||
use crate::{DomView, ViewCtx};
|
||||
|
||||
pub struct Fill<V, State, Action> {
|
||||
child: V,
|
||||
|
@ -166,11 +166,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
child_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.child.message(child_state, id_path, message, app_state)
|
||||
self.child.message(child_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,11 +295,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.child.message(view_state, id_path, message, app_state)
|
||||
self.child.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
use peniko::kurbo::{BezPath, Circle, Line, Rect};
|
||||
|
||||
use crate::core::{MessageResult, Mut, OrphanView, ViewId};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, OrphanView};
|
||||
use crate::modifiers::{Attributes, WithModifier};
|
||||
use crate::{DynMessage, FromWithContext, Pod, SVG_NS, ViewCtx};
|
||||
use crate::{FromWithContext, Pod, SVG_NS, ViewCtx};
|
||||
|
||||
fn create_element<R>(
|
||||
name: &str,
|
||||
|
@ -74,11 +74,12 @@ impl<State: 'static, Action: 'static> OrphanView<Line, State, Action> for ViewCt
|
|||
fn orphan_message(
|
||||
_view: &Line,
|
||||
(): &mut Self::OrphanViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::OrphanElement>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Stale(message)
|
||||
// TODO: Panic here?
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,11 +132,11 @@ impl<State: 'static, Action: 'static> OrphanView<Rect, State, Action> for ViewCt
|
|||
fn orphan_message(
|
||||
_view: &Rect,
|
||||
(): &mut Self::OrphanViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::OrphanElement>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,11 +187,11 @@ impl<State: 'static, Action: 'static> OrphanView<Circle, State, Action> for View
|
|||
fn orphan_message(
|
||||
_view: &Circle,
|
||||
(): &mut Self::OrphanViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::OrphanElement>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,11 +244,11 @@ impl<State: 'static, Action: 'static> OrphanView<BezPath, State, Action> for Vie
|
|||
fn orphan_message(
|
||||
_view: &BezPath,
|
||||
_view_state: &mut Self::OrphanViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::OrphanElement>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ use std::rc::Rc;
|
|||
|
||||
use wasm_bindgen::UnwrapThrowExt;
|
||||
|
||||
use crate::core::{MessageResult, Mut, View, ViewId, ViewMarker};
|
||||
use crate::{DomView, DynMessage, PodMut, ViewCtx};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, View, ViewMarker};
|
||||
use crate::{DomView, PodMut, ViewCtx};
|
||||
|
||||
/// This view creates an internally cached deep-clone of the underlying DOM node. When the inner view is created again, this will be done more efficiently.
|
||||
pub struct Templated<V>(Rc<V>);
|
||||
|
@ -77,11 +77,11 @@ where
|
|||
fn message(
|
||||
&self,
|
||||
view_state: &mut Self::ViewState,
|
||||
id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
message: &mut MessageContext,
|
||||
element: Mut<'_, Self::Element>,
|
||||
app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
self.0.message(view_state, id_path, message, app_state)
|
||||
self.0.message(view_state, message, element, app_state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
use crate::core::{MessageResult, Mut, OrphanView, ViewId};
|
||||
use crate::{DynMessage, Pod, PodFlags, ViewCtx};
|
||||
use crate::core::{MessageContext, MessageResult, Mut, OrphanView};
|
||||
use crate::{Pod, PodFlags, ViewCtx};
|
||||
|
||||
// strings -> text nodes
|
||||
macro_rules! impl_string_view {
|
||||
|
@ -52,11 +52,12 @@ macro_rules! impl_string_view {
|
|||
fn orphan_message(
|
||||
_view: &$ty,
|
||||
_view_state: &mut Self::OrphanViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::OrphanElement>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Stale(message)
|
||||
// TODO: Panic?
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -111,11 +112,11 @@ macro_rules! impl_to_string_view {
|
|||
fn orphan_message(
|
||||
_view: &$ty,
|
||||
_view_state: &mut Self::OrphanViewState,
|
||||
_id_path: &[ViewId],
|
||||
message: DynMessage,
|
||||
_message: &mut MessageContext,
|
||||
_element: Mut<'_, Self::OrphanElement>,
|
||||
_app_state: &mut State,
|
||||
) -> MessageResult<Action> {
|
||||
MessageResult::Stale(message)
|
||||
MessageResult::Stale
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue