diff --git a/core/embed/rust/src/ui/model_tt/component/frame.rs b/core/embed/rust/src/ui/model_tt/component/frame.rs index c44ad434af..4102ee78e3 100644 --- a/core/embed/rust/src/ui/model_tt/component/frame.rs +++ b/core/embed/rust/src/ui/model_tt/component/frame.rs @@ -1,15 +1,14 @@ use super::theme; use crate::ui::{ - component::{text::TextStyle, Child, Component, Event, EventCtx}, + component::{label::Label, text::TextStyle, Child, Component, Event, EventCtx}, display::{self, Color, Font}, - geometry::{Insets, Offset, Rect}, + geometry::{Alignment, Insets, Offset, Rect}, util::icon_text_center, }; pub struct Frame { - area: Rect, border: Insets, - title: U, + title: Child>, content: Child, } @@ -18,15 +17,26 @@ where T: Component, U: AsRef, { - pub fn new(title: U, content: T) -> Self { + pub fn new(style: TextStyle, alignment: Alignment, title: U, content: T) -> Self { Self { - title, - area: Rect::zero(), + title: Child::new(Label::new(title, alignment, style)), border: theme::borders_scroll(), content: Child::new(content), } } + pub fn left_aligned(style: TextStyle, title: U, content: T) -> Self { + Self::new(style, Alignment::Start, title, content) + } + + pub fn right_aligned(style: TextStyle, title: U, content: T) -> Self { + Self::new(style, Alignment::End, title, content) + } + + pub fn centered(style: TextStyle, title: U, content: T) -> Self { + Self::new(style, Alignment::Center, title, content) + } + pub fn with_border(mut self, border: Insets) -> Self { self.border = border; self @@ -50,31 +60,26 @@ where let (title_area, content_area) = bounds .inset(self.border) .split_top(Font::BOLD.text_height()); - let title_area = title_area.inset(Insets::left(theme::CONTENT_BORDER)); + let title_area = title_area.inset(Insets::sides(theme::CONTENT_BORDER)); let content_area = content_area.inset(Insets::top(TITLE_SPACE)); - self.area = title_area; + self.title.place(title_area); self.content.place(content_area); bounds } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { + self.title.event(ctx, event); self.content.event(ctx, event) } fn paint(&mut self) { - display::text( - self.area.bottom_left(), - self.title.as_ref(), - Font::BOLD, - theme::GREY_LIGHT, - theme::BG, - ); + self.title.paint(); self.content.paint(); } fn bounds(&self, sink: &mut dyn FnMut(Rect)) { - sink(self.area); + self.title.bounds(sink); self.content.bounds(sink); } } diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index e826858482..3682535857 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -354,10 +354,11 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M } else { SwipeHoldPage::new(paragraphs, theme::BG) }; - LayoutObj::new(Frame::new(title, page))? + LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))? } else { let buttons = Button::cancel_confirm_text(verb_cancel, verb); - LayoutObj::new(Frame::new( + LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipePage::new(paragraphs, buttons, theme::BG), ))? @@ -387,10 +388,15 @@ fn confirm_blob( .into_paragraphs(); let obj = if hold { - LayoutObj::new(Frame::new(title, SwipeHoldPage::new(paragraphs, theme::BG)))? + LayoutObj::new(Frame::left_aligned( + theme::label_title(), + title, + SwipeHoldPage::new(paragraphs, theme::BG), + ))? } else if let Some(verb) = verb { let buttons = Button::cancel_confirm_text(verb_cancel, verb); - LayoutObj::new(Frame::new( + LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipePage::new(paragraphs, buttons, theme::BG), ))? @@ -441,13 +447,15 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m &theme::TEXT_MONO, )?; let obj = if hold { - LayoutObj::new(Frame::new( + LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipeHoldPage::new(paragraphs.into_paragraphs(), theme::BG), ))? } else { let buttons = Button::cancel_confirm_text(None, "CONFIRM"); - LayoutObj::new(Frame::new( + LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipePage::new(paragraphs.into_paragraphs(), buttons, theme::BG), ))? @@ -471,7 +479,8 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: ]); let buttons = Button::cancel_confirm_text(None, "CONTINUE"); - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipePage::new(paragraphs, buttons, theme::BG), ))?; @@ -494,7 +503,8 @@ extern "C" fn new_show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) -> ); let obj = LayoutObj::new( - Frame::new( + Frame::left_aligned( + theme::label_title(), title, Dialog::new( painter::qrcode_painter(address, theme::QR_SIDE_MAX, case_sensitive), @@ -538,7 +548,8 @@ extern "C" fn new_confirm_joint_total(n_args: usize, args: *const Obj, kwargs: * Paragraph::new(&theme::TEXT_MONO, total_amount), ]); - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), "JOINT TRANSACTION", SwipeHoldPage::new(paragraphs, theme::BG), ))?; @@ -575,7 +586,8 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: 2, ); - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), "MODIFY AMOUNT", SwipePage::new(paragraphs, buttons, theme::BG), ))?; @@ -609,7 +621,8 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m 2, ); - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), "MODIFY FEE", SwipePage::new(paragraphs, buttons, theme::BG), ))?; @@ -712,14 +725,16 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map }; let controls = Button::cancel_confirm( - Button::with_icon(theme::ICON_CANCEL).styled(theme::button_cancel()), + Button::with_icon(theme::ICON_CANCEL), Button::with_text("CONFIRM").styled(theme::button_confirm()), 2, ); let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls); - let obj = LayoutObj::new(Frame::new(title, fido_page).with_border(theme::borders()))?; + let obj = LayoutObj::new( + Frame::centered(theme::label_title(), title, fido_page).with_border(theme::borders()), + )?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -775,7 +790,8 @@ extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; let obj = if let Some(t) = title { - LayoutObj::new(Frame::new( + LayoutObj::new(Frame::left_aligned( + theme::label_title(), t, Dialog::new( Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]), @@ -827,8 +843,12 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu let buttons = Button::cancel_info_confirm(button, info_button); let obj = LayoutObj::new( - Frame::new(title, Dialog::new(paragraphs.into_paragraphs(), buttons)) - .with_border(theme::borders()), + Frame::left_aligned( + theme::label_title(), + title, + Dialog::new(paragraphs.into_paragraphs(), buttons), + ) + .with_border(theme::borders()), )?; Ok(obj.into()) }; @@ -857,7 +877,8 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) })); - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipePage::new(paragraphs.into_paragraphs(), button, theme::BG).with_back_button(), ))?; @@ -878,7 +899,8 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut Paragraph::new(&theme::TEXT_BOLD, max_feerate), ]); - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), "AUTHORIZE COINJOIN", SwipeHoldPage::new(paragraphs, theme::BG), ))?; @@ -937,7 +959,8 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]); let buttons = Button::select_word(words); - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipePage::new(paragraphs, buttons, theme::BG), ))?; @@ -959,7 +982,8 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut paragraphs.add(Paragraph::new(&theme::TEXT_MONO, text).break_after()); } - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, SwipeHoldPage::without_cancel(paragraphs.into_paragraphs(), theme::BG), ))?; @@ -986,7 +1010,8 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M }; let obj = LayoutObj::new( - Frame::new( + Frame::left_aligned( + theme::label_title(), title, NumberInputDialog::new(min_count, max_count, count, callback), ) @@ -1018,7 +1043,8 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M } let obj = LayoutObj::new( - Frame::new( + Frame::left_aligned( + theme::label_title(), title, Dialog::new( Checklist::from_paragraphs( @@ -1109,8 +1135,12 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu ); let obj = LayoutObj::new( - Frame::new(title, Dialog::new(paragraphs, SelectWordCount::new())) - .with_border(theme::borders()), + Frame::left_aligned( + theme::label_title(), + title, + Dialog::new(paragraphs, SelectWordCount::new()), + ) + .with_border(theme::borders()), )?; Ok(obj.into()) }; @@ -1151,7 +1181,8 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs: .add(Paragraph::new(&theme::TEXT_NORMAL, description).break_after()); } - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), "REMAINING SHARES", SwipePage::new( paragraphs.into_paragraphs(), @@ -1227,7 +1258,8 @@ extern "C" fn new_show_busyscreen(n_args: usize, args: *const Obj, kwargs: *mut let time_ms: u32 = kwargs.get(Qstr::MP_QSTR_time_ms)?.try_into()?; let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?; - let obj = LayoutObj::new(Frame::new( + let obj = LayoutObj::new(Frame::left_aligned( + theme::label_title(), title, Dialog::new( Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, description).centered()), diff --git a/core/embed/rust/src/ui/model_tt/theme.rs b/core/embed/rust/src/ui/model_tt/theme.rs index 7460456300..0c5f333761 100644 --- a/core/embed/rust/src/ui/model_tt/theme.rs +++ b/core/embed/rust/src/ui/model_tt/theme.rs @@ -125,6 +125,10 @@ pub const fn label_progress() -> TextStyle { TEXT_BOLD } +pub const fn label_title() -> TextStyle { + TextStyle::new(Font::BOLD, GREY_LIGHT, BG, GREY_LIGHT, GREY_LIGHT) +} + pub fn button_default() -> ButtonStyleSheet { ButtonStyleSheet { normal: &ButtonStyle { diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index a3569f4afb..275955bb62 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -1684,7 +1684,7 @@ "TT_tezos-test_sign_tx.py::test_tezos_smart_contract_delegation": "8e87bb8ce0de9197f04e9059feeff5397be454b4cba6ca70ba2d6814f2583f14", "TT_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer": "ce56ce9d3ae4d75cc14d4897352938ff52e9cf68f14dba1407a32c94c4110ca3", "TT_tezos-test_sign_tx.py::test_tezos_smart_contract_transfer_to_contract": "5776f71c1f1f6ccc81cfa941c82e544fa8f4c29cea7622d38bd82edd513137a6", -"TT_webauthn-test_msg_webauthn.py::test_add_remove": "eba0b7596430b530df1440f35a33a713192da8e0433a53d3cc30b98a12dc2841", +"TT_webauthn-test_msg_webauthn.py::test_add_remove": "7a74a2944c22536ed96568db71bfea1a3838b450f872fb26b907d9828b441d39", "TT_webauthn-test_u2f_counter.py::test_u2f_counter": "6bf6db360292afbda963a1133152100a98378d05796a10fb505d757b39ede2aa", "TT_zcash-test_sign_tx.py::test_external_presigned": "cdb1416def8474959540b6312d77199ce7cd42ae2a2a44957eaaa977b8f21e23", "TT_zcash-test_sign_tx.py::test_one_two": "7f6911261bdd703469b0a32ed3d9d4e6abfdc6ba186b2975d6a12f130292ed18",