1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-14 03:30:02 +00:00

refactor(core/ui): improve LayoutObj initialization

* RequestPaint message is sent at construction time to force calculation
  of number of pages
* given that Attach corresponds to "start the layout" message, Child now
  responds to Attach the same way it responds to RequestPaint, by
  force-repainting everything.
This commit is contained in:
matejcik 2023-11-06 11:42:24 +01:00 committed by M1nd3r
parent b579796dd6
commit 98a6a2149c
2 changed files with 34 additions and 31 deletions

View File

@ -143,7 +143,7 @@ where
// Handle the internal invalidation event here, so components don't have to. We
// still pass it inside, so the event propagates correctly to all components in
// the sub-tree.
if let Event::RequestPaint = event {
if matches!(event, Event::RequestPaint | Event::Attach(_)) {
ctx.request_paint();
}
c.event(ctx, event)
@ -371,8 +371,9 @@ pub enum Event {
Timer(TimerToken),
/// Advance progress bar. Progress screens only.
Progress(u16, TString<'static>),
/// Component has been attached to component tree. This event is sent once
/// before any other events.
/// Component has been attached to component tree, all children should
/// prepare for painting and/or start their timers.
/// This event is sent once before any other events.
Attach(AttachType),
/// Internally-handled event to inform all `Child` wrappers in a sub-tree to
/// get scheduled for painting.
@ -431,9 +432,8 @@ impl EventCtx {
Self {
timers: Vec::new(),
next_token: Self::STARTING_TIMER_TOKEN,
place_requested: true, // We need to perform a place pass in the beginning.
paint_requested: false, /* We also need to paint, but this is supplemented by
* `Child::marked_for_paint` being true. */
place_requested: false,
paint_requested: false,
anim_frame_scheduled: false,
page_count: None,
button_request: None,
@ -536,17 +536,13 @@ impl EventCtx {
}
pub fn clear(&mut self) {
self.place_requested = false;
self.paint_requested = false;
self.anim_frame_scheduled = false;
self.page_count = None;
#[cfg(feature = "ui_debug")]
assert!(self.button_request.is_none());
self.button_request = None;
self.root_repaint_requested = false;
self.swipe_disable_req = false;
self.swipe_enable_req = false;
self.transition_out = None;
// replace self with a new instance, keeping only the fields we care about
*self = Self {
next_token: self.next_token,
..Self::new()
}
}
fn register_timer(&mut self, token: TimerToken, duration: Duration) {

View File

@ -128,6 +128,7 @@ where
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "debug", derive(ufmt::derive::uDebug))]
enum Repaint {
None,
Partial,
@ -158,14 +159,22 @@ impl LayoutObjInner {
pub fn new(root: impl ObjComponent + 'static) -> Result<Self, Error> {
let root = GcBox::new(root)?;
Ok(Self {
let mut new = Self {
root: Some(gc::coerce!(ObjComponent, root)),
event_ctx: EventCtx::new(),
timer_fn: Obj::const_none(),
page_count: 1,
repaint: Repaint::Full,
transition_out: AttachType::Initial,
})
};
// invoke the initial placement
new.root_mut().obj_place(constant::screen());
// cause a repaint pass to update the number of pages
let msg = new.obj_event(Event::RequestPaint);
assert!(matches!(msg, Ok(s) if s == Obj::const_none()));
Ok(new)
}
fn obj_delete(&mut self) {
@ -188,14 +197,14 @@ impl LayoutObjInner {
fn obj_request_repaint(&mut self) {
self.repaint = Repaint::Full;
let mut dummy_ctx = EventCtx::new();
let mut event_ctx = EventCtx::new();
let paint_msg = self
.root_mut()
.obj_event(&mut dummy_ctx, Event::RequestPaint);
.obj_event(&mut event_ctx, Event::RequestPaint);
// paint_msg must not be an error and it must not return a result
assert!(matches!(paint_msg, Ok(s) if s == Obj::const_none()));
// there must be no timers set
assert!(dummy_ctx.pop_timer().is_none());
assert!(event_ctx.pop_timer().is_none());
}
/// Run an event pass over the component tree. After the traversal, any
@ -204,21 +213,22 @@ impl LayoutObjInner {
/// an error, `Ok` with the message otherwise.
fn obj_event(&mut self, event: Event) -> Result<Obj, Error> {
let root = unwrap!(self.root.as_mut());
// Place the root component on the screen in case it was previously requested.
if self.event_ctx.needs_place() {
root.obj_place(constant::screen());
}
// Clear the leftover flags from the previous event pass.
// Get the event context ready for a new event
self.event_ctx.clear();
// Send the event down the component tree. Bail out in case of failure.
let msg = root.obj_event(&mut self.event_ctx, event)?;
// Place the root component on the screen in case it was requested.
if self.event_ctx.needs_place() {
root.obj_place(constant::screen());
}
// Check if we should repaint next time
if self.event_ctx.needs_repaint_root() {
self.obj_request_repaint();
} else if self.event_ctx.needs_repaint() {
} else if self.event_ctx.needs_repaint() && self.repaint == Repaint::None {
self.repaint = Repaint::Partial;
}
@ -236,10 +246,12 @@ impl LayoutObjInner {
}
}
// Update page count if it changed
if let Some(count) = self.event_ctx.page_count() {
self.page_count = count as u16;
}
// Update outgoing transition if set
if let Some(t) = self.event_ctx.get_transition_out() {
self.transition_out = t;
}
@ -254,11 +266,6 @@ impl LayoutObjInner {
display::clear();
}
// Place the root component on the screen in case it was previously requested.
if self.event_ctx.needs_place() {
self.root_mut().obj_place(constant::screen());
}
display::sync();
if self.repaint != Repaint::None {