mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-06 10:16:07 +00:00
chore(core/rust): Simplify LayoutObj
This commit is contained in:
parent
2b6f1a4d5c
commit
4c027a688c
@ -43,12 +43,32 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In order to store any type of component in a layout, we need to access it
|
#[cfg(feature = "ui_debug")]
|
||||||
/// through an object-safe trait. `Component` itself is not object-safe because
|
mod maybe_trace {
|
||||||
/// of `Component::Msg` associated type. `ObjComponent` is a simple object-safe
|
pub trait MaybeTrace: crate::trace::Trace {}
|
||||||
/// wrapping trait that is implemented for all components where `Component::Msg`
|
impl<T> MaybeTrace for T where T: crate::trace::Trace {}
|
||||||
/// can be converted to `Obj` through the `ComponentMsgObj` trait.
|
}
|
||||||
pub trait ObjComponent {
|
|
||||||
|
#[cfg(not(feature = "ui_debug"))]
|
||||||
|
mod maybe_trace {
|
||||||
|
pub trait MaybeTrace {}
|
||||||
|
impl<T> 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<dyn
|
||||||
|
/// T>` 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<Obj, Error>;
|
fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result<Obj, Error>;
|
||||||
fn obj_paint(&mut self);
|
fn obj_paint(&mut self);
|
||||||
fn obj_bounds(&self, sink: &mut dyn FnMut(Rect));
|
fn obj_bounds(&self, sink: &mut dyn FnMut(Rect));
|
||||||
@ -56,7 +76,7 @@ pub trait ObjComponent {
|
|||||||
|
|
||||||
impl<T> ObjComponent for Child<T>
|
impl<T> ObjComponent for Child<T>
|
||||||
where
|
where
|
||||||
T: ComponentMsgObj,
|
T: ComponentMsgObj + MaybeTrace,
|
||||||
{
|
{
|
||||||
fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result<Obj, Error> {
|
fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result<Obj, Error> {
|
||||||
if let Some(msg) = self.event(ctx, event) {
|
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<T> ObjComponentTrace for T where T: super::ObjComponent + crate::trace::Trace {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "ui_debug"))]
|
|
||||||
mod maybe_trace {
|
|
||||||
pub trait ObjComponentTrace: super::ObjComponent {}
|
|
||||||
impl<T> 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` is a GC-allocated object exported to MicroPython, with type
|
||||||
/// `LayoutObj::obj_type()`. It wraps a root component through the
|
/// `LayoutObj::obj_type()`. It wraps a root component through the
|
||||||
/// `ObjComponent` trait.
|
/// `ObjComponent` trait.
|
||||||
@ -101,17 +105,20 @@ pub struct LayoutObj {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct LayoutObjInner {
|
struct LayoutObjInner {
|
||||||
root: Gc<dyn ObjComponentTrace>,
|
root: Gc<dyn ObjComponent>,
|
||||||
event_ctx: EventCtx,
|
event_ctx: EventCtx,
|
||||||
timer_fn: Obj,
|
timer_fn: Obj,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutObj {
|
impl LayoutObj {
|
||||||
/// Create a new `LayoutObj`, wrapping a root component.
|
/// Create a new `LayoutObj`, wrapping a root component.
|
||||||
pub fn new(root: impl ObjComponentTrace + 'static) -> Result<Gc<Self>, Error> {
|
pub fn new(root: impl ComponentMsgObj + MaybeTrace + 'static) -> Result<Gc<Self>, 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.
|
// SAFETY: We are coercing GC-allocated sized ptr into an unsized one.
|
||||||
let root =
|
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 {
|
Gc::new(Self {
|
||||||
base: Self::obj_type().as_base(),
|
base: Self::obj_type().as_base(),
|
||||||
|
@ -3,7 +3,7 @@ use core::convert::TryInto;
|
|||||||
use crate::{
|
use crate::{
|
||||||
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
|
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
|
||||||
ui::{
|
ui::{
|
||||||
component::{text::paragraphs::Paragraphs, Child, FormattedText},
|
component::{text::paragraphs::Paragraphs, FormattedText},
|
||||||
display,
|
display,
|
||||||
layout::obj::LayoutObj,
|
layout::obj::LayoutObj,
|
||||||
},
|
},
|
||||||
@ -44,7 +44,7 @@ extern "C" fn ui_layout_new_confirm_action(
|
|||||||
let right = verb
|
let right = verb
|
||||||
.map(|label| |area, pos| Button::with_text(area, pos, label, theme::button_default()));
|
.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(
|
ButtonPage::new(
|
||||||
area,
|
area,
|
||||||
|area| {
|
|area| {
|
||||||
@ -54,7 +54,7 @@ extern "C" fn ui_layout_new_confirm_action(
|
|||||||
},
|
},
|
||||||
theme::BG,
|
theme::BG,
|
||||||
)
|
)
|
||||||
})))?;
|
}))?;
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
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<Buffer> =
|
let description: Option<Buffer> =
|
||||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_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(
|
ButtonPage::new(
|
||||||
area,
|
area,
|
||||||
|area| {
|
|area| {
|
||||||
@ -85,7 +85,7 @@ extern "C" fn ui_layout_new_confirm_text(
|
|||||||
},
|
},
|
||||||
theme::BG,
|
theme::BG,
|
||||||
)
|
)
|
||||||
})))?;
|
}))?;
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
@ -125,7 +125,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trace_example_layout() {
|
fn trace_example_layout() {
|
||||||
let layout = Child::new(Dialog::new(
|
let layout = Dialog::new(
|
||||||
display::screen(),
|
display::screen(),
|
||||||
|area| {
|
|area| {
|
||||||
FormattedText::new::<theme::T1DefaultText>(
|
FormattedText::new::<theme::T1DefaultText>(
|
||||||
@ -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, "Left", theme::button_cancel())),
|
||||||
Some(|area, pos| Button::with_text(area, pos, "Right", theme::button_default())),
|
Some(|area, pos| Button::with_text(area, pos, "Right", theme::button_default())),
|
||||||
));
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
trace(&layout),
|
trace(&layout),
|
||||||
r#"<Dialog content:<Text content:Testing text layout,
|
r#"<Dialog content:<Text content:Testing text layout,
|
||||||
@ -148,7 +148,7 @@ arameters! > left:<Button text:Left > right:<Button text:Right > >"#
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trace_layout_title() {
|
fn trace_layout_title() {
|
||||||
let layout = Child::new(Frame::new(display::screen(), "Please confirm", |area| {
|
let layout = Frame::new(display::screen(), "Please confirm", |area| {
|
||||||
Dialog::new(
|
Dialog::new(
|
||||||
area,
|
area,
|
||||||
|area| {
|
|area| {
|
||||||
@ -161,7 +161,7 @@ arameters! > left:<Button text:Left > right:<Button text:Right > >"#
|
|||||||
Some(|area, pos| Button::with_text(area, pos, "Left", theme::button_cancel())),
|
Some(|area, pos| Button::with_text(area, pos, "Left", theme::button_cancel())),
|
||||||
Some(|area, pos| Button::with_text(area, pos, "Right", theme::button_default())),
|
Some(|area, pos| Button::with_text(area, pos, "Right", theme::button_default())),
|
||||||
)
|
)
|
||||||
}));
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
trace(&layout),
|
trace(&layout),
|
||||||
r#"<Frame title:Please confirm content:<Dialog content:<Text content:Testing text layout,
|
r#"<Frame title:Please confirm content:<Dialog content:<Text content:Testing text layout,
|
||||||
|
@ -52,13 +52,13 @@ where
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn ui_layout_new_example(_param: Obj) -> Obj {
|
extern "C" fn ui_layout_new_example(_param: Obj) -> Obj {
|
||||||
let block = move || {
|
let block = move || {
|
||||||
let layout = LayoutObj::new(Child::new(HoldToConfirm::new(display::screen(), |area| {
|
let layout = LayoutObj::new(HoldToConfirm::new(display::screen(), |area| {
|
||||||
FormattedText::new::<theme::TTDefaultText>(
|
FormattedText::new::<theme::TTDefaultText>(
|
||||||
area,
|
area,
|
||||||
"Testing text layout, with some text, and some more text. And {param}",
|
"Testing text layout, with some text, and some more text. And {param}",
|
||||||
)
|
)
|
||||||
.with(b"param", b"parameters!")
|
.with(b"param", b"parameters!")
|
||||||
})))?;
|
}))?;
|
||||||
Ok(layout.into())
|
Ok(layout.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_or_raise(block) }
|
unsafe { util::try_or_raise(block) }
|
||||||
|
Loading…
Reference in New Issue
Block a user