parent
3260386292
commit
cc450203e7
@ -0,0 +1,43 @@
|
||||
use crate::time;
|
||||
|
||||
pub struct AnimTimer {
|
||||
base: Option<time::Instant>,
|
||||
limit: Option<time::Duration>,
|
||||
}
|
||||
|
||||
impl AnimTimer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: None,
|
||||
limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.base = None;
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
self.base = Some(time::Instant::now());
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.base = None;
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.base.is_some()
|
||||
}
|
||||
|
||||
pub fn elapsed(&self) -> f32 {
|
||||
if let Some(t) = self.base {
|
||||
time::Instant::now()
|
||||
.checked_duration_since(t)
|
||||
.unwrap()
|
||||
.to_millis() as f32
|
||||
/ 1000.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
use super::anim_timer::AnimTimer;
|
||||
use crate::ui::{
|
||||
component::Event,
|
||||
display::{self, Color, Font},
|
||||
event::TouchEvent,
|
||||
geometry::{Offset, Point, Rect},
|
||||
shape::{self, Viewport},
|
||||
};
|
||||
|
||||
use crate::{time, 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: time::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() as f32 / 1.0).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()
|
||||
}
|
||||
|
||||
// Returns the split point between two screens
|
||||
// (ranging from -240 to 0)
|
||||
fn anim_screen_transition(timer: &AnimTimer) -> 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),
|
||||
)
|
||||
.repeat(10.0);
|
||||
|
||||
anim.eval(timer.elapsed()) as i16
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn drawlib_demo() {
|
||||
let screen1 = build_screen1().unwrap();
|
||||
let screen2 = build_screen2().unwrap();
|
||||
|
||||
let mut timer = AnimTimer::new();
|
||||
timer.start();
|
||||
|
||||
loop {
|
||||
if let Some(e) = touch_event() {
|
||||
screen1.obj_event(Event::Touch(e)).unwrap();
|
||||
screen2.obj_event(Event::Touch(e)).unwrap();
|
||||
}
|
||||
|
||||
let split = anim_screen_transition(&timer);
|
||||
|
||||
display::sync();
|
||||
|
||||
let start_time = time::Instant::now();
|
||||
|
||||
screen1.obj_render(Offset::x(split));
|
||||
screen2.obj_render(Offset::x(240 + split));
|
||||
|
||||
let duration = time::Instant::now()
|
||||
.checked_duration_since(start_time)
|
||||
.unwrap();
|
||||
|
||||
render_time_overlay(duration);
|
||||
|
||||
display::refresh();
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
pub mod anim_timer;
|
||||
pub mod demo_core;
|
||||
pub mod screen1;
|
||||
pub mod screen2;
|
@ -0,0 +1,129 @@
|
||||
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::{
|
||||
error::Error,
|
||||
micropython::{gc::Gc, obj::Obj},
|
||||
};
|
||||
use pareen;
|
||||
|
||||
use super::anim_timer::AnimTimer;
|
||||
|
||||
pub struct Screen1 {
|
||||
jump_time: AnimTimer,
|
||||
}
|
||||
|
||||
impl Screen1 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
jump_time: AnimTimer::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Screen1 {
|
||||
fn anim_ball_coords(&self) -> Point {
|
||||
let x_anim = pareen::constant(30.0).seq_ease_out(
|
||||
0.0,
|
||||
pareen::easer::functions::Cubic,
|
||||
1.5,
|
||||
pareen::constant(210.0),
|
||||
);
|
||||
|
||||
let y_anim = pareen::constant(30.0).seq_ease_out(
|
||||
0.0,
|
||||
pareen::easer::functions::Bounce,
|
||||
1.5,
|
||||
pareen::constant(210.0),
|
||||
);
|
||||
|
||||
let t = self.jump_time.elapsed();
|
||||
|
||||
Point::new(x_anim.eval(t) as i16, y_anim.eval(t) as i16)
|
||||
}
|
||||
|
||||
fn anim_text_coords(&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.jump_time.elapsed();
|
||||
|
||||
Point::new(x_anim.eval(t) as i16, 120)
|
||||
}
|
||||
}
|
||||
|
||||
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.jump_time.start();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
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.anim_text_coords(), "Touch to start")
|
||||
.with_font(Font::BOLD)
|
||||
.render(target);
|
||||
|
||||
shape::Circle::new(self.anim_ball_coords(), 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,134 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display::Color,
|
||||
event::TouchEvent,
|
||||
geometry::{Offset, 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 super::anim_timer::AnimTimer;
|
||||
|
||||
pub struct Screen1 {
|
||||
jump_time: AnimTimer,
|
||||
}
|
||||
|
||||
impl Screen1 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
jump_time: {
|
||||
let mut timer = AnimTimer::new();
|
||||
timer.start();
|
||||
timer
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Screen1 {
|
||||
fn anim_radius(&self) -> (i16, i16, i16) {
|
||||
let r1_anim = pareen::circle()
|
||||
.cos()
|
||||
.scale_min_max(60f32, 64f32)
|
||||
.repeat(1.0f32);
|
||||
|
||||
let r2_anim = pareen::circle()
|
||||
.cos()
|
||||
.scale_min_max(52f32, 58f32)
|
||||
.squeeze(0f32..=0.5f32)
|
||||
.repeat(0.5f32);
|
||||
|
||||
let r3_anim = pareen::circle()
|
||||
.cos()
|
||||
.scale_min_max(40f32, 50f32)
|
||||
.squeeze(0f32..=2f32)
|
||||
.repeat(2f32);
|
||||
|
||||
let t = self.jump_time.elapsed();
|
||||
|
||||
(
|
||||
r1_anim.eval(t) as i16,
|
||||
r2_anim.eval(t) as i16,
|
||||
r3_anim.eval(t) as i16,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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.jump_time.start();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
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.anim_radius();
|
||||
|
||||
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