mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-29 10:58:21 +00:00
feat(core): introdue UiFeaturesFirmware
This commit introduces a trait defining high level UI building blocks. The trait is common for all models. trezorui_api module exposes these functions to MicroPython world. `show_info` is implemented as a first function.
This commit is contained in:
parent
99bb1965eb
commit
8683b0158a
@ -1,12 +1,43 @@
|
|||||||
use crate::micropython::map::Map;
|
|
||||||
use crate::ui::layout::obj::ATTACH_TYPE_OBJ;
|
|
||||||
use crate::ui::layout::base::LAYOUT_STATE;
|
|
||||||
use crate::ui::backlight::BACKLIGHT_LEVELS_OBJ;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
micropython::{macros::obj_module, module::Module, qstr::Qstr},
|
micropython::{
|
||||||
ui::layout::result::{CANCELLED, CONFIRMED, INFO},
|
macros::{obj_fn_kw, obj_module},
|
||||||
|
map::Map,
|
||||||
|
module::Module,
|
||||||
|
obj::Obj,
|
||||||
|
qstr::Qstr,
|
||||||
|
util,
|
||||||
|
},
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
backlight::BACKLIGHT_LEVELS_OBJ,
|
||||||
|
layout::{
|
||||||
|
base::LAYOUT_STATE,
|
||||||
|
obj::{LayoutObj, ATTACH_TYPE_OBJ},
|
||||||
|
result::{CANCELLED, CONFIRMED, INFO},
|
||||||
|
},
|
||||||
|
ui_features::ModelUI,
|
||||||
|
ui_features_fw::UIFeaturesFirmware,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// free-standing functions exported to MicroPython mirror `trait
|
||||||
|
// UIFeaturesFirmware`
|
||||||
|
|
||||||
|
extern "C" fn show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
|
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||||
|
let button: TString = kwargs
|
||||||
|
.get_or(Qstr::MP_QSTR_button, TString::empty())?
|
||||||
|
.try_into()?;
|
||||||
|
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?.try_into()?;
|
||||||
|
|
||||||
|
let obj = ModelUI::show_info(title, description, button, time_ms)?;
|
||||||
|
Ok(obj.into())
|
||||||
|
};
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mp_module_trezorui_api: Module = obj_module! {
|
pub static mp_module_trezorui_api: Module = obj_module! {
|
||||||
/// from trezor import utils
|
/// from trezor import utils
|
||||||
@ -103,6 +134,16 @@ pub static mp_module_trezorui_api: Module = obj_module! {
|
|||||||
/// INFO: UiResult
|
/// INFO: UiResult
|
||||||
Qstr::MP_QSTR_INFO => INFO.as_obj(),
|
Qstr::MP_QSTR_INFO => INFO.as_obj(),
|
||||||
|
|
||||||
|
/// def show_info(
|
||||||
|
/// *,
|
||||||
|
/// title: str,
|
||||||
|
/// description: str = "",
|
||||||
|
/// button: str = "",
|
||||||
|
/// time_ms: int = 0,
|
||||||
|
/// ) -> LayoutObj[UiResult]:
|
||||||
|
/// """Info screen."""
|
||||||
|
Qstr::MP_QSTR_show_info => obj_fn_kw!(0, show_info).as_obj(),
|
||||||
|
|
||||||
/// class BacklightLevels:
|
/// class BacklightLevels:
|
||||||
/// """Backlight levels. Values dynamically update based on user settings."""
|
/// """Backlight levels. Values dynamically update based on user settings."""
|
||||||
/// MAX: ClassVar[int]
|
/// MAX: ClassVar[int]
|
||||||
@ -130,4 +171,5 @@ pub static mp_module_trezorui_api: Module = obj_module! {
|
|||||||
/// TRANSITIONING: "ClassVar[LayoutState]"
|
/// TRANSITIONING: "ClassVar[LayoutState]"
|
||||||
/// DONE: "ClassVar[LayoutState]"
|
/// DONE: "ClassVar[LayoutState]"
|
||||||
Qstr::MP_QSTR_LayoutState => LAYOUT_STATE.as_obj(),
|
Qstr::MP_QSTR_LayoutState => LAYOUT_STATE.as_obj(),
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -94,7 +94,7 @@ pub trait ComponentMsgObj: Component {
|
|||||||
pub trait ComponentMaybeTrace: Component + ComponentMsgObj + MaybeTrace {}
|
pub trait ComponentMaybeTrace: Component + ComponentMsgObj + MaybeTrace {}
|
||||||
impl<T> ComponentMaybeTrace for T where T: Component + ComponentMsgObj + MaybeTrace {}
|
impl<T> ComponentMaybeTrace for T where T: Component + ComponentMsgObj + MaybeTrace {}
|
||||||
|
|
||||||
struct RootComponent<T, M>
|
pub struct RootComponent<T, M>
|
||||||
where
|
where
|
||||||
T: Component,
|
T: Component,
|
||||||
M: UIFeaturesCommon,
|
M: UIFeaturesCommon,
|
||||||
|
@ -23,6 +23,7 @@ pub mod model_mercury;
|
|||||||
pub mod model_tr;
|
pub mod model_tr;
|
||||||
#[cfg(feature = "model_tt")]
|
#[cfg(feature = "model_tt")]
|
||||||
pub mod model_tt;
|
pub mod model_tt;
|
||||||
|
|
||||||
pub mod ui_features;
|
pub mod ui_features;
|
||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
pub mod ui_features_fw;
|
pub mod ui_features_fw;
|
||||||
|
@ -1003,21 +1003,6 @@ extern "C" fn new_show_success(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
|
||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
|
||||||
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
|
||||||
let content = Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description));
|
|
||||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
|
||||||
Frame::left_aligned(title, SwipeContent::new(content))
|
|
||||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
|
||||||
.with_swipe(Direction::Up, SwipeSettings::default()),
|
|
||||||
))?;
|
|
||||||
Ok(obj.into())
|
|
||||||
};
|
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
@ -1527,7 +1512,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
///
|
///
|
||||||
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorui2.to_obj(),
|
||||||
|
|
||||||
|
|
||||||
/// def disable_animation(disable: bool) -> None:
|
/// def disable_animation(disable: bool) -> None:
|
||||||
/// """Disable animations, debug builds only."""
|
/// """Disable animations, debug builds only."""
|
||||||
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
|
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
|
||||||
@ -1746,17 +1730,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Success screen. Description is used in the footer."""
|
/// """Success screen. Description is used in the footer."""
|
||||||
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
|
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
|
||||||
|
|
||||||
/// def show_info(
|
|
||||||
/// *,
|
|
||||||
/// title: str,
|
|
||||||
/// button: str = "CONTINUE",
|
|
||||||
/// description: str = "",
|
|
||||||
/// allow_cancel: bool = False,
|
|
||||||
/// time_ms: int = 0,
|
|
||||||
/// ) -> LayoutObj[UiResult]:
|
|
||||||
/// """Info modal. No buttons shown when `button` is empty string."""
|
|
||||||
Qstr::MP_QSTR_show_info => obj_fn_kw!(0, new_show_info).as_obj(),
|
|
||||||
|
|
||||||
/// def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
/// def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
||||||
/// """Warning modal, receiving address mismatch."""
|
/// """Warning modal, receiving address mismatch."""
|
||||||
Qstr::MP_QSTR_show_mismatch => obj_fn_kw!(0, new_show_mismatch).as_obj(),
|
Qstr::MP_QSTR_show_mismatch => obj_fn_kw!(0, new_show_mismatch).as_obj(),
|
||||||
|
37
core/embed/rust/src/ui/model_mercury/ui_features_fw.rs
Normal file
37
core/embed/rust/src/ui/model_mercury/ui_features_fw.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use crate::{
|
||||||
|
error::Error,
|
||||||
|
micropython::gc::Gc,
|
||||||
|
strutil::TString,
|
||||||
|
translations::TR,
|
||||||
|
ui::{
|
||||||
|
component::{
|
||||||
|
swipe_detect::SwipeSettings,
|
||||||
|
text::paragraphs::{Paragraph, Paragraphs},
|
||||||
|
},
|
||||||
|
geometry::Direction,
|
||||||
|
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
|
||||||
|
ui_features_fw::UIFeaturesFirmware,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
component::{Frame, SwipeContent, SwipeUpScreen},
|
||||||
|
theme, ModelMercuryFeatures,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl UIFeaturesFirmware for ModelMercuryFeatures {
|
||||||
|
fn show_info(
|
||||||
|
title: TString<'static>,
|
||||||
|
description: TString<'static>,
|
||||||
|
_button: TString<'static>,
|
||||||
|
_time_ms: u32,
|
||||||
|
) -> Result<Gc<LayoutObj>, Error> {
|
||||||
|
let content = Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description));
|
||||||
|
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||||
|
Frame::left_aligned(title, SwipeContent::new(content))
|
||||||
|
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||||
|
.with_swipe(Direction::Up, SwipeSettings::default()),
|
||||||
|
))?;
|
||||||
|
Ok(obj)
|
||||||
|
}
|
||||||
|
}
|
@ -1098,31 +1098,6 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
|
||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
|
||||||
let description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
|
|
||||||
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
|
|
||||||
|
|
||||||
let content = Frame::new(
|
|
||||||
title,
|
|
||||||
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
|
|
||||||
);
|
|
||||||
let obj = if time_ms == 0 {
|
|
||||||
// No timer, used when we only want to draw the dialog once and
|
|
||||||
// then throw away the layout object.
|
|
||||||
LayoutObj::new(content)?
|
|
||||||
} else {
|
|
||||||
// Timeout.
|
|
||||||
let timeout = Timeout::new(time_ms);
|
|
||||||
LayoutObj::new((timeout, content.map(|_| None)))?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(obj.into())
|
|
||||||
};
|
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn new_show_passphrase() -> Obj {
|
extern "C" fn new_show_passphrase() -> Obj {
|
||||||
let block = move || {
|
let block = move || {
|
||||||
let text: TString = TR::passphrase__please_enter.into();
|
let text: TString = TR::passphrase__please_enter.into();
|
||||||
@ -1872,15 +1847,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Warning modal with middle button and centered text."""
|
/// """Warning modal with middle button and centered text."""
|
||||||
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
|
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
|
||||||
|
|
||||||
/// def show_info(
|
|
||||||
/// *,
|
|
||||||
/// title: str,
|
|
||||||
/// description: str = "",
|
|
||||||
/// time_ms: int = 0,
|
|
||||||
/// ) -> LayoutObj[UiResult]:
|
|
||||||
/// """Info modal."""
|
|
||||||
Qstr::MP_QSTR_show_info => obj_fn_kw!(0, new_show_info).as_obj(),
|
|
||||||
|
|
||||||
/// def show_passphrase() -> LayoutObj[UiResult]:
|
/// def show_passphrase() -> LayoutObj[UiResult]:
|
||||||
/// """Show passphrase on host dialog."""
|
/// """Show passphrase on host dialog."""
|
||||||
Qstr::MP_QSTR_show_passphrase => obj_fn_0!(new_show_passphrase).as_obj(),
|
Qstr::MP_QSTR_show_passphrase => obj_fn_0!(new_show_passphrase).as_obj(),
|
||||||
|
@ -12,6 +12,7 @@ mod screens;
|
|||||||
pub mod theme;
|
pub mod theme;
|
||||||
|
|
||||||
pub struct ModelTRFeatures {}
|
pub struct ModelTRFeatures {}
|
||||||
|
pub mod ui_features_fw;
|
||||||
|
|
||||||
impl UIFeaturesCommon for ModelTRFeatures {
|
impl UIFeaturesCommon for ModelTRFeatures {
|
||||||
const SCREEN: Rect = constant::SCREEN;
|
const SCREEN: Rect = constant::SCREEN;
|
||||||
|
38
core/embed/rust/src/ui/model_tr/ui_features_fw.rs
Normal file
38
core/embed/rust/src/ui/model_tr/ui_features_fw.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use crate::{
|
||||||
|
error::Error,
|
||||||
|
micropython::gc::Gc,
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{
|
||||||
|
text::paragraphs::{Paragraph, Paragraphs}, ComponentExt, Timeout
|
||||||
|
},
|
||||||
|
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
|
||||||
|
ui_features_fw::UIFeaturesFirmware,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{component::Frame, theme, ModelTRFeatures};
|
||||||
|
|
||||||
|
impl UIFeaturesFirmware for ModelTRFeatures {
|
||||||
|
fn show_info(
|
||||||
|
title: TString<'static>,
|
||||||
|
description: TString<'static>,
|
||||||
|
_button: TString<'static>,
|
||||||
|
time_ms: u32,
|
||||||
|
) -> Result<Gc<LayoutObj>, Error> {
|
||||||
|
let content = Frame::new(
|
||||||
|
title,
|
||||||
|
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
|
||||||
|
);
|
||||||
|
let obj = if time_ms == 0 {
|
||||||
|
// No timer, used when we only want to draw the dialog once and
|
||||||
|
// then throw away the layout object.
|
||||||
|
LayoutObj::new(content)?
|
||||||
|
} else {
|
||||||
|
// Timeout.
|
||||||
|
let timeout = Timeout::new(time_ms);
|
||||||
|
LayoutObj::new((timeout, content.map(|_| None)))?
|
||||||
|
};
|
||||||
|
Ok(obj)
|
||||||
|
}
|
||||||
|
}
|
@ -1011,20 +1011,6 @@ extern "C" fn new_show_success(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
|
||||||
let icon = BlendedImage::new(
|
|
||||||
theme::IMAGE_BG_CIRCLE,
|
|
||||||
theme::IMAGE_FG_INFO,
|
|
||||||
theme::INFO_COLOR,
|
|
||||||
theme::FG,
|
|
||||||
theme::BG,
|
|
||||||
);
|
|
||||||
new_show_modal(kwargs, icon, theme::button_info())
|
|
||||||
};
|
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
@ -1640,7 +1626,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// from trezor import utils
|
/// from trezor import utils
|
||||||
/// from trezorui_api import *
|
/// from trezorui_api import *
|
||||||
///
|
///
|
||||||
|
|
||||||
/// def disable_animation(disable: bool) -> None:
|
/// def disable_animation(disable: bool) -> None:
|
||||||
/// """Disable animations, debug builds only."""
|
/// """Disable animations, debug builds only."""
|
||||||
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
|
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
|
||||||
@ -1853,17 +1838,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Success modal. No buttons shown when `button` is empty string."""
|
/// """Success modal. No buttons shown when `button` is empty string."""
|
||||||
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
|
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
|
||||||
|
|
||||||
/// def show_info(
|
|
||||||
/// *,
|
|
||||||
/// title: str,
|
|
||||||
/// button: str = "CONTINUE",
|
|
||||||
/// description: str = "",
|
|
||||||
/// allow_cancel: bool = False,
|
|
||||||
/// time_ms: int = 0,
|
|
||||||
/// ) -> LayoutObj[UiResult]:
|
|
||||||
/// """Info modal. No buttons shown when `button` is empty string."""
|
|
||||||
Qstr::MP_QSTR_show_info => obj_fn_kw!(0, new_show_info).as_obj(),
|
|
||||||
|
|
||||||
/// def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
/// def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
||||||
/// """Warning modal, receiving address mismatch."""
|
/// """Warning modal, receiving address mismatch."""
|
||||||
Qstr::MP_QSTR_show_mismatch => obj_fn_kw!(0, new_show_mismatch).as_obj(),
|
Qstr::MP_QSTR_show_mismatch => obj_fn_kw!(0, new_show_mismatch).as_obj(),
|
||||||
|
@ -19,6 +19,7 @@ use crate::ui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub struct ModelTTFeatures;
|
pub struct ModelTTFeatures;
|
||||||
|
pub mod ui_features_fw;
|
||||||
|
|
||||||
impl UIFeaturesCommon for ModelTTFeatures {
|
impl UIFeaturesCommon for ModelTTFeatures {
|
||||||
#[cfg(feature = "backlight")]
|
#[cfg(feature = "backlight")]
|
||||||
|
111
core/embed/rust/src/ui/model_tt/ui_features_fw.rs
Normal file
111
core/embed/rust/src/ui/model_tt/ui_features_fw.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
use crate::{
|
||||||
|
error::Error,
|
||||||
|
micropython::gc::Gc,
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{image::BlendedImage, ComponentExt, Empty, Timeout},
|
||||||
|
layout::obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
|
||||||
|
ui_features_fw::UIFeaturesFirmware,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
component::{Button, ButtonMsg, ButtonStyleSheet, CancelConfirmMsg, IconDialog},
|
||||||
|
theme, ModelTTFeatures,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl UIFeaturesFirmware for ModelTTFeatures {
|
||||||
|
fn show_info(
|
||||||
|
title: TString<'static>,
|
||||||
|
description: TString<'static>,
|
||||||
|
button: TString<'static>,
|
||||||
|
time_ms: u32,
|
||||||
|
) -> Result<Gc<LayoutObj>, Error> {
|
||||||
|
assert!(
|
||||||
|
!button.is_empty() || time_ms > 0,
|
||||||
|
"either button or timeout must be set"
|
||||||
|
);
|
||||||
|
|
||||||
|
let icon = BlendedImage::new(
|
||||||
|
theme::IMAGE_BG_CIRCLE,
|
||||||
|
theme::IMAGE_FG_INFO,
|
||||||
|
theme::INFO_COLOR,
|
||||||
|
theme::FG,
|
||||||
|
theme::BG,
|
||||||
|
);
|
||||||
|
let res = new_show_modal(
|
||||||
|
title,
|
||||||
|
TString::empty(),
|
||||||
|
description,
|
||||||
|
TString::empty(),
|
||||||
|
false,
|
||||||
|
time_ms,
|
||||||
|
icon,
|
||||||
|
theme::button_info(),
|
||||||
|
)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_show_modal(
|
||||||
|
title: TString<'static>,
|
||||||
|
value: TString<'static>,
|
||||||
|
description: TString<'static>,
|
||||||
|
button: TString<'static>,
|
||||||
|
allow_cancel: bool,
|
||||||
|
time_ms: u32,
|
||||||
|
icon: BlendedImage,
|
||||||
|
button_style: ButtonStyleSheet,
|
||||||
|
) -> Result<Gc<LayoutObj>, Error> {
|
||||||
|
let no_buttons = button.is_empty();
|
||||||
|
let obj = if no_buttons && time_ms == 0 {
|
||||||
|
// No buttons and no timer, used when we only want to draw the dialog once and
|
||||||
|
// then throw away the layout object.
|
||||||
|
LayoutObj::new(
|
||||||
|
IconDialog::new(icon, title, Empty)
|
||||||
|
.with_value(value)
|
||||||
|
.with_description(description),
|
||||||
|
)?
|
||||||
|
} else if no_buttons && time_ms > 0 {
|
||||||
|
// Timeout, no buttons.
|
||||||
|
LayoutObj::new(
|
||||||
|
IconDialog::new(
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
Timeout::new(time_ms).map(|_| Some(CancelConfirmMsg::Confirmed)),
|
||||||
|
)
|
||||||
|
.with_value(value)
|
||||||
|
.with_description(description),
|
||||||
|
)?
|
||||||
|
} else if allow_cancel {
|
||||||
|
// Two buttons.
|
||||||
|
LayoutObj::new(
|
||||||
|
IconDialog::new(
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
Button::cancel_confirm(
|
||||||
|
Button::with_icon(theme::ICON_CANCEL),
|
||||||
|
Button::with_text(button).styled(button_style),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.with_value(value)
|
||||||
|
.with_description(description),
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
// Single button.
|
||||||
|
LayoutObj::new(
|
||||||
|
IconDialog::new(
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
theme::button_bar(Button::with_text(button).styled(button_style).map(|msg| {
|
||||||
|
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.with_value(value)
|
||||||
|
.with_description(description),
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(obj)
|
||||||
|
}
|
12
core/embed/rust/src/ui/ui_features_fw.rs
Normal file
12
core/embed/rust/src/ui/ui_features_fw.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use crate::{error::Error, micropython::gc::Gc, strutil::TString};
|
||||||
|
|
||||||
|
use super::layout::obj::{LayoutMaybeTrace, LayoutObj};
|
||||||
|
|
||||||
|
pub trait UIFeaturesFirmware {
|
||||||
|
fn show_info(
|
||||||
|
title: TString<'static>,
|
||||||
|
description: TString<'static>,
|
||||||
|
button: TString<'static>,
|
||||||
|
time_ms: u32,
|
||||||
|
) -> Result<Gc<LayoutObj>, Error>;
|
||||||
|
}
|
@ -239,18 +239,6 @@ def show_success(
|
|||||||
"""Success screen. Description is used in the footer."""
|
"""Success screen. Description is used in the footer."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
|
||||||
def show_info(
|
|
||||||
*,
|
|
||||||
title: str,
|
|
||||||
button: str = "CONTINUE",
|
|
||||||
description: str = "",
|
|
||||||
allow_cancel: bool = False,
|
|
||||||
time_ms: int = 0,
|
|
||||||
) -> LayoutObj[UiResult]:
|
|
||||||
"""Info modal. No buttons shown when `button` is empty string."""
|
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
||||||
"""Warning modal, receiving address mismatch."""
|
"""Warning modal, receiving address mismatch."""
|
||||||
@ -799,16 +787,6 @@ def show_warning(
|
|||||||
"""Warning modal with middle button and centered text."""
|
"""Warning modal with middle button and centered text."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tr/layout.rs
|
|
||||||
def show_info(
|
|
||||||
*,
|
|
||||||
title: str,
|
|
||||||
description: str = "",
|
|
||||||
time_ms: int = 0,
|
|
||||||
) -> LayoutObj[UiResult]:
|
|
||||||
"""Info modal."""
|
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tr/layout.rs
|
# rust/src/ui/model_tr/layout.rs
|
||||||
def show_passphrase() -> LayoutObj[UiResult]:
|
def show_passphrase() -> LayoutObj[UiResult]:
|
||||||
"""Show passphrase on host dialog."""
|
"""Show passphrase on host dialog."""
|
||||||
@ -1258,18 +1236,6 @@ def show_success(
|
|||||||
"""Success modal. No buttons shown when `button` is empty string."""
|
"""Success modal. No buttons shown when `button` is empty string."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tt/layout.rs
|
|
||||||
def show_info(
|
|
||||||
*,
|
|
||||||
title: str,
|
|
||||||
button: str = "CONTINUE",
|
|
||||||
description: str = "",
|
|
||||||
allow_cancel: bool = False,
|
|
||||||
time_ms: int = 0,
|
|
||||||
) -> LayoutObj[UiResult]:
|
|
||||||
"""Info modal. No buttons shown when `button` is empty string."""
|
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tt/layout.rs
|
# rust/src/ui/model_tt/layout.rs
|
||||||
def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
def show_mismatch(*, title: str) -> LayoutObj[UiResult]:
|
||||||
"""Warning modal, receiving address mismatch."""
|
"""Warning modal, receiving address mismatch."""
|
||||||
|
@ -68,6 +68,17 @@ CANCELLED: UiResult
|
|||||||
INFO: UiResult
|
INFO: UiResult
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/api/firmware_upy.rs
|
||||||
|
def show_info(
|
||||||
|
*,
|
||||||
|
title: str,
|
||||||
|
description: str = "",
|
||||||
|
button: str = "",
|
||||||
|
time_ms: int = 0,
|
||||||
|
) -> LayoutObj[UiResult]:
|
||||||
|
"""Info screen."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/api/firmware_upy.rs
|
# rust/src/ui/api/firmware_upy.rs
|
||||||
class BacklightLevels:
|
class BacklightLevels:
|
||||||
"""Backlight levels. Values dynamically update based on user settings."""
|
"""Backlight levels. Values dynamically update based on user settings."""
|
||||||
|
@ -290,8 +290,9 @@ async def show_intro_backup(single_share: bool, num_of_words: int | None) -> Non
|
|||||||
description = TR.backup__info_multi_share_backup
|
description = TR.backup__info_multi_share_backup
|
||||||
|
|
||||||
await interact(
|
await interact(
|
||||||
trezorui2.show_info(
|
trezorui_api.show_info(
|
||||||
title=TR.backup__title_create_wallet_backup, description=description
|
title=TR.backup__title_create_wallet_backup,
|
||||||
|
description=description,
|
||||||
),
|
),
|
||||||
"backup_intro",
|
"backup_intro",
|
||||||
ButtonRequestType.ResetDevice,
|
ButtonRequestType.ResetDevice,
|
||||||
|
@ -1141,7 +1141,7 @@ def error_popup(
|
|||||||
description = description.format(description_param)
|
description = description.format(description_param)
|
||||||
if subtitle:
|
if subtitle:
|
||||||
description = f"{subtitle}\n{description}"
|
description = f"{subtitle}\n{description}"
|
||||||
return trezorui2.show_info(
|
return trezorui_api.show_info(
|
||||||
title=title,
|
title=title,
|
||||||
description=description,
|
description=description,
|
||||||
time_ms=timeout_ms,
|
time_ms=timeout_ms,
|
||||||
|
@ -313,11 +313,10 @@ def show_intro_backup(single_share: bool, num_of_words: int | None) -> Awaitable
|
|||||||
description = TR.backup__info_multi_share_backup
|
description = TR.backup__info_multi_share_backup
|
||||||
|
|
||||||
return raise_if_not_confirmed(
|
return raise_if_not_confirmed(
|
||||||
trezorui2.show_info(
|
trezorui_api.show_info(
|
||||||
title="",
|
title="",
|
||||||
button=TR.buttons__continue,
|
|
||||||
description=description,
|
description=description,
|
||||||
allow_cancel=False,
|
button=TR.buttons__continue,
|
||||||
),
|
),
|
||||||
"backup_intro",
|
"backup_intro",
|
||||||
ButtonRequestType.ResetDevice,
|
ButtonRequestType.ResetDevice,
|
||||||
@ -326,10 +325,10 @@ def show_intro_backup(single_share: bool, num_of_words: int | None) -> Awaitable
|
|||||||
|
|
||||||
def show_warning_backup() -> Awaitable[trezorui_api.UiResult]:
|
def show_warning_backup() -> Awaitable[trezorui_api.UiResult]:
|
||||||
return interact(
|
return interact(
|
||||||
trezorui2.show_info(
|
trezorui_api.show_info(
|
||||||
title=TR.reset__never_make_digital_copy,
|
title=TR.reset__never_make_digital_copy,
|
||||||
|
description="",
|
||||||
button=TR.buttons__ok_i_understand,
|
button=TR.buttons__ok_i_understand,
|
||||||
allow_cancel=False,
|
|
||||||
),
|
),
|
||||||
"backup_warning",
|
"backup_warning",
|
||||||
ButtonRequestType.ResetDevice,
|
ButtonRequestType.ResetDevice,
|
||||||
|
Loading…
Reference in New Issue
Block a user