1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-09 16:18:10 +00:00

feat(core): allow ProgressLayout with danger

- adds `danger` parameter to `show_progress` which can be used by
individual layout implementations to render a loader with a sever color
- this feature is used for wipe device handler
- this feature is implemented for Eckhart layout in this commit

[no changelog]
This commit is contained in:
obrusvit 2025-05-18 14:47:14 +02:00 committed by Vít Obrusník
parent c73351f6df
commit ee8bba40b2
11 changed files with 47 additions and 8 deletions

View File

@ -951,8 +951,9 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma
.get(Qstr::MP_QSTR_title) .get(Qstr::MP_QSTR_title)
.and_then(Obj::try_into_option) .and_then(Obj::try_into_option)
.unwrap_or(None); .unwrap_or(None);
let danger: bool = kwargs.get_or(Qstr::MP_QSTR_danger, false)?;
let layout = ModelUI::show_progress(description, indeterminate, title)?; let layout = ModelUI::show_progress(description, indeterminate, title, danger)?;
Ok(LayoutObj::new_root(layout)?.into()) Ok(LayoutObj::new_root(layout)?.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1739,6 +1740,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// description: str, /// description: str,
/// indeterminate: bool = False, /// indeterminate: bool = False,
/// title: str | None = None, /// title: str | None = None,
/// danger: bool = False,
/// ) -> LayoutObj[UiResult]: /// ) -> LayoutObj[UiResult]:
/// """Show progress loader. Please note that the number of lines reserved on screen for /// """Show progress loader. Please note that the number of lines reserved on screen for
/// description is determined at construction time. If you want multiline descriptions /// description is determined at construction time. If you want multiline descriptions

View File

@ -1031,6 +1031,7 @@ impl FirmwareUI for UIBolt {
description: TString<'static>, description: TString<'static>,
indeterminate: bool, indeterminate: bool,
title: Option<TString<'static>>, title: Option<TString<'static>>,
_danger: bool,
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let (title, description) = if let Some(title) = title { let (title, description) = if let Some(title) = title {
(title, description) (title, description)

View File

@ -1166,6 +1166,7 @@ impl FirmwareUI for UICaesar {
description: TString<'static>, description: TString<'static>,
indeterminate: bool, indeterminate: bool,
title: Option<TString<'static>>, title: Option<TString<'static>>,
_danger: bool,
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let mut progress = Progress::new(indeterminate, description); let mut progress = Progress::new(indeterminate, description);
if let Some(title) = title { if let Some(title) = title {

View File

@ -1003,6 +1003,7 @@ impl FirmwareUI for UIDelizia {
description: TString<'static>, description: TString<'static>,
indeterminate: bool, indeterminate: bool,
title: Option<TString<'static>>, title: Option<TString<'static>>,
_danger: bool,
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let (title, description) = if let Some(title) = title { let (title, description) = if let Some(title) = title {
(title, description) (title, description)

View File

@ -38,12 +38,17 @@ impl ProgressScreen {
title: TString<'static>, title: TString<'static>,
indeterminate: bool, indeterminate: bool,
description: TString<'static>, description: TString<'static>,
danger: bool,
) -> Self { ) -> Self {
Self { Self {
indeterminate, indeterminate,
text: Self::create_paragraphs(title, description), text: Self::create_paragraphs(title, description),
value: 0, value: 0,
border: ScreenBorder::new(theme::GREEN_LIME), border: ScreenBorder::new(if danger {
theme::ORANGE
} else {
theme::GREEN_LIME
}),
coinjoin_progress: false, coinjoin_progress: false,
coinjoin_do_not_disconnect: None, coinjoin_do_not_disconnect: None,
} }

View File

@ -1191,6 +1191,7 @@ impl FirmwareUI for UIEckhart {
description: TString<'static>, description: TString<'static>,
indeterminate: bool, indeterminate: bool,
title: Option<TString<'static>>, title: Option<TString<'static>>,
danger: bool,
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let (title, description) = if let Some(title) = title { let (title, description) = if let Some(title) = title {
(title, description) (title, description)
@ -1202,6 +1203,7 @@ impl FirmwareUI for UIEckhart {
title, title,
indeterminate, indeterminate,
description, description,
danger,
)); ));
Ok(layout) Ok(layout)
} }

View File

@ -358,6 +358,7 @@ pub trait FirmwareUI {
description: TString<'static>, description: TString<'static>,
indeterminate: bool, indeterminate: bool,
title: Option<TString<'static>>, title: Option<TString<'static>>,
danger: bool,
) -> Result<impl LayoutMaybeTrace, Error>; ) -> Result<impl LayoutMaybeTrace, Error>;
fn show_progress_coinjoin( fn show_progress_coinjoin(

View File

@ -626,6 +626,7 @@ def show_progress(
description: str, description: str,
indeterminate: bool = False, indeterminate: bool = False,
title: str | None = None, title: str | None = None,
danger: bool = False,
) -> LayoutObj[UiResult]: ) -> LayoutObj[UiResult]:
"""Show progress loader. Please note that the number of lines reserved on screen for """Show progress loader. Please note that the number of lines reserved on screen for
description is determined at construction time. If you want multiline descriptions description is determined at construction time. If you want multiline descriptions

View File

@ -27,7 +27,7 @@ async def wipe_device(msg: WipeDevice) -> Success:
) )
# start an empty progress screen so that the screen is not blank while waiting # start an empty progress screen so that the screen is not blank while waiting
render_empty_loader(config.StorageMessage.PROCESSING_MSG) render_empty_loader(config.StorageMessage.PROCESSING_MSG, danger=True)
# wipe storage # wipe storage
storage.wipe() storage.wipe()

View File

@ -26,14 +26,22 @@ def allow_all_loader_messages() -> None:
_ignore_loader_messages = False _ignore_loader_messages = False
def render_empty_loader(message: config.StorageMessage, description: str = "") -> None: def render_empty_loader(
"""Render empty loader to prevent the screen appear to be frozen.""" message: config.StorageMessage, description: str = "", danger: bool = False
) -> None:
"""Render empty loader to prevent the screen from appearing frozen.
Args:
message (config.StorageMessage): The message to display in the loader.
description (str, optional): Additional description to display. Defaults to an empty string.
danger (bool, optional): If True, renders the loader in a 'danger' style to indicate a critical operation (e.g. wipe device). Defaults to False.
"""
from trezor.ui.layouts.progress import pin_progress from trezor.ui.layouts.progress import pin_progress
global _progress_layout global _progress_layout
global _started_with_empty_loader global _started_with_empty_loader
_progress_layout = pin_progress(message, description) _progress_layout = pin_progress(message, description, danger)
_progress_layout.report(0, None) _progress_layout.report(0, None)
_started_with_empty_loader = True _started_with_empty_loader = True

View File

@ -25,7 +25,19 @@ def progress(
description: str | None = None, description: str | None = None,
title: str | None = None, title: str | None = None,
indeterminate: bool = False, indeterminate: bool = False,
danger: bool = False,
) -> ui.ProgressLayout: ) -> ui.ProgressLayout:
"""
Displays a progress layout with optional description and title.
Args:
description (str | None): The description text to display. Defaults to a generic "please wait" message.
title (str | None): The title text to display. Defaults to None.
indeterminate (bool): Whether the progress is indeterminate (e.g., a spinner). Defaults to False.
danger (bool): Whether to apply the "danger" style to indicate a critical operation, e.g. wipe device. Defaults to False.
Returns:
ui.ProgressLayout: The configured progress layout.
"""
if description is None: if description is None:
description = TR.progress__please_wait # def_arg description = TR.progress__please_wait # def_arg
@ -34,6 +46,7 @@ def progress(
description=description, description=description,
title=title, title=title,
indeterminate=indeterminate, indeterminate=indeterminate,
danger=danger,
) )
) )
@ -48,8 +61,12 @@ def coinjoin_progress(message: str) -> ui.ProgressLayout:
) )
def pin_progress(title: config.StorageMessage, description: str) -> ui.ProgressLayout: def pin_progress(
return progress(description=description, title=_storage_message_to_str(title)) title: config.StorageMessage, description: str, danger: bool = False
) -> ui.ProgressLayout:
return progress(
description=description, title=_storage_message_to_str(title), danger=danger
)
if not utils.BITCOIN_ONLY: if not utils.BITCOIN_ONLY: