mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-21 05:48:23 +00:00
fix(core/ui): T3T1: remove ButtonPage, Dialog, IconDialog
[no changelog]
This commit is contained in:
parent
e268f79749
commit
4d6af487f4
@ -15,7 +15,7 @@ pub struct SwipePage<T> {
|
||||
current: usize,
|
||||
}
|
||||
|
||||
impl<T: Component + Paginate + Clone> SwipePage<T> {
|
||||
impl<T: Component + Paginate> SwipePage<T> {
|
||||
pub fn vertical(inner: T) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
@ -37,7 +37,7 @@ impl<T: Component + Paginate + Clone> SwipePage<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component + Paginate + Clone> Component for SwipePage<T> {
|
||||
impl<T: Component + Paginate> Component for SwipePage<T> {
|
||||
type Msg = T::Msg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
|
@ -1,230 +0,0 @@
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{
|
||||
image::BlendedImage,
|
||||
text::{
|
||||
paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
|
||||
TextStyle,
|
||||
},
|
||||
Child, Component, Event, EventCtx, Never,
|
||||
},
|
||||
geometry::{Insets, LinearPlacement, Rect},
|
||||
shape::Renderer,
|
||||
},
|
||||
};
|
||||
|
||||
use super::theme;
|
||||
|
||||
pub enum DialogMsg<T, U> {
|
||||
Content(T),
|
||||
Controls(U),
|
||||
}
|
||||
|
||||
pub struct Dialog<T, U> {
|
||||
content: Child<T>,
|
||||
controls: Child<U>,
|
||||
}
|
||||
|
||||
impl<T, U> Dialog<T, U>
|
||||
where
|
||||
T: Component,
|
||||
U: Component,
|
||||
{
|
||||
pub fn new(content: T, controls: U) -> Self {
|
||||
Self {
|
||||
content: Child::new(content),
|
||||
controls: Child::new(controls),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &T {
|
||||
self.content.inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Component for Dialog<T, U>
|
||||
where
|
||||
T: Component,
|
||||
U: Component,
|
||||
{
|
||||
type Msg = DialogMsg<T::Msg, U::Msg>;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let controls_area = self.controls.place(bounds);
|
||||
let content_area = bounds
|
||||
.inset(Insets::bottom(controls_area.height()))
|
||||
.inset(Insets::bottom(theme::BUTTON_SPACING))
|
||||
.inset(Insets::left(theme::CONTENT_BORDER));
|
||||
self.content.place(content_area);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
self.content
|
||||
.event(ctx, event)
|
||||
.map(Self::Msg::Content)
|
||||
.or_else(|| self.controls.event(ctx, event).map(Self::Msg::Controls))
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.content.paint();
|
||||
self.controls.paint();
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
self.content.render(target);
|
||||
self.controls.render(target);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
self.content.bounds(sink);
|
||||
self.controls.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for Dialog<T, U>
|
||||
where
|
||||
T: crate::trace::Trace,
|
||||
U: crate::trace::Trace,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Dialog");
|
||||
t.child("content", &self.content);
|
||||
t.child("controls", &self.controls);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IconDialog<U> {
|
||||
image: Child<BlendedImage>,
|
||||
paragraphs: Paragraphs<ParagraphVecShort<'static>>,
|
||||
controls: Child<U>,
|
||||
}
|
||||
|
||||
impl<U> IconDialog<U>
|
||||
where
|
||||
U: Component,
|
||||
{
|
||||
pub fn new(icon: BlendedImage, title: impl Into<TString<'static>>, controls: U) -> Self {
|
||||
Self {
|
||||
image: Child::new(icon),
|
||||
paragraphs: Paragraphs::new(ParagraphVecShort::from_iter([Paragraph::new(
|
||||
&theme::TEXT_DEMIBOLD,
|
||||
title,
|
||||
)
|
||||
.centered()]))
|
||||
.with_placement(
|
||||
LinearPlacement::vertical()
|
||||
.align_at_center()
|
||||
.with_spacing(Self::VALUE_SPACE),
|
||||
),
|
||||
controls: Child::new(controls),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_paragraph(mut self, para: Paragraph<'static>) -> Self {
|
||||
if !para.content().is_empty() {
|
||||
self.paragraphs.inner_mut().add(para);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_text(self, style: &'static TextStyle, text: impl Into<TString<'static>>) -> Self {
|
||||
self.with_paragraph(Paragraph::new(style, text).centered())
|
||||
}
|
||||
|
||||
pub fn with_description(self, description: impl Into<TString<'static>>) -> Self {
|
||||
self.with_text(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, description)
|
||||
}
|
||||
|
||||
pub fn with_value(self, value: impl Into<TString<'static>>) -> Self {
|
||||
self.with_text(&theme::TEXT_MONO, value)
|
||||
}
|
||||
|
||||
pub fn new_shares(lines: [impl Into<TString<'static>>; 4], controls: U) -> Self {
|
||||
let [l0, l1, l2, l3] = lines;
|
||||
Self {
|
||||
image: Child::new(BlendedImage::new(
|
||||
theme::IMAGE_BG_CIRCLE,
|
||||
theme::IMAGE_FG_SUCCESS,
|
||||
theme::SUCCESS_COLOR,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
)),
|
||||
paragraphs: ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, l0).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, l1).centered(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, l2).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, l3).centered(),
|
||||
])
|
||||
.into_paragraphs()
|
||||
.with_placement(LinearPlacement::vertical().align_at_center()),
|
||||
controls: Child::new(controls),
|
||||
}
|
||||
}
|
||||
|
||||
pub const ICON_AREA_PADDING: i16 = 2;
|
||||
pub const ICON_AREA_HEIGHT: i16 = 60;
|
||||
pub const VALUE_SPACE: i16 = 5;
|
||||
}
|
||||
|
||||
impl<U> Component for IconDialog<U>
|
||||
where
|
||||
U: Component,
|
||||
{
|
||||
type Msg = DialogMsg<Never, U::Msg>;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let bounds = bounds
|
||||
.inset(theme::borders())
|
||||
.inset(Insets::top(Self::ICON_AREA_PADDING));
|
||||
|
||||
let controls_area = self.controls.place(bounds);
|
||||
let content_area = bounds.inset(Insets::bottom(controls_area.height()));
|
||||
|
||||
let (image_area, content_area) = content_area.split_top(Self::ICON_AREA_HEIGHT);
|
||||
|
||||
self.image.place(image_area);
|
||||
self.paragraphs.place(content_area);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
self.paragraphs.event(ctx, event);
|
||||
self.controls.event(ctx, event).map(Self::Msg::Controls)
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.image.paint();
|
||||
self.paragraphs.paint();
|
||||
self.controls.paint();
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
self.image.render(target);
|
||||
self.paragraphs.render(target);
|
||||
self.controls.render(target);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
self.image.bounds(sink);
|
||||
self.paragraphs.bounds(sink);
|
||||
self.controls.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<U> crate::trace::Trace for IconDialog<U>
|
||||
where
|
||||
U: crate::trace::Trace,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("IconDialog");
|
||||
t.child("image", &self.image);
|
||||
t.child("content", &self.paragraphs);
|
||||
t.child("controls", &self.controls);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ pub mod bl_confirm;
|
||||
mod button;
|
||||
#[cfg(feature = "translations")]
|
||||
mod coinjoin_progress;
|
||||
mod dialog;
|
||||
mod fido;
|
||||
mod footer;
|
||||
mod vertical_menu;
|
||||
@ -12,7 +11,6 @@ mod vertical_menu;
|
||||
mod fido_icons;
|
||||
mod error;
|
||||
mod frame;
|
||||
|
||||
#[cfg(feature = "translations")]
|
||||
mod hold_to_confirm;
|
||||
#[cfg(feature = "translations")]
|
||||
@ -23,8 +21,6 @@ mod loader;
|
||||
#[cfg(feature = "translations")]
|
||||
mod number_input;
|
||||
pub mod number_input_slider;
|
||||
#[cfg(feature = "translations")]
|
||||
mod page;
|
||||
mod progress;
|
||||
#[cfg(feature = "translations")]
|
||||
mod prompt_screen;
|
||||
@ -34,7 +30,6 @@ mod scroll;
|
||||
mod set_brightness;
|
||||
#[cfg(feature = "translations")]
|
||||
mod share_words;
|
||||
mod simple_page;
|
||||
mod status_screen;
|
||||
mod swipe_content;
|
||||
#[cfg(feature = "translations")]
|
||||
@ -51,7 +46,6 @@ pub use button::{
|
||||
};
|
||||
#[cfg(feature = "translations")]
|
||||
pub use coinjoin_progress::CoinJoinProgress;
|
||||
pub use dialog::{Dialog, DialogMsg, IconDialog};
|
||||
pub use error::ErrorScreen;
|
||||
pub use fido::{FidoConfirm, FidoMsg};
|
||||
pub use footer::Footer;
|
||||
@ -74,8 +68,6 @@ pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
|
||||
pub use number_input::{NumberInputDialog, NumberInputDialogMsg};
|
||||
#[cfg(feature = "translations")]
|
||||
pub use number_input_slider::NumberInputSliderDialog;
|
||||
#[cfg(feature = "translations")]
|
||||
pub use page::ButtonPage;
|
||||
pub use progress::Progress;
|
||||
#[cfg(feature = "translations")]
|
||||
pub use prompt_screen::PromptScreen;
|
||||
@ -85,7 +77,6 @@ pub use scroll::ScrollBar;
|
||||
pub use set_brightness::SetBrightnessDialog;
|
||||
#[cfg(feature = "translations")]
|
||||
pub use share_words::ShareWords;
|
||||
pub use simple_page::SimplePage;
|
||||
pub use status_screen::StatusScreen;
|
||||
pub use swipe_content::SwipeContent;
|
||||
#[cfg(feature = "translations")]
|
||||
|
@ -1,852 +0,0 @@
|
||||
use crate::{
|
||||
error::Error,
|
||||
strutil::TString,
|
||||
time::Instant,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
paginated::PageMsg, Component, ComponentExt, Event, EventCtx, Pad, Paginate, Swipe,
|
||||
SwipeDirection,
|
||||
},
|
||||
constant,
|
||||
display::{self, Color},
|
||||
geometry::{Insets, Rect},
|
||||
shape::Renderer,
|
||||
util::animation_disabled,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
theme, Button, ButtonContent, ButtonMsg, ButtonStyleSheet, Loader, LoaderMsg, ScrollBar,
|
||||
};
|
||||
|
||||
use core::cell::Cell;
|
||||
|
||||
/// Allows pagination of inner component. Shows scroll bar, confirm & cancel
|
||||
/// buttons. Optionally handles hold-to-confirm with loader.
|
||||
pub struct ButtonPage<T> {
|
||||
/// Inner component.
|
||||
content: T,
|
||||
/// Cleared when page changes.
|
||||
pad: Pad,
|
||||
/// Swipe controller.
|
||||
swipe: Swipe,
|
||||
scrollbar: ScrollBar,
|
||||
/// Hold-to-confirm mode whenever this is `Some(loader)`.
|
||||
loader: Option<Loader>,
|
||||
button_cancel: Option<Button>,
|
||||
button_confirm: Button,
|
||||
button_prev: Button,
|
||||
button_next: Button,
|
||||
/// Show cancel button instead of back button.
|
||||
cancel_from_any_page: bool,
|
||||
/// Whether to pass-through left swipe to parent component.
|
||||
swipe_left: bool,
|
||||
/// Whether to pass-through right swipe to parent component.
|
||||
swipe_right: bool,
|
||||
/// Fade to given backlight level on next paint().
|
||||
fade: Cell<Option<u16>>,
|
||||
}
|
||||
|
||||
impl<T> ButtonPage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
{
|
||||
pub fn with_hold(mut self) -> Result<Self, Error> {
|
||||
self.button_confirm =
|
||||
Button::with_text(TR::buttons__hold_to_confirm.into()).styled(theme::button_confirm());
|
||||
self.loader = Some(Loader::new());
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ButtonPage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
{
|
||||
pub fn new(content: T, background: Color) -> Self {
|
||||
Self {
|
||||
content,
|
||||
pad: Pad::with_background(background),
|
||||
swipe: Swipe::new(),
|
||||
scrollbar: ScrollBar::vertical(),
|
||||
loader: None,
|
||||
button_cancel: Some(Button::with_icon(theme::ICON_CANCEL)),
|
||||
button_confirm: Button::with_icon(theme::ICON_CONFIRM).styled(theme::button_confirm()),
|
||||
button_prev: Button::with_icon(theme::ICON_UP).initially_enabled(false),
|
||||
button_next: Button::with_icon(theme::ICON_DOWN),
|
||||
cancel_from_any_page: false,
|
||||
swipe_left: false,
|
||||
swipe_right: false,
|
||||
fade: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn without_cancel(mut self) -> Self {
|
||||
self.button_cancel = None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cancel_confirm(
|
||||
mut self,
|
||||
left: Option<TString<'static>>,
|
||||
right: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
let cancel = match left {
|
||||
Some(verb) => verb.map(|s| match s {
|
||||
"^" => Button::with_icon(theme::ICON_UP),
|
||||
"<" => Button::with_icon(theme::ICON_BACK),
|
||||
_ => Button::with_text(verb),
|
||||
}),
|
||||
_ => Button::with_icon(theme::ICON_CANCEL),
|
||||
};
|
||||
let confirm = match right {
|
||||
Some(verb) => Button::with_text(verb).styled(theme::button_confirm()),
|
||||
_ => Button::with_icon(theme::ICON_CONFIRM).styled(theme::button_confirm()),
|
||||
};
|
||||
self.button_cancel = Some(cancel);
|
||||
self.button_confirm = confirm;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_back_button(mut self) -> Self {
|
||||
self.cancel_from_any_page = true;
|
||||
self.button_prev = Button::with_icon(theme::ICON_BACK).initially_enabled(false);
|
||||
self.button_cancel = Some(Button::with_icon(theme::ICON_BACK));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cancel_arrow(mut self) -> Self {
|
||||
self.button_cancel = Some(Button::with_icon(theme::ICON_UP));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_confirm_style(mut self, style: ButtonStyleSheet) -> Self {
|
||||
self.button_confirm = self.button_confirm.styled(style);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_swipe_left(mut self) -> Self {
|
||||
self.swipe_left = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_swipe_right(mut self) -> Self {
|
||||
self.swipe_right = true;
|
||||
self
|
||||
}
|
||||
|
||||
fn setup_swipe(&mut self) {
|
||||
self.swipe.allow_up = self.scrollbar.has_next_page();
|
||||
self.swipe.allow_down = self.scrollbar.has_previous_page();
|
||||
self.swipe.allow_left = self.swipe_left;
|
||||
self.swipe.allow_right = self.swipe_right;
|
||||
}
|
||||
|
||||
fn change_page(&mut self, ctx: &mut EventCtx, step: isize) {
|
||||
// Advance scrollbar.
|
||||
self.scrollbar.go_to_relative(step);
|
||||
|
||||
// Adjust the swipe parameters according to the scrollbar.
|
||||
self.setup_swipe();
|
||||
|
||||
// Enable/disable prev button.
|
||||
self.button_prev
|
||||
.enable_if(ctx, self.scrollbar.has_previous_page());
|
||||
|
||||
// Change the page in the content, make sure it gets completely repainted and
|
||||
// clear the background under it.
|
||||
self.content.change_page(self.scrollbar.active_page);
|
||||
self.content.request_complete_repaint(ctx);
|
||||
self.pad.clear();
|
||||
|
||||
// Swipe has dimmed the screen, so fade back to normal backlight after the next
|
||||
// paint.
|
||||
self.fade
|
||||
.set(Some(theme::backlight::get_backlight_normal()));
|
||||
}
|
||||
|
||||
fn is_cancel_visible(&self) -> bool {
|
||||
self.cancel_from_any_page || !self.scrollbar.has_previous_page()
|
||||
}
|
||||
|
||||
/// Area for drawing loader (and black rectangle behind it). Can be outside
|
||||
/// bounds as we repaint entire UI tree after hiding the loader.
|
||||
const fn loader_area() -> Rect {
|
||||
constant::screen()
|
||||
.inset(theme::borders())
|
||||
.inset(Insets::bottom(theme::BUTTON_HEIGHT + theme::BUTTON_SPACING))
|
||||
}
|
||||
|
||||
fn handle_swipe(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
event: Event,
|
||||
) -> HandleResult<<Self as Component>::Msg> {
|
||||
if let Some(swipe) = self.swipe.event(ctx, event) {
|
||||
match swipe {
|
||||
SwipeDirection::Up => {
|
||||
// Scroll down, if possible.
|
||||
return HandleResult::NextPage;
|
||||
}
|
||||
SwipeDirection::Down => {
|
||||
// Scroll up, if possible.
|
||||
return HandleResult::PrevPage;
|
||||
}
|
||||
SwipeDirection::Left if self.swipe_left => {
|
||||
return HandleResult::Return(PageMsg::SwipeLeft);
|
||||
}
|
||||
SwipeDirection::Right if self.swipe_right => {
|
||||
return HandleResult::Return(PageMsg::SwipeRight);
|
||||
}
|
||||
_ => {
|
||||
// Ignore other directions.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandleResult::Continue
|
||||
}
|
||||
|
||||
fn handle_button(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
event: Event,
|
||||
) -> HandleResult<(Option<<Self as Component>::Msg>, Option<ButtonMsg>)> {
|
||||
if self.scrollbar.has_next_page() {
|
||||
if let Some(ButtonMsg::Clicked) = self.button_next.event(ctx, event) {
|
||||
return HandleResult::NextPage;
|
||||
}
|
||||
} else {
|
||||
let result = self.button_confirm.event(ctx, event);
|
||||
match result {
|
||||
Some(ButtonMsg::Clicked) => {
|
||||
return HandleResult::Return((Some(PageMsg::Confirmed), result))
|
||||
}
|
||||
Some(_) => return HandleResult::Return((None, result)),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
if self.is_cancel_visible() {
|
||||
if let Some(ButtonMsg::Clicked) = self.button_cancel.event(ctx, event) {
|
||||
return HandleResult::Return((Some(PageMsg::Cancelled), None));
|
||||
}
|
||||
} else if let Some(ButtonMsg::Clicked) = self.button_prev.event(ctx, event) {
|
||||
return HandleResult::PrevPage;
|
||||
}
|
||||
|
||||
HandleResult::Continue
|
||||
}
|
||||
|
||||
fn handle_hold(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
event: Event,
|
||||
button_msg: &Option<ButtonMsg>,
|
||||
) -> HandleResult<<Self as Component>::Msg> {
|
||||
let Some(loader) = &mut self.loader else {
|
||||
return HandleResult::Continue;
|
||||
};
|
||||
let now = Instant::now();
|
||||
|
||||
if let Some(LoaderMsg::ShrunkCompletely) = loader.event(ctx, event) {
|
||||
// Switch it to the initial state, so we stop painting it.
|
||||
loader.reset();
|
||||
// Re-draw the whole content tree.
|
||||
self.content.request_complete_repaint(ctx);
|
||||
// Loader overpainted our bounds, repaint entire screen from scratch.
|
||||
ctx.request_repaint_root()
|
||||
// This can be a result of an animation frame event, we should take
|
||||
// care to not short-circuit here and deliver the event to the
|
||||
// content as well.
|
||||
}
|
||||
match button_msg {
|
||||
Some(ButtonMsg::Pressed) => {
|
||||
loader.start_growing(ctx, now);
|
||||
loader.pad.clear(); // Clear the remnants of the content.
|
||||
}
|
||||
Some(ButtonMsg::Released) => {
|
||||
loader.start_shrinking(ctx, now);
|
||||
}
|
||||
Some(ButtonMsg::Clicked) => {
|
||||
if loader.is_completely_grown(now) || animation_disabled() {
|
||||
return HandleResult::Return(PageMsg::Confirmed);
|
||||
} else {
|
||||
loader.start_shrinking(ctx, now);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
HandleResult::Continue
|
||||
}
|
||||
}
|
||||
|
||||
enum HandleResult<T> {
|
||||
Return(T),
|
||||
PrevPage,
|
||||
NextPage,
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl<T> Component for ButtonPage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
{
|
||||
type Msg = PageMsg<T::Msg>;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let small_left_button = match (&self.button_cancel, &self.button_confirm) {
|
||||
(None, _) => true,
|
||||
(Some(cancel), confirm) => match (cancel.content(), confirm.content()) {
|
||||
(ButtonContent::Text(t), _) => t.len() <= 4,
|
||||
(ButtonContent::Icon(_), ButtonContent::Icon(_)) => false,
|
||||
_ => true,
|
||||
},
|
||||
};
|
||||
let layout = PageLayout::new(bounds, small_left_button);
|
||||
self.pad.place(bounds);
|
||||
self.swipe.place(bounds);
|
||||
self.button_cancel.place(layout.button_left);
|
||||
self.button_confirm.place(layout.button_right);
|
||||
self.button_prev.place(layout.button_left);
|
||||
self.button_next.place(layout.button_right);
|
||||
self.scrollbar.place(layout.scrollbar);
|
||||
|
||||
// Layout the content. Try to fit it on a single page first, and reduce the area
|
||||
// to make space for a scrollbar if it doesn't fit.
|
||||
self.content.place(layout.content_single_page);
|
||||
let page_count = {
|
||||
let count = self.content.page_count();
|
||||
if count > 1 {
|
||||
self.content.place(layout.content);
|
||||
self.content.page_count() // Make sure to re-count it with the
|
||||
// new size.
|
||||
} else {
|
||||
count // Content fits on a single page.
|
||||
}
|
||||
};
|
||||
|
||||
if page_count == 1 && self.button_cancel.is_none() {
|
||||
self.button_confirm.place(layout.button_both);
|
||||
}
|
||||
|
||||
// Now that we finally have the page count, we can setup the scrollbar and the
|
||||
// swiper.
|
||||
self.scrollbar.set_count_and_active_page(page_count, 0);
|
||||
self.setup_swipe();
|
||||
|
||||
self.loader.place(Self::loader_area());
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
ctx.set_page_count(self.scrollbar.page_count);
|
||||
|
||||
match self.handle_swipe(ctx, event) {
|
||||
HandleResult::Return(r) => return Some(r),
|
||||
HandleResult::PrevPage => {
|
||||
self.change_page(ctx, -1);
|
||||
return None;
|
||||
}
|
||||
HandleResult::NextPage => {
|
||||
self.change_page(ctx, 1);
|
||||
return None;
|
||||
}
|
||||
HandleResult::Continue => {}
|
||||
}
|
||||
|
||||
if let Some(msg) = self.content.event(ctx, event) {
|
||||
return Some(PageMsg::Content(msg));
|
||||
}
|
||||
|
||||
let mut confirm_button_msg = None;
|
||||
let mut button_result = None;
|
||||
|
||||
match self.handle_button(ctx, event) {
|
||||
HandleResult::Return((Some(r), None)) => return Some(r),
|
||||
HandleResult::Return((r, m)) => {
|
||||
button_result = r;
|
||||
confirm_button_msg = m;
|
||||
}
|
||||
HandleResult::PrevPage => {
|
||||
self.change_page(ctx, -1);
|
||||
return None;
|
||||
}
|
||||
HandleResult::NextPage => {
|
||||
self.change_page(ctx, 1);
|
||||
return None;
|
||||
}
|
||||
HandleResult::Continue => {}
|
||||
}
|
||||
|
||||
if self.loader.is_some() {
|
||||
return match self.handle_hold(ctx, event, &confirm_button_msg) {
|
||||
HandleResult::Return(r) => Some(r),
|
||||
HandleResult::Continue => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
button_result
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.pad.paint();
|
||||
match &self.loader {
|
||||
Some(l) if l.is_animating() => self.loader.paint(),
|
||||
_ => {
|
||||
self.content.paint();
|
||||
if self.scrollbar.has_pages() {
|
||||
self.scrollbar.paint();
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.button_cancel.is_some() && self.is_cancel_visible() {
|
||||
self.button_cancel.paint();
|
||||
} else {
|
||||
self.button_prev.paint();
|
||||
}
|
||||
if self.scrollbar.has_next_page() {
|
||||
self.button_next.paint();
|
||||
} else {
|
||||
self.button_confirm.paint();
|
||||
}
|
||||
if let Some(val) = self.fade.take() {
|
||||
// Note that this is blocking and takes some time.
|
||||
display::fade_backlight(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
self.pad.render(target);
|
||||
match &self.loader {
|
||||
Some(l) if l.is_animating() => self.loader.render(target),
|
||||
_ => {
|
||||
self.content.render(target);
|
||||
if self.scrollbar.has_pages() {
|
||||
self.scrollbar.render(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.button_cancel.is_some() && self.is_cancel_visible() {
|
||||
self.button_cancel.render(target);
|
||||
} else {
|
||||
self.button_prev.render(target);
|
||||
}
|
||||
if self.scrollbar.has_next_page() {
|
||||
self.button_next.render(target);
|
||||
} else {
|
||||
self.button_confirm.render(target);
|
||||
}
|
||||
if let Some(val) = self.fade.take() {
|
||||
// Note that this is blocking and takes some time.
|
||||
display::fade_backlight(val);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
sink(self.pad.area);
|
||||
self.scrollbar.bounds(sink);
|
||||
self.content.bounds(sink);
|
||||
self.button_cancel.bounds(sink);
|
||||
self.button_confirm.bounds(sink);
|
||||
self.button_prev.bounds(sink);
|
||||
self.button_next.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for ButtonPage<T>
|
||||
where
|
||||
T: crate::trace::Trace,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("ButtonPage");
|
||||
t.int("active_page", self.scrollbar.active_page as i64);
|
||||
t.int("page_count", self.scrollbar.page_count as i64);
|
||||
t.bool("hold", self.loader.is_some());
|
||||
t.child("content", &self.content);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PageLayout {
|
||||
/// Content when it fits on single page (no scrollbar).
|
||||
pub content_single_page: Rect,
|
||||
/// Content when multiple pages.
|
||||
pub content: Rect,
|
||||
/// Scroll bar when multiple pages.
|
||||
pub scrollbar: Rect,
|
||||
/// Controls displayed on last page.
|
||||
pub button_left: Rect,
|
||||
pub button_right: Rect,
|
||||
pub button_both: Rect,
|
||||
}
|
||||
|
||||
impl PageLayout {
|
||||
const SCROLLBAR_WIDTH: i16 = 8;
|
||||
const SCROLLBAR_SPACE: i16 = 5;
|
||||
|
||||
pub fn new(area: Rect, small_left_button: bool) -> Self {
|
||||
let (area, button_both) = area.split_bottom(theme::BUTTON_HEIGHT);
|
||||
let area = area.inset(Insets::bottom(theme::BUTTON_SPACING));
|
||||
let (_space, content) = area.split_left(theme::CONTENT_BORDER);
|
||||
let (content_single_page, _space) = content.split_right(theme::CONTENT_BORDER);
|
||||
let (content, scrollbar) =
|
||||
content.split_right(Self::SCROLLBAR_SPACE + Self::SCROLLBAR_WIDTH);
|
||||
let (_space, scrollbar) = scrollbar.split_left(Self::SCROLLBAR_SPACE);
|
||||
|
||||
let width = if small_left_button {
|
||||
theme::BUTTON_WIDTH
|
||||
} else {
|
||||
(button_both.width() - theme::BUTTON_SPACING) / 2
|
||||
};
|
||||
let (button_left, button_right) = button_both.split_left(width);
|
||||
let button_right = button_right.inset(Insets::left(theme::BUTTON_SPACING));
|
||||
|
||||
Self {
|
||||
content_single_page,
|
||||
content,
|
||||
scrollbar,
|
||||
button_left,
|
||||
button_right,
|
||||
button_both,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
|
||||
use crate::{
|
||||
trace::tests::trace,
|
||||
ui::{
|
||||
component::text::paragraphs::{Paragraph, Paragraphs},
|
||||
event::TouchEvent,
|
||||
geometry::Point,
|
||||
model_mercury::{constant, theme},
|
||||
},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
const SCREEN: Rect = constant::screen().inset(theme::borders());
|
||||
|
||||
fn swipe(component: &mut impl Component, points: &[(i16, i16)]) {
|
||||
let last = points.len().saturating_sub(1);
|
||||
let mut first = true;
|
||||
let mut ctx = EventCtx::new();
|
||||
for (i, &(x, y)) in points.iter().enumerate() {
|
||||
let p = Point::new(x, y);
|
||||
let ev = if first {
|
||||
TouchEvent::TouchStart(p)
|
||||
} else if i == last {
|
||||
TouchEvent::TouchEnd(p)
|
||||
} else {
|
||||
TouchEvent::TouchMove(p)
|
||||
};
|
||||
component.event(&mut ctx, Event::Touch(ev));
|
||||
ctx.clear();
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn swipe_up(component: &mut impl Component) {
|
||||
swipe(component, &[(20, 100), (20, 60), (20, 20)])
|
||||
}
|
||||
|
||||
fn swipe_down(component: &mut impl Component) {
|
||||
swipe(component, &[(20, 20), (20, 60), (20, 100)])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraphs_empty() {
|
||||
let mut page = ButtonPage::new(Paragraphs::<[Paragraph<'static>; 0]>::new([]), theme::BG);
|
||||
page.place(SCREEN);
|
||||
|
||||
let expected = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 0,
|
||||
"page_count": 1,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
|
||||
assert_eq!(trace(&page), expected);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), expected);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraphs_single() {
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new([
|
||||
Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
"This is the first paragraph and it should fit on the screen entirely.",
|
||||
),
|
||||
Paragraph::new(
|
||||
&theme::TEXT_BOLD,
|
||||
"Second, bold, paragraph should also fit on the screen.",
|
||||
),
|
||||
]),
|
||||
theme::BG,
|
||||
);
|
||||
page.place(SCREEN);
|
||||
|
||||
let expected = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 0,
|
||||
"page_count": 1,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
["This is the first", "\n", "paragraph and it should", "\n", "fit on the screen", "\n", "entirely."],
|
||||
["Second, bold,", "\n", "paragraph should also", "\n", "fit on the screen."],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
|
||||
assert_eq!(trace(&page), expected);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), expected);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraphs_one_long() {
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new(
|
||||
Paragraph::new(
|
||||
&theme::TEXT_BOLD,
|
||||
"This is somewhat long paragraph that goes on and on and on and on and on and will definitely not fit on just a single screen. You have to swipe a bit to see all the text it contains I guess. There's just so much letters in it.",
|
||||
)
|
||||
),
|
||||
theme::BG,
|
||||
);
|
||||
page.place(SCREEN);
|
||||
|
||||
let first_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 0,
|
||||
"page_count": 2,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"This is somewhat long", "\n",
|
||||
"paragraph that goes", "\n",
|
||||
"on and on and on and", "\n",
|
||||
"on and on and will", "\n",
|
||||
"definitely not fit on", "\n",
|
||||
"just a single screen.", "\n",
|
||||
"You have to swipe a", "...",
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
let second_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 1,
|
||||
"page_count": 2,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"bit to see all the text it", "\n",
|
||||
"contains I guess.", "\n",
|
||||
"There's just so much", "\n",
|
||||
"letters in it."
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
|
||||
assert_eq!(trace(&page), first_page);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), first_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), second_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), second_page);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), first_page);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraphs_three_long() {
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new([
|
||||
Paragraph::new(
|
||||
&theme::TEXT_BOLD,
|
||||
"This paragraph is using a bold font. It doesn't need to be all that long.",
|
||||
),
|
||||
Paragraph::new(
|
||||
&theme::TEXT_MONO,
|
||||
"And this one is using MONO. Monospace is nice for numbers, they have the same width and can be scanned quickly. Even if they span several pages or something.",
|
||||
),
|
||||
Paragraph::new(
|
||||
&theme::TEXT_BOLD,
|
||||
"Let's add another one for a good measure. This one should overflow all the way to the third page with a bit of luck.",
|
||||
),
|
||||
]),
|
||||
theme::BG,
|
||||
);
|
||||
page.place(SCREEN);
|
||||
|
||||
let first_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 0,
|
||||
"page_count": 3,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"This paragraph is", "\n",
|
||||
"using a bold font. It", "\n",
|
||||
"doesn't need to be all", "\n",
|
||||
"that long.",
|
||||
],
|
||||
[
|
||||
"And this one is u", "\n",
|
||||
"sing MONO. Monosp", "\n",
|
||||
"ace is nice f", "...",
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
let second_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 1,
|
||||
"page_count": 3,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"...", "or numbers, t", "\n",
|
||||
"hey have the same", "\n",
|
||||
"width and can be", "\n",
|
||||
"scanned quickly.", "\n",
|
||||
"Even if they span", "\n",
|
||||
"several pages or", "\n",
|
||||
"something."
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
let third_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 2,
|
||||
"page_count": 3,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"Let's add another one", "\n",
|
||||
"for a good measure.", "\n",
|
||||
"This one should", "\n",
|
||||
"overflow all the way to", "\n",
|
||||
"the third page with a", "\n",
|
||||
"bit of luck.",
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
|
||||
assert_eq!(trace(&page), first_page);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), first_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), second_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), third_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), third_page);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), second_page);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), first_page);
|
||||
swipe_down(&mut page);
|
||||
assert_eq!(trace(&page), first_page);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraphs_hard_break() {
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_NORMAL, "Short one.").break_after(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL, "Short two.").break_after(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL, "Short three.").break_after(),
|
||||
]),
|
||||
theme::BG,
|
||||
);
|
||||
page.place(SCREEN);
|
||||
|
||||
let first_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 0,
|
||||
"page_count": 3,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"Short one.",
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
let second_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 1,
|
||||
"page_count": 3,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"Short two.",
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
let third_page = serde_json::json!({
|
||||
"component": "ButtonPage",
|
||||
"active_page": 2,
|
||||
"page_count": 3,
|
||||
"content": {
|
||||
"component": "Paragraphs",
|
||||
"paragraphs": [
|
||||
[
|
||||
"Short three.",
|
||||
],
|
||||
],
|
||||
},
|
||||
"hold": false,
|
||||
});
|
||||
|
||||
assert_eq!(trace(&page), first_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), second_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), third_page);
|
||||
swipe_up(&mut page);
|
||||
assert_eq!(trace(&page), third_page);
|
||||
}
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
use crate::ui::{
|
||||
component::{
|
||||
base::ComponentExt, Component, Event, EventCtx, Pad, PageMsg, Paginate, Swipe,
|
||||
SwipeDirection,
|
||||
},
|
||||
display::{self, Color},
|
||||
geometry::{Axis, Insets, Rect},
|
||||
shape::Renderer,
|
||||
};
|
||||
|
||||
use super::{theme, ScrollBar};
|
||||
use core::cell::Cell;
|
||||
|
||||
const SCROLLBAR_HEIGHT: i16 = 18;
|
||||
const SCROLLBAR_BORDER: i16 = 4;
|
||||
|
||||
pub struct SimplePage<T> {
|
||||
content: T,
|
||||
pad: Pad,
|
||||
swipe: Swipe,
|
||||
scrollbar: ScrollBar,
|
||||
axis: Axis,
|
||||
swipe_right_to_go_back: bool,
|
||||
fade: Cell<Option<u16>>,
|
||||
}
|
||||
|
||||
impl<T> SimplePage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
{
|
||||
pub fn new(content: T, axis: Axis, background: Color) -> Self {
|
||||
Self {
|
||||
content,
|
||||
swipe: Swipe::new(),
|
||||
pad: Pad::with_background(background),
|
||||
scrollbar: ScrollBar::new(axis),
|
||||
axis,
|
||||
swipe_right_to_go_back: false,
|
||||
fade: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn horizontal(content: T, background: Color) -> Self {
|
||||
Self::new(content, Axis::Horizontal, background)
|
||||
}
|
||||
|
||||
pub fn vertical(content: T, background: Color) -> Self {
|
||||
Self::new(content, Axis::Vertical, background)
|
||||
}
|
||||
|
||||
pub fn with_swipe_right_to_go_back(mut self) -> Self {
|
||||
self.swipe_right_to_go_back = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &T {
|
||||
&self.content
|
||||
}
|
||||
|
||||
fn setup_swipe(&mut self) {
|
||||
if self.is_horizontal() {
|
||||
self.swipe.allow_left = self.scrollbar.has_next_page();
|
||||
self.swipe.allow_right =
|
||||
self.scrollbar.has_previous_page() || self.swipe_right_to_go_back;
|
||||
} else {
|
||||
self.swipe.allow_up = self.scrollbar.has_next_page();
|
||||
self.swipe.allow_down = self.scrollbar.has_previous_page();
|
||||
self.swipe.allow_right = self.swipe_right_to_go_back;
|
||||
}
|
||||
}
|
||||
|
||||
fn change_page(&mut self, ctx: &mut EventCtx, step: isize) {
|
||||
// Advance scrollbar.
|
||||
self.scrollbar.go_to_relative(step);
|
||||
// Adjust the swipe parameters according to the scrollbar.
|
||||
self.setup_swipe();
|
||||
|
||||
// Change the page in the content, make sure it gets completely repainted and
|
||||
// clear the background under it.
|
||||
self.content.change_page(self.scrollbar.active_page);
|
||||
self.content.request_complete_repaint(ctx);
|
||||
self.pad.clear();
|
||||
|
||||
// Swipe has dimmed the screen, so fade back to normal backlight after the next
|
||||
// paint.
|
||||
self.fade
|
||||
.set(Some(theme::backlight::get_backlight_normal()));
|
||||
}
|
||||
|
||||
fn is_horizontal(&self) -> bool {
|
||||
matches!(self.axis, Axis::Horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for SimplePage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
{
|
||||
type Msg = PageMsg<T::Msg>;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.swipe.place(bounds);
|
||||
|
||||
let (content, scrollbar) = if self.is_horizontal() {
|
||||
bounds.split_bottom(SCROLLBAR_HEIGHT + SCROLLBAR_BORDER)
|
||||
} else {
|
||||
bounds.split_right(SCROLLBAR_HEIGHT + SCROLLBAR_BORDER)
|
||||
};
|
||||
|
||||
self.content.place(bounds);
|
||||
if self.content.page_count() > 1 {
|
||||
self.pad.place(content);
|
||||
self.content.place(content);
|
||||
} else {
|
||||
self.pad.place(bounds);
|
||||
}
|
||||
|
||||
if self.is_horizontal() {
|
||||
self.scrollbar
|
||||
.place(scrollbar.inset(Insets::bottom(SCROLLBAR_BORDER)));
|
||||
} else {
|
||||
self.scrollbar
|
||||
.place(scrollbar.inset(Insets::right(SCROLLBAR_BORDER)));
|
||||
}
|
||||
|
||||
self.scrollbar
|
||||
.set_count_and_active_page(self.content.page_count(), 0);
|
||||
self.setup_swipe();
|
||||
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
ctx.set_page_count(self.scrollbar.page_count);
|
||||
if let Some(swipe) = self.swipe.event(ctx, event) {
|
||||
match (swipe, self.axis) {
|
||||
(SwipeDirection::Left, Axis::Horizontal) | (SwipeDirection::Up, Axis::Vertical) => {
|
||||
self.change_page(ctx, 1);
|
||||
return None;
|
||||
}
|
||||
(SwipeDirection::Right, _)
|
||||
if self.swipe_right_to_go_back && self.scrollbar.active_page == 0 =>
|
||||
{
|
||||
return Some(PageMsg::Cancelled);
|
||||
}
|
||||
(SwipeDirection::Right, Axis::Horizontal)
|
||||
| (SwipeDirection::Down, Axis::Vertical) => {
|
||||
self.change_page(ctx, -1);
|
||||
return None;
|
||||
}
|
||||
_ => {
|
||||
// Ignore other directions.
|
||||
}
|
||||
}
|
||||
}
|
||||
self.content.event(ctx, event).map(PageMsg::Content)
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.pad.paint();
|
||||
self.content.paint();
|
||||
if self.scrollbar.has_pages() {
|
||||
self.scrollbar.paint();
|
||||
}
|
||||
if let Some(val) = self.fade.take() {
|
||||
// Note that this is blocking and takes some time.
|
||||
display::fade_backlight(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
self.pad.render(target);
|
||||
self.content.render(target);
|
||||
if self.scrollbar.has_pages() {
|
||||
self.scrollbar.render(target);
|
||||
}
|
||||
if let Some(val) = self.fade.take() {
|
||||
// Note that this is blocking and takes some time.
|
||||
display::fade_backlight(val);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
sink(self.pad.area);
|
||||
self.scrollbar.bounds(sink);
|
||||
self.content.bounds(sink);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for SimplePage<T>
|
||||
where
|
||||
T: crate::trace::Trace,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("SimplePage");
|
||||
t.int("active_page", self.scrollbar.active_page as i64);
|
||||
t.int("page_count", self.scrollbar.page_count as i64);
|
||||
t.child("content", &self.content);
|
||||
}
|
||||
}
|
@ -236,7 +236,7 @@ fn new_confirm_action_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_confirm_action_simple<T: Component + Paginate + Clone + MaybeTrace + 'static>(
|
||||
pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
content: T,
|
||||
title: TString<'static>,
|
||||
subtitle: Option<TString<'static>>,
|
||||
|
@ -14,9 +14,7 @@ use crate::{
|
||||
component::{
|
||||
base::{AttachType, ComponentExt},
|
||||
connect::Connect,
|
||||
image::BlendedImage,
|
||||
jpeg::Jpeg,
|
||||
paginated::{PageMsg, Paginate},
|
||||
swipe_detect::SwipeSettings,
|
||||
text::{
|
||||
op::OpTextLayout,
|
||||
@ -26,7 +24,7 @@ use crate::{
|
||||
},
|
||||
TextStyle,
|
||||
},
|
||||
Border, Component, Empty, FormattedText, Label, Never, SwipeDirection, Timeout,
|
||||
Border, Component, FormattedText, Label, Never, SwipeDirection, Timeout,
|
||||
},
|
||||
flow::Swipable,
|
||||
geometry,
|
||||
@ -41,13 +39,12 @@ use crate::{
|
||||
|
||||
use super::{
|
||||
component::{
|
||||
AddressDetails, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet,
|
||||
CancelConfirmMsg, CancelInfoConfirmMsg, CoinJoinProgress, Dialog, DialogMsg, FidoConfirm,
|
||||
FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg, IconDialog, Lockscreen, MnemonicInput,
|
||||
MnemonicKeyboard, MnemonicKeyboardMsg, PassphraseKeyboard, PassphraseKeyboardMsg,
|
||||
PinKeyboard, PinKeyboardMsg, Progress, PromptScreen, SelectWordCount, SelectWordCountMsg,
|
||||
SetBrightnessDialog, SimplePage, Slip39Input, StatusScreen, SwipeUpScreen,
|
||||
SwipeUpScreenMsg, VerticalMenu, VerticalMenuChoiceMsg,
|
||||
AddressDetails, Bip39Input, Button, CancelConfirmMsg, CancelInfoConfirmMsg,
|
||||
CoinJoinProgress, FidoConfirm, FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg,
|
||||
Lockscreen, MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, PassphraseKeyboard,
|
||||
PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress, PromptScreen,
|
||||
SelectWordCount, SelectWordCountMsg, SetBrightnessDialog, Slip39Input, StatusScreen,
|
||||
SwipeUpScreen, SwipeUpScreenMsg, VerticalMenu, VerticalMenuChoiceMsg,
|
||||
},
|
||||
flow, theme,
|
||||
};
|
||||
@ -108,33 +105,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for Dialog<T, U>
|
||||
where
|
||||
T: ComponentMsgObj,
|
||||
U: Component,
|
||||
<U as Component>::Msg: TryInto<Obj, Error = Error>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
DialogMsg::Content(c) => Ok(self.inner().msg_try_into_obj(c)?),
|
||||
DialogMsg::Controls(msg) => msg.try_into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> ComponentMsgObj for IconDialog<U>
|
||||
where
|
||||
U: Component,
|
||||
<U as Component>::Msg: TryInto<Obj, Error = Error>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
DialogMsg::Controls(msg) => msg.try_into(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentMsgObj for PinKeyboard<'_> {
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
@ -220,21 +190,6 @@ impl<T: Component + Swipable> ComponentMsgObj for SwipeUpScreen<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for ButtonPage<T>
|
||||
where
|
||||
T: Component + Paginate,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
PageMsg::Content(_) => Err(Error::TypeError),
|
||||
PageMsg::Confirmed => Ok(CONFIRMED.as_obj()),
|
||||
PageMsg::Cancelled => Ok(CANCELLED.as_obj()),
|
||||
PageMsg::SwipeLeft => Ok(INFO.as_obj()),
|
||||
PageMsg::SwipeRight => Ok(CANCELLED.as_obj()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clippy/compiler complains about conflicting implementations
|
||||
// TODO move the common impls to a common module
|
||||
#[cfg(not(feature = "clippy"))]
|
||||
@ -289,19 +244,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for SimplePage<T>
|
||||
where
|
||||
T: ComponentMsgObj + Paginate,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
PageMsg::Content(inner_msg) => Ok(self.inner().msg_try_into_obj(inner_msg)?),
|
||||
PageMsg::Cancelled => Ok(CANCELLED.as_obj()),
|
||||
_ => Err(Error::TypeError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentMsgObj for AddressDetails {
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
Ok(CANCELLED.as_obj())
|
||||
@ -350,12 +292,13 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m
|
||||
}
|
||||
}
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
flow::new_confirm_action_simple(
|
||||
FormattedText::new(ops).vertically_centered(),
|
||||
title,
|
||||
ButtonPage::new(FormattedText::new(ops).vertically_centered(), theme::BG)
|
||||
.with_cancel_confirm(None, verb),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
None,
|
||||
verb,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -423,42 +366,6 @@ impl ConfirmBlobParams {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_layout(self) -> Result<Obj, Error> {
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: self.description.unwrap_or("".into()),
|
||||
extra: self.extra.unwrap_or("".into()),
|
||||
data: self.data.try_into()?,
|
||||
description_font: &theme::TEXT_NORMAL,
|
||||
extra_font: &theme::TEXT_DEMIBOLD,
|
||||
data_font: if self.chunkify {
|
||||
let data: TString = self.data.try_into()?;
|
||||
theme::get_chunkified_text_style(data.len())
|
||||
} else if self.text_mono {
|
||||
&theme::TEXT_MONO
|
||||
} else {
|
||||
&theme::TEXT_NORMAL
|
||||
},
|
||||
}
|
||||
.into_paragraphs();
|
||||
|
||||
let mut page = ButtonPage::new(paragraphs, theme::BG);
|
||||
if let Some(verb) = self.verb {
|
||||
page = page.with_cancel_confirm(self.verb_cancel, Some(verb))
|
||||
}
|
||||
if self.hold {
|
||||
page = page.with_hold()?
|
||||
}
|
||||
let mut frame = Frame::left_aligned(self.title, page);
|
||||
if let Some(subtitle) = self.subtitle {
|
||||
frame = frame.with_subtitle(subtitle);
|
||||
}
|
||||
if self.info_button {
|
||||
frame = frame.with_menu_button();
|
||||
}
|
||||
let obj = LayoutObj::new(frame)?;
|
||||
Ok(obj.into())
|
||||
}
|
||||
|
||||
fn into_flow(self) -> Result<Obj, Error> {
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: self.description.unwrap_or("".into()),
|
||||
@ -521,7 +428,6 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let description: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let verb: TString = kwargs.get_or(Qstr::MP_QSTR_verb, TR::buttons__confirm.into())?;
|
||||
let extra: Option<TString> = kwargs.get(Qstr::MP_QSTR_extra)?.try_into_option()?;
|
||||
let data: Obj = kwargs.get(Qstr::MP_QSTR_data)?;
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
@ -543,16 +449,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
}
|
||||
.into_paragraphs();
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(
|
||||
title,
|
||||
ButtonPage::new(paragraphs, theme::BG)
|
||||
.with_swipe_left()
|
||||
.with_cancel_confirm(None, Some(verb)),
|
||||
)
|
||||
.with_menu_button(),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
flow::new_confirm_action_simple(paragraphs, title, None, None, None)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -560,7 +457,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||
let _hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?; // FIXME
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
|
||||
let paragraphs = PropsList::new(
|
||||
@ -569,14 +466,14 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
|
||||
&theme::TEXT_MONO,
|
||||
&theme::TEXT_MONO,
|
||||
)?;
|
||||
let page = if hold {
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?
|
||||
} else {
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(TR::buttons__confirm.into()))
|
||||
};
|
||||
let obj = LayoutObj::new(Frame::left_aligned(title, page))?;
|
||||
Ok(obj.into())
|
||||
|
||||
flow::new_confirm_action_simple(
|
||||
paragraphs.into_paragraphs(),
|
||||
title,
|
||||
None,
|
||||
Some(TR::buttons__confirm.into()),
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -588,34 +485,41 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
|
||||
|
||||
let jpeg: BinaryData = image.try_into()?;
|
||||
|
||||
if jpeg.is_empty() {
|
||||
let obj = if jpeg.is_empty() {
|
||||
// Incoming data may be empty, meaning we should
|
||||
// display default homescreen message.
|
||||
let buttons = Button::cancel_confirm_text(None, Some(TR::buttons__change.into()));
|
||||
let obj = LayoutObj::new(Frame::centered(
|
||||
title,
|
||||
Dialog::new(
|
||||
Paragraphs::new([Paragraph::new(
|
||||
LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::centered(
|
||||
title,
|
||||
SwipeContent::new(Paragraphs::new([Paragraph::new(
|
||||
&theme::TEXT_DEMIBOLD,
|
||||
TR::homescreen__set_default,
|
||||
)
|
||||
.centered()]),
|
||||
buttons,
|
||||
),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
.centered()])),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_footer(
|
||||
TR::instructions__swipe_up.into(),
|
||||
Some(TR::buttons__change.into()),
|
||||
)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))
|
||||
} else {
|
||||
if !check_homescreen_format(jpeg) {
|
||||
return Err(value_error!("Invalid image."));
|
||||
};
|
||||
|
||||
let buttons = Button::cancel_confirm_text(None, Some(TR::buttons__change.into()));
|
||||
let obj = LayoutObj::new(Frame::centered(
|
||||
title,
|
||||
Dialog::new(Jpeg::new(jpeg, 1), buttons),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
}
|
||||
LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(title, Jpeg::new(jpeg, 1))
|
||||
.with_cancel_button()
|
||||
.with_footer(
|
||||
TR::instructions__swipe_up.into(),
|
||||
Some(TR::buttons__change.into()),
|
||||
)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))
|
||||
};
|
||||
Ok(obj?.into())
|
||||
};
|
||||
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -625,7 +529,7 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs:
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let horizontal: bool = kwargs.get_or(Qstr::MP_QSTR_horizontal, false)?;
|
||||
let _horizontal: bool = kwargs.get_or(Qstr::MP_QSTR_horizontal, false)?; // FIXME
|
||||
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
|
||||
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
@ -645,19 +549,10 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs:
|
||||
}
|
||||
}
|
||||
|
||||
let axis = match horizontal {
|
||||
true => geometry::Axis::Horizontal,
|
||||
_ => geometry::Axis::Vertical,
|
||||
};
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(
|
||||
title,
|
||||
SimplePage::new(paragraphs.into_paragraphs(), axis, theme::BG)
|
||||
.with_swipe_right_to_go_back(),
|
||||
)
|
||||
.with_cancel_button(),
|
||||
)?;
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(title, SwipeContent::new(paragraphs.into_paragraphs()))
|
||||
.with_cancel_button(),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -698,8 +593,6 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
|
||||
let cancel_arrow: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_arrow, false)?;
|
||||
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
|
||||
@ -708,18 +601,14 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break());
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
|
||||
}
|
||||
let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?;
|
||||
if cancel_arrow {
|
||||
page = page.with_cancel_arrow()
|
||||
}
|
||||
if info_button {
|
||||
page = page.with_swipe_left();
|
||||
}
|
||||
let mut frame = Frame::left_aligned(title, page);
|
||||
if info_button {
|
||||
frame = frame.with_menu_button();
|
||||
}
|
||||
let obj = LayoutObj::new(frame)?;
|
||||
|
||||
// FIXME: hold
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(title, SwipeContent::new(paragraphs.into_paragraphs()))
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -798,82 +687,13 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
fn new_show_modal(
|
||||
kwargs: &Map,
|
||||
icon: BlendedImage,
|
||||
button_style: ButtonStyleSheet,
|
||||
) -> Result<Obj, Error> {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
|
||||
let description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
|
||||
let button: TString = kwargs.get_or(Qstr::MP_QSTR_button, TR::buttons__continue.into())?;
|
||||
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
|
||||
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
|
||||
|
||||
let no_buttons = button.is_empty();
|
||||
let obj = if no_buttons && time_ms == 0 {
|
||||
// No buttons and no timer, used when we only want to draw the dialog once and
|
||||
// then throw away the layout object.
|
||||
LayoutObj::new(
|
||||
IconDialog::new(icon, title, Empty)
|
||||
.with_value(value)
|
||||
.with_description(description),
|
||||
)?
|
||||
.into()
|
||||
} else if no_buttons && time_ms > 0 {
|
||||
// Timeout, no buttons.
|
||||
LayoutObj::new(
|
||||
IconDialog::new(
|
||||
icon,
|
||||
title,
|
||||
Timeout::new(time_ms).map(|_| Some(CancelConfirmMsg::Confirmed)),
|
||||
)
|
||||
.with_value(value)
|
||||
.with_description(description),
|
||||
)?
|
||||
.into()
|
||||
} else if allow_cancel {
|
||||
// Two buttons.
|
||||
LayoutObj::new(
|
||||
IconDialog::new(
|
||||
icon,
|
||||
title,
|
||||
Button::cancel_confirm(
|
||||
Button::with_icon(theme::ICON_CANCEL),
|
||||
Button::with_text(button).styled(button_style),
|
||||
false,
|
||||
),
|
||||
)
|
||||
.with_value(value)
|
||||
.with_description(description),
|
||||
)?
|
||||
.into()
|
||||
} else {
|
||||
// Single button.
|
||||
LayoutObj::new(
|
||||
IconDialog::new(
|
||||
icon,
|
||||
title,
|
||||
theme::button_bar(Button::with_text(button).styled(button_style).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
})),
|
||||
)
|
||||
.with_value(value)
|
||||
.with_description(description),
|
||||
)?
|
||||
.into()
|
||||
};
|
||||
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
extern "C" fn new_show_error(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let allow_cancel: bool = kwargs.get(Qstr::MP_QSTR_allow_cancel)?.try_into()?;
|
||||
|
||||
let content = Paragraphs::new([Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description)]);
|
||||
let content = Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description));
|
||||
let frame = if allow_cancel {
|
||||
Frame::left_aligned(title, SwipeContent::new(content))
|
||||
.with_cancel_button()
|
||||
@ -965,7 +785,7 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let content = Paragraphs::new([Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description)]);
|
||||
let content = Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description));
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(title, SwipeContent::new(content))
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
@ -983,33 +803,17 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
let url: TString = TR::addr_mismatch__support_url.into();
|
||||
let button: TString = TR::buttons__quit.into();
|
||||
|
||||
let icon = BlendedImage::new(
|
||||
theme::IMAGE_BG_OCTAGON,
|
||||
theme::IMAGE_FG_WARN,
|
||||
theme::WARN_COLOR,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
);
|
||||
let obj = LayoutObj::new(
|
||||
IconDialog::new(
|
||||
icon,
|
||||
title,
|
||||
Button::cancel_confirm(
|
||||
Button::with_icon(theme::ICON_BACK),
|
||||
Button::with_text(button).styled(theme::button_default()),
|
||||
true,
|
||||
),
|
||||
)
|
||||
.with_paragraph(
|
||||
Paragraph::new(&theme::TEXT_NORMAL, description)
|
||||
.centered()
|
||||
.with_bottom_padding(
|
||||
theme::TEXT_NORMAL.text_font.text_height()
|
||||
- theme::TEXT_DEMIBOLD.text_font.text_height(),
|
||||
),
|
||||
)
|
||||
.with_text(&theme::TEXT_DEMIBOLD, url),
|
||||
)?;
|
||||
let paragraphs = Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_NORMAL, description).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, url).centered(),
|
||||
]);
|
||||
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(title, SwipeContent::new(paragraphs))
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), Some(button))
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))?;
|
||||
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1018,24 +822,14 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
|
||||
extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: Option<TString> = kwargs.get(Qstr::MP_QSTR_title)?.try_into_option()?;
|
||||
let description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
|
||||
|
||||
let obj = if let Some(t) = title {
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
t,
|
||||
Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, description)),
|
||||
))?
|
||||
.into()
|
||||
} else {
|
||||
LayoutObj::new(Border::new(
|
||||
theme::borders(),
|
||||
Paragraphs::new(Paragraph::new(&theme::TEXT_DEMIBOLD, description)),
|
||||
))?
|
||||
.into()
|
||||
};
|
||||
let obj = LayoutObj::new(Border::new(
|
||||
theme::borders(),
|
||||
Paragraphs::new(Paragraph::new(&theme::TEXT_DEMIBOLD, description)),
|
||||
))?;
|
||||
|
||||
Ok(obj)
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -1044,7 +838,6 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||
let info_button: TString = kwargs.get(Qstr::MP_QSTR_info_button)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
@ -1060,7 +853,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
}
|
||||
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(title, paragraphs.into_paragraphs())
|
||||
Frame::left_aligned(title, SwipeContent::new(paragraphs.into_paragraphs()))
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), Some(button))
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
@ -1073,7 +866,6 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
|
||||
let mut paragraphs = ParagraphVecLong::new();
|
||||
@ -1085,12 +877,11 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
paragraphs.add(Paragraph::new(style, text));
|
||||
}
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
title,
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(button))
|
||||
.with_confirm_style(theme::button_default())
|
||||
.with_back_button(),
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(title, SwipeContent::new(paragraphs.into_paragraphs()))
|
||||
.with_cancel_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1109,11 +900,8 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
Paragraph::new(&theme::TEXT_MONO, max_feerate),
|
||||
]);
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
TR::coinjoin__title.into(),
|
||||
ButtonPage::new(paragraphs, theme::BG).with_hold()?,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
// FIXME: hold
|
||||
flow::new_confirm_action_simple(paragraphs, TR::coinjoin__title.into(), None, None, None)
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
@ -1253,7 +1041,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
let recovery_type: u32 = kwargs.get(Qstr::MP_QSTR_recovery_type)?.try_into()?;
|
||||
let _info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
|
||||
|
||||
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]);
|
||||
let paragraphs = Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, description));
|
||||
|
||||
let notification = match recovery_type {
|
||||
RECOVERY_TYPE_DRY_RUN => TR::recovery__title_dry_run.into(),
|
||||
@ -1292,12 +1080,21 @@ extern "C" fn new_show_group_share_success(
|
||||
let lines_iterable: Obj = kwargs.get(Qstr::MP_QSTR_lines)?;
|
||||
let lines: [TString; 4] = util::iter_into_array(lines_iterable)?;
|
||||
|
||||
let obj = LayoutObj::new(IconDialog::new_shares(
|
||||
lines,
|
||||
theme::button_bar(Button::with_text(TR::buttons__continue.into()).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
})),
|
||||
let paragraphs = ParagraphVecShort::from_iter([
|
||||
Paragraph::new(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, lines[0]).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, lines[1]).centered(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL_GREY_EXTRA_LIGHT, lines[2]).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, lines[3]).centered(),
|
||||
])
|
||||
.into_paragraphs()
|
||||
.with_placement(geometry::LinearPlacement::vertical().align_at_center());
|
||||
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned("".into(), SwipeContent::new(paragraphs))
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))?;
|
||||
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1315,12 +1112,13 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs:
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, description).break_after());
|
||||
}
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
TR::recovery__title_remaining_shares.into(),
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(TR::buttons__continue.into()))
|
||||
.with_confirm_style(theme::button_default())
|
||||
.without_cancel(),
|
||||
let obj = LayoutObj::new(SwipeUpScreen::new(
|
||||
Frame::left_aligned(
|
||||
TR::recovery__title_remaining_shares.into(),
|
||||
SwipeContent::new(paragraphs.into_paragraphs()),
|
||||
)
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default()),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -2046,59 +1844,3 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// mock:global
|
||||
Qstr::MP_QSTR_BacklightLevels => BACKLIGHT_LEVELS_OBJ.as_obj(),
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
|
||||
use crate::{
|
||||
trace::tests::trace,
|
||||
ui::{component::text::op::OpTextLayout, geometry::Rect, model_mercury::constant},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
const SCREEN: Rect = constant::screen().inset(theme::borders());
|
||||
|
||||
#[test]
|
||||
fn trace_example_layout() {
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::with_text("Left".into()),
|
||||
Button::with_text("Right".into()),
|
||||
false,
|
||||
);
|
||||
|
||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
|
||||
.text_normal("Testing text layout, with some text, and some more text. And ")
|
||||
.text_bold("parameters!");
|
||||
let formatted = FormattedText::new(ops);
|
||||
let mut layout = Dialog::new(formatted, buttons);
|
||||
layout.place(SCREEN);
|
||||
|
||||
let expected = serde_json::json!({
|
||||
"component": "Dialog",
|
||||
"content": {
|
||||
"component": "FormattedText",
|
||||
"text": ["Testing text layout, with", "\n", "some text, and some", "\n",
|
||||
"more text. And ", "parame", "-", "\n", "ters!"],
|
||||
"fits": true,
|
||||
},
|
||||
"controls": {
|
||||
"component": "FixedHeightBar",
|
||||
"inner": {
|
||||
"component": "Split",
|
||||
"first": {
|
||||
"component": "Button",
|
||||
"text": "Left",
|
||||
},
|
||||
"second": {
|
||||
"component": "Button",
|
||||
"text": "Right",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert_eq!(trace(&layout), expected);
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
@ -99,7 +99,6 @@ include_icon!(
|
||||
ICON_LOCKSCREEN_FILTER,
|
||||
"model_mercury/res/lockscreen_filter.toif"
|
||||
);
|
||||
include_icon!(ICON_CENTRAL_CIRCLE, "model_mercury/res/central_circle.toif");
|
||||
|
||||
// Scrollbar/PIN dots - taken from model T
|
||||
include_icon!(DOT_ACTIVE, "model_mercury/res/scroll-active.toif");
|
||||
@ -127,14 +126,6 @@ include_icon!(
|
||||
// Icon for "next keyboard layout" for special characters
|
||||
include_icon!(ICON_ASTERISK, "model_mercury/res/asterisk16.toif");
|
||||
|
||||
// Large, three-color icons.
|
||||
pub const WARN_COLOR: Color = ORANGE_LIGHT;
|
||||
pub const INFO_COLOR: Color = GREY_LIGHT;
|
||||
pub const SUCCESS_COLOR: Color = GREEN;
|
||||
pub const ERROR_COLOR: Color = ORANGE_DIMMED;
|
||||
include_icon!(IMAGE_FG_SUCCESS, "model_mercury/res/fg-check48.toif");
|
||||
include_icon!(IMAGE_BG_CIRCLE, "model_mercury/res/circle48.toif");
|
||||
|
||||
// Welcome screen.
|
||||
include_icon!(ICON_LOGO, "model_mercury/res/lock_full.toif");
|
||||
|
||||
@ -160,12 +151,6 @@ include_icon!(ICON_LOCK_BIG, "model_tt/res/lock24.toif");
|
||||
include_icon!(ICON_COINJOIN, "model_tt/res/coinjoin16.toif");
|
||||
include_icon!(ICON_MAGIC, "model_tt/res/magic.toif");
|
||||
|
||||
include_icon!(IMAGE_FG_WARN, "model_tt/res/fg-warning48.toif");
|
||||
include_icon!(IMAGE_FG_ERROR, "model_tt/res/fg-error48.toif");
|
||||
include_icon!(IMAGE_FG_INFO, "model_tt/res/fg-info48.toif");
|
||||
include_icon!(IMAGE_FG_USER, "model_tt/res/fg-user48.toif");
|
||||
include_icon!(IMAGE_BG_OCTAGON, "model_tt/res/octagon48.toif");
|
||||
|
||||
// Non-square button backgrounds.
|
||||
include_icon!(IMAGE_BG_BACK_BTN, "model_tt/res/bg-back40.toif");
|
||||
include_icon!(IMAGE_BG_BACK_BTN_TALL, "model_tt/res/bg-back52.toif");
|
||||
|
@ -316,6 +316,7 @@ def test_signmessage(
|
||||
|
||||
@pytest.mark.skip_t1b1
|
||||
@pytest.mark.skip_t2b1
|
||||
@pytest.mark.skip_t3t1(reason="Not yet implemented in new UI")
|
||||
@pytest.mark.parametrize(
|
||||
"coin_name, path, script_type, no_script_type, address, message, signature", VECTORS
|
||||
)
|
||||
|
@ -430,6 +430,7 @@ HEXDATA = "0123456789abcd000023456789abcd010003456789abcd020000456789abcd0300000
|
||||
@pytest.mark.parametrize(
|
||||
"flow", (input_flow_data_skip, input_flow_data_scroll_down, input_flow_data_go_back)
|
||||
)
|
||||
@pytest.mark.skip_t3t1(reason="Not yet implemented in new UI")
|
||||
@pytest.mark.skip_t1b1
|
||||
def test_signtx_data_pagination(client: Client, flow):
|
||||
def _sign_tx_call():
|
||||
|
@ -18514,23 +18514,23 @@
|
||||
"T3T1_en_binance-test_get_address.py::test_binance_get_address_chunkify_details[m-44h-714h-0h-0-0-bn-59d4996f": "16bec6ab85f7ccec2eed3f2d9f273ab561fa4edc2779a8333e96e325e39a87ca",
|
||||
"T3T1_en_binance-test_get_address.py::test_binance_get_address_chunkify_details[m-44h-714h-0h-0-1-bn-c9025900": "ef31054cf45938b5cba8bf2b7d8f5b691f74a436ee7fa417cf678d7517856255",
|
||||
"T3T1_en_binance-test_get_public_key.py::test_binance_get_public_key": "69fcdf948ddc30dcc97f6901b82fe80376c7b0c0428099010490521ca37d788a",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[False-message0-expected_response0]": "01f8fd01fc6dacca738224f7eec1ff8dc79acc7f5c5e172aef6ccd460592c6b2",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[False-message1-expected_response1]": "7adbbc301984f9ea5c78dc9e6dfc1af774869e4d87d81363ad14346b94c12b03",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[False-message0-expected_response0]": "89b890924880b128afeaad4f93de40be28cbbcfeeab46e491b2936d81274752d",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[False-message1-expected_response1]": "0d928a4dc6873a2a79441bb1964491f4f22f683e14ff47bbc68d4204db85754c",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[False-message2-expected_response2]": "0b46d6e740b98bfa899618291cad1bb90efa9384b6755b361b45673cd95f3639",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[True-message0-expected_response0]": "01f8fd01fc6dacca738224f7eec1ff8dc79acc7f5c5e172aef6ccd460592c6b2",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[True-message1-expected_response1]": "7adbbc301984f9ea5c78dc9e6dfc1af774869e4d87d81363ad14346b94c12b03",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[True-message0-expected_response0]": "89b890924880b128afeaad4f93de40be28cbbcfeeab46e491b2936d81274752d",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[True-message1-expected_response1]": "0d928a4dc6873a2a79441bb1964491f4f22f683e14ff47bbc68d4204db85754c",
|
||||
"T3T1_en_binance-test_sign_tx.py::test_binance_sign_message[True-message2-expected_response2]": "0b46d6e740b98bfa899618291cad1bb90efa9384b6755b361b45673cd95f3639",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_cancel_authorization": "5fb0f48e4136f400eea4c1958c9a28b0cd205f0a78a6ed2142e1567703b7e242",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_cancel_authorization": "e08e582ace0266765662772d8676497b2f540166fa784420016c14c35f27789f",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_get_address": "67ef3bfb498805795a5c111437e9e31e9996dc2b2a3d7ed410b820a76879737a",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_get_public_key": "1a34efcfdc19f56e11bd7a5a888644efd3e17b1d122c0fbc1e8cb7933da07870",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "15cdff9ad1bc9e2d1fd11bed1c1ecdea556d2abc277f33b70034a99821cee76b",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx[False]": "0ca4bcbaa1a549cb84429dbc77d607ab2cba4c6ce3174699a675251891e8b7f9",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx[True]": "0ca4bcbaa1a549cb84429dbc77d607ab2cba4c6ce3174699a675251891e8b7f9",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx_large": "7500e2783981dbb877e46acf18016f79675cfc89fbc32bccfcff9b2e32f89599",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "70b9d19990ec21925a41d0a20778698dc7a3648e4de15f0f9a8d5252c4fb8712",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx[False]": "5e68efc63caa84a88f9490f00adaca1c7a97d92ae35911778b3818ace34b6be4",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx[True]": "5e68efc63caa84a88f9490f00adaca1c7a97d92ae35911778b3818ace34b6be4",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx_large": "507bee28bfee7cc86a6f12f812f8f52bed2c78d64390514a4d3392a8c2707ad3",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx_migration": "6c35025a5a714e4b7bb147186b755672b66c29f72bafaabab02da3d901d60e21",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_sign_tx_spend": "8b7dcef01953de33d6a52a40951134b9a2b8daec4066df5ffa2e8b238f4e07a4",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_wrong_account_type": "8a9aa98a0099a826ee682dc6983059d38c3abbe9dfc2caf8937e3b6e99090e6e",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_wrong_coordinator": "8a9aa98a0099a826ee682dc6983059d38c3abbe9dfc2caf8937e3b6e99090e6e",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_wrong_account_type": "2a30f5df79cd7071056cf8111c858f58881225c19513fb1cdb85d92e687eefd1",
|
||||
"T3T1_en_bitcoin-test_authorize_coinjoin.py::test_wrong_coordinator": "2a30f5df79cd7071056cf8111c858f58881225c19513fb1cdb85d92e687eefd1",
|
||||
"T3T1_en_bitcoin-test_bcash.py::test_attack_change_input": "3fe9761bcc0b60cc685d9c0f7e557a3cb2ec18046de63b499670cca7040bdd40",
|
||||
"T3T1_en_bitcoin-test_bcash.py::test_send_bch_change": "3fe9761bcc0b60cc685d9c0f7e557a3cb2ec18046de63b499670cca7040bdd40",
|
||||
"T3T1_en_bitcoin-test_bcash.py::test_send_bch_external_presigned": "2863996beef75967773099e62aaea6d7d5494856dd98e936ef664163b56b5601",
|
||||
@ -18748,11 +18748,11 @@
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-3h-100h-4-255-script_types1]": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-4-255-script_types0]": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-49-0-63-0-255-script_types4]": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-6-255-script_types3]": "728660776f62f628e3bc48034dc19bc92560f7e1eb3b228e3e086e512240bd11",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-script_types2]": "06c3732134ec930d8f1b50c5f4c21ac15106ae5adda319835c6d89d34e7aa09c",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-3h-100h-4-255-script_types1]": "f18f3f737c6610f5a6c1da1f96cad6050aed20b4c9db5745160675eefd08b924",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-4-255-script_types0]": "82206cbfa43366c0562ab93109da5e5ef680b230e3ac3befbe4f2e5623c23118",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-49-0-63-0-255-script_types4]": "bb9daa1fda9dd7b5329d15c43dd587dca5678433f551d96d38e919cc307e613c",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-6-255-script_types3]": "02fbd513872b7b00f65e6373d6c6d5b98b6bcf828a29678aa0031b6802bcf145",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-script_types2]": "a360e6195514444bf22144a7b8790eacaab32535c6c39dbb04345cf617ff5fa6",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-3h-100h-4-255-script_types1]": "b69dbb3b01109c9ed8c7a47a4a595287d04c2c80751dec5dc186eb4708ae8b73",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-4-255-script_types0]": "dcb6bd72011dbcdccf5e833c9a15d228af212430838894c5bd358e385064411a",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signmessage[m-49-0-63-0-255-script_types4]": "5956c3900f013afb69b450ccb1b8c95398b0f29862f0d37f35d240fc3da8f65c",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-6-255-script_types3]": "884923a572f62240cb5472321cff4b7bc66b168d7afe022beb64d2474631a5d7",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-script_types2]": "095604e9b8497113a4845d928ca72798f3adbf3d7a89b68b6de846dfba8298d0",
|
||||
"T3T1_en_bitcoin-test_nonstandard_paths.py::test_signtx[m-3h-100h-4-255-script_types1]": "4127933c0fd4d1708aa666dc912b17fb92576f83519e17f11fdefc7f85b8516b",
|
||||
@ -18772,52 +18772,33 @@
|
||||
"T3T1_en_bitcoin-test_peercoin.py::test_timestamp_included": "2df967ba9a788011f8dc1f73dc8dd818027a10c245ec00d24958150ff9a55484",
|
||||
"T3T1_en_bitcoin-test_peercoin.py::test_timestamp_missing": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_bitcoin-test_peercoin.py::test_timestamp_missing_prevtx": "53f7cffed7f0140f04d33a4d7b0981a4e82eff6e9c6d671203edfb37fed54574",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[NFC message]": "e9f3d5df4d1bd04be1a6f6ddbd761d31699316b1a19c3bfc92ad52a394a90287",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[NFKD message]": "e9f3d5df4d1bd04be1a6f6ddbd761d31699316b1a19c3bfc92ad52a394a90287",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[bcash]": "f4adb0934b1f3df13e2c649936319bda77cc5e9bd91fa167bee225c7af6265cf",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[grs-p2pkh]": "05b3d84d111da58af6482f53d764e6931842c6f93b6e98ec9c0b0e30cc6df616",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[grs-segwit-native]": "6720b09e96f003adafb45042c6f79d456891de8a0d238963870fef7aac61cbb9",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[grs-segwit-p2sh]": "b23e559e7612a09ef187d721b217acbbdd31593c8d32add8a05d25815a6a4d3d",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh long message]": "67f67220227c6945b3d202693ecdfad497e03f4c14428db9a2881e4188aaa3ac",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh0]": "b6c475a7e2cf0149f78a803b15bd0bbc3a40704da9fd89f06802668b2719123e",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh1]": "b6c475a7e2cf0149f78a803b15bd0bbc3a40704da9fd89f06802668b2719123e",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh2]": "a7b84107f82a334ce4233c5d848314a42fcb8517d86ddc5dbbcd9083af66ed65",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native long message]": "6c65e6a24dd82d066574ffe9f6ca739d1afb8a34e1dd0735cd7e82248c115189",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native0]": "799646e7845719d4aad141f7875eb4e924c08743498d69adf91284359d5f8871",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native1]": "799646e7845719d4aad141f7875eb4e924c08743498d69adf91284359d5f8871",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native2]": "374321a97826ef612d94d191ac4d3530842b2026780fd53cba251a7cca64e34d",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh long message]": "3d06e0814afd67a805d00d7b75a438cc5517a61a794c883b3b982c664a2b0299",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh0]": "57313215dcf9c3dac621ade2419b5e101a160a6165551f9aef75b3ecfb382aae",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh1]": "57313215dcf9c3dac621ade2419b5e101a160a6165551f9aef75b3ecfb382aae",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh2]": "3ab152ef1bd8cb7ffd91c26b8b8e22d371d6d04057aa832300af6bc6c762e192",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[t1 firmware path]": "28ff073d1c6b6e1ade0c569e86987578bbc8bb0f2a516d57c011964c2b6d8acc",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[NFC message]": "bab75ef4bcfc8757a610befce5077796c5f07a4521c4da2caf3a3e688355fcd5",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[NFKD message]": "bab75ef4bcfc8757a610befce5077796c5f07a4521c4da2caf3a3e688355fcd5",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[bcash]": "005fce2151d5d00041216b8df4c7a1b7c44b3d1fea44e2c53d2ac93eded687ec",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[grs-p2pkh]": "63b80d24787d21a3734ef875f9cbc506830b57511f6a41832988939a063a39f9",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[grs-segwit-native]": "25715b14066b081975e3e82e47c0d73dbec6885d1672bbc7ad1b3bf68941d7e4",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[grs-segwit-p2sh]": "d8f8bc5393264b0d717f9a760cb8ba8cbdda82aea0d077b30ce1070094a99466",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[p2pkh long message]": "227985471ce8341a82962a2a634c3786a5f7feb18addc95f92393f4c60c1beba",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[p2pkh0]": "ab00467d9e4fdc9c3e63afc53926072a50ce89e9c140c26f636b65a6e205ce72",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[p2pkh1]": "ab00467d9e4fdc9c3e63afc53926072a50ce89e9c140c26f636b65a6e205ce72",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[p2pkh2]": "e5cc85d2e4803c96aeefae36b4638f02e2c0f674d0a038fb8fe75832b5eb8d43",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-native long message]": "33bf48ac4364c7557daa97a5c13e1208604f85bbd45077d3b9364fde921b1f47",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-native0]": "283acfdd4526d7775c15c6de32b69f5de0b32611ea7db8cf910a32c25c931b57",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-native1]": "283acfdd4526d7775c15c6de32b69f5de0b32611ea7db8cf910a32c25c931b57",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-native2]": "cfff484653d935cb3e294a5ea168ae1174194c4cce408100c96156d8c26445df",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-p2sh long message]": "757ebd325ab076d705f7ac805f03f8cdc964fd1b3b6d3e6f30d30a4007e77900",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-p2sh0]": "8c0eaf50ecc37645397cb4bf74418e12cbd6a1fac9cd187cdfe32077bc4a375a",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-p2sh1]": "8c0eaf50ecc37645397cb4bf74418e12cbd6a1fac9cd187cdfe32077bc4a375a",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[segwit-p2sh2]": "815ff0f02676e74d04e527de234a7739d58d04d455ac9978614d698022ad3979",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_info[t1 firmware path]": "28fa7d38bc1252c346b57361aad936f399fbf73555d25b64987b019f55129523",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[long_words]": "341394d80b5d0d6b9d65f3bebf184ef81ada6fc6af3d2bfe50445dc4e421cc52",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[newlines]": "29fa0814bcca57c8df1d3475cf7df720a739ab54fd41343ac61ed645d3216513",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[no_spaces]": "bb613b0198d0cd8a1823db142019b79b9b6a0d6a6d2f1ca7023c4fa4b64d5684",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[normal_text]": "3ab717646032afcce058e08d9fbde406c3101df7e309334a648761f925d3560f",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[single_line_over]": "6a56d56bbbfab1f910b49fda5059ed4ec84815687a7dc9b0de1b2586cb1211ce",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[utf_nospace]": "35c95744c026a93759f795953851beff19cbab1883423d2535db610fe91de8d4",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[utf_text]": "cba6c516d1699aa0134a0a05e514681295870cfd56a5f806d1cebff0a232d643",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_path_warning": "969b3c76206be865afeb09fd7b0d25d08f0a026061b1717988b9c2b65f4c517c",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[NFC message]": "16e4ca277fa5920f39772c5a9e183b78507ba9c43c5bf18eb01ca948305f1ecd",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[NFKD message]": "16e4ca277fa5920f39772c5a9e183b78507ba9c43c5bf18eb01ca948305f1ecd",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[bcash]": "4d83b12408caf5d986b00e4353096e33dab7c40e301b191b475aab7f1b0815f9",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[grs-p2pkh]": "8c9b98de0aa76a35a383e098613cde939fb6f2af4b789ce664377838d2d0c8ac",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[grs-segwit-native]": "62f2b3c454c9f2e3b292a1ad814cfff6e7afc6900232a950d36b35f6ddf0a485",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[grs-segwit-p2sh]": "9dfba44e949bbcf3cd4960e1bdbe0bbdc17393d6273417af878faf9e38e5904d",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh long message]": "37a07035fd875bd3988323c6fd2ccc324f0f574d6fa39ed5842105487ba74bab",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh0]": "027f3af245cab497f274e0b4856722b30a90691cab36e12f9ce32adf45f840eb",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh1]": "027f3af245cab497f274e0b4856722b30a90691cab36e12f9ce32adf45f840eb",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[p2pkh2]": "77f8a0e4bcc8ebd74b689ecf6920963dfa5489c57d8fe354215def74293757bf",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native long message]": "6abee9ee8b76c0a7a750bc6a0e5f78f86ba298312ab554b809f285984eb0114b",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native0]": "3e2fc00ceaa1a0e5719164a069b9172c3e8829db1cfa2852400661a141aa3e08",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native1]": "3e2fc00ceaa1a0e5719164a069b9172c3e8829db1cfa2852400661a141aa3e08",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-native2]": "84b55eee88be0a06e6445f7fb3a0d922e63bffef1e1544857415916b40e0fbd1",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh long message]": "25b6ac2fb4e1bf9c9603ba2892bc499c28f2a0e0962f6c4b3340517dd2638238",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh0]": "128354a0f8d2c2f4e45001d0b0dbe5c88f1af680fe5e894cdecf8dbee964536c",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh1]": "128354a0f8d2c2f4e45001d0b0dbe5c88f1af680fe5e894cdecf8dbee964536c",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[segwit-p2sh2]": "76c8b7803e444a17a5c22916126706ede2fd494876e8701e88cbb8ae655ddaea",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage[t1 firmware path]": "3d9031a4bcb61c4d0fdf9f02371bed19ad57942cf1bce3f8ef399d456c0e52a3",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[long_words]": "139a959ffc1a87e6c9709b5a80fa731851054e8b4cac0a626544aefafff46eb2",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[newlines]": "86242ecc0b5c04d410448c90843dd3be267ee410b57526c73b3680df3e82994d",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[no_spaces]": "bcb4cd069c4ae56051792831a36385722fc95ea63e0f8a78417f9cf479dd7389",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[normal_text]": "22567b74d504a0a3b9e6676888224dfac10eb2509bf1bebec05ffb7c8f3c54cc",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[single_line_over]": "56d6ad0fa3828981feed2211dc236e502eb0c37e7c18b75fb26417ee6e6fd34f",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[utf_nospace]": "85e814ffda72370f0e7f054713811a8cf1a765cba7905001ca64dc1a2f794f97",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_pagination[utf_text]": "9fcb1d0d499c190314bb4cf9d161a4a20994d867572f9b3c9e523b0b6fec5108",
|
||||
"T3T1_en_bitcoin-test_signmessage.py::test_signmessage_path_warning": "520e8e8518c199461e835a7d84635f013e4b665f2a3dc290ea0cdfc8ffeb33dc",
|
||||
"T3T1_en_bitcoin-test_signtx.py::test_attack_change_input_address": "62f927a78bcb0b89e65ded4e2d349143e687f3a947a5827e936dcbefa337a8ae",
|
||||
"T3T1_en_bitcoin-test_signtx.py::test_attack_change_outputs": "8ac53a90eafa8b8d392cf36ab42c797577928258c9205fd1847a1fff64d2be2b",
|
||||
"T3T1_en_bitcoin-test_signtx.py::test_attack_modify_change_address": "1f6941265d2cf09bfbf79db2526242c8caa44a187e24f01a848ff02e9e84f244",
|
||||
@ -18959,21 +18940,21 @@
|
||||
"T3T1_en_bitcoin-test_signtx_taproot.py::test_send_p2tr[False]": "53511d2b2ccfa47ccc114861cc6c62a8cbd9aa5a1cda65d17488fed9d03c478f",
|
||||
"T3T1_en_bitcoin-test_signtx_taproot.py::test_send_p2tr[True]": "ca34314cd4ed59516fe95514ca020017739374b19d315c622e901b2a173d4baf",
|
||||
"T3T1_en_bitcoin-test_signtx_taproot.py::test_send_two_with_change": "2091037ff45d876fc3f2c47cbaf4fe67882fb97cb1b07afafbd6e7d7db116278",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_grs": "8774222aa7ffaca6d767fa0d323c39ffbf53cdd113bdaa68070b2b0a01375fc2",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_long": "63976f9d8e033d1d72c2a5567f5de2828aadecaa561bca7a797330e29743d773",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_testnet": "c87d4f66667ee698d8fc1a2f25642e4efd71a8f4a1f6176ce7a9e6067150384a",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_verify": "49b82a5d4384741da8cc06d844adfe64c4e9710dc805cfa50f7a31dcfcfdf3b9",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_verify_bcash": "66cad602f6a8aa960fe0a7c040baaaf2a9be29a8b35663b24e4c6de6ee6fa33b",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_verify_bitcoind": "3fe7a318c6c5f6b729151274fc045ef7613bb8a788b31407f4bd1f89f981315d",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_verify_utf": "2e61cd8165e9367a2235fc4e1c0699ddf8e928719efd7777069ee5f94c949a2a",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_message_long": "adfc1326cc4b4df2f316932ec847b261d444f967d959c08e4ba4ef07cac3c2ee",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_message_testnet": "8b0d1ac04237051807cc3741007ed7f81a8a6964ff1d7d12ce9da07dd1c51ef8",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_message_verify": "511b3abdc5312161852b65b919d2c533f67cb6f7833f5604e635886bbd640f32",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_verify_utf": "4561c7a72fe2512d6db042348f91e41f17df8cf60f41ec530684bf5c7bcd1ef9",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_message_long": "0a771818ba81808bcf0691ab1e7e60493f460141b4eb3056cd797f9636be66d9",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_message_testnet": "1f3ffb6945ea54f4abff98e2531dfb03f17cce71b4278c75859d1df65d52e345",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_message_verify": "c0d9b5df36bd7b52e9cbb369736023c6b9ffd3158d6138ac7f4640d68b6e2697",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_verify_utf": "457e4a8083428bcd5085900c2f571067cc75e968ceeddca98ecc44622a2723b0",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_grs": "6d4a8d4604f601f80be82f0f038d6caef866c82054949846e4f115e9774faef0",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_long": "770a0b7933a14b5a3b9911bdd25ea5c96e271c9849a29175ee77b96498777862",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_testnet": "92d844b2ec51ead7b5013343a4ea7a8ef683f8cba3b505938e8ddb7debe465f6",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_verify": "6e071435d62a5beba33278d1036957b499309cd989a05ab0a9f28f005520f02e",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_message_verify_bcash": "5607540bd05406c5ac9526ee02377b92f30c410db113de3e1fa220c7bf5e1c1e",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_verify_bitcoind": "4ca1c66ba5403e14c7141e80505f622f5db40bd1bc38cad4224a74fdfe665d91",
|
||||
"T3T1_en_bitcoin-test_verifymessage.py::test_verify_utf": "8b2a3b5e55dfa774d83e8d21142a035330b437e4e94d8beb0878b4ef7cf74b2c",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_message_long": "0efff3f4184c7488064ccfdddb05a330bbd049137a45def0235be3dbda2e3219",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_message_testnet": "96e66eb12925d1c4b31ab1fa7c4afb688e8203edb09ab5624d9a8628a3487a7d",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_message_verify": "c183e117bdbfd04081265528c103136bc22af9854faa0a63b601193c19774c08",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit.py::test_verify_utf": "c08c7e64e166dbf3c122ed6c7181899b3882bc6e03ef6c2a708cee85b9e99450",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_message_long": "b6dc72bd8a8e9f6d27a2142329a9ccae258221921a4ca5d52deb0dc9063717bd",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_message_testnet": "b067955e593bdca92c2fde93ee342a285a63dd14ea1ea36afab87085dd5a02a3",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_message_verify": "846acdbfaa7973769a82ba4b5fbd88d3bc61c02916438f6b1c517b013cbfaef2",
|
||||
"T3T1_en_bitcoin-test_verifymessage_segwit_native.py::test_verify_utf": "127c596ccf2bcaabffd7f7942eae69b8ec3ab6691132c846dc8dd9ff32759271",
|
||||
"T3T1_en_bitcoin-test_zcash.py::test_external_presigned": "e516a868683d108e916edee64ae6b6de68cb3bfc6a018aade8483202f7b7c26a",
|
||||
"T3T1_en_bitcoin-test_zcash.py::test_one_one_fee_sapling": "5a87239937a388a1a3ba4e371142e0e488e8270d13b3f4ffde9b3970b9575767",
|
||||
"T3T1_en_bitcoin-test_zcash.py::test_spend_old_versions": "7464fc1bb6d19d9a3670ff58b04490684f1163ad03426f578654f3817f1554fb",
|
||||
@ -19317,10 +19298,10 @@
|
||||
"T3T1_en_ethereum-test_definitions.py::test_chain_id_allowed": "7daae0a73f578b588d7051e8da25b2121ddc971a550fb2a65a288f770ba23d07",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_chain_id_mismatch": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_definition_does_not_override_builtin": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_chain_token_mismatch": "5594f33aad9731a6575ed40bf8362b03d1a2d39411e88e2b11f7749eb11b9d80",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_chain_token_ok": "4b512d783707893aee5e52e1709fbacf286e14aaa0ac85e7c4786d3952a00af2",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_chain_without_token": "2d3e0f7eeacf34df053401319a02b9f91da70f8c6a07ad933ea537dc83e11ce9",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_token": "f48184a187991499473244d4872ae4ef5bd7b80adcf71f7a577ab95d8922aa46",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_chain_token_mismatch": "cbf597551b5c08a3c5d8e777ab3a9df39ec0652813f8c3f2f137d457c7aeb838",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_chain_token_ok": "d7d82df450b05a26c35d639b2dc527fa4a356fa3fb3da8c9c3ab8e1b12ec7bbc",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_chain_without_token": "78f90c45fc4919f6a2e5da233be493e90b9691e908ccc983c9340850a645cde8",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_external_token": "e1449f004dd877f4d96ed2636cd0f96b3f154a6240f3f37d4e396060d79839b4",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_method_builtin[_call_getaddress]": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_method_builtin[_call_sign_typed_data]": "eab5a0fb53c4503b745f12fdd2db6f320caf7fd359d6f0bf14b393e0adef9af0",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_method_builtin[_call_signmessage]": "8777026a6234d2a6b8d898a15c60901bb6b81630786f31cff975712e7424b1f3",
|
||||
@ -19334,7 +19315,7 @@
|
||||
"T3T1_en_ethereum-test_definitions.py::test_method_external_mismatch[_call_sign_typed_data]": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_method_external_mismatch[_call_signmessage]": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_slip44_disallowed": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_slip44_external": "4458d1110986ddcb4fd7aed32c018978cc3221f29151321f7b174f89cc2f273d",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_slip44_external": "3a3abfd3700491806152e6589c095f8ff509b0e1fc94e25db12c577f00913572",
|
||||
"T3T1_en_ethereum-test_definitions.py::test_slip44_external_disallowed": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions_bad.py::test_bad_prefix": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_ethereum-test_definitions_bad.py::test_bad_proof": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
@ -19541,14 +19522,14 @@
|
||||
"T3T1_en_reset_recovery-test_recovery_bip39_t2.py::test_tt_nopin_nopassphrase": "335a5a9a1b71d445f5528aefcf664689baa2cbc30a7350c5f97a949f4813dda7",
|
||||
"T3T1_en_reset_recovery-test_recovery_bip39_t2.py::test_tt_pin_passphrase": "d91852e23f8a089b9f3c38f20fa5bd24a27365f7a47284ea059e6174cf5f0bd2",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_abort": "587a9d7ebe9f38edb69ed2ef3ebce72274f13ece62ac77566d5d106d3112b878",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_extra_share_entered": "f50972880eacfadb02252ddd77cf95f75b9bfef68e8d0a5d14da5b6f765d3a31",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_group_threshold_reached": "1e10bf06c3f6ee2322a5b18407afcd56ae00cd1050f8d478797f71b0f01c15bb",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_noabort": "d9069164bffce9ffcd768986bd9f6b9091d1a93b42d1031531755f8559accded",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_same_share": "251a0f02d357a1634ece78931d3d0a5ea5e2ed1db59709420170da3c44b24cfc",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f1-afc2dad5": "38d2728275c00a63b2f42d868fc51ff7067268418f123ef62b3fee229c15d489",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0-eb47093e": "22368230917b79e6ddd9c05c94fb9707829ca3ea344972e71ea9167fdbab512d",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_dryrun": "8b7f0f8a29a71df1561aa347caf57410dbb80c4fc5eb15f2c1b08fa8ccaea3f2",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "c19ef735f3a4f28b1d151230548f60aabeef4e99a053d1883971fcf1bd2c83f5",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_extra_share_entered": "8fa4c189c7a584755be4e7cd02ffb954040065c00eb9ff15a93c2408ba656ec3",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_group_threshold_reached": "9ab79f7aa8130f98fa24cc653c883a9a341bcac08ea2de6483bccf28d031907c",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_noabort": "1cd935cab0ba2407a31ccd7105c5e44883ee293d76a68437c3f617d9e2da7cc3",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_same_share": "b746b024274f7008bcb15d5cc1b9df818165b95da8d0e236c21cb7c1c0886e61",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f1-afc2dad5": "dedd702fdfd3d7131088bf8b1c2ef803a822f779315fad0a5a75ae7f7b313ff6",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0-eb47093e": "071494931a255961687a551e6575ec911a532402fff59aaeb5a564837ddd2ba7",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_dryrun": "e28016b19934e6c465a342111432fc16dd4edd8b4ad584d502ce74aa34afa170",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "04284464e108c182d2def58391cbd72815ef5747a7baf0564145522b49025061",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_basic.py::test_1of1": "01238e49c12e7419cbddf33b535a57e1d1283cd60f308462b7644e95a03d13d0",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_basic.py::test_abort": "587a9d7ebe9f38edb69ed2ef3ebce72274f13ece62ac77566d5d106d3112b878",
|
||||
"T3T1_en_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_first_share": "3c8d138bd4f0485864a6d3c1786a5bcb2267ce85d48e7e6ca1a9425b2b76b748",
|
||||
@ -19731,7 +19712,7 @@
|
||||
"T3T1_en_test_autolock.py::test_apply_auto_lock_delay_valid[60]": "d5dacbfada3fd814942e9ecf45f949b9471533ee2e8f0abc120afca3eb94a2b9",
|
||||
"T3T1_en_test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "72cfddf432bd7f4a21f7e5598b6670b44e37ffcdd4b8d7e766f7efe2524ee3de",
|
||||
"T3T1_en_test_autolock.py::test_autolock_cancels_ui": "107fef3042f43e6426bdb3fa75e5796c3c543062a63ca1d7800b83ded3c080f3",
|
||||
"T3T1_en_test_autolock.py::test_autolock_default_value": "cea810ad632407b1e8fbfdac17ccc89b1789895d113997657a42562f06717fc8",
|
||||
"T3T1_en_test_autolock.py::test_autolock_default_value": "ce7bdbdc4938e4725951ebbc00cfc8f242a3fa430c2206a7905e528acc3b4048",
|
||||
"T3T1_en_test_autolock.py::test_autolock_ignores_getaddress": "b2c6e6edbc2a19419193502dc652cd8c0aeeac08c9126905d655e7431422b9b9",
|
||||
"T3T1_en_test_autolock.py::test_autolock_ignores_initialize": "b2c6e6edbc2a19419193502dc652cd8c0aeeac08c9126905d655e7431422b9b9",
|
||||
"T3T1_en_test_basic.py::test_capabilities": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
@ -19771,15 +19752,15 @@
|
||||
"T3T1_en_test_language.py::test_switch_from_english_not_silent": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_test_language.py::test_switch_language": "f0e2703955ee26c7d9918a510b6b2744a8d6df0f83e0dcc8031be465dd1b77b4",
|
||||
"T3T1_en_test_language.py::test_translations_renders_on_screen": "386d167297cc9f3f421fd44e816f5643adf63b74bef472730bd2d84a3858fb25",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_homescreen_jpeg": "6d357a26a304a5626d93418ca9fc40de0acc4d98dc78d7cc16b913be680a3fe4",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_homescreen_jpeg": "51232e5df87a3709e68602d4a223cd4e910ad3a78114febd53b7e7c1db98b25d",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_homescreen_jpeg_progressive": "8c7c77033a5713b5711bfadffa4571699a57e45a7fe706bce1124aac9d388b04",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_homescreen_jpeg_wrong_size": "8c7c77033a5713b5711bfadffa4571699a57e45a7fe706bce1124aac9d388b04",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_homescreen_toif": "8c7c77033a5713b5711bfadffa4571699a57e45a7fe706bce1124aac9d388b04",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_settings": "32ab92c6244ed354fbe9eacac290fc387cf4cd69ff0321f85b253e4fbb6dbccc",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_settings": "cd172368ef0a87f66c26666a8984c1b3b4abe8a740dc588968400587d19cf83b",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_settings_passphrase": "f94f6a29ef26d7f65da8b654eb2af8fd62d980317f76236fe8fcff757e610c32",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_settings_passphrase_on_device": "34dd7ad1f185a666c2383500eac5825887286b307839995cd8514d2f3aa1349a",
|
||||
"T3T1_en_test_msg_applysettings.py::test_apply_settings_rotation": "51675a9ea00ff4b3978e8f9d3f162ea6b83c806af17f710d4c0c4dc98941c9a5",
|
||||
"T3T1_en_test_msg_applysettings.py::test_experimental_features": "5c7dfd9d9149551760fc232353de0e85d855f2b0825e7b74c50156bc4c7c2615",
|
||||
"T3T1_en_test_msg_applysettings.py::test_experimental_features": "a9dc0aad862b5d5d3443272a3e7fa5ebc414e7836c4a4d70266e900185af4b0f",
|
||||
"T3T1_en_test_msg_applysettings.py::test_label_too_long": "0373a0f99ec1445924dc60f071ca11f626176d9b7e40ba4e0b0afad195ad31e0",
|
||||
"T3T1_en_test_msg_applysettings.py::test_safety_checks": "2fa4cfafc8252551e45da6e53ff029ddf3661639e3108d79920cb2300f85ffea",
|
||||
"T3T1_en_test_msg_backup_device.py::test_backup_bip39": "9a1005431cbe80dff773b55dd1daeaae135dda1e450539442b825b113e4f2dd6",
|
||||
|
Loading…
Reference in New Issue
Block a user