1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-23 15:08:19 +00:00

WIP - replacing T: AsRef<str> by StrBuffer

This commit is contained in:
grdddj 2023-01-01 14:05:49 +01:00
parent e5c52da136
commit 96c10e9b42
20 changed files with 269 additions and 382 deletions

View File

@ -104,7 +104,7 @@ impl Font {
} }
#[cfg(feature = "model_tr")] #[cfg(feature = "model_tr")]
impl<T: Clone + AsRef<str>> ButtonDetails<T> { impl ButtonDetails {
pub fn print(&self) { pub fn print(&self) {
let text: String<20> = if let Some(text) = self.text.clone() { let text: String<20> = if let Some(text) = self.text.clone() {
text.as_ref().into() text.as_ref().into()

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
micropython::buffer::StrBuffer,
time::Duration, time::Duration,
ui::{ ui::{
component::{Component, Event, EventCtx}, component::{Component, Event, EventCtx},
@ -27,16 +28,16 @@ pub enum ButtonPos {
Right, Right,
} }
pub struct Button<T> { pub struct Button {
bounds: Rect, bounds: Rect,
pos: ButtonPos, pos: ButtonPos,
content: ButtonContent<T>, content: ButtonContent,
styles: ButtonStyleSheet, styles: ButtonStyleSheet,
state: State, state: State,
} }
impl<T: AsRef<str>> Button<T> { impl Button {
pub fn new(pos: ButtonPos, content: ButtonContent<T>, styles: ButtonStyleSheet) -> Self { pub fn new(pos: ButtonPos, content: ButtonContent, styles: ButtonStyleSheet) -> Self {
Self { Self {
pos, pos,
content, content,
@ -46,7 +47,7 @@ impl<T: AsRef<str>> Button<T> {
} }
} }
pub fn with_text(pos: ButtonPos, text: T, styles: ButtonStyleSheet) -> Self { pub fn with_text(pos: ButtonPos, text: StrBuffer, styles: ButtonStyleSheet) -> Self {
Self::new(pos, ButtonContent::Text(text), styles) Self::new(pos, ButtonContent::Text(text), styles)
} }
@ -54,7 +55,7 @@ impl<T: AsRef<str>> Button<T> {
Self::new(pos, ButtonContent::Icon(image), styles) Self::new(pos, ButtonContent::Icon(image), styles)
} }
pub fn content(&self) -> &ButtonContent<T> { pub fn content(&self) -> &ButtonContent {
&self.content &self.content
} }
@ -71,7 +72,7 @@ impl<T: AsRef<str>> Button<T> {
} }
/// Changing the text content of the button. /// Changing the text content of the button.
pub fn set_text(&mut self, text: T) { pub fn set_text(&mut self, text: StrBuffer) {
self.content = ButtonContent::Text(text); self.content = ButtonContent::Text(text);
} }
@ -167,10 +168,7 @@ impl<T: AsRef<str>> Button<T> {
} }
} }
impl<T> Component for Button<T> impl Component for Button {
where
T: AsRef<str>,
{
type Msg = ButtonMsg; type Msg = ButtonMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -265,10 +263,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Button<T> impl crate::trace::Trace for Button {
where
T: AsRef<str> + crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Button"); t.open("Button");
match &self.content { match &self.content {
@ -285,8 +280,8 @@ enum State {
Pressed, Pressed,
} }
pub enum ButtonContent<T> { pub enum ButtonContent {
Text(T), Text(StrBuffer),
Icon(Icon), Icon(Icon),
} }
@ -353,9 +348,9 @@ impl ButtonStyleSheet {
} }
/// Describing the button on the screen - only visuals. /// Describing the button on the screen - only visuals.
#[derive(Clone, Copy)] #[derive(Clone)]
pub struct ButtonDetails<T> { pub struct ButtonDetails {
pub text: Option<T>, pub text: Option<StrBuffer>,
pub icon: Option<Icon>, pub icon: Option<Icon>,
pub duration: Option<Duration>, pub duration: Option<Duration>,
pub with_outline: bool, pub with_outline: bool,
@ -364,9 +359,9 @@ pub struct ButtonDetails<T> {
pub offset: Option<Offset>, pub offset: Option<Offset>,
} }
impl<T: Clone + AsRef<str>> ButtonDetails<T> { impl ButtonDetails {
/// Text button. /// Text button.
pub fn text(text: T) -> Self { pub fn text(text: StrBuffer) -> Self {
Self { Self {
text: Some(text), text: Some(text),
icon: None, icon: None,
@ -392,7 +387,7 @@ impl<T: Clone + AsRef<str>> ButtonDetails<T> {
} }
/// Text with arms signalling double press. /// Text with arms signalling double press.
pub fn armed_text(text: T) -> Self { pub fn armed_text(text: StrBuffer) -> Self {
Self::text(text).with_arms() Self::text(text).with_arms()
} }
@ -489,17 +484,17 @@ impl<T: Clone + AsRef<str>> ButtonDetails<T> {
/// Holding the button details for all three possible buttons. /// Holding the button details for all three possible buttons.
#[derive(Clone)] #[derive(Clone)]
pub struct ButtonLayout<T> { pub struct ButtonLayout {
pub btn_left: Option<ButtonDetails<T>>, pub btn_left: Option<ButtonDetails>,
pub btn_middle: Option<ButtonDetails<T>>, pub btn_middle: Option<ButtonDetails>,
pub btn_right: Option<ButtonDetails<T>>, pub btn_right: Option<ButtonDetails>,
} }
impl<T: AsRef<str>> ButtonLayout<T> { impl ButtonLayout {
pub fn new( pub fn new(
btn_left: Option<ButtonDetails<T>>, btn_left: Option<ButtonDetails>,
btn_middle: Option<ButtonDetails<T>>, btn_middle: Option<ButtonDetails>,
btn_right: Option<ButtonDetails<T>>, btn_right: Option<ButtonDetails>,
) -> Self { ) -> Self {
Self { Self {
btn_left, btn_left,
@ -513,16 +508,14 @@ impl<T: AsRef<str>> ButtonLayout<T> {
pub fn empty() -> Self { pub fn empty() -> Self {
Self::new(None, None, None) Self::new(None, None, None)
} }
}
impl ButtonLayout<&'static str> {
/// Default button layout for all three buttons - icons. /// Default button layout for all three buttons - icons.
pub fn default_three_icons() -> Self { pub fn default_three_icons() -> Self {
Self::three_icons_middle_text("SELECT") Self::three_icons_middle_text("SELECT".into())
} }
/// Special middle text for default icon layout. /// Special middle text for default icon layout.
pub fn three_icons_middle_text(middle_text: &'static str) -> Self { pub fn three_icons_middle_text(middle_text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::left_arrow_icon()), Some(ButtonDetails::left_arrow_icon()),
Some(ButtonDetails::armed_text(middle_text)), Some(ButtonDetails::armed_text(middle_text)),
@ -531,7 +524,7 @@ impl ButtonLayout<&'static str> {
} }
/// Left and right texts. /// Left and right texts.
pub fn left_right_text(text_left: &'static str, text_right: &'static str) -> Self { pub fn left_right_text(text_left: StrBuffer, text_right: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::text(text_left)), Some(ButtonDetails::text(text_left)),
None, None,
@ -540,7 +533,7 @@ impl ButtonLayout<&'static str> {
} }
/// Only right text. /// Only right text.
pub fn only_right_text(text_right: &'static str) -> Self { pub fn only_right_text(text_right: StrBuffer) -> Self {
Self::new(None, None, Some(ButtonDetails::text(text_right))) Self::new(None, None, Some(ButtonDetails::text(text_right)))
} }
@ -572,7 +565,7 @@ impl ButtonLayout<&'static str> {
} }
/// Cancel cross on left and text on the right. /// Cancel cross on left and text on the right.
pub fn cancel_and_text(text: &'static str) -> Self { pub fn cancel_and_text(text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
@ -581,7 +574,7 @@ impl ButtonLayout<&'static str> {
} }
/// Cancel cross on left and hold-to-confirm text on the right. /// Cancel cross on left and hold-to-confirm text on the right.
pub fn cancel_and_htc_text(text: &'static str, duration: Duration) -> Self { pub fn cancel_and_htc_text(text: StrBuffer, duration: Duration) -> Self {
Self::new( Self::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
@ -590,7 +583,7 @@ impl ButtonLayout<&'static str> {
} }
/// Arrow back on left and hold-to-confirm text on the right. /// Arrow back on left and hold-to-confirm text on the right.
pub fn back_and_htc_text(text: &'static str, duration: Duration) -> Self { pub fn back_and_htc_text(text: StrBuffer, duration: Duration) -> Self {
Self::new( Self::new(
Some(ButtonDetails::left_arrow_icon()), Some(ButtonDetails::left_arrow_icon()),
None, None,
@ -599,7 +592,7 @@ impl ButtonLayout<&'static str> {
} }
/// Arrow back on left and text on the right. /// Arrow back on left and text on the right.
pub fn back_and_text(text: &'static str) -> Self { pub fn back_and_text(text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::left_arrow_icon()), Some(ButtonDetails::left_arrow_icon()),
None, None,
@ -608,12 +601,12 @@ impl ButtonLayout<&'static str> {
} }
/// Only armed text in the middle. /// Only armed text in the middle.
pub fn middle_armed_text(text: &'static str) -> Self { pub fn middle_armed_text(text: StrBuffer) -> Self {
Self::new(None, Some(ButtonDetails::armed_text(text)), None) Self::new(None, Some(ButtonDetails::armed_text(text)), None)
} }
/// Only hold-to-confirm with text on the right. /// Only hold-to-confirm with text on the right.
pub fn htc_only(text: &'static str, duration: Duration) -> Self { pub fn htc_only(text: StrBuffer, duration: Duration) -> Self {
Self::new( Self::new(
None, None,
None, None,
@ -628,10 +621,7 @@ impl ButtonLayout<&'static str> {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for ButtonDetails<T> impl crate::trace::Trace for ButtonDetails {
where
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("ButtonDetails"); t.open("ButtonDetails");
let mut btn_text: String<30> = String::new(); let mut btn_text: String<30> = String::new();

View File

@ -46,18 +46,14 @@ pub enum ButtonControllerMsg {
} }
/// Defines what kind of button should be currently used. /// Defines what kind of button should be currently used.
pub enum ButtonType<T> { pub enum ButtonType {
Button(Button<T>), Button(Button),
HoldToConfirm(HoldToConfirm<T>), HoldToConfirm(HoldToConfirm),
Nothing, Nothing,
} }
impl<T> ButtonType<T> impl ButtonType {
where pub fn from_button_details(pos: ButtonPos, btn_details: Option<ButtonDetails>) -> Self {
T: AsRef<str>,
T: Clone,
{
pub fn from_button_details(pos: ButtonPos, btn_details: Option<ButtonDetails<T>>) -> Self {
if let Some(btn_details) = btn_details { if let Some(btn_details) = btn_details {
if btn_details.duration.is_some() { if btn_details.duration.is_some() {
Self::HoldToConfirm(Self::get_hold_to_confirm(pos, btn_details)) Self::HoldToConfirm(Self::get_hold_to_confirm(pos, btn_details))
@ -70,7 +66,7 @@ where
} }
/// Create `Button` component from `btn_details`. /// Create `Button` component from `btn_details`.
fn get_button(pos: ButtonPos, btn_details: ButtonDetails<T>) -> Button<T> { fn get_button(pos: ButtonPos, btn_details: ButtonDetails) -> Button {
// Deciding between text and icon // Deciding between text and icon
if let Some(text) = btn_details.clone().text { if let Some(text) = btn_details.clone().text {
Button::with_text(pos, text, btn_details.style()) Button::with_text(pos, text, btn_details.style())
@ -82,7 +78,7 @@ where
} }
/// Create `HoldToConfirm` component from `btn_details`. /// Create `HoldToConfirm` component from `btn_details`.
fn get_hold_to_confirm(pos: ButtonPos, btn_details: ButtonDetails<T>) -> HoldToConfirm<T> { fn get_hold_to_confirm(pos: ButtonPos, btn_details: ButtonDetails) -> HoldToConfirm {
let duration = btn_details let duration = btn_details
.duration .duration
.unwrap_or_else(|| Duration::from_millis(1000)); .unwrap_or_else(|| Duration::from_millis(1000));
@ -123,15 +119,15 @@ where
/// ///
/// Users have a choice of a normal button or Hold-to-confirm button. /// Users have a choice of a normal button or Hold-to-confirm button.
/// `button_type` specified what from those two is used, if anything. /// `button_type` specified what from those two is used, if anything.
pub struct ButtonContainer<T> { pub struct ButtonContainer {
pos: ButtonPos, pos: ButtonPos,
button_type: ButtonType<T>, button_type: ButtonType,
} }
impl<T: Clone + AsRef<str>> ButtonContainer<T> { impl ButtonContainer {
/// Supplying `None` as `btn_details` marks the button inactive /// Supplying `None` as `btn_details` marks the button inactive
/// (it can be later activated in `set()`). /// (it can be later activated in `set()`).
pub fn new(pos: ButtonPos, btn_details: Option<ButtonDetails<T>>) -> Self { pub fn new(pos: ButtonPos, btn_details: Option<ButtonDetails>) -> Self {
Self { Self {
pos, pos,
button_type: ButtonType::from_button_details(pos, btn_details), button_type: ButtonType::from_button_details(pos, btn_details),
@ -141,7 +137,7 @@ impl<T: Clone + AsRef<str>> ButtonContainer<T> {
/// Changing the state of the button. /// Changing the state of the button.
/// ///
/// Passing `None` as `btn_details` will mark the button as inactive. /// Passing `None` as `btn_details` will mark the button as inactive.
pub fn set(&mut self, btn_details: Option<ButtonDetails<T>>, button_area: Rect) { pub fn set(&mut self, btn_details: Option<ButtonDetails>, button_area: Rect) {
self.button_type = ButtonType::from_button_details(self.pos, btn_details); self.button_type = ButtonType::from_button_details(self.pos, btn_details);
self.button_type.place(button_area); self.button_type.place(button_area);
} }
@ -205,11 +201,11 @@ impl<T: Clone + AsRef<str>> ButtonContainer<T> {
/// ///
/// Only "final" button events are returned in `ButtonControllerMsg::Triggered`, /// Only "final" button events are returned in `ButtonControllerMsg::Triggered`,
/// based upon the buttons being long-press or not. /// based upon the buttons being long-press or not.
pub struct ButtonController<T> { pub struct ButtonController {
pad: Pad, pad: Pad,
left_btn: ButtonContainer<T>, left_btn: ButtonContainer,
middle_btn: ButtonContainer<T>, middle_btn: ButtonContainer,
right_btn: ButtonContainer<T>, right_btn: ButtonContainer,
state: ButtonState, state: ButtonState,
// Button area is needed so the buttons // Button area is needed so the buttons
// can be "re-placed" after their text is changed // can be "re-placed" after their text is changed
@ -217,8 +213,8 @@ pub struct ButtonController<T> {
button_area: Rect, button_area: Rect,
} }
impl<T: Clone + AsRef<str>> ButtonController<T> { impl ButtonController {
pub fn new(btn_layout: ButtonLayout<T>) -> Self { pub fn new(btn_layout: ButtonLayout) -> Self {
Self { Self {
pad: Pad::with_background(theme::BG).with_clear(), pad: Pad::with_background(theme::BG).with_clear(),
left_btn: ButtonContainer::new(ButtonPos::Left, btn_layout.btn_left), left_btn: ButtonContainer::new(ButtonPos::Left, btn_layout.btn_left),
@ -230,7 +226,7 @@ impl<T: Clone + AsRef<str>> ButtonController<T> {
} }
/// Updating all the three buttons to the wanted states. /// Updating all the three buttons to the wanted states.
pub fn set(&mut self, btn_layout: ButtonLayout<T>) { pub fn set(&mut self, btn_layout: ButtonLayout) {
self.pad.clear(); self.pad.clear();
self.left_btn.set(btn_layout.btn_left, self.button_area); self.left_btn.set(btn_layout.btn_left, self.button_area);
self.middle_btn.set(btn_layout.btn_middle, self.button_area); self.middle_btn.set(btn_layout.btn_middle, self.button_area);
@ -279,7 +275,7 @@ impl<T: Clone + AsRef<str>> ButtonController<T> {
} }
} }
impl<T: Clone + AsRef<str>> Component for ButtonController<T> { impl Component for ButtonController {
type Msg = ButtonControllerMsg; type Msg = ButtonControllerMsg;
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> { fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
@ -443,10 +439,7 @@ impl<T: Clone + AsRef<str>> Component for ButtonController<T> {
use super::ButtonContent; use super::ButtonContent;
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for ButtonContainer<T> impl crate::trace::Trace for ButtonContainer {
where
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("ButtonContainer"); t.open("ButtonContainer");
@ -477,10 +470,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for ButtonController<T> impl crate::trace::Trace for ButtonController {
where
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("ButtonController"); t.open("ButtonController");
t.field("left_btn", &self.left_btn); t.field("left_btn", &self.left_btn);

View File

@ -24,7 +24,7 @@ pub struct Flow<F, const M: usize> {
content_area: Rect, content_area: Rect,
title_area: Rect, title_area: Rect,
pad: Pad, pad: Pad,
buttons: Child<ButtonController<&'static str>>, buttons: Child<ButtonController>,
page_counter: u8, page_counter: u8,
} }

View File

@ -58,7 +58,7 @@ where
pub struct Page<const M: usize> { pub struct Page<const M: usize> {
ops: Vec<Op, M>, ops: Vec<Op, M>,
text_layout: TextLayout, text_layout: TextLayout,
btn_layout: ButtonLayout<&'static str>, btn_layout: ButtonLayout,
btn_actions: ButtonActions, btn_actions: ButtonActions,
current_page: usize, current_page: usize,
page_count: usize, page_count: usize,
@ -68,7 +68,7 @@ pub struct Page<const M: usize> {
// For `layout.rs` // For `layout.rs`
impl<const M: usize> Page<M> { impl<const M: usize> Page<M> {
pub fn new( pub fn new(
btn_layout: ButtonLayout<&'static str>, btn_layout: ButtonLayout,
btn_actions: ButtonActions, btn_actions: ButtonActions,
initial_text_font: Font, initial_text_font: Font,
) -> Self { ) -> Self {
@ -99,7 +99,7 @@ impl<const M: usize> Page<M> {
self.layout_content(&mut TextRenderer); self.layout_content(&mut TextRenderer);
} }
pub fn btn_layout(&self) -> ButtonLayout<&'static str> { pub fn btn_layout(&self) -> ButtonLayout {
// When we are in pagination inside this flow, // When we are in pagination inside this flow,
// show the up and down arrows on appropriate sides. // show the up and down arrows on appropriate sides.
let current = self.btn_layout.clone(); let current = self.btn_layout.clone();

View File

@ -1,25 +1,27 @@
use super::{common, theme, ScrollBar}; use super::{common, theme, ScrollBar};
use crate::ui::{ use crate::{
component::{Child, Component, Event, EventCtx}, micropython::buffer::StrBuffer,
geometry::{Insets, Rect}, ui::{
component::{Child, Component, Event, EventCtx},
geometry::{Insets, Rect},
},
}; };
/// Component for holding another component and displaying a title. /// Component for holding another component and displaying a title.
/// Also is allocating space for a scrollbar. /// Also is allocating space for a scrollbar.
pub struct Frame<T, U> { pub struct Frame<T> {
area: Rect, area: Rect,
title: U, title: StrBuffer,
title_centered: bool, title_centered: bool,
account_for_scrollbar: bool, account_for_scrollbar: bool,
content: Child<T>, content: Child<T>,
} }
impl<T, U> Frame<T, U> impl<T> Frame<T>
where where
T: Component, T: Component,
U: AsRef<str>,
{ {
pub fn new(title: U, content: T) -> Self { pub fn new(title: StrBuffer, content: T) -> Self {
Self { Self {
title, title,
area: Rect::zero(), area: Rect::zero(),
@ -51,10 +53,9 @@ where
} }
} }
impl<T, U> Component for Frame<T, U> impl<T> Component for Frame<T>
where where
T: Component, T: Component,
U: AsRef<str>,
{ {
type Msg = T::Msg; type Msg = T::Msg;
@ -96,10 +97,9 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for Frame<T, U> impl<T> crate::trace::Trace for Frame<T>
where where
T: crate::trace::Trace, T: crate::trace::Trace,
U: crate::trace::Trace + AsRef<str>,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Frame"); t.open("Frame");

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
micropython::buffer::StrBuffer,
time::{Duration, Instant}, time::{Duration, Instant},
ui::{ ui::{
component::{Component, Event, EventCtx}, component::{Component, Event, EventCtx},
@ -13,15 +14,20 @@ pub enum HoldToConfirmMsg {
FailedToConfirm, FailedToConfirm,
} }
pub struct HoldToConfirm<T> { pub struct HoldToConfirm {
area: Rect, area: Rect,
pos: ButtonPos, pos: ButtonPos,
loader: Loader<T>, loader: Loader,
text_width: i16, text_width: i16,
} }
impl<T: AsRef<str>> HoldToConfirm<T> { impl HoldToConfirm {
pub fn text(pos: ButtonPos, text: T, styles: LoaderStyleSheet, duration: Duration) -> Self { pub fn text(
pos: ButtonPos,
text: StrBuffer,
styles: LoaderStyleSheet,
duration: Duration,
) -> Self {
let text_width = styles.normal.font.text_width(text.as_ref()); let text_width = styles.normal.font.text_width(text.as_ref());
Self { Self {
area: Rect::zero(), area: Rect::zero(),
@ -32,8 +38,8 @@ impl<T: AsRef<str>> HoldToConfirm<T> {
} }
/// Updating the text of the component and re-placing it. /// Updating the text of the component and re-placing it.
pub fn set_text(&mut self, text: T, button_area: Rect) { pub fn set_text(&mut self, text: StrBuffer, button_area: Rect) {
self.text_width = self.loader.get_text_width(&text) as i16; self.text_width = self.loader.get_text_width(text.clone()) as i16;
self.loader.set_text(text); self.loader.set_text(text);
self.place(button_area); self.place(button_area);
} }
@ -50,7 +56,7 @@ impl<T: AsRef<str>> HoldToConfirm<T> {
self.loader.get_duration() self.loader.get_duration()
} }
pub fn get_text(&self) -> &T { pub fn get_text(&self) -> &StrBuffer {
self.loader.get_text() self.loader.get_text()
} }
@ -64,7 +70,7 @@ impl<T: AsRef<str>> HoldToConfirm<T> {
} }
} }
impl<T: AsRef<str>> Component for HoldToConfirm<T> { impl Component for HoldToConfirm {
type Msg = HoldToConfirmMsg; type Msg = HoldToConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -103,7 +109,7 @@ impl<T: AsRef<str>> Component for HoldToConfirm<T> {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T: AsRef<str>> crate::trace::Trace for HoldToConfirm<T> { impl crate::trace::Trace for HoldToConfirm {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("HoldToConfirm"); d.open("HoldToConfirm");
self.loader.trace(d); self.loader.trace(d);

View File

@ -1,9 +1,12 @@
use crate::ui::{ use crate::{
component::{Component, Event, EventCtx, Pad}, micropython::buffer::StrBuffer,
display::Font, ui::{
event::{ButtonEvent, USBEvent}, component::{Component, Event, EventCtx, Pad},
geometry::{Offset, Point, Rect}, display::Font,
model_tr::constant, event::{ButtonEvent, USBEvent},
geometry::{Offset, Point, Rect},
model_tr::constant,
},
}; };
use super::{common::display_center, theme}; use super::{common::display_center, theme};
@ -14,9 +17,9 @@ const LABEL_Y: i16 = 62;
const LOCKED_Y: i16 = 32; const LOCKED_Y: i16 = 32;
const TAP_Y: i16 = 47; const TAP_Y: i16 = 47;
pub struct Homescreen<T> { pub struct Homescreen {
label: T, label: StrBuffer,
notification: Option<(T, u8)>, notification: Option<(StrBuffer, u8)>,
usb_connected: bool, usb_connected: bool,
pad: Pad, pad: Pad,
} }
@ -25,11 +28,8 @@ pub enum HomescreenMsg {
Dismissed, Dismissed,
} }
impl<T> Homescreen<T> impl Homescreen {
where pub fn new(label: StrBuffer, notification: Option<(StrBuffer, u8)>) -> Self {
T: AsRef<str>,
{
pub fn new(label: T, notification: Option<(T, u8)>) -> Self {
Self { Self {
label, label,
notification, notification,
@ -57,10 +57,7 @@ where
} }
} }
impl<T> Component for Homescreen<T> impl Component for Homescreen {
where
T: AsRef<str>,
{
type Msg = HomescreenMsg; type Msg = HomescreenMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -89,7 +86,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T: AsRef<str>> crate::trace::Trace for Homescreen<T> { impl crate::trace::Trace for Homescreen {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("Homescreen"); d.open("Homescreen");
d.kw_pair("active_page", "0"); d.kw_pair("active_page", "0");
@ -99,21 +96,18 @@ impl<T: AsRef<str>> crate::trace::Trace for Homescreen<T> {
} }
} }
pub struct Lockscreen<T> { pub struct Lockscreen {
label: T, label: StrBuffer,
bootscreen: bool, bootscreen: bool,
} }
impl<T> Lockscreen<T> { impl Lockscreen {
pub fn new(label: T, bootscreen: bool) -> Self { pub fn new(label: StrBuffer, bootscreen: bool) -> Self {
Lockscreen { label, bootscreen } Lockscreen { label, bootscreen }
} }
} }
impl<T> Component for Lockscreen<T> impl Component for Lockscreen {
where
T: AsRef<str>,
{
type Msg = HomescreenMsg; type Msg = HomescreenMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -144,10 +138,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Lockscreen<T> impl crate::trace::Trace for Lockscreen {
where
T: AsRef<str>,
{
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("Lockscreen"); d.open("Lockscreen");
d.field("label", &self.label.as_ref()); d.field("label", &self.label.as_ref());

View File

@ -58,8 +58,11 @@ impl ChoiceFactory for ChoiceFactoryBIP39 {
match self { match self {
Self::Letters(letter_choices) => { Self::Letters(letter_choices) => {
if choice_index >= letter_choices.len() as u8 { if choice_index >= letter_choices.len() as u8 {
ChoiceItem::new("DELETE", ButtonLayout::three_icons_middle_text("CONFIRM")) ChoiceItem::new(
.with_icon(Icon::new(theme::ICON_DELETE)) "DELETE",
ButtonLayout::three_icons_middle_text("CONFIRM".into()),
)
.with_icon(Icon::new(theme::ICON_DELETE))
} else { } else {
let letter = letter_choices[choice_index as usize]; let letter = letter_choices[choice_index as usize];
ChoiceItem::new( ChoiceItem::new(
@ -70,9 +73,11 @@ impl ChoiceFactory for ChoiceFactoryBIP39 {
} }
Self::Words(word_choices) => { Self::Words(word_choices) => {
if choice_index >= word_choices.len() as u8 { if choice_index >= word_choices.len() as u8 {
let mut item = let mut item = ChoiceItem::new(
ChoiceItem::new("DELETE", ButtonLayout::three_icons_middle_text("CONFIRM")) "DELETE",
.with_icon(Icon::new(theme::ICON_DELETE)); ButtonLayout::three_icons_middle_text("CONFIRM".into()),
)
.with_icon(Icon::new(theme::ICON_DELETE));
item.set_right_btn(None); item.set_right_btn(None);
item item
} else { } else {

View File

@ -27,7 +27,7 @@ pub trait Choice {
fn paint_right(&self, _area: Rect, _show_incomplete: bool) -> Option<i16> { fn paint_right(&self, _area: Rect, _show_incomplete: bool) -> Option<i16> {
None None
} }
fn btn_layout(&self) -> ButtonLayout<&'static str> { fn btn_layout(&self) -> ButtonLayout {
ButtonLayout::default_three_icons() ButtonLayout::default_three_icons()
} }
} }
@ -69,7 +69,7 @@ where
{ {
choices: F, choices: F,
pad: Pad, pad: Pad,
buttons: Child<ButtonController<&'static str>>, buttons: Child<ButtonController>,
page_counter: u8, page_counter: u8,
/// How many pixels from top should we render the items. /// How many pixels from top should we render the items.
y_baseline: i16, y_baseline: i16,

View File

@ -15,12 +15,12 @@ use super::super::{
pub struct ChoiceItem { pub struct ChoiceItem {
text: String<50>, text: String<50>,
icon: Option<Icon>, icon: Option<Icon>,
btn_layout: ButtonLayout<&'static str>, btn_layout: ButtonLayout,
font: Font, font: Font,
} }
impl ChoiceItem { impl ChoiceItem {
pub fn new<T>(text: T, btn_layout: ButtonLayout<&'static str>) -> Self pub fn new<T>(text: T, btn_layout: ButtonLayout) -> Self
where where
T: AsRef<str>, T: AsRef<str>,
{ {
@ -105,17 +105,17 @@ impl ChoiceItem {
} }
/// Setting left button. /// Setting left button.
pub fn set_left_btn(&mut self, btn_left: Option<ButtonDetails<&'static str>>) { pub fn set_left_btn(&mut self, btn_left: Option<ButtonDetails>) {
self.btn_layout.btn_left = btn_left; self.btn_layout.btn_left = btn_left;
} }
/// Setting middle button. /// Setting middle button.
pub fn set_middle_btn(&mut self, btn_middle: Option<ButtonDetails<&'static str>>) { pub fn set_middle_btn(&mut self, btn_middle: Option<ButtonDetails>) {
self.btn_layout.btn_middle = btn_middle; self.btn_layout.btn_middle = btn_middle;
} }
/// Setting right button. /// Setting right button.
pub fn set_right_btn(&mut self, btn_right: Option<ButtonDetails<&'static str>>) { pub fn set_right_btn(&mut self, btn_right: Option<ButtonDetails>) {
self.btn_layout.btn_right = btn_right; self.btn_layout.btn_right = btn_right;
} }
@ -193,7 +193,7 @@ impl Choice for ChoiceItem {
} }
/// Getting current button layout. /// Getting current button layout.
fn btn_layout(&self) -> ButtonLayout<&'static str> { fn btn_layout(&self) -> ButtonLayout {
self.btn_layout.clone() self.btn_layout.clone()
} }
} }

View File

@ -111,10 +111,14 @@ impl ChoiceFactoryPassphrase {
// Including accept button on the left and cancel on the very right. // Including accept button on the left and cancel on the very right.
// TODO: could have some icons instead of the shortcut text // TODO: could have some icons instead of the shortcut text
if choice_index == 0 { if choice_index == 0 {
menu_item.set_left_btn(Some(ButtonDetails::text("ACC").with_default_duration())); menu_item.set_left_btn(Some(
ButtonDetails::text("ACC".into()).with_default_duration(),
));
} }
if choice_index == MENU.len() as u8 - 1 { if choice_index == MENU.len() as u8 - 1 {
menu_item.set_right_btn(Some(ButtonDetails::text("CAN").with_default_duration())); menu_item.set_right_btn(Some(
ButtonDetails::text("CAN".into()).with_default_duration(),
));
} }
// Including icons for some items. // Including icons for some items.
@ -131,7 +135,10 @@ impl ChoiceFactoryPassphrase {
/// return back /// return back
fn get_character_item(&self, choice_index: u8) -> ChoiceItem { fn get_character_item(&self, choice_index: u8) -> ChoiceItem {
if is_menu_choice(&self.current_category, choice_index) { if is_menu_choice(&self.current_category, choice_index) {
ChoiceItem::new("MENU", ButtonLayout::three_icons_middle_text("RETURN")) ChoiceItem::new(
"MENU",
ButtonLayout::three_icons_middle_text("RETURN".into()),
)
} else { } else {
let ch = get_char(&self.current_category, choice_index); let ch = get_char(&self.current_category, choice_index);
ChoiceItem::new(char_to_string::<1>(ch), ButtonLayout::default_three_icons()) ChoiceItem::new(char_to_string::<1>(ch), ButtonLayout::default_three_icons())

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
micropython::buffer::StrBuffer,
trezorhal::random, trezorhal::random,
ui::{ ui::{
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx}, component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
@ -48,7 +49,7 @@ impl ChoiceFactory for ChoiceFactoryPIN {
// Action buttons have different middle button text // Action buttons have different middle button text
if [DELETE_INDEX, SHOW_INDEX, ENTER_INDEX].contains(&(choice_index as usize)) { if [DELETE_INDEX, SHOW_INDEX, ENTER_INDEX].contains(&(choice_index as usize)) {
let confirm_btn = ButtonDetails::armed_text("CONFIRM"); let confirm_btn = ButtonDetails::armed_text("CONFIRM".into());
choice_item.set_middle_btn(Some(confirm_btn)); choice_item.set_middle_btn(Some(confirm_btn));
} }
@ -70,20 +71,17 @@ impl ChoiceFactory for ChoiceFactoryPIN {
} }
/// Component for entering a PIN. /// Component for entering a PIN.
pub struct PinEntry<T> { pub struct PinEntry {
choice_page: ChoicePage<ChoiceFactoryPIN>, choice_page: ChoicePage<ChoiceFactoryPIN>,
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>, pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
subprompt_line: Child<ChangingTextLine<T>>, subprompt_line: Child<ChangingTextLine<StrBuffer>>,
prompt: T, prompt: StrBuffer,
show_real_pin: bool, show_real_pin: bool,
textbox: TextBox<MAX_PIN_LENGTH>, textbox: TextBox<MAX_PIN_LENGTH>,
} }
impl<T> PinEntry<T> impl PinEntry {
where pub fn new(prompt: StrBuffer, subprompt: StrBuffer) -> Self {
T: AsRef<str> + Clone,
{
pub fn new(prompt: T, subprompt: T) -> Self {
let choices = ChoiceFactoryPIN::new(); let choices = ChoiceFactoryPIN::new();
Self { Self {
@ -158,10 +156,7 @@ where
} }
} }
impl<T> Component for PinEntry<T> impl Component for PinEntry {
where
T: AsRef<str> + Clone,
{
type Msg = PinEntryMsg; type Msg = PinEntryMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -224,10 +219,7 @@ where
use super::super::{ButtonAction, ButtonPos}; use super::super::{ButtonAction, ButtonPos};
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for PinEntry<T> impl crate::trace::Trace for PinEntry {
where
T: AsRef<str> + Clone,
{
fn get_btn_action(&self, pos: ButtonPos) -> String<25> { fn get_btn_action(&self, pos: ButtonPos) -> String<25> {
match pos { match pos {
ButtonPos::Left => ButtonAction::PrevPage.string(), ButtonPos::Left => ButtonAction::PrevPage.string(),

View File

@ -1,6 +1,9 @@
use crate::ui::{ use crate::{
component::{Component, Event, EventCtx}, micropython::buffer::StrBuffer,
geometry::Rect, ui::{
component::{Component, Event, EventCtx},
geometry::Rect,
},
}; };
use super::super::{ButtonLayout, ChoiceFactory, ChoiceItem, ChoicePage, ChoicePageMsg}; use super::super::{ButtonLayout, ChoiceFactory, ChoiceItem, ChoicePage, ChoicePageMsg};
@ -13,24 +16,18 @@ pub enum SimpleChoiceMsg {
Result(String<50>), Result(String<50>),
} }
struct ChoiceFactorySimple<T, const N: usize> { struct ChoiceFactorySimple<const N: usize> {
choices: Vec<T, N>, choices: Vec<StrBuffer, N>,
carousel: bool, carousel: bool,
} }
impl<T, const N: usize> ChoiceFactorySimple<T, N> impl<const N: usize> ChoiceFactorySimple<N> {
where fn new(choices: Vec<StrBuffer, N>, carousel: bool) -> Self {
T: AsRef<str>,
{
fn new(choices: Vec<T, N>, carousel: bool) -> Self {
Self { choices, carousel } Self { choices, carousel }
} }
} }
impl<T, const N: usize> ChoiceFactory for ChoiceFactorySimple<T, N> impl<const N: usize> ChoiceFactory for ChoiceFactorySimple<N> {
where
T: AsRef<str>,
{
type Item = ChoiceItem; type Item = ChoiceItem;
fn count(&self) -> u8 { fn count(&self) -> u8 {
@ -58,21 +55,13 @@ where
/// Simple wrapper around `ChoicePage` that allows for /// Simple wrapper around `ChoicePage` that allows for
/// inputting a list of values and receiving the chosen one. /// inputting a list of values and receiving the chosen one.
pub struct SimpleChoice<T, const N: usize> pub struct SimpleChoice<const N: usize> {
where choices: Vec<StrBuffer, N>,
T: AsRef<str>, choice_page: ChoicePage<ChoiceFactorySimple<N>>,
T: Clone,
{
choices: Vec<T, N>,
choice_page: ChoicePage<ChoiceFactorySimple<T, N>>,
} }
impl<T, const N: usize> SimpleChoice<T, N> impl<const N: usize> SimpleChoice<N> {
where pub fn new(str_choices: Vec<StrBuffer, N>, carousel: bool, show_incomplete: bool) -> Self {
T: AsRef<str>,
T: Clone,
{
pub fn new(str_choices: Vec<T, N>, carousel: bool, show_incomplete: bool) -> Self {
let choices = ChoiceFactorySimple::new(str_choices.clone(), carousel); let choices = ChoiceFactorySimple::new(str_choices.clone(), carousel);
Self { Self {
choices: str_choices, choices: str_choices,
@ -83,11 +72,7 @@ where
} }
} }
impl<T, const N: usize> Component for SimpleChoice<T, N> impl<const N: usize> Component for SimpleChoice<N> {
where
T: AsRef<str>,
T: Clone,
{
type Msg = SimpleChoiceMsg; type Msg = SimpleChoiceMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -111,11 +96,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, const N: usize> crate::trace::Trace for SimpleChoice<T, N> impl<const N: usize> crate::trace::Trace for SimpleChoice<N> {
where
T: AsRef<str>,
T: Clone,
{
fn get_btn_action(&self, pos: ButtonPos) -> String<25> { fn get_btn_action(&self, pos: ButtonPos) -> String<25> {
match pos { match pos {
ButtonPos::Left => match self.choice_page.has_previous_choice() { ButtonPos::Left => match self.choice_page.has_previous_choice() {

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
micropython::buffer::StrBuffer,
time::{Duration, Instant}, time::{Duration, Instant},
ui::{ ui::{
animation::Animation, animation::Animation,
@ -21,19 +22,19 @@ enum State {
Grown, Grown,
} }
pub struct Loader<T> { pub struct Loader {
area: Rect, area: Rect,
state: State, state: State,
growing_duration: Duration, growing_duration: Duration,
shrinking_duration: Duration, shrinking_duration: Duration,
text_overlay: display::TextOverlay<T>, text_overlay: display::TextOverlay<StrBuffer>,
styles: LoaderStyleSheet, styles: LoaderStyleSheet,
} }
impl<T: AsRef<str>> Loader<T> { impl Loader {
pub const SIZE: Offset = Offset::new(120, 120); pub const SIZE: Offset = Offset::new(120, 120);
pub fn new(text_overlay: display::TextOverlay<T>, styles: LoaderStyleSheet) -> Self { pub fn new(text_overlay: display::TextOverlay<StrBuffer>, styles: LoaderStyleSheet) -> Self {
Self { Self {
area: Rect::zero(), area: Rect::zero(),
state: State::Initial, state: State::Initial,
@ -44,7 +45,7 @@ impl<T: AsRef<str>> Loader<T> {
} }
} }
pub fn text(text: T, styles: LoaderStyleSheet) -> Self { pub fn text(text: StrBuffer, styles: LoaderStyleSheet) -> Self {
let text_overlay = display::TextOverlay::new(text, styles.normal.font); let text_overlay = display::TextOverlay::new(text, styles.normal.font);
Self::new(text_overlay, styles) Self::new(text_overlay, styles)
@ -64,17 +65,17 @@ impl<T: AsRef<str>> Loader<T> {
self.growing_duration self.growing_duration
} }
pub fn get_text(&self) -> &T { pub fn get_text(&self) -> &StrBuffer {
self.text_overlay.get_text() self.text_overlay.get_text()
} }
/// Change the text of the loader. /// Change the text of the loader.
pub fn set_text(&mut self, text: T) { pub fn set_text(&mut self, text: StrBuffer) {
self.text_overlay.set_text(text); self.text_overlay.set_text(text);
} }
/// Return width of given text according to current style. /// Return width of given text according to current style.
pub fn get_text_width(&self, text: &T) -> i16 { pub fn get_text_width(&self, text: StrBuffer) -> i16 {
self.styles.normal.font.text_width(text.as_ref()) self.styles.normal.font.text_width(text.as_ref())
} }
@ -161,7 +162,7 @@ impl<T: AsRef<str>> Loader<T> {
} }
} }
impl<T: AsRef<str>> Component for Loader<T> { impl Component for Loader {
type Msg = LoaderMsg; type Msg = LoaderMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -241,7 +242,7 @@ impl LoaderStyleSheet {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T: AsRef<str>> crate::trace::Trace for Loader<T> { impl crate::trace::Trace for Loader {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("Loader"); d.open("Loader");
d.close(); d.close();

View File

@ -1,70 +1,39 @@
use crate::{ use crate::ui::{
micropython::buffer::StrBuffer, component::{Child, Component, ComponentExt, Event, EventCtx, Pad, PageMsg, Paginate},
ui::{ display::Color,
component::{Child, Component, ComponentExt, Event, EventCtx, Pad, PageMsg, Paginate}, geometry::{Insets, Offset, Rect},
display::Color,
geometry::{Insets, Offset, Rect},
},
}; };
use super::{ use super::{
theme, ButtonController, ButtonControllerMsg, ButtonDetails, ButtonLayout, ButtonPos, ScrollBar, theme, ButtonController, ButtonControllerMsg, ButtonDetails, ButtonLayout, ButtonPos, ScrollBar,
}; };
pub struct ButtonPage<S, T> { pub struct ButtonPage<T> {
content: Child<T>, content: Child<T>,
scrollbar: Child<ScrollBar>, scrollbar: Child<ScrollBar>,
/// Optional available area for scrollbar defined by parent component. /// Optional available area for scrollbar defined by parent component.
parent_scrollbar_area: Option<Rect>, parent_scrollbar_area: Option<Rect>,
pad: Pad, pad: Pad,
/// Left button of the first screen /// Left button of the first screen
cancel_btn_details: Option<ButtonDetails<S>>, cancel_btn_details: Option<ButtonDetails>,
/// Right button of the last screen /// Right button of the last screen
confirm_btn_details: Option<ButtonDetails<S>>, confirm_btn_details: Option<ButtonDetails>,
/// Left button of the last page /// Left button of the last page
last_back_btn_details: Option<ButtonDetails<S>>, last_back_btn_details: Option<ButtonDetails>,
/// Left button of every screen in the middle /// Left button of every screen in the middle
back_btn_details: Option<ButtonDetails<S>>, back_btn_details: Option<ButtonDetails>,
/// Right button of every screen apart the last one /// Right button of every screen apart the last one
next_btn_details: Option<ButtonDetails<S>>, next_btn_details: Option<ButtonDetails>,
buttons: Child<ButtonController<S>>, buttons: Child<ButtonController>,
/// Scrollbar may or may not be shown (but will be counting pages anyway). /// Scrollbar may or may not be shown (but will be counting pages anyway).
show_scrollbar: bool, show_scrollbar: bool,
} }
impl<T> ButtonPage<&'static str, T> impl<T> ButtonPage<T>
where where
T: Paginate, T: Component + Paginate,
T: Component,
{ {
/// Constructor for `&'static str` button-text type. pub fn new(content: T, background: Color) -> Self {
pub fn new_str(content: T, background: Color) -> Self {
Self {
content: Child::new(content),
scrollbar: Child::new(ScrollBar::to_be_filled_later()),
parent_scrollbar_area: None,
pad: Pad::with_background(background).with_clear(),
cancel_btn_details: Some(ButtonDetails::cancel_icon()),
confirm_btn_details: Some(ButtonDetails::text("CONFIRM")),
back_btn_details: Some(ButtonDetails::up_arrow_icon_wide()),
last_back_btn_details: Some(ButtonDetails::up_arrow_icon()),
next_btn_details: Some(ButtonDetails::down_arrow_icon_wide()),
// Setting empty layout for now, we do not yet know the page count.
// Initial button layout will be set in `place()` after we can call
// `content.page_count()`.
buttons: Child::new(ButtonController::new(ButtonLayout::empty())),
show_scrollbar: true,
}
}
}
impl<T> ButtonPage<StrBuffer, T>
where
T: Paginate,
T: Component,
{
/// Constructor for `StrBuffer` button-text type.
pub fn new_str_buf(content: T, background: Color) -> Self {
Self { Self {
content: Child::new(content), content: Child::new(content),
scrollbar: Child::new(ScrollBar::to_be_filled_later()), scrollbar: Child::new(ScrollBar::to_be_filled_later()),
@ -82,31 +51,23 @@ where
show_scrollbar: true, show_scrollbar: true,
} }
} }
}
impl<S, T> ButtonPage<S, T> pub fn with_cancel_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
where
T: Paginate,
T: Component,
S: AsRef<str>,
S: Clone,
{
pub fn with_cancel_btn(mut self, btn_details: Option<ButtonDetails<S>>) -> Self {
self.cancel_btn_details = btn_details; self.cancel_btn_details = btn_details;
self self
} }
pub fn with_confirm_btn(mut self, btn_details: Option<ButtonDetails<S>>) -> Self { pub fn with_confirm_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
self.confirm_btn_details = btn_details; self.confirm_btn_details = btn_details;
self self
} }
pub fn with_back_btn(mut self, btn_details: Option<ButtonDetails<S>>) -> Self { pub fn with_back_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
self.back_btn_details = btn_details; self.back_btn_details = btn_details;
self self
} }
pub fn with_next_btn(mut self, btn_details: Option<ButtonDetails<S>>) -> Self { pub fn with_next_btn(mut self, btn_details: Option<ButtonDetails>) -> Self {
self.next_btn_details = btn_details; self.next_btn_details = btn_details;
self self
} }
@ -147,7 +108,7 @@ where
}); });
} }
fn get_button_layout(&self, has_prev: bool, has_next: bool) -> ButtonLayout<S> { fn get_button_layout(&self, has_prev: bool, has_next: bool) -> ButtonLayout {
let btn_left = self.get_left_button_details(!has_prev, !has_next); let btn_left = self.get_left_button_details(!has_prev, !has_next);
let btn_right = self.get_right_button_details(has_next); let btn_right = self.get_right_button_details(has_next);
ButtonLayout::new(btn_left, None, btn_right) ButtonLayout::new(btn_left, None, btn_right)
@ -155,7 +116,7 @@ where
/// Get the let button details, depending whether the page is first, last, /// Get the let button details, depending whether the page is first, last,
/// or in the middle. /// or in the middle.
fn get_left_button_details(&self, is_first: bool, is_last: bool) -> Option<ButtonDetails<S>> { fn get_left_button_details(&self, is_first: bool, is_last: bool) -> Option<ButtonDetails> {
if is_first { if is_first {
self.cancel_btn_details.clone() self.cancel_btn_details.clone()
} else if is_last { } else if is_last {
@ -167,7 +128,7 @@ where
/// Get the right button details, depending on whether there is a next /// Get the right button details, depending on whether there is a next
/// page. /// page.
fn get_right_button_details(&self, has_next_page: bool) -> Option<ButtonDetails<S>> { fn get_right_button_details(&self, has_next_page: bool) -> Option<ButtonDetails> {
if has_next_page { if has_next_page {
self.next_btn_details.clone() self.next_btn_details.clone()
} else { } else {
@ -176,12 +137,9 @@ where
} }
} }
impl<S, T> Component for ButtonPage<S, T> impl<T> Component for ButtonPage<T>
where where
S: Clone, T: Component + Paginate,
S: AsRef<str>,
T: Component,
T: Paginate,
{ {
type Msg = PageMsg<T::Msg, bool>; type Msg = PageMsg<T::Msg, bool>;
@ -278,10 +236,9 @@ use super::ButtonAction;
use heapless::String; use heapless::String;
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<S, T> crate::trace::Trace for ButtonPage<S, T> impl<T> crate::trace::Trace for ButtonPage<T>
where where
T: crate::trace::Trace, T: crate::trace::Trace,
S: AsRef<str>,
{ {
fn get_btn_action(&self, pos: ButtonPos) -> String<25> { fn get_btn_action(&self, pos: ButtonPos) -> String<25> {
match pos { match pos {

View File

@ -2,11 +2,12 @@ use core::mem;
use crate::{ use crate::{
error::Error, error::Error,
micropython::buffer::StrBuffer,
ui::{ ui::{
component::{ component::{
base::ComponentExt, base::ComponentExt,
paginated::Paginate, paginated::Paginate,
text::paragraphs::{Paragraph, ParagraphStrType, Paragraphs}, text::paragraphs::{Paragraph, Paragraphs},
Child, Component, Event, EventCtx, Label, Never, Pad, Child, Component, Event, EventCtx, Label, Never, Pad,
}, },
display::{self, Font}, display::{self, Font},
@ -18,27 +19,24 @@ use crate::{
use super::theme; use super::theme;
pub struct Progress<T> { pub struct Progress {
title: Child<Label<T>>, title: Child<Label<StrBuffer>>,
value: u16, value: u16,
loader_y_offset: i16, loader_y_offset: i16,
indeterminate: bool, indeterminate: bool,
description: Child<Paragraphs<Paragraph<T>>>, description: Child<Paragraphs<Paragraph<StrBuffer>>>,
description_pad: Pad, description_pad: Pad,
update_description: fn(&str) -> Result<T, Error>, update_description: fn(&str) -> Result<StrBuffer, Error>,
} }
impl<T> Progress<T> impl Progress {
where
T: ParagraphStrType,
{
const AREA: Rect = constant::screen(); const AREA: Rect = constant::screen();
pub fn new( pub fn new(
title: T, title: StrBuffer,
indeterminate: bool, indeterminate: bool,
description: T, description: StrBuffer,
update_description: fn(&str) -> Result<T, Error>, update_description: fn(&str) -> Result<StrBuffer, Error>,
) -> Self { ) -> Self {
Self { Self {
title: Label::centered(title, theme::TEXT_HEADER).into_child(), title: Label::centered(title, theme::TEXT_HEADER).into_child(),
@ -55,10 +53,7 @@ where
} }
} }
impl<T> Component for Progress<T> impl Component for Progress {
where
T: ParagraphStrType,
{
type Msg = Never; type Msg = Never;
fn place(&mut self, _bounds: Rect) -> Rect { fn place(&mut self, _bounds: Rect) -> Rect {
@ -126,10 +121,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Progress<T> impl crate::trace::Trace for Progress {
where
T: ParagraphStrType,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Progress"); t.open("Progress");
t.close(); t.close();

View File

@ -1,7 +1,10 @@
use crate::ui::{ use crate::{
component::{Child, Component, Event, EventCtx}, micropython::buffer::StrBuffer,
display::{self, Font}, ui::{
geometry::Rect, component::{Child, Component, Event, EventCtx},
display::{self, Font},
geometry::Rect,
},
}; };
use super::{theme, ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos}; use super::{theme, ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos};
@ -11,18 +14,15 @@ pub enum QRCodePageMessage {
Cancelled, Cancelled,
} }
pub struct QRCodePage<F, T> { pub struct QRCodePage<T> {
title: T, title: StrBuffer,
title_area: Rect, title_area: Rect,
qr_code: F, qr_code: T,
buttons: Child<ButtonController<T>>, buttons: Child<ButtonController>,
} }
impl<F, T> QRCodePage<F, T> impl<T> QRCodePage<T> {
where pub fn new(title: StrBuffer, qr_code: T, btn_layout: ButtonLayout) -> Self {
T: AsRef<str> + Clone,
{
pub fn new(title: T, qr_code: F, btn_layout: ButtonLayout<T>) -> Self {
Self { Self {
title, title,
title_area: Rect::zero(), title_area: Rect::zero(),
@ -32,10 +32,9 @@ where
} }
} }
impl<F, T> Component for QRCodePage<F, T> impl<T> Component for QRCodePage<T>
where where
T: AsRef<str> + Clone, T: Component,
F: Component,
{ {
type Msg = QRCodePageMessage; type Msg = QRCodePageMessage;
@ -86,10 +85,7 @@ use super::ButtonAction;
use heapless::String; use heapless::String;
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<F, T> crate::trace::Trace for QRCodePage<F, T> impl<T> crate::trace::Trace for QRCodePage<T> {
where
T: AsRef<str> + Clone,
{
fn get_btn_action(&self, pos: ButtonPos) -> String<25> { fn get_btn_action(&self, pos: ButtonPos) -> String<25> {
match pos { match pos {
ButtonPos::Left => ButtonAction::Cancel.string(), ButtonPos::Left => ButtonAction::Cancel.string(),

View File

@ -1,8 +1,9 @@
use crate::{ use crate::{
micropython::buffer::StrBuffer,
time::Instant, time::Instant,
ui::{ ui::{
component::{ component::{
text::paragraphs::{Paragraph, ParagraphStrType, Paragraphs}, text::paragraphs::{Paragraph, Paragraphs},
Child, Component, ComponentExt, Event, EventCtx, Label, Pad, Child, Component, ComponentExt, Event, EventCtx, Label, Pad,
}, },
constant::screen, constant::screen,
@ -20,14 +21,14 @@ pub enum ResultPopupMsg {
Confirmed, Confirmed,
} }
pub struct ResultPopup<S> { pub struct ResultPopup {
area: Rect, area: Rect,
pad: Pad, pad: Pad,
result_anim: Child<ResultAnim>, result_anim: Child<ResultAnim>,
headline_baseline: Point, headline_baseline: Point,
headline: Option<Label<&'static str>>, headline: Option<Label<&'static str>>,
text: Child<Paragraphs<Paragraph<S>>>, text: Child<Paragraphs<Paragraph<StrBuffer>>>,
buttons: Option<Child<ButtonController<&'static str>>>, buttons: Option<Child<ButtonController>>,
autoclose: bool, autoclose: bool,
} }
@ -37,10 +38,10 @@ const ANIM_POS: i16 = 32;
const ANIM_POS_ADJ_HEADLINE: i16 = 10; const ANIM_POS_ADJ_HEADLINE: i16 = 10;
const ANIM_POS_ADJ_BUTTON: i16 = 6; const ANIM_POS_ADJ_BUTTON: i16 = 6;
impl<S: ParagraphStrType> ResultPopup<S> { impl ResultPopup {
pub fn new( pub fn new(
icon: &'static [u8], icon: &'static [u8],
text: S, text: StrBuffer,
headline: Option<&'static str>, headline: Option<&'static str>,
button_text: Option<&'static str>, button_text: Option<&'static str>,
) -> Self { ) -> Self {
@ -48,7 +49,7 @@ impl<S: ParagraphStrType> ResultPopup<S> {
.with_placement(LinearPlacement::vertical().align_at_center()); .with_placement(LinearPlacement::vertical().align_at_center());
let buttons = button_text.map(|text| { let buttons = button_text.map(|text| {
let btn_layout = ButtonLayout::only_right_text(text); let btn_layout = ButtonLayout::only_right_text(text.into());
Child::new(ButtonController::new(btn_layout)) Child::new(ButtonController::new(btn_layout))
}); });
@ -84,7 +85,7 @@ impl<S: ParagraphStrType> ResultPopup<S> {
} }
} }
impl<S: ParagraphStrType> Component for ResultPopup<S> { impl Component for ResultPopup {
type Msg = ResultPopupMsg; type Msg = ResultPopupMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -155,7 +156,7 @@ impl<S: ParagraphStrType> Component for ResultPopup<S> {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<S: ParagraphStrType> crate::trace::Trace for ResultPopup<S> { impl crate::trace::Trace for ResultPopup {
fn trace(&self, d: &mut dyn crate::trace::Tracer) { fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("ResultPopup"); d.open("ResultPopup");
self.text.trace(d); self.text.trace(d);

View File

@ -20,8 +20,7 @@ use crate::{
paginated::{PageMsg, Paginate}, paginated::{PageMsg, Paginate},
painter, painter,
text::paragraphs::{ text::paragraphs::{
Paragraph, ParagraphSource, ParagraphStrType, ParagraphVecLong, ParagraphVecShort, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, Paragraphs, VecExt,
Paragraphs, VecExt,
}, },
ComponentExt, Empty, Timeout, TimeoutMsg, ComponentExt, Empty, Timeout, TimeoutMsg,
}, },
@ -74,11 +73,9 @@ where
} }
} }
impl<S, T> ComponentMsgObj for ButtonPage<S, T> impl<T> ComponentMsgObj for ButtonPage<T>
where where
T: Component + Paginate, T: Component + Paginate,
S: AsRef<str>,
S: Clone,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
@ -102,10 +99,9 @@ where
} }
} }
impl<F, T> ComponentMsgObj for QRCodePage<F, T> impl<T> ComponentMsgObj for QRCodePage<T>
where where
T: AsRef<str> + Clone, T: Component,
F: Component,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
@ -115,10 +111,7 @@ where
} }
} }
impl<T> ComponentMsgObj for PinEntry<T> impl ComponentMsgObj for PinEntry {
where
T: AsRef<str> + Clone,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
PinEntryMsg::Confirmed => self.pin().try_into(), PinEntryMsg::Confirmed => self.pin().try_into(),
@ -127,11 +120,7 @@ where
} }
} }
impl<T, const N: usize> ComponentMsgObj for SimpleChoice<T, N> impl<const N: usize> ComponentMsgObj for SimpleChoice<N> {
where
T: AsRef<str>,
T: Clone,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
SimpleChoiceMsg::Result(choice) => choice.as_str().try_into(), SimpleChoiceMsg::Result(choice) => choice.as_str().try_into(),
@ -156,29 +145,22 @@ impl ComponentMsgObj for PassphraseEntry {
} }
} }
impl<T, U> ComponentMsgObj for Frame<T, U> impl<T> ComponentMsgObj for Frame<T>
where where
T: ComponentMsgObj, T: ComponentMsgObj,
U: AsRef<str>,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
self.inner().msg_try_into_obj(msg) self.inner().msg_try_into_obj(msg)
} }
} }
impl<T> ComponentMsgObj for Progress<T> impl ComponentMsgObj for Progress {
where
T: ParagraphStrType,
{
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
unreachable!() unreachable!()
} }
} }
impl<T> ComponentMsgObj for Homescreen<T> impl ComponentMsgObj for Homescreen {
where
T: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()), HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
@ -186,10 +168,7 @@ where
} }
} }
impl<T> ComponentMsgObj for Lockscreen<T> impl ComponentMsgObj for Lockscreen {
where
T: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()), HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
@ -250,7 +229,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
confirm_btn = confirm_btn.map(|btn| btn.with_default_duration()); confirm_btn = confirm_btn.map(|btn| btn.with_default_duration());
} }
let content = ButtonPage::new_str_buf(paragraphs, theme::BG) let content = ButtonPage::new(paragraphs, theme::BG)
.with_cancel_btn(cancel_btn) .with_cancel_btn(cancel_btn)
.with_confirm_btn(confirm_btn); .with_confirm_btn(confirm_btn);
@ -292,9 +271,9 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
} }
} }
let mut content = ButtonPage::new_str(paragraphs.into_paragraphs(), theme::BG); let mut content = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG);
if hold { if hold {
let confirm_btn = Some(ButtonDetails::text("CONFIRM").with_default_duration()); let confirm_btn = Some(ButtonDetails::text("CONFIRM".into()).with_default_duration());
content = content.with_confirm_btn(confirm_btn); content = content.with_confirm_btn(confirm_btn);
} }
let obj = LayoutObj::new(Frame::new(title, content))?; let obj = LayoutObj::new(Frame::new(title, content))?;
@ -306,9 +285,6 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], kwargs: &Map| { let block = |_args: &[Obj], kwargs: &Map| {
let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?; let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
// Getting this from micropython so it is also a `StrBuffer`, not having
// to handle the string operation in Rust, which would make it a `String`
// (which would them cause issues with general `T: AsRef<str>` parameter)
let truncated_address: StrBuffer = let truncated_address: StrBuffer =
kwargs.get(Qstr::MP_QSTR_truncated_address)?.try_into()?; kwargs.get(Qstr::MP_QSTR_truncated_address)?.try_into()?;
let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?; let amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount)?.try_into()?;
@ -322,7 +298,7 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
Some(ButtonDetails::text("CONTINUE")), Some(ButtonDetails::text("CONTINUE".into())),
); );
let btn_actions = ButtonActions::cancel_next(); let btn_actions = ButtonActions::cancel_next();
Page::<20>::new(btn_layout, btn_actions, Font::MONO).icon_label_text( Page::<20>::new(btn_layout, btn_actions, Font::MONO).icon_label_text(
@ -336,7 +312,7 @@ extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut M
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
Some(ButtonDetails::text("HOLD TO CONFIRM").with_default_duration()), Some(ButtonDetails::text("HOLD TO CONFIRM".into()).with_default_duration()),
); );
let btn_actions = ButtonActions::cancel_confirm(); let btn_actions = ButtonActions::cancel_confirm();
Page::<20>::new(btn_layout, btn_actions, Font::MONO) Page::<20>::new(btn_layout, btn_actions, Font::MONO)
@ -377,7 +353,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
let btn_layout = ButtonLayout::new( let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()), Some(ButtonDetails::cancel_icon()),
None, None,
Some(ButtonDetails::text("HOLD TO SEND").with_default_duration()), Some(ButtonDetails::text("HOLD TO SEND".into()).with_default_duration()),
); );
let btn_actions = ButtonActions::cancel_confirm(); let btn_actions = ButtonActions::cancel_confirm();
@ -461,7 +437,7 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
fn tutorial_screen( fn tutorial_screen(
title: StrBuffer, title: StrBuffer,
text: StrBuffer, text: StrBuffer,
btn_layout: ButtonLayout<&'static str>, btn_layout: ButtonLayout,
btn_actions: ButtonActions, btn_actions: ButtonActions,
) -> Page<10> { ) -> Page<10> {
let mut page = Page::<10>::new( let mut page = Page::<10>::new(
@ -513,7 +489,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen( tutorial_screen(
"HOLD TO CONFIRM".into(), "HOLD TO CONFIRM".into(),
"Press and hold right to approve important operations.".into(), "Press and hold right to approve important operations.".into(),
ButtonLayout::back_and_htc_text("HOLD TO CONFIRM", Duration::from_millis(1000)), ButtonLayout::back_and_htc_text("HOLD TO CONFIRM".into(), Duration::from_millis(1000)),
ButtonActions::prev_next(), ButtonActions::prev_next(),
) )
}, },
@ -521,7 +497,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen( tutorial_screen(
"SCREEN SCROLL".into(), "SCREEN SCROLL".into(),
"Press right to scroll down to read all content when text\ndoesn't fit on one screen. Press left to scroll up.".into(), "Press right to scroll down to read all content when text\ndoesn't fit on one screen. Press left to scroll up.".into(),
ButtonLayout::back_and_text("GOT IT"), ButtonLayout::back_and_text("GOT IT".into()),
ButtonActions::prev_next(), ButtonActions::prev_next(),
) )
}, },
@ -529,14 +505,14 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen( tutorial_screen(
"CONFIRM".into(), "CONFIRM".into(),
"Press both left and right at the same time to confirm.".into(), "Press both left and right at the same time to confirm.".into(),
ButtonLayout::middle_armed_text("CONFIRM"), ButtonLayout::middle_armed_text("CONFIRM".into()),
ButtonActions::prev_next_with_middle(), ButtonActions::prev_next_with_middle(),
) )
}, },
// This page is special // This page is special
5 => { 5 => {
Page::<10>::new( Page::<10>::new(
ButtonLayout::left_right_text("AGAIN", "FINISH"), ButtonLayout::left_right_text("AGAIN".into(), "FINISH".into()),
ButtonActions::beginning_confirm(), ButtonActions::beginning_confirm(),
Font::MONO, Font::MONO,
) )
@ -551,7 +527,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
tutorial_screen( tutorial_screen(
"SKIP TUTORIAL".into(), "SKIP TUTORIAL".into(),
"Are you sure you want to skip the tutorial?".into(), "Are you sure you want to skip the tutorial?".into(),
ButtonLayout::cancel_and_text("SKIP"), ButtonLayout::cancel_and_text("SKIP".into()),
ButtonActions::beginning_cancel(), ButtonActions::beginning_cancel(),
) )
}, },
@ -585,11 +561,11 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?; let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
let share_words: Vec<StrBuffer, 24> = iter_into_vec(share_words_obj)?; let share_words: Vec<StrBuffer, 24> = iter_into_vec(share_words_obj)?;
let confirm_btn = Some(ButtonDetails::text("HOLD TO CONFIRM").with_default_duration()); let confirm_btn =
Some(ButtonDetails::text("HOLD TO CONFIRM".into()).with_default_duration());
let obj = LayoutObj::new( let obj = LayoutObj::new(
ButtonPage::new_str(ShareWords::new(share_words), theme::BG) ButtonPage::new(ShareWords::new(share_words), theme::BG).with_confirm_btn(confirm_btn),
.with_confirm_btn(confirm_btn),
)?; )?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -615,7 +591,9 @@ extern "C" fn new_request_word_count(n_args: usize, args: *const Obj, kwargs: *m
let block = |_args: &[Obj], kwargs: &Map| { let block = |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let choices: Vec<&str, 3> = ["12", "18", "24"].into_iter().collect(); let choices: Vec<StrBuffer, 3> = ["12".into(), "18".into(), "24".into()]
.into_iter()
.collect();
let obj = LayoutObj::new(Frame::new(title, SimpleChoice::new(choices, false, false)))?; let obj = LayoutObj::new(Frame::new(title, SimpleChoice::new(choices, false, false)))?;
Ok(obj.into()) Ok(obj.into())