diff --git a/core/embed/rust/src/ui/component/image.rs b/core/embed/rust/src/ui/component/image.rs index 7a5b9d46f..fb590b608 100644 --- a/core/embed/rust/src/ui/component/image.rs +++ b/core/embed/rust/src/ui/component/image.rs @@ -104,6 +104,10 @@ impl BlendedImage { self.area_color, ); } + + pub fn height(&self) -> i16 { + self.bg.toif.size().y + } } impl Component for BlendedImage { diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index fbb26b55c..358444009 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -156,6 +156,13 @@ where chr = 0; } } + + pub fn current_height(&self) -> i16 { + self.visible + .iter() + .map(|layout| layout.bounds.height()) + .sum() + } } impl Component for Paragraphs diff --git a/core/embed/rust/src/ui/model_tt/component/dialog.rs b/core/embed/rust/src/ui/model_tt/component/dialog.rs index bbb496f42..79efdaa58 100644 --- a/core/embed/rust/src/ui/model_tt/component/dialog.rs +++ b/core/embed/rust/src/ui/model_tt/component/dialog.rs @@ -7,9 +7,10 @@ use crate::{ paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt}, TextStyle, }, - Child, Component, Event, EventCtx, Never, + Child, Component, Event, EventCtx, Never, Paginate, }, - geometry::{Insets, LinearPlacement, Rect}, + geometry::{Insets, Rect}, + util::ResultExt, }, }; @@ -102,6 +103,10 @@ where T: StringType, U: Component, { + pub const ICON_AREA_PADDING: i16 = 2; + pub const TEXT_BUTTON_PADDING: i16 = 15; + pub const TEXT_ICON_PADDING: i16 = 20; + pub fn new(icon: BlendedImage, title: T, controls: U) -> Self { Self { image: Child::new(icon), @@ -109,12 +114,19 @@ where &theme::TEXT_DEMIBOLD, title, ) - .centered()])) - .with_placement( - LinearPlacement::vertical() - .align_at_center() - .with_spacing(Self::VALUE_SPACE), - ), + .centered()])), + controls: Child::new(controls), + } + } + + pub fn from_paragraphs( + icon: BlendedImage, + paragraphs: Paragraphs>, + controls: U, + ) -> Self { + Self { + image: Child::new(icon), + paragraphs, controls: Child::new(controls), } } @@ -148,15 +160,10 @@ where Paragraph::new(&theme::TEXT_NORMAL_OFF_WHITE, l2).centered(), Paragraph::new(&theme::TEXT_DEMIBOLD, l3).centered(), ]) - .into_paragraphs() - .with_placement(LinearPlacement::vertical().align_at_center()), + .into_paragraphs(), controls: Child::new(controls), } } - - pub const ICON_AREA_PADDING: i16 = 2; - pub const ICON_AREA_HEIGHT: i16 = 60; - pub const VALUE_SPACE: i16 = 5; } impl Component for IconDialog @@ -172,9 +179,24 @@ where .inset(Insets::top(Self::ICON_AREA_PADDING)); let controls_area = self.controls.place(bounds); - let content_area = bounds.inset(Insets::bottom(controls_area.height())); + let content_area = bounds.inset(Insets::bottom( + controls_area.height() + Self::TEXT_BUTTON_PADDING, + )); - let (image_area, content_area) = content_area.split_top(Self::ICON_AREA_HEIGHT); + // Determining the height of the content and image for placing them correctly. + // There are always fixes paddings between components. + self.paragraphs.place(content_area); + // if self.paragraphs.page_count() > 1 { + // Err::<(), ()>(()).assert_if_debugging_ui("Must be single page"); + // } + let content_height = self.paragraphs.current_height(); + let (image_area, content_area) = content_area.split_bottom(content_height); + let image_area = image_area.inset(Insets::bottom(Self::TEXT_ICON_PADDING)); + let image_height = self.image.inner().height(); + // if image_height < image_area.height() { + // Err::<(), ()>(()).assert_if_debugging_ui("Not enough space for image"); + // } + let (_, image_area) = image_area.split_bottom(image_height); self.image.place(image_area); self.paragraphs.place(content_area); diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 54323f2bc..1562c3b13 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -1065,6 +1065,13 @@ extern "C" fn new_show_mismatch() -> Obj { let url: StrBuffer = "trezor.io/support".into(); let button = "QUIT"; + let paragraphs = ParagraphVecShort::from_iter([ + Paragraph::new(&theme::TEXT_DEMIBOLD, title).centered(), + Paragraph::new(&theme::TEXT_NORMAL_OFF_WHITE, description).centered(), + Paragraph::new(&theme::TEXT_DEMIBOLD, url).centered(), + ]) + .into_paragraphs(); + let icon = BlendedImage::new( theme::IMAGE_BG_OCTAGON, theme::IMAGE_FG_WARN, @@ -1072,19 +1079,15 @@ extern "C" fn new_show_mismatch() -> Obj { theme::FG, theme::BG, ); - let obj = LayoutObj::new( - IconDialog::new( - icon, - title, - Button::cancel_confirm( - Button::with_icon(theme::ICON_BACK), - Button::with_text(button).styled(theme::button_reset()), - true, - ), - ) - .with_description(description) - .with_text(&theme::TEXT_DEMIBOLD, url), - )?; + let obj = LayoutObj::new(IconDialog::from_paragraphs( + icon, + paragraphs, + Button::cancel_confirm( + Button::with_icon(theme::ICON_BACK), + Button::with_text(button).styled(theme::button_reset()), + true, + ), + ))?; Ok(obj.into()) }; diff --git a/core/src/trezor/ui/layouts/tt_v2/__init__.py b/core/src/trezor/ui/layouts/tt_v2/__init__.py index 3604ccfe2..bb4f3aaf1 100644 --- a/core/src/trezor/ui/layouts/tt_v2/__init__.py +++ b/core/src/trezor/ui/layouts/tt_v2/__init__.py @@ -1102,6 +1102,9 @@ async def confirm_signverify( ) +# TODO: currently used only in FIDO +# If we used button instead of timeout, we could use show_warning there +# and delete this completely async def show_error_popup( title: str, description: str, @@ -1197,16 +1200,16 @@ async def pin_mismatch_popup( await button_request("pin_mismatch", code=BR_TYPE_OTHER) title = "Wipe code mismatch" if is_wipe_code else "PIN mismatch" description = "wipe codes" if is_wipe_code else "PINs" - return await show_error_popup( + return await show_warning( title, - f"The {description} you entered do not match.", + f"Entered {description} do not match!", button="TRY AGAIN", ) async def wipe_code_same_as_pin_popup() -> None: await button_request("wipe_code_same_as_pin", code=BR_TYPE_OTHER) - return await show_error_popup( + return await show_warning( "Invalid wipe code", "The wipe code must be different from your PIN.", button="TRY AGAIN",