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,
|
||||
antialiasing_method: vello::AaConfig::Area,
|
||||
};
|
||||
// TODO: Run this in-between `submit` and `present`.
|
||||
window.pre_present_notify();
|
||||
self.renderer
|
||||
.get_or_insert_with(|| Renderer::new(device, renderer_options).unwrap())
|
||||
.render_to_surface(device, queue, scene_ref, &surface_texture, &render_params)
|
||||
|
@ -420,6 +422,7 @@ impl MasonryState<'_> {
|
|||
.handle_window_event(WindowEvent::Rescale(scale_factor));
|
||||
}
|
||||
WinitWindowEvent::RedrawRequested => {
|
||||
self.render_root.handle_window_event(WindowEvent::AnimFrame);
|
||||
let (scene, tree_update) = self.render_root.redraw();
|
||||
self.render(scene);
|
||||
let WindowState::Rendering {
|
||||
|
|
|
@ -49,11 +49,13 @@ impl Spinner {
|
|||
}
|
||||
}
|
||||
|
||||
const DEFAULT_SPINNER_COLOR: Color = theme::TEXT_COLOR;
|
||||
|
||||
impl Default for Spinner {
|
||||
fn default() -> Self {
|
||||
Spinner {
|
||||
t: 0.0,
|
||||
color: theme::TEXT_COLOR,
|
||||
color: DEFAULT_SPINNER_COLOR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +71,11 @@ impl WidgetMut<'_, Spinner> {
|
|||
self.widget.color = color.into();
|
||||
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 ---
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
use winit::error::EventLoopError;
|
||||
use xilem::{
|
||||
core::one_of::{OneOf, OneOf3},
|
||||
view::{button, flex, label, prose},
|
||||
view::{button, flex, label, prose, sized_box, spinner},
|
||||
EventLoop, WidgetView, Xilem,
|
||||
};
|
||||
|
||||
|
@ -67,6 +67,10 @@ fn app_logic(app_data: &mut StateMachine) -> impl WidgetView<StateMachine> {
|
|||
}),
|
||||
prose(&*app_data.history),
|
||||
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),
|
||||
// TODO: When we have a canvas widget, visualise the entire state machine here.
|
||||
))
|
||||
|
|
|
@ -16,6 +16,9 @@ pub use flex::*;
|
|||
mod sized_box;
|
||||
pub use sized_box::*;
|
||||
|
||||
mod spinner;
|
||||
pub use spinner::*;
|
||||
|
||||
mod 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