1
0
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:
grdddj 2023-01-11 12:31:32 +01:00
parent fe90b0a5cd
commit 5398849275
5 changed files with 854 additions and 772 deletions

View File

@ -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,

View File

@ -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

View File

@ -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,
)
)

View File

@ -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