mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-24 15:28:10 +00:00
feat(core/ui): add PageCounter to T3T1 Footer
PageCounter sub-component is used within Footer to indicate progress in screens, rendered e.g. as "1 / 20" for the first word in wallet backup.
This commit is contained in:
parent
aaa2ece3ba
commit
c73115df7b
1
core/.changelog.d/3917.added
Normal file
1
core/.changelog.d/3917.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
[T3T1] added word counter during wallet creation
|
@ -2,9 +2,9 @@ use crate::{
|
|||||||
strutil::TString,
|
strutil::TString,
|
||||||
ui::{
|
ui::{
|
||||||
component::{text::TextStyle, Component, Event, EventCtx, Never, SwipeDirection},
|
component::{text::TextStyle, Component, Event, EventCtx, Never, SwipeDirection},
|
||||||
display::Color,
|
display::{Color, Font},
|
||||||
event::SwipeEvent,
|
event::SwipeEvent,
|
||||||
geometry::{Alignment, Offset, Rect},
|
geometry::{Alignment, Alignment2D, Offset, Point, Rect},
|
||||||
lerp::Lerp,
|
lerp::Lerp,
|
||||||
model_mercury::theme,
|
model_mercury::theme,
|
||||||
shape,
|
shape,
|
||||||
@ -12,38 +12,46 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Component showing a task instruction (e.g. "Swipe up") and optionally task
|
use heapless::String;
|
||||||
/// description (e.g. "Confirm transaction") to a user. A host of this component
|
|
||||||
/// is responsible of providing the exact area considering also the spacing. The
|
/// Component showing a task instruction, e.g. "Swipe up", and an optional
|
||||||
/// height must be 18px (only instruction) or 37px (both description and
|
/// content consisting of one of these:
|
||||||
/// instruction). The content and style of both description and instruction is
|
/// - a task description e.g. "Confirm transaction", or
|
||||||
/// configurable separatedly.
|
/// - a page counter e.g. "1 / 3", meaning the first screen of three total.
|
||||||
|
/// A host of this component is responsible of providing the exact area
|
||||||
|
/// considering also the spacing. The height must be 18px (only instruction) or
|
||||||
|
/// 37px (instruction and description/position).
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Footer<'a> {
|
pub struct Footer<'a> {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
text_instruction: TString<'a>,
|
instruction: TString<'a>,
|
||||||
text_description: Option<TString<'a>>,
|
content: Option<FooterContent<'a>>,
|
||||||
style_instruction: &'static TextStyle,
|
|
||||||
style_description: &'static TextStyle,
|
|
||||||
swipe_allow_up: bool,
|
swipe_allow_up: bool,
|
||||||
swipe_allow_down: bool,
|
swipe_allow_down: bool,
|
||||||
progress: i16,
|
progress: i16,
|
||||||
dir: SwipeDirection,
|
dir: SwipeDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum FooterContent<'a> {
|
||||||
|
Description(TString<'a>),
|
||||||
|
PageCounter(PageCounter),
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Footer<'a> {
|
impl<'a> Footer<'a> {
|
||||||
/// height of the component with only instruction [px]
|
/// height of the component with only instruction [px]
|
||||||
pub const HEIGHT_SIMPLE: i16 = 18;
|
pub const HEIGHT_SIMPLE: i16 = 18;
|
||||||
/// height of the component with both description and instruction [px]
|
/// height of the component with instruction and additional content [px]
|
||||||
pub const HEIGHT_DEFAULT: i16 = 37;
|
pub const HEIGHT_DEFAULT: i16 = 37;
|
||||||
|
|
||||||
|
const STYLE_INSTRUCTION: &'static TextStyle = &theme::TEXT_SUB_GREY;
|
||||||
|
const STYLE_DESCRIPTION: &'static TextStyle = &theme::TEXT_SUB_GREY_LIGHT;
|
||||||
|
|
||||||
pub fn new<T: Into<TString<'a>>>(instruction: T) -> Self {
|
pub fn new<T: Into<TString<'a>>>(instruction: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
area: Rect::zero(),
|
area: Rect::zero(),
|
||||||
text_instruction: instruction.into(),
|
instruction: instruction.into(),
|
||||||
text_description: None,
|
content: None,
|
||||||
style_instruction: &theme::TEXT_SUB_GREY,
|
|
||||||
style_description: &theme::TEXT_SUB_GREY_LIGHT,
|
|
||||||
swipe_allow_down: false,
|
swipe_allow_down: false,
|
||||||
swipe_allow_up: false,
|
swipe_allow_up: false,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
@ -53,33 +61,49 @@ impl<'a> Footer<'a> {
|
|||||||
|
|
||||||
pub fn with_description<T: Into<TString<'a>>>(self, description: T) -> Self {
|
pub fn with_description<T: Into<TString<'a>>>(self, description: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
text_description: Some(description.into()),
|
content: Some(FooterContent::Description(description.into())),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_page_counter(self, max_pages: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
content: Some(FooterContent::PageCounter(PageCounter::new(max_pages))),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_instruction<T: Into<TString<'a>>>(&mut self, ctx: &mut EventCtx, s: T) {
|
pub fn update_instruction<T: Into<TString<'a>>>(&mut self, ctx: &mut EventCtx, s: T) {
|
||||||
self.text_instruction = s.into();
|
self.instruction = s.into();
|
||||||
ctx.request_paint();
|
ctx.request_paint();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_description<T: Into<TString<'a>>>(&mut self, ctx: &mut EventCtx, s: T) {
|
pub fn update_description<T: Into<TString<'a>>>(&mut self, ctx: &mut EventCtx, s: T) {
|
||||||
self.text_description = Some(s.into());
|
if let Some(ref mut content) = self.content {
|
||||||
|
if let Some(text) = content.as_description_mut() {
|
||||||
|
*text = s.into();
|
||||||
ctx.request_paint();
|
ctx.request_paint();
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
panic!("footer does not have description")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_instruction_style(&mut self, ctx: &mut EventCtx, style: &'static TextStyle) {
|
pub fn update_page_counter(&mut self, ctx: &mut EventCtx, n: u8) {
|
||||||
self.style_instruction = style;
|
if let Some(ref mut content) = self.content {
|
||||||
|
if let Some(counter) = content.as_page_counter_mut() {
|
||||||
|
counter.update_current_page(n);
|
||||||
ctx.request_paint();
|
ctx.request_paint();
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
panic!("footer does not have counter")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_description_style(&mut self, ctx: &mut EventCtx, style: &'static TextStyle) {
|
|
||||||
self.style_description = style;
|
|
||||||
ctx.request_paint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> i16 {
|
pub fn height(&self) -> i16 {
|
||||||
if self.text_description.is_some() {
|
if self.content.is_some() {
|
||||||
Footer::HEIGHT_DEFAULT
|
Footer::HEIGHT_DEFAULT
|
||||||
} else {
|
} else {
|
||||||
Footer::HEIGHT_SIMPLE
|
Footer::HEIGHT_SIMPLE
|
||||||
@ -106,8 +130,15 @@ impl<'a> Component for Footer<'a> {
|
|||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
let h = bounds.height();
|
let h = bounds.height();
|
||||||
assert!(h == Footer::HEIGHT_SIMPLE || h == Footer::HEIGHT_DEFAULT);
|
assert!(h == Footer::HEIGHT_SIMPLE || h == Footer::HEIGHT_DEFAULT);
|
||||||
|
if let Some(content) = &mut self.content {
|
||||||
|
if let Some(counter) = content.as_page_counter_mut() {
|
||||||
|
if h == Footer::HEIGHT_DEFAULT {
|
||||||
|
counter.place(bounds.split_top(Footer::HEIGHT_SIMPLE).0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
self.area = bounds;
|
self.area = bounds;
|
||||||
bounds
|
self.area
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, _ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
@ -137,8 +168,7 @@ impl<'a> Component for Footer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
// TODO: remove when ui-t3t1 done
|
todo!("remove when ui-t3t1 done")
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
@ -162,40 +192,45 @@ impl<'a> Component for Footer<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
target.with_origin(offset, &|target| {
|
target.with_origin(offset, &|target| {
|
||||||
// show description only if there is space for it
|
// show description/counter only if there is space for it
|
||||||
if self.area.height() == Footer::HEIGHT_DEFAULT {
|
if self.area.height() == Footer::HEIGHT_DEFAULT {
|
||||||
if let Some(description) = self.text_description {
|
if let Some(content) = &self.content {
|
||||||
|
match content {
|
||||||
|
FooterContent::Description(text) => {
|
||||||
let area_description = self.area.split_top(Footer::HEIGHT_SIMPLE).0;
|
let area_description = self.area.split_top(Footer::HEIGHT_SIMPLE).0;
|
||||||
let text_description_font_descent = self
|
let text_description_font_descent = Footer::STYLE_DESCRIPTION
|
||||||
.style_description
|
|
||||||
.text_font
|
.text_font
|
||||||
.visible_text_height_ex("Ay")
|
.visible_text_height_ex("Ay")
|
||||||
.1;
|
.1;
|
||||||
let text_description_baseline =
|
let text_description_baseline = area_description.bottom_center()
|
||||||
area_description.bottom_center() - Offset::y(text_description_font_descent);
|
- Offset::y(text_description_font_descent);
|
||||||
|
|
||||||
description.map(|t| {
|
text.map(|t| {
|
||||||
Text::new(text_description_baseline, t)
|
Text::new(text_description_baseline, t)
|
||||||
.with_font(self.style_description.text_font)
|
.with_font(Footer::STYLE_DESCRIPTION.text_font)
|
||||||
.with_fg(self.style_description.text_color)
|
.with_fg(Footer::STYLE_DESCRIPTION.text_color)
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
.render(target);
|
.render(target);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
FooterContent::PageCounter(counter) => {
|
||||||
|
counter.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let area_instruction = self.area.split_bottom(Footer::HEIGHT_SIMPLE).1;
|
let area_instruction = self.area.split_bottom(Footer::HEIGHT_SIMPLE).1;
|
||||||
let text_instruction_font_descent = self
|
let text_instruction_font_descent = Footer::STYLE_INSTRUCTION
|
||||||
.style_instruction
|
|
||||||
.text_font
|
.text_font
|
||||||
.visible_text_height_ex("Ay")
|
.visible_text_height_ex("Ay")
|
||||||
.1;
|
.1;
|
||||||
let text_instruction_baseline =
|
let text_instruction_baseline =
|
||||||
area_instruction.bottom_center() - Offset::y(text_instruction_font_descent);
|
area_instruction.bottom_center() - Offset::y(text_instruction_font_descent);
|
||||||
self.text_instruction.map(|t| {
|
self.instruction.map(|t| {
|
||||||
Text::new(text_instruction_baseline, t)
|
Text::new(text_instruction_baseline, t)
|
||||||
.with_font(self.style_instruction.text_font)
|
.with_font(Footer::STYLE_INSTRUCTION.text_font)
|
||||||
.with_fg(self.style_instruction.text_color)
|
.with_fg(Footer::STYLE_INSTRUCTION.text_color)
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
.render(target);
|
.render(target);
|
||||||
});
|
});
|
||||||
@ -218,9 +253,120 @@ impl<'a> Component for Footer<'a> {
|
|||||||
impl crate::trace::Trace for Footer<'_> {
|
impl crate::trace::Trace for Footer<'_> {
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("Footer");
|
t.component("Footer");
|
||||||
if let Some(description) = self.text_description {
|
if let Some(content) = &self.content {
|
||||||
t.string("description", description);
|
match content {
|
||||||
|
FooterContent::Description(text) => t.string("description", *text),
|
||||||
|
FooterContent::PageCounter(counter) => counter.trace(t),
|
||||||
}
|
}
|
||||||
t.string("instruction", self.text_instruction);
|
}
|
||||||
|
t.string("instruction", self.instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FooterContent<'a> {
|
||||||
|
pub fn as_description_mut(&mut self) -> Option<&mut TString<'a>> {
|
||||||
|
match self {
|
||||||
|
FooterContent::Description(ref mut text) => Some(text),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_page_counter_mut(&mut self) -> Option<&mut PageCounter> {
|
||||||
|
match self {
|
||||||
|
FooterContent::PageCounter(ref mut counter) => Some(counter),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper component used within Footer instead of description for page count
|
||||||
|
/// indication, rendered e.g. as: '1 / 20'.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PageCounter {
|
||||||
|
area: Rect,
|
||||||
|
page_curr: u8,
|
||||||
|
page_max: u8,
|
||||||
|
font: Font,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageCounter {
|
||||||
|
fn new(page_max: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
area: Rect::zero(),
|
||||||
|
page_curr: 1,
|
||||||
|
page_max,
|
||||||
|
font: Font::SUB,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_current_page(&mut self, new_value: u8) {
|
||||||
|
self.page_curr = new_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for PageCounter {
|
||||||
|
type Msg = Never;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.area = bounds;
|
||||||
|
self.area
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self) {
|
||||||
|
todo!("remove when ui-t3t1 done")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
let color = if self.page_curr == self.page_max {
|
||||||
|
theme::GREEN_LIGHT
|
||||||
|
} else {
|
||||||
|
theme::GREY_LIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
let string_curr = uformat!("{}", self.page_curr);
|
||||||
|
let string_max = uformat!("{}", self.page_max);
|
||||||
|
|
||||||
|
// center the whole counter "x / yz"
|
||||||
|
let offset_x = Offset::x(4); // spacing between foreslash and numbers
|
||||||
|
let width_num_curr = self.font.text_width(&string_curr);
|
||||||
|
let width_foreslash = theme::ICON_FORESLASH.toif.width();
|
||||||
|
let width_num_max = self.font.text_width(&string_max);
|
||||||
|
let width_total = width_num_curr + width_foreslash + width_num_max + 2 * offset_x.x;
|
||||||
|
|
||||||
|
let center_x = self.area.center().x;
|
||||||
|
let counter_y = self.font.vert_center(self.area.y0, self.area.y1, "0");
|
||||||
|
let counter_start_x = center_x - width_total / 2;
|
||||||
|
let counter_end_x = center_x + width_total / 2;
|
||||||
|
let base_num_curr = Point::new(counter_start_x, counter_y);
|
||||||
|
let base_foreslash = Point::new(counter_start_x + width_num_curr + offset_x.x, counter_y);
|
||||||
|
let base_num_max = Point::new(counter_end_x, counter_y);
|
||||||
|
|
||||||
|
Text::new(base_num_curr, &string_curr)
|
||||||
|
.with_align(Alignment::Start)
|
||||||
|
.with_fg(color)
|
||||||
|
.with_font(self.font)
|
||||||
|
.render(target);
|
||||||
|
shape::ToifImage::new(base_foreslash, theme::ICON_FORESLASH.toif)
|
||||||
|
.with_align(Alignment2D::BOTTOM_LEFT)
|
||||||
|
.with_fg(color)
|
||||||
|
.render(target);
|
||||||
|
Text::new(base_num_max, &string_max)
|
||||||
|
.with_align(Alignment::End)
|
||||||
|
.with_fg(color)
|
||||||
|
.with_font(self.font)
|
||||||
|
.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for PageCounter {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("PageCounter");
|
||||||
|
t.int("page current", self.page_curr.into());
|
||||||
|
t.int("page max", self.page_max.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,12 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn with_footer_counter(mut self, instruction: TString<'static>, max_value: u8) -> Self {
|
||||||
|
self.footer = Some(Footer::new(instruction).with_page_counter(max_value));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_danger(self) -> Self {
|
pub fn with_danger(self) -> Self {
|
||||||
self.button_styled(theme::button_danger())
|
self.button_styled(theme::button_danger())
|
||||||
.title_styled(theme::label_title_danger())
|
.title_styled(theme::label_title_danger())
|
||||||
@ -195,6 +201,12 @@ where
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_footer_counter(&mut self, ctx: &mut EventCtx, new_val: u8) {
|
||||||
|
if let Some(footer) = &mut self.footer {
|
||||||
|
footer.update_page_counter(ctx, new_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn with_swipe(mut self, dir: SwipeDirection, settings: SwipeSettings) -> Self {
|
pub fn with_swipe(mut self, dir: SwipeDirection, settings: SwipeSettings) -> Self {
|
||||||
self.footer = self.footer.map(|f| match dir {
|
self.footer = self.footer.map(|f| match dir {
|
||||||
|
@ -11,7 +11,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
event::SwipeEvent,
|
event::SwipeEvent,
|
||||||
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
||||||
model_mercury::component::{Footer, Frame, FrameMsg},
|
model_mercury::component::{Frame, FrameMsg},
|
||||||
shape::{self, Renderer},
|
shape::{self, Renderer},
|
||||||
util,
|
util,
|
||||||
},
|
},
|
||||||
@ -46,13 +46,15 @@ impl<'a> ShareWords<'a> {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let n_words = share_words.len();
|
||||||
Self {
|
Self {
|
||||||
subtitle,
|
subtitle,
|
||||||
frame: Frame::left_aligned(title, ShareWordsInner::new(share_words))
|
frame: Frame::left_aligned(title, ShareWordsInner::new(share_words))
|
||||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
||||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||||
.with_vertical_pages()
|
.with_vertical_pages()
|
||||||
.with_subtitle(subtitle),
|
.with_subtitle(subtitle)
|
||||||
|
.with_footer_counter(TR::instructions__swipe_up.into(), n_words as u8),
|
||||||
repeated_indices,
|
repeated_indices,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +83,7 @@ impl<'a> Component for ShareWords<'a> {
|
|||||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
let page_index = self.frame.inner().page_index as u8;
|
let page_index = self.frame.inner().page_index as u8;
|
||||||
if let Some(repeated_indices) = &self.repeated_indices {
|
if let Some(repeated_indices) = &self.repeated_indices {
|
||||||
if repeated_indices.contains(&(page_index as usize)) {
|
if repeated_indices.contains(&page_index) {
|
||||||
let updated_subtitle = TString::from_translation(TR::reset__the_word_is_repeated);
|
let updated_subtitle = TString::from_translation(TR::reset__the_word_is_repeated);
|
||||||
self.frame
|
self.frame
|
||||||
.update_subtitle(ctx, updated_subtitle, Some(theme::TEXT_SUB_GREEN_LIME));
|
.update_subtitle(ctx, updated_subtitle, Some(theme::TEXT_SUB_GREEN_LIME));
|
||||||
@ -133,8 +135,6 @@ struct ShareWordsInner<'a> {
|
|||||||
area_word: Rect,
|
area_word: Rect,
|
||||||
/// `Some` when transition animation is in progress
|
/// `Some` when transition animation is in progress
|
||||||
animation: Option<Animation<f32>>,
|
animation: Option<Animation<f32>>,
|
||||||
/// Footer component for instructions and word counting
|
|
||||||
footer: Footer<'static>,
|
|
||||||
progress: i16,
|
progress: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +148,6 @@ impl<'a> ShareWordsInner<'a> {
|
|||||||
next_index: 0,
|
next_index: 0,
|
||||||
area_word: Rect::zero(),
|
area_word: Rect::zero(),
|
||||||
animation: None,
|
animation: None,
|
||||||
footer: Footer::new(TR::instructions__swipe_up),
|
|
||||||
progress: 0,
|
progress: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -287,9 +286,6 @@ impl<'a> Component for ShareWordsInner<'a> {
|
|||||||
self.render_word(self.page_index, target);
|
self.render_word(self.page_index, target);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// footer with instructions
|
|
||||||
self.footer.render(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_bounds")]
|
#[cfg(feature = "ui_bounds")]
|
||||||
|
@ -57,6 +57,9 @@ pub const QR_SIDE_MAX: u32 = 140;
|
|||||||
|
|
||||||
// UI icons (white color).
|
// UI icons (white color).
|
||||||
|
|
||||||
|
// 12x12
|
||||||
|
include_icon!(ICON_FORESLASH, "model_mercury/res/foreslash12.toif");
|
||||||
|
|
||||||
// 20x20
|
// 20x20
|
||||||
include_icon!(
|
include_icon!(
|
||||||
ICON_BULLET_CHECKMARK,
|
ICON_BULLET_CHECKMARK,
|
||||||
@ -141,9 +144,6 @@ include_icon!(ICON_UP, "model_tt/res/caret-up24.toif");
|
|||||||
include_icon!(ICON_DOWN, "model_tt/res/caret-down24.toif");
|
include_icon!(ICON_DOWN, "model_tt/res/caret-down24.toif");
|
||||||
include_icon!(ICON_CLICK, "model_tt/res/finger24.toif");
|
include_icon!(ICON_CLICK, "model_tt/res/finger24.toif");
|
||||||
|
|
||||||
include_icon!(ICON_CORNER_CANCEL, "model_tt/res/x32.toif");
|
|
||||||
include_icon!(ICON_CORNER_INFO, "model_tt/res/info32.toif");
|
|
||||||
|
|
||||||
// Homescreen notifications.
|
// Homescreen notifications.
|
||||||
include_icon!(ICON_WARN, "model_tt/res/warning16.toif");
|
include_icon!(ICON_WARN, "model_tt/res/warning16.toif");
|
||||||
include_icon!(ICON_WARNING40, "model_tt/res/warning40.toif");
|
include_icon!(ICON_WARNING40, "model_tt/res/warning40.toif");
|
||||||
|
@ -27,7 +27,7 @@ pub struct Text<'a> {
|
|||||||
|
|
||||||
impl<'a> Text<'a> {
|
impl<'a> Text<'a> {
|
||||||
/// Creates a `shape::Text` structure with a specified
|
/// Creates a `shape::Text` structure with a specified
|
||||||
/// text (`str`) and the top-left corner (`pos`).
|
/// text (`str`) and the bottom-left corner (`pos`).
|
||||||
pub fn new(pos: Point, text: &'a str) -> Self {
|
pub fn new(pos: Point, text: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pos,
|
pos,
|
||||||
|
Loading…
Reference in New Issue
Block a user