mirror of https://github.com/linebender/xilem
Restore animation support - add `spinner` view (#497)
This is the smallest change possible to make animations work in Masonry. Essentially, we treat every redraw as a potential animation frame, so that animations do work. I've also added the `spinner` view in Xilem to test this out. The `state_machine` examples uses this. This is work done for the variable fonts demo.
This commit is contained in:
parent
a52c3c7b3c
commit
cfb0ef231e
|
@ -384,6 +384,8 @@ impl MasonryState<'_> {
|
||||||
height,
|
height,
|
||||||
antialiasing_method: vello::AaConfig::Area,
|
antialiasing_method: vello::AaConfig::Area,
|
||||||
};
|
};
|
||||||
|
// TODO: Run this in-between `submit` and `present`.
|
||||||
|
window.pre_present_notify();
|
||||||
self.renderer
|
self.renderer
|
||||||
.get_or_insert_with(|| Renderer::new(device, renderer_options).unwrap())
|
.get_or_insert_with(|| Renderer::new(device, renderer_options).unwrap())
|
||||||
.render_to_surface(device, queue, scene_ref, &surface_texture, &render_params)
|
.render_to_surface(device, queue, scene_ref, &surface_texture, &render_params)
|
||||||
|
@ -420,6 +422,7 @@ impl MasonryState<'_> {
|
||||||
.handle_window_event(WindowEvent::Rescale(scale_factor));
|
.handle_window_event(WindowEvent::Rescale(scale_factor));
|
||||||
}
|
}
|
||||||
WinitWindowEvent::RedrawRequested => {
|
WinitWindowEvent::RedrawRequested => {
|
||||||
|
self.render_root.handle_window_event(WindowEvent::AnimFrame);
|
||||||
let (scene, tree_update) = self.render_root.redraw();
|
let (scene, tree_update) = self.render_root.redraw();
|
||||||
self.render(scene);
|
self.render(scene);
|
||||||
let WindowState::Rendering {
|
let WindowState::Rendering {
|
||||||
|
|
|
@ -49,11 +49,13 @@ impl Spinner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_SPINNER_COLOR: Color = theme::TEXT_COLOR;
|
||||||
|
|
||||||
impl Default for Spinner {
|
impl Default for Spinner {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Spinner {
|
Spinner {
|
||||||
t: 0.0,
|
t: 0.0,
|
||||||
color: theme::TEXT_COLOR,
|
color: DEFAULT_SPINNER_COLOR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +71,11 @@ impl WidgetMut<'_, Spinner> {
|
||||||
self.widget.color = color.into();
|
self.widget.color = color.into();
|
||||||
self.ctx.request_paint();
|
self.ctx.request_paint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset the spinner's color to its default value.
|
||||||
|
pub fn reset_color(&mut self) {
|
||||||
|
self.set_color(DEFAULT_SPINNER_COLOR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- MARK: IMPL WIDGET ---
|
// --- MARK: IMPL WIDGET ---
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
use winit::error::EventLoopError;
|
use winit::error::EventLoopError;
|
||||||
use xilem::{
|
use xilem::{
|
||||||
core::one_of::{OneOf, OneOf3},
|
core::one_of::{OneOf, OneOf3},
|
||||||
view::{button, flex, label, prose},
|
view::{button, flex, label, prose, sized_box, spinner},
|
||||||
EventLoop, WidgetView, Xilem,
|
EventLoop, WidgetView, Xilem,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,6 +67,10 @@ fn app_logic(app_data: &mut StateMachine) -> impl WidgetView<StateMachine> {
|
||||||
}),
|
}),
|
||||||
prose(&*app_data.history),
|
prose(&*app_data.history),
|
||||||
label(format!("Current state: {:?}", app_data.state)),
|
label(format!("Current state: {:?}", app_data.state)),
|
||||||
|
// TODO: Make `spinner` not need a `sized_box` to appear.
|
||||||
|
sized_box::<StateMachine, (), _>(spinner())
|
||||||
|
.height(40.)
|
||||||
|
.width(40.),
|
||||||
state_machine(app_data),
|
state_machine(app_data),
|
||||||
// TODO: When we have a canvas widget, visualise the entire state machine here.
|
// TODO: When we have a canvas widget, visualise the entire state machine here.
|
||||||
))
|
))
|
||||||
|
|
|
@ -16,6 +16,9 @@ pub use flex::*;
|
||||||
mod sized_box;
|
mod sized_box;
|
||||||
pub use sized_box::*;
|
pub use sized_box::*;
|
||||||
|
|
||||||
|
mod spinner;
|
||||||
|
pub use spinner::*;
|
||||||
|
|
||||||
mod label;
|
mod label;
|
||||||
pub use label::*;
|
pub use label::*;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2024 the Xilem Authors
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use masonry::{widget, Color};
|
||||||
|
use xilem_core::{Mut, ViewMarker};
|
||||||
|
|
||||||
|
use crate::{MessageResult, Pod, View, ViewCtx, ViewId};
|
||||||
|
|
||||||
|
/// An indefinite spinner.
|
||||||
|
///
|
||||||
|
/// This can be used to display that progress is happening on some process,
|
||||||
|
/// but that the exact status is not known.
|
||||||
|
///
|
||||||
|
/// The underlying widget is the Masonry [Spinner](widget::Spinner).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # use xilem::{view::{spinner, flex}, WidgetView, core::one_of::Either};
|
||||||
|
/// # struct ApiClient;
|
||||||
|
/// # struct RequestState { pending: bool }
|
||||||
|
/// # impl RequestState {
|
||||||
|
/// # fn request_result(&mut self) -> impl WidgetView<ApiClient> { flex(()) }
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// fn show_request_outcome(data: &mut RequestState) -> impl WidgetView<ApiClient> {
|
||||||
|
/// if data.pending {
|
||||||
|
/// Either::A(spinner())
|
||||||
|
/// } else {
|
||||||
|
/// Either::B(data.request_result())
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn spinner() -> Spinner {
|
||||||
|
Spinner { color: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`View`] created by [`spinner`].
|
||||||
|
///
|
||||||
|
/// See `spinner`'s docs for more details.
|
||||||
|
pub struct Spinner {
|
||||||
|
color: Option<Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spinner {
|
||||||
|
/// Set the color for this spinner.
|
||||||
|
pub fn color(mut self, color: impl Into<Color>) -> Self {
|
||||||
|
self.color = Some(color.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewMarker for Spinner {}
|
||||||
|
impl<State, Action> View<State, Action, ViewCtx> for Spinner {
|
||||||
|
type Element = Pod<widget::Spinner>;
|
||||||
|
type ViewState = ();
|
||||||
|
|
||||||
|
fn build(&self, _: &mut ViewCtx) -> (Self::Element, Self::ViewState) {
|
||||||
|
(Pod::new(widget::Spinner::new()), ())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rebuild<'el>(
|
||||||
|
&self,
|
||||||
|
prev: &Self,
|
||||||
|
(): &mut Self::ViewState,
|
||||||
|
_: &mut ViewCtx,
|
||||||
|
mut element: Mut<'el, Self::Element>,
|
||||||
|
) -> Mut<'el, Self::Element> {
|
||||||
|
if prev.color != self.color {
|
||||||
|
match self.color {
|
||||||
|
Some(color) => element.set_color(color),
|
||||||
|
None => element.reset_color(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
element
|
||||||
|
}
|
||||||
|
|
||||||
|
fn teardown(&self, (): &mut Self::ViewState, _: &mut ViewCtx, _: Mut<'_, Self::Element>) {}
|
||||||
|
|
||||||
|
fn message(
|
||||||
|
&self,
|
||||||
|
(): &mut Self::ViewState,
|
||||||
|
_: &[ViewId],
|
||||||
|
message: xilem_core::DynMessage,
|
||||||
|
_: &mut State,
|
||||||
|
) -> MessageResult<Action> {
|
||||||
|
tracing::error!("Message arrived in Label::message, but Label doesn't consume any messages, this is a bug");
|
||||||
|
MessageResult::Stale(message)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue