chore(core/rust): Simplify LayoutObj

pull/2137/head
Jan Pochyla 2 years ago committed by matejcik
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…
Cancel
Save