mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-29 18:08:19 +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::{
|
layout::{
|
||||||
obj::{ComponentMsgObj, LayoutObj},
|
obj::{ComponentMsgObj, LayoutObj},
|
||||||
result::{CANCELLED, CONFIRMED, INFO},
|
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},
|
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 {
|
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
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()?;
|
||||||
@ -215,40 +260,36 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
paragraphs.into_paragraphs()
|
paragraphs.into_paragraphs()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Left button - icon, text or nothing.
|
paragraphs_in_button_page(title, paragraphs, verb, verb_cancel, hold)
|
||||||
let cancel_btn = if let Some(verb_cancel) = verb_cancel {
|
};
|
||||||
if !verb_cancel.is_empty() {
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
Some(ButtonDetails::text(verb_cancel))
|
}
|
||||||
} else {
|
|
||||||
Some(ButtonDetails::cancel_icon())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Right button - text or nothing.
|
extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
// Optional HoldToConfirm
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let mut confirm_btn = if !verb.is_empty() {
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
Some(ButtonDetails::text(verb))
|
let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?;
|
||||||
} else {
|
let description: Option<StrBuffer> =
|
||||||
None
|
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||||
};
|
let extra: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
||||||
if hold {
|
let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?;
|
||||||
// TODO: clients might want to set the duration
|
let verb_cancel: Option<StrBuffer> = kwargs
|
||||||
confirm_btn = confirm_btn.map(|btn| btn.with_default_duration());
|
.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)
|
paragraphs_in_button_page(title, paragraphs, verb, verb_cancel, hold)
|
||||||
.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())
|
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
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 mut iter_buf = IterBuf::new();
|
||||||
let iter = Iter::try_from_obj_with_buf(items, &mut iter_buf)?;
|
let iter = Iter::try_from_obj_with_buf(items, &mut iter_buf)?;
|
||||||
for para in iter {
|
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 key = key.try_into_option::<StrBuffer>()?;
|
||||||
let value = value.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 let Some(key) = key {
|
||||||
if value.is_some() {
|
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 {
|
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);
|
paragraphs_in_button_page(
|
||||||
if hold {
|
title,
|
||||||
let confirm_btn = Some(ButtonDetails::text("CONFIRM".into()).with_default_duration());
|
paragraphs.into_paragraphs(),
|
||||||
content = content.with_confirm_btn(confirm_btn);
|
"CONFIRM".into(),
|
||||||
}
|
None,
|
||||||
let obj = LayoutObj::new(ScrollableFrame::new(content).with_title(title))?;
|
hold,
|
||||||
Ok(obj.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) }
|
||||||
}
|
}
|
||||||
@ -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_layout = ButtonLayout::cancel_armed_text("CONFIRM".into(), "i".into());
|
||||||
let btn_actions = ButtonActions::last_confirm_next();
|
let btn_actions = ButtonActions::last_confirm_next();
|
||||||
Page::<15>::new(btn_layout, btn_actions, Font::BOLD)
|
Page::<15>::new(btn_layout, btn_actions, Font::BOLD)
|
||||||
|
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
|
||||||
.text_bold(title)
|
.text_bold(title)
|
||||||
.newline()
|
.newline()
|
||||||
.newline_half()
|
.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())?;
|
kwargs.get_or(Qstr::MP_QSTR_description, StrBuffer::empty())?;
|
||||||
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
|
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
|
||||||
|
|
||||||
let content = Paragraphs::new([
|
let content = Frame::new(
|
||||||
Paragraph::new(&theme::TEXT_BOLD, title),
|
title,
|
||||||
Paragraph::new(&theme::TEXT_MONO, description),
|
Paragraphs::new([Paragraph::new(&theme::TEXT_MONO, description)]),
|
||||||
]);
|
);
|
||||||
let obj = if time_ms == 0 {
|
let obj = if time_ms == 0 {
|
||||||
// No timer, used when we only want to draw the dialog once and
|
// No timer, used when we only want to draw the dialog once and
|
||||||
// then throw away the layout object.
|
// then throw away the layout object.
|
||||||
@ -922,6 +970,18 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Confirm action."""
|
/// """Confirm action."""
|
||||||
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),
|
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(
|
/// def confirm_properties(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str,
|
/// title: str,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::text::TextStyle,
|
component::{text::TextStyle, LineBreaking},
|
||||||
display::{Color, Font, IconAndName},
|
display::{Color, Font, IconAndName},
|
||||||
geometry::Offset,
|
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_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_DEMIBOLD: TextStyle = TextStyle::new(Font::DEMIBOLD, FG, BG, FG, FG);
|
||||||
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, 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 =
|
pub const TEXT_MONO: TextStyle =
|
||||||
TextStyle::new(Font::MONO, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0);
|
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);
|
pub const TEXT_HEADER: TextStyle = TextStyle::new(Font::BOLD, FG, BG, FG, FG);
|
||||||
|
|
||||||
// Icons with their names for debugging purposes
|
// Icons with their names for debugging purposes
|
||||||
|
@ -606,28 +606,33 @@ async def confirm_backup(ctx: GenericContext) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
async def confirm_path_warning(
|
async def confirm_path_warning(
|
||||||
ctx: GenericContext, path: str, path_type: str = "Path"
|
ctx: GenericContext,
|
||||||
|
path: str,
|
||||||
|
path_type: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
if path_type:
|
||||||
|
title = f"Unknown {path_type}"
|
||||||
|
else:
|
||||||
|
title = "Unknown path"
|
||||||
return await _placeholder_confirm(
|
return await _placeholder_confirm(
|
||||||
ctx,
|
ctx,
|
||||||
"path_warning",
|
"path_warning",
|
||||||
"CONFIRM PATH",
|
title.upper(),
|
||||||
f"{path_type}\n{path} is unknown.\nAre you sure?",
|
description=path,
|
||||||
br_code=ButtonRequestType.UnknownDerivationPath,
|
br_code=ButtonRequestType.UnknownDerivationPath,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
||||||
content = RustLayout(
|
return RustLayout(
|
||||||
trezorui2.confirm_action(
|
trezorui2.confirm_blob(
|
||||||
title=title.upper(),
|
title=title.upper(),
|
||||||
action="",
|
data=xpub,
|
||||||
description=xpub,
|
|
||||||
verb="CONFIRM",
|
|
||||||
verb_cancel=cancel,
|
verb_cancel=cancel,
|
||||||
|
description=None,
|
||||||
|
extra=None,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
async def show_xpub(ctx: GenericContext, xpub: str, title: str) -> None:
|
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,
|
br_code: ButtonRequestType = BR_TYPE_OTHER,
|
||||||
ask_pagination: bool = False,
|
ask_pagination: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
await confirm_action(
|
title = title.upper()
|
||||||
ctx,
|
description = description or ""
|
||||||
br_type,
|
layout = RustLayout(
|
||||||
title.upper(),
|
trezorui2.confirm_blob(
|
||||||
description,
|
title=title,
|
||||||
str(data),
|
description=description,
|
||||||
hold=hold,
|
data=data,
|
||||||
br_code=br_code,
|
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):
|
if isinstance(prop[1], bytes):
|
||||||
return (prop[0], hexlify(prop[1]).decode(), True)
|
return (prop[0], hexlify(prop[1]).decode(), True)
|
||||||
else:
|
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(
|
await raise_if_cancelled(
|
||||||
interact(
|
interact(
|
||||||
@ -1208,12 +1227,13 @@ async def show_popup(
|
|||||||
description_param: str = "",
|
description_param: str = "",
|
||||||
timeout_ms: int = 3000,
|
timeout_ms: int = 3000,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
description = description.format(description_param)
|
||||||
if subtitle:
|
if subtitle:
|
||||||
title += f"\n{subtitle}"
|
description = f"{subtitle}\n{description}"
|
||||||
await RustLayout(
|
await RustLayout(
|
||||||
trezorui2.show_info(
|
trezorui2.show_info(
|
||||||
title=title,
|
title=title,
|
||||||
description=description.format(description_param),
|
description=description,
|
||||||
time_ms=timeout_ms,
|
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:
|
def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
||||||
content = RustLayout(
|
return RustLayout(
|
||||||
trezorui2.confirm_blob(
|
trezorui2.confirm_blob(
|
||||||
title=title,
|
title=title,
|
||||||
data=xpub,
|
data=xpub,
|
||||||
@ -340,7 +340,6 @@ def _show_xpub(xpub: str, title: str, cancel: str | None) -> ui.Layout:
|
|||||||
description=None,
|
description=None,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
async def show_xpub(ctx: GenericContext, xpub: str, title: str) -> None:
|
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