1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-22 14:28:07 +00:00

refactor(core/rust): reorganize LayoutObj

* move most actual functionality to LayoutObjInner
* subsume features of top-level Root and Child into LayoutObjInner
(saving ~7 kB of flash because LayoutObjInner is not generic)
* make use of GcBox to drop the top-level component
This commit is contained in:
matejcik 2024-06-26 15:43:53 +02:00 committed by matejcik
parent 2a896c44f6
commit 863dee1a43
3 changed files with 152 additions and 253 deletions

View File

@ -8,7 +8,7 @@ use crate::{
ui::{ ui::{
button_request::{ButtonRequest, ButtonRequestCode}, button_request::{ButtonRequest, ButtonRequestCode},
component::{maybe::PaintOverlapping, MsgMap, PageMap}, component::{maybe::PaintOverlapping, MsgMap, PageMap},
display::{self, Color}, display::Color,
geometry::{Offset, Rect}, geometry::{Offset, Rect},
shape::Renderer, shape::Renderer,
}, },
@ -207,111 +207,6 @@ where
} }
} }
/// Same as `Child` but also handles screen clearing when layout is first
/// painted.
pub struct Root<T> {
inner: Option<Child<T>>,
marked_for_clear: bool,
transition_out: Option<AttachType>,
}
impl<T> Root<T> {
pub fn new(component: T) -> Self {
Self {
inner: Some(Child::new(component)),
marked_for_clear: true,
transition_out: None,
}
}
pub fn inner_mut(&mut self) -> &mut Child<T> {
if let Some(ref mut c) = self.inner {
c
} else {
fatal_error!("Root object is deallocated")
}
}
pub fn inner(&self) -> &Child<T> {
if let Some(ref c) = self.inner {
c
} else {
fatal_error!("Root object is deallocated")
}
}
pub fn skip_paint(&mut self) {
self.inner_mut().skip_paint();
}
pub fn clear_screen(&mut self) {
self.marked_for_clear = true;
}
pub fn get_transition_out(&self) -> Option<AttachType> {
self.transition_out
}
pub fn delete(&mut self) {
self.inner = None;
}
}
impl<T> Component for Root<T>
where
T: Component,
{
type Msg = T::Msg;
fn place(&mut self, bounds: Rect) -> Rect {
self.inner_mut().place(bounds)
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
let msg = self.inner_mut().event(ctx, event);
if ctx.needs_repaint_root() {
self.marked_for_clear = true;
let mut dummy_ctx = EventCtx::new();
let paint_msg = self.inner_mut().event(&mut dummy_ctx, Event::RequestPaint);
assert!(paint_msg.is_none());
assert!(dummy_ctx.timers.is_empty());
}
if let Some(t) = ctx.get_transition_out() {
self.transition_out = Some(t);
}
msg
}
fn paint(&mut self) {
if self.marked_for_clear && self.inner().will_paint() {
self.marked_for_clear = false;
display::clear()
}
self.inner.paint();
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.inner().render(target);
}
#[cfg(feature = "ui_bounds")]
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.inner().bounds(sink)
}
}
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Root<T>
where
T: crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
self.inner().trace(t);
}
}
impl<M, T, U> Component for (T, U) impl<M, T, U> Component for (T, U)
where where
T: Component<Msg = M>, T: Component<Msg = M>,
@ -582,7 +477,7 @@ impl EventCtx {
/// Returns `true` if we should first perform a place traversal before /// Returns `true` if we should first perform a place traversal before
/// processing events or painting. /// processing events or painting.
pub fn needs_place_before_next_event_or_paint(&self) -> bool { pub fn needs_place(&self) -> bool {
self.place_requested self.place_requested
} }
@ -616,6 +511,10 @@ impl EventCtx {
self.root_repaint_requested self.root_repaint_requested
} }
pub fn needs_repaint(&self) -> bool {
self.paint_requested
}
pub fn set_page_count(&mut self, count: usize) { pub fn set_page_count(&mut self, count: usize) {
// #[cfg(feature = "ui_debug")] // #[cfg(feature = "ui_debug")]
// assert!(self.page_count.unwrap_or(count) == count); // assert!(self.page_count.unwrap_or(count) == count);

View File

@ -27,7 +27,7 @@ pub mod text;
pub mod timeout; pub mod timeout;
pub use bar::Bar; pub use bar::Bar;
pub use base::{Child, Component, ComponentExt, Event, EventCtx, Never, Root, TimerToken}; pub use base::{Child, Component, ComponentExt, Event, EventCtx, Never, TimerToken};
pub use border::Border; pub use border::Border;
pub use button_request::{ButtonRequestExt, OneButtonRequest}; pub use button_request::{ButtonRequestExt, OneButtonRequest};
#[cfg(all(feature = "jpeg", feature = "ui_image_buffer", feature = "micropython"))] #[cfg(all(feature = "jpeg", feature = "ui_image_buffer", feature = "micropython"))]

View File

@ -1,6 +1,7 @@
use core::{ use core::{
cell::RefCell, cell::{RefCell, RefMut},
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
ops::{Deref, DerefMut},
}; };
use num_traits::{FromPrimitive, ToPrimitive}; use num_traits::{FromPrimitive, ToPrimitive};
@ -9,7 +10,7 @@ use crate::{
maybe_trace::MaybeTrace, maybe_trace::MaybeTrace,
micropython::{ micropython::{
buffer::StrBuffer, buffer::StrBuffer,
gc::Gc, gc::{self, Gc, GcBox},
macros::{obj_dict, obj_fn_1, obj_fn_2, obj_fn_3, obj_fn_var, obj_map, obj_type}, macros::{obj_dict, obj_fn_1, obj_fn_2, obj_fn_3, obj_fn_var, obj_map, obj_type},
map::Map, map::Map,
obj::{Obj, ObjBase}, obj::{Obj, ObjBase},
@ -20,9 +21,8 @@ use crate::{
time::Duration, time::Duration,
ui::{ ui::{
button_request::ButtonRequest, button_request::ButtonRequest,
component::{Component, Event, EventCtx, Never, Root, TimerToken}, component::{Component, Event, EventCtx, Never, TimerToken},
constant, constant, display,
display::sync,
geometry::Rect, geometry::Rect,
}, },
}; };
@ -77,17 +77,13 @@ pub trait ComponentMsgObj: Component {
pub trait ObjComponent: MaybeTrace { pub trait ObjComponent: MaybeTrace {
fn obj_place(&mut self, bounds: Rect) -> Rect; fn obj_place(&mut self, bounds: Rect) -> Rect;
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) -> bool; fn obj_paint(&mut self);
fn obj_bounds(&self, _sink: &mut dyn FnMut(Rect)) {} fn obj_bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
fn obj_skip_paint(&mut self) {}
fn obj_request_clear(&mut self) {}
fn obj_delete(&mut self) {}
fn obj_get_transition_out(&self) -> Result<Obj, Error>;
} }
impl<T> ObjComponent for Root<T> impl<T> ObjComponent for T
where where
T: ComponentMsgObj + MaybeTrace, T: Component + ComponentMsgObj + MaybeTrace,
{ {
fn obj_place(&mut self, bounds: Rect) -> Rect { fn obj_place(&mut self, bounds: Rect) -> Rect {
self.place(bounds) self.place(bounds)
@ -95,30 +91,23 @@ where
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) {
self.inner().inner().msg_try_into_obj(msg) self.msg_try_into_obj(msg)
} else { } else {
Ok(Obj::const_none()) Ok(Obj::const_none())
} }
} }
fn obj_paint(&mut self) -> bool { fn obj_paint(&mut self) {
#[cfg(not(feature = "new_rendering"))] #[cfg(not(feature = "new_rendering"))]
{ {
let will_paint = self.inner().will_paint();
self.paint(); self.paint();
will_paint
} }
#[cfg(feature = "new_rendering")] #[cfg(feature = "new_rendering")]
{ {
let will_paint = self.inner().will_paint();
if will_paint {
render_on_display(None, Some(Color::black()), |target| { render_on_display(None, Some(Color::black()), |target| {
self.render(target); self.render(target);
}); });
self.skip_paint();
}
will_paint
} }
} }
@ -126,26 +115,13 @@ where
fn obj_bounds(&self, sink: &mut dyn FnMut(Rect)) { fn obj_bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.bounds(sink) self.bounds(sink)
} }
}
fn obj_skip_paint(&mut self) { #[derive(Copy, Clone, PartialEq, Eq)]
self.skip_paint() enum Repaint {
} None,
Partial,
fn obj_request_clear(&mut self) { Full,
self.clear_screen()
}
fn obj_delete(&mut self) {
self.delete()
}
fn obj_get_transition_out(&self) -> Result<Obj, Error> {
if let Some(msg) = self.get_transition_out() {
Ok(msg.to_obj())
} else {
Ok(Obj::const_none())
}
}
} }
/// `LayoutObj` is a GC-allocated object exported to MicroPython, with type /// `LayoutObj` is a GC-allocated object exported to MicroPython, with type
@ -158,119 +134,130 @@ pub struct LayoutObj {
} }
struct LayoutObjInner { struct LayoutObjInner {
root: Gc<dyn ObjComponent>, root: Option<GcBox<dyn ObjComponent>>,
event_ctx: EventCtx, event_ctx: EventCtx,
timer_fn: Obj, timer_fn: Obj,
page_count: u16, page_count: u16,
repaint: Repaint,
transition_out: AttachType,
} }
impl LayoutObj { impl LayoutObjInner {
/// Create a new `LayoutObj`, wrapping a root component. /// Create a new `LayoutObj`, wrapping a root component.
#[inline(never)] #[inline(never)]
pub fn new(root: impl ComponentMsgObj + MaybeTrace + 'static) -> Result<Gc<Self>, Error> { pub fn new(root: impl ObjComponent + 'static) -> Result<Self, Error> {
// Let's wrap the root component into a `Root` to maintain the top-level let root = GcBox::new(root)?;
// invalidation logic.
let wrapped_root = Root::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(wrapped_root)?) as *mut dyn ObjComponent) };
// SAFETY: This is a Python object and hase a base as first element Ok(Self {
unsafe { root: Some(gc::coerce!(ObjComponent, root)),
Gc::new_with_custom_finaliser(Self {
base: Self::obj_type().as_base(),
inner: RefCell::new(LayoutObjInner {
root,
event_ctx: EventCtx::new(), event_ctx: EventCtx::new(),
timer_fn: Obj::const_none(), timer_fn: Obj::const_none(),
page_count: 1, page_count: 1,
}), repaint: Repaint::Full,
transition_out: AttachType::Initial,
}) })
} }
}
pub fn obj_delete(&self) { fn obj_delete(&mut self) {
let mut inner = self.inner.borrow_mut(); self.root = None;
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
unsafe { Gc::as_mut(&mut inner.root) }.obj_delete();
}
pub fn skip_first_paint(&self) {
let mut inner = self.inner.borrow_mut();
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
unsafe { Gc::as_mut(&mut inner.root) }.obj_skip_paint();
} }
/// Timer callback is expected to be a callable object of the following /// Timer callback is expected to be a callable object of the following
/// form: `def timer(token: int, deadline_in_ms: int)`. /// form: `def timer(token: int, deadline_in_ms: int)`.
fn obj_set_timer_fn(&self, timer_fn: Obj) { fn obj_set_timer_fn(&mut self, timer_fn: Obj) {
self.inner.borrow_mut().timer_fn = timer_fn; self.timer_fn = timer_fn;
}
fn root(&self) -> &impl Deref<Target = dyn ObjComponent> {
unwrap!(self.root.as_ref())
}
fn root_mut(&mut self) -> &mut impl DerefMut<Target = dyn ObjComponent> {
unwrap!(self.root.as_mut())
}
fn obj_request_repaint(&mut self) {
self.repaint = Repaint::Full;
let mut dummy_ctx = EventCtx::new();
let paint_msg = self
.root_mut()
.obj_event(&mut dummy_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());
} }
/// Run an event pass over the component tree. After the traversal, any /// Run an event pass over the component tree. After the traversal, any
/// pending timers are drained into `self.timer_callback`. Returns `Err` /// pending timers are drained into `self.timer_callback`. Returns `Err`
/// in case the timer callback raises or one of the components returns /// in case the timer callback raises or one of the components returns
/// an error, `Ok` with the message otherwise. /// an error, `Ok` with the message otherwise.
fn obj_event(&self, event: Event) -> Result<Obj, Error> { fn obj_event(&mut self, event: Event) -> Result<Obj, Error> {
let inner = &mut *self.inner.borrow_mut(); let root = unwrap!(self.root.as_mut());
// Place the root component on the screen in case it was previously requested. // Place the root component on the screen in case it was previously requested.
if inner.event_ctx.needs_place_before_next_event_or_paint() { if self.event_ctx.needs_place() {
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`. root.obj_place(constant::screen());
unsafe { Gc::as_mut(&mut inner.root) }.obj_place(constant::screen());
} }
// Clear the leftover flags from the previous event pass. // Clear the leftover flags from the previous event pass.
inner.event_ctx.clear(); self.event_ctx.clear();
// Send the event down the component tree. Bail out in case of failure. // Send the event down the component tree. Bail out in case of failure.
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`. let msg = root.obj_event(&mut self.event_ctx, event)?;
let msg = unsafe { Gc::as_mut(&mut inner.root) }.obj_event(&mut inner.event_ctx, event)?;
// 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() {
self.repaint = Repaint::Partial;
}
// All concerning `Child` wrappers should have already marked themselves for // All concerning `Child` wrappers should have already marked themselves for
// painting by now, and we're prepared for a paint pass. // painting by now, and we're prepared for a paint pass.
// Drain any pending timers into the callback. // Drain any pending timers into the callback.
while let Some((token, deadline)) = inner.event_ctx.pop_timer() { while let Some((token, deadline)) = self.event_ctx.pop_timer() {
let token = token.try_into(); let token = token.try_into();
let deadline = deadline.try_into(); let deadline = deadline.try_into();
if let (Ok(token), Ok(deadline)) = (token, deadline) { if let (Ok(token), Ok(deadline)) = (token, deadline) {
inner.timer_fn.call_with_n_args(&[token, deadline])?; self.timer_fn.call_with_n_args(&[token, deadline])?;
} else { } else {
// Failed to convert token or deadline into `Obj`, skip. // Failed to convert token or deadline into `Obj`, skip.
} }
} }
if let Some(count) = inner.event_ctx.page_count() { if let Some(count) = self.event_ctx.page_count() {
inner.page_count = count as u16; self.page_count = count as u16;
}
if let Some(t) = self.event_ctx.get_transition_out() {
self.transition_out = t;
} }
Ok(msg) Ok(msg)
} }
fn obj_request_clear(&self) {
let mut inner = self.inner.borrow_mut();
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
unsafe { Gc::as_mut(&mut inner.root) }.obj_request_clear();
}
/// Run a paint pass over the component tree. Returns true if any component /// Run a paint pass over the component tree. Returns true if any component
/// actually requested painting since last invocation of the function. /// actually requested painting since last invocation of the function.
fn obj_paint_if_requested(&self) -> bool { fn obj_paint_if_requested(&mut self) -> bool {
let mut inner = self.inner.borrow_mut(); if self.repaint == Repaint::Full {
display::clear();
// Place the root component on the screen in case it was previously requested.
if inner.event_ctx.needs_place_before_next_event_or_paint() {
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
unsafe { Gc::as_mut(&mut inner.root) }.obj_place(constant::screen());
} }
sync(); // 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());
}
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`. display::sync();
unsafe { Gc::as_mut(&mut inner.root) }.obj_paint()
if self.repaint != Repaint::None {
self.repaint = Repaint::None;
self.root_mut().obj_paint();
true
} else {
false
}
} }
/// Run a tracing pass over the component tree. Passed `callback` is called /// Run a tracing pass over the component tree. Passed `callback` is called
@ -293,18 +280,16 @@ impl LayoutObj {
// because trait upcasting is unstable. // because trait upcasting is unstable.
// Luckily, calling `root.trace()` works perfectly fine in spite of the above.) // Luckily, calling `root.trace()` works perfectly fine in spite of the above.)
tracer.root(&|t| { tracer.root(&|t| {
self.inner.borrow().root.trace(t); self.root().trace(t);
}); });
} }
fn obj_page_count(&self) -> Obj { fn obj_page_count(&self) -> Obj {
self.inner.borrow().page_count.into() self.page_count.into()
} }
fn obj_button_request(&self) -> Result<Obj, Error> { fn obj_button_request(&mut self) -> Result<Obj, Error> {
let inner = &mut *self.inner.borrow_mut(); match self.event_ctx.button_request() {
match inner.event_ctx.button_request() {
None => Ok(Obj::const_none()), None => Ok(Obj::const_none()),
Some(ButtonRequest { code, br_type }) => { Some(ButtonRequest { code, br_type }) => {
(code.num().into(), br_type.try_into()?).try_into() (code.num().into(), br_type.try_into()?).try_into()
@ -312,18 +297,12 @@ impl LayoutObj {
} }
} }
fn obj_get_transition_out(&self) -> Result<Obj, Error> { fn obj_get_transition_out(&self) -> Obj {
let inner = &mut *self.inner.borrow_mut(); self.transition_out.to_obj()
// Get transition out result
// SAFETY: `inner.root` is unique because of the `inner.borrow_mut()`.
unsafe { Gc::as_mut(&mut inner.root) }.obj_get_transition_out()
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
fn obj_bounds(&self) { fn obj_bounds(&self) {
use crate::ui::display;
// Sink for `Trace::bounds` that draws the boundaries using pseudorandom color. // Sink for `Trace::bounds` that draws the boundaries using pseudorandom color.
fn wireframe(r: Rect) { fn wireframe(r: Rect) {
let w = r.width() as u16; let w = r.width() as u16;
@ -334,7 +313,25 @@ impl LayoutObj {
// use crate::ui::model_tt::theme; // use crate::ui::model_tt::theme;
// wireframe(theme::borders()); // wireframe(theme::borders());
self.inner.borrow().root.obj_bounds(&mut wireframe); self.root().obj_bounds(&mut wireframe);
}
}
impl LayoutObj {
/// Create a new `LayoutObj`, wrapping a root component.
#[inline(never)]
pub fn new(root: impl ComponentMsgObj + MaybeTrace + 'static) -> Result<Gc<Self>, Error> {
// SAFETY: This is a Python object and hase a base as first element
unsafe {
Gc::new_with_custom_finaliser(Self {
base: Self::obj_type().as_base(),
inner: RefCell::new(LayoutObjInner::new(root)?),
})
}
}
fn inner_mut(&self) -> RefMut<LayoutObjInner> {
self.inner.borrow_mut()
} }
fn obj_type() -> &'static Type { fn obj_type() -> &'static Type {
@ -359,6 +356,10 @@ impl LayoutObj {
}; };
&TYPE &TYPE
} }
pub fn skip_first_paint(&self) {
self.inner_mut().repaint = Repaint::None;
}
} }
impl From<Gc<LayoutObj>> for Obj { impl From<Gc<LayoutObj>> for Obj {
@ -424,9 +425,11 @@ impl TryFrom<Never> for Obj {
extern "C" fn ui_layout_attach_timer_fn(this: Obj, timer_fn: Obj, attach_type: Obj) -> Obj { extern "C" fn ui_layout_attach_timer_fn(this: Obj, timer_fn: Obj, attach_type: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
this.obj_set_timer_fn(timer_fn); this.inner_mut().obj_set_timer_fn(timer_fn);
let msg = this.obj_event(Event::Attach(AttachType::try_from_obj(attach_type)?))?; let msg = this
.inner_mut()
.obj_event(Event::Attach(AttachType::try_from_obj(attach_type)?))?;
assert!(msg == Obj::const_none()); assert!(msg == Obj::const_none());
Ok(Obj::const_none()) Ok(Obj::const_none())
}; };
@ -445,7 +448,7 @@ extern "C" fn ui_layout_touch_event(n_args: usize, args: *const Obj) -> Obj {
args[2].try_into()?, args[2].try_into()?,
args[3].try_into()?, args[3].try_into()?,
)?; )?;
let msg = this.obj_event(Event::Touch(event))?; let msg = this.inner_mut().obj_event(Event::Touch(event))?;
Ok(msg) Ok(msg)
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
@ -464,7 +467,7 @@ extern "C" fn ui_layout_button_event(n_args: usize, args: *const Obj) -> Obj {
} }
let this: Gc<LayoutObj> = args[0].try_into()?; let this: Gc<LayoutObj> = args[0].try_into()?;
let event = ButtonEvent::new(args[1].try_into()?, args[2].try_into()?)?; let event = ButtonEvent::new(args[1].try_into()?, args[2].try_into()?)?;
let msg = this.obj_event(Event::Button(event))?; let msg = this.inner_mut().obj_event(Event::Button(event))?;
Ok(msg) Ok(msg)
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
@ -483,7 +486,9 @@ extern "C" fn ui_layout_progress_event(n_args: usize, args: *const Obj) -> Obj {
let this: Gc<LayoutObj> = args[0].try_into()?; let this: Gc<LayoutObj> = args[0].try_into()?;
let value: u16 = args[1].try_into()?; let value: u16 = args[1].try_into()?;
let description: StrBuffer = args[2].try_into()?; let description: StrBuffer = args[2].try_into()?;
let msg = this.obj_event(Event::Progress(value, description.into()))?; let msg = this
.inner_mut()
.obj_event(Event::Progress(value, description.into()))?;
Ok(msg) Ok(msg)
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
@ -496,7 +501,7 @@ extern "C" fn ui_layout_usb_event(n_args: usize, args: *const Obj) -> Obj {
} }
let this: Gc<LayoutObj> = args[0].try_into()?; let this: Gc<LayoutObj> = args[0].try_into()?;
let event = USBEvent::Connected(args[1].try_into()?); let event = USBEvent::Connected(args[1].try_into()?);
let msg = this.obj_event(Event::USB(event))?; let msg = this.inner_mut().obj_event(Event::USB(event))?;
Ok(msg) Ok(msg)
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
@ -506,7 +511,7 @@ extern "C" fn ui_layout_timer(this: Obj, token: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
let event = Event::Timer(token.try_into()?); let event = Event::Timer(token.try_into()?);
let msg = this.obj_event(event)?; let msg = this.inner_mut().obj_event(event)?;
Ok(msg) Ok(msg)
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
@ -515,7 +520,7 @@ extern "C" fn ui_layout_timer(this: Obj, token: Obj) -> Obj {
extern "C" fn ui_layout_paint(this: Obj) -> Obj { extern "C" fn ui_layout_paint(this: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
let painted = this.obj_paint_if_requested().into(); let painted = this.inner_mut().obj_paint_if_requested().into();
Ok(painted) Ok(painted)
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
@ -524,15 +529,7 @@ extern "C" fn ui_layout_paint(this: Obj) -> Obj {
extern "C" fn ui_layout_request_complete_repaint(this: Obj) -> Obj { extern "C" fn ui_layout_request_complete_repaint(this: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
let event = Event::RequestPaint; this.inner_mut().obj_request_repaint();
let msg = this.obj_event(event)?;
if msg != Obj::const_none() {
// Messages raised during a `RequestPaint` dispatch are not propagated, let's
// make sure we don't do that.
#[cfg(feature = "ui_debug")]
fatal_error!("Cannot raise messages during RequestPaint");
};
this.obj_request_clear();
Ok(Obj::const_none()) Ok(Obj::const_none())
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
@ -541,7 +538,8 @@ extern "C" fn ui_layout_request_complete_repaint(this: Obj) -> Obj {
extern "C" fn ui_layout_page_count(this: Obj) -> Obj { extern "C" fn ui_layout_page_count(this: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
Ok(this.obj_page_count()) let page_count = this.inner_mut().obj_page_count();
Ok(page_count)
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
} }
@ -549,7 +547,8 @@ extern "C" fn ui_layout_page_count(this: Obj) -> Obj {
extern "C" fn ui_layout_button_request(this: Obj) -> Obj { extern "C" fn ui_layout_button_request(this: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
this.obj_button_request() let button_request = this.inner_mut().obj_button_request();
button_request
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
} }
@ -557,7 +556,8 @@ extern "C" fn ui_layout_button_request(this: Obj) -> Obj {
extern "C" fn ui_layout_get_transition_out(this: Obj) -> Obj { extern "C" fn ui_layout_get_transition_out(this: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
this.obj_get_transition_out() let transition_out = this.inner_mut().obj_get_transition_out();
Ok(transition_out)
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
} }
@ -572,7 +572,7 @@ pub extern "C" fn ui_debug_layout_type() -> &'static Type {
extern "C" fn ui_layout_trace(this: Obj, callback: Obj) -> Obj { extern "C" fn ui_layout_trace(this: Obj, callback: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
this.obj_trace(callback); this.inner_mut().obj_trace(callback);
Ok(Obj::const_none()) Ok(Obj::const_none())
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
@ -587,7 +587,7 @@ extern "C" fn ui_layout_trace(_this: Obj, _callback: Obj) -> Obj {
extern "C" fn ui_layout_bounds(this: Obj) -> Obj { extern "C" fn ui_layout_bounds(this: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
this.obj_bounds(); this.inner_mut().obj_bounds();
Ok(Obj::const_none()) Ok(Obj::const_none())
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }
@ -601,7 +601,7 @@ extern "C" fn ui_layout_bounds(_this: Obj) -> Obj {
extern "C" fn ui_layout_delete(this: Obj) -> Obj { extern "C" fn ui_layout_delete(this: Obj) -> Obj {
let block = || { let block = || {
let this: Gc<LayoutObj> = this.try_into()?; let this: Gc<LayoutObj> = this.try_into()?;
this.obj_delete(); this.inner_mut().obj_delete();
Ok(Obj::const_none()) Ok(Obj::const_none())
}; };
unsafe { util::try_or_raise(block) } unsafe { util::try_or_raise(block) }