-
Notifications
You must be signed in to change notification settings - Fork 117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the lens
component
#587
Changes from 6 commits
ad3277c
975b3f5
90911b6
986cc46
6cea13a
c131e0d
ce57976
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,7 @@ | ||
# Don't warn about these identifiers when using clippy::doc_markdown. | ||
doc-valid-idents = ["MathML", ".."] | ||
|
||
# The default clippy value for this is 250, which causes warnings for rather simple types | ||
# like Box<dyn Fn(&mut Env, &T)>, which seems overly strict. The new value of 400 is | ||
# a simple guess. It might be worth lowering this, or using the default, in the future. | ||
type-complexity-threshold = 400 | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,12 +5,13 @@ use core::marker::PhantomData; | |
|
||
use crate::{MessageResult, Mut, View, ViewId, ViewMarker, ViewPathTracker}; | ||
|
||
/// A view that "extracts" state from a [`View<ParentState,_,_>`] to [`View<ChildState,_,_>`]. | ||
/// This allows modularization of views based on their state. | ||
pub struct MapState<ParentState, ChildState, V, F = fn(&mut ParentState) -> &mut ChildState> { | ||
f: F, | ||
/// The View for [`map_state`] and [`lens`]. | ||
/// | ||
/// See their documentation for more context. | ||
pub struct MapState<V, F, ParentState, ChildState, Action, Context, Message> { | ||
map_state: F, | ||
child: V, | ||
phantom: PhantomData<fn() -> (ParentState, ChildState)>, | ||
phantom: PhantomData<fn(ParentState) -> (ChildState, Action, Context, Message)>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, has this kind caused issues already in practice? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so, but I could foresee it being troublesome for the tests. It might have been fine. I will add a comment that the variance consequences of this have not been reasoned about. |
||
} | ||
|
||
/// A view that "extracts" state from a [`View<ParentState,_,_>`] to [`View<ChildState,_,_>`]. | ||
|
@@ -42,28 +43,90 @@ pub struct MapState<ParentState, ChildState, V, F = fn(&mut ParentState) -> &mut | |
pub fn map_state<ParentState, ChildState, Action, Context: ViewPathTracker, Message, V, F>( | ||
view: V, | ||
f: F, | ||
) -> MapState<ParentState, ChildState, V, F> | ||
) -> MapState<V, F, ParentState, ChildState, Action, Context, Message> | ||
where | ||
ParentState: 'static, | ||
ChildState: 'static, | ||
V: View<ChildState, Action, Context, Message>, | ||
F: Fn(&mut ParentState) -> &mut ChildState + 'static, | ||
{ | ||
MapState { | ||
f, | ||
map_state: f, | ||
child: view, | ||
phantom: PhantomData, | ||
} | ||
} | ||
|
||
impl<ParentState, ChildState, V, F> ViewMarker for MapState<ParentState, ChildState, V, F> {} | ||
impl<ParentState, ChildState, Action, Context: ViewPathTracker, Message, V, F> | ||
View<ParentState, Action, Context, Message> for MapState<ParentState, ChildState, V, F> | ||
/// An adapter which allows using a component which only uses one field of the current state. | ||
/// | ||
/// In Xilem, many components are functions of the form `fn my_component(&mut SomeState) -> impl WidgetView<SomeState>`. | ||
/// For example, a date picker might be of the form `fn date_picker(&mut Date) -> impl WidgetView<Date>`. | ||
/// The `lens` View allows using these components in a higher-level component, where the higher level state has | ||
/// a field of the inner component's state type. | ||
/// For example, a flight finder app might have a `Date` field for the currently selected date. | ||
/// | ||
/// The parameters of this view are: | ||
/// - `state`: The current outer view's state | ||
/// - `map`: A function from the higher-level state type to `component`'s state type | ||
/// - `component`: The child component the lens is being created for. | ||
DJMcNab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// This is a wrapper around [`map_state`]. | ||
/// That view can be used if the child doesn't follow the expected component signature. | ||
/// | ||
/// # Examples | ||
/// | ||
/// In code, the date picker example might look like: | ||
/// | ||
/// ``` | ||
/// # use xilem_core::docs::{DocsView as WidgetView, State as Date, State as Flight, some_component as date_picker}; | ||
/// use xilem_core::lens; | ||
/// | ||
/// fn app_logic(state: &mut FlightPlanner) -> impl WidgetView<FlightPlanner> { | ||
/// lens(date_picker, state, |state| &mut state.date) | ||
/// } | ||
/// | ||
/// struct FlightPlanner { | ||
/// date: Date, | ||
/// available_flights: Vec<Flight>, | ||
/// } | ||
/// ``` | ||
pub fn lens<OuterState, Action, Context, Message, InnerState, StateF, InnerView, Component>( | ||
component: Component, | ||
state: &mut OuterState, | ||
// This parameter ordering does run into https://github.com/rust-lang/rustfmt/issues/3605 | ||
// Our general advice is to make sure that the lens arguments are short enough... | ||
map: StateF, | ||
) -> MapState<InnerView, StateF, OuterState, InnerState, Action, Context, Message> | ||
where | ||
StateF: Fn(&mut OuterState) -> &mut InnerState + Send + Sync + 'static, | ||
Component: FnOnce(&mut InnerState) -> InnerView, | ||
InnerView: View<InnerState, Action, Context, Message>, | ||
Context: ViewPathTracker, | ||
{ | ||
let mapped = map(state); | ||
let view = component(mapped); | ||
MapState { | ||
child: view, | ||
map_state: map, | ||
phantom: PhantomData, | ||
} | ||
} | ||
|
||
impl<V, F, ParentState, ChildState, Action, Context, Message> ViewMarker | ||
for MapState<V, F, ParentState, ChildState, Action, Context, Message> | ||
{ | ||
} | ||
impl<ParentState, ChildState, Action, Context, Message, V, F> | ||
View<ParentState, Action, Context, Message> | ||
for MapState<V, F, ParentState, ChildState, Action, Context, Message> | ||
where | ||
ParentState: 'static, | ||
ChildState: 'static, | ||
V: View<ChildState, Action, Context, Message>, | ||
F: Fn(&mut ParentState) -> &mut ChildState + 'static, | ||
Action: 'static, | ||
Context: ViewPathTracker + 'static, | ||
Message: 'static, | ||
{ | ||
type ViewState = V::ViewState; | ||
type Element = V::Element; | ||
|
@@ -99,6 +162,6 @@ where | |
app_state: &mut ParentState, | ||
) -> MessageResult<Action, Message> { | ||
self.child | ||
.message(view_state, id_path, message, (self.f)(app_state)) | ||
.message(view_state, id_path, message, (self.map_state)(app_state)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,9 @@ use crate::{MessageResult, NoElement, View, ViewMarker, ViewPathTracker}; | |
/// This can be useful for logging a value: | ||
/// | ||
/// ``` | ||
/// # use xilem_core::{run_once, View, docs::{Fake as ViewCtx}, PhantomView}; | ||
/// # use xilem_core::{run_once, View, docs::{Fake as ViewCtx, DocsView as WidgetView}}; | ||
/// # struct AppData; | ||
/// fn log_lifecycle(data: &mut AppData) -> impl PhantomView<AppData, (), ViewCtx> { | ||
/// fn log_lifecycle(data: &mut AppData) -> impl WidgetView<AppData, ()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just noticed this (+ |
||
/// run_once(|| eprintln!("View constructed")) | ||
/// } | ||
/// ``` | ||
|
@@ -32,11 +32,11 @@ use crate::{MessageResult, NoElement, View, ViewMarker, ViewPathTracker}; | |
/// // <https://doc.rust-lang.org/error_codes/E0080.html> | ||
/// // Note that this error code is only checked on nightly | ||
/// ```compile_fail,E0080 | ||
/// # use xilem_core::{run_once, View, docs::{Fake as ViewCtx}, PhantomView}; | ||
/// # use xilem_core::{run_once, View, docs::{DocsView as WidgetView}}; | ||
/// # struct AppData { | ||
/// # data: u32 | ||
/// # } | ||
/// fn log_data(app: &mut AppData) -> impl PhantomView<AppData, (), ViewCtx> { | ||
/// fn log_data(app: &mut AppData) -> impl WidgetView<AppData, ()> { | ||
/// let val = app.data; | ||
/// run_once(move || println!("{}", val)) | ||
/// } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍