Skip to main content

AnimatedWidget

Trait AnimatedWidget 

Source
pub trait AnimatedWidget {
    type State;

    // Required method
    fn render(
        &self,
        surface: &mut Surface<'_>,
        state: &mut Self::State,
        frame: &Frame,
    );
}
Expand description

Like StatefulWidget, but for widgets whose state evolves with wall-clock time.

Covers state like crate::ScrollState’s momentum/rubber-band physics or a Tween-driven transition, which advance on their own rather than only in response to input.

StatefulWidget has no way to reach the Frame an App already receives every frame, so a widget with time-based state has nowhere to advance it: not in render (no Frame parameter), and not in a second, app-defined call, because nothing enforces that call happening before render rather than after it: the two orders differ by one frame of animation, silently. AnimatedWidget closes that gap with a single call that both advances and draws, so the ordering question doesn’t arise. See Scrollbar’s impl for a worked example: it ticks crate::ScrollState’s physics forward by frame.delta, then draws the thumb at the resulting offset, in one call.

A sibling of StatefulWidget, not a replacement: a widget with no time-based state (a selection index that only moves on a keypress, say) has no use for frame and should keep implementing StatefulWidget instead. Nothing stops a widget from implementing both, the way Scrollbar implements Widget (a plain, offset-at-a-fixed-value track+thumb) alongside this trait (an animated one driven by crate::ScrollState).

§Examples

use core::time::Duration;
use retroglyph_core::app::Frame;
use retroglyph_core::grid::{Grid, Rect};
use retroglyph_ui::{AnimatedWidget, Surface};

struct Blinker;

impl AnimatedWidget for Blinker {
    type State = Duration;

    fn render(&self, surface: &mut Surface<'_>, state: &mut Self::State, frame: &Frame) {
        *state += frame.delta;
        let on = state.as_millis() / 500 % 2 == 0;
        surface.put((0, 0), if on { '*' } else { ' ' }, retroglyph_core::color::Style::new());
    }
}

let area = Rect::new(0, 0, 4, 1);
let mut grid = Grid::new(4, 1);
let mut state = Duration::ZERO;
let frame = Frame { delta: Duration::from_millis(100), frame: 0 };
Blinker.render(&mut Surface::new(&mut grid, area, 0), &mut state, &frame);
assert_eq!(state, Duration::from_millis(100));

Required Associated Types§

Source

type State

The externally owned, time-evolving state this widget reads and/or updates while rendering, e.g. crate::ScrollState.

Required Methods§

Source

fn render( &self, surface: &mut Surface<'_>, state: &mut Self::State, frame: &Frame, )

Advances state by frame.delta, then draws this widget into surface.area(), both in the same call, so there’s exactly one place, not two independently ordered ones, where time-based state moves forward.

Implementors§