diff --git a/core/SConscript.firmware b/core/SConscript.firmware index ae498f4d0e..8e1c015a19 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -23,7 +23,7 @@ SOURCE_MOD = [] PYOPT = ARGUMENTS.get('PYOPT', '1') FROZEN = True -if TREZOR_MODEL in ('1', 'R'): +if TREZOR_MODEL in ('R'): FONT_NORMAL='Font_Unifont_Regular_16' FONT_DEMIBOLD='Font_Unifont_Bold_16' FONT_BOLD='Font_PixelOperator_Bold_8' @@ -386,11 +386,6 @@ elif TREZOR_MODEL in ('R',): 'embed/trezorhal/button.c', 'embed/trezorhal/rgb_led.c', ] -elif TREZOR_MODEL in ('1',): - SOURCE_TREZORHAL += [ - 'embed/trezorhal/button.c', - ] - if DMA2D: CPPDEFINES_MOD += [ @@ -460,12 +455,6 @@ if TREZOR_MODEL in ('T', 'R'): CPU_MODEL = 'STM32F427xx' LD_VARIANT = '' RUST_TARGET = 'thumbv7em-none-eabihf' -elif TREZOR_MODEL in ('1',): - CPU_ASFLAGS = '-mthumb -mcpu=cortex-m3 -mfloat-abi=soft' - CPU_CCFLAGS = '-mthumb -mtune=cortex-m3 -mcpu=cortex-m3 -mfloat-abi=soft ' - CPU_MODEL = 'STM32F405xx' - LD_VARIANT = '' if EVERYTHING else '_min' - RUST_TARGET = 'thumbv7m-none-eabi' else: raise ValueError('Unknown Trezor model') @@ -637,8 +626,6 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/homescreen/tt.py')) elif TREZOR_MODEL in ('R',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/homescreen/tr.py')) - elif TREZOR_MODEL in ('1',): - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/homescreen/t1.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/management/*.py', exclude=[ SOURCE_PY_DIR + 'apps/management/sd_protect.py', diff --git a/core/SConscript.unix b/core/SConscript.unix index 3a6b22d84b..e96336d154 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -24,7 +24,7 @@ PYOPT = ARGUMENTS.get('PYOPT', '1') FROZEN = ARGUMENTS.get('TREZOR_EMULATOR_FROZEN', 0) RASPI = os.getenv('TREZOR_EMULATOR_RASPI') == '1' -if TREZOR_MODEL in ('1', 'R'): +if TREZOR_MODEL in ('R'): FONT_NORMAL='Font_Unifont_Regular_16' FONT_DEMIBOLD='Font_Unifont_Bold_16' FONT_BOLD='Font_PixelOperator_Bold_8' @@ -589,8 +589,6 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/homescreen/tt.py')) elif TREZOR_MODEL in ('R',): SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/homescreen/tr.py')) - elif TREZOR_MODEL in ('1',): - SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/homescreen/t1.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/management/*.py', exclude=[ SOURCE_PY_DIR + 'apps/management/sd_protect.py', diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml index 0e85bab4e5..aab0a8c012 100644 --- a/core/embed/rust/Cargo.toml +++ b/core/embed/rust/Cargo.toml @@ -9,7 +9,6 @@ build = "build.rs" default = ["model_tt"] bitcoin_only = [] model_tt = ["touch", "jpeg"] -model_t1 = ["buttons"] model_tr = ["buttons"] micropython = [] protobuf = ["micropython"] diff --git a/core/embed/rust/src/ui/constant.rs b/core/embed/rust/src/ui/constant.rs index 2216fffe64..91fd83f704 100644 --- a/core/embed/rust/src/ui/constant.rs +++ b/core/embed/rust/src/ui/constant.rs @@ -1,12 +1,6 @@ //! Reexporting the `constant` module according to the //! current feature (Trezor model) -#[cfg(all( - feature = "model_t1", - not(feature = "model_tr"), - not(feature = "model_tt") -))] -pub use super::model_t1::constant::*; #[cfg(all(feature = "model_tr", not(feature = "model_tt")))] pub use super::model_tr::constant::*; #[cfg(feature = "model_tt")] diff --git a/core/embed/rust/src/ui/mod.rs b/core/embed/rust/src/ui/mod.rs index 8019603fba..40da054eee 100644 --- a/core/embed/rust/src/ui/mod.rs +++ b/core/embed/rust/src/ui/mod.rs @@ -14,8 +14,6 @@ pub mod util; #[cfg(feature = "micropython")] pub mod layout; -#[cfg(feature = "model_t1")] -pub mod model_t1; #[cfg(feature = "model_tr")] pub mod model_tr; #[cfg(feature = "model_tt")] diff --git a/core/embed/rust/src/ui/model_t1/component/button.rs b/core/embed/rust/src/ui/model_t1/component/button.rs deleted file mode 100644 index ea6332b044..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/button.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::ui::{ - component::{Component, Event, EventCtx}, - display::{self, Color, Font}, - event::{ButtonEvent, PhysicalButton}, - geometry::{Offset, Point, Rect}, -}; - -use super::theme; - -pub enum ButtonMsg { - Clicked, -} - -#[derive(Copy, Clone)] -pub enum ButtonPos { - Left, - Right, -} - -impl ButtonPos { - fn hit(&self, b: &PhysicalButton) -> bool { - matches!( - (self, b), - (Self::Left, PhysicalButton::Left) | (Self::Right, PhysicalButton::Right) - ) - } -} - -pub struct Button { - area: Rect, - pos: ButtonPos, - baseline: Point, - content: ButtonContent, - styles: ButtonStyleSheet, - state: State, -} - -impl> Button { - pub fn new(pos: ButtonPos, content: ButtonContent, styles: ButtonStyleSheet) -> Self { - Self { - pos, - content, - styles, - baseline: Point::zero(), - area: Rect::zero(), - state: State::Released, - } - } - - pub fn with_text(pos: ButtonPos, text: T, styles: ButtonStyleSheet) -> Self { - Self::new(pos, ButtonContent::Text(text), styles) - } - - pub fn with_icon(pos: ButtonPos, image: &'static [u8], styles: ButtonStyleSheet) -> Self { - Self::new(pos, ButtonContent::Icon(image), styles) - } - - pub fn content(&self) -> &ButtonContent { - &self.content - } - - fn style(&self) -> &ButtonStyle { - match self.state { - State::Released => self.styles.normal, - State::Pressed => self.styles.active, - } - } - - fn set(&mut self, ctx: &mut EventCtx, state: State) { - if self.state != state { - self.state = state; - ctx.request_paint(); - } - } - - fn placement( - area: Rect, - pos: ButtonPos, - content: &ButtonContent, - styles: &ButtonStyleSheet, - ) -> (Rect, Point) { - let border_width = if styles.normal.border_horiz { 2 } else { 0 }; - let content_width = match content { - ButtonContent::Text(text) => styles.normal.font.text_width(text.as_ref()) - 1, - ButtonContent::Icon(_icon) => todo!(), - }; - let button_width = content_width + 2 * border_width; - let area = match pos { - ButtonPos::Left => area.split_left(button_width).0, - ButtonPos::Right => area.split_right(button_width).1, - }; - - let start_of_baseline = area.bottom_left() + Offset::new(border_width, -2); - - (area, start_of_baseline) - } -} - -impl Component for Button -where - T: AsRef, -{ - type Msg = ButtonMsg; - - fn place(&mut self, bounds: Rect) -> Rect { - let (area, baseline) = Self::placement(bounds, self.pos, &self.content, &self.styles); - self.area = area; - self.baseline = baseline; - self.area - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - match event { - Event::Button(ButtonEvent::ButtonPressed(which)) if self.pos.hit(&which) => { - self.set(ctx, State::Pressed); - } - Event::Button(ButtonEvent::ButtonReleased(which)) if self.pos.hit(&which) => { - if matches!(self.state, State::Pressed) { - self.set(ctx, State::Released); - return Some(ButtonMsg::Clicked); - } - } - _ => {} - }; - None - } - - fn paint(&mut self) { - let style = self.style(); - - match &self.content { - ButtonContent::Text(text) => { - let background_color = style.text_color.negate(); - if style.border_horiz { - display::rect_fill_rounded(self.area, background_color, theme::BG, 1); - } else { - display::rect_fill(self.area, background_color) - } - - display::text_left( - self.baseline, - text.as_ref(), - style.font, - style.text_color, - background_color, - ); - } - ButtonContent::Icon(_image) => { - todo!(); - } - } - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Button -where - T: AsRef + crate::trace::Trace, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Button"); - match &self.content { - ButtonContent::Text(text) => t.field("text", text), - ButtonContent::Icon(_) => t.symbol("icon"), - } - t.close(); - } -} - -#[derive(PartialEq, Eq)] -enum State { - Released, - Pressed, -} - -pub enum ButtonContent { - Text(T), - Icon(&'static [u8]), -} - -pub struct ButtonStyleSheet { - pub normal: &'static ButtonStyle, - pub active: &'static ButtonStyle, -} - -pub struct ButtonStyle { - pub font: Font, - pub text_color: Color, - pub border_horiz: bool, -} diff --git a/core/embed/rust/src/ui/model_t1/component/dialog.rs b/core/embed/rust/src/ui/model_t1/component/dialog.rs deleted file mode 100644 index 39820b3b36..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/dialog.rs +++ /dev/null @@ -1,94 +0,0 @@ -use super::button::{Button, ButtonMsg::Clicked}; -use crate::ui::{ - component::{Child, Component, Event, EventCtx}, - display::Font, - geometry::Rect, -}; - -pub enum DialogMsg { - Content(T), - LeftClicked, - RightClicked, -} - -pub struct Dialog { - content: Child, - left_btn: Option>>, - right_btn: Option>>, -} - -impl Dialog -where - T: Component, - U: AsRef, -{ - pub fn new(content: T, left: Option>, right: Option>) -> Self { - Self { - content: Child::new(content), - left_btn: left.map(Child::new), - right_btn: right.map(Child::new), - } - } - - pub fn inner(&self) -> &T { - self.content.inner() - } -} - -impl Component for Dialog -where - T: Component, - U: AsRef, -{ - type Msg = DialogMsg; - - fn place(&mut self, bounds: Rect) -> Rect { - let button_height = Font::BOLD.line_height() + 2; - let (content_area, button_area) = bounds.split_bottom(button_height); - self.content.place(content_area); - self.left_btn.as_mut().map(|b| b.place(button_area)); - self.right_btn.as_mut().map(|b| b.place(button_area)); - bounds - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - if let Some(msg) = self.content.event(ctx, event) { - Some(DialogMsg::Content(msg)) - } else if let Some(Clicked) = self.left_btn.as_mut().and_then(|b| b.event(ctx, event)) { - Some(DialogMsg::LeftClicked) - } else if let Some(Clicked) = self.right_btn.as_mut().and_then(|b| b.event(ctx, event)) { - Some(DialogMsg::RightClicked) - } else { - None - } - } - - fn paint(&mut self) { - self.content.paint(); - if let Some(b) = self.left_btn.as_mut() { - b.paint(); - } - if let Some(b) = self.right_btn.as_mut() { - b.paint(); - } - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Dialog -where - T: crate::trace::Trace, - U: crate::trace::Trace + AsRef, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Dialog"); - t.field("content", &self.content); - if let Some(label) = &self.left_btn { - t.field("left", label); - } - if let Some(label) = &self.right_btn { - t.field("right", label); - } - t.close(); - } -} diff --git a/core/embed/rust/src/ui/model_t1/component/frame.rs b/core/embed/rust/src/ui/model_t1/component/frame.rs deleted file mode 100644 index 28fe85e43d..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/frame.rs +++ /dev/null @@ -1,79 +0,0 @@ -use super::theme; -use crate::ui::{ - component::{Child, Component, Event, EventCtx}, - display::{self, Font}, - geometry::{Insets, Offset, Rect}, -}; - -pub struct Frame { - area: Rect, - title: U, - content: Child, -} - -impl Frame -where - T: Component, - U: AsRef, -{ - pub fn new(title: U, content: T) -> Self { - Self { - title, - area: Rect::zero(), - content: Child::new(content), - } - } - - pub fn inner(&self) -> &T { - self.content.inner() - } -} - -impl Component for Frame -where - T: Component, - U: AsRef, -{ - type Msg = T::Msg; - - fn place(&mut self, bounds: Rect) -> Rect { - const TITLE_SPACE: i16 = 4; - - let (title_area, content_area) = bounds.split_top(Font::BOLD.line_height()); - let content_area = content_area.inset(Insets::top(TITLE_SPACE)); - - self.area = title_area; - self.content.place(content_area); - bounds - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - self.content.event(ctx, event) - } - - fn paint(&mut self) { - display::text_left( - self.area.bottom_left() - Offset::y(2), - self.title.as_ref(), - Font::BOLD, - theme::FG, - theme::BG, - ); - display::dotted_line_horizontal(self.area.bottom_left(), self.area.width(), theme::FG, 2); - self.content.paint(); - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Frame -where - T: crate::trace::Trace, - U: crate::trace::Trace + AsRef, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Frame"); - t.field("title", &self.title); - t.field("content", &self.content); - t.close(); - } -} diff --git a/core/embed/rust/src/ui/model_t1/component/mod.rs b/core/embed/rust/src/ui/model_t1/component/mod.rs deleted file mode 100644 index dc8c10edb5..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod button; -mod dialog; -mod frame; -mod page; - -use super::theme; - -pub use button::{Button, ButtonContent, ButtonMsg, ButtonPos, ButtonStyle, ButtonStyleSheet}; -pub use dialog::{Dialog, DialogMsg}; -pub use frame::Frame; -pub use page::ButtonPage; diff --git a/core/embed/rust/src/ui/model_t1/component/page.rs b/core/embed/rust/src/ui/model_t1/component/page.rs deleted file mode 100644 index dadb410937..0000000000 --- a/core/embed/rust/src/ui/model_t1/component/page.rs +++ /dev/null @@ -1,227 +0,0 @@ -use crate::ui::{ - component::{Component, ComponentExt, Event, EventCtx, Never, Pad, PageMsg, Paginate}, - display::{self, Color, Font}, - geometry::{Insets, Offset, Point, Rect}, -}; - -use super::{theme, Button, ButtonMsg, ButtonPos}; - -pub struct ButtonPage { - content: T, - scrollbar: ScrollBar, - pad: Pad, - prev: Button<&'static str>, - next: Button<&'static str>, - cancel: Button<&'static str>, - confirm: Button<&'static str>, -} - -impl ButtonPage -where - T: Paginate, - T: Component, -{ - pub fn new(content: T, background: Color) -> Self { - Self { - content, - scrollbar: ScrollBar::vertical(), - pad: Pad::with_background(background), - prev: Button::with_text(ButtonPos::Left, "BACK", theme::button_cancel()), - next: Button::with_text(ButtonPos::Right, "NEXT", theme::button_default()), - cancel: Button::with_text(ButtonPos::Left, "CANCEL", theme::button_cancel()), - confirm: Button::with_text(ButtonPos::Right, "CONFIRM", theme::button_default()), - } - } - - fn change_page(&mut self, ctx: &mut EventCtx, page: usize) { - // Change the page in the content, clear the background under it and make sure - // it gets completely repainted. - self.content.change_page(page); - self.content.request_complete_repaint(ctx); - self.pad.clear(); - } -} - -impl Component for ButtonPage -where - T: Component, - T: Paginate, -{ - type Msg = PageMsg; - - fn place(&mut self, bounds: Rect) -> Rect { - let button_height = Font::BOLD.line_height() + 2; - let (content_area, button_area) = bounds.split_bottom(button_height); - let (content_area, scrollbar_area) = content_area.split_right(ScrollBar::WIDTH); - let content_area = content_area.inset(Insets::top(1)); - self.pad.place(bounds); - self.content.place(content_area); - let page_count = self.content.page_count(); - self.scrollbar.set_count_and_active_page(page_count, 0); - self.scrollbar.place(scrollbar_area); - self.prev.place(button_area); - self.next.place(button_area); - self.cancel.place(button_area); - self.confirm.place(button_area); - bounds - } - - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - ctx.set_page_count(self.scrollbar.page_count); - if self.scrollbar.has_previous_page() { - if let Some(ButtonMsg::Clicked) = self.prev.event(ctx, event) { - // Scroll up. - self.scrollbar.go_to_previous_page(); - self.change_page(ctx, self.scrollbar.active_page); - return None; - } - } else if let Some(ButtonMsg::Clicked) = self.cancel.event(ctx, event) { - return Some(PageMsg::Controls(false)); - } - - if self.scrollbar.has_next_page() { - if let Some(ButtonMsg::Clicked) = self.next.event(ctx, event) { - // Scroll down. - self.scrollbar.go_to_next_page(); - self.change_page(ctx, self.scrollbar.active_page); - return None; - } - } else if let Some(ButtonMsg::Clicked) = self.confirm.event(ctx, event) { - return Some(PageMsg::Controls(true)); - } - - if let Some(msg) = self.content.event(ctx, event) { - return Some(PageMsg::Content(msg)); - } - None - } - - fn paint(&mut self) { - self.pad.paint(); - self.content.paint(); - self.scrollbar.paint(); - if self.scrollbar.has_previous_page() { - self.prev.paint(); - } else { - self.cancel.paint(); - } - if self.scrollbar.has_next_page() { - self.next.paint(); - } else { - self.confirm.paint(); - } - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for ButtonPage -where - T: crate::trace::Trace, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("ButtonPage"); - t.field("active_page", &self.scrollbar.active_page); - t.field("page_count", &self.scrollbar.page_count); - t.field("content", &self.content); - t.close(); - } -} - -pub struct ScrollBar { - area: Rect, - page_count: usize, - active_page: usize, -} - -impl ScrollBar { - pub const WIDTH: i16 = 8; - pub const DOT_SIZE: Offset = Offset::new(4, 4); - pub const DOT_INTERVAL: i16 = 6; - - pub fn vertical() -> Self { - Self { - area: Rect::zero(), - page_count: 0, - active_page: 0, - } - } - - pub fn set_count_and_active_page(&mut self, page_count: usize, active_page: usize) { - self.page_count = page_count; - self.active_page = active_page; - } - - pub fn has_next_page(&self) -> bool { - self.active_page < self.page_count - 1 - } - - pub fn has_previous_page(&self) -> bool { - self.active_page > 0 - } - - pub fn go_to_next_page(&mut self) { - self.active_page = self.active_page.saturating_add(1).min(self.page_count - 1); - } - - pub fn go_to_previous_page(&mut self) { - self.active_page = self.active_page.saturating_sub(1); - } - - fn paint_dot(&self, active: bool, top_left: Point) { - let sides = [ - Rect::from_top_left_and_size(top_left + Offset::x(1), Offset::new(2, 1)), - Rect::from_top_left_and_size(top_left + Offset::y(1), Offset::new(1, 2)), - Rect::from_top_left_and_size( - top_left + Offset::new(1, Self::DOT_SIZE.y - 1), - Offset::new(2, 1), - ), - Rect::from_top_left_and_size( - top_left + Offset::new(Self::DOT_SIZE.x - 1, 1), - Offset::new(1, 2), - ), - ]; - for side in sides { - display::rect_fill(side, theme::FG) - } - if active { - display::rect_fill( - Rect::from_top_left_and_size(top_left, Self::DOT_SIZE).inset(Insets::uniform(1)), - theme::FG, - ) - } - } -} - -impl Component for ScrollBar { - type Msg = Never; - - fn place(&mut self, bounds: Rect) -> Rect { - self.area = bounds; - self.area - } - - fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option { - None - } - - fn paint(&mut self) { - let count = self.page_count as i16; - let interval = { - let available_height = self.area.height(); - let naive_height = count * Self::DOT_INTERVAL; - if naive_height > available_height { - available_height / count - } else { - Self::DOT_INTERVAL - } - }; - let mut dot = Point::new( - self.area.center().x - Self::DOT_SIZE.x / 2, - self.area.center().y - (count / 2) * interval, - ); - for i in 0..self.page_count { - self.paint_dot(i == self.active_page, dot); - dot.y += interval - } - } -} diff --git a/core/embed/rust/src/ui/model_t1/constant.rs b/core/embed/rust/src/ui/model_t1/constant.rs deleted file mode 100644 index d4de5b79bf..0000000000 --- a/core/embed/rust/src/ui/model_t1/constant.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::ui::geometry::{Offset, Point, Rect}; - -pub const WIDTH: i16 = 128; -pub const HEIGHT: i16 = 64; -pub const LINE_SPACE: i16 = 1; -pub const FONT_BPP: i16 = 1; - -pub const fn size() -> Offset { - Offset::new(WIDTH, HEIGHT) -} - -pub const fn screen() -> Rect { - Rect::from_top_left_and_size(Point::zero(), size()) -} diff --git a/core/embed/rust/src/ui/model_t1/layout.rs b/core/embed/rust/src/ui/model_t1/layout.rs deleted file mode 100644 index 478dd3828d..0000000000 --- a/core/embed/rust/src/ui/model_t1/layout.rs +++ /dev/null @@ -1,244 +0,0 @@ -use core::convert::TryInto; - -use crate::{ - error::Error, - micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr, util}, - ui::{ - component::{ - base::Component, - paginated::{PageMsg, Paginate}, - text::paragraphs::{Paragraph, Paragraphs}, - FormattedText, - }, - layout::{ - obj::{ComponentMsgObj, LayoutObj}, - result::{CANCELLED, CONFIRMED}, - util::upy_disable_animation, - }, - }, -}; - -use super::{ - component::{Button, ButtonPage, ButtonPos, Frame}, - theme, -}; - -impl ComponentMsgObj for ButtonPage -where - T: Component + Paginate, -{ - fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { - match msg { - PageMsg::Content(_) => Err(Error::TypeError), - PageMsg::Controls(true) => Ok(CONFIRMED.as_obj()), - PageMsg::Controls(false) => Ok(CANCELLED.as_obj()), - PageMsg::GoBack => unreachable!(), - } - } -} - -impl ComponentMsgObj for Frame -where - T: ComponentMsgObj, - U: AsRef, -{ - fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { - self.inner().msg_try_into_obj(msg) - } -} - -extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = |_args: &[Obj], kwargs: &Map| { - let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let action: Option = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?; - let description: Option = - kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; - let verb: Option = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?; - let verb_cancel: Option = - kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into_option()?; - let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?; - - let format = match (&action, &description, reverse) { - (Some(_), Some(_), false) => "{Font::bold}{action}\n\r{Font::normal}{description}", - (Some(_), Some(_), true) => "{Font::normal}{description}\n\r{Font::bold}{action}", - (Some(_), None, _) => "{Font::bold}{action}", - (None, Some(_), _) => "{Font::normal}{description}", - _ => "", - }; - - let _left = verb_cancel - .map(|label| Button::with_text(ButtonPos::Left, label, theme::button_cancel())); - let _right = - verb.map(|label| Button::with_text(ButtonPos::Right, label, theme::button_default())); - - let obj = LayoutObj::new(Frame::new( - title, - ButtonPage::new( - FormattedText::new(theme::TEXT_NORMAL, theme::FORMATTED, format) - .with("action", action.unwrap_or_default()) - .with("description", description.unwrap_or_default()), - theme::BG, - ), - ))?; - Ok(obj.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - -extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = |_args: &[Obj], kwargs: &Map| { - let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let data: StrBuffer = kwargs.get(Qstr::MP_QSTR_data)?.try_into()?; - let description: Option = - kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?; - - let obj = LayoutObj::new(Frame::new( - title, - ButtonPage::new( - Paragraphs::new([ - Paragraph::new(&theme::TEXT_NORMAL, description.unwrap_or_default()), - Paragraph::new(&theme::TEXT_BOLD, data), - ]), - theme::BG, - ), - ))?; - Ok(obj.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - -#[no_mangle] -pub static mp_module_trezorui2: Module = obj_module! { - Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(), - - /// CONFIRMED: object - Qstr::MP_QSTR_CONFIRMED => CONFIRMED.as_obj(), - - /// CANCELLED: object - Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(), - - /// def disable_animation(disable: bool) -> None: - /// """Disable animations, debug builds only.""" - Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(), - - /// def confirm_action( - /// *, - /// title: str, - /// action: str | None = None, - /// description: str | None = None, - /// verb: str | None = None, - /// verb_cancel: str | None = None, - /// hold: bool | None = None, - /// reverse: bool = False, - /// ) -> object: - /// """Confirm action.""" - Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(), - - /// def confirm_text( - /// *, - /// title: str, - /// data: str, - /// description: str | None, - /// ) -> object: - /// """Confirm text.""" - Qstr::MP_QSTR_confirm_text => obj_fn_kw!(0, new_confirm_text).as_obj(), -}; - -#[cfg(test)] -mod tests { - use crate::{ - trace::Trace, - ui::{ - component::Component, - model_t1::{ - component::{Dialog, DialogMsg}, - constant, - }, - }, - }; - - use super::*; - - fn trace(val: &impl Trace) -> String { - let mut t = Vec::new(); - val.trace(&mut t); - String::from_utf8(t).unwrap() - } - - impl ComponentMsgObj for Dialog - where - T: ComponentMsgObj, - U: AsRef, - { - fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { - match msg { - DialogMsg::Content(c) => self.inner().msg_try_into_obj(c), - DialogMsg::LeftClicked => Ok(CANCELLED.as_obj()), - DialogMsg::RightClicked => Ok(CONFIRMED.as_obj()), - } - } - } - - #[test] - fn trace_example_layout() { - let mut layout = Dialog::new( - FormattedText::new( - theme::TEXT_NORMAL, - theme::FORMATTED, - "Testing text layout, with some text, and some more text. And {param}", - ) - .with("param", "parameters!"), - Some(Button::with_text( - ButtonPos::Left, - "Left", - theme::button_cancel(), - )), - Some(Button::with_text( - ButtonPos::Right, - "Right", - theme::button_default(), - )), - ); - layout.place(constant::screen()); - assert_eq!( - trace(&layout), - r#" left: