mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-07 23:28:07 +00:00
WIP - implementing QR code
This commit is contained in:
parent
6e9e7265c4
commit
dd79a868a3
@ -373,7 +373,7 @@ pub struct TextRenderer;
|
||||
|
||||
impl LayoutSink for TextRenderer {
|
||||
fn text(&mut self, cursor: Point, layout: &TextLayout, text: &str) {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
text,
|
||||
layout.style.text_font,
|
||||
@ -383,7 +383,7 @@ impl LayoutSink for TextRenderer {
|
||||
}
|
||||
|
||||
fn hyphen(&mut self, cursor: Point, layout: &TextLayout) {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
"-",
|
||||
layout.style.text_font,
|
||||
@ -403,7 +403,7 @@ impl LayoutSink for TextRenderer {
|
||||
layout.style.background_color,
|
||||
);
|
||||
} else {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
"...",
|
||||
layout.style.text_font,
|
||||
|
@ -6,6 +6,8 @@ pub mod loader;
|
||||
#[cfg(feature = "jpeg")]
|
||||
pub mod tjpgd;
|
||||
|
||||
use heapless::String;
|
||||
|
||||
use super::{
|
||||
constant,
|
||||
geometry::{Offset, Point, Rect},
|
||||
@ -948,11 +950,62 @@ pub fn paint_point(point: &Point, color: Color) {
|
||||
display::bar(point.x, point.y, 1, 1, color.into());
|
||||
}
|
||||
|
||||
/// Display QR code
|
||||
pub fn qrcode(center: Point, data: &str, max_size: u32, case_sensitive: bool) -> Result<(), Error> {
|
||||
qr::render_qrcode(center.x, center.y, data, max_size, case_sensitive)
|
||||
}
|
||||
|
||||
pub fn text(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
|
||||
/// Draws longer multiline texts inside an area.
|
||||
/// Does not add any characters on the line boundaries.
|
||||
///
|
||||
/// If it fits, returns the rest of the area.
|
||||
/// If it does not fit, returns `None`.
|
||||
pub fn text_multiline(
|
||||
area: Rect,
|
||||
text: &str,
|
||||
font: Font,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
) -> Option<Rect> {
|
||||
let line_height = font.line_height();
|
||||
let characters_overall = text.chars().count();
|
||||
let mut taken_from_top = 0;
|
||||
let mut characters_drawn = 0;
|
||||
'lines: loop {
|
||||
let baseline = area.top_left() + Offset::y(line_height + taken_from_top);
|
||||
if !area.contains(baseline) {
|
||||
// The whole area was consumed.
|
||||
return None;
|
||||
}
|
||||
let mut line_text: String<50> = String::new();
|
||||
'characters: loop {
|
||||
if let Some(character) = text.chars().nth(characters_drawn) {
|
||||
unwrap!(line_text.push(character));
|
||||
characters_drawn += 1;
|
||||
} else {
|
||||
// No more characters to draw.
|
||||
break 'characters;
|
||||
}
|
||||
if font.text_width(&line_text) > area.width() {
|
||||
// Cannot fit on the line anymore.
|
||||
line_text.pop();
|
||||
characters_drawn -= 1;
|
||||
break 'characters;
|
||||
}
|
||||
}
|
||||
text_left(baseline, &line_text, font, fg_color, bg_color);
|
||||
if characters_drawn == characters_overall {
|
||||
// No more lines to draw.
|
||||
break 'lines;
|
||||
}
|
||||
taken_from_top += line_height;
|
||||
}
|
||||
// Some of the area was unused and is free to draw some further text.
|
||||
Some(area.split_top(taken_from_top).1)
|
||||
}
|
||||
|
||||
/// Display text left-alligned to a certain Point
|
||||
pub fn text_left(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
|
||||
display::text(
|
||||
baseline.x,
|
||||
baseline.y,
|
||||
@ -963,6 +1016,7 @@ pub fn text(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color:
|
||||
);
|
||||
}
|
||||
|
||||
/// Display text centered around a certain Point
|
||||
pub fn text_center(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
|
||||
let w = font.text_width(text);
|
||||
display::text(
|
||||
@ -975,6 +1029,7 @@ pub fn text_center(baseline: Point, text: &str, font: Font, fg_color: Color, bg_
|
||||
);
|
||||
}
|
||||
|
||||
/// Display text right-alligned to a certain Point
|
||||
pub fn text_right(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
|
||||
let w = font.text_width(text);
|
||||
display::text(
|
||||
|
@ -137,7 +137,7 @@ where
|
||||
display::rect_fill(self.area, background_color)
|
||||
}
|
||||
|
||||
display::text(
|
||||
display::text_left(
|
||||
self.baseline,
|
||||
text.as_ref(),
|
||||
style.font,
|
||||
|
@ -52,7 +52,7 @@ where
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
display::text(
|
||||
display::text_left(
|
||||
self.area.bottom_left() - Offset::y(2),
|
||||
self.title.as_ref(),
|
||||
Font::BOLD,
|
||||
|
@ -249,7 +249,7 @@ where
|
||||
|
||||
match &self.content {
|
||||
ButtonContent::Text(text) => {
|
||||
display::text(
|
||||
display::text_left(
|
||||
self.get_text_baseline(&style),
|
||||
text.as_ref(),
|
||||
style.font,
|
||||
|
@ -9,12 +9,12 @@ use super::theme;
|
||||
|
||||
/// Display white text on black background
|
||||
pub fn display<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
|
||||
display::text(baseline, text.as_ref(), font, theme::FG, theme::BG);
|
||||
display::text_left(baseline, text.as_ref(), font, theme::FG, theme::BG);
|
||||
}
|
||||
|
||||
/// Display black text on white background
|
||||
pub fn display_inverse<T: AsRef<str>>(baseline: Point, text: &T, font: Font) {
|
||||
display::text(baseline, text.as_ref(), font, theme::BG, theme::FG);
|
||||
display::text_left(baseline, text.as_ref(), font, theme::BG, theme::FG);
|
||||
}
|
||||
|
||||
/// Display white text on black background,
|
||||
|
@ -500,7 +500,7 @@ impl LayoutSink for TextRenderer {
|
||||
|
||||
match layout.style.line_alignment {
|
||||
LineAlignment::Left => {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
text,
|
||||
layout.style.text_font,
|
||||
@ -532,7 +532,7 @@ impl LayoutSink for TextRenderer {
|
||||
}
|
||||
|
||||
fn hyphen(&mut self, cursor: Point, layout: &TextLayout) {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
"-",
|
||||
layout.style.text_font,
|
||||
@ -552,7 +552,7 @@ impl LayoutSink for TextRenderer {
|
||||
layout.style.background_color,
|
||||
);
|
||||
} else {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
"...",
|
||||
layout.style.text_font,
|
||||
|
@ -15,6 +15,7 @@ mod loader;
|
||||
mod page;
|
||||
mod passphrase;
|
||||
mod pin;
|
||||
mod qr_code;
|
||||
mod result_anim;
|
||||
mod result_popup;
|
||||
mod scrollbar;
|
||||
@ -43,6 +44,7 @@ pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
|
||||
pub use page::ButtonPage;
|
||||
pub use passphrase::{PassphraseEntry, PassphraseEntryMsg};
|
||||
pub use pin::{PinEntry, PinEntryMsg};
|
||||
pub use qr_code::{QRCodePage, QRCodePageMessage};
|
||||
pub use result_anim::{ResultAnim, ResultAnimMsg};
|
||||
pub use result_popup::{ResultPopup, ResultPopupMsg};
|
||||
pub use scrollbar::ScrollBar;
|
||||
|
113
core/embed/rust/src/ui/model_tr/component/qr_code.rs
Normal file
113
core/embed/rust/src/ui/model_tr/component/qr_code.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx},
|
||||
display::{self, Font},
|
||||
geometry::{Rect},
|
||||
};
|
||||
|
||||
use super::{theme, ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos};
|
||||
|
||||
pub enum QRCodePageMessage {
|
||||
Confirmed,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
pub struct QRCodePage<F, T> {
|
||||
title: T,
|
||||
title_area: Rect,
|
||||
qr_code: F,
|
||||
buttons: Child<ButtonController<T>>,
|
||||
}
|
||||
|
||||
impl<F, T> QRCodePage<F, T>
|
||||
where
|
||||
T: AsRef<str> + Clone,
|
||||
{
|
||||
pub fn new(title: T, qr_code: F, btn_layout: ButtonLayout<T>) -> Self {
|
||||
Self {
|
||||
title,
|
||||
title_area: Rect::zero(),
|
||||
qr_code,
|
||||
buttons: Child::new(ButtonController::new(btn_layout)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> Component for QRCodePage<F, T>
|
||||
where
|
||||
T: AsRef<str> + Clone,
|
||||
F: Component,
|
||||
{
|
||||
type Msg = QRCodePageMessage;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let (content_area, button_area) = bounds.split_bottom(theme::BUTTON_HEIGHT);
|
||||
let (qr_code_area, title_area) = content_area.split_left(theme::QR_SIDE_MAX);
|
||||
self.title_area = title_area;
|
||||
self.qr_code.place(qr_code_area);
|
||||
self.buttons.place(button_area);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
let button_event = self.buttons.event(ctx, event);
|
||||
|
||||
if let Some(ButtonControllerMsg::Triggered(pos)) = button_event {
|
||||
match pos {
|
||||
ButtonPos::Left => {
|
||||
return Some(QRCodePageMessage::Cancelled);
|
||||
}
|
||||
ButtonPos::Right => {
|
||||
return Some(QRCodePageMessage::Confirmed);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.qr_code.paint();
|
||||
// TODO: add the Label from Suite
|
||||
display::text_multiline(
|
||||
self.title_area,
|
||||
self.title.as_ref(),
|
||||
Font::MONO,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
);
|
||||
self.buttons.paint();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use super::ButtonAction;
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use heapless::String;
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F, T> crate::trace::Trace for QRCodePage<F, T>
|
||||
where
|
||||
T: AsRef<str> + Clone,
|
||||
{
|
||||
fn get_btn_action(&self, pos: ButtonPos) -> String<25> {
|
||||
match pos {
|
||||
ButtonPos::Left => ButtonAction::Cancel.string(),
|
||||
ButtonPos::Right => ButtonAction::Confirm.string(),
|
||||
ButtonPos::Middle => ButtonAction::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.open("QRCodePage");
|
||||
t.kw_pair("active_page", "0");
|
||||
t.kw_pair("page_count", "1");
|
||||
self.report_btn_actions(t);
|
||||
t.content_flag();
|
||||
t.string("QR CODE");
|
||||
t.string(self.title.as_ref());
|
||||
t.content_flag();
|
||||
t.field("buttons", &self.buttons);
|
||||
t.close();
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ use crate::{
|
||||
component::{
|
||||
base::{Component, ComponentExt},
|
||||
paginated::{PageMsg, Paginate},
|
||||
painter,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecLong, Paragraphs, VecExt},
|
||||
FormattedText,
|
||||
},
|
||||
@ -35,7 +36,7 @@ use super::{
|
||||
component::{
|
||||
Bip39Entry, Bip39EntryMsg, ButtonActions, ButtonDetails, ButtonLayout, ButtonPage, Flow,
|
||||
FlowMsg, FlowPages, Frame, Page, PassphraseEntry, PassphraseEntryMsg, PinEntry,
|
||||
PinEntryMsg, ShareWords, SimpleChoice, SimpleChoiceMsg,
|
||||
PinEntryMsg, QRCodePage, QRCodePageMessage, ShareWords, SimpleChoice, SimpleChoiceMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
@ -68,6 +69,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> ComponentMsgObj for QRCodePage<F, T>
|
||||
where
|
||||
T: AsRef<str> + Clone,
|
||||
F: Component,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
QRCodePageMessage::Confirmed => Ok(CONFIRMED.as_obj()),
|
||||
QRCodePageMessage::Cancelled => Ok(CANCELLED.as_obj()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentMsgObj for PinEntry {
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
@ -132,22 +146,26 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
// TODO: could be replaced by Flow with one element after it supports pagination
|
||||
|
||||
let format = match (&action, &description, reverse) {
|
||||
(Some(_), Some(_), false) => "{Font::bold}{action}\n\r{Font::normal}{description}",
|
||||
(Some(_), Some(_), true) => "{Font::normal}{description}\n\r{Font::bold}{action}",
|
||||
(Some(_), None, _) => "{Font::bold}{action}",
|
||||
(None, Some(_), _) => "{Font::normal}{description}",
|
||||
(Some(_), Some(_), false) => "{bold}{action}\n\r{mono}{description}",
|
||||
(Some(_), Some(_), true) => "{mono}{description}\n\r{bold}{action}",
|
||||
(Some(_), None, _) => "{bold}{action}",
|
||||
(None, Some(_), _) => "{mono}{description}",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let verb_cancel = verb_cancel.unwrap_or_default();
|
||||
let verb = verb.unwrap_or_default();
|
||||
|
||||
let cancel_btn = if verb_cancel.len() > 0 {
|
||||
Some(ButtonDetails::cancel_icon())
|
||||
// Left button - icon, text or nothing.
|
||||
let cancel_btn = if let Some(verb_cancel) = verb_cancel {
|
||||
if verb_cancel.len() > 0 {
|
||||
Some(ButtonDetails::text(verb_cancel))
|
||||
} else {
|
||||
Some(ButtonDetails::cancel_icon())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Right button - text or nothing.
|
||||
let verb = verb.unwrap_or_default();
|
||||
let mut confirm_btn = if verb.len() > 0 {
|
||||
Some(ButtonDetails::text(verb))
|
||||
} else {
|
||||
@ -350,6 +368,29 @@ extern "C" fn confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn show_qr(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 address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?;
|
||||
let verb_cancel: StrBuffer = kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into()?;
|
||||
let case_sensitive: bool = kwargs.get(Qstr::MP_QSTR_case_sensitive)?.try_into()?;
|
||||
|
||||
let verb: StrBuffer = "CONFIRM".into();
|
||||
|
||||
let qr_code = painter::qrcode_painter(address, theme::QR_SIDE_MAX as u32, case_sensitive);
|
||||
|
||||
let btn_layout = ButtonLayout::new(
|
||||
Some(ButtonDetails::text(verb_cancel)),
|
||||
None,
|
||||
Some(ButtonDetails::text(verb)),
|
||||
);
|
||||
|
||||
let obj = LayoutObj::new(QRCodePage::new(title, qr_code, btn_layout))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
/// General pattern of most tutorial screens.
|
||||
/// (title, text, btn_layout, btn_actions)
|
||||
fn tutorial_screen(
|
||||
@ -611,6 +652,16 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm summary of a transaction. Specific for model R."""
|
||||
Qstr::MP_QSTR_confirm_total_r => obj_fn_kw!(0, confirm_total).as_obj(),
|
||||
|
||||
/// def show_qr(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// address: str,
|
||||
/// verb_cancel: str,
|
||||
/// case_sensitive: bool,
|
||||
/// ) -> object:
|
||||
/// """Show QR code."""
|
||||
Qstr::MP_QSTR_show_qr => obj_fn_kw!(0, show_qr).as_obj(),
|
||||
|
||||
/// def tutorial() -> object:
|
||||
/// """Show user how to interact with the device."""
|
||||
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, tutorial).as_obj(),
|
||||
|
@ -80,3 +80,6 @@ pub const ICON_WARNING: IconAndName =
|
||||
pub const BUTTON_CONTENT_HEIGHT: i16 = 7;
|
||||
pub const BUTTON_OUTLINE: i16 = 3;
|
||||
pub const BUTTON_HEIGHT: i16 = BUTTON_CONTENT_HEIGHT + 2 * BUTTON_OUTLINE;
|
||||
|
||||
// Full-size QR code.
|
||||
pub const QR_SIDE_MAX: i16 = 64 - BUTTON_HEIGHT;
|
||||
|
@ -189,7 +189,7 @@ impl<T> Button<T> {
|
||||
let start_of_baseline = self.area.center()
|
||||
+ Offset::new(-width / 2, height / 2)
|
||||
+ Offset::y(Self::BASELINE_OFFSET);
|
||||
display::text(
|
||||
display::text_left(
|
||||
start_of_baseline,
|
||||
text,
|
||||
style.font,
|
||||
|
@ -109,7 +109,7 @@ impl Component for Bip39Input {
|
||||
// Content starts in the left-center point, offset by 16px to the right and 8px
|
||||
// to the bottom.
|
||||
let text_baseline = area.top_left().center(area.bottom_left()) + Offset::new(16, 8);
|
||||
display::text(
|
||||
display::text_left(
|
||||
text_baseline,
|
||||
text,
|
||||
style.font,
|
||||
@ -120,7 +120,7 @@ impl Component for Bip39Input {
|
||||
// Paint the rest of the suggested dictionary word.
|
||||
if let Some(word) = self.suggested_word.and_then(|w| w.get(text.len()..)) {
|
||||
let word_baseline = text_baseline + Offset::new(width, 0);
|
||||
display::text(
|
||||
display::text_left(
|
||||
word_baseline,
|
||||
word,
|
||||
style.font,
|
||||
|
@ -327,7 +327,7 @@ impl Component for Input {
|
||||
let ellipsis_width = style.text_font.text_width(ellipsis);
|
||||
|
||||
// Drawing the ellipsis and moving the baseline for the rest of the text.
|
||||
display::text(
|
||||
display::text_left(
|
||||
text_baseline,
|
||||
ellipsis,
|
||||
style.text_font,
|
||||
@ -346,7 +346,7 @@ impl Component for Input {
|
||||
&text[text.len() - chars_from_right..]
|
||||
};
|
||||
|
||||
display::text(
|
||||
display::text_left(
|
||||
text_baseline,
|
||||
text_to_display,
|
||||
style.text_font,
|
||||
|
@ -158,7 +158,7 @@ impl Component for Slip39Input {
|
||||
.assert_if_debugging_ui("Text buffer is too small");
|
||||
}
|
||||
}
|
||||
display::text(
|
||||
display::text_left(
|
||||
text_baseline,
|
||||
text.as_str(),
|
||||
style.font,
|
||||
|
@ -425,7 +425,7 @@ async def get_bool(
|
||||
data: str,
|
||||
description: str | None = None,
|
||||
verb: str | None = "CONFIRM",
|
||||
verb_cancel: str | None = "CANCEL",
|
||||
verb_cancel: str | None = "",
|
||||
hold: bool = False,
|
||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
||||
) -> bool:
|
||||
@ -585,16 +585,27 @@ async def confirm_path_warning(
|
||||
)
|
||||
|
||||
|
||||
def _show_xpub(xpub: str, title: str, cancel: str) -> ui.Layout:
|
||||
content = RustLayout(
|
||||
trezorui2.confirm_text(
|
||||
title=title.upper(),
|
||||
data=xpub,
|
||||
# verb_cancel=cancel,
|
||||
)
|
||||
)
|
||||
return content
|
||||
|
||||
|
||||
async def show_xpub(
|
||||
ctx: wire.GenericContext, xpub: str, title: str, cancel: str
|
||||
) -> None:
|
||||
return await _placeholder_confirm(
|
||||
ctx=ctx,
|
||||
br_type="show_xpub",
|
||||
title=title.upper(),
|
||||
data=xpub,
|
||||
description="",
|
||||
br_code=ButtonRequestType.PublicKey,
|
||||
await raise_if_cancelled(
|
||||
interact(
|
||||
ctx,
|
||||
_show_xpub(xpub, title, cancel),
|
||||
"show_xpub",
|
||||
ButtonRequestType.PublicKey,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@ -611,20 +622,62 @@ async def show_address(
|
||||
address_extra: str | None = None,
|
||||
title_qr: str | None = None,
|
||||
) -> None:
|
||||
text = ""
|
||||
is_multisig = len(xpubs) > 0
|
||||
# TODO: replace with confirm_blob
|
||||
data = address
|
||||
if network:
|
||||
text += f"{network} network\n"
|
||||
data += f"\n\n{network}"
|
||||
if address_extra:
|
||||
text += f"{address_extra}\n"
|
||||
text += address
|
||||
return await _placeholder_confirm(
|
||||
ctx=ctx,
|
||||
br_type="show_address",
|
||||
title=title.upper(),
|
||||
data=text,
|
||||
description="",
|
||||
br_code=ButtonRequestType.Address,
|
||||
)
|
||||
data += f"\n\n{address_extra}"
|
||||
while True:
|
||||
result = await interact(
|
||||
ctx,
|
||||
RustLayout(
|
||||
trezorui2.confirm_action(
|
||||
title=title.upper(),
|
||||
action=data,
|
||||
description=None,
|
||||
verb="CONFIRM",
|
||||
verb_cancel="QR",
|
||||
reverse=False,
|
||||
hold=False,
|
||||
)
|
||||
),
|
||||
"show_address",
|
||||
ButtonRequestType.Address,
|
||||
)
|
||||
if result is trezorui2.CONFIRMED:
|
||||
break
|
||||
|
||||
result = await interact(
|
||||
ctx,
|
||||
RustLayout(
|
||||
trezorui2.show_qr(
|
||||
address=address if address_qr is None else address_qr,
|
||||
case_sensitive=case_sensitive,
|
||||
title=title.upper() if title_qr is None else title_qr.upper(),
|
||||
verb_cancel="XPUBs" if is_multisig else "ADDRESS",
|
||||
)
|
||||
),
|
||||
"show_qr",
|
||||
ButtonRequestType.Address,
|
||||
)
|
||||
if result is trezorui2.CONFIRMED:
|
||||
break
|
||||
|
||||
if is_multisig:
|
||||
for i, xpub in enumerate(xpubs):
|
||||
cancel = "NEXT" if i < len(xpubs) - 1 else "ADDRESS"
|
||||
title_xpub = f"XPUB #{i + 1}"
|
||||
title_xpub += " (yours)" if i == multisig_index else " (cosigner)"
|
||||
result = await interact(
|
||||
ctx,
|
||||
_show_xpub(xpub, title=title_xpub, cancel=cancel),
|
||||
"show_xpub",
|
||||
ButtonRequestType.PublicKey,
|
||||
)
|
||||
if result is trezorui2.CONFIRMED:
|
||||
return
|
||||
|
||||
|
||||
def show_pubkey(
|
||||
|
Loading…
Reference in New Issue
Block a user