Purify Animation API and introduce application::timed
This commit is contained in:
parent
4334923add
commit
29a19fcde1
9 changed files with 281 additions and 104 deletions
|
|
@ -90,15 +90,17 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Transitions the [`Animation`] from its current state to the given new state.
|
||||
pub fn go(mut self, new_state: T) -> Self {
|
||||
self.go_mut(new_state);
|
||||
/// Transitions the [`Animation`] from its current state to the given new state
|
||||
/// at the given time.
|
||||
pub fn go(mut self, new_state: T, at: Instant) -> Self {
|
||||
self.go_mut(new_state, at);
|
||||
self
|
||||
}
|
||||
|
||||
/// Transitions the [`Animation`] from its current state to the given new state, by reference.
|
||||
pub fn go_mut(&mut self, new_state: T) {
|
||||
self.raw.transition(new_state, Instant::now());
|
||||
/// Transitions the [`Animation`] from its current state to the given new state
|
||||
/// at the given time, by reference.
|
||||
pub fn go_mut(&mut self, new_state: T, at: Instant) {
|
||||
self.raw.transition(new_state, at);
|
||||
}
|
||||
|
||||
/// Returns true if the [`Animation`] is currently in progress.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use iced::window;
|
|||
use iced::{Element, Fill, Point, Rectangle, Renderer, Subscription, Theme};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
iced::application(Arc::default, Arc::update, Arc::view)
|
||||
iced::application(Arc::new, Arc::update, Arc::view)
|
||||
.subscription(Arc::subscription)
|
||||
.theme(|_| Theme::Dark)
|
||||
.run()
|
||||
|
|
@ -25,6 +25,13 @@ enum Message {
|
|||
}
|
||||
|
||||
impl Arc {
|
||||
fn new() -> Self {
|
||||
Arc {
|
||||
start: Instant::now(),
|
||||
cache: Cache::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _: Message) {
|
||||
self.cache.clear();
|
||||
}
|
||||
|
|
@ -38,15 +45,6 @@ impl Arc {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Arc {
|
||||
fn default() -> Self {
|
||||
Arc {
|
||||
start: Instant::now(),
|
||||
cache: Cache::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> canvas::Program<Message> for Arc {
|
||||
type State = ();
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use iced::{
|
|||
pub fn main() -> iced::Result {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
iced::application(Clock::default, Clock::update, Clock::view)
|
||||
iced::application(Clock::new, Clock::update, Clock::view)
|
||||
.subscription(Clock::subscription)
|
||||
.theme(Clock::theme)
|
||||
.run()
|
||||
|
|
@ -28,6 +28,13 @@ enum Message {
|
|||
}
|
||||
|
||||
impl Clock {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
now: chrono::offset::Local::now(),
|
||||
clock: Cache::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::Tick(local_time) => {
|
||||
|
|
@ -58,15 +65,6 @@ impl Clock {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Clock {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
now: chrono::offset::Local::now(),
|
||||
clock: Cache::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> canvas::Program<Message> for Clock {
|
||||
type State = ();
|
||||
|
||||
|
|
|
|||
|
|
@ -21,14 +21,15 @@ use iced::{
|
|||
use std::collections::HashMap;
|
||||
|
||||
fn main() -> iced::Result {
|
||||
iced::application(Gallery::new, Gallery::update, Gallery::view)
|
||||
.window_size((
|
||||
Preview::WIDTH as f32 * 4.0,
|
||||
Preview::HEIGHT as f32 * 2.5,
|
||||
))
|
||||
.subscription(Gallery::subscription)
|
||||
.theme(Gallery::theme)
|
||||
.run()
|
||||
iced::application::timed(
|
||||
Gallery::new,
|
||||
Gallery::update,
|
||||
Gallery::subscription,
|
||||
Gallery::view,
|
||||
)
|
||||
.window_size((Preview::WIDTH as f32 * 4.0, Preview::HEIGHT as f32 * 2.5))
|
||||
.theme(Gallery::theme)
|
||||
.run()
|
||||
}
|
||||
|
||||
struct Gallery {
|
||||
|
|
@ -48,7 +49,7 @@ enum Message {
|
|||
BlurhashDecoded(Id, civitai::Blurhash),
|
||||
Open(Id),
|
||||
Close,
|
||||
Animate(Instant),
|
||||
Animate,
|
||||
}
|
||||
|
||||
impl Gallery {
|
||||
|
|
@ -76,13 +77,15 @@ impl Gallery {
|
|||
|| self.viewer.is_animating(self.now);
|
||||
|
||||
if is_animating {
|
||||
window::frames().map(Message::Animate)
|
||||
window::frames().map(|_| Message::Animate)
|
||||
} else {
|
||||
Subscription::none()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
pub fn update(&mut self, message: Message, now: Instant) -> Task<Message> {
|
||||
self.now = now;
|
||||
|
||||
match message {
|
||||
Message::ImagesListed(Ok(images)) => {
|
||||
self.images = images;
|
||||
|
|
@ -109,16 +112,16 @@ impl Gallery {
|
|||
)
|
||||
}
|
||||
Message::ImageDownloaded(Ok(rgba)) => {
|
||||
self.viewer.show(rgba);
|
||||
self.viewer.show(rgba, self.now);
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::ThumbnailDownloaded(id, Ok(rgba)) => {
|
||||
let thumbnail = if let Some(preview) = self.previews.remove(&id)
|
||||
{
|
||||
preview.load(rgba)
|
||||
preview.load(rgba, self.now)
|
||||
} else {
|
||||
Preview::ready(rgba)
|
||||
Preview::ready(rgba, self.now)
|
||||
};
|
||||
|
||||
let _ = self.previews.insert(id, thumbnail);
|
||||
|
|
@ -127,7 +130,7 @@ impl Gallery {
|
|||
}
|
||||
Message::ThumbnailHovered(id, is_hovered) => {
|
||||
if let Some(preview) = self.previews.get_mut(&id) {
|
||||
preview.toggle_zoom(is_hovered);
|
||||
preview.toggle_zoom(is_hovered, self.now);
|
||||
}
|
||||
|
||||
Task::none()
|
||||
|
|
@ -136,7 +139,7 @@ impl Gallery {
|
|||
if !self.previews.contains_key(&id) {
|
||||
let _ = self
|
||||
.previews
|
||||
.insert(id, Preview::loading(blurhash.rgba));
|
||||
.insert(id, Preview::loading(blurhash.rgba, self.now));
|
||||
}
|
||||
|
||||
Task::none()
|
||||
|
|
@ -151,7 +154,7 @@ impl Gallery {
|
|||
return Task::none();
|
||||
};
|
||||
|
||||
self.viewer.open();
|
||||
self.viewer.open(self.now);
|
||||
|
||||
Task::perform(
|
||||
image.download(Size::Original),
|
||||
|
|
@ -159,15 +162,11 @@ impl Gallery {
|
|||
)
|
||||
}
|
||||
Message::Close => {
|
||||
self.viewer.close();
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::Animate(now) => {
|
||||
self.now = now;
|
||||
self.viewer.close(self.now);
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::Animate => Task::none(),
|
||||
Message::ImagesListed(Err(error))
|
||||
| Message::ImageDownloaded(Err(error))
|
||||
| Message::ThumbnailDownloaded(_, Err(error)) => {
|
||||
|
|
@ -293,13 +292,13 @@ impl Preview {
|
|||
const WIDTH: u32 = 320;
|
||||
const HEIGHT: u32 = 410;
|
||||
|
||||
fn loading(rgba: Rgba) -> Self {
|
||||
fn loading(rgba: Rgba, now: Instant) -> Self {
|
||||
Self::Loading {
|
||||
blurhash: Blurhash {
|
||||
fade_in: Animation::new(false)
|
||||
.duration(milliseconds(700))
|
||||
.easing(animation::Easing::EaseIn)
|
||||
.go(true),
|
||||
.go(true, now),
|
||||
handle: image::Handle::from_rgba(
|
||||
rgba.width,
|
||||
rgba.height,
|
||||
|
|
@ -309,27 +308,27 @@ impl Preview {
|
|||
}
|
||||
}
|
||||
|
||||
fn ready(rgba: Rgba) -> Self {
|
||||
fn ready(rgba: Rgba, now: Instant) -> Self {
|
||||
Self::Ready {
|
||||
blurhash: None,
|
||||
thumbnail: Thumbnail::new(rgba),
|
||||
thumbnail: Thumbnail::new(rgba, now),
|
||||
}
|
||||
}
|
||||
|
||||
fn load(self, rgba: Rgba) -> Self {
|
||||
fn load(self, rgba: Rgba, now: Instant) -> Self {
|
||||
let Self::Loading { blurhash } = self else {
|
||||
return self;
|
||||
};
|
||||
|
||||
Self::Ready {
|
||||
blurhash: Some(blurhash),
|
||||
thumbnail: Thumbnail::new(rgba),
|
||||
thumbnail: Thumbnail::new(rgba, now),
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_zoom(&mut self, enabled: bool) {
|
||||
fn toggle_zoom(&mut self, enabled: bool, now: Instant) {
|
||||
if let Self::Ready { thumbnail, .. } = self {
|
||||
thumbnail.zoom.go_mut(enabled);
|
||||
thumbnail.zoom.go_mut(enabled, now);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -357,14 +356,14 @@ impl Preview {
|
|||
}
|
||||
|
||||
impl Thumbnail {
|
||||
pub fn new(rgba: Rgba) -> Self {
|
||||
pub fn new(rgba: Rgba, now: Instant) -> Self {
|
||||
Self {
|
||||
handle: image::Handle::from_rgba(
|
||||
rgba.width,
|
||||
rgba.height,
|
||||
rgba.pixels,
|
||||
),
|
||||
fade_in: Animation::new(false).slow().go(true),
|
||||
fade_in: Animation::new(false).slow().go(true, now),
|
||||
zoom: Animation::new(false)
|
||||
.quick()
|
||||
.easing(animation::Easing::EaseInOut),
|
||||
|
|
@ -391,24 +390,24 @@ impl Viewer {
|
|||
}
|
||||
}
|
||||
|
||||
fn open(&mut self) {
|
||||
fn open(&mut self, now: Instant) {
|
||||
self.image = None;
|
||||
self.background_fade_in.go_mut(true);
|
||||
self.background_fade_in.go_mut(true, now);
|
||||
}
|
||||
|
||||
fn show(&mut self, rgba: Rgba) {
|
||||
fn show(&mut self, rgba: Rgba, now: Instant) {
|
||||
self.image = Some(image::Handle::from_rgba(
|
||||
rgba.width,
|
||||
rgba.height,
|
||||
rgba.pixels,
|
||||
));
|
||||
self.background_fade_in.go_mut(true);
|
||||
self.image_fade_in.go_mut(true);
|
||||
self.background_fade_in.go_mut(true, now);
|
||||
self.image_fade_in.go_mut(true, now);
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
self.background_fade_in.go_mut(false);
|
||||
self.image_fade_in.go_mut(false);
|
||||
fn close(&mut self, now: Instant) {
|
||||
self.background_fade_in.go_mut(false, now);
|
||||
self.image_fade_in.go_mut(false, now);
|
||||
}
|
||||
|
||||
fn is_animating(&self, now: Instant) -> bool {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,15 @@ use std::io;
|
|||
use std::sync::Arc;
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
iced::application(Markdown::new, Markdown::update, Markdown::view)
|
||||
.font(icon::FONT)
|
||||
.subscription(Markdown::subscription)
|
||||
.theme(Markdown::theme)
|
||||
.run()
|
||||
iced::application::timed(
|
||||
Markdown::new,
|
||||
Markdown::update,
|
||||
Markdown::subscription,
|
||||
Markdown::view,
|
||||
)
|
||||
.font(icon::FONT)
|
||||
.theme(Markdown::theme)
|
||||
.run()
|
||||
}
|
||||
|
||||
struct Markdown {
|
||||
|
|
@ -58,7 +62,7 @@ enum Message {
|
|||
ImageDownloaded(markdown::Url, Result<image::Handle, Error>),
|
||||
ToggleStream(bool),
|
||||
NextToken,
|
||||
Animate(Instant),
|
||||
Tick,
|
||||
}
|
||||
|
||||
impl Markdown {
|
||||
|
|
@ -78,7 +82,9 @@ impl Markdown {
|
|||
)
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Task<Message> {
|
||||
fn update(&mut self, message: Message, now: Instant) -> Task<Message> {
|
||||
self.now = now;
|
||||
|
||||
match message {
|
||||
Message::Edit(action) => {
|
||||
let is_edit = action.is_edit();
|
||||
|
|
@ -119,7 +125,7 @@ impl Markdown {
|
|||
fade_in: Animation::new(false)
|
||||
.quick()
|
||||
.easing(animation::Easing::EaseInOut)
|
||||
.go(true),
|
||||
.go(true, self.now),
|
||||
})
|
||||
.unwrap_or_else(Image::Errored),
|
||||
);
|
||||
|
|
@ -164,11 +170,7 @@ impl Markdown {
|
|||
|
||||
Task::none()
|
||||
}
|
||||
Message::Animate(now) => {
|
||||
self.now = now;
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::Tick => Task::none(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +232,7 @@ impl Markdown {
|
|||
});
|
||||
|
||||
if is_animating {
|
||||
window::frames().map(Message::Animate)
|
||||
window::frames().map(|_| Message::Tick)
|
||||
} else {
|
||||
Subscription::none()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,31 +21,36 @@ use std::time::Instant;
|
|||
pub fn main() -> iced::Result {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
iced::application(
|
||||
SolarSystem::default,
|
||||
iced::application::timed(
|
||||
SolarSystem::new,
|
||||
SolarSystem::update,
|
||||
SolarSystem::subscription,
|
||||
SolarSystem::view,
|
||||
)
|
||||
.subscription(SolarSystem::subscription)
|
||||
.theme(SolarSystem::theme)
|
||||
.run()
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SolarSystem {
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Message {
|
||||
Tick(Instant),
|
||||
Tick,
|
||||
}
|
||||
|
||||
impl SolarSystem {
|
||||
fn update(&mut self, message: Message) {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
state: State::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message, now: Instant) {
|
||||
match message {
|
||||
Message::Tick(instant) => {
|
||||
self.state.update(instant);
|
||||
Message::Tick => {
|
||||
self.state.update(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +64,7 @@ impl SolarSystem {
|
|||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
window::frames().map(Message::Tick)
|
||||
window::frames().map(|_| Message::Tick)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,10 +110,7 @@ impl State {
|
|||
}
|
||||
|
||||
pub fn update(&mut self, now: Instant) {
|
||||
if self.start > now {
|
||||
self.start = now;
|
||||
}
|
||||
|
||||
self.start = self.start.min(now);
|
||||
self.now = now;
|
||||
self.system_cache.clear();
|
||||
}
|
||||
|
|
@ -206,9 +208,3 @@ impl<Message> canvas::Program<Message> for State {
|
|||
vec![background, system]
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use iced::mouse;
|
||||
use iced::time::{self, Instant, milliseconds};
|
||||
use iced::time::{self, milliseconds};
|
||||
use iced::widget::canvas;
|
||||
use iced::{
|
||||
Color, Element, Fill, Font, Point, Rectangle, Renderer, Subscription, Theme,
|
||||
|
|
@ -22,13 +22,13 @@ struct TheMatrix {
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Message {
|
||||
Tick(Instant),
|
||||
Tick,
|
||||
}
|
||||
|
||||
impl TheMatrix {
|
||||
fn update(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::Tick(_now) => {
|
||||
Message::Tick => {
|
||||
self.tick += 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ impl TheMatrix {
|
|||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
time::every(milliseconds(50)).map(Message::Tick)
|
||||
time::every(milliseconds(50)).map(|_| Message::Tick)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ use crate::{
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub mod timed;
|
||||
|
||||
pub use timed::timed;
|
||||
|
||||
/// Creates an iced [`Application`] given its boot, update, and view logic.
|
||||
///
|
||||
/// # Example
|
||||
|
|
|
|||
178
src/application/timed.rs
Normal file
178
src/application/timed.rs
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
//! An [`Application`] that receives an [`Instant`] in update logic.
|
||||
use crate::application::{Application, Boot, View};
|
||||
use crate::program;
|
||||
use crate::theme;
|
||||
use crate::time::Instant;
|
||||
use crate::window;
|
||||
use crate::{Element, Program, Settings, Subscription, Task};
|
||||
|
||||
/// Creates an [`Application`] with an `update` function that also
|
||||
/// takes the [`Instant`] of each `Message`.
|
||||
///
|
||||
/// This constructor is useful to create animated applications that
|
||||
/// are _pure_ (e.g. without relying on side-effect calls like [`Instant::now`]).
|
||||
///
|
||||
/// Purity is needed when you want your application to end up in the
|
||||
/// same exact state given the same history of messages. This property
|
||||
/// enables proper time traveling debugging with [`comet`].
|
||||
///
|
||||
/// [`comet`]: https://github.com/iced-rs/comet
|
||||
pub fn timed<State, Message, Theme, Renderer>(
|
||||
boot: impl Boot<State, Message>,
|
||||
update: impl Update<State, Message>,
|
||||
subscription: impl Fn(&State) -> Subscription<Message>,
|
||||
view: impl for<'a> View<'a, State, Message, Theme, Renderer>,
|
||||
) -> Application<
|
||||
impl Program<State = State, Message = (Message, Instant), Theme = Theme>,
|
||||
>
|
||||
where
|
||||
State: 'static,
|
||||
Message: program::Message + 'static,
|
||||
Theme: Default + theme::Base + 'static,
|
||||
Renderer: program::Renderer + 'static,
|
||||
{
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct Instance<
|
||||
State,
|
||||
Message,
|
||||
Theme,
|
||||
Renderer,
|
||||
Boot,
|
||||
Update,
|
||||
Subscription,
|
||||
View,
|
||||
> {
|
||||
boot: Boot,
|
||||
update: Update,
|
||||
subscription: Subscription,
|
||||
view: View,
|
||||
_state: PhantomData<State>,
|
||||
_message: PhantomData<Message>,
|
||||
_theme: PhantomData<Theme>,
|
||||
_renderer: PhantomData<Renderer>,
|
||||
}
|
||||
|
||||
impl<State, Message, Theme, Renderer, Boot, Update, Subscription, View>
|
||||
Program
|
||||
for Instance<
|
||||
State,
|
||||
Message,
|
||||
Theme,
|
||||
Renderer,
|
||||
Boot,
|
||||
Update,
|
||||
Subscription,
|
||||
View,
|
||||
>
|
||||
where
|
||||
Message: program::Message + 'static,
|
||||
Theme: Default + theme::Base + 'static,
|
||||
Renderer: program::Renderer + 'static,
|
||||
Boot: self::Boot<State, Message>,
|
||||
Update: self::Update<State, Message>,
|
||||
Subscription: Fn(&State) -> self::Subscription<Message>,
|
||||
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
|
||||
{
|
||||
type State = State;
|
||||
type Message = (Message, Instant);
|
||||
type Theme = Theme;
|
||||
type Renderer = Renderer;
|
||||
type Executor = iced_futures::backend::default::Executor;
|
||||
|
||||
fn name() -> &'static str {
|
||||
let name = std::any::type_name::<State>();
|
||||
|
||||
name.split("::").next().unwrap_or("a_cool_application")
|
||||
}
|
||||
|
||||
fn boot(&self) -> (State, Task<Self::Message>) {
|
||||
let (state, task) = self.boot.boot();
|
||||
|
||||
(state, task.map(|message| (message, Instant::now())))
|
||||
}
|
||||
|
||||
fn update(
|
||||
&self,
|
||||
state: &mut Self::State,
|
||||
(message, now): Self::Message,
|
||||
) -> Task<Self::Message> {
|
||||
self.update
|
||||
.update(state, message, now)
|
||||
.into()
|
||||
.map(|message| (message, Instant::now()))
|
||||
}
|
||||
|
||||
fn view<'a>(
|
||||
&self,
|
||||
state: &'a Self::State,
|
||||
_window: window::Id,
|
||||
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
|
||||
self.view
|
||||
.view(state)
|
||||
.into()
|
||||
.map(|message| (message, Instant::now()))
|
||||
}
|
||||
|
||||
fn subscription(
|
||||
&self,
|
||||
state: &Self::State,
|
||||
) -> self::Subscription<Self::Message> {
|
||||
(self.subscription)(state).map(|message| (message, Instant::now()))
|
||||
}
|
||||
}
|
||||
|
||||
Application {
|
||||
raw: Instance {
|
||||
boot,
|
||||
update,
|
||||
subscription,
|
||||
view,
|
||||
_state: PhantomData,
|
||||
_message: PhantomData,
|
||||
_theme: PhantomData,
|
||||
_renderer: PhantomData,
|
||||
},
|
||||
settings: Settings::default(),
|
||||
window: window::Settings::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The update logic of some timed [`Application`].
|
||||
///
|
||||
/// This is like [`application::Update`](super::Update),
|
||||
/// but it also takes an [`Instant`].
|
||||
pub trait Update<State, Message> {
|
||||
/// Processes the message and updates the state of the [`Application`].
|
||||
fn update(
|
||||
&self,
|
||||
state: &mut State,
|
||||
message: Message,
|
||||
now: Instant,
|
||||
) -> impl Into<Task<Message>>;
|
||||
}
|
||||
|
||||
impl<State, Message> Update<State, Message> for () {
|
||||
fn update(
|
||||
&self,
|
||||
_state: &mut State,
|
||||
_message: Message,
|
||||
_now: Instant,
|
||||
) -> impl Into<Task<Message>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, State, Message, C> Update<State, Message> for T
|
||||
where
|
||||
T: Fn(&mut State, Message, Instant) -> C,
|
||||
C: Into<Task<Message>>,
|
||||
{
|
||||
fn update(
|
||||
&self,
|
||||
state: &mut State,
|
||||
message: Message,
|
||||
now: Instant,
|
||||
) -> impl Into<Task<Message>> {
|
||||
self(state, message, now)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue