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