mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +00:00
refactor(core/rust): Paragraph is now based on TString
This commit is contained in:
parent
9c287adf64
commit
39b7b22777
@ -1,10 +1,6 @@
|
|||||||
use core::{convert::TryFrom, ops::Deref, ptr, slice, str};
|
use core::{convert::TryFrom, ops::Deref, ptr, slice, str};
|
||||||
|
|
||||||
use crate::{
|
use crate::{error::Error, micropython::obj::Obj, strutil::hexlify};
|
||||||
error::Error,
|
|
||||||
micropython::obj::Obj,
|
|
||||||
strutil::{hexlify, SkipPrefix},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::ffi;
|
use super::ffi;
|
||||||
|
|
||||||
@ -93,10 +89,8 @@ impl StrBuffer {
|
|||||||
unsafe { slice::from_raw_parts(self.ptr.add(self.off.into()), self.len.into()) }
|
unsafe { slice::from_raw_parts(self.ptr.add(self.off.into()), self.len.into()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl SkipPrefix for StrBuffer {
|
pub fn skip_prefix(&self, skip_bytes: usize) -> Self {
|
||||||
fn skip_prefix(&self, skip_bytes: usize) -> Self {
|
|
||||||
let off: u16 = unwrap!(skip_bytes.try_into());
|
let off: u16 = unwrap!(skip_bytes.try_into());
|
||||||
assert!(off <= self.len);
|
assert!(off <= self.len);
|
||||||
assert!(self.as_ref().is_char_boundary(skip_bytes));
|
assert!(self.as_ref().is_char_boundary(skip_bytes));
|
||||||
|
@ -9,38 +9,18 @@ use crate::micropython::{buffer::StrBuffer, obj::Obj};
|
|||||||
#[cfg(feature = "translations")]
|
#[cfg(feature = "translations")]
|
||||||
use crate::translations::TR;
|
use crate::translations::TR;
|
||||||
|
|
||||||
/// Trait for slicing off string prefix by a specified number of bytes.
|
/// Trait for internal representation of strings. This is a legacy crutch before
|
||||||
/// See `StringType` for deeper explanation.
|
/// we fully transition to `TString`. For now, it allows some manner of
|
||||||
pub trait SkipPrefix {
|
/// compatibility between `&str` and `StrBuffer`. Implies the following
|
||||||
fn skip_prefix(&self, bytes: usize) -> Self;
|
/// operations:
|
||||||
}
|
/// - dereference into a short-lived `&str` reference (AsRef<str>) (probably not
|
||||||
|
/// strictly necessary anymore)
|
||||||
// XXX only implemented in bootloader, as we don't want &str to satisfy
|
|
||||||
// StringType in the main firmware. This is because we want to avoid duplication
|
|
||||||
// of every StringType-parametrized component.
|
|
||||||
#[cfg(feature = "bootloader")]
|
|
||||||
impl SkipPrefix for &str {
|
|
||||||
fn skip_prefix(&self, chars: usize) -> Self {
|
|
||||||
&self[chars..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for internal representation of strings.
|
|
||||||
/// Exists so that we can support `StrBuffer` as well as `&str` in the UI
|
|
||||||
/// components. Implies the following operations:
|
|
||||||
/// - dereference into a short-lived `&str` reference (AsRef<str>)
|
|
||||||
/// - create a new string by skipping some number of bytes (SkipPrefix) - used
|
|
||||||
/// when rendering continuations of long strings
|
|
||||||
/// - create a new string from a string literal (From<&'static str>)
|
/// - create a new string from a string literal (From<&'static str>)
|
||||||
pub trait StringType:
|
/// - infallibly convert into a `TString` (Into<TString<'static>>), which is
|
||||||
AsRef<str> + From<&'static str> + Into<TString<'static>> + SkipPrefix
|
/// then used for other operations.
|
||||||
{
|
pub trait StringType: AsRef<str> + From<&'static str> + Into<TString<'static>> {}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> StringType for T where
|
impl<T> StringType for T where T: AsRef<str> + From<&'static str> + Into<TString<'static>> {}
|
||||||
T: AsRef<str> + From<&'static str> + Into<TString<'static>> + SkipPrefix
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unified-length String type, long enough for most simple use-cases.
|
/// Unified-length String type, long enough for most simple use-cases.
|
||||||
pub type ShortString = String<50>;
|
pub type ShortString = String<50>;
|
||||||
@ -129,6 +109,23 @@ impl TString<'_> {
|
|||||||
Self::Str(s) => fun(s),
|
Self::Str(s) => fun(s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn skip_prefix(&self, skip_bytes: usize) -> Self {
|
||||||
|
self.map(|s| {
|
||||||
|
assert!(skip_bytes <= s.len());
|
||||||
|
assert!(s.is_char_boundary(skip_bytes));
|
||||||
|
});
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "micropython")]
|
||||||
|
Self::Allocated(s) => Self::Allocated(s.skip_prefix(skip_bytes)),
|
||||||
|
#[cfg(feature = "translations")]
|
||||||
|
Self::Translation { tr, offset } => Self::Translation {
|
||||||
|
tr: *tr,
|
||||||
|
offset: offset + skip_bytes as u16,
|
||||||
|
},
|
||||||
|
Self::Str(s) => Self::Str(&s[skip_bytes..]),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TString<'static> {
|
impl TString<'static> {
|
||||||
@ -198,22 +195,3 @@ impl<'a, 'b> PartialEq<TString<'a>> for TString<'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for TString<'_> {}
|
impl Eq for TString<'_> {}
|
||||||
|
|
||||||
impl SkipPrefix for TString<'_> {
|
|
||||||
fn skip_prefix(&self, skip_bytes: usize) -> Self {
|
|
||||||
self.map(|s| {
|
|
||||||
assert!(skip_bytes <= s.len());
|
|
||||||
assert!(s.is_char_boundary(skip_bytes));
|
|
||||||
});
|
|
||||||
match self {
|
|
||||||
#[cfg(feature = "micropython")]
|
|
||||||
Self::Allocated(s) => Self::Allocated(s.skip_prefix(skip_bytes)),
|
|
||||||
#[cfg(feature = "translations")]
|
|
||||||
Self::Translation { tr, offset } => Self::Translation {
|
|
||||||
tr: *tr,
|
|
||||||
offset: offset + skip_bytes as u16,
|
|
||||||
},
|
|
||||||
Self::Str(s) => Self::Str(&s[skip_bytes..]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ use core::mem;
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
ui::{
|
ui::{
|
||||||
component::{maybe::PaintOverlapping, MsgMap},
|
component::{maybe::PaintOverlapping, MsgMap},
|
||||||
@ -415,7 +416,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum Event<'a> {
|
pub enum Event {
|
||||||
#[cfg(feature = "button")]
|
#[cfg(feature = "button")]
|
||||||
Button(ButtonEvent),
|
Button(ButtonEvent),
|
||||||
#[cfg(feature = "touch")]
|
#[cfg(feature = "touch")]
|
||||||
@ -425,7 +426,7 @@ pub enum Event<'a> {
|
|||||||
/// token (another timer has to be requested).
|
/// token (another timer has to be requested).
|
||||||
Timer(TimerToken),
|
Timer(TimerToken),
|
||||||
/// Advance progress bar. Progress screens only.
|
/// Advance progress bar. Progress screens only.
|
||||||
Progress(u16, &'a str),
|
Progress(u16, TString<'static>),
|
||||||
/// Component has been attached to component tree. This event is sent once
|
/// Component has been attached to component tree. This event is sent once
|
||||||
/// before any other events.
|
/// before any other events.
|
||||||
Attach,
|
Attach,
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
use crate::{
|
use crate::ui::{
|
||||||
strutil::StringType,
|
component::{Component, Event, EventCtx, Never, Paginate},
|
||||||
ui::{
|
geometry::{Alignment, Offset, Rect},
|
||||||
component::{Component, Event, EventCtx, Never, Paginate},
|
|
||||||
geometry::{Alignment, Offset, Rect},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -12,15 +9,15 @@ use super::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FormattedText<T: StringType + Clone> {
|
pub struct FormattedText {
|
||||||
op_layout: OpTextLayout<T>,
|
op_layout: OpTextLayout<'static>,
|
||||||
vertical: Alignment,
|
vertical: Alignment,
|
||||||
char_offset: usize,
|
char_offset: usize,
|
||||||
y_offset: i16,
|
y_offset: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: StringType + Clone> FormattedText<T> {
|
impl FormattedText {
|
||||||
pub fn new(op_layout: OpTextLayout<T>) -> Self {
|
pub fn new(op_layout: OpTextLayout<'static>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
op_layout,
|
op_layout,
|
||||||
vertical: Alignment::Start,
|
vertical: Alignment::Start,
|
||||||
@ -54,7 +51,7 @@ impl<T: StringType + Clone> FormattedText<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
impl<T: StringType + Clone> Paginate for FormattedText<T> {
|
impl Paginate for FormattedText {
|
||||||
fn page_count(&mut self) -> usize {
|
fn page_count(&mut self) -> usize {
|
||||||
let mut page_count = 1; // There's always at least one page.
|
let mut page_count = 1; // There's always at least one page.
|
||||||
|
|
||||||
@ -118,7 +115,7 @@ impl<T: StringType + Clone> Paginate for FormattedText<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: StringType + Clone> Component for FormattedText<T> {
|
impl Component for FormattedText {
|
||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
@ -145,7 +142,7 @@ impl<T: StringType + Clone> Component for FormattedText<T> {
|
|||||||
// DEBUG-ONLY SECTION BELOW
|
// DEBUG-ONLY SECTION BELOW
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T: StringType + Clone> crate::trace::Trace for FormattedText<T> {
|
impl crate::trace::Trace for FormattedText {
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
use crate::ui::component::text::layout::trace::TraceSink;
|
use crate::ui::component::text::layout::trace::TraceSink;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::StringType,
|
strutil::TString,
|
||||||
ui::{
|
ui::{
|
||||||
display::{Color, Font},
|
display::{Color, Font},
|
||||||
geometry::{Alignment, Offset, Rect},
|
geometry::{Alignment, Offset, Rect},
|
||||||
@ -26,12 +26,12 @@ const PROCESSED_CHARS_ONE: usize = 1;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
/// Extension of TextLayout, allowing for Op-based operations
|
/// Extension of TextLayout, allowing for Op-based operations
|
||||||
pub struct OpTextLayout<T: StringType + Clone> {
|
pub struct OpTextLayout<'a> {
|
||||||
pub layout: TextLayout,
|
pub layout: TextLayout,
|
||||||
ops: Vec<Op<T>, MAX_OPS>,
|
ops: Vec<Op<'a>, MAX_OPS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
impl<'a> OpTextLayout<'a> {
|
||||||
pub fn new(style: TextStyle) -> Self {
|
pub fn new(style: TextStyle) -> Self {
|
||||||
Self {
|
Self {
|
||||||
layout: TextLayout::new(style),
|
layout: TextLayout::new(style),
|
||||||
@ -116,7 +116,7 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
// (just for incomplete texts that were separated)
|
// (just for incomplete texts that were separated)
|
||||||
layout.continues_from_prev_page = continued;
|
layout.continues_from_prev_page = continued;
|
||||||
|
|
||||||
let fit = layout.layout_text(text.as_ref(), cursor, sink);
|
let fit = text.map(|t| layout.layout_text(t, cursor, sink));
|
||||||
|
|
||||||
match fit {
|
match fit {
|
||||||
LayoutFit::Fitting {
|
LayoutFit::Fitting {
|
||||||
@ -148,9 +148,12 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
/// Gets rid of all action-Ops that are before the `skip_bytes` threshold.
|
/// Gets rid of all action-Ops that are before the `skip_bytes` threshold.
|
||||||
/// (Not removing the style changes, e.g. Font or Color, because they need
|
/// (Not removing the style changes, e.g. Font or Color, because they need
|
||||||
/// to be correctly set for future Text operations.)
|
/// to be correctly set for future Text operations.)
|
||||||
fn filter_skipped_ops<'b, I>(ops_iter: I, skip_bytes: usize) -> impl Iterator<Item = Op<T>> + 'b
|
fn filter_skipped_ops<'b, I>(
|
||||||
|
ops_iter: I,
|
||||||
|
skip_bytes: usize,
|
||||||
|
) -> impl Iterator<Item = Op<'a>> + 'b
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'b Op<T>> + 'b,
|
I: Iterator<Item = &'b Op<'a>> + 'b,
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
{
|
{
|
||||||
let mut skipped = 0;
|
let mut skipped = 0;
|
||||||
@ -158,7 +161,7 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
match op {
|
match op {
|
||||||
Op::Text(text, _continued) if skipped < skip_bytes => {
|
Op::Text(text, _continued) if skipped < skip_bytes => {
|
||||||
let skip_text_bytes_if_fits_partially = skip_bytes - skipped;
|
let skip_text_bytes_if_fits_partially = skip_bytes - skipped;
|
||||||
skipped = skipped.saturating_add(text.as_ref().len());
|
skipped = skipped.saturating_add(text.len());
|
||||||
if skipped > skip_bytes {
|
if skipped > skip_bytes {
|
||||||
// Fits partially
|
// Fits partially
|
||||||
// Skipping some bytes at the beginning, leaving rest
|
// Skipping some bytes at the beginning, leaving rest
|
||||||
@ -187,15 +190,15 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Op-adding operations
|
// Op-adding operations
|
||||||
impl<T: StringType + Clone> OpTextLayout<T> {
|
impl<'a> OpTextLayout<'a> {
|
||||||
pub fn with_new_item(mut self, item: Op<T>) -> Self {
|
pub fn with_new_item(mut self, item: Op<'a>) -> Self {
|
||||||
self.ops
|
self.ops
|
||||||
.push(item)
|
.push(item)
|
||||||
.assert_if_debugging_ui("Could not push to self.ops - increase MAX_OPS.");
|
.assert_if_debugging_ui("Could not push to self.ops - increase MAX_OPS.");
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(self, text: T) -> Self {
|
pub fn text(self, text: TString<'a>) -> Self {
|
||||||
self.with_new_item(Op::Text(text, false))
|
self.with_new_item(Op::Text(text, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,21 +240,21 @@ impl<T: StringType + Clone> OpTextLayout<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Op-adding aggregation operations
|
// Op-adding aggregation operations
|
||||||
impl<T: StringType + Clone> OpTextLayout<T> {
|
impl<'a> OpTextLayout<'a> {
|
||||||
pub fn text_normal(self, text: T) -> Self {
|
pub fn text_normal(self, text: impl Into<TString<'a>>) -> Self {
|
||||||
self.font(Font::NORMAL).text(text)
|
self.font(Font::NORMAL).text(text.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_mono(self, text: T) -> Self {
|
pub fn text_mono(self, text: impl Into<TString<'a>>) -> Self {
|
||||||
self.font(Font::MONO).text(text)
|
self.font(Font::MONO).text(text.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_bold(self, text: T) -> Self {
|
pub fn text_bold(self, text: impl Into<TString<'a>>) -> Self {
|
||||||
self.font(Font::BOLD).text(text)
|
self.font(Font::BOLD).text(text.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_demibold(self, text: T) -> Self {
|
pub fn text_demibold(self, text: impl Into<TString<'a>>) -> Self {
|
||||||
self.font(Font::DEMIBOLD).text(text)
|
self.font(Font::DEMIBOLD).text(text.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunkify_text(self, chunks: Option<(Chunks, i16)>) -> Self {
|
pub fn chunkify_text(self, chunks: Option<(Chunks, i16)>) -> Self {
|
||||||
@ -264,11 +267,11 @@ impl<T: StringType + Clone> OpTextLayout<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Op<T: StringType> {
|
pub enum Op<'a> {
|
||||||
/// Render text with current color and font.
|
/// Render text with current color and font.
|
||||||
/// Bool signifies whether this is a split Text Op continued from previous
|
/// Bool signifies whether this is a split Text Op continued from previous
|
||||||
/// page. If true, a leading ellipsis will be rendered.
|
/// page. If true, a leading ellipsis will be rendered.
|
||||||
Text(T, bool),
|
Text(TString<'a>, bool),
|
||||||
/// Set current text color.
|
/// Set current text color.
|
||||||
Color(Color),
|
Color(Color),
|
||||||
/// Set currently used font.
|
/// Set currently used font.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
strutil::StringType,
|
strutil::TString,
|
||||||
ui::{
|
ui::{
|
||||||
component::{Component, Event, EventCtx, Never, Paginate},
|
component::{Component, Event, EventCtx, Never, Paginate},
|
||||||
display::toif::Icon,
|
display::toif::Icon,
|
||||||
@ -26,16 +26,13 @@ pub const PARAGRAPH_TOP_SPACE: i16 = -1;
|
|||||||
/// Offset of paragraph bounding box bottom relative to bottom of its text.
|
/// Offset of paragraph bounding box bottom relative to bottom of its text.
|
||||||
pub const PARAGRAPH_BOTTOM_SPACE: i16 = 5;
|
pub const PARAGRAPH_BOTTOM_SPACE: i16 = 5;
|
||||||
|
|
||||||
pub type ParagraphVecLong<T> = Vec<Paragraph<T>, 32>;
|
pub type ParagraphVecLong<'a> = Vec<Paragraph<'a>, 32>;
|
||||||
pub type ParagraphVecShort<T> = Vec<Paragraph<T>, 8>;
|
pub type ParagraphVecShort<'a> = Vec<Paragraph<'a>, 8>;
|
||||||
|
|
||||||
pub trait ParagraphSource {
|
|
||||||
/// Determines the output type produced.
|
|
||||||
type StrType: StringType;
|
|
||||||
|
|
||||||
|
pub trait ParagraphSource<'a> {
|
||||||
/// Return text and associated style for given paragraph index and character
|
/// Return text and associated style for given paragraph index and character
|
||||||
/// offset within the paragraph.
|
/// offset within the paragraph.
|
||||||
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType>;
|
fn at(&self, index: usize, offset: usize) -> Paragraph<'a>;
|
||||||
|
|
||||||
/// Number of paragraphs.
|
/// Number of paragraphs.
|
||||||
fn size(&self) -> usize;
|
fn size(&self) -> usize;
|
||||||
@ -56,9 +53,9 @@ pub struct Paragraphs<T> {
|
|||||||
source: T,
|
source: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Paragraphs<T>
|
impl<'a, T> Paragraphs<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
pub fn new(source: T) -> Self {
|
pub fn new(source: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -101,10 +98,10 @@ where
|
|||||||
|
|
||||||
/// Helper for `change_offset` which should not get monomorphized as it
|
/// Helper for `change_offset` which should not get monomorphized as it
|
||||||
/// doesn't refer to T or Self.
|
/// doesn't refer to T or Self.
|
||||||
fn dyn_change_offset<S: StringType>(
|
fn dyn_change_offset(
|
||||||
mut area: Rect,
|
mut area: Rect,
|
||||||
mut offset: PageOffset,
|
mut offset: PageOffset,
|
||||||
source: &dyn ParagraphSource<StrType = S>,
|
source: &dyn ParagraphSource<'_>,
|
||||||
visible: &mut Vec<TextLayoutProxy, MAX_LINES>,
|
visible: &mut Vec<TextLayoutProxy, MAX_LINES>,
|
||||||
) {
|
) {
|
||||||
visible.clear();
|
visible.clear();
|
||||||
@ -135,9 +132,9 @@ where
|
|||||||
|
|
||||||
/// Iterate over visible layouts (bounding box, style) together
|
/// Iterate over visible layouts (bounding box, style) together
|
||||||
/// with corresponding string content. Should not get monomorphized.
|
/// with corresponding string content. Should not get monomorphized.
|
||||||
fn foreach_visible<'a, S: StringType>(
|
fn foreach_visible<'b>(
|
||||||
source: &'a dyn ParagraphSource<StrType = S>,
|
source: &'b dyn ParagraphSource<'a>,
|
||||||
visible: &'a [TextLayoutProxy],
|
visible: &'b [TextLayoutProxy],
|
||||||
offset: PageOffset,
|
offset: PageOffset,
|
||||||
func: &mut dyn FnMut(&TextLayout, &str),
|
func: &mut dyn FnMut(&TextLayout, &str),
|
||||||
) {
|
) {
|
||||||
@ -146,13 +143,13 @@ where
|
|||||||
|
|
||||||
for par in offset.par..source.size() {
|
for par in offset.par..source.size() {
|
||||||
let s = source.at(par, chr).content;
|
let s = source.at(par, chr).content;
|
||||||
if s.as_ref().is_empty() {
|
if s.is_empty() {
|
||||||
chr = 0;
|
chr = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(layout_proxy) = vis_iter.next() {
|
if let Some(layout_proxy) = vis_iter.next() {
|
||||||
let layout = layout_proxy.layout(source);
|
let layout = layout_proxy.layout(source);
|
||||||
func(&layout, s.as_ref());
|
s.map(|t| func(&layout, t));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -161,9 +158,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for Paragraphs<T>
|
impl<'a, T> Component for Paragraphs<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
@ -197,9 +194,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Paginate for Paragraphs<T>
|
impl<'a, T> Paginate for Paragraphs<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
fn page_count(&mut self) -> usize {
|
fn page_count(&mut self) -> usize {
|
||||||
// There's always at least one page.
|
// There's always at least one page.
|
||||||
@ -223,7 +220,7 @@ pub mod trace {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl<T: ParagraphSource> crate::trace::Trace for Paragraphs<T> {
|
impl<'a, T: ParagraphSource<'a>> crate::trace::Trace for Paragraphs<T> {
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.string("component", "Paragraphs".into());
|
t.string("component", "Paragraphs".into());
|
||||||
t.in_list("paragraphs", &|par_list| {
|
t.in_list("paragraphs", &|par_list| {
|
||||||
@ -247,9 +244,9 @@ pub mod trace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Paragraph<T> {
|
pub struct Paragraph<'a> {
|
||||||
/// Paragraph text.
|
/// Paragraph text.
|
||||||
content: T,
|
content: TString<'a>,
|
||||||
/// Paragraph style.
|
/// Paragraph style.
|
||||||
style: &'static TextStyle,
|
style: &'static TextStyle,
|
||||||
/// Paragraph alignment.
|
/// Paragraph alignment.
|
||||||
@ -263,10 +260,10 @@ pub struct Paragraph<T> {
|
|||||||
padding_bottom: i16,
|
padding_bottom: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Paragraph<T> {
|
impl<'a> Paragraph<'a> {
|
||||||
pub const fn new(style: &'static TextStyle, content: T) -> Self {
|
pub fn new<T: Into<TString<'a>>>(style: &'static TextStyle, content: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
content,
|
content: content.into(),
|
||||||
style,
|
style,
|
||||||
align: Alignment::Start,
|
align: Alignment::Start,
|
||||||
break_after: false,
|
break_after: false,
|
||||||
@ -301,25 +298,17 @@ impl<T> Paragraph<T> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content(&self) -> &T {
|
pub fn content(&self) -> &TString<'a> {
|
||||||
&self.content
|
&self.content
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, content: T) {
|
pub fn update<T: Into<TString<'a>>>(&mut self, content: T) {
|
||||||
self.content = content
|
self.content = content.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy style and replace content.
|
pub fn skip_prefix(&self, offset: usize) -> Paragraph<'a> {
|
||||||
pub fn map<U>(&self, func: impl FnOnce(&T) -> U) -> Paragraph<U> {
|
let content = self.content.skip_prefix(offset);
|
||||||
Paragraph {
|
Paragraph { content, ..*self }
|
||||||
content: func(&self.content),
|
|
||||||
style: self.style,
|
|
||||||
align: self.align,
|
|
||||||
break_after: self.break_after,
|
|
||||||
no_break: self.no_break,
|
|
||||||
padding_top: self.padding_top,
|
|
||||||
padding_bottom: self.padding_bottom,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&self, area: Rect) -> TextLayout {
|
fn layout(&self, area: Rect) -> TextLayout {
|
||||||
@ -343,7 +332,7 @@ impl TextLayoutProxy {
|
|||||||
Self { offset, bounds }
|
Self { offset, bounds }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout<S: StringType>(&self, source: &dyn ParagraphSource<StrType = S>) -> TextLayout {
|
fn layout(&self, source: &dyn ParagraphSource<'_>) -> TextLayout {
|
||||||
let content = source.at(self.offset.par, self.offset.chr);
|
let content = source.at(self.offset.par, self.offset.chr);
|
||||||
let mut layout = content.layout(self.bounds);
|
let mut layout = content.layout(self.bounds);
|
||||||
layout.continues_from_prev_page = self.offset.chr > 0;
|
layout.continues_from_prev_page = self.offset.chr > 0;
|
||||||
@ -382,16 +371,16 @@ impl PageOffset {
|
|||||||
///
|
///
|
||||||
/// If the returned remaining area is not None then it holds that
|
/// If the returned remaining area is not None then it holds that
|
||||||
/// `next_offset.par == self.par + 1`.
|
/// `next_offset.par == self.par + 1`.
|
||||||
fn advance<S: StringType>(
|
fn advance(
|
||||||
mut self,
|
mut self,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
source: &dyn ParagraphSource<StrType = S>,
|
source: &dyn ParagraphSource<'_>,
|
||||||
full_height: i16,
|
full_height: i16,
|
||||||
) -> (PageOffset, Option<Rect>, Option<TextLayoutProxy>) {
|
) -> (PageOffset, Option<Rect>, Option<TextLayoutProxy>) {
|
||||||
let paragraph = source.at(self.par, self.chr);
|
let paragraph = source.at(self.par, self.chr);
|
||||||
|
|
||||||
// Skip empty paragraphs.
|
// Skip empty paragraphs.
|
||||||
if paragraph.content.as_ref().is_empty() {
|
if paragraph.content().is_empty() {
|
||||||
self.par += 1;
|
self.par += 1;
|
||||||
self.chr = 0;
|
self.chr = 0;
|
||||||
return (self, Some(area), None);
|
return (self, Some(area), None);
|
||||||
@ -416,7 +405,7 @@ impl PageOffset {
|
|||||||
// Find out the dimensions of the paragraph at given char offset.
|
// Find out the dimensions of the paragraph at given char offset.
|
||||||
let mut layout = paragraph.layout(area);
|
let mut layout = paragraph.layout(area);
|
||||||
layout.continues_from_prev_page = self.chr > 0;
|
layout.continues_from_prev_page = self.chr > 0;
|
||||||
let fit = layout.fit_text(paragraph.content.as_ref());
|
let fit = paragraph.content().map(|t| layout.fit_text(t));
|
||||||
let (used, remaining_area) = area.split_top(fit.height());
|
let (used, remaining_area) = area.split_top(fit.height());
|
||||||
|
|
||||||
let layout = TextLayoutProxy::new(self, used);
|
let layout = TextLayoutProxy::new(self, used);
|
||||||
@ -447,9 +436,9 @@ impl PageOffset {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_place_pair_on_next_page<S: StringType>(
|
fn should_place_pair_on_next_page(
|
||||||
this_paragraph: &Paragraph<S>,
|
this_paragraph: &Paragraph<'_>,
|
||||||
next_paragraph: &Paragraph<S>,
|
next_paragraph: &Paragraph<'_>,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
full_height: i16,
|
full_height: i16,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
@ -461,13 +450,11 @@ impl PageOffset {
|
|||||||
|
|
||||||
let full_area = area.with_height(full_height);
|
let full_area = area.with_height(full_height);
|
||||||
let key_height = this_paragraph
|
let key_height = this_paragraph
|
||||||
.layout(full_area)
|
.content()
|
||||||
.fit_text(this_paragraph.content.as_ref())
|
.map(|t| this_paragraph.layout(full_area).fit_text(t).height());
|
||||||
.height();
|
|
||||||
let val_height = next_paragraph
|
let val_height = next_paragraph
|
||||||
.layout(full_area)
|
.content()
|
||||||
.fit_text(next_paragraph.content.as_ref())
|
.map(|t| next_paragraph.layout(full_area).fit_text(t).height());
|
||||||
.height();
|
|
||||||
let screen_full_threshold = this_paragraph.style.text_font.line_height()
|
let screen_full_threshold = this_paragraph.style.text_font.line_height()
|
||||||
+ next_paragraph.style.text_font.line_height();
|
+ next_paragraph.style.text_font.line_height();
|
||||||
|
|
||||||
@ -497,10 +484,10 @@ struct PageBreakIterator<'a, T> {
|
|||||||
current: Option<PageOffset>,
|
current: Option<PageOffset>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ParagraphSource> PageBreakIterator<'_, T> {
|
impl<'a, T: ParagraphSource<'a>> PageBreakIterator<'_, T> {
|
||||||
fn dyn_next<S: StringType>(
|
fn dyn_next(
|
||||||
mut area: Rect,
|
mut area: Rect,
|
||||||
paragraphs: &dyn ParagraphSource<StrType = S>,
|
paragraphs: &dyn ParagraphSource<'_>,
|
||||||
mut offset: PageOffset,
|
mut offset: PageOffset,
|
||||||
) -> Option<PageOffset> {
|
) -> Option<PageOffset> {
|
||||||
let full_height = area.height();
|
let full_height = area.height();
|
||||||
@ -527,7 +514,7 @@ impl<T: ParagraphSource> PageBreakIterator<'_, T> {
|
|||||||
|
|
||||||
/// Yields indices to beginnings of successive pages. First value is always
|
/// Yields indices to beginnings of successive pages. First value is always
|
||||||
/// `PageOffset { 0, 0 }` even if the paragraph vector is empty.
|
/// `PageOffset { 0, 0 }` even if the paragraph vector is empty.
|
||||||
impl<T: ParagraphSource> Iterator for PageBreakIterator<'_, T> {
|
impl<'a, T: ParagraphSource<'a>> Iterator for PageBreakIterator<'_, T> {
|
||||||
/// `PageOffset` denotes the first paragraph that is rendered and a
|
/// `PageOffset` denotes the first paragraph that is rendered and a
|
||||||
/// character offset in that paragraph.
|
/// character offset in that paragraph.
|
||||||
type Item = PageOffset;
|
type Item = PageOffset;
|
||||||
@ -608,9 +595,9 @@ impl<T> Checklist<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for Checklist<T>
|
impl<'a, T> Component for Checklist<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
@ -652,9 +639,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Paginate for Checklist<T>
|
impl<'a, T> Paginate for Checklist<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
fn page_count(&mut self) -> usize {
|
fn page_count(&mut self) -> usize {
|
||||||
1
|
1
|
||||||
@ -664,7 +651,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T: ParagraphSource> crate::trace::Trace for Checklist<T> {
|
impl<'a, T: ParagraphSource<'a>> crate::trace::Trace for Checklist<T> {
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("Checklist");
|
t.component("Checklist");
|
||||||
t.int("current", self.current as i64);
|
t.int("current", self.current as i64);
|
||||||
@ -672,16 +659,13 @@ impl<T: ParagraphSource> crate::trace::Trace for Checklist<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VecExt<T> {
|
pub trait VecExt<'a> {
|
||||||
fn add(&mut self, paragraph: Paragraph<T>) -> &mut Self;
|
fn add(&mut self, paragraph: Paragraph<'a>) -> &mut Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> VecExt<T> for Vec<Paragraph<T>, N>
|
impl<'a, const N: usize> VecExt<'a> for Vec<Paragraph<'a>, N> {
|
||||||
where
|
fn add(&mut self, paragraph: Paragraph<'a>) -> &mut Self {
|
||||||
T: AsRef<str>,
|
if paragraph.content().is_empty() {
|
||||||
{
|
|
||||||
fn add(&mut self, paragraph: Paragraph<T>) -> &mut Self {
|
|
||||||
if paragraph.content.as_ref().is_empty() {
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
if self.push(paragraph).is_err() {
|
if self.push(paragraph).is_err() {
|
||||||
@ -692,12 +676,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: StringType, const N: usize> ParagraphSource for Vec<Paragraph<T>, N> {
|
impl<'a, const N: usize> ParagraphSource<'a> for Vec<Paragraph<'a>, N> {
|
||||||
type StrType = T;
|
fn at(&self, index: usize, offset: usize) -> Paragraph<'a> {
|
||||||
|
|
||||||
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
|
|
||||||
let para = &self[index];
|
let para = &self[index];
|
||||||
para.map(|content| content.skip_prefix(offset))
|
para.skip_prefix(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
@ -705,12 +687,10 @@ impl<T: StringType, const N: usize> ParagraphSource for Vec<Paragraph<T>, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: StringType, const N: usize> ParagraphSource for [Paragraph<T>; N] {
|
impl<'a, const N: usize> ParagraphSource<'a> for [Paragraph<'a>; N] {
|
||||||
type StrType = T;
|
fn at(&self, index: usize, offset: usize) -> Paragraph<'a> {
|
||||||
|
|
||||||
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
|
|
||||||
let para = &self[index];
|
let para = &self[index];
|
||||||
para.map(|content| content.skip_prefix(offset))
|
para.skip_prefix(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
@ -718,12 +698,10 @@ impl<T: StringType, const N: usize> ParagraphSource for [Paragraph<T>; N] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: StringType> ParagraphSource for Paragraph<T> {
|
impl<'a> ParagraphSource<'a> for Paragraph<'a> {
|
||||||
type StrType = T;
|
fn at(&self, index: usize, offset: usize) -> Paragraph<'a> {
|
||||||
|
|
||||||
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
|
|
||||||
assert_eq!(index, 0);
|
assert_eq!(index, 0);
|
||||||
self.map(|content| content.skip_prefix(offset))
|
self.skip_prefix(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
|
@ -389,7 +389,7 @@ extern "C" fn ui_layout_progress_event(n_args: usize, args: *const Obj) -> Obj {
|
|||||||
let this: Gc<LayoutObj> = args[0].try_into()?;
|
let this: Gc<LayoutObj> = args[0].try_into()?;
|
||||||
let value: u16 = args[1].try_into()?;
|
let value: u16 = args[1].try_into()?;
|
||||||
let description: StrBuffer = args[2].try_into()?;
|
let description: StrBuffer = args[2].try_into()?;
|
||||||
let msg = this.obj_event(Event::Progress(value, description.as_ref()))?;
|
let msg = this.obj_event(Event::Progress(value, description.into()))?;
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
|
||||||
|
@ -8,7 +8,6 @@ use crate::{
|
|||||||
util::{iter_into_array, try_or_raise},
|
util::{iter_into_array, try_or_raise},
|
||||||
},
|
},
|
||||||
storage::{get_avatar_len, load_avatar},
|
storage::{get_avatar_len, load_avatar},
|
||||||
strutil::SkipPrefix,
|
|
||||||
ui::{
|
ui::{
|
||||||
component::text::{
|
component::text::{
|
||||||
paragraphs::{Paragraph, ParagraphSource},
|
paragraphs::{Paragraph, ParagraphSource},
|
||||||
@ -62,10 +61,8 @@ pub struct ConfirmBlob {
|
|||||||
pub data_font: &'static TextStyle,
|
pub data_font: &'static TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParagraphSource for ConfirmBlob {
|
impl ParagraphSource<'static> for ConfirmBlob {
|
||||||
type StrType = StrBuffer;
|
fn at(&self, index: usize, offset: usize) -> Paragraph<'static> {
|
||||||
|
|
||||||
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
|
|
||||||
match index {
|
match index {
|
||||||
0 => Paragraph::new(self.description_font, self.description.skip_prefix(offset)),
|
0 => Paragraph::new(self.description_font, self.description.skip_prefix(offset)),
|
||||||
1 => Paragraph::new(self.extra_font, self.extra.skip_prefix(offset)),
|
1 => Paragraph::new(self.extra_font, self.extra.skip_prefix(offset)),
|
||||||
@ -102,10 +99,8 @@ impl PropsList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParagraphSource for PropsList {
|
impl ParagraphSource<'static> for PropsList {
|
||||||
type StrType = StrBuffer;
|
fn at(&self, index: usize, offset: usize) -> Paragraph<'static> {
|
||||||
|
|
||||||
fn at(&self, index: usize, offset: usize) -> Paragraph<Self::StrType> {
|
|
||||||
let block = move || {
|
let block = move || {
|
||||||
let entry = self.items.get(index / 2)?;
|
let entry = self.items.get(index / 2)?;
|
||||||
let [key, value, value_is_mono]: [Obj; 3] = iter_into_array(entry)?;
|
let [key, value, value_is_mono]: [Obj; 3] = iter_into_array(entry)?;
|
||||||
|
@ -22,8 +22,8 @@ const QR_BORDER: i16 = 3;
|
|||||||
|
|
||||||
pub struct AddressDetails {
|
pub struct AddressDetails {
|
||||||
qr_code: Qr,
|
qr_code: Qr,
|
||||||
details_view: Paragraphs<ParagraphVecShort<StrBuffer>>,
|
details_view: Paragraphs<ParagraphVecShort<'static>>,
|
||||||
xpub_view: Frame<Paragraphs<Paragraph<StrBuffer>>, StrBuffer>,
|
xpub_view: Frame<Paragraphs<Paragraph<'static>>, StrBuffer>,
|
||||||
xpubs: Vec<(StrBuffer, StrBuffer), MAX_XPUBS>,
|
xpubs: Vec<(StrBuffer, StrBuffer), MAX_XPUBS>,
|
||||||
current_page: usize,
|
current_page: usize,
|
||||||
current_subpage: usize,
|
current_subpage: usize,
|
||||||
@ -43,16 +43,13 @@ impl AddressDetails {
|
|||||||
let details_view = {
|
let details_view = {
|
||||||
let mut para = ParagraphVecShort::new();
|
let mut para = ParagraphVecShort::new();
|
||||||
if let Some(account) = account {
|
if let Some(account) = account {
|
||||||
para.add(Paragraph::new(
|
para.add(Paragraph::new(&theme::TEXT_BOLD, TR::words__account_colon));
|
||||||
&theme::TEXT_BOLD,
|
|
||||||
TR::words__account_colon.try_into()?,
|
|
||||||
));
|
|
||||||
para.add(Paragraph::new(&theme::TEXT_MONO, account));
|
para.add(Paragraph::new(&theme::TEXT_MONO, account));
|
||||||
}
|
}
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
para.add(Paragraph::new(
|
para.add(Paragraph::new(
|
||||||
&theme::TEXT_BOLD,
|
&theme::TEXT_BOLD,
|
||||||
TR::address_details__derivation_path.try_into()?,
|
TR::address_details__derivation_path,
|
||||||
));
|
));
|
||||||
para.add(Paragraph::new(&theme::TEXT_MONO, path));
|
para.add(Paragraph::new(&theme::TEXT_MONO, path));
|
||||||
}
|
}
|
||||||
@ -60,7 +57,7 @@ impl AddressDetails {
|
|||||||
};
|
};
|
||||||
let xpub_view = Frame::new(
|
let xpub_view = Frame::new(
|
||||||
"".into(),
|
"".into(),
|
||||||
Paragraph::new(&theme::TEXT_MONO_DATA, "".into()).into_paragraphs(),
|
Paragraph::new(&theme::TEXT_MONO_DATA, "").into_paragraphs(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = Self {
|
let result = Self {
|
||||||
|
@ -78,7 +78,7 @@ pub struct Page<T>
|
|||||||
where
|
where
|
||||||
T: StringType + Clone,
|
T: StringType + Clone,
|
||||||
{
|
{
|
||||||
formatted: FormattedText<T>,
|
formatted: FormattedText,
|
||||||
btn_layout: ButtonLayout,
|
btn_layout: ButtonLayout,
|
||||||
btn_actions: ButtonActions,
|
btn_actions: ButtonActions,
|
||||||
current_page: usize,
|
current_page: usize,
|
||||||
@ -95,7 +95,7 @@ where
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
btn_layout: ButtonLayout,
|
btn_layout: ButtonLayout,
|
||||||
btn_actions: ButtonActions,
|
btn_actions: ButtonActions,
|
||||||
formatted: FormattedText<T>,
|
formatted: FormattedText,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut page = Self {
|
let mut page = Self {
|
||||||
formatted,
|
formatted,
|
||||||
|
@ -274,7 +274,7 @@ where
|
|||||||
|
|
||||||
pub fn start(&mut self, ctx: &mut EventCtx) {
|
pub fn start(&mut self, ctx: &mut EventCtx) {
|
||||||
self.start_time = Some(Instant::now());
|
self.start_time = Some(Instant::now());
|
||||||
self.loader.event(ctx, Event::Progress(0, ""));
|
self.loader.event(ctx, Event::Progress(0, "".into()));
|
||||||
self.loader.mutate(ctx, |ctx, loader| {
|
self.loader.mutate(ctx, |ctx, loader| {
|
||||||
loader.request_paint(ctx);
|
loader.request_paint(ctx);
|
||||||
});
|
});
|
||||||
@ -318,7 +318,7 @@ where
|
|||||||
let percentage = self.percentage(now);
|
let percentage = self.percentage(now);
|
||||||
let new_loader_value = (percentage * LOADER_MAX as u32) / 100;
|
let new_loader_value = (percentage * LOADER_MAX as u32) / 100;
|
||||||
self.loader
|
self.loader
|
||||||
.event(ctx, Event::Progress(new_loader_value as u16, ""));
|
.event(ctx, Event::Progress(new_loader_value as u16, "".into()));
|
||||||
// Returning only after the loader was fully painted
|
// Returning only after the loader was fully painted
|
||||||
if percentage >= 100 {
|
if percentage >= 100 {
|
||||||
return Some(LoaderMsg::GrownCompletely);
|
return Some(LoaderMsg::GrownCompletely);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
|
||||||
strutil::StringType,
|
strutil::StringType,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
@ -22,9 +21,6 @@ const BOTTOM_DESCRIPTION_MARGIN: i16 = 10;
|
|||||||
const LOADER_Y_OFFSET_TITLE: i16 = -10;
|
const LOADER_Y_OFFSET_TITLE: i16 = -10;
|
||||||
const LOADER_Y_OFFSET_NO_TITLE: i16 = -20;
|
const LOADER_Y_OFFSET_NO_TITLE: i16 = -20;
|
||||||
|
|
||||||
// Clippy was complaining about `very complex type used`
|
|
||||||
type UpdateDescriptionFn<T, Error> = fn(&str) -> Result<T, Error>;
|
|
||||||
|
|
||||||
pub struct Progress<T>
|
pub struct Progress<T>
|
||||||
where
|
where
|
||||||
T: StringType,
|
T: StringType,
|
||||||
@ -33,9 +29,8 @@ where
|
|||||||
value: u16,
|
value: u16,
|
||||||
loader_y_offset: i16,
|
loader_y_offset: i16,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
description: Child<Paragraphs<Paragraph<T>>>,
|
description: Child<Paragraphs<Paragraph<'static>>>,
|
||||||
description_pad: Pad,
|
description_pad: Pad,
|
||||||
update_description: Option<UpdateDescriptionFn<T, Error>>,
|
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +50,6 @@ where
|
|||||||
Paragraph::new(&theme::TEXT_NORMAL, description).centered(),
|
Paragraph::new(&theme::TEXT_NORMAL, description).centered(),
|
||||||
)),
|
)),
|
||||||
description_pad: Pad::with_background(theme::BG),
|
description_pad: Pad::with_background(theme::BG),
|
||||||
update_description: None,
|
|
||||||
icon: theme::ICON_TICK_FAT,
|
icon: theme::ICON_TICK_FAT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,14 +59,6 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_update_description(
|
|
||||||
mut self,
|
|
||||||
update_description: UpdateDescriptionFn<T, Error>,
|
|
||||||
) -> Self {
|
|
||||||
self.update_description = Some(update_description);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_icon(mut self, icon: Icon) -> Self {
|
pub fn with_icon(mut self, icon: Icon) -> Self {
|
||||||
self.icon = icon;
|
self.icon = icon;
|
||||||
self
|
self
|
||||||
@ -105,10 +91,7 @@ where
|
|||||||
.inner()
|
.inner()
|
||||||
.inner()
|
.inner()
|
||||||
.content()
|
.content()
|
||||||
.as_ref()
|
.map(|t| t.chars().filter(|c| *c == '\n').count() as i16);
|
||||||
.chars()
|
|
||||||
.filter(|c| *c == '\n')
|
|
||||||
.count() as i16;
|
|
||||||
|
|
||||||
let no_title_case = (Rect::zero(), Self::AREA, LOADER_Y_OFFSET_NO_TITLE);
|
let no_title_case = (Rect::zero(), Self::AREA, LOADER_Y_OFFSET_NO_TITLE);
|
||||||
let (title, rest, loader_y_offset) = if let Some(self_title) = &self.title {
|
let (title, rest, loader_y_offset) = if let Some(self_title) = &self.title {
|
||||||
@ -140,21 +123,16 @@ where
|
|||||||
if mem::replace(&mut self.value, new_value) != new_value {
|
if mem::replace(&mut self.value, new_value) != new_value {
|
||||||
self.request_paint(ctx);
|
self.request_paint(ctx);
|
||||||
}
|
}
|
||||||
if let Some(update_description) = self.update_description {
|
self.description.mutate(ctx, |ctx, para| {
|
||||||
self.description.mutate(ctx, |ctx, para| {
|
// NOTE: not doing any change for empty new descriptions
|
||||||
// NOTE: not doing any change for empty new descriptions
|
// (currently, there is no use-case for deleting the description)
|
||||||
// (currently, there is no use-case for deleting the description)
|
if !new_description.is_empty() && para.inner_mut().content() != &new_description {
|
||||||
if !new_description.is_empty()
|
para.inner_mut().update(new_description);
|
||||||
&& para.inner_mut().content().as_ref() != new_description
|
para.change_page(0); // Recompute bounding box.
|
||||||
{
|
ctx.request_paint();
|
||||||
let new_description = unwrap!((update_description)(new_description));
|
self.description_pad.clear();
|
||||||
para.inner_mut().update(new_description);
|
}
|
||||||
para.change_page(0); // Recompute bounding box.
|
});
|
||||||
ctx.request_paint();
|
|
||||||
self.description_pad.clear();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -77,9 +77,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ComponentMsgObj for Paragraphs<T>
|
impl<'a, T> ComponentMsgObj for Paragraphs<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
@ -442,12 +442,12 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
|
|||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||||
|
|
||||||
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL)
|
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
|
||||||
.text_normal(TR::reset__by_continuing.try_into()?)
|
.text_normal(TR::reset__by_continuing)
|
||||||
.next_page()
|
.next_page()
|
||||||
.text_normal(TR::reset__more_info_at.try_into()?)
|
.text_normal(TR::reset__more_info_at)
|
||||||
.newline()
|
.newline()
|
||||||
.text_bold(TR::reset__tos_link.try_into()?);
|
.text_bold(TR::reset__tos_link);
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
|
|
||||||
content_in_button_page(title, formatted, button, Some("".into()), false)
|
content_in_button_page(title, formatted, button, Some("".into()), false)
|
||||||
@ -459,27 +459,24 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
let block = move |_args: &[Obj], _kwargs: &Map| {
|
let block = move |_args: &[Obj], _kwargs: &Map| {
|
||||||
// cached allocated translations that get_page can reuse
|
// cached allocated translations that get_page can reuse
|
||||||
let tr_title_success: StrBuffer = TR::words__title_success.try_into()?;
|
let tr_title_success: StrBuffer = TR::words__title_success.try_into()?;
|
||||||
let tr_new_wallet_created: StrBuffer = TR::backup__new_wallet_created.try_into()?;
|
|
||||||
let tr_it_should_be_backed_up_now: StrBuffer =
|
|
||||||
TR::backup__it_should_be_backed_up_now.try_into()?;
|
|
||||||
let tr_title_backup_wallet: StrBuffer = TR::backup__title_backup_wallet.try_into()?;
|
let tr_title_backup_wallet: StrBuffer = TR::backup__title_backup_wallet.try_into()?;
|
||||||
let tr_recover_anytime: StrBuffer = TR::backup__recover_anytime.try_into()?;
|
|
||||||
|
|
||||||
let get_page = move |page_index| match page_index {
|
let get_page = move |page_index| match page_index {
|
||||||
0 => {
|
0 => {
|
||||||
let btn_layout = ButtonLayout::text_none_arrow_wide(TR::buttons__skip.into());
|
let btn_layout = ButtonLayout::text_none_arrow_wide(TR::buttons__skip.into());
|
||||||
let btn_actions = ButtonActions::cancel_none_next();
|
let btn_actions = ButtonActions::cancel_none_next();
|
||||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
|
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
|
||||||
.text_normal(tr_new_wallet_created)
|
.text_normal(TR::backup__new_wallet_created)
|
||||||
.newline()
|
.newline()
|
||||||
.text_normal(tr_it_should_be_backed_up_now);
|
.text_normal(TR::backup__it_should_be_backed_up_now);
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
Page::new(btn_layout, btn_actions, formatted).with_title(tr_title_success)
|
Page::new(btn_layout, btn_actions, formatted).with_title(tr_title_success)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__back_up.into());
|
let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__back_up.into());
|
||||||
let btn_actions = ButtonActions::prev_none_confirm();
|
let btn_actions = ButtonActions::prev_none_confirm();
|
||||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(tr_recover_anytime);
|
let ops =
|
||||||
|
OpTextLayout::new(theme::TEXT_NORMAL).text_normal(TR::backup__recover_anytime);
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||||
.with_title(tr_title_backup_wallet)
|
.with_title(tr_title_backup_wallet)
|
||||||
@ -550,15 +547,9 @@ extern "C" fn new_confirm_joint_total(n_args: usize, args: *const Obj, kwargs: *
|
|||||||
let total_amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?;
|
let total_amount: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_amount)?.try_into()?;
|
||||||
|
|
||||||
let paragraphs = Paragraphs::new([
|
let paragraphs = Paragraphs::new([
|
||||||
Paragraph::new(
|
Paragraph::new(&theme::TEXT_BOLD, TR::joint__you_are_contributing),
|
||||||
&theme::TEXT_BOLD,
|
|
||||||
TR::joint__you_are_contributing.try_into()?,
|
|
||||||
),
|
|
||||||
Paragraph::new(&theme::TEXT_MONO, spending_amount),
|
Paragraph::new(&theme::TEXT_MONO, spending_amount),
|
||||||
Paragraph::new(
|
Paragraph::new(&theme::TEXT_BOLD, TR::joint__to_the_total_amount),
|
||||||
&theme::TEXT_BOLD,
|
|
||||||
TR::joint__to_the_total_amount.try_into()?,
|
|
||||||
),
|
|
||||||
Paragraph::new(&theme::TEXT_MONO, total_amount),
|
Paragraph::new(&theme::TEXT_MONO, total_amount),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -586,9 +577,9 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
|
|||||||
};
|
};
|
||||||
|
|
||||||
let paragraphs = Paragraphs::new([
|
let paragraphs = Paragraphs::new([
|
||||||
Paragraph::new(&theme::TEXT_NORMAL, description.try_into()?),
|
Paragraph::new(&theme::TEXT_NORMAL, description),
|
||||||
Paragraph::new(&theme::TEXT_MONO, amount_change).break_after(),
|
Paragraph::new(&theme::TEXT_MONO, amount_change).break_after(),
|
||||||
Paragraph::new(&theme::TEXT_BOLD, TR::modify_amount__new_amount.try_into()?),
|
Paragraph::new(&theme::TEXT_BOLD, TR::modify_amount__new_amount),
|
||||||
Paragraph::new(&theme::TEXT_MONO, amount_new),
|
Paragraph::new(&theme::TEXT_MONO, amount_new),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -677,12 +668,6 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
let total_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_label)?.try_into()?;
|
let total_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_total_label)?.try_into()?;
|
||||||
let fee_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?;
|
let fee_label: StrBuffer = kwargs.get(Qstr::MP_QSTR_fee_label)?.try_into()?;
|
||||||
|
|
||||||
// cached allocated translated strings that get_page can reuse
|
|
||||||
let tr_title_fee = TR::confirm_total__title_fee.try_into()?;
|
|
||||||
let tr_fee_rate = TR::confirm_total__fee_rate.try_into()?;
|
|
||||||
let tr_title_sending_from = TR::confirm_total__title_sending_from.try_into()?;
|
|
||||||
let tr_account = TR::words__account_colon.try_into()?;
|
|
||||||
|
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
match page_index {
|
match page_index {
|
||||||
0 => {
|
0 => {
|
||||||
@ -701,7 +686,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
.text_mono(fee_amount);
|
.text_mono(fee_amount);
|
||||||
|
|
||||||
let formatted = FormattedText::new(ops);
|
let formatted = FormattedText::new(ops);
|
||||||
Page::new(btn_layout, btn_actions, formatted)
|
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
// Fee rate info
|
// Fee rate info
|
||||||
@ -711,11 +696,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
let fee_rate_amount = fee_rate_amount.unwrap_or_default();
|
let fee_rate_amount = fee_rate_amount.unwrap_or_default();
|
||||||
|
|
||||||
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
||||||
.text_bold(tr_title_fee)
|
.text_bold(TR::confirm_total__title_fee)
|
||||||
.newline()
|
.newline()
|
||||||
.newline()
|
.newline()
|
||||||
.newline_half()
|
.newline_half()
|
||||||
.text_bold(tr_fee_rate)
|
.text_bold(TR::confirm_total__fee_rate)
|
||||||
.newline()
|
.newline()
|
||||||
.text_mono(fee_rate_amount);
|
.text_mono(fee_rate_amount);
|
||||||
|
|
||||||
@ -732,11 +717,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
// TODO: include wallet info when available
|
// TODO: include wallet info when available
|
||||||
|
|
||||||
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
let ops = OpTextLayout::new(theme::TEXT_MONO)
|
||||||
.text_bold(tr_title_sending_from)
|
.text_bold(TR::confirm_total__title_sending_from)
|
||||||
.newline()
|
.newline()
|
||||||
.newline()
|
.newline()
|
||||||
.newline_half()
|
.newline_half()
|
||||||
.text_bold(tr_account)
|
.text_bold(TR::words__account_colon)
|
||||||
.newline()
|
.newline()
|
||||||
.text_mono(account_label);
|
.text_mono(account_label);
|
||||||
|
|
||||||
@ -802,9 +787,9 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
|
|||||||
ops = ops.next_page();
|
ops = ops.next_page();
|
||||||
}
|
}
|
||||||
ops = ops
|
ops = ops
|
||||||
.text_bold(unwrap!(key.try_into()))
|
.text_bold(unwrap!(TString::try_from(key)))
|
||||||
.newline()
|
.newline()
|
||||||
.text_mono(unwrap!(value.try_into()));
|
.text_mono(unwrap!(TString::try_from(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
@ -858,11 +843,11 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
/// (title, text, btn_layout, btn_actions, text_y_offset)
|
/// (title, text, btn_layout, btn_actions, text_y_offset)
|
||||||
fn tutorial_screen(
|
fn tutorial_screen(
|
||||||
title: StrBuffer,
|
title: StrBuffer,
|
||||||
text: StrBuffer,
|
text: TR,
|
||||||
btn_layout: ButtonLayout,
|
btn_layout: ButtonLayout,
|
||||||
btn_actions: ButtonActions,
|
btn_actions: ButtonActions,
|
||||||
) -> Page<StrBuffer> {
|
) -> Page<StrBuffer> {
|
||||||
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL).text_normal(text);
|
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
Page::new(btn_layout, btn_actions, formatted).with_title(title)
|
Page::new(btn_layout, btn_actions, formatted).with_title(title)
|
||||||
}
|
}
|
||||||
@ -873,19 +858,12 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
|||||||
|
|
||||||
// cached allocated translated strings that get_page can reuse
|
// cached allocated translated strings that get_page can reuse
|
||||||
let tr_title_hello: StrBuffer = TR::tutorial__title_hello.try_into()?;
|
let tr_title_hello: StrBuffer = TR::tutorial__title_hello.try_into()?;
|
||||||
let tr_welcome_press_right: StrBuffer = TR::tutorial__welcome_press_right.try_into()?;
|
|
||||||
let tr_use_trezor: StrBuffer = TR::tutorial__use_trezor.try_into()?;
|
|
||||||
let tr_hold_to_confirm: StrBuffer = TR::buttons__hold_to_confirm.try_into()?;
|
let tr_hold_to_confirm: StrBuffer = TR::buttons__hold_to_confirm.try_into()?;
|
||||||
let tr_press_and_hold: StrBuffer = TR::tutorial__press_and_hold.try_into()?;
|
|
||||||
let tr_title_screen_scroll: StrBuffer = TR::tutorial__title_screen_scroll.try_into()?;
|
let tr_title_screen_scroll: StrBuffer = TR::tutorial__title_screen_scroll.try_into()?;
|
||||||
let tr_scroll_down: StrBuffer = TR::tutorial__scroll_down.try_into()?;
|
|
||||||
let tr_confirm: StrBuffer = TR::buttons__confirm.try_into()?;
|
let tr_confirm: StrBuffer = TR::buttons__confirm.try_into()?;
|
||||||
let tr_middle_click: StrBuffer = TR::tutorial__middle_click.try_into()?;
|
|
||||||
let tr_title_tutorial_complete: StrBuffer =
|
let tr_title_tutorial_complete: StrBuffer =
|
||||||
TR::tutorial__title_tutorial_complete.try_into()?;
|
TR::tutorial__title_tutorial_complete.try_into()?;
|
||||||
let tr_ready_to_use: StrBuffer = TR::tutorial__ready_to_use.try_into()?;
|
|
||||||
let tr_title_skip: StrBuffer = TR::tutorial__title_skip.try_into()?;
|
let tr_title_skip: StrBuffer = TR::tutorial__title_skip.try_into()?;
|
||||||
let tr_sure_you_want_skip: StrBuffer = TR::tutorial__sure_you_want_skip.try_into()?;
|
|
||||||
|
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
// Lazy-loaded list of screens to show, with custom content,
|
// Lazy-loaded list of screens to show, with custom content,
|
||||||
@ -897,37 +875,37 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
|||||||
// title, text, btn_layout, btn_actions
|
// title, text, btn_layout, btn_actions
|
||||||
0 => tutorial_screen(
|
0 => tutorial_screen(
|
||||||
tr_title_hello,
|
tr_title_hello,
|
||||||
tr_welcome_press_right,
|
TR::tutorial__welcome_press_right,
|
||||||
ButtonLayout::cancel_none_arrow(),
|
ButtonLayout::cancel_none_arrow(),
|
||||||
ButtonActions::last_none_next(),
|
ButtonActions::last_none_next(),
|
||||||
),
|
),
|
||||||
1 => tutorial_screen(
|
1 => tutorial_screen(
|
||||||
"".into(),
|
"".into(),
|
||||||
tr_use_trezor,
|
TR::tutorial__use_trezor,
|
||||||
ButtonLayout::arrow_none_arrow(),
|
ButtonLayout::arrow_none_arrow(),
|
||||||
ButtonActions::prev_none_next(),
|
ButtonActions::prev_none_next(),
|
||||||
),
|
),
|
||||||
2 => tutorial_screen(
|
2 => tutorial_screen(
|
||||||
tr_hold_to_confirm,
|
tr_hold_to_confirm,
|
||||||
tr_press_and_hold,
|
TR::tutorial__press_and_hold,
|
||||||
ButtonLayout::arrow_none_htc(TR::buttons__hold_to_confirm.into()),
|
ButtonLayout::arrow_none_htc(TR::buttons__hold_to_confirm.into()),
|
||||||
ButtonActions::prev_none_next(),
|
ButtonActions::prev_none_next(),
|
||||||
),
|
),
|
||||||
3 => tutorial_screen(
|
3 => tutorial_screen(
|
||||||
tr_title_screen_scroll,
|
tr_title_screen_scroll,
|
||||||
tr_scroll_down,
|
TR::tutorial__scroll_down,
|
||||||
ButtonLayout::arrow_none_text(TR::buttons__continue.into()),
|
ButtonLayout::arrow_none_text(TR::buttons__continue.into()),
|
||||||
ButtonActions::prev_none_next(),
|
ButtonActions::prev_none_next(),
|
||||||
),
|
),
|
||||||
4 => tutorial_screen(
|
4 => tutorial_screen(
|
||||||
tr_confirm,
|
tr_confirm,
|
||||||
tr_middle_click,
|
TR::tutorial__middle_click,
|
||||||
ButtonLayout::none_armed_none(TR::buttons__confirm.into()),
|
ButtonLayout::none_armed_none(TR::buttons__confirm.into()),
|
||||||
ButtonActions::none_next_none(),
|
ButtonActions::none_next_none(),
|
||||||
),
|
),
|
||||||
5 => tutorial_screen(
|
5 => tutorial_screen(
|
||||||
tr_title_tutorial_complete,
|
tr_title_tutorial_complete,
|
||||||
tr_ready_to_use,
|
TR::tutorial__ready_to_use,
|
||||||
ButtonLayout::text_none_text(
|
ButtonLayout::text_none_text(
|
||||||
TR::buttons__again.into(),
|
TR::buttons__again.into(),
|
||||||
TR::buttons__continue.into(),
|
TR::buttons__continue.into(),
|
||||||
@ -936,7 +914,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
|||||||
),
|
),
|
||||||
6 => tutorial_screen(
|
6 => tutorial_screen(
|
||||||
tr_title_skip,
|
tr_title_skip,
|
||||||
tr_sure_you_want_skip,
|
TR::tutorial__sure_you_want_skip,
|
||||||
ButtonLayout::arrow_none_text(TR::buttons__skip.into()),
|
ButtonLayout::arrow_none_text(TR::buttons__skip.into()),
|
||||||
ButtonActions::beginning_none_cancel(),
|
ButtonActions::beginning_none_cancel(),
|
||||||
),
|
),
|
||||||
@ -976,23 +954,14 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
|||||||
|
|
||||||
let mut paragraphs_vec = ParagraphVecShort::new();
|
let mut paragraphs_vec = ParagraphVecShort::new();
|
||||||
paragraphs_vec
|
paragraphs_vec
|
||||||
.add(Paragraph::new(&theme::TEXT_BOLD, description.try_into()?))
|
.add(Paragraph::new(&theme::TEXT_BOLD, description))
|
||||||
.add(Paragraph::new(&theme::TEXT_MONO, change))
|
.add(Paragraph::new(&theme::TEXT_MONO, change))
|
||||||
.add(
|
.add(Paragraph::new(&theme::TEXT_BOLD, TR::modify_fee__transaction_fee).no_break())
|
||||||
Paragraph::new(
|
|
||||||
&theme::TEXT_BOLD,
|
|
||||||
TR::modify_fee__transaction_fee.try_into()?,
|
|
||||||
)
|
|
||||||
.no_break(),
|
|
||||||
)
|
|
||||||
.add(Paragraph::new(&theme::TEXT_MONO, total_fee_new));
|
.add(Paragraph::new(&theme::TEXT_MONO, total_fee_new));
|
||||||
|
|
||||||
if let Some(fee_rate_amount) = fee_rate_amount {
|
if let Some(fee_rate_amount) = fee_rate_amount {
|
||||||
paragraphs_vec
|
paragraphs_vec
|
||||||
.add(
|
.add(Paragraph::new(&theme::TEXT_BOLD, TR::modify_fee__fee_rate).no_break())
|
||||||
Paragraph::new(&theme::TEXT_BOLD, TR::modify_fee__fee_rate.try_into()?)
|
|
||||||
.no_break(),
|
|
||||||
)
|
|
||||||
.add(Paragraph::new(&theme::TEXT_MONO, fee_rate_amount));
|
.add(Paragraph::new(&theme::TEXT_MONO, fee_rate_amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,7 +979,7 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
|||||||
extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let verb: TString<'static> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into()?;
|
let verb: TString = kwargs.get(Qstr::MP_QSTR_verb)?.try_into()?;
|
||||||
let items: Gc<List> = kwargs.get(Qstr::MP_QSTR_items)?.try_into()?;
|
let items: Gc<List> = kwargs.get(Qstr::MP_QSTR_items)?.try_into()?;
|
||||||
|
|
||||||
// Cache the page count so that we can move `items` into the closure.
|
// Cache the page count so that we can move `items` into the closure.
|
||||||
@ -1021,7 +990,7 @@ extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs:
|
|||||||
// the need of any allocation here in Rust.
|
// the need of any allocation here in Rust.
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
let item_obj = unwrap!(items.get(page_index));
|
let item_obj = unwrap!(items.get(page_index));
|
||||||
let text = unwrap!(item_obj.try_into());
|
let text = unwrap!(TString::try_from(item_obj));
|
||||||
|
|
||||||
let (btn_layout, btn_actions) = if page_count == 1 {
|
let (btn_layout, btn_actions) = if page_count == 1 {
|
||||||
// There is only one page
|
// There is only one page
|
||||||
@ -1052,7 +1021,7 @@ extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs:
|
|||||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
|
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
|
|
||||||
Page::new(btn_layout, btn_actions, formatted)
|
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||||
};
|
};
|
||||||
|
|
||||||
let pages = FlowPages::new(get_page, page_count);
|
let pages = FlowPages::new(get_page, page_count);
|
||||||
@ -1076,7 +1045,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
// the need of any allocation here in Rust.
|
// the need of any allocation here in Rust.
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
let account_obj = unwrap!(accounts.get(page_index));
|
let account_obj = unwrap!(accounts.get(page_index));
|
||||||
let account = account_obj.try_into().unwrap_or_else(|_| "".into());
|
let account = TString::try_from(account_obj).unwrap_or_else(|_| TString::empty());
|
||||||
|
|
||||||
let (btn_layout, btn_actions) = if page_count == 1 {
|
let (btn_layout, btn_actions) = if page_count == 1 {
|
||||||
// There is only one page
|
// There is only one page
|
||||||
@ -1111,7 +1080,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
.text_bold(account);
|
.text_bold(account);
|
||||||
let formatted = FormattedText::new(ops);
|
let formatted = FormattedText::new(ops);
|
||||||
|
|
||||||
Page::new(btn_layout, btn_actions, formatted)
|
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||||
};
|
};
|
||||||
|
|
||||||
let pages = FlowPages::new(get_page, page_count);
|
let pages = FlowPages::new(get_page, page_count);
|
||||||
@ -1137,7 +1106,7 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
|
|
||||||
let btn_layout = ButtonLayout::none_armed_none(button);
|
let btn_layout = ButtonLayout::none_armed_none(button);
|
||||||
let btn_actions = ButtonActions::none_confirm_none();
|
let btn_actions = ButtonActions::none_confirm_none();
|
||||||
let mut ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL);
|
let mut ops = OpTextLayout::new(theme::TEXT_NORMAL);
|
||||||
ops = ops.alignment(geometry::Alignment::Center);
|
ops = ops.alignment(geometry::Alignment::Center);
|
||||||
if !warning.is_empty() {
|
if !warning.is_empty() {
|
||||||
ops = ops.text_bold(warning).newline();
|
ops = ops.text_bold(warning).newline();
|
||||||
@ -1146,7 +1115,7 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
ops = ops.text_normal(description);
|
ops = ops.text_normal(description);
|
||||||
}
|
}
|
||||||
let formatted = FormattedText::new(ops).vertically_centered();
|
let formatted = FormattedText::new(ops).vertically_centered();
|
||||||
Page::new(btn_layout, btn_actions, formatted)
|
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||||
};
|
};
|
||||||
let pages = FlowPages::new(get_page, 1);
|
let pages = FlowPages::new(get_page, 1);
|
||||||
let obj = LayoutObj::new(Flow::new(pages))?;
|
let obj = LayoutObj::new(Flow::new(pages))?;
|
||||||
@ -1207,24 +1176,20 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
|
|
||||||
// cached allocated translated strings that get_page can reuse
|
|
||||||
let tr_contact_support_at = TR::addr_mismatch__contact_support_at.try_into()?;
|
|
||||||
let tr_support_url = TR::addr_mismatch__support_url.try_into()?;
|
|
||||||
|
|
||||||
let get_page = move |page_index| {
|
let get_page = move |page_index| {
|
||||||
assert!(page_index == 0);
|
assert!(page_index == 0);
|
||||||
|
|
||||||
let btn_layout = ButtonLayout::arrow_none_text(TR::buttons__quit.into());
|
let btn_layout = ButtonLayout::arrow_none_text(TR::buttons__quit.into());
|
||||||
let btn_actions = ButtonActions::cancel_none_confirm();
|
let btn_actions = ButtonActions::cancel_none_confirm();
|
||||||
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL)
|
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
|
||||||
.text_bold(title)
|
.text_bold(title)
|
||||||
.newline()
|
.newline()
|
||||||
.newline_half()
|
.newline_half()
|
||||||
.text_normal(tr_contact_support_at)
|
.text_normal(TR::addr_mismatch__contact_support_at)
|
||||||
.newline()
|
.newline()
|
||||||
.text_bold(tr_support_url);
|
.text_bold(TR::addr_mismatch__support_url);
|
||||||
let formatted = FormattedText::new(ops);
|
let formatted = FormattedText::new(ops);
|
||||||
Page::new(btn_layout, btn_actions, formatted)
|
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||||
};
|
};
|
||||||
let pages = FlowPages::new(get_page, 1);
|
let pages = FlowPages::new(get_page, 1);
|
||||||
|
|
||||||
@ -1258,7 +1223,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
|
|||||||
|
|
||||||
let obj = LayoutObj::new(Frame::new(
|
let obj = LayoutObj::new(Frame::new(
|
||||||
title,
|
title,
|
||||||
ShowMore::<Paragraphs<ParagraphVecShort<StrBuffer>>>::new(
|
ShowMore::<Paragraphs<ParagraphVecShort>>::new(
|
||||||
paragraphs.into_paragraphs(),
|
paragraphs.into_paragraphs(),
|
||||||
verb_cancel,
|
verb_cancel,
|
||||||
button,
|
button,
|
||||||
@ -1302,10 +1267,9 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
|
|
||||||
// Decreasing bottom padding between paragraphs to fit one screen
|
// Decreasing bottom padding between paragraphs to fit one screen
|
||||||
let paragraphs = Paragraphs::new([
|
let paragraphs = Paragraphs::new([
|
||||||
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_rounds.try_into()?)
|
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_rounds).with_bottom_padding(2),
|
||||||
.with_bottom_padding(2),
|
|
||||||
Paragraph::new(&theme::TEXT_MONO, max_rounds),
|
Paragraph::new(&theme::TEXT_MONO, max_rounds),
|
||||||
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_mining_fee.try_into()?)
|
Paragraph::new(&theme::TEXT_BOLD, TR::coinjoin__max_mining_fee)
|
||||||
.with_bottom_padding(2)
|
.with_bottom_padding(2)
|
||||||
.no_break(),
|
.no_break(),
|
||||||
Paragraph::new(&theme::TEXT_MONO, max_feerate).with_bottom_padding(2),
|
Paragraph::new(&theme::TEXT_MONO, max_feerate).with_bottom_padding(2),
|
||||||
@ -1498,11 +1462,11 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
paragraphs
|
paragraphs
|
||||||
.add(Paragraph::new(
|
.add(Paragraph::new(
|
||||||
&theme::TEXT_NORMAL,
|
&theme::TEXT_NORMAL,
|
||||||
TR::recovery__only_first_n_letters.try_into()?,
|
TR::recovery__only_first_n_letters,
|
||||||
))
|
))
|
||||||
.add(Paragraph::new(
|
.add(Paragraph::new(
|
||||||
&theme::TEXT_NORMAL,
|
&theme::TEXT_NORMAL,
|
||||||
TR::recovery__cursor_will_change.try_into()?,
|
TR::recovery__cursor_will_change,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1578,8 +1542,7 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
.and_then(Obj::try_into_option)
|
.and_then(Obj::try_into_option)
|
||||||
.unwrap_or(None);
|
.unwrap_or(None);
|
||||||
|
|
||||||
let mut progress =
|
let mut progress = Progress::new(indeterminate, description);
|
||||||
Progress::new(indeterminate, description).with_update_description(StrBuffer::alloc);
|
|
||||||
if let Some(title) = title {
|
if let Some(title) = title {
|
||||||
progress = progress.with_title(title);
|
progress = progress.with_title(title);
|
||||||
};
|
};
|
||||||
|
@ -20,8 +20,8 @@ const MAX_XPUBS: usize = 16;
|
|||||||
|
|
||||||
pub struct AddressDetails<T> {
|
pub struct AddressDetails<T> {
|
||||||
qr_code: Frame<Qr, T>,
|
qr_code: Frame<Qr, T>,
|
||||||
details: Frame<Paragraphs<ParagraphVecShort<StrBuffer>>, T>,
|
details: Frame<Paragraphs<ParagraphVecShort<'static>>, T>,
|
||||||
xpub_view: Frame<Paragraphs<Paragraph<T>>, T>,
|
xpub_view: Frame<Paragraphs<Paragraph<'static>>, T>,
|
||||||
xpubs: Vec<(T, T), MAX_XPUBS>,
|
xpubs: Vec<(T, T), MAX_XPUBS>,
|
||||||
xpub_page_count: Vec<u8, MAX_XPUBS>,
|
xpub_page_count: Vec<u8, MAX_XPUBS>,
|
||||||
current_page: usize,
|
current_page: usize,
|
||||||
@ -46,14 +46,14 @@ where
|
|||||||
if let Some(a) = account {
|
if let Some(a) = account {
|
||||||
para.add(Paragraph::new(
|
para.add(Paragraph::new(
|
||||||
&theme::TEXT_NORMAL,
|
&theme::TEXT_NORMAL,
|
||||||
TR::words__account_colon.try_into()?,
|
TR::words__account_colon,
|
||||||
));
|
));
|
||||||
para.add(Paragraph::new(&theme::TEXT_MONO, a));
|
para.add(Paragraph::new(&theme::TEXT_MONO, a));
|
||||||
}
|
}
|
||||||
if let Some(p) = path {
|
if let Some(p) = path {
|
||||||
para.add(Paragraph::new(
|
para.add(Paragraph::new(
|
||||||
&theme::TEXT_NORMAL,
|
&theme::TEXT_NORMAL,
|
||||||
TR::address_details__derivation_path.try_into()?,
|
TR::address_details__derivation_path,
|
||||||
));
|
));
|
||||||
para.add(Paragraph::new(&theme::TEXT_MONO, p));
|
para.add(Paragraph::new(&theme::TEXT_MONO, p));
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ where
|
|||||||
xpub_view: Frame::left_aligned(
|
xpub_view: Frame::left_aligned(
|
||||||
theme::label_title(),
|
theme::label_title(),
|
||||||
" \n ".into(),
|
" \n ".into(),
|
||||||
Paragraph::new(&theme::TEXT_MONO, "".into()).into_paragraphs(),
|
Paragraph::new(&theme::TEXT_MONO, "").into_paragraphs(),
|
||||||
)
|
)
|
||||||
.with_cancel_button()
|
.with_cancel_button()
|
||||||
.with_border(theme::borders_horizontal_scroll()),
|
.with_border(theme::borders_horizontal_scroll()),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::StringType,
|
strutil::TString,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
image::BlendedImage,
|
image::BlendedImage,
|
||||||
@ -91,18 +91,17 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IconDialog<T, U> {
|
pub struct IconDialog<U> {
|
||||||
image: Child<BlendedImage>,
|
image: Child<BlendedImage>,
|
||||||
paragraphs: Paragraphs<ParagraphVecShort<T>>,
|
paragraphs: Paragraphs<ParagraphVecShort<'static>>,
|
||||||
controls: Child<U>,
|
controls: Child<U>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> IconDialog<T, U>
|
impl<U> IconDialog<U>
|
||||||
where
|
where
|
||||||
T: StringType,
|
|
||||||
U: Component,
|
U: Component,
|
||||||
{
|
{
|
||||||
pub fn new(icon: BlendedImage, title: T, controls: U) -> Self {
|
pub fn new(icon: BlendedImage, title: impl Into<TString<'static>>, controls: U) -> Self {
|
||||||
Self {
|
Self {
|
||||||
image: Child::new(icon),
|
image: Child::new(icon),
|
||||||
paragraphs: Paragraphs::new(ParagraphVecShort::from_iter([Paragraph::new(
|
paragraphs: Paragraphs::new(ParagraphVecShort::from_iter([Paragraph::new(
|
||||||
@ -119,26 +118,26 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_paragraph(mut self, para: Paragraph<T>) -> Self {
|
pub fn with_paragraph(mut self, para: Paragraph<'static>) -> Self {
|
||||||
if !para.content().as_ref().is_empty() {
|
if !para.content().is_empty() {
|
||||||
self.paragraphs.inner_mut().add(para);
|
self.paragraphs.inner_mut().add(para);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_text(self, style: &'static TextStyle, text: T) -> Self {
|
pub fn with_text(self, style: &'static TextStyle, text: impl Into<TString<'static>>) -> Self {
|
||||||
self.with_paragraph(Paragraph::new(style, text).centered())
|
self.with_paragraph(Paragraph::new(style, text).centered())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_description(self, description: T) -> Self {
|
pub fn with_description(self, description: impl Into<TString<'static>>) -> Self {
|
||||||
self.with_text(&theme::TEXT_NORMAL_OFF_WHITE, description)
|
self.with_text(&theme::TEXT_NORMAL_OFF_WHITE, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_value(self, value: T) -> Self {
|
pub fn with_value(self, value: impl Into<TString<'static>>) -> Self {
|
||||||
self.with_text(&theme::TEXT_MONO, value)
|
self.with_text(&theme::TEXT_MONO, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_shares(lines: [T; 4], controls: U) -> Self {
|
pub fn new_shares(lines: [impl Into<TString<'static>>; 4], controls: U) -> Self {
|
||||||
let [l0, l1, l2, l3] = lines;
|
let [l0, l1, l2, l3] = lines;
|
||||||
Self {
|
Self {
|
||||||
image: Child::new(BlendedImage::new(
|
image: Child::new(BlendedImage::new(
|
||||||
@ -165,9 +164,8 @@ where
|
|||||||
pub const VALUE_SPACE: i16 = 5;
|
pub const VALUE_SPACE: i16 = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Component for IconDialog<T, U>
|
impl<U> Component for IconDialog<U>
|
||||||
where
|
where
|
||||||
T: StringType,
|
|
||||||
U: Component,
|
U: Component,
|
||||||
{
|
{
|
||||||
type Msg = DialogMsg<Never, U::Msg>;
|
type Msg = DialogMsg<Never, U::Msg>;
|
||||||
@ -207,9 +205,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T, U> crate::trace::Trace for IconDialog<T, U>
|
impl<U> crate::trace::Trace for IconDialog<U>
|
||||||
where
|
where
|
||||||
T: StringType,
|
|
||||||
U: crate::trace::Trace,
|
U: crate::trace::Trace,
|
||||||
{
|
{
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
@ -29,7 +29,7 @@ where
|
|||||||
area: Rect,
|
area: Rect,
|
||||||
description_func: F,
|
description_func: F,
|
||||||
input: Child<NumberInput>,
|
input: Child<NumberInput>,
|
||||||
paragraphs: Child<Paragraphs<Paragraph<T>>>,
|
paragraphs: Child<Paragraphs<Paragraph<'static>>>,
|
||||||
paragraphs_pad: Pad,
|
paragraphs_pad: Pad,
|
||||||
info_button: Child<Button<StrBuffer>>,
|
info_button: Child<Button<StrBuffer>>,
|
||||||
confirm_button: Child<Button<StrBuffer>>,
|
confirm_button: Child<Button<StrBuffer>>,
|
||||||
|
@ -485,16 +485,13 @@ impl PageLayout {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde_json;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
strutil::SkipPrefix,
|
|
||||||
trace::tests::trace,
|
trace::tests::trace,
|
||||||
ui::{
|
ui::{
|
||||||
component::text::paragraphs::{Paragraph, Paragraphs},
|
component::text::paragraphs::{Paragraph, Paragraphs},
|
||||||
event::TouchEvent,
|
event::TouchEvent,
|
||||||
geometry::Point,
|
geometry::Point,
|
||||||
model_tt::{constant, theme},
|
model_tt::constant,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -502,12 +499,6 @@ mod tests {
|
|||||||
|
|
||||||
const SCREEN: Rect = constant::screen().inset(theme::borders());
|
const SCREEN: Rect = constant::screen().inset(theme::borders());
|
||||||
|
|
||||||
impl SkipPrefix for &str {
|
|
||||||
fn skip_prefix(&self, chars: usize) -> Self {
|
|
||||||
&self[chars..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn swipe(component: &mut impl Component, points: &[(i16, i16)]) {
|
fn swipe(component: &mut impl Component, points: &[(i16, i16)]) {
|
||||||
let last = points.len().saturating_sub(1);
|
let last = points.len().saturating_sub(1);
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
@ -538,7 +529,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn paragraphs_empty() {
|
fn paragraphs_empty() {
|
||||||
let mut page = ButtonPage::<_, &'static str>::new(
|
let mut page = ButtonPage::<_, &'static str>::new(
|
||||||
Paragraphs::<[Paragraph<&'static str>; 0]>::new([]),
|
Paragraphs::<[Paragraph<'static>; 0]>::new([]),
|
||||||
theme::BG,
|
theme::BG,
|
||||||
);
|
);
|
||||||
page.place(SCREEN);
|
page.place(SCREEN);
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
strutil::{StringType, TString},
|
||||||
strutil::StringType,
|
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
base::ComponentExt,
|
base::ComponentExt,
|
||||||
@ -24,9 +23,8 @@ pub struct Progress<T> {
|
|||||||
value: u16,
|
value: u16,
|
||||||
loader_y_offset: i16,
|
loader_y_offset: i16,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
description: Child<Paragraphs<Paragraph<T>>>,
|
description: Child<Paragraphs<Paragraph<'static>>>,
|
||||||
description_pad: Pad,
|
description_pad: Pad,
|
||||||
update_description: fn(&str) -> Result<T, Error>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Progress<T>
|
impl<T> Progress<T>
|
||||||
@ -35,12 +33,7 @@ where
|
|||||||
{
|
{
|
||||||
const AREA: Rect = constant::screen().inset(theme::borders());
|
const AREA: Rect = constant::screen().inset(theme::borders());
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(title: T, indeterminate: bool, description: TString<'static>) -> Self {
|
||||||
title: T,
|
|
||||||
indeterminate: bool,
|
|
||||||
description: T,
|
|
||||||
update_description: fn(&str) -> Result<T, Error>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
title: Label::centered(title, theme::label_progress()).into_child(),
|
title: Label::centered(title, theme::label_progress()).into_child(),
|
||||||
value: 0,
|
value: 0,
|
||||||
@ -51,7 +44,6 @@ where
|
|||||||
)
|
)
|
||||||
.into_child(),
|
.into_child(),
|
||||||
description_pad: Pad::with_background(theme::BG),
|
description_pad: Pad::with_background(theme::BG),
|
||||||
update_description,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,10 +60,7 @@ where
|
|||||||
.inner()
|
.inner()
|
||||||
.inner()
|
.inner()
|
||||||
.content()
|
.content()
|
||||||
.as_ref()
|
.map(|t| t.chars().filter(|c| *c == '\n').count() as i16);
|
||||||
.chars()
|
|
||||||
.filter(|c| *c == '\n')
|
|
||||||
.count() as i16;
|
|
||||||
let (title, rest) = Self::AREA.split_top(self.title.inner().max_size().y);
|
let (title, rest) = Self::AREA.split_top(self.title.inner().max_size().y);
|
||||||
let (loader, description) =
|
let (loader, description) =
|
||||||
rest.split_bottom(Font::NORMAL.line_height() * description_lines);
|
rest.split_bottom(Font::NORMAL.line_height() * description_lines);
|
||||||
@ -90,8 +79,7 @@ where
|
|||||||
ctx.request_paint();
|
ctx.request_paint();
|
||||||
}
|
}
|
||||||
self.description.mutate(ctx, |ctx, para| {
|
self.description.mutate(ctx, |ctx, para| {
|
||||||
if para.inner_mut().content().as_ref() != new_description {
|
if para.inner_mut().content() != &new_description {
|
||||||
let new_description = unwrap!((self.update_description)(new_description));
|
|
||||||
para.inner_mut().update(new_description);
|
para.inner_mut().update(new_description);
|
||||||
para.change_page(0); // Recompute bounding box.
|
para.change_page(0); // Recompute bounding box.
|
||||||
ctx.request_paint();
|
ctx.request_paint();
|
||||||
|
@ -128,9 +128,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> ComponentMsgObj for IconDialog<T, U>
|
impl<U> ComponentMsgObj for IconDialog<U>
|
||||||
where
|
where
|
||||||
T: StringType,
|
|
||||||
U: Component,
|
U: Component,
|
||||||
<U as Component>::Msg: TryInto<Obj, Error = Error>,
|
<U as Component>::Msg: TryInto<Obj, Error = Error>,
|
||||||
{
|
{
|
||||||
@ -223,27 +222,24 @@ where
|
|||||||
// Clippy/compiler complains about conflicting implementations
|
// Clippy/compiler complains about conflicting implementations
|
||||||
// TODO move the common impls to a common module
|
// TODO move the common impls to a common module
|
||||||
#[cfg(not(feature = "clippy"))]
|
#[cfg(not(feature = "clippy"))]
|
||||||
impl<T> ComponentMsgObj for Paragraphs<T>
|
impl<'a, T> ComponentMsgObj for Paragraphs<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ComponentMsgObj for FormattedText<T>
|
impl ComponentMsgObj for FormattedText {
|
||||||
where
|
|
||||||
T: StringType + Clone,
|
|
||||||
{
|
|
||||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ComponentMsgObj for Checklist<T>
|
impl<'a, T> ComponentMsgObj for Checklist<T>
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
{
|
{
|
||||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
@ -298,10 +294,9 @@ impl ComponentMsgObj for Lockscreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S> ComponentMsgObj for (GridPlaced<Paragraphs<T>>, GridPlaced<FormattedText<S>>)
|
impl<'a, T> ComponentMsgObj for (GridPlaced<Paragraphs<T>>, GridPlaced<FormattedText>)
|
||||||
where
|
where
|
||||||
T: ParagraphSource,
|
T: ParagraphSource<'a>,
|
||||||
S: StringType + Clone,
|
|
||||||
{
|
{
|
||||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
@ -429,7 +424,7 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m
|
|||||||
let mut ops = OpTextLayout::new(theme::TEXT_NORMAL);
|
let mut ops = OpTextLayout::new(theme::TEXT_NORMAL);
|
||||||
for item in IterBuf::new().try_iterate(items)? {
|
for item in IterBuf::new().try_iterate(items)? {
|
||||||
if item.is_str() {
|
if item.is_str() {
|
||||||
ops = ops.text_normal(item.try_into()?)
|
ops = ops.text_normal(StrBuffer::try_from(item)?)
|
||||||
} else {
|
} else {
|
||||||
let [emphasis, text]: [Obj; 2] = util::iter_into_array(item)?;
|
let [emphasis, text]: [Obj; 2] = util::iter_into_array(item)?;
|
||||||
let text: StrBuffer = text.try_into()?;
|
let text: StrBuffer = text.try_into()?;
|
||||||
@ -688,11 +683,10 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
|
|||||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||||
|
|
||||||
let par_array: [Paragraph<StrBuffer>; 3] = [
|
let par_array: [Paragraph<'static>; 3] = [
|
||||||
Paragraph::new(&theme::TEXT_NORMAL, TR::reset__by_continuing.try_into()?)
|
Paragraph::new(&theme::TEXT_NORMAL, TR::reset__by_continuing).with_bottom_padding(17), /* simulating a carriage return */
|
||||||
.with_bottom_padding(17), // simulating a carriage return
|
Paragraph::new(&theme::TEXT_NORMAL, TR::reset__more_info_at),
|
||||||
Paragraph::new(&theme::TEXT_NORMAL, TR::reset__more_info_at.try_into()?),
|
Paragraph::new(&theme::TEXT_DEMIBOLD, TR::reset__tos_link),
|
||||||
Paragraph::new(&theme::TEXT_DEMIBOLD, TR::reset__tos_link.try_into()?),
|
|
||||||
];
|
];
|
||||||
let paragraphs = Paragraphs::new(par_array);
|
let paragraphs = Paragraphs::new(par_array);
|
||||||
let buttons = Button::cancel_confirm(
|
let buttons = Button::cancel_confirm(
|
||||||
@ -855,18 +849,15 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
|
|||||||
let amount_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_new)?.try_into()?;
|
let amount_new: StrBuffer = kwargs.get(Qstr::MP_QSTR_amount_new)?.try_into()?;
|
||||||
|
|
||||||
let description = if sign < 0 {
|
let description = if sign < 0 {
|
||||||
TR::modify_amount__decrease_amount.try_into()?
|
TR::modify_amount__decrease_amount
|
||||||
} else {
|
} else {
|
||||||
TR::modify_amount__increase_amount.try_into()?
|
TR::modify_amount__increase_amount
|
||||||
};
|
};
|
||||||
|
|
||||||
let paragraphs = Paragraphs::new([
|
let paragraphs = Paragraphs::new([
|
||||||
Paragraph::new(&theme::TEXT_NORMAL, description),
|
Paragraph::new(&theme::TEXT_NORMAL, description),
|
||||||
Paragraph::new(&theme::TEXT_MONO, amount_change),
|
Paragraph::new(&theme::TEXT_MONO, amount_change),
|
||||||
Paragraph::new(
|
Paragraph::new(&theme::TEXT_NORMAL, TR::modify_amount__new_amount),
|
||||||
&theme::TEXT_NORMAL,
|
|
||||||
TR::modify_amount__new_amount.try_into()?,
|
|
||||||
),
|
|
||||||
Paragraph::new(&theme::TEXT_MONO, amount_new),
|
Paragraph::new(&theme::TEXT_MONO, amount_new),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -891,19 +882,19 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
|||||||
|
|
||||||
let (description, change, total_label) = match sign {
|
let (description, change, total_label) = match sign {
|
||||||
s if s < 0 => (
|
s if s < 0 => (
|
||||||
TR::modify_fee__decrease_fee.try_into()?,
|
TR::modify_fee__decrease_fee,
|
||||||
user_fee_change,
|
user_fee_change,
|
||||||
TR::modify_fee__new_transaction_fee.try_into()?,
|
TR::modify_fee__new_transaction_fee,
|
||||||
),
|
),
|
||||||
s if s > 0 => (
|
s if s > 0 => (
|
||||||
TR::modify_fee__increase_fee.try_into()?,
|
TR::modify_fee__increase_fee,
|
||||||
user_fee_change,
|
user_fee_change,
|
||||||
TR::modify_fee__new_transaction_fee.try_into()?,
|
TR::modify_fee__new_transaction_fee,
|
||||||
),
|
),
|
||||||
_ => (
|
_ => (
|
||||||
TR::modify_fee__no_change.try_into()?,
|
TR::modify_fee__no_change,
|
||||||
StrBuffer::empty(),
|
StrBuffer::empty(),
|
||||||
TR::modify_fee__transaction_fee.try_into()?,
|
TR::modify_fee__transaction_fee,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1240,12 +1231,9 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
let max_feerate: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?;
|
let max_feerate: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?;
|
||||||
|
|
||||||
let paragraphs = Paragraphs::new([
|
let paragraphs = Paragraphs::new([
|
||||||
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_rounds.try_into()?),
|
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_rounds),
|
||||||
Paragraph::new(&theme::TEXT_MONO, max_rounds),
|
Paragraph::new(&theme::TEXT_MONO, max_rounds),
|
||||||
Paragraph::new(
|
Paragraph::new(&theme::TEXT_NORMAL, TR::coinjoin__max_mining_fee),
|
||||||
&theme::TEXT_NORMAL,
|
|
||||||
TR::coinjoin__max_mining_fee.try_into()?,
|
|
||||||
),
|
|
||||||
Paragraph::new(&theme::TEXT_MONO, max_feerate),
|
Paragraph::new(&theme::TEXT_MONO, max_feerate),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -1482,9 +1470,9 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
|
|||||||
TR::recovery__title.try_into()?
|
TR::recovery__title.try_into()?
|
||||||
};
|
};
|
||||||
|
|
||||||
let paragraphs = Paragraphs::new(Paragraph::<StrBuffer>::new(
|
let paragraphs = Paragraphs::new(Paragraph::new(
|
||||||
&theme::TEXT_DEMIBOLD,
|
&theme::TEXT_DEMIBOLD,
|
||||||
TR::recovery__select_num_of_words.try_into()?,
|
TR::recovery__select_num_of_words,
|
||||||
));
|
));
|
||||||
|
|
||||||
let obj = LayoutObj::new(Frame::left_aligned(
|
let obj = LayoutObj::new(Frame::left_aligned(
|
||||||
@ -1562,12 +1550,7 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
|||||||
|
|
||||||
// Description updates are received as &str and we need to provide a way to
|
// Description updates are received as &str and we need to provide a way to
|
||||||
// convert them to StrBuffer.
|
// convert them to StrBuffer.
|
||||||
let obj = LayoutObj::new(Progress::new(
|
let obj = LayoutObj::new(Progress::new(title, indeterminate, description.into()))?;
|
||||||
title,
|
|
||||||
indeterminate,
|
|
||||||
description,
|
|
||||||
StrBuffer::alloc,
|
|
||||||
))?;
|
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||||
|
Loading…
Reference in New Issue
Block a user