-
-
Notifications
You must be signed in to change notification settings - Fork 27
feat: add Step/StepMut/StepWith/StepWithMut traits #108
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| //! Pull-based stepping traits for widget event loops. | ||
| //! | ||
| //! Mogwai widgets are driven by a pull-based event loop: a caller awaits the | ||
| //! widget's next event, reacts to it, then awaits again. This module | ||
| //! formalizes that convention as four traits so the compiler — not prose — | ||
| //! enforces the contract and so generic composition (e.g. racing N children) | ||
| //! becomes possible. | ||
| //! | ||
| //! ## When to use which trait | ||
| //! | ||
| //! | Trait | Receiver | Use when | | ||
| //! |------|----------|----------| | ||
| //! | [`Step`] | `&self` | `step` only awaits event listeners (interior mutability). Lets a parent race multiple children concurrently. | | ||
| //! | [`StepMut`] | `&mut self` | `step` mutates the widget's own fields or drives a mutable resource. Children cannot be raced concurrently. | | ||
| //! | [`StepWith<T>`] | `&self` | A container of `T`-typed children that races a per-child future (supplied by a closure) against its own event. | | ||
| //! | [`StepWithMut<T>`] | `&mut self` | Same, but with exclusive access to each child. | | ||
| //! | ||
| //! ## Object safety | ||
| //! | ||
| //! These traits use `impl Future` returns (RPITIT) and are therefore **not** | ||
| //! object-safe. If a `dyn Step` need ever arises, add a boxed companion trait | ||
| //! with a blanket bridge as a non-breaking addition. | ||
|
|
||
| use std::{future::Future, pin::Pin}; | ||
|
|
||
| /// Pull-based event source — immutable borrow. | ||
| /// | ||
| /// Implement this when `step` only awaits event listeners (which use interior | ||
| /// mutability). This lets a parent race multiple children's `step()` futures | ||
| /// concurrently without borrow conflicts. | ||
| /// | ||
| /// ## Example | ||
| /// | ||
| /// ```no_run | ||
| /// use mogwai::{prelude::*, step::Step}; | ||
| /// | ||
| /// struct Button<V: View> { | ||
| /// on_click: V::EventListener, | ||
| /// } | ||
| /// | ||
| /// impl<V: View> Step for Button<V> { | ||
| /// type Output = V::Event; | ||
| /// fn step(&self) -> impl Future<Output = V::Event> { | ||
| /// self.on_click.next() | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| pub trait Step { | ||
| /// The event produced by a single step. Must be `'static` so any `Step` | ||
| /// can be plugged into a [`StepWith`] closure. | ||
| type Output: 'static; | ||
|
|
||
| /// Advance the widget by one event. May resolve to `Self::Output` or | ||
| /// never resolve (e.g. a presentational component with no events). | ||
| fn step(&self) -> impl Future<Output = Self::Output>; | ||
| } | ||
|
|
||
| /// Pull-based event source — exclusive borrow. | ||
| /// | ||
| /// Implement this when `step` mutates the widget's own fields or drives a | ||
| /// mutable resource it owns. A parent cannot race two `StepMut` children | ||
| /// concurrently; use [`Step`] instead when concurrent racing is needed. | ||
| /// | ||
| /// ## Example | ||
| /// | ||
| /// ```no_run | ||
| /// use mogwai::{prelude::*, step::StepMut}; | ||
| /// | ||
| /// struct Counter { | ||
| /// count: u32, | ||
| /// on_click: <mogwai::web::Web as View>::EventListener, | ||
| /// } | ||
| /// | ||
| /// impl StepMut for Counter { | ||
| /// type Output = (); | ||
| /// fn step_mut(&mut self) -> impl Future<Output = ()> { | ||
| /// async move { | ||
| /// let _ev = self.on_click.next().await; | ||
| /// self.count += 1; | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| pub trait StepMut { | ||
| /// The event produced by a single step. Must be `'static` so any | ||
| /// `StepMut` can be plugged into a [`StepWithMut`] closure. | ||
| type Output: 'static; | ||
|
|
||
| /// Advance the widget by one event, mutating internal state. | ||
| fn step_mut(&mut self) -> impl Future<Output = Self::Output>; | ||
| } | ||
|
|
||
| /// A container of `T`-typed children that races a per-child future (supplied by | ||
| /// a closure) against its own event future — immutable borrow. | ||
| /// | ||
| /// This generalizes the `List::step` / `ButtonGroup::step` / `TabList::step` | ||
| /// pattern: the container owns `N` children of type `T`, and the caller | ||
| /// decides how each child produces a future of type `Ev`. | ||
| /// | ||
| /// ## Example | ||
| /// | ||
| /// ```no_run | ||
| /// use mogwai::{ | ||
| /// prelude::*, | ||
| /// step::{Step, StepWith}, | ||
| /// }; | ||
| /// use std::pin::Pin; | ||
| /// | ||
| /// struct List<V: View, T> { | ||
| /// items: Vec<T>, | ||
| /// // ... | ||
| /// # _phantom: std::marker::PhantomData<V>, | ||
| /// } | ||
| /// | ||
| /// impl<V: View, T> StepWith<T> for List<V, T> { | ||
| /// type Output = (); | ||
| /// fn step_with<Ev>( | ||
| /// &self, | ||
| /// f: impl for<'a> FnMut(&'a T) -> Pin<Box<dyn Future<Output = Ev> + 'a>>, | ||
| /// ) -> impl Future<Output = ()> | ||
| /// where | ||
| /// Ev: 'static, | ||
| /// { | ||
| /// async move { | ||
| /// // race all children's futures... | ||
| /// # std::future::pending::<()>().await | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| pub trait StepWith<T> { | ||
| /// The event produced by the container's own step. | ||
| type Output: 'static; | ||
|
|
||
| /// Race the container's own event future against a future produced by | ||
| /// `f` for each child. The first to resolve wins. | ||
| /// | ||
| /// The closure uses a higher-ranked trait bound (`for<'a>`) so the returned | ||
| /// boxed future is allowed to borrow from the child reference for exactly | ||
| /// as long as that borrow lives — e.g. `Box::pin(child.step())` where | ||
| /// `step` borrows `&'a self`. | ||
| fn step_with<Ev>( | ||
| &self, | ||
| f: impl for<'a> FnMut(&'a T) -> Pin<Box<dyn Future<Output = Ev> + 'a>>, | ||
| ) -> impl Future<Output = Self::Output> | ||
| where | ||
| Ev: 'static; | ||
| } | ||
|
|
||
| /// A container of `T`-typed children that races a per-child future (supplied by | ||
| /// a closure) against its own event future — mutable borrow. | ||
| /// | ||
| /// This generalizes the `TabPanel::step_with` / `Table::step_with` pattern: the | ||
| /// container owns `N` children of type `T` mutably, and the caller decides how | ||
| /// each produces a future of type `Ev`. | ||
| /// | ||
| /// ## Example | ||
| /// | ||
| /// ```no_run | ||
| /// use mogwai::{prelude::*, step::StepWithMut}; | ||
| /// use std::pin::Pin; | ||
| /// | ||
| /// struct TabPanel<V: View, P> { | ||
| /// panes: Vec<P>, | ||
| /// // ... | ||
| /// # _phantom: std::marker::PhantomData<V>, | ||
| /// } | ||
| /// | ||
| /// impl<V: View, P> StepWithMut<P> for TabPanel<V, P> { | ||
| /// type Output = (); | ||
| /// fn step_with_mut<Ev>( | ||
| /// &mut self, | ||
| /// f: impl for<'a> FnMut(&'a mut P) -> Pin<Box<dyn Future<Output = Ev> + 'a>>, | ||
| /// ) -> impl Future<Output = ()> | ||
| /// where | ||
| /// Ev: 'static, | ||
| /// { | ||
| /// async move { | ||
| /// // race tab clicks against all pane futures... | ||
| /// # std::future::pending::<()>().await | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| pub trait StepWithMut<T> { | ||
| /// The event produced by the container's own step. | ||
| type Output: 'static; | ||
|
|
||
| /// Race the container's own event future against a future produced by | ||
| /// `f` for each child (with mutable access). The first to resolve wins. | ||
| /// | ||
| /// The closure uses a higher-ranked trait bound (`for<'a>`) so the returned | ||
| /// boxed future is allowed to borrow from the child reference for exactly | ||
| /// as long as that borrow lives — e.g. `Box::pin(child.step_mut())` where | ||
| /// `step_mut` borrows `&'a mut self`. | ||
| fn step_with_mut<Ev>( | ||
| &mut self, | ||
| f: impl for<'a> FnMut(&'a mut T) -> Pin<Box<dyn Future<Output = Ev> + 'a>>, | ||
| ) -> impl Future<Output = Self::Output> | ||
| where | ||
| Ev: 'static; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.