parent
1116c70527
commit
9bd011c098
@ -0,0 +1,125 @@
|
||||
use crate::ui::{
|
||||
component::{Event, TimerToken},
|
||||
display::{self, Color, Font},
|
||||
event::TouchEvent,
|
||||
geometry::{Offset, Point, Rect},
|
||||
shape::{self, Viewport},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
time::{Duration, Stopwatch},
|
||||
trezorhal::io::io_touch_read,
|
||||
};
|
||||
use core::fmt::Write;
|
||||
use heapless::String;
|
||||
|
||||
use super::{screen1::build_screen1, screen2::build_screen2};
|
||||
|
||||
fn render_time_overlay(duration: Duration) {
|
||||
shape::render_on_display(
|
||||
Some(Viewport::new(Rect::new(
|
||||
Point::new(200, 0),
|
||||
Point::new(240, 20),
|
||||
))),
|
||||
Some(Color::rgb(0, 0, 255)),
|
||||
|target| {
|
||||
let text_color = Color::rgb(255, 255, 0);
|
||||
|
||||
let mut info = String::<128>::new();
|
||||
write!(info, "{}", duration.to_millis()).unwrap();
|
||||
|
||||
let font = Font::NORMAL;
|
||||
let pt = Point::new(200, font.vert_center(0, 20, "A"));
|
||||
shape::Text::new(pt, info.as_str())
|
||||
.with_fg(text_color)
|
||||
.with_font(font)
|
||||
.render(target);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn touch_event() -> Option<TouchEvent> {
|
||||
let event = io_touch_read();
|
||||
if event == 0 {
|
||||
return None;
|
||||
}
|
||||
let event_type = event >> 24;
|
||||
let ex = ((event >> 12) & 0xFFF) as i16;
|
||||
let ey = (event & 0xFFF) as i16;
|
||||
|
||||
TouchEvent::new(event_type, ex as _, ey as _).ok()
|
||||
}
|
||||
|
||||
// Animates the split point between two screens
|
||||
// (ranging from -240 to 0)
|
||||
|
||||
#[derive(Default)]
|
||||
struct ScreenTransitionAnim {
|
||||
timer: Stopwatch,
|
||||
}
|
||||
|
||||
impl ScreenTransitionAnim {
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.timer.is_running()
|
||||
}
|
||||
|
||||
pub fn eval(&self) -> i16 {
|
||||
let anim = pareen::constant(0.0)
|
||||
.seq_ease_in_out(
|
||||
0.0,
|
||||
pareen::easer::functions::Back,
|
||||
1.5,
|
||||
pareen::constant(-240.0),
|
||||
)
|
||||
.seq_ease_in_out(
|
||||
5.0,
|
||||
pareen::easer::functions::Back,
|
||||
0.75,
|
||||
pareen::constant(0.0),
|
||||
)
|
||||
.dur(10.0)
|
||||
.repeat();
|
||||
|
||||
anim.eval(self.timer.elapsed().into()) as i16
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn void() {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn drawlib_demo() {
|
||||
let screen1 = build_screen1().unwrap();
|
||||
let screen2 = build_screen2().unwrap();
|
||||
|
||||
screen1.obj_event(Event::Attach).unwrap();
|
||||
screen2.obj_event(Event::Attach).unwrap();
|
||||
|
||||
let mut anim = ScreenTransitionAnim::default();
|
||||
anim.timer.start();
|
||||
|
||||
loop {
|
||||
//screen1.obj_event(Event::Timer(TimerToken::INVALID)).unwrap();
|
||||
//screen2.obj_event(Event::Timer(TimerToken::INVALID)).unwrap();
|
||||
|
||||
if let Some(e) = touch_event() {
|
||||
screen1.obj_event(Event::Touch(e)).unwrap();
|
||||
screen2.obj_event(Event::Touch(e)).unwrap();
|
||||
}
|
||||
|
||||
let split = anim.eval();
|
||||
|
||||
display::sync();
|
||||
|
||||
let stopwatch = Stopwatch::new_started();
|
||||
|
||||
//screen1.obj_paint_if_requested();
|
||||
|
||||
screen1.obj_render(Offset::x(split));
|
||||
screen2.obj_render(Offset::x(240 + split));
|
||||
|
||||
render_time_overlay(stopwatch.elapsed());
|
||||
|
||||
display::refresh();
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
pub mod demo_core;
|
||||
pub mod screen1;
|
||||
pub mod screen2;
|
@ -0,0 +1,158 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display::{font::Font, Color},
|
||||
event::TouchEvent,
|
||||
geometry::{Point, Rect},
|
||||
layout::obj::{ComponentMsgObj, LayoutObj},
|
||||
model_tt::{component::Frame, theme},
|
||||
shape,
|
||||
shape::Renderer,
|
||||
};
|
||||
|
||||
use crate::time::{Duration, Stopwatch};
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::{gc::Gc, obj::Obj},
|
||||
};
|
||||
use pareen;
|
||||
|
||||
#[derive(Default)]
|
||||
struct BallAnim {
|
||||
pub timer: Stopwatch,
|
||||
}
|
||||
|
||||
impl BallAnim {
|
||||
const DURATION: f32 = 1.5;
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.timer.is_running_within(Duration::from(Self::DURATION))
|
||||
}
|
||||
|
||||
pub fn eval(&self) -> Point {
|
||||
let x_anim = pareen::constant(30.0).seq_ease_out(
|
||||
0.0,
|
||||
pareen::easer::functions::Cubic,
|
||||
Self::DURATION,
|
||||
pareen::constant(210.0),
|
||||
);
|
||||
|
||||
let y_anim = pareen::constant(30.0).seq_ease_out(
|
||||
0.0,
|
||||
pareen::easer::functions::Bounce,
|
||||
Self::DURATION,
|
||||
pareen::constant(210.0),
|
||||
);
|
||||
|
||||
let t = self.timer.elapsed().into();
|
||||
|
||||
Point::new(x_anim.eval(t) as i16, y_anim.eval(t) as i16)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TextAnim {
|
||||
pub timer: Stopwatch,
|
||||
}
|
||||
|
||||
impl TextAnim {
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.timer.is_running_within(Duration::from(3.4))
|
||||
}
|
||||
|
||||
pub fn eval(&self) -> Point {
|
||||
let x_anim = pareen::constant(30.0)
|
||||
.seq_ease_in(
|
||||
0.0,
|
||||
pareen::easer::functions::Cubic,
|
||||
0.3,
|
||||
pareen::constant(240.0),
|
||||
)
|
||||
.seq_ease_out(
|
||||
2.5,
|
||||
pareen::easer::functions::Elastic,
|
||||
0.9,
|
||||
pareen::constant(30.0),
|
||||
);
|
||||
|
||||
let t = self.timer.elapsed().into();
|
||||
|
||||
Point::new(x_anim.eval(t) as i16, 120)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Screen1 {
|
||||
ball_anim: BallAnim,
|
||||
text_anim: TextAnim,
|
||||
}
|
||||
|
||||
impl Screen1 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ball_anim: Default::default(),
|
||||
text_anim: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Screen1 {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
match event {
|
||||
Event::Touch(TouchEvent::TouchStart(_)) => {
|
||||
self.ball_anim.timer.start();
|
||||
self.text_anim.timer.start();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if self.ball_anim.is_active() && self.text_anim.is_active() {
|
||||
ctx.request_anim_frame();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
let r = Rect::new(Point::new(10, 30), Point::new(230, 230));
|
||||
shape::Bar::new(r)
|
||||
.with_bg(Color::rgb(0, 20, 40))
|
||||
.render(target);
|
||||
|
||||
shape::Text::new(self.text_anim.eval(), "Touch to start")
|
||||
.with_font(Font::BOLD)
|
||||
.render(target);
|
||||
|
||||
shape::Circle::new(self.ball_anim.eval(), 16)
|
||||
.with_bg(Color::rgb(96, 128, 128))
|
||||
.render(target);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for Screen1 {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Demo screen");
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentMsgObj for Screen1 {
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_screen1() -> Result<Gc<LayoutObj>, Error> {
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
"Animation Demo".into(),
|
||||
Screen1::new(),
|
||||
))
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display::Color,
|
||||
geometry::{Point, Rect},
|
||||
layout::obj::{ComponentMsgObj, LayoutObj},
|
||||
model_tt::{component::Frame, theme},
|
||||
shape,
|
||||
shape::Renderer,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::{gc::Gc, obj::Obj},
|
||||
};
|
||||
use pareen;
|
||||
|
||||
use crate::time::Stopwatch;
|
||||
|
||||
#[derive(Default)]
|
||||
struct LoaderAnim {
|
||||
pub timer: Stopwatch,
|
||||
}
|
||||
|
||||
impl LoaderAnim {
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.timer.is_running()
|
||||
}
|
||||
|
||||
pub fn eval(&self) -> (i16, i16, i16) {
|
||||
let r1_anim = pareen::circle().cos().scale_min_max(60.0, 64.0).repeat(1.0);
|
||||
|
||||
let r2_anim = pareen::circle()
|
||||
.cos()
|
||||
.scale_min_max(52.0, 58.0)
|
||||
.squeeze(0.0..=0.5)
|
||||
.repeat(0.5);
|
||||
|
||||
let r3_anim = pareen::circle()
|
||||
.cos()
|
||||
.scale_min_max(40.0, 50.0)
|
||||
.squeeze(0.0..=2.0)
|
||||
.repeat(2.0);
|
||||
|
||||
let t = self.timer.elapsed().into();
|
||||
|
||||
(
|
||||
r1_anim.eval(t) as i16,
|
||||
r2_anim.eval(t) as i16,
|
||||
r3_anim.eval(t) as i16,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Screen1 {
|
||||
loader_anim: LoaderAnim,
|
||||
}
|
||||
|
||||
impl Screen1 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
loader_anim: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Screen1 {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
match event {
|
||||
Event::Attach => {
|
||||
self.loader_anim.timer.start();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if self.loader_anim.is_active() {
|
||||
ctx.request_anim_frame();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
let bg_color = Color::rgb(40, 20, 0);
|
||||
|
||||
let r = Rect::new(Point::new(10, 30), Point::new(230, 230));
|
||||
shape::Bar::new(r).with_bg(bg_color).render(target);
|
||||
|
||||
let center = Point::new(120, 120);
|
||||
|
||||
let (r1, r2, r3) = self.loader_anim.eval();
|
||||
|
||||
shape::Circle::new(center, r1)
|
||||
.with_thickness(2)
|
||||
.with_bg(bg_color)
|
||||
.with_fg(Color::rgb(0, 120, 0))
|
||||
.render(target);
|
||||
|
||||
shape::Circle::new(center, r2)
|
||||
.with_thickness(3)
|
||||
.with_bg(bg_color)
|
||||
.with_fg(Color::rgb(0, 120, 30))
|
||||
.render(target);
|
||||
|
||||
shape::Circle::new(center, r3)
|
||||
.with_thickness(4)
|
||||
.with_bg(bg_color)
|
||||
.with_fg(Color::rgb(0, 120, 30))
|
||||
.render(target);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for Screen1 {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Demo screen");
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentMsgObj for Screen1 {
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_screen2() -> Result<Gc<LayoutObj>, Error> {
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
"Animation Demo 2".into(),
|
||||
Screen1::new(),
|
||||
))
|
||||
}
|
Loading…
Reference in new issue