mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-28 17:38:35 +00:00
WIP - create confirm_blob with not showing hyphens and line-ends
This commit is contained in:
parent
fe90b0a5cd
commit
5398849275
@ -30,7 +30,7 @@ use crate::{
|
||||
layout::{
|
||||
obj::{ComponentMsgObj, LayoutObj},
|
||||
result::{CANCELLED, CONFIRMED, INFO},
|
||||
util::{iter_into_objs, iter_into_vec, upy_disable_animation},
|
||||
util::{iter_into_objs, iter_into_vec, upy_disable_animation, ConfirmBlob},
|
||||
},
|
||||
model_tr::component::{ScrollableContent, ScrollableFrame},
|
||||
},
|
||||
@ -185,6 +185,51 @@ impl ComponentMsgObj for Lockscreen {
|
||||
}
|
||||
}
|
||||
|
||||
/// Function to create and call a `ButtonPage` dialog based on `Paragraphs`
|
||||
/// Has optional title (supply empty `StrBuffer` for that) and hold-to-confirm
|
||||
/// functionality.
|
||||
fn paragraphs_in_button_page<T: ParagraphSource + 'static>(
|
||||
title: StrBuffer,
|
||||
paragraphs: Paragraphs<T>,
|
||||
verb: StrBuffer,
|
||||
verb_cancel: Option<StrBuffer>,
|
||||
hold: bool,
|
||||
) -> Result<Obj, Error> {
|
||||
// Left button - icon, text or nothing.
|
||||
let cancel_btn = if let Some(verb_cancel) = verb_cancel {
|
||||
if !verb_cancel.is_empty() {
|
||||
Some(ButtonDetails::text(verb_cancel))
|
||||
} else {
|
||||
Some(ButtonDetails::cancel_icon())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Right button - text or nothing.
|
||||
// Optional HoldToConfirm
|
||||
let mut confirm_btn = if !verb.is_empty() {
|
||||
Some(ButtonDetails::text(verb))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if hold {
|
||||
confirm_btn = confirm_btn.map(|btn| btn.with_default_duration());
|
||||
}
|
||||
|
||||
let content = ButtonPage::new(paragraphs, theme::BG)
|
||||
.with_cancel_btn(cancel_btn)
|
||||
.with_confirm_btn(confirm_btn);
|
||||
|
||||
let mut frame = ScrollableFrame::new(content);
|
||||
if !title.as_ref().is_empty() {
|
||||
frame = frame.with_title(title);
|
||||
}
|
||||
let obj = LayoutObj::new(frame)?;
|
||||
|
||||
Ok(obj.into())
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = |_args: &[Obj], kwargs: &Map| {
|
||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
@ -215,40 +260,36 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
paragraphs.into_paragraphs()
|
||||
};
|
||||
|
||||
// Left button - icon, text or nothing.
|
||||
let cancel_btn = if let Some(verb_cancel) = verb_cancel {
|
||||
if !verb_cancel.is_empty() {
|
||||
Some(ButtonDetails::text(verb_cancel))
|
||||
} else {
|
||||
Some(ButtonDetails::cancel_icon())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
paragraphs_in_button_page(title, paragraphs, verb, verb_cancel, hold)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
// Right button - text or nothing.
|
||||
// Optional HoldToConfirm
|
||||
let mut confirm_btn = if !verb.is_empty() {
|
||||
Some(ButtonDetails::text(verb))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if hold {
|
||||
// TODO: clients might want to set the duration
|
||||
confirm_btn = confirm_btn.map(|btn| btn.with_default_duration());
|
||||
extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?;
|
||||
let description: Option<StrBuffer> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let extra: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
||||
let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?;
|
||||
let verb_cancel: Option<StrBuffer> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: description.unwrap_or_else(StrBuffer::empty),
|
||||
extra: extra.unwrap_or_else(StrBuffer::empty),
|
||||
data: data.try_into()?,
|
||||
description_font: &theme::TEXT_BOLD,
|
||||
extra_font: &theme::TEXT_MONO,
|
||||
data_font: &theme::TEXT_MONO_DATA,
|
||||
}
|
||||
.into_paragraphs();
|
||||
|
||||
let content = ButtonPage::new(paragraphs, theme::BG)
|
||||
.with_cancel_btn(cancel_btn)
|
||||
.with_confirm_btn(confirm_btn);
|
||||
|
||||
let obj = if title.as_ref().is_empty() {
|
||||
LayoutObj::new(ScrollableFrame::new(content))?
|
||||
} else {
|
||||
LayoutObj::new(ScrollableFrame::new(content).with_title(title))?
|
||||
};
|
||||
|
||||
Ok(obj.into())
|
||||
paragraphs_in_button_page(title, paragraphs, verb, verb_cancel, hold)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -264,9 +305,10 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
|
||||
let mut iter_buf = IterBuf::new();
|
||||
let iter = Iter::try_from_obj_with_buf(items, &mut iter_buf)?;
|
||||
for para in iter {
|
||||
let [key, value, _is_mono]: [Obj; 3] = iter_into_objs(para)?;
|
||||
let [key, value, is_data]: [Obj; 3] = iter_into_objs(para)?;
|
||||
let key = key.try_into_option::<StrBuffer>()?;
|
||||
let value = value.try_into_option::<StrBuffer>()?;
|
||||
let is_data: bool = is_data.try_into()?;
|
||||
|
||||
if let Some(key) = key {
|
||||
if value.is_some() {
|
||||
@ -276,17 +318,22 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
|
||||
}
|
||||
}
|
||||
if let Some(value) = value {
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
|
||||
let style = if is_data {
|
||||
&theme::TEXT_MONO_DATA
|
||||
} else {
|
||||
&theme::TEXT_MONO
|
||||
};
|
||||
paragraphs.add(Paragraph::new(style, value));
|
||||
}
|
||||
}
|
||||
|
||||
let mut content = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG);
|
||||
if hold {
|
||||
let confirm_btn = Some(ButtonDetails::text("CONFIRM".into()).with_default_duration());
|
||||
content = content.with_confirm_btn(confirm_btn);
|
||||
}
|
||||
let obj = LayoutObj::new(ScrollableFrame::new(content).with_title(title))?;
|
||||
Ok(obj.into())
|
||||
paragraphs_in_button_page(
|
||||
title,
|
||||
paragraphs.into_paragraphs(),
|
||||
"CONFIRM".into(),
|
||||
None,
|
||||
hold,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -389,6 +436,7 @@ extern "C" fn new_show_receive_address(n_args: usize, args: *const Obj, kwargs:
|
||||
let btn_layout = ButtonLayout::cancel_armed_text("CONFIRM".into(), "i".into());
|
||||
let btn_actions = ButtonActions::last_confirm_next();
|
||||
Page::<15>::new(btn_layout, btn_actions, Font::BOLD)
|
||||
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
|
||||
.text_bold(title)
|
||||
.newline()
|
||||
.newline_half()
|
||||
@ -446,10 +494,10 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
|
||||
kwargs.get_or(Qstr::MP_QSTR_description, StrBuffer::empty())?;
|
||||
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
|
||||
|
||||
let content = Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_BOLD, title),
|
||||
Paragraph::new(&theme::TEXT_MONO, description),
|
||||
]);
|
||||
let content = Frame::new(
|
||||
title,
|
||||
Paragraphs::new([Paragraph::new(&theme::TEXT_MONO, 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.
|
||||
@ -922,6 +970,18 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm action."""
|
||||
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),
|
||||
|
||||
/// def confirm_blob(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// data: str | bytes,
|
||||
/// description: str | None,
|
||||
/// extra: str | None,
|
||||
/// verb_cancel: str | None = None,
|
||||
/// hold: bool = False,
|
||||
/// ) -> object:
|
||||
/// """Confirm byte sequence data."""
|
||||
Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(),
|
||||
|
||||
/// def confirm_properties(
|
||||
/// *,
|
||||
/// title: str,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ui::{
|
||||
component::text::TextStyle,
|
||||
component::{text::TextStyle, LineBreaking},
|
||||
display::{Color, Font, IconAndName},
|
||||
geometry::Offset,
|
||||
};
|
||||
@ -17,9 +17,12 @@ pub const FONT_CHOICE_ITEMS: Font = Font::NORMAL;
|
||||
pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, FG, BG, FG, FG);
|
||||
pub const TEXT_DEMIBOLD: TextStyle = TextStyle::new(Font::DEMIBOLD, FG, BG, FG, FG);
|
||||
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, FG, BG, FG, FG);
|
||||
/// Mono text has the icon ellipsis
|
||||
pub const TEXT_MONO: TextStyle =
|
||||
TextStyle::new(Font::MONO, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0);
|
||||
// Header does not have the ellipsis
|
||||
/// Mono data text does not have hyphens
|
||||
pub const TEXT_MONO_DATA: TextStyle =
|
||||
TEXT_MONO.with_line_breaking(LineBreaking::BreakWordsNoHyphen);
|
||||
pub const TEXT_HEADER: TextStyle = TextStyle::new(Font::BOLD, FG, BG, FG, FG);
|
||||
|
||||
// Icons with their names for debugging purposes
|
||||
|
@ -606,28 +606,33 @@ async def confirm_backup(ctx: GenericContext) -> bool:
|
||||
|
||||
|
||||
async def confirm_path_warning(
|
||||
ctx: GenericContext, path: str, path_type: str = "Path"
|
||||
ctx: GenericContext,
|
||||
path: str,
|
||||
path_type: str | None = None,
|
||||
) -> None:
|
||||
if path_type:
|
||||
title = f"Unknown {path_type}"
|
||||
else:
|
||||
title = "Unknown path"
|
||||
return await _placeholder_confirm(
|
||||
ctx,
|
||||
"path_warning",
|
||||
"CONFIRM PATH",
|
||||
f"{path_type}\n{path} is unknown.\nAre you sure?",
|
||||
title.upper(),
|
||||
description=path,
|
||||
br_code=ButtonRequestType.UnknownDerivationPath,
|
||||
)
|
||||
|
||||
|
||||
def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
||||
content = RustLayout(
|
||||
trezorui2.confirm_action(
|
||||
return RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title.upper(),
|
||||
action="",
|
||||
description=xpub,
|
||||
verb="CONFIRM",
|
||||
data=xpub,
|
||||
verb_cancel=cancel,
|
||||
description=None,
|
||||
extra=None,
|
||||
)
|
||||
)
|
||||
return content
|
||||
|
||||
|
||||
async def show_xpub(ctx: GenericContext, xpub: str, title: str) -> None:
|
||||
@ -880,14 +885,25 @@ async def confirm_blob(
|
||||
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||
ask_pagination: bool = False,
|
||||
) -> None:
|
||||
await confirm_action(
|
||||
ctx,
|
||||
br_type,
|
||||
title.upper(),
|
||||
description,
|
||||
str(data),
|
||||
hold=hold,
|
||||
br_code=br_code,
|
||||
title = title.upper()
|
||||
description = description or ""
|
||||
layout = RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title,
|
||||
description=description,
|
||||
data=data,
|
||||
extra=None,
|
||||
hold=hold,
|
||||
)
|
||||
)
|
||||
|
||||
await raise_if_cancelled(
|
||||
interact(
|
||||
ctx,
|
||||
layout,
|
||||
br_type,
|
||||
br_code,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@ -959,7 +975,10 @@ async def confirm_properties(
|
||||
if isinstance(prop[1], bytes):
|
||||
return (prop[0], hexlify(prop[1]).decode(), True)
|
||||
else:
|
||||
return (prop[0], prop[1], False)
|
||||
# When there is not space in the text, taking it as data
|
||||
# to not include hyphens
|
||||
is_data = prop[1] and " " not in prop[1]
|
||||
return (prop[0], prop[1], is_data)
|
||||
|
||||
await raise_if_cancelled(
|
||||
interact(
|
||||
@ -1208,12 +1227,13 @@ async def show_popup(
|
||||
description_param: str = "",
|
||||
timeout_ms: int = 3000,
|
||||
) -> None:
|
||||
description = description.format(description_param)
|
||||
if subtitle:
|
||||
title += f"\n{subtitle}"
|
||||
description = f"{subtitle}\n{description}"
|
||||
await RustLayout(
|
||||
trezorui2.show_info(
|
||||
title=title,
|
||||
description=description.format(description_param),
|
||||
description=description,
|
||||
time_ms=timeout_ms,
|
||||
)
|
||||
)
|
||||
|
@ -331,7 +331,7 @@ async def confirm_path_warning(
|
||||
|
||||
|
||||
def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
||||
content = RustLayout(
|
||||
return RustLayout(
|
||||
trezorui2.confirm_blob(
|
||||
title=title,
|
||||
data=xpub,
|
||||
@ -340,7 +340,6 @@ def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
||||
description=None,
|
||||
)
|
||||
)
|
||||
return content
|
||||
|
||||
|
||||
async def show_xpub(ctx: GenericContext, xpub: str, title: str) -> None:
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user