1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-05 13:26:57 +00:00

fixup! refactor(core): integrate new drawing library

This commit is contained in:
cepetr 2024-03-11 11:20:53 +01:00
parent 434b272f44
commit 6602bd9b86
83 changed files with 244 additions and 238 deletions

View File

@ -61,7 +61,7 @@ pub trait Component {
/// the `Child` wrapper.
fn paint(&mut self);
fn render(&mut self, _target: &mut impl Renderer);
fn render<'s>(&'s self, _target: &mut impl Renderer<'s>);
#[cfg(feature = "ui_bounds")]
/// Report current paint bounds of this component. Used for debugging.
@ -156,7 +156,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.component.render(target);
}
@ -260,7 +260,7 @@ where
self.inner.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.inner.render(target);
}
@ -302,7 +302,7 @@ where
self.1.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.0.render(target);
self.1.render(target);
}
@ -356,7 +356,7 @@ where
self.2.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.0.render(target);
self.1.render(target);
self.2.render(target);
@ -389,8 +389,8 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
if let Some(ref mut c) = self {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
if let Some(ref c) = self {
c.render(target)
}
}

View File

@ -42,7 +42,7 @@ where
self.inner.paint()
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.inner.render(target);
}

View File

@ -57,7 +57,7 @@ impl Component for Connect {
});
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let font = Font::NORMAL;
self.bg.render(target);

View File

@ -16,7 +16,7 @@ impl Component for Empty {
fn paint(&mut self) {}
fn render(&mut self, _target: &mut impl Renderer) {}
fn render<'s>(&'s self, _target: &mut impl Renderer<'s>) {}
}
#[cfg(feature = "ui_debug")]

View File

@ -50,7 +50,7 @@ impl Component for Image {
self.draw(self.area.center(), Alignment2D::CENTER);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
shape::ToifImage::new(self.area.center(), self.toif)
.with_align(Alignment2D::CENTER)
.render(target);
@ -138,7 +138,7 @@ impl Component for BlendedImage {
self.paint_image();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
shape::ToifImage::new(self.bg_top_left, self.bg.toif)
.with_fg(self.bg_color)
.render(target);

View File

@ -120,7 +120,7 @@ where
self.layout.render_text(self.text.as_ref());
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.layout.render_text2(self.text.as_ref(), target);
}

View File

@ -31,7 +31,7 @@ where
self.inner.paint()
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.inner.render(target);
}

View File

@ -132,7 +132,7 @@ where
);
}
pub fn render_anim(&mut self, target: &mut impl Renderer, offset: i16) {
pub fn render_anim<'s>(&'s self, target: &mut impl Renderer<'s>, offset: i16) {
target.in_window(self.area, &mut |target| {
let text_height = self.font.text_height();
let pos = self.area.top_left() + Offset::new(offset, text_height - 1);
@ -237,7 +237,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let now = Instant::now();
match self.state {

View File

@ -95,7 +95,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
if self.visible {
self.inner.render(target);

View File

@ -55,7 +55,7 @@ impl Pad {
}
}
pub fn render(&mut self, target: &mut impl Renderer) {
pub fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
shape::Bar::new(self.area)
.with_bg(self.color)
.render(target);

View File

@ -41,7 +41,7 @@ where
(self.func)(self.area);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let area = self.area;
shape::Bar::new(area)
.with_thickness(1)

View File

@ -65,7 +65,7 @@ where
self.inner.paint()
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.inner.render(target);
}
}
@ -112,7 +112,7 @@ where
self.inner.paint()
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.inner.render(target);
}
}
@ -188,7 +188,7 @@ where
self.inner.paint()
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.inner.render(target);
}
}
@ -283,7 +283,7 @@ where
self.second.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.first.render(target);
self.second.render(target);
}

View File

@ -143,7 +143,7 @@ impl Component for Qr {
Self::draw(&qr, area, self.border, scale);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let mut outbuffer = [0u8; QR_MAX_VERSION.buffer_len()];
let mut tempbuffer = [0u8; QR_MAX_VERSION.buffer_len()];

View File

@ -140,8 +140,8 @@ impl<T: StringType + Clone> Component for FormattedText<T> {
self.layout_content(&mut TextRenderer);
}
fn render(&mut self, target: &mut impl Renderer) {
self.layout_content(&mut TextRenderer2(target));
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.layout_content(&mut TextRenderer2::new(target));
}
#[cfg(feature = "ui_bounds")]

View File

@ -238,8 +238,12 @@ impl TextLayout {
}
/// Draw as much text as possible on the current screen.
pub fn render_text2(&self, text: &str, target: &mut impl Renderer) -> LayoutFit {
self.layout_text(text, &mut self.initial_cursor(), &mut TextRenderer2(target))
pub fn render_text2<'s>(&self, text: &str, target: &mut impl Renderer<'s>) -> LayoutFit {
self.layout_text(
text,
&mut self.initial_cursor(),
&mut TextRenderer2::new(target),
)
}
/// Loop through the `text` and try to fit it on the current screen,
@ -537,13 +541,22 @@ impl LayoutSink for TextRenderer {
}
}
pub struct TextRenderer2<'a, R>(pub &'a mut R)
pub struct TextRenderer2<'a, 's, R>(pub &'a mut R, core::marker::PhantomData<&'s ()>)
where
R: Renderer;
R: Renderer<'s>;
impl<'a, R> LayoutSink for TextRenderer2<'a, R>
impl<'a, 's, R> TextRenderer2<'a, 's, R>
where
R: Renderer,
R: Renderer<'s>,
{
pub fn new(target: &'a mut R) -> Self {
Self(target, core::marker::PhantomData)
}
}
impl<'a, 's, R> LayoutSink for TextRenderer2<'a, 's, R>
where
R: Renderer<'s>,
{
fn text(&mut self, cursor: Point, layout: &TextLayout, text: &str) {
shape::Text::new(cursor, text)

View File

@ -190,7 +190,7 @@ where
)
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
Self::foreach_visible(
&self.source,
&self.visible,
@ -620,12 +620,12 @@ impl<T> Checklist<T> {
);
}
fn render_icon(
fn render_icon<'s>(
&self,
layout: &TextLayout,
icon: Icon,
offset: Offset,
target: &mut impl Renderer,
target: &mut impl Renderer<'s>,
) {
let top_left = Point::new(self.area.x0, layout.bounds.y0);
shape::ToifImage::new(top_left + offset, icon.toif)
@ -671,7 +671,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.paragraphs.render(target);
let current_visible = self.current.saturating_sub(self.paragraphs.offset.par);

View File

@ -45,8 +45,8 @@ pub fn text_multiline(
///
/// If it fits, returns the rest of the area.
/// If it does not fit, returns `None`.
pub fn text_multiline2(
target: &mut impl Renderer,
pub fn text_multiline2<'s>(
target: &mut impl Renderer<'s>,
area: Rect,
text: TString<'_>,
font: Font,
@ -97,8 +97,8 @@ pub fn text_multiline_bottom(
/// Same as `text_multiline` above, but aligns the text to the bottom of the
/// area.
pub fn text_multiline_bottom2(
target: &mut impl Renderer,
pub fn text_multiline_bottom2<'s>(
target: &mut impl Renderer<'s>,
area: Rect,
text: TString<'_>,
font: Font,

View File

@ -46,7 +46,7 @@ impl Component for Timeout {
fn paint(&mut self) {}
fn render(&mut self, _target: &mut impl Renderer) {}
fn render<'s>(&'s self, _target: &mut impl Renderer<'s>) {}
}
#[cfg(feature = "ui_debug")]

View File

@ -14,7 +14,6 @@ use crate::{
},
};
#[cfg(not(feature = "new_rendering"))]
pub fn jpeg(data: &[u8], pos: Point, scale: u8) {
let mut buffer = BufferJpegWork::get_cleared();
let pool = buffer.buffer.as_mut_slice();

View File

@ -105,7 +105,7 @@ impl<'a> Component for Intro<'a> {
self.buttons.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
self.title.render(target);

View File

@ -73,7 +73,7 @@ impl Choice for MenuChoice {
);
}
fn render_center(&self, target: &mut impl Renderer, _area: Rect, _inverse: bool) {
fn render_center<'s>(&self, target: &mut impl Renderer<'s>, _area: Rect, _inverse: bool) {
// Icon on top and two lines of text below
shape::ToifImage::new(SCREEN_CENTER + Offset::y(-20), self.icon.toif)
.with_align(Alignment2D::CENTER)
@ -186,7 +186,7 @@ impl Component for Menu {
self.choice_page.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
self.choice_page.render(target);
}

View File

@ -60,7 +60,7 @@ impl Component for Welcome {
);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
let top_center = self.bg.area.top_center();

View File

@ -257,7 +257,7 @@ impl Component for AddressDetails {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
self.buttons.render(target);
match self.current_page {

View File

@ -229,8 +229,8 @@ where
self.buttons.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
self.bg.paint();
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
let mut display_top_left = |text: TString<'static>| {
text.map(|t| {

View File

@ -268,7 +268,7 @@ impl Component for Button {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let style = self.style();
let fg_color = style.text_color;
let bg_color = fg_color.negate();

View File

@ -95,7 +95,7 @@ impl ButtonType {
}
}
pub fn render(&mut self, target: &mut impl Renderer) {
pub fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
match self {
Self::Button(button) => {
button.render(target);
@ -167,7 +167,7 @@ impl ButtonContainer {
self.button_type.paint();
}
pub fn render(&mut self, target: &mut impl Renderer) {
pub fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.button_type.render(target);
}
@ -592,7 +592,7 @@ impl Component for ButtonController {
self.right_btn.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
self.left_btn.render(target);
self.middle_btn.render(target);
@ -778,7 +778,7 @@ impl Component for AutomaticMover {
fn paint(&mut self) {}
fn render(&mut self, _target: &mut impl Renderer) {}
fn render<'s>(&'s self, _target: &mut impl Renderer<'s>) {}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
// Moving automatically only when we receive a TimerToken that we have

View File

@ -111,7 +111,7 @@ where
common::display_left(baseline, &self.text, self.font);
}
fn render_left(&self, target: &mut impl Renderer) {
fn render_left<'s>(&'s self, target: &mut impl Renderer<'s>) {
let baseline = Point::new(self.pad.area.x0, self.y_baseline());
shape::Text::new(baseline, &self.text.as_ref())
.with_font(self.font)
@ -123,7 +123,7 @@ where
common::display_center(baseline, &self.text, self.font);
}
fn render_center(&self, target: &mut impl Renderer) {
fn render_center<'s>(&'s self, target: &mut impl Renderer<'s>) {
let baseline = Point::new(self.pad.area.bottom_center().x, self.y_baseline());
shape::Text::new(baseline, &self.text.as_ref())
.with_align(Alignment::Center)
@ -136,7 +136,7 @@ where
common::display_right(baseline, &self.text, self.font);
}
fn render_right(&self, target: &mut impl Renderer) {
fn render_right<'s>(&'s self, target: &mut impl Renderer<'s>) {
let baseline = Point::new(self.pad.area.x1, self.y_baseline());
shape::Text::new(baseline, &self.text.as_ref())
.with_align(Alignment::End)
@ -201,7 +201,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
if self.show_content {
// In the case text cannot fit, show ellipsis and its right part

View File

@ -136,7 +136,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// TOP
let center = self.area.center() + Offset::y(self.loader_y_offset);

View File

@ -99,7 +99,7 @@ impl<T: AsRef<str>> Component for ErrorScreen<T> {
self.footer.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
if self.show_icons {

View File

@ -310,7 +310,7 @@ where
self.current_page.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
// Scrollbars are painted only with a title and when requested
if self.title.is_some() {

View File

@ -133,8 +133,7 @@ where
self.formatted.paint();
}
pub fn render(&mut self, target: &mut impl Renderer) {
self.change_page(self.current_page);
pub fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.formatted.render(target);
}

View File

@ -85,7 +85,7 @@ where
self.content.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.title.render(target);
self.content.render(target);
}
@ -211,7 +211,7 @@ where
self.content.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.title.render(target);
self.scrollbar.render(target);
self.content.render(target);

View File

@ -124,7 +124,7 @@ impl Component for HoldToConfirm {
self.loader.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.loader.render(target);
}
}

View File

@ -1,6 +1,10 @@
use crate::{
error::Error,
micropython::buffer::StrBuffer,
micropython::{
buffer::{get_buffer, StrBuffer},
gc::Gc,
obj::Obj,
},
strutil::StringType,
translations::TR,
trezorhal::usb::usb_configured,
@ -50,7 +54,7 @@ fn paint_default_image() {
);
}
fn render_default_image(target: &mut impl Renderer) {
fn render_default_image<'s>(target: &mut impl Renderer<'s>) {
shape::ToifImage::new(
TOP_CENTER + Offset::y(LOGO_ICON_TOP_MARGIN),
theme::ICON_LOGO.toif,
@ -74,6 +78,7 @@ where
// always painted, so we need to always paint the label too
label: Label<T>,
notification: Option<(T, u8)>,
custom_image: Option<Gc<[u8]>>,
/// Used for HTC functionality to lock device from homescreen
invisible_buttons: Child<ButtonController>,
/// Holds the loader component
@ -97,6 +102,7 @@ where
Self {
label: Label::centered(label, theme::TEXT_BIG),
notification,
custom_image: get_user_custom_image().ok(),
invisible_buttons: Child::new(ButtonController::new(invisible_btn_layout)),
loader,
show_loader: false,
@ -105,8 +111,8 @@ where
}
fn paint_homescreen_image(&self) {
let homescreen_bytes = get_user_custom_image().ok();
let homescreen = homescreen_bytes
let homescreen = self
.custom_image
.as_ref()
.and_then(|data| Toif::new(data.as_ref()).ok())
.filter(check_homescreen_format);
@ -117,17 +123,17 @@ where
}
}
fn render_homescreen_image(&self, target: &mut impl Renderer) {
let homescreen_bytes = get_user_custom_image().ok();
let homescreen = homescreen_bytes
fn render_homescreen_image<'s>(&'s self, target: &mut impl Renderer<'s>) {
let homescreen = self
.custom_image
.as_ref()
.and_then(|data| Toif::new(data.as_ref()).ok())
.filter(check_homescreen_format);
if let Some(toif) = homescreen {
/*shape::ToifImage::new(TOP_CENTER, toif)
shape::ToifImage::new(TOP_CENTER, toif)
.with_align(Alignment2D::TOP_CENTER)
.with_fg(theme::FG)
.render(target);*/ // !@# lifetime problem
.render(target);
} else {
render_default_image(target);
}
@ -164,7 +170,7 @@ where
}
}
fn render_notification(&self, target: &mut impl Renderer) {
fn render_notification<'s>(&'s self, target: &mut impl Renderer<'s>) {
let baseline = TOP_CENTER + Offset::y(NOTIFICATION_FONT.line_height());
if !usb_configured() {
shape::Bar::new(AREA.split_top(NOTIFICATION_HEIGHT).0)
@ -217,12 +223,12 @@ where
self.label.paint();
}
fn render_label(&mut self, target: &mut impl Renderer) {
fn render_label<'s>(&'s self, target: &mut impl Renderer<'s>) {
// paint black background to place the label
let mut outset = Insets::uniform(LABEL_OUTSET);
// the margin at top is bigger (caused by text-height vs line-height?)
// compensate by shrinking the outset
outset.top -= 2;
outset.top -= 5;
shape::Bar::new(self.label.text_area().outset(outset))
.with_bg(theme::BG)
.render(target);
@ -321,19 +327,11 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// Redraw the whole screen when the screen changes (loader vs homescreen)
if self.show_loader {
if !matches!(self.current_screen, CurrentScreen::Loader) {
// display::clear(); !@# what's this??
self.current_screen = CurrentScreen::Loader;
}
self.loader.render(target);
} else {
if !matches!(self.current_screen, CurrentScreen::Homescreen) {
// display::clear(); !@# what's this??
self.current_screen = CurrentScreen::Homescreen;
}
// Painting the homescreen image first, as the notification and label
// should be "on top of it"
self.render_homescreen_image(target);
@ -426,7 +424,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
if self.screensaver {
// keep screen blank
return;
@ -451,33 +449,32 @@ where
}
}
pub struct ConfirmHomescreen<T, F>
pub struct ConfirmHomescreen<T>
where
T: StringType,
{
title: Child<Label<T>>,
buffer_func: F,
image: Obj,
buttons: Child<ButtonController>,
}
impl<T, F> ConfirmHomescreen<T, F>
impl<T> ConfirmHomescreen<T>
where
T: StringType + Clone,
{
pub fn new(title: T, buffer_func: F) -> Self {
pub fn new(title: T, image: Obj) -> Self {
let btn_layout = ButtonLayout::cancel_none_text(TR::buttons__change.into());
ConfirmHomescreen {
title: Child::new(Label::centered(title, theme::TEXT_BOLD)),
buffer_func,
image,
buttons: Child::new(ButtonController::new(btn_layout)),
}
}
}
impl<'a, T, F> Component for ConfirmHomescreen<T, F>
impl<'a, T> Component for ConfirmHomescreen<T>
where
T: StringType + Clone,
F: Fn() -> &'a [u8],
{
type Msg = CancelConfirmMsg;
@ -504,11 +501,13 @@ where
fn paint(&mut self) {
// Drawing the image full-screen first and then other things on top
let buffer = (self.buffer_func)();
if buffer.is_empty() {
// SAFETY: We expect no existing mutable reference. Resulting reference is
// discarded before returning to micropython.
let image_data = unwrap!(unsafe { get_buffer(self.image) });
if image_data.is_empty() {
paint_default_image();
} else {
let toif_data = unwrap!(Toif::new(buffer));
let toif_data = unwrap!(Toif::new(image_data));
toif_data.draw(TOP_CENTER, Alignment2D::TOP_CENTER, theme::FG, theme::BG);
};
// Need to make all the title background black, so the title text is well
@ -519,16 +518,18 @@ where
self.buttons.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// Drawing the image full-screen first and then other things on top
let buffer = (self.buffer_func)();
if buffer.is_empty() {
// SAFETY: We expect no existing mutable reference. Resulting reference is
// discarded before returning to micropython.
let image_data = unwrap!(unsafe { get_buffer(self.image) });
if image_data.is_empty() {
render_default_image(target);
} else {
/*let toif_data = unwrap!(Toif::new(buffer));
let toif_data = unwrap!(Toif::new(image_data));
shape::ToifImage::new(TOP_CENTER, toif_data)
.with_fg(theme::FG)
.render(target);*/ // !@# lifetime problem
.render(target);
};
// Need to make all the title background black, so the title text is well
// visible
@ -572,7 +573,7 @@ where
}
#[cfg(feature = "ui_debug")]
impl<T, F> crate::trace::Trace for ConfirmHomescreen<T, F>
impl<T> crate::trace::Trace for ConfirmHomescreen<T>
where
T: StringType,
{

View File

@ -16,7 +16,7 @@ pub trait Choice {
// and therefore has a default implementation.
fn paint_center(&self, area: Rect, inverse: bool);
fn render_center(&self, target: &mut impl Renderer, _area: Rect, _inverse: bool);
fn render_center<'s>(&self, target: &mut impl Renderer<'s>, _area: Rect, _inverse: bool);
fn width_center(&self) -> i16 {
0
@ -24,7 +24,7 @@ pub trait Choice {
fn paint_side(&self, _area: Rect) {}
fn render_side(&self, _target: &mut impl Renderer, _area: Rect) {}
fn render_side<'s>(&self, _target: &mut impl Renderer<'s>, _area: Rect) {}
fn width_side(&self) -> i16 {
0
@ -257,11 +257,11 @@ where
/// Display current, previous and next choices according to
/// the current ChoiceItem.
fn render_choices(&mut self, target: &mut impl Renderer) {
fn render_choices<'s>(&'s self, target: &mut impl Renderer<'s>) {
// Getting the row area for the choices - so that displaying
// items in the used font will show them in the middle vertically.
let area_height_half = self.pad.area.height() / 2;
let font_size_half = theme::FONT_CHOICE_ITEMS.text_height() / 2;
let font_size_half = theme::FONT_CHOICE_ITEMS.visible_text_height("Ay") / 2;
let center_row_area = self
.pad
.area
@ -341,14 +341,9 @@ where
}
/// Display the current choice in the middle.
fn show_current_choice2(&mut self, target: &mut impl Renderer, area: Rect) {
fn show_current_choice2<'s>(&'s self, target: &mut impl Renderer<'s>, area: Rect) {
self.get_current_item()
.render_center(target, area, self.inverse_selected_item);
// Color inversion is just one-time thing.
if self.inverse_selected_item {
self.inverse_selected_item = false;
}
}
/// Display all the choices fitting on the left side.
@ -395,7 +390,7 @@ where
/// Display all the choices fitting on the left side.
/// Going as far as possible.
fn show_left_choices2(&self, target: &mut impl Renderer, area: Rect) {
fn show_left_choices2<'s>(&'s self, target: &mut impl Renderer<'s>, area: Rect) {
// NOTE: page index can get negative here, so having it as i16 instead of usize
let mut page_index = self.page_counter as i16 - 1;
let mut current_area = area.split_right(self.items_distance).0;
@ -478,7 +473,7 @@ where
/// Display all the choices fitting on the right side.
/// Going as far as possible.
fn show_right_choices2(&self, target: &mut impl Renderer, area: Rect) {
fn show_right_choices2<'s>(&'s self, target: &mut impl Renderer<'s>, area: Rect) {
let mut page_index = self.page_counter + 1;
let mut current_area = area.split_left(self.items_distance).1;
while current_area.width() > 0 {
@ -624,7 +619,6 @@ where
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
// Cancel highlighting of the current choice.
// The Highlighting is started by pressing the middle button and
// canceled immediately when any other event is processed
@ -728,7 +722,7 @@ where
self.paint_choices();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
self.buttons.render(target);
self.render_choices(target);

View File

@ -111,12 +111,12 @@ impl Choice for ChoiceItem {
/// Painting the item as the main choice in the middle.
/// Showing both the icon and text, if the icon is available.
fn render_center(&self, target: &mut impl Renderer, area: Rect, inverse: bool) {
fn render_center<'s>(&self, target: &mut impl Renderer<'s>, area: Rect, inverse: bool) {
let width = text_icon_width(Some(self.text.as_ref()), self.icon, self.font);
render_rounded_highlight(
target,
area,
Offset::new(width, self.font.text_height()),
Offset::new(width, self.font.visible_text_height("Ay")),
inverse,
);
render_text_icon(
@ -149,7 +149,7 @@ impl Choice for ChoiceItem {
}
/// Painting smaller version of the item on the side.
fn render_side(&self, target: &mut impl Renderer, area: Rect) {
fn render_side<'s>(&self, target: &mut impl Renderer<'s>, area: Rect) {
let width = text_icon_width(self.side_text(), self.icon, self.font);
render_text_icon(
target,
@ -188,7 +188,12 @@ fn paint_rounded_highlight(area: Rect, size: Offset, inverse: bool) {
}
}
fn render_rounded_highlight(target: &mut impl Renderer, area: Rect, size: Offset, inverse: bool) {
fn render_rounded_highlight<'s>(
target: &mut impl Renderer<'s>,
area: Rect,
size: Offset,
inverse: bool,
) {
let bound = theme::BUTTON_OUTLINE;
let left_bottom = area.bottom_center() + Offset::new(-size.x / 2 - bound, bound + 1);
let x_size = size.x + 2 * bound;
@ -257,8 +262,8 @@ fn paint_text_icon(
}
}
fn render_text_icon(
target: &mut impl Renderer,
fn render_text_icon<'s>(
target: &mut impl Renderer<'s>,
area: Rect,
width: i16,
text: Option<&str>,
@ -270,7 +275,7 @@ fn render_text_icon(
let mut baseline = area.bottom_center() - Offset::x(width / 2);
if let Some(icon) = icon {
let height_diff = font.text_height() - icon.toif.height();
let height_diff = font.visible_text_height("Ay") - icon.toif.height();
let vertical_offset = Offset::y(-height_diff / 2);
shape::ToifImage::new(baseline + vertical_offset, icon.toif)
.with_align(Alignment2D::BOTTOM_LEFT)

View File

@ -83,7 +83,7 @@ impl Component for NumberInput {
self.choice_page.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.choice_page.render(target);
}
}

View File

@ -450,7 +450,7 @@ impl Component for PassphraseEntry {
self.choice_page.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.passphrase_dots.render(target);
self.choice_page.render(target);
}

View File

@ -331,7 +331,7 @@ where
self.choice_page.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.header_line.render(target);
self.pin_line.render(target);
self.choice_page.render(target);

View File

@ -117,7 +117,7 @@ impl Component for SimpleChoice {
self.choice_page.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.choice_page.render(target);
}
}

View File

@ -312,7 +312,7 @@ impl Component for WordlistEntry {
self.choice_page.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.chosen_letters.render(target);
self.choice_page.render(target);
}

View File

@ -167,7 +167,12 @@ impl Loader {
);
}
pub fn render_loader(&mut self, target: &mut impl Renderer, style: &LoaderStyle, done: i32) {
pub fn render_loader<'s>(
&'s self,
target: &mut impl Renderer<'s>,
style: &LoaderStyle,
done: i32,
) {
let width = self.area.width();
// NOTE: need to calculate this in `i32`, it would overflow using `i16`
let split_point = (((width as i32 + 1) * done) / (display::LOADER_MAX as i32)) as i16;
@ -266,7 +271,7 @@ impl Component for Loader {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// TODO: Consider passing the current instant along with the event -- that way,
// we could synchronize painting across the component tree. Also could be useful
// in automated tests.
@ -397,7 +402,7 @@ where
self.loader.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.loader.render(target);
}
}

View File

@ -217,7 +217,7 @@ where
self.buttons.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
self.content.render(target);
self.buttons.render(target);

View File

@ -185,7 +185,7 @@ where
self.description.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.title.render(target);
let area = constant::screen();

View File

@ -110,7 +110,7 @@ impl<'a> Component for ResultScreen<'a> {
self.message_bottom.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
self.small_pad.render(target);

View File

@ -108,7 +108,7 @@ impl ScrollBar {
/// Create a (seemingly circular) dot given its top left point.
/// Make it full when it is active, otherwise paint just the perimeter and
/// leave center empty.
fn render_dot(&self, target: &mut impl Renderer, dot_type: &DotType, top_right: Point) {
fn render_dot<'s>(&self, target: &mut impl Renderer<'s>, dot_type: &DotType, top_right: Point) {
let full_square =
Rect::from_top_right_and_size(top_right, Offset::uniform(Self::MAX_DOT_SIZE));
@ -234,7 +234,7 @@ impl ScrollBar {
}
}
fn render_horizontal(&mut self, target: &mut impl Renderer) {
fn render_horizontal<'s>(&'s self, target: &mut impl Renderer<'s>) {
let mut top_right = self.pad.area.top_right();
for dot in self.get_drawable_dots().iter().rev() {
self.render_dot(target, dot, top_right);
@ -274,7 +274,7 @@ impl Component for ScrollBar {
}
/// Displaying one dot for each page.
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// Not showing the scrollbar dot when there is only one page
if self.page_count <= 1 {
return;

View File

@ -99,7 +99,7 @@ where
}
/// Display the final page with user confirmation.
fn render_final_page(&mut self, target: &mut impl Renderer) {
fn render_final_page<'s>(&'s self, target: &mut impl Renderer<'s>) {
let final_text = self.get_final_text();
text_multiline2(
target,
@ -131,7 +131,7 @@ where
}
/// Display current set of recovery words.
fn render_words(&mut self, target: &mut impl Renderer) {
fn render_words<'s>(&'s self, target: &mut impl Renderer<'s>) {
let mut y_offset = 0;
// Showing the word index and the words itself
for i in 0..WORDS_PER_PAGE {
@ -191,7 +191,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// Showing scrollbar in all cases
// Individual pages are responsible for not colliding with it
self.scrollbar.render(target);

View File

@ -79,7 +79,7 @@ where
self.buttons.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.content.render(target);
self.buttons.render(target);
}

View File

@ -72,7 +72,7 @@ where
}
/// Display title/header at the top left of the given area.
pub fn render_header_left(target: &mut impl Renderer, title: &T, area: Rect) {
pub fn render_header_left<'s>(target: &mut impl Renderer<'s>, title: &T, area: Rect) {
let text_height = theme::FONT_HEADER.text_height();
let title_baseline = area.top_left() + Offset::y(text_height - 1);
shape::Text::new(title_baseline, title.as_ref())
@ -95,7 +95,7 @@ where
}
/// Display title/header centered at the top of the given area.
pub fn render_header_centered(target: &mut impl Renderer, title: &T, area: Rect) {
pub fn render_header_centered<'s>(target: &mut impl Renderer<'s>, title: &T, area: Rect) {
let text_height = theme::FONT_HEADER.text_height();
let title_baseline = area.top_center() + Offset::y(text_height - 1);
shape::Text::new(title_baseline, title.as_ref())
@ -140,7 +140,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
if self.needs_marquee {
self.marquee.render(target);
} else if self.centered {

View File

@ -55,7 +55,7 @@ impl Component for WelcomeScreen {
);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
shape::ToifImage::new(
self.area.bottom_center() - Offset::y(5),
theme::ICON_DEVICE_NAME.toif,

View File

@ -44,13 +44,13 @@ impl HorizontalLine {
Self { step, ..self }
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
}
impl Shape for HorizontalLine {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
impl<'s> Shape<'s> for HorizontalLine {
fn bounds(&self, _cache: &DrawingCache<'s>) -> Rect {
let size = Offset::new(self.length, self.thickness as i16);
Rect::from_top_left_and_size(self.pos, size)
}
@ -77,8 +77,8 @@ impl Shape for HorizontalLine {
}
}
impl ShapeClone for HorizontalLine {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'s> ShapeClone<'s> for HorizontalLine {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'s>>
where
T: LocalAllocLeakExt<'alloc>,
{

View File

@ -77,13 +77,13 @@ impl LoaderCircular {
Rect::from_top_left_and_size(pt, Offset::uniform(self.scale))
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
}
impl Shape for LoaderCircular {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
impl<'s> Shape<'s> for LoaderCircular {
fn bounds(&self, _cache: &DrawingCache<'s>) -> Rect {
let cells = self.cells();
if cells.is_empty() {
@ -106,7 +106,7 @@ impl Shape for LoaderCircular {
}
}
impl ShapeClone for LoaderCircular {
impl ShapeClone<'_> for LoaderCircular {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
where
T: LocalAllocLeakExt<'alloc>,

View File

@ -48,12 +48,12 @@ impl LoaderSmall {
Self { color, ..self }
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
}
impl Shape for LoaderSmall {
impl Shape<'_> for LoaderSmall {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
Rect::from_top_left_and_size(self.pos, Offset::uniform(1)).expand(RADIUS + 1)
}
@ -78,7 +78,7 @@ impl Shape for LoaderSmall {
}
}
impl ShapeClone for LoaderSmall {
impl ShapeClone<'_> for LoaderSmall {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
where
T: LocalAllocLeakExt<'alloc>,

View File

@ -52,7 +52,7 @@ impl LoaderStarry {
Self { color, ..self }
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
@ -72,7 +72,7 @@ impl LoaderStarry {
}
}
impl Shape for LoaderStarry {
impl Shape<'_> for LoaderStarry {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
Rect::from_top_left_and_size(self.pos, Offset::uniform(1)).expand(RADIUS + STAR_LARGE)
}
@ -95,7 +95,7 @@ impl Shape for LoaderStarry {
}
}
impl ShapeClone for LoaderStarry {
impl ShapeClone<'_> for LoaderStarry {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
where
T: LocalAllocLeakExt<'alloc>,

View File

@ -234,10 +234,9 @@ where
}
}
impl<'a, T, F> ComponentMsgObj for ConfirmHomescreen<T, F>
impl<'a, T> ComponentMsgObj for ConfirmHomescreen<T>
where
T: StringType + Clone,
F: Fn() -> &'a [u8],
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
@ -422,15 +421,8 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let data: Obj = kwargs.get(Qstr::MP_QSTR_image)?;
// Layout needs to hold the Obj to play nice with GC. Obj is resolved to &[u8]
// in every paint pass.
// SAFETY: We expect no existing mutable reference. Resulting reference is
// discarded before returning to micropython.
let buffer_func = move || unsafe { unwrap!(get_buffer(data)) };
let obj = LayoutObj::new(ConfirmHomescreen::new(title, buffer_func))?;
let image: Obj = kwargs.get(Qstr::MP_QSTR_image)?;
let obj = LayoutObj::new(ConfirmHomescreen::new(title, image))?;
Ok(obj.into())
};

View File

@ -103,7 +103,7 @@ impl<'a> Component for Intro<'a> {
self.menu.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
self.title.render(target);
self.text.render(target);

View File

@ -109,7 +109,7 @@ impl Component for Menu {
self.reset.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
self.title.render(target);
self.close.render(target);

View File

@ -59,8 +59,9 @@ impl Component for Welcome {
);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
shape::Text::new(screen().top_center() + Offset::y(102), "Get started with")
.with_align(Alignment::Center)
.with_font(Font::NORMAL)

View File

@ -188,7 +188,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
match self.current_page {
0 => self.qr_code.render(target),
1 => self.details.render(target),

View File

@ -236,11 +236,11 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
self.content_pad.render(target);
if let Some(info) = self.info.as_mut() {
if let Some(info) = self.info.as_ref() {
if self.show_info {
info.close_button.render(target);
info.title.render(target);
@ -259,7 +259,7 @@ where
self.alert.render(target);
self.left_button.render(target);
self.right_button.render(target);
match &mut self.title {
match &self.title {
ConfirmTitle::Text(label) => label.render(target),
ConfirmTitle::Icon(icon) => {
shape::ToifImage::new(Point::new(screen().center().x, ICON_TOP), icon.toif)

View File

@ -191,7 +191,7 @@ impl<T> Button<T> {
}
}
pub fn render_background(&self, target: &mut impl Renderer, style: &ButtonStyle) {
pub fn render_background<'s>(&self, target: &mut impl Renderer<'s>, style: &ButtonStyle) {
match &self.content {
ButtonContent::IconBlend(_, _, _) => {}
_ => shape::Bar::new(self.area)
@ -244,7 +244,7 @@ impl<T> Button<T> {
}
}
pub fn render_content(&self, target: &mut impl Renderer, style: &ButtonStyle)
pub fn render_content<'s>(&self, target: &mut impl Renderer<'s>, style: &ButtonStyle)
where
T: AsRef<str>,
{
@ -375,7 +375,7 @@ where
self.paint_content(style);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let style = self.style();
self.render_background(target, style);
self.render_content(target, style);

View File

@ -134,7 +134,7 @@ where
self.label.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.content.render(target);
let center = constant::screen().center() + Offset::y(LOADER_OFFSET);

View File

@ -72,7 +72,7 @@ where
self.controls.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.content.render(target);
self.controls.render(target);
}
@ -204,7 +204,7 @@ where
self.controls.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.image.render(target);
self.paragraphs.render(target);
self.controls.render(target);

View File

@ -89,7 +89,7 @@ impl<T: AsRef<str>> Component for ErrorScreen<'_, T> {
self.footer.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
let icon = ICON_WARNING40;

View File

@ -195,7 +195,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.icon.render(target);
self.controls.render(target);
self.app_name.render(target);
@ -204,8 +204,6 @@ where
self.scrollbar.render(target);
}
let current_account = (self.get_account)(self.active_page());
// Erasing the old text content before writing the new one.
let account_name_area = self.account_name.area();
let real_area = account_name_area
@ -215,15 +213,13 @@ where
// Account name is optional.
// Showing it only if it differs from app name.
// (Dummy requests usually have some text as both app_name and account_name.)
if !current_account.as_ref().is_empty()
&& current_account.as_ref() != self.app_name.text().as_ref()
{
self.account_name.set_text(current_account);
let account_name = self.account_name.text().as_ref();
let app_name = self.app_name.text().as_ref();
if !account_name.is_empty() && account_name != app_name {
self.account_name.render(target);
}
if self.fade {
self.fade = false;
if self.fade.take() {
// Note that this is blocking and takes some time.
display::fade_backlight(theme::BACKLIGHT_NORMAL);
}

View File

@ -170,7 +170,7 @@ where
self.content.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.title.render(target);
self.subtitle.render(target);
self.button.render(target);

View File

@ -123,7 +123,7 @@ impl Homescreen {
self.loader.paint()
}
fn render_loader(&mut self, target: &mut impl Renderer) {
fn render_loader<'s>(&'s self, target: &mut impl Renderer<'s>) {
TR::progress__locking_device.map_translated(|t| {
shape::Text::new(TOP_CENTER + Offset::y(HOLD_Y), t)
.with_align(Alignment::Center)
@ -267,13 +267,13 @@ impl Component for Homescreen {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
if self.loader.is_animating() || self.loader.is_completely_grown(Instant::now()) {
self.render_loader(target);
} else {
let img_data = match self.custom_image {
Some(ref img) => IMAGE_HOMESCREEN, //img.as_ref(), !@# solve lifetime problem
Some(ref img) => img.as_ref(),
None => IMAGE_HOMESCREEN,
};
@ -458,9 +458,9 @@ impl Component for Lockscreen {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let img_data = match self.custom_image {
Some(ref img) => IMAGE_HOMESCREEN, //img.as_ref(), !@# solve lifetime problem
Some(ref img) => img.as_ref(),
None => IMAGE_HOMESCREEN,
};

View File

@ -156,7 +156,7 @@ impl Component for Bip39Input {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let area = self.button.area();
let style = self.button.style();

View File

@ -131,8 +131,8 @@ pub fn paint_pending_marker(text_baseline: Point, text: &str, font: Font, color:
}
/// Create a visible "underscoring" of the last letter of a text.
pub fn render_pending_marker(
target: &mut impl Renderer,
pub fn render_pending_marker<'s>(
target: &mut impl Renderer<'s>,
text_baseline: Point,
text: &str,
font: Font,

View File

@ -183,12 +183,12 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.prompt.render(target);
self.input.render(target);
self.back.render(target);
for btn in &mut self.keys {
for btn in &self.keys {
btn.render(target);
}
}

View File

@ -298,16 +298,15 @@ impl Component for PassphraseKeyboard {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.input.render(target);
self.scrollbar.render(target);
self.confirm.render(target);
self.back.render(target);
for btn in &mut self.keys {
for btn in &self.keys {
btn.render(target);
}
if self.fade {
self.fade = false;
if self.fade.take() {
// Note that this is blocking and takes some time.
display::fade_backlight(theme::BACKLIGHT_NORMAL);
}
@ -393,7 +392,7 @@ impl Component for Input {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let style = theme::label_keyboard();
let text_baseline = self.area.top_left() + Offset::y(style.text_font.text_height())

View File

@ -269,11 +269,11 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.erase_btn.render(target);
self.textbox_pad.render(target);
if self.textbox.inner().is_empty() {
if let Some(ref mut w) = self.major_warning {
if let Some(ref w) = self.major_warning {
w.render(target);
} else {
self.major_prompt.render(target);
@ -284,7 +284,7 @@ where
self.textbox.render(target);
}
self.confirm_btn.render(target);
for btn in &mut self.digit_btns {
for btn in &self.digit_btns {
btn.render(target);
}
}
@ -389,7 +389,7 @@ impl PinDots {
}
}
fn render_digits(&self, area: Rect, target: &mut impl Renderer) {
fn render_digits<'s>(&self, area: Rect, target: &mut impl Renderer<'s>) {
let center = area.center() + Offset::y(Font::MONO.text_height() / 2);
let right = center + Offset::x(Font::MONO.text_width("0") * (MAX_VISIBLE_DOTS as i16) / 2);
let digits = self.digits.len();
@ -454,7 +454,7 @@ impl PinDots {
}
}
fn render_dots(&self, area: Rect, target: &mut impl Renderer) {
fn render_dots<'s>(&self, area: Rect, target: &mut impl Renderer<'s>) {
let mut cursor = self.size().snap(area.center(), Alignment2D::CENTER);
let digits = self.digits.len();
@ -533,7 +533,7 @@ impl Component for PinDots {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let dot_area = self.area.inset(HEADER_PADDING);
self.pad.render(target);
if self.display_digits {

View File

@ -187,7 +187,7 @@ impl Component for Slip39Input {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let area = self.button.area();
let style = self.button.style();

View File

@ -58,8 +58,8 @@ impl Component for SelectWordCount {
}
}
fn render(&mut self, target: &mut impl Renderer) {
for btn in self.button.iter_mut() {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
for btn in self.button.iter() {
btn.render(target)
}
}

View File

@ -209,7 +209,7 @@ impl Component for Loader {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// TODO: Consider passing the current instant along with the event -- that way,
// we could synchronize painting across the component tree. Also could be useful
// in automated tests.
@ -250,7 +250,7 @@ impl Component for Loader {
if let Some((icon, color)) = style.icon {
shape::ToifImage::new(center, icon.toif)
.with_align(Alignment2D::CENTER)
.with_bg(color)
.with_fg(color)
.render(target);
}
}

View File

@ -124,7 +124,7 @@ where
self.confirm_button.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.input.render(target);
self.paragraphs_pad.render(target);
self.paragraphs.render(target);
@ -240,7 +240,7 @@ impl Component for NumberInput {
self.inc.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let mut buf = [0u8; 10];
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {

View File

@ -18,6 +18,8 @@ use super::{
SwipeDirection,
};
use core::cell::Cell;
/// Allows pagination of inner component. Shows scroll bar, confirm & cancel
/// buttons. Optionally handles hold-to-confirm with loader.
pub struct ButtonPage<T, U> {
@ -41,7 +43,7 @@ pub struct ButtonPage<T, U> {
/// Whether to pass-through right swipe to parent component.
swipe_right: bool,
/// Fade to given backlight level on next paint().
fade: Option<u16>,
fade: Cell<Option<u16>>,
}
impl<T> ButtonPage<T, StrBuffer>
@ -77,7 +79,7 @@ where
cancel_from_any_page: false,
swipe_left: false,
swipe_right: false,
fade: None,
fade: Cell::new(None),
}
}
@ -157,7 +159,7 @@ where
// Swipe has dimmed the screen, so fade back to normal backlight after the next
// paint.
self.fade = Some(theme::BACKLIGHT_NORMAL);
self.fade.set(Some(theme::BACKLIGHT_NORMAL));
}
fn is_cancel_visible(&self) -> bool {
@ -413,7 +415,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
match &self.loader {
Some(l) if l.is_animating() => self.loader.render(target),

View File

@ -123,7 +123,7 @@ where
self.description.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.title.render(target);
let center = constant::screen().center() + Offset::y(self.loader_y_offset);

View File

@ -92,7 +92,7 @@ impl<T: AsRef<str>> Component for ResultFooter<'_, T> {
self.text.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// divider line
let bar = Rect::from_center_and_size(
Point::new(self.area.center().x, self.area.y0),
@ -181,7 +181,7 @@ impl<'a, T: StringType> Component for ResultScreen<'a, T> {
self.footer.paint();
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
self.footer_pad.render(target);

View File

@ -124,7 +124,7 @@ impl Component for ScrollBar {
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
fn dotsize(distance: usize, nhidden: usize) -> Icon {
match (nhidden.saturating_sub(distance)).min(2 - distance) {
0 => theme::DOT_INACTIVE,

View File

@ -166,7 +166,7 @@ where
}
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.pad.render(target);
self.content.render(target);
if self.scrollbar.has_pages() {

View File

@ -161,5 +161,5 @@ impl Component for Swipe {
fn paint(&mut self) {}
fn render(&mut self, _target: &mut impl Renderer) {}
fn render<'s>(&'s self, _target: &mut impl Renderer<'s>) {}
}

View File

@ -71,7 +71,7 @@ impl Component for WelcomeScreen {
);
}
fn render(&mut self, target: &mut impl Renderer) {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
let logo = if self.empty_lock {
theme::ICON_LOGO_EMPTY
} else {