diff --git a/core/embed/rust/src/ui/layout/obj.rs b/core/embed/rust/src/ui/layout/obj.rs index 034777b58..7fd6598e6 100644 --- a/core/embed/rust/src/ui/layout/obj.rs +++ b/core/embed/rust/src/ui/layout/obj.rs @@ -43,12 +43,32 @@ where } } -/// In order to store any type of component in a layout, we need to access it -/// through an object-safe trait. `Component` itself is not object-safe because -/// of `Component::Msg` associated type. `ObjComponent` is a simple object-safe -/// wrapping trait that is implemented for all components where `Component::Msg` -/// can be converted to `Obj` through the `ComponentMsgObj` trait. -pub trait ObjComponent { +#[cfg(feature = "ui_debug")] +mod maybe_trace { + pub trait MaybeTrace: crate::trace::Trace {} + impl MaybeTrace for T where T: crate::trace::Trace {} +} + +#[cfg(not(feature = "ui_debug"))] +mod maybe_trace { + pub trait MaybeTrace {} + impl MaybeTrace for T {} +} + +/// Stand-in for the optionally-compiled trait `Trace`. +/// If UI debugging is enabled, `MaybeTrace` implies `Trace` and is implemented +/// for everything that implements Trace. If disabled, `MaybeTrace` is +/// implemented for everything. +use maybe_trace::MaybeTrace; + +/// Object-safe interface between trait `Component` and MicroPython world. It +/// converts the result of `Component::event` into `Obj` via the +/// `ComponentMsgObj` trait, in order to easily return the value to Python. It +/// also optionally implies `Trace` for UI debugging. +/// Note: we need to use an object-safe trait in order to store it in a `Gc` field. `Component` itself is not object-safe because of `Component::Msg` +/// associated type. +pub trait ObjComponent: MaybeTrace { fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result; fn obj_paint(&mut self); fn obj_bounds(&self, sink: &mut dyn FnMut(Rect)); @@ -56,7 +76,7 @@ pub trait ObjComponent { impl ObjComponent for Child where - T: ComponentMsgObj, + T: ComponentMsgObj + MaybeTrace, { fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result { if let Some(msg) = self.event(ctx, event) { @@ -75,22 +95,6 @@ where } } -#[cfg(feature = "ui_debug")] -mod maybe_trace { - pub trait ObjComponentTrace: super::ObjComponent + crate::trace::Trace {} - impl ObjComponentTrace for T where T: super::ObjComponent + crate::trace::Trace {} -} - -#[cfg(not(feature = "ui_debug"))] -mod maybe_trace { - pub trait ObjComponentTrace: super::ObjComponent {} - impl ObjComponentTrace for T where T: super::ObjComponent {} -} - -/// Trait that combines `ObjComponent` with `Trace` if `ui_debug` is enabled, -/// and pure `ObjComponent` otherwise. -use maybe_trace::ObjComponentTrace; - /// `LayoutObj` is a GC-allocated object exported to MicroPython, with type /// `LayoutObj::obj_type()`. It wraps a root component through the /// `ObjComponent` trait. @@ -101,17 +105,20 @@ pub struct LayoutObj { } struct LayoutObjInner { - root: Gc, + root: Gc, event_ctx: EventCtx, timer_fn: Obj, } impl LayoutObj { /// Create a new `LayoutObj`, wrapping a root component. - pub fn new(root: impl ObjComponentTrace + 'static) -> Result, Error> { + pub fn new(root: impl ComponentMsgObj + MaybeTrace + 'static) -> Result, Error> { + // Let's wrap the root component into a `Child` to maintain the top-level + // invalidation logic. + let wrapped_root = Child::new(root); // SAFETY: We are coercing GC-allocated sized ptr into an unsized one. let root = - unsafe { Gc::from_raw(Gc::into_raw(Gc::new(root)?) as *mut dyn ObjComponentTrace) }; + unsafe { Gc::from_raw(Gc::into_raw(Gc::new(wrapped_root)?) as *mut dyn ObjComponent) }; Gc::new(Self { base: Self::obj_type().as_base(), diff --git a/core/embed/rust/src/ui/model_t1/layout.rs b/core/embed/rust/src/ui/model_t1/layout.rs index 3c3d353af..d1e350754 100644 --- a/core/embed/rust/src/ui/model_t1/layout.rs +++ b/core/embed/rust/src/ui/model_t1/layout.rs @@ -3,7 +3,7 @@ use core::convert::TryInto; use crate::{ micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr}, ui::{ - component::{text::paragraphs::Paragraphs, Child, FormattedText}, + component::{text::paragraphs::Paragraphs, FormattedText}, display, layout::obj::LayoutObj, }, @@ -44,7 +44,7 @@ extern "C" fn ui_layout_new_confirm_action( let right = verb .map(|label| |area, pos| Button::with_text(area, pos, label, theme::button_default())); - let obj = LayoutObj::new(Child::new(Frame::new(display::screen(), title, |area| { + let obj = LayoutObj::new(Frame::new(display::screen(), title, |area| { ButtonPage::new( area, |area| { @@ -54,7 +54,7 @@ extern "C" fn ui_layout_new_confirm_action( }, theme::BG, ) - })))?; + }))?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -72,7 +72,7 @@ extern "C" fn ui_layout_new_confirm_text( let description: Option = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; - let obj = LayoutObj::new(Child::new(Frame::new(display::screen(), title, |area| { + let obj = LayoutObj::new(Frame::new(display::screen(), title, |area| { ButtonPage::new( area, |area| { @@ -85,7 +85,7 @@ extern "C" fn ui_layout_new_confirm_text( }, theme::BG, ) - })))?; + }))?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -125,7 +125,7 @@ mod tests { #[test] fn trace_example_layout() { - let layout = Child::new(Dialog::new( + let layout = Dialog::new( display::screen(), |area| { FormattedText::new::( @@ -136,7 +136,7 @@ mod tests { }, Some(|area, pos| Button::with_text(area, pos, "Left", theme::button_cancel())), Some(|area, pos| Button::with_text(area, pos, "Right", theme::button_default())), - )); + ); assert_eq!( trace(&layout), r#" left: