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
/// 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<T> MaybeTrace for T where T: crate::trace::Trace {}
}
#[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_paint(&mut self);
fn obj_bounds(&self, sink: &mut dyn FnMut(Rect));
@ -56,7 +76,7 @@ pub trait ObjComponent {
impl<T> ObjComponent for Child<T>
where
T: ComponentMsgObj,
T: ComponentMsgObj + MaybeTrace,
{
fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result<Obj, Error> {
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::obj_type()`. It wraps a root component through the
/// `ObjComponent` trait.
@ -101,17 +105,20 @@ pub struct LayoutObj {
}
struct LayoutObjInner {
root: Gc<dyn ObjComponentTrace>,
root: Gc<dyn ObjComponent>,
event_ctx: EventCtx,
timer_fn: Obj,
}
impl LayoutObj {
/// 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.
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(),

@ -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<Buffer> =
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::<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, "Right", theme::button_default())),
));
);
assert_eq!(
trace(&layout),
r#"<Dialog content:<Text content:Testing text layout,
@ -148,7 +148,7 @@ arameters! > left:<Button text:Left > right:<Button text:Right > >"#
#[test]
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(
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, "Right", theme::button_default())),
)
}));
});
assert_eq!(
trace(&layout),
r#"<Frame title:Please confirm content:<Dialog content:<Text content:Testing text layout,

@ -52,13 +52,13 @@ where
#[no_mangle]
extern "C" fn ui_layout_new_example(_param: Obj) -> Obj {
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>(
area,
"Testing text layout, with some text, and some more text. And {param}",
)
.with(b"param", b"parameters!")
})))?;
}))?;
Ok(layout.into())
};
unsafe { util::try_or_raise(block) }

Loading…
Cancel
Save