fix(core/ui): Model T style update
[no changelog]
BIN
core/assets/arrow-right16.png
Normal file
After Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 6.4 KiB |
BIN
core/assets/bg-back52.png
Normal file
After Width: | Height: | Size: 356 B |
Before Width: | Height: | Size: 2.0 KiB |
BIN
core/assets/caret-down24.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
core/assets/caret-left24.png
Normal file
After Width: | Height: | Size: 135 B |
BIN
core/assets/caret-right24.png
Normal file
After Width: | Height: | Size: 134 B |
BIN
core/assets/caret-up24.png
Normal file
After Width: | Height: | Size: 125 B |
Before Width: | Height: | Size: 1.3 KiB |
BIN
core/assets/check16.png
Normal file
After Width: | Height: | Size: 129 B |
BIN
core/assets/check24.png
Normal file
After Width: | Height: | Size: 356 B |
Before Width: | Height: | Size: 327 B |
Before Width: | Height: | Size: 8.7 KiB |
BIN
core/assets/circle48.png
Normal file
After Width: | Height: | Size: 385 B |
BIN
core/assets/coinjoin16.png
Normal file
After Width: | Height: | Size: 209 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.1 KiB |
BIN
core/assets/fg-check48.png
Normal file
After Width: | Height: | Size: 168 B |
BIN
core/assets/fg-error48.png
Normal file
After Width: | Height: | Size: 166 B |
BIN
core/assets/fg-info48.png
Normal file
After Width: | Height: | Size: 151 B |
BIN
core/assets/fg-user48.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
core/assets/fg-warning48.png
Normal file
After Width: | Height: | Size: 157 B |
Before Width: | Height: | Size: 266 B |
Before Width: | Height: | Size: 1.9 KiB |
BIN
core/assets/info32.png
Normal file
After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 182 B After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 468 B |
BIN
core/assets/octagon48.png
Normal file
After Width: | Height: | Size: 227 B |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 383 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 5.9 KiB |
BIN
core/assets/warning16.png
Normal file
After Width: | Height: | Size: 188 B |
BIN
core/assets/x24.png
Normal file
After Width: | Height: | Size: 365 B |
BIN
core/assets/x32.png
Normal file
After Width: | Height: | Size: 142 B |
@ -27,7 +27,7 @@
|
||||
|
||||
#define BUFFER_PIXELS DISPLAY_RESX
|
||||
|
||||
#define TEXT_BUFFER_HEIGHT 32
|
||||
#define TEXT_BUFFER_HEIGHT 36
|
||||
|
||||
#if TEXT_BUFFER_HEIGHT < FONT_MAX_HEIGHT
|
||||
#error Text buffer height is too small, please adjust to match used fonts
|
||||
|
@ -149,7 +149,7 @@ void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b,
|
||||
|
||||
void display_bar_radius_buffer(int x, int y, int w, int h, uint8_t r,
|
||||
buffer_text_t *buffer) {
|
||||
if (h > 32) {
|
||||
if (h > TEXT_BUFFER_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
if (r != 2 && r != 4 && r != 8 && r != 16) {
|
||||
|
@ -29,6 +29,7 @@
|
||||
/// FONT_MONO: int # id of monospace font
|
||||
/// FONT_NORMAL: int # id of normal-width font
|
||||
/// FONT_BOLD: int # id of bold-width font
|
||||
/// FONT_DEMIBOLD: int # id of demibold font
|
||||
/// TOIF_FULL_COLOR_BE: int # full color big endian TOI image
|
||||
/// TOIF_FULL_COLOR_LE: int # full color little endian TOI image
|
||||
/// TOIF_GRAYSCALE_EH: int # grayscale even high TOI image
|
||||
@ -621,6 +622,7 @@ STATIC const mp_rom_map_elem_t mod_trezorui_Display_locals_dict_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(DISPLAY_RESY)},
|
||||
{MP_ROM_QSTR(MP_QSTR_FONT_NORMAL), MP_ROM_INT(FONT_NORMAL)},
|
||||
{MP_ROM_QSTR(MP_QSTR_FONT_BOLD), MP_ROM_INT(FONT_BOLD)},
|
||||
{MP_ROM_QSTR(MP_QSTR_FONT_DEMIBOLD), MP_ROM_INT(FONT_DEMIBOLD)},
|
||||
{MP_ROM_QSTR(MP_QSTR_FONT_MONO), MP_ROM_INT(FONT_MONO)},
|
||||
{MP_ROM_QSTR(MP_QSTR_TOIF_FULL_COLOR_BE), MP_ROM_INT(TOIF_FULL_COLOR_BE)},
|
||||
{MP_ROM_QSTR(MP_QSTR_TOIF_FULL_COLOR_LE), MP_ROM_INT(TOIF_FULL_COLOR_LE)},
|
||||
|
@ -26,7 +26,7 @@ pub use maybe::Maybe;
|
||||
pub use pad::Pad;
|
||||
pub use paginated::{AuxPageMsg, PageMsg, Paginate};
|
||||
pub use painter::Painter;
|
||||
pub use placed::{FixedHeightBar, Floating, GridPlaced, VSplit};
|
||||
pub use placed::{FixedHeightBar, Floating, GridPlaced, Split};
|
||||
pub use qr_code::Qr;
|
||||
pub use text::{
|
||||
formatted::FormattedText,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx},
|
||||
geometry::{Alignment, Alignment2D, Grid, GridCellSpan, Insets, Offset, Rect, TOP_RIGHT},
|
||||
geometry::{Alignment, Alignment2D, Axis, Grid, GridCellSpan, Insets, Offset, Rect, TOP_RIGHT},
|
||||
};
|
||||
|
||||
pub struct GridPlaced<T> {
|
||||
@ -195,25 +195,35 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VSplit<T, U> {
|
||||
pub struct Split<T, U> {
|
||||
first: T,
|
||||
second: U,
|
||||
width: i16,
|
||||
axis: Axis,
|
||||
size: i16,
|
||||
spacing: i16,
|
||||
}
|
||||
|
||||
impl<T, U> VSplit<T, U> {
|
||||
pub const fn new(width: i16, spacing: i16, first: T, second: U) -> Self {
|
||||
impl<T, U> Split<T, U> {
|
||||
pub const fn new(axis: Axis, size: i16, spacing: i16, first: T, second: U) -> Self {
|
||||
Self {
|
||||
first,
|
||||
second,
|
||||
width,
|
||||
axis,
|
||||
size,
|
||||
spacing,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn vertical(size: i16, spacing: i16, first: T, second: U) -> Self {
|
||||
Self::new(Axis::Vertical, size, spacing, first, second)
|
||||
}
|
||||
|
||||
pub const fn horizontal(size: i16, spacing: i16, first: T, second: U) -> Self {
|
||||
Self::new(Axis::Horizontal, size, spacing, first, second)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, T, U> Component for VSplit<T, U>
|
||||
impl<M, T, U> Component for Split<T, U>
|
||||
where
|
||||
T: Component<Msg = M>,
|
||||
U: Component<Msg = M>,
|
||||
@ -221,10 +231,26 @@ where
|
||||
type Msg = M;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let (left, right) = bounds.split_left(self.width);
|
||||
let right = right.inset(Insets::left(self.spacing));
|
||||
self.first.place(left);
|
||||
self.second.place(right);
|
||||
let size = if self.size == 0 {
|
||||
(bounds.size().axis(self.axis.cross()) - self.spacing) / 2
|
||||
} else {
|
||||
self.size
|
||||
};
|
||||
let (first, second) = match self.axis {
|
||||
Axis::Vertical if size > 0 => bounds.split_left(size),
|
||||
Axis::Vertical => bounds.split_right(-size),
|
||||
Axis::Horizontal if size > 0 => bounds.split_top(size),
|
||||
Axis::Horizontal => bounds.split_bottom(-size),
|
||||
};
|
||||
let (first, second) = match self.axis {
|
||||
Axis::Vertical if size > 0 => (first, second.inset(Insets::left(self.spacing))),
|
||||
Axis::Vertical => (first.inset(Insets::right(self.spacing)), second),
|
||||
Axis::Horizontal if size > 0 => (first, second.inset(Insets::top(self.spacing))),
|
||||
Axis::Horizontal => (first.inset(Insets::bottom(self.spacing)), second),
|
||||
};
|
||||
|
||||
self.first.place(first);
|
||||
self.second.place(second);
|
||||
bounds
|
||||
}
|
||||
|
||||
@ -241,13 +267,13 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for VSplit<T, U>
|
||||
impl<T, U> crate::trace::Trace for Split<T, U>
|
||||
where
|
||||
T: Component + crate::trace::Trace,
|
||||
U: Component + crate::trace::Trace,
|
||||
{
|
||||
fn trace(&self, d: &mut dyn crate::trace::Tracer) {
|
||||
d.open("VSplit");
|
||||
d.open("Split");
|
||||
d.field("first", &self.first);
|
||||
d.field("second", &self.second);
|
||||
d.close();
|
||||
|
@ -56,8 +56,8 @@ pub const TITLE_AREA_START_Y: i16 = 8;
|
||||
pub const BUTTON_AREA_START: i16 = 188;
|
||||
|
||||
// UI icons.
|
||||
pub const ICON_CANCEL: &[u8] = include_res!("model_tt/res/cancel.toif");
|
||||
pub const ICON_CONFIRM: &[u8] = include_res!("model_tt/res/confirm.toif");
|
||||
pub const ICON_CANCEL: &[u8] = include_res!("model_tt/res/x24.toif");
|
||||
pub const ICON_CONFIRM: &[u8] = include_res!("model_tt/res/check24.toif");
|
||||
|
||||
// BLD icons
|
||||
pub const CLOSE: &[u8] = include_res!("model_tt/res/close.toif");
|
||||
|
@ -15,24 +15,30 @@ use crate::{
|
||||
|
||||
use super::{theme, Frame};
|
||||
|
||||
const MAX_XPUBS: usize = 16;
|
||||
|
||||
pub struct AddressDetails<T> {
|
||||
qr_code: Frame<Qr, T>,
|
||||
details: Frame<Paragraphs<ParagraphVecShort<T>>, T>,
|
||||
xpub_view: Frame<Paragraphs<Paragraph<T>>, T>,
|
||||
xpubs: Vec<(T, T), 16>,
|
||||
xpubs: Vec<(T, T), MAX_XPUBS>,
|
||||
xpub_page_count: Vec<u8, MAX_XPUBS>,
|
||||
current_page: usize,
|
||||
}
|
||||
|
||||
impl<T> AddressDetails<T>
|
||||
where
|
||||
T: ParagraphStrType + From<&'static str>,
|
||||
T: ParagraphStrType,
|
||||
{
|
||||
pub fn new(
|
||||
qr_address: T,
|
||||
case_sensitive: bool,
|
||||
account: Option<T>,
|
||||
path: Option<T>,
|
||||
) -> Result<Self, Error> {
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
T: From<&'static str>,
|
||||
{
|
||||
let mut para = ParagraphVecShort::new();
|
||||
if let Some(a) = account {
|
||||
para.add(Paragraph::new(&theme::TEXT_NORMAL, "Account:".into()));
|
||||
@ -61,10 +67,11 @@ where
|
||||
xpub_view: Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
" \n ".into(),
|
||||
Paragraph::new(&theme::TEXT_XPUB, "".into()).into_paragraphs(),
|
||||
Paragraph::new(&theme::TEXT_MONO, "".into()).into_paragraphs(),
|
||||
)
|
||||
.with_border(theme::borders_horizontal_scroll()),
|
||||
xpubs: Vec::new(),
|
||||
xpub_page_count: Vec::new(),
|
||||
current_page: 0,
|
||||
};
|
||||
Ok(result)
|
||||
@ -75,6 +82,41 @@ where
|
||||
.push((title, xpub))
|
||||
.map_err(|_| Error::OutOfRange)
|
||||
}
|
||||
|
||||
fn switch_xpub(&mut self, i: usize, page: usize) -> usize
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
// Context is needed for updating child so that it can request repaint. In this
|
||||
// case the parent component that handles paging always requests complete
|
||||
// repaint after page change so we can use a dummy context here.
|
||||
let mut dummy_ctx = EventCtx::new();
|
||||
self.xpub_view
|
||||
.update_title(&mut dummy_ctx, self.xpubs[i].0.clone());
|
||||
self.xpub_view.update_content(&mut dummy_ctx, |p| {
|
||||
p.inner_mut().update(self.xpubs[i].1.clone());
|
||||
let npages = p.page_count();
|
||||
p.change_page(page);
|
||||
npages
|
||||
})
|
||||
}
|
||||
|
||||
fn lookup(&self, scrollbar_page: usize) -> (usize, usize) {
|
||||
let mut xpub_index = 0;
|
||||
let mut xpub_page = scrollbar_page;
|
||||
for page_count in self.xpub_page_count.iter().map(|pc| {
|
||||
let upc: usize = (*pc).into();
|
||||
upc
|
||||
}) {
|
||||
if page_count <= xpub_page {
|
||||
xpub_page -= page_count;
|
||||
xpub_index += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(xpub_index, xpub_page)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Paginate for AddressDetails<T>
|
||||
@ -82,30 +124,23 @@ where
|
||||
T: ParagraphStrType + Clone,
|
||||
{
|
||||
fn page_count(&mut self) -> usize {
|
||||
2 + self.xpubs.len()
|
||||
let total_xpub_pages: u8 = self.xpub_page_count.iter().copied().sum();
|
||||
2usize.saturating_add(total_xpub_pages.into())
|
||||
}
|
||||
|
||||
fn change_page(&mut self, to_page: usize) {
|
||||
self.current_page = to_page;
|
||||
if to_page > 1 {
|
||||
let i = to_page - 2;
|
||||
// Context is needed for updating child so that it can request repaint. In this
|
||||
// case the parent component that handles paging always requests complete
|
||||
// repaint after page change so we can use a dummy context here.
|
||||
let mut dummy_ctx = EventCtx::new();
|
||||
self.xpub_view
|
||||
.update_title(&mut dummy_ctx, self.xpubs[i].0.clone());
|
||||
self.xpub_view.update_content(&mut dummy_ctx, |p| {
|
||||
p.inner_mut().update(self.xpubs[i].1.clone());
|
||||
p.change_page(0)
|
||||
});
|
||||
let (xpub_index, xpub_page) = self.lookup(i);
|
||||
self.switch_xpub(xpub_index, xpub_page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for AddressDetails<T>
|
||||
where
|
||||
T: ParagraphStrType,
|
||||
T: ParagraphStrType + Clone,
|
||||
{
|
||||
type Msg = Never;
|
||||
|
||||
@ -113,6 +148,13 @@ where
|
||||
self.qr_code.place(bounds);
|
||||
self.details.place(bounds);
|
||||
self.xpub_view.place(bounds);
|
||||
|
||||
self.xpub_page_count.clear();
|
||||
for i in 0..self.xpubs.len() {
|
||||
let npages = self.switch_xpub(i, 0) as u8;
|
||||
unwrap!(self.xpub_page_count.push(npages));
|
||||
}
|
||||
|
||||
bounds
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@ use crate::{
|
||||
time::Duration,
|
||||
ui::{
|
||||
component::{
|
||||
Component, ComponentExt, Event, EventCtx, FixedHeightBar, Floating, GridPlaced, Map,
|
||||
Paginate, TimerToken, VSplit,
|
||||
Component, ComponentExt, Event, EventCtx, FixedHeightBar, Floating, Map, Paginate,
|
||||
Split, TimerToken,
|
||||
},
|
||||
display::{self, toif::Icon, Color, Font},
|
||||
event::TouchEvent,
|
||||
@ -33,7 +33,7 @@ pub struct Button<T> {
|
||||
impl<T> Button<T> {
|
||||
/// Offsets the baseline of the button text either up (negative) or down
|
||||
/// (positive).
|
||||
pub const BASELINE_OFFSET: i16 = -3;
|
||||
pub const BASELINE_OFFSET: i16 = -2;
|
||||
|
||||
pub const fn new(content: ButtonContent<T>) -> Self {
|
||||
Self {
|
||||
@ -63,16 +63,16 @@ impl<T> Button<T> {
|
||||
Self::new(ButtonContent::IconBlend(bg, fg, fg_offset))
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
pub const fn empty() -> Self {
|
||||
Self::new(ButtonContent::Empty)
|
||||
}
|
||||
|
||||
pub fn styled(mut self, styles: ButtonStyleSheet) -> Self {
|
||||
pub const fn styled(mut self, styles: ButtonStyleSheet) -> Self {
|
||||
self.styles = styles;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_expanded_touch_area(mut self, expand: Insets) -> Self {
|
||||
pub const fn with_expanded_touch_area(mut self, expand: Insets) -> Self {
|
||||
self.touch_expand = Some(expand);
|
||||
self
|
||||
}
|
||||
@ -377,7 +377,7 @@ impl<T> Button<T> {
|
||||
pub fn cancel_confirm(
|
||||
left: Button<T>,
|
||||
right: Button<T>,
|
||||
right_size_factor: usize,
|
||||
left_is_small: bool,
|
||||
) -> CancelConfirm<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
@ -386,59 +386,13 @@ impl<T> Button<T> {
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
let columns = 1 + right_size_factor;
|
||||
theme::button_bar((
|
||||
GridPlaced::new(left)
|
||||
.with_grid(1, columns)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_row_col(0, 0)
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Cancelled)
|
||||
}),
|
||||
GridPlaced::new(right)
|
||||
.with_grid(1, columns)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_from_to((0, 1), (0, right_size_factor))
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn cancel_confirm_text(
|
||||
left: Option<T>,
|
||||
right: T,
|
||||
) -> CancelConfirm<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
let (left, right_size_factor) = if let Some(verb) = left {
|
||||
(Button::with_text(verb), 1)
|
||||
let width = if left_is_small {
|
||||
theme::BUTTON_WIDTH
|
||||
} else {
|
||||
(Button::with_icon(Icon::new(theme::ICON_CANCEL)), 2)
|
||||
0
|
||||
};
|
||||
let right = Button::with_text(right).styled(theme::button_confirm());
|
||||
|
||||
Self::cancel_confirm(left, right, right_size_factor)
|
||||
}
|
||||
|
||||
pub fn cancel_confirm_square(
|
||||
left: Button<T>,
|
||||
right: Button<T>,
|
||||
) -> CancelConfirmSquare<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
theme::button_bar(VSplit::new(
|
||||
theme::BUTTON_HEIGHT,
|
||||
theme::button_bar(Split::vertical(
|
||||
width,
|
||||
theme::BUTTON_SPACING,
|
||||
left.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Cancelled)
|
||||
@ -449,6 +403,34 @@ impl<T> Button<T> {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn cancel_confirm_text(
|
||||
left: Option<T>,
|
||||
right: Option<T>,
|
||||
) -> CancelConfirm<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
let left_is_small: bool;
|
||||
|
||||
let left = if let Some(verb) = left {
|
||||
left_is_small = verb.as_ref().len() <= 4;
|
||||
Button::with_text(verb)
|
||||
} else {
|
||||
left_is_small = right.is_some();
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL))
|
||||
};
|
||||
let right = if let Some(verb) = right {
|
||||
Button::with_text(verb).styled(theme::button_confirm())
|
||||
} else {
|
||||
Button::with_icon(Icon::new(theme::ICON_CONFIRM)).styled(theme::button_confirm())
|
||||
};
|
||||
Self::cancel_confirm(left, right, left_is_small)
|
||||
}
|
||||
|
||||
pub fn cancel_info_confirm(
|
||||
confirm: T,
|
||||
info: T,
|
||||
@ -461,69 +443,29 @@ impl<T> Button<T> {
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
let right = Button::with_text(confirm).styled(theme::button_confirm());
|
||||
let top = Button::with_text(info);
|
||||
let left = Button::with_icon(Icon::new(theme::ICON_CANCEL));
|
||||
theme::button_bar_rows(
|
||||
2,
|
||||
(
|
||||
GridPlaced::new(left)
|
||||
.with_grid(2, 3)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_row_col(1, 0)
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Cancelled)
|
||||
}),
|
||||
GridPlaced::new(top)
|
||||
.with_grid(2, 3)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_from_to((0, 0), (0, 2))
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Info)
|
||||
}),
|
||||
GridPlaced::new(right)
|
||||
.with_grid(2, 3)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_from_to((1, 1), (1, 2))
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Confirmed)
|
||||
}),
|
||||
let right = Button::with_text(confirm)
|
||||
.styled(theme::button_confirm())
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Confirmed)
|
||||
});
|
||||
let top = Button::with_text(info)
|
||||
.styled(theme::button_moreinfo())
|
||||
.map(|msg| (matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Info));
|
||||
let left = Button::with_icon(Icon::new(theme::ICON_CANCEL)).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Cancelled)
|
||||
});
|
||||
let total_height = theme::BUTTON_HEIGHT + theme::BUTTON_SPACING + theme::INFO_BUTTON_HEIGHT;
|
||||
FixedHeightBar::bottom(
|
||||
Split::horizontal(
|
||||
theme::INFO_BUTTON_HEIGHT,
|
||||
theme::BUTTON_SPACING,
|
||||
top,
|
||||
Split::vertical(theme::BUTTON_WIDTH, theme::BUTTON_SPACING, left, right),
|
||||
),
|
||||
total_height,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn abort_info_enter() -> CancelInfoConfirm<
|
||||
&'static str,
|
||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||
> {
|
||||
let left = Button::with_text("ABORT").styled(theme::button_cancel());
|
||||
let middle = Button::with_text("INFO");
|
||||
let right = Button::with_text("ENTER").styled(theme::button_confirm());
|
||||
theme::button_bar((
|
||||
GridPlaced::new(left)
|
||||
.with_grid(1, 3)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_row_col(0, 0)
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Cancelled)
|
||||
}),
|
||||
GridPlaced::new(middle)
|
||||
.with_grid(1, 3)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_row_col(0, 1)
|
||||
.map(|msg| (matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Info)),
|
||||
GridPlaced::new(right)
|
||||
.with_grid(1, 3)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_row_col(0, 2)
|
||||
.map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelInfoConfirmMsg::Confirmed)
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn select_word(
|
||||
words: [T; 3],
|
||||
) -> CancelInfoConfirm<
|
||||
@ -536,38 +478,41 @@ impl<T> Button<T> {
|
||||
T: AsRef<str>,
|
||||
{
|
||||
let btn = move |i, word| {
|
||||
GridPlaced::new(Button::with_text(word))
|
||||
.with_grid(3, 1)
|
||||
.with_spacing(theme::BUTTON_SPACING)
|
||||
.with_row_col(i, 0)
|
||||
Button::with_text(word)
|
||||
.styled(theme::button_pin())
|
||||
.map(move |msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| SelectWordMsg::Selected(i))
|
||||
})
|
||||
};
|
||||
|
||||
let [top, middle, bottom] = words;
|
||||
theme::button_bar_rows(3, (btn(0, top), btn(1, middle), btn(2, bottom)))
|
||||
let total_height = 3 * theme::BUTTON_HEIGHT + 2 * theme::BUTTON_SPACING;
|
||||
FixedHeightBar::bottom(
|
||||
Split::horizontal(
|
||||
theme::BUTTON_HEIGHT,
|
||||
theme::BUTTON_SPACING,
|
||||
btn(0, top),
|
||||
Split::horizontal(
|
||||
theme::BUTTON_HEIGHT,
|
||||
theme::BUTTON_SPACING,
|
||||
btn(1, middle),
|
||||
btn(2, bottom),
|
||||
),
|
||||
),
|
||||
total_height,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type CancelConfirm<T, F0, F1> = FixedHeightBar<(
|
||||
Map<GridPlaced<Button<T>>, F0>,
|
||||
Map<GridPlaced<Button<T>>, F1>,
|
||||
)>;
|
||||
|
||||
pub enum CancelConfirmMsg {
|
||||
Cancelled,
|
||||
Confirmed,
|
||||
}
|
||||
|
||||
type CancelInfoConfirm<T, F0, F1, F2> = FixedHeightBar<(
|
||||
Map<GridPlaced<Button<T>>, F0>,
|
||||
Map<GridPlaced<Button<T>>, F1>,
|
||||
Map<GridPlaced<Button<T>>, F2>,
|
||||
)>;
|
||||
type CancelInfoConfirm<T, F0, F1, F2> =
|
||||
FixedHeightBar<Split<Map<Button<T>, F0>, Split<Map<Button<T>, F1>, Map<Button<T>, F2>>>>;
|
||||
|
||||
type CancelConfirmSquare<T, F0, F1> =
|
||||
FixedHeightBar<VSplit<Map<Button<T>, F0>, Map<Button<T>, F1>>>;
|
||||
type CancelConfirm<T, F0, F1> = FixedHeightBar<Split<Map<Button<T>, F0>, Map<Button<T>, F1>>>;
|
||||
|
||||
pub enum CancelInfoConfirmMsg {
|
||||
Cancelled,
|
||||
@ -658,7 +603,9 @@ where
|
||||
button: Floating::top_right(
|
||||
theme::CORNER_BUTTON_SIDE,
|
||||
theme::CORNER_BUTTON_SPACING,
|
||||
Button::with_icon(icon),
|
||||
Button::with_icon(icon)
|
||||
.with_expanded_touch_area(Insets::uniform(theme::CORNER_BUTTON_SPACING))
|
||||
.styled(theme::button_moreinfo()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -60,8 +60,8 @@ where
|
||||
page_swipe.allow_left = scrollbar.has_next_page();
|
||||
|
||||
Self {
|
||||
app_name: Label::new(app_name, Alignment::Center, theme::TEXT_BOLD),
|
||||
account_name: Label::new("".into(), Alignment::Center, theme::TEXT_BOLD),
|
||||
app_name: Label::new(app_name, Alignment::Center, theme::TEXT_DEMIBOLD),
|
||||
account_name: Label::new("".into(), Alignment::Center, theme::TEXT_DEMIBOLD),
|
||||
page_swipe,
|
||||
icon: Child::new(Image::new(icon_data)),
|
||||
get_account,
|
||||
|
@ -3,9 +3,8 @@ use crate::ui::{
|
||||
component::{
|
||||
base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx,
|
||||
},
|
||||
display::{self, toif::Icon, Color},
|
||||
display::{self, Color, Font},
|
||||
geometry::{Alignment, Insets, Offset, Rect},
|
||||
util::icon_text_center,
|
||||
};
|
||||
|
||||
pub struct Frame<T, U> {
|
||||
@ -55,13 +54,14 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_content<F>(&mut self, ctx: &mut EventCtx, update_fn: F)
|
||||
pub fn update_content<F, R>(&mut self, ctx: &mut EventCtx, update_fn: F) -> R
|
||||
where
|
||||
F: Fn(&mut T),
|
||||
F: Fn(&mut T) -> R,
|
||||
{
|
||||
self.content.mutate(ctx, |ctx, c| {
|
||||
update_fn(c);
|
||||
c.request_complete_repaint(ctx)
|
||||
let res = update_fn(c);
|
||||
c.request_complete_repaint(ctx);
|
||||
res
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,6 @@ where
|
||||
|
||||
pub struct NotificationFrame<T, U> {
|
||||
area: Rect,
|
||||
icon: Icon,
|
||||
title: U,
|
||||
content: Child<T>,
|
||||
}
|
||||
@ -126,15 +125,12 @@ where
|
||||
T: Component,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
const HEIGHT: i16 = 32;
|
||||
const HEIGHT: i16 = 36;
|
||||
const COLOR: Color = theme::YELLOW;
|
||||
const TEXT_OFFSET: Offset = Offset::new(1, -2);
|
||||
const ICON_SPACE: i16 = 8;
|
||||
const BORDER: i16 = 8;
|
||||
const BORDER: i16 = 6;
|
||||
|
||||
pub fn new(icon: Icon, title: U, content: T) -> Self {
|
||||
pub fn new(title: U, content: T) -> Self {
|
||||
Self {
|
||||
icon,
|
||||
title,
|
||||
area: Rect::zero(),
|
||||
content: Child::new(content),
|
||||
@ -145,22 +141,18 @@ where
|
||||
self.content.inner()
|
||||
}
|
||||
|
||||
pub fn paint_notification(area: Rect, icon: Icon, title: &str, color: Color) {
|
||||
pub fn paint_notification(area: Rect, title: &str, color: Color) {
|
||||
let (area, _) = area
|
||||
.inset(Insets::uniform(Self::BORDER))
|
||||
.split_top(Self::HEIGHT);
|
||||
let style = TextStyle {
|
||||
background_color: color,
|
||||
..theme::TEXT_BOLD
|
||||
};
|
||||
let font = Font::BOLD;
|
||||
display::rect_fill_rounded(area, color, theme::BG, 2);
|
||||
icon_text_center(
|
||||
area.center(),
|
||||
icon,
|
||||
Self::ICON_SPACE,
|
||||
display::text_center(
|
||||
area.center() + Offset::y((font.text_max_height() - font.text_baseline()) / 2),
|
||||
title,
|
||||
style,
|
||||
Self::TEXT_OFFSET,
|
||||
Font::BOLD,
|
||||
theme::FG,
|
||||
color,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -184,7 +176,7 @@ where
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
Self::paint_notification(self.area, self.icon, self.title.as_ref(), Self::COLOR);
|
||||
Self::paint_notification(self.area, self.title.as_ref(), Self::COLOR);
|
||||
self.content.paint();
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ use super::{theme, Loader, LoaderMsg};
|
||||
|
||||
const AREA: Rect = constant::screen();
|
||||
const TOP_CENTER: Point = AREA.top_center();
|
||||
const LABEL_Y: i16 = 216;
|
||||
const LABEL_Y: i16 = 222;
|
||||
const LOCKED_Y: i16 = 107;
|
||||
const TAP_Y: i16 = 134;
|
||||
const HOLD_Y: i16 = 35;
|
||||
@ -181,7 +181,7 @@ where
|
||||
if self.loader.is_animating() || self.loader.is_completely_grown(Instant::now()) {
|
||||
self.paint_loader();
|
||||
} else {
|
||||
let mut label_style = theme::TEXT_BOLD;
|
||||
let mut label_style = theme::TEXT_DEMIBOLD;
|
||||
label_style.text_color = theme::FG;
|
||||
|
||||
let text = HomescreenText {
|
||||
@ -262,10 +262,7 @@ where
|
||||
("LOCKED", "Tap to unlock")
|
||||
};
|
||||
|
||||
let mut tap_style = theme::TEXT_NORMAL;
|
||||
tap_style.text_color = theme::OFF_WHITE;
|
||||
|
||||
let mut label_style = theme::TEXT_BOLD;
|
||||
let mut label_style = theme::TEXT_DEMIBOLD;
|
||||
label_style.text_color = theme::GREY_LIGHT;
|
||||
|
||||
let texts: [HomescreenText; 3] = [
|
||||
@ -277,7 +274,7 @@ where
|
||||
},
|
||||
HomescreenText {
|
||||
text: tap,
|
||||
style: tap_style,
|
||||
style: theme::TEXT_NORMAL,
|
||||
offset: Offset::new(10, TAP_Y),
|
||||
icon: None,
|
||||
},
|
||||
|
@ -53,19 +53,20 @@ struct HomescreenTextInfo {
|
||||
pub const HOMESCREEN_IMAGE_SIZE: i16 = 240;
|
||||
|
||||
const HOMESCREEN_MAX_ICON_SIZE: i16 = 20;
|
||||
const NOTIFICATION_HEIGHT: i16 = 32;
|
||||
const NOTIFICATION_BORDER: i16 = 8;
|
||||
const NOTIFICATION_HEIGHT: i16 = 36;
|
||||
const NOTIFICATION_BORDER: i16 = 6;
|
||||
const NOTIFICATION_ICON_SPACE: i16 = 8;
|
||||
const NOTIFICATION_TEXT_OFFSET: Offset = Offset::new(1, -2);
|
||||
const TEXT_ICON_SPACE: i16 = 2;
|
||||
|
||||
const HOMESCREEN_DIM_HIEGHT: i16 = 30;
|
||||
const HOMESCREEN_DIM_START: i16 = 195;
|
||||
const HOMESCREEN_DIM: f32 = 0.85;
|
||||
const HOMESCREEN_DIM_BORDER: i16 = 20;
|
||||
const HOMESCREEN_DIM_HEIGHT: i16 = 35;
|
||||
const HOMESCREEN_DIM_START: i16 = 198;
|
||||
const HOMESCREEN_DIM: f32 = 0.65;
|
||||
const HOMESCREEN_DIM_BORDER: i16 = theme::BUTTON_SPACING;
|
||||
|
||||
const LOCKSCREEN_DIM: f32 = 0.85;
|
||||
const LOCKSCREEN_DIM: f32 = 0.55;
|
||||
const LOCKSCREEN_DIM_BG: f32 = 0.0;
|
||||
const LOCKSCREEN_DIM_ALL: bool = true;
|
||||
|
||||
const BLUR_SIZE: usize = 9;
|
||||
const BLUR_DIV: u32 =
|
||||
@ -174,14 +175,14 @@ fn homescreen_position_text(
|
||||
fn homescreen_dim_area(x: i16, y: i16) -> bool {
|
||||
y >= HOMESCREEN_DIM_START
|
||||
&& (y > HOMESCREEN_DIM_START + 1
|
||||
&& y < (HOMESCREEN_DIM_START + HOMESCREEN_DIM_HIEGHT - 1)
|
||||
&& y < (HOMESCREEN_DIM_START + HOMESCREEN_DIM_HEIGHT - 1)
|
||||
&& x > HOMESCREEN_DIM_BORDER
|
||||
&& x < WIDTH - HOMESCREEN_DIM_BORDER)
|
||||
|| (y > HOMESCREEN_DIM_START
|
||||
&& y < (HOMESCREEN_DIM_START + HOMESCREEN_DIM_HIEGHT)
|
||||
&& y < (HOMESCREEN_DIM_START + HOMESCREEN_DIM_HEIGHT)
|
||||
&& x > HOMESCREEN_DIM_BORDER + 1
|
||||
&& x < WIDTH - (HOMESCREEN_DIM_BORDER + 1))
|
||||
|| ((HOMESCREEN_DIM_START..=(HOMESCREEN_DIM_START + HOMESCREEN_DIM_HIEGHT)).contains(&y)
|
||||
|| ((HOMESCREEN_DIM_START..=(HOMESCREEN_DIM_START + HOMESCREEN_DIM_HEIGHT)).contains(&y)
|
||||
&& x > HOMESCREEN_DIM_BORDER + 2
|
||||
&& x < WIDTH - (HOMESCREEN_DIM_BORDER + 2))
|
||||
}
|
||||
@ -197,7 +198,7 @@ fn homescreen_line_blurred(
|
||||
let mut img_buffer = unsafe { get_buffer_16bpp((y & 0x1) as u16, false) };
|
||||
|
||||
for x in 0..HOMESCREEN_IMAGE_SIZE {
|
||||
let c = if homescreen_dim_area(x, y) {
|
||||
let c = if LOCKSCREEN_DIM_ALL {
|
||||
let x = x as usize;
|
||||
|
||||
let coef = (65536_f32 * LOCKSCREEN_DIM) as u32;
|
||||
|
@ -3,12 +3,13 @@ use crate::ui::{
|
||||
base::ComponentExt, AuxPageMsg, Component, Event, EventCtx, Never, Pad, PageMsg, Paginate,
|
||||
},
|
||||
display::{self, Color},
|
||||
geometry::Rect,
|
||||
geometry::{Insets, Rect},
|
||||
};
|
||||
|
||||
use super::{theme, ScrollBar, Swipe, SwipeDirection};
|
||||
|
||||
const SCROLLBAR_HEIGHT: i16 = 32;
|
||||
const SCROLLBAR_HEIGHT: i16 = 18;
|
||||
const SCROLLBAR_BORDER: i16 = 4;
|
||||
|
||||
pub struct HorizontalPage<T> {
|
||||
content: T,
|
||||
@ -75,10 +76,11 @@ where
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.swipe.place(bounds);
|
||||
|
||||
let (content, scrollbar) = bounds.split_bottom(SCROLLBAR_HEIGHT);
|
||||
let (content, scrollbar) = bounds.split_bottom(SCROLLBAR_HEIGHT + SCROLLBAR_BORDER);
|
||||
self.pad.place(content);
|
||||
self.content.place(content);
|
||||
self.scrollbar.place(scrollbar);
|
||||
self.scrollbar
|
||||
.place(scrollbar.inset(Insets::bottom(SCROLLBAR_BORDER)));
|
||||
|
||||
self.scrollbar
|
||||
.set_count_and_active_page(self.content.page_count(), 0);
|
||||
|
@ -215,20 +215,21 @@ impl Bip39Input {
|
||||
{
|
||||
// Confirm button.
|
||||
self.button.enable(ctx);
|
||||
self.button.set_stylesheet(ctx, theme::button_confirm());
|
||||
self.button.set_stylesheet(ctx, theme::button_pin_confirm());
|
||||
self.button
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM)));
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_LIST_CHECK)));
|
||||
} else {
|
||||
// Auto-complete button.
|
||||
self.button.enable(ctx);
|
||||
self.button.set_stylesheet(ctx, theme::button_default());
|
||||
self.button
|
||||
.set_stylesheet(ctx, theme::button_pin_autocomplete());
|
||||
self.button
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CLICK)));
|
||||
}
|
||||
} else {
|
||||
// Disabled button.
|
||||
self.button.disable(ctx);
|
||||
self.button.set_stylesheet(ctx, theme::button_default());
|
||||
self.button.set_stylesheet(ctx, theme::button_pin());
|
||||
self.button.set_content(ctx, ButtonContent::Text(""));
|
||||
}
|
||||
}
|
||||
|
@ -43,11 +43,13 @@ where
|
||||
Icon::new(theme::ICON_BACK),
|
||||
Offset::new(30, 17),
|
||||
)
|
||||
.styled(theme::button_clear())
|
||||
.styled(theme::button_reset())
|
||||
.with_long_press(theme::ERASE_HOLD_DURATION),
|
||||
)),
|
||||
input: Child::new(Maybe::hidden(theme::BG, input)),
|
||||
keys: T::keys().map(Button::with_text).map(Child::new),
|
||||
keys: T::keys()
|
||||
.map(|t| Button::with_text(t).styled(theme::button_pin()))
|
||||
.map(Child::new),
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,8 +96,10 @@ where
|
||||
type Msg = MnemonicKeyboardMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let grid =
|
||||
Grid::new(bounds.inset(theme::borders()), 4, 3).with_spacing(theme::KEYBOARD_SPACING);
|
||||
let (_, bounds) = bounds
|
||||
.inset(theme::borders())
|
||||
.split_bottom(4 * theme::MNEMONIC_BUTTON_HEIGHT + 3 * theme::KEYBOARD_SPACING);
|
||||
let grid = Grid::new(bounds, 4, 3).with_spacing(theme::KEYBOARD_SPACING);
|
||||
let back_area = grid.row_col(0, 0);
|
||||
let input_area = grid.row_col(0, 1).union(grid.row_col(0, 3));
|
||||
|
||||
|
@ -2,13 +2,10 @@ use crate::ui::{
|
||||
component::{base::ComponentExt, Child, Component, Event, EventCtx, Never},
|
||||
display,
|
||||
display::toif::Icon,
|
||||
geometry::{Grid, Insets, Offset, Rect},
|
||||
geometry::{Grid, Offset, Rect},
|
||||
model_tt::component::{
|
||||
button::{Button, ButtonContent, ButtonMsg},
|
||||
keyboard::common::{
|
||||
paint_pending_marker, MultiTapKeyboard, TextBox, HEADER_HEIGHT, HEADER_PADDING_BOTTOM,
|
||||
HEADER_PADDING_SIDE,
|
||||
},
|
||||
keyboard::common::{paint_pending_marker, MultiTapKeyboard, TextBox},
|
||||
swipe::{Swipe, SwipeDirection},
|
||||
theme, ScrollBar,
|
||||
},
|
||||
@ -41,6 +38,7 @@ const KEYBOARD: [[&str; KEY_COUNT]; PAGE_COUNT] = [
|
||||
];
|
||||
|
||||
const MAX_LENGTH: usize = 50;
|
||||
const INPUT_AREA_HEIGHT: i16 = ScrollBar::DOT_SIZE + 9;
|
||||
|
||||
impl PassphraseKeyboard {
|
||||
pub fn new() -> Self {
|
||||
@ -59,8 +57,9 @@ impl PassphraseKeyboard {
|
||||
.initially_enabled(false)
|
||||
.with_long_press(theme::ERASE_HOLD_DURATION)
|
||||
.into_child(),
|
||||
keys: KEYBOARD[STARTING_PAGE]
|
||||
.map(|text| Child::new(Button::new(Self::key_content(text)))),
|
||||
keys: KEYBOARD[STARTING_PAGE].map(|text| {
|
||||
Child::new(Button::new(Self::key_content(text)).styled(theme::button_pin()))
|
||||
}),
|
||||
scrollbar: ScrollBar::horizontal(),
|
||||
fade: false,
|
||||
}
|
||||
@ -152,14 +151,13 @@ impl Component for PassphraseKeyboard {
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let bounds = bounds.inset(theme::borders());
|
||||
|
||||
let (input_area, key_grid_area) = bounds.split_top(HEADER_HEIGHT + HEADER_PADDING_BOTTOM);
|
||||
let (input_area, key_grid_area) =
|
||||
bounds.split_bottom(4 * theme::PIN_BUTTON_HEIGHT + 3 * theme::BUTTON_SPACING);
|
||||
|
||||
let (input_area, scroll_area) =
|
||||
input_area.split_bottom(ScrollBar::DOT_SIZE + theme::KEYBOARD_SPACING);
|
||||
let (input_area, scroll_area) = input_area.split_bottom(INPUT_AREA_HEIGHT);
|
||||
let (scroll_area, _) = scroll_area.split_top(ScrollBar::DOT_SIZE);
|
||||
let input_area = input_area.inset(Insets::sides(HEADER_PADDING_SIDE));
|
||||
|
||||
let key_grid = Grid::new(key_grid_area, 4, 3).with_spacing(theme::KEYBOARD_SPACING);
|
||||
let key_grid = Grid::new(key_grid_area, 4, 3).with_spacing(theme::BUTTON_SPACING);
|
||||
let confirm_btn_area = key_grid.cell(11);
|
||||
let back_btn_area = key_grid.cell(9);
|
||||
|
||||
|
@ -59,8 +59,8 @@ where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
// Label position fine-tuning.
|
||||
const MAJOR_OFF: Offset = Offset::y(-2);
|
||||
const MINOR_OFF: Offset = Offset::y(-1);
|
||||
const MAJOR_OFF: Offset = Offset::y(11);
|
||||
const MINOR_OFF: Offset = Offset::y(11);
|
||||
|
||||
pub fn new(
|
||||
major_prompt: T,
|
||||
@ -165,14 +165,14 @@ where
|
||||
// Prompts and PIN dots display.
|
||||
let (header, keypad) = bounds
|
||||
.inset(borders_no_top)
|
||||
.split_top(theme::borders().top + HEADER_HEIGHT + HEADER_PADDING_BOTTOM);
|
||||
.split_bottom(4 * theme::PIN_BUTTON_HEIGHT + 3 * theme::BUTTON_SPACING);
|
||||
let prompt = header.inset(HEADER_PADDING);
|
||||
// the inset -3 is a workaround for long text in "re-enter wipe code"
|
||||
let major_area = prompt.translate(Self::MAJOR_OFF).inset(Insets::right(-3));
|
||||
let minor_area = prompt.translate(Self::MINOR_OFF);
|
||||
|
||||
// Control buttons.
|
||||
let grid = Grid::new(keypad, 4, 3).with_spacing(theme::KEYBOARD_SPACING);
|
||||
let grid = Grid::new(keypad, 4, 3).with_spacing(theme::BUTTON_SPACING);
|
||||
|
||||
// Prompts and PIN dots display.
|
||||
self.textbox_pad.place(header);
|
||||
|
@ -224,13 +224,13 @@ impl Slip39Input {
|
||||
if self.final_word.is_some() {
|
||||
// Confirm button.
|
||||
self.button.enable(ctx);
|
||||
self.button.set_stylesheet(ctx, theme::button_confirm());
|
||||
self.button.set_stylesheet(ctx, theme::button_pin_confirm());
|
||||
self.button
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM)));
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_LIST_CHECK)));
|
||||
} else {
|
||||
// Disabled button.
|
||||
self.button.disable(ctx);
|
||||
self.button.set_stylesheet(ctx, theme::button_default());
|
||||
self.button.set_stylesheet(ctx, theme::button_pin());
|
||||
self.button.set_content(ctx, ButtonContent::Text(""));
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ pub enum SelectWordCountMsg {
|
||||
impl SelectWordCount {
|
||||
pub fn new() -> Self {
|
||||
SelectWordCount {
|
||||
button: LABELS.map(Button::with_text),
|
||||
button: LABELS.map(|t| Button::with_text(t).styled(theme::button_pin())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ impl Component for SelectWordCount {
|
||||
type Msg = SelectWordCountMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let (_, bounds) = bounds.split_bottom(theme::button_rows(2));
|
||||
let (_, bounds) = bounds.split_bottom(2 * theme::BUTTON_HEIGHT + theme::BUTTON_SPACING);
|
||||
let grid = Grid::new(bounds, 2, 6).with_spacing(theme::BUTTON_SPACING);
|
||||
for (btn, (x, y)) in self.button.iter_mut().zip(CELLS) {
|
||||
btn.place(grid.cells(GridCellSpan {
|
||||
|
@ -87,13 +87,12 @@ where
|
||||
theme::CONTENT_BORDER,
|
||||
));
|
||||
|
||||
let grid = Grid::new(button_area, 1, 3).with_spacing(theme::KEYBOARD_SPACING);
|
||||
let grid = Grid::new(button_area, 1, 2).with_spacing(theme::KEYBOARD_SPACING);
|
||||
self.input.place(input_area);
|
||||
self.paragraphs.place(content_area);
|
||||
self.paragraphs_pad.place(content_area);
|
||||
self.info_button.place(grid.row_col(0, 0));
|
||||
self.confirm_button
|
||||
.place(grid.row_col(0, 1).union(grid.row_col(0, 2)));
|
||||
self.confirm_button.place(grid.row_col(0, 1));
|
||||
bounds
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ where
|
||||
|
||||
impl<T> ComponentMsgObj for AddressDetails<T>
|
||||
where
|
||||
T: ParagraphStrType,
|
||||
T: ParagraphStrType + Clone,
|
||||
{
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
unreachable!();
|
||||
@ -381,7 +381,10 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
let action: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
|
||||
let description: Option<StrBuffer> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let verb: StrBuffer = kwargs.get_or(Qstr::MP_QSTR_verb, "CONFIRM".into())?;
|
||||
let verb: Option<StrBuffer> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb_cancel: Option<StrBuffer> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
@ -396,12 +399,12 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
if !reverse {
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_BOLD, action))
|
||||
.add(Paragraph::new(&theme::TEXT_DEMIBOLD, action))
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, description));
|
||||
} else {
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, description))
|
||||
.add(Paragraph::new(&theme::TEXT_BOLD, action));
|
||||
.add(Paragraph::new(&theme::TEXT_DEMIBOLD, action));
|
||||
}
|
||||
paragraphs.into_paragraphs()
|
||||
};
|
||||
@ -440,7 +443,7 @@ fn confirm_blob(
|
||||
extra: extra.unwrap_or_else(StrBuffer::empty),
|
||||
data: data.try_into()?,
|
||||
description_font: &theme::TEXT_NORMAL,
|
||||
extra_font: &theme::TEXT_BOLD,
|
||||
extra_font: &theme::TEXT_DEMIBOLD,
|
||||
data_font: &theme::TEXT_MONO,
|
||||
}
|
||||
.into_paragraphs();
|
||||
@ -452,7 +455,7 @@ fn confirm_blob(
|
||||
SwipeHoldPage::new(paragraphs, theme::BG),
|
||||
))?
|
||||
} else if let Some(verb) = verb {
|
||||
let buttons = Button::cancel_confirm_text(verb_cancel, verb);
|
||||
let buttons = Button::cancel_confirm_text(verb_cancel, Some(verb));
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
@ -499,19 +502,14 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
extra: extra.unwrap_or_else(StrBuffer::empty),
|
||||
data: data.try_into()?,
|
||||
description_font: &theme::TEXT_NORMAL,
|
||||
extra_font: &theme::TEXT_BOLD,
|
||||
extra_font: &theme::TEXT_DEMIBOLD,
|
||||
data_font: &theme::TEXT_MONO,
|
||||
}
|
||||
.into_paragraphs();
|
||||
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::<&'static str>::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::<&'static str>::with_icon(Icon::new(theme::ICON_CONFIRM))
|
||||
.styled(theme::button_confirm()),
|
||||
1,
|
||||
);
|
||||
let buttons = Button::cancel_confirm_text(None, Some("CONFIRM"));
|
||||
let obj = LayoutObj::new(FloatingButton::top_right_corner(
|
||||
Icon::new(theme::ICON_INFO_CIRCLE),
|
||||
Icon::new(theme::ICON_CORNER_INFO),
|
||||
Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
@ -542,7 +540,7 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
|
||||
SwipeHoldPage::new(paragraphs.into_paragraphs(), theme::BG),
|
||||
))?
|
||||
} else {
|
||||
let buttons = Button::cancel_confirm_text(None, "CONFIRM");
|
||||
let buttons = Button::cancel_confirm_text(None, Some("CONFIRM"));
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
@ -570,7 +568,7 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
|
||||
_ => return Err(Error::ValueError(cstr!("Invalid image."))),
|
||||
};
|
||||
|
||||
let buttons = Button::cancel_confirm_text(None, "CONFIRM");
|
||||
let buttons = Button::cancel_confirm_text(None, Some("CONFIRM"));
|
||||
let obj = LayoutObj::new(
|
||||
Frame::centered(
|
||||
theme::label_title(),
|
||||
@ -590,33 +588,26 @@ 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 button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
|
||||
|
||||
let paragraphs = Paragraphs::new(Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
StrBuffer::from("By continuing you agree\nto Trezor Company's\nterms and conditions."),
|
||||
));
|
||||
let url = FormattedText::new(
|
||||
theme::TEXT_NORMAL,
|
||||
theme::FORMATTED,
|
||||
"More info at {demibold}trezor.io/tos",
|
||||
);
|
||||
let paragraphs = Paragraphs::new([
|
||||
Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
StrBuffer::from(
|
||||
"By continuing you agree\nto Trezor Company's\nterms and conditions.\r",
|
||||
),
|
||||
),
|
||||
Paragraph::new(&theme::TEXT_NORMAL, StrBuffer::from("More info at")),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, StrBuffer::from("trezor.io/tos")),
|
||||
]);
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::with_text(button).styled(theme::button_confirm()),
|
||||
3,
|
||||
true,
|
||||
);
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
Dialog::new(
|
||||
(
|
||||
GridPlaced::new(paragraphs)
|
||||
.with_grid(3, 1)
|
||||
.with_from_to((0, 0), (1, 0)),
|
||||
GridPlaced::new(url).with_grid(3, 1).with_row_col(2, 0),
|
||||
),
|
||||
buttons,
|
||||
),
|
||||
Dialog::new(paragraphs, buttons),
|
||||
)
|
||||
.with_border(theme::borders()),
|
||||
)?;
|
||||
@ -635,7 +626,7 @@ extern "C" fn new_show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) ->
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::with_text(verb_cancel),
|
||||
Button::with_text("CONFIRM".into()).styled(theme::button_confirm()),
|
||||
1,
|
||||
false,
|
||||
);
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
@ -671,7 +662,7 @@ extern "C" fn new_show_address_details(n_args: usize, args: *const Obj, kwargs:
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
HorizontalPage::new(
|
||||
FloatingButton::top_right_corner(Icon::new(theme::ICON_CANCEL_LARGER), ad),
|
||||
FloatingButton::top_right_corner(Icon::new(theme::ICON_CORNER_CANCEL), ad),
|
||||
theme::BG,
|
||||
)
|
||||
.with_swipe_right_to_go_back(),
|
||||
@ -746,7 +737,7 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::with_text("NEXT").styled(theme::button_confirm()),
|
||||
2,
|
||||
true,
|
||||
);
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
@ -781,7 +772,7 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::with_text("NEXT").styled(theme::button_confirm()),
|
||||
2,
|
||||
true,
|
||||
);
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
@ -832,7 +823,7 @@ fn new_show_modal(
|
||||
Button::cancel_confirm(
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)).styled(theme::button_cancel()),
|
||||
Button::with_text(button).styled(button_style),
|
||||
2,
|
||||
true,
|
||||
),
|
||||
)
|
||||
.with_description(description),
|
||||
@ -890,7 +881,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
let controls = Button::cancel_confirm(
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::with_text("CONFIRM").styled(theme::button_confirm()),
|
||||
2,
|
||||
true,
|
||||
);
|
||||
|
||||
let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls);
|
||||
@ -906,7 +897,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let icon = BlendedImage::new(
|
||||
Icon::new(theme::IMAGE_BG_TRIANGLE),
|
||||
Icon::new(theme::IMAGE_BG_OCTAGON),
|
||||
Icon::new(theme::IMAGE_FG_WARN),
|
||||
theme::WARN_COLOR,
|
||||
theme::FG,
|
||||
@ -952,15 +943,21 @@ extern "C" fn new_show_mismatch() -> Obj {
|
||||
let url: StrBuffer = "trezor.io/support".into();
|
||||
let button = "QUIT";
|
||||
|
||||
let icon = BlendedImage::single(Icon::new(theme::ICON_OCTA), theme::WARN_COLOR, theme::BG);
|
||||
|
||||
let icon = BlendedImage::new(
|
||||
Icon::new(theme::IMAGE_BG_OCTAGON),
|
||||
Icon::new(theme::IMAGE_FG_WARN),
|
||||
theme::WARN_COLOR,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
);
|
||||
let obj = LayoutObj::new(
|
||||
IconDialog::new(
|
||||
icon,
|
||||
title,
|
||||
Button::cancel_confirm_square(
|
||||
Button::cancel_confirm(
|
||||
Button::with_icon(Icon::new(theme::ICON_BACK)),
|
||||
Button::with_text(button).styled(theme::button_reset()),
|
||||
true,
|
||||
),
|
||||
)
|
||||
.with_description(description)
|
||||
@ -1095,9 +1092,9 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
|
||||
let paragraphs = Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_NORMAL, "Maximum rounds:".into()),
|
||||
Paragraph::new(&theme::TEXT_BOLD, max_rounds),
|
||||
Paragraph::new(&theme::TEXT_MONO, max_rounds),
|
||||
Paragraph::new(&theme::TEXT_NORMAL, "Maximum mining fee:".into()),
|
||||
Paragraph::new(&theme::TEXT_BOLD, max_feerate),
|
||||
Paragraph::new(&theme::TEXT_MONO, max_feerate),
|
||||
]);
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
@ -1162,7 +1159,7 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
||||
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
|
||||
let words: [StrBuffer; 3] = iter_into_array(words_iterable)?;
|
||||
|
||||
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]);
|
||||
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_DEMIBOLD, description)]);
|
||||
let buttons = Button::select_word(words);
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
@ -1295,7 +1292,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false).unwrap();
|
||||
|
||||
let paragraphs = Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_BOLD, title).centered(),
|
||||
Paragraph::new(&theme::TEXT_DEMIBOLD, title).centered(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL_OFF_WHITE, description).centered(),
|
||||
])
|
||||
.with_spacing(theme::RECOVERY_SPACING);
|
||||
@ -1308,15 +1305,16 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
|
||||
let obj = if info_button {
|
||||
LayoutObj::new(NotificationFrame::new(
|
||||
Icon::new(theme::ICON_WARN),
|
||||
notification,
|
||||
Dialog::new(paragraphs, Button::<&'static str>::abort_info_enter()),
|
||||
Dialog::new(
|
||||
paragraphs,
|
||||
Button::cancel_info_confirm("CONTINUE", "MORE INFO"),
|
||||
),
|
||||
))?
|
||||
} else {
|
||||
LayoutObj::new(NotificationFrame::new(
|
||||
Icon::new(theme::ICON_WARN),
|
||||
notification,
|
||||
Dialog::new(paragraphs, Button::cancel_confirm_text(None, button)),
|
||||
Dialog::new(paragraphs, Button::cancel_confirm_text(None, Some(button))),
|
||||
))?
|
||||
};
|
||||
Ok(obj.into())
|
||||
@ -1334,11 +1332,15 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
let title = if dry_run {
|
||||
"SEED CHECK"
|
||||
} else {
|
||||
"RECOVERY MODE"
|
||||
"WALLET RECOVERY"
|
||||
};
|
||||
|
||||
let paragraphs = Paragraphs::new(
|
||||
Paragraph::new(&theme::TEXT_BOLD, StrBuffer::from("Number of words?")).centered(),
|
||||
Paragraph::new(
|
||||
&theme::TEXT_DEMIBOLD,
|
||||
StrBuffer::from("Select number of words in your recovery seed."),
|
||||
)
|
||||
.centered(),
|
||||
);
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
@ -1384,7 +1386,7 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs:
|
||||
for page in iter {
|
||||
let [title, description]: [StrBuffer; 2] = iter_into_array(page)?;
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_BOLD, title))
|
||||
.add(Paragraph::new(&theme::TEXT_DEMIBOLD, title))
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, description).break_after());
|
||||
}
|
||||
|
||||
@ -1915,7 +1917,7 @@ mod tests {
|
||||
#[test]
|
||||
fn trace_example_layout() {
|
||||
let buttons =
|
||||
Button::cancel_confirm(Button::with_text("Left"), Button::with_text("Right"), 1);
|
||||
Button::cancel_confirm(Button::with_text("Left"), Button::with_text("Right"), false);
|
||||
let mut layout = Dialog::new(
|
||||
FormattedText::new(
|
||||
theme::TEXT_NORMAL,
|
||||
|