mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-10 15:30:55 +00:00
fix(core): rebase on drawlib and TStringificaton
[no changelog]
This commit is contained in:
parent
8978f36096
commit
ac39b026cf
@ -9,5 +9,5 @@
|
|||||||
pub use super::model_mercury::constant::*;
|
pub use super::model_mercury::constant::*;
|
||||||
#[cfg(all(feature = "model_tr", not(feature = "model_tt")))]
|
#[cfg(all(feature = "model_tr", not(feature = "model_tt")))]
|
||||||
pub use super::model_tr::constant::*;
|
pub use super::model_tr::constant::*;
|
||||||
#[cfg(all(feature = "model_tt", not(feature = "model_mercury")))]
|
#[cfg(feature = "model_tt")]
|
||||||
pub use super::model_tt::constant::*;
|
pub use super::model_tt::constant::*;
|
||||||
|
@ -2,8 +2,7 @@ use heapless::Vec;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
micropython::buffer::StrBuffer,
|
strutil::TString,
|
||||||
strutil::StringType,
|
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
@ -19,58 +18,57 @@ use super::{theme, Frame, FrameMsg};
|
|||||||
|
|
||||||
const MAX_XPUBS: usize = 16;
|
const MAX_XPUBS: usize = 16;
|
||||||
|
|
||||||
pub struct AddressDetails<T> {
|
pub struct AddressDetails {
|
||||||
qr_code: Frame<Qr, T>,
|
qr_code: Frame<Qr>,
|
||||||
details: Frame<Paragraphs<ParagraphVecShort<StrBuffer>>, T>,
|
details: Frame<Paragraphs<ParagraphVecShort<'static>>>,
|
||||||
xpub_view: Frame<Paragraphs<Paragraph<T>>, T>,
|
xpub_view: Frame<Paragraphs<Paragraph<'static>>>,
|
||||||
xpubs: Vec<(T, T), MAX_XPUBS>,
|
xpubs: Vec<(TString<'static>, TString<'static>), MAX_XPUBS>,
|
||||||
xpub_page_count: Vec<u8, MAX_XPUBS>,
|
xpub_page_count: Vec<u8, MAX_XPUBS>,
|
||||||
current_page: usize,
|
current_page: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AddressDetails<T>
|
impl AddressDetails {
|
||||||
where
|
|
||||||
T: StringType,
|
|
||||||
{
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
qr_title: T,
|
qr_title: TString<'static>,
|
||||||
qr_address: T,
|
qr_address: TString<'static>,
|
||||||
case_sensitive: bool,
|
case_sensitive: bool,
|
||||||
details_title: T,
|
details_title: TString<'static>,
|
||||||
account: Option<StrBuffer>,
|
account: Option<TString<'static>>,
|
||||||
path: Option<StrBuffer>,
|
path: Option<TString<'static>>,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error> {
|
||||||
where
|
|
||||||
T: From<&'static str>,
|
|
||||||
{
|
|
||||||
let mut para = ParagraphVecShort::new();
|
let mut para = ParagraphVecShort::new();
|
||||||
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));
|
||||||
}
|
}
|
||||||
let result = Self {
|
let result = Self {
|
||||||
qr_code: Frame::left_aligned(
|
qr_code: Frame::left_aligned(
|
||||||
qr_title,
|
qr_title,
|
||||||
Qr::new(qr_address, case_sensitive)?.with_border(7),
|
qr_address
|
||||||
|
.map(|s| Qr::new(s, case_sensitive))?
|
||||||
|
.with_border(7),
|
||||||
)
|
)
|
||||||
.with_cancel_button()
|
.with_cancel_button()
|
||||||
.with_border(theme::borders_horizontal_scroll()),
|
.with_border(theme::borders_horizontal_scroll()),
|
||||||
details: Frame::left_aligned(details_title, para.into_paragraphs())
|
details: Frame::left_aligned(
|
||||||
|
details_title,
|
||||||
|
para.into_paragraphs(),
|
||||||
|
)
|
||||||
.with_cancel_button()
|
.with_cancel_button()
|
||||||
.with_border(theme::borders_horizontal_scroll()),
|
.with_border(theme::borders_horizontal_scroll()),
|
||||||
xpub_view: Frame::left_aligned(
|
xpub_view: Frame::left_aligned(
|
||||||
" \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()),
|
||||||
@ -81,24 +79,24 @@ where
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_xpub(&mut self, title: T, xpub: T) -> Result<(), Error> {
|
pub fn add_xpub(
|
||||||
|
&mut self,
|
||||||
|
title: TString<'static>,
|
||||||
|
xpub: TString<'static>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
self.xpubs
|
self.xpubs
|
||||||
.push((title, xpub))
|
.push((title, xpub))
|
||||||
.map_err(|_| Error::OutOfRange)
|
.map_err(|_| Error::OutOfRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch_xpub(&mut self, i: usize, page: usize) -> usize
|
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
|
// Context is needed for updating child so that it can request repaint. In this
|
||||||
// case the parent component that handles paging always requests complete
|
// case the parent component that handles paging always requests complete
|
||||||
// repaint after page change so we can use a dummy context here.
|
// repaint after page change so we can use a dummy context here.
|
||||||
let mut dummy_ctx = EventCtx::new();
|
let mut dummy_ctx = EventCtx::new();
|
||||||
self.xpub_view
|
self.xpub_view.update_title(&mut dummy_ctx, self.xpubs[i].0);
|
||||||
.update_title(&mut dummy_ctx, self.xpubs[i].0.clone());
|
|
||||||
self.xpub_view.update_content(&mut dummy_ctx, |p| {
|
self.xpub_view.update_content(&mut dummy_ctx, |p| {
|
||||||
p.inner_mut().update(self.xpubs[i].1.clone());
|
p.inner_mut().update(self.xpubs[i].1);
|
||||||
let npages = p.page_count();
|
let npages = p.page_count();
|
||||||
p.change_page(page);
|
p.change_page(page);
|
||||||
npages
|
npages
|
||||||
@ -123,10 +121,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Paginate for AddressDetails<T>
|
impl Paginate for AddressDetails {
|
||||||
where
|
|
||||||
T: StringType + Clone,
|
|
||||||
{
|
|
||||||
fn page_count(&mut self) -> usize {
|
fn page_count(&mut self) -> usize {
|
||||||
let total_xpub_pages: u8 = self.xpub_page_count.iter().copied().sum();
|
let total_xpub_pages: u8 = self.xpub_page_count.iter().copied().sum();
|
||||||
2usize.saturating_add(total_xpub_pages.into())
|
2usize.saturating_add(total_xpub_pages.into())
|
||||||
@ -142,10 +137,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for AddressDetails<T>
|
impl Component for AddressDetails {
|
||||||
where
|
|
||||||
T: StringType + Clone,
|
|
||||||
{
|
|
||||||
type Msg = ();
|
type Msg = ();
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
@ -201,10 +193,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T> crate::trace::Trace for AddressDetails<T>
|
impl crate::trace::Trace for AddressDetails {
|
||||||
where
|
|
||||||
T: StringType,
|
|
||||||
{
|
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("AddressDetails");
|
t.component("AddressDetails");
|
||||||
match self.current_page {
|
match self.current_page {
|
||||||
|
@ -32,7 +32,7 @@ pub struct Button {
|
|||||||
long_timer: Option<TimerToken>,
|
long_timer: Option<TimerToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Button<T> {
|
impl Button {
|
||||||
/// Offsets the baseline of the button text
|
/// Offsets the baseline of the button text
|
||||||
/// -x/+x => left/right
|
/// -x/+x => left/right
|
||||||
/// -y/+y => up/down
|
/// -y/+y => up/down
|
||||||
@ -58,8 +58,8 @@ impl<T> Button<T> {
|
|||||||
Self::new(ButtonContent::Icon(icon))
|
Self::new(ButtonContent::Icon(icon))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn with_icon_and_text(content: IconText<T>) -> Self {
|
pub const fn with_icon_and_text(content: IconText) -> Self {
|
||||||
Self::new(ButtonContent::IconAndText::<T>(content))
|
Self::new(ButtonContent::IconAndText(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn with_icon_blend(bg: Icon, fg: Icon, fg_offset: Offset) -> Self {
|
pub const fn with_icon_blend(bg: Icon, fg: Icon, fg_offset: Offset) -> Self {
|
||||||
@ -204,8 +204,8 @@ impl<T> Button<T> {
|
|||||||
match &self.content {
|
match &self.content {
|
||||||
ButtonContent::Empty => {}
|
ButtonContent::Empty => {}
|
||||||
ButtonContent::Text(text) => {
|
ButtonContent::Text(text) => {
|
||||||
let text = text.as_ref();
|
|
||||||
let start_of_baseline = self.area.center() + Self::BASELINE_OFFSET;
|
let start_of_baseline = self.area.center() + Self::BASELINE_OFFSET;
|
||||||
|
text.map(|text| {
|
||||||
display::text_left(
|
display::text_left(
|
||||||
start_of_baseline,
|
start_of_baseline,
|
||||||
text,
|
text,
|
||||||
@ -213,6 +213,7 @@ impl<T> Button<T> {
|
|||||||
style.text_color,
|
style.text_color,
|
||||||
style.button_color,
|
style.button_color,
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ButtonContent::Icon(icon) => {
|
ButtonContent::Icon(icon) => {
|
||||||
icon.draw(
|
icon.draw(
|
||||||
@ -238,12 +239,13 @@ impl<T> Button<T> {
|
|||||||
match &self.content {
|
match &self.content {
|
||||||
ButtonContent::Empty => {}
|
ButtonContent::Empty => {}
|
||||||
ButtonContent::Text(text) => {
|
ButtonContent::Text(text) => {
|
||||||
let text = text.as_ref();
|
|
||||||
let start_of_baseline = self.area.left_center() + Self::BASELINE_OFFSET;
|
let start_of_baseline = self.area.left_center() + Self::BASELINE_OFFSET;
|
||||||
|
text.map(|text| {
|
||||||
shape::Text::new(start_of_baseline, text)
|
shape::Text::new(start_of_baseline, text)
|
||||||
.with_font(style.font)
|
.with_font(style.font)
|
||||||
.with_fg(style.text_color)
|
.with_fg(style.text_color)
|
||||||
.render(target);
|
.render(target);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ButtonContent::Icon(icon) => {
|
ButtonContent::Icon(icon) => {
|
||||||
shape::ToifImage::new(self.area.center(), icon.toif)
|
shape::ToifImage::new(self.area.center(), icon.toif)
|
||||||
@ -376,7 +378,7 @@ impl crate::trace::Trace for Button {
|
|||||||
ButtonContent::Text(text) => t.string("text", *text),
|
ButtonContent::Text(text) => t.string("text", *text),
|
||||||
ButtonContent::Icon(_) => t.bool("icon", true),
|
ButtonContent::Icon(_) => t.bool("icon", true),
|
||||||
ButtonContent::IconAndText(content) => {
|
ButtonContent::IconAndText(content) => {
|
||||||
t.string("text", content.text.as_ref().into());
|
t.string("text", content.text.into());
|
||||||
t.bool("icon", true);
|
t.bool("icon", true);
|
||||||
}
|
}
|
||||||
ButtonContent::IconBlend(_, _, _) => t.bool("icon", true),
|
ButtonContent::IconBlend(_, _, _) => t.bool("icon", true),
|
||||||
@ -397,7 +399,7 @@ pub enum ButtonContent {
|
|||||||
Empty,
|
Empty,
|
||||||
Text(TString<'static>),
|
Text(TString<'static>),
|
||||||
Icon(Icon),
|
Icon(Icon),
|
||||||
IconAndText(IconText<T>),
|
IconAndText(IconText),
|
||||||
IconBlend(Icon, Icon, Offset),
|
IconBlend(Icon, Icon, Offset),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,18 +421,15 @@ pub struct ButtonStyle {
|
|||||||
pub border_width: i16,
|
pub border_width: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Button<T> {
|
impl Button {
|
||||||
pub fn cancel_confirm(
|
pub fn cancel_confirm(
|
||||||
left: Button<T>,
|
left: Button,
|
||||||
right: Button<T>,
|
right: Button,
|
||||||
left_is_small: bool,
|
left_is_small: bool,
|
||||||
) -> CancelConfirm<
|
) -> CancelConfirm<
|
||||||
T,
|
|
||||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||||
>
|
>
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
{
|
||||||
let width = if left_is_small {
|
let width = if left_is_small {
|
||||||
theme::BUTTON_WIDTH
|
theme::BUTTON_WIDTH
|
||||||
@ -450,21 +449,18 @@ impl<T> Button<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel_confirm_text(
|
pub fn cancel_confirm_text(
|
||||||
left: Option<T>,
|
left: Option<TString<'static>>,
|
||||||
right: Option<T>,
|
right: Option<TString<'static>>,
|
||||||
) -> CancelConfirm<
|
) -> CancelConfirm<
|
||||||
T,
|
|
||||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||||
>
|
>
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
{
|
||||||
let left_is_small: bool;
|
let left_is_small: bool;
|
||||||
|
|
||||||
let left = if let Some(verb) = left {
|
let left = if let Some(verb) = left {
|
||||||
left_is_small = verb.as_ref().len() <= 4;
|
left_is_small = verb.len() <= 4;
|
||||||
if verb.as_ref() == "^" {
|
if verb == "^".into() {
|
||||||
Button::with_icon(theme::ICON_UP)
|
Button::with_icon(theme::ICON_UP)
|
||||||
} else {
|
} else {
|
||||||
Button::with_text(verb)
|
Button::with_text(verb)
|
||||||
@ -482,16 +478,13 @@ impl<T> Button<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel_info_confirm(
|
pub fn cancel_info_confirm(
|
||||||
confirm: T,
|
confirm: TString<'static>,
|
||||||
info: T,
|
info: TString<'static>,
|
||||||
) -> CancelInfoConfirm<
|
) -> CancelInfoConfirm<
|
||||||
T,
|
|
||||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||||
>
|
>
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
{
|
||||||
let right = Button::with_text(confirm)
|
let right = Button::with_text(confirm)
|
||||||
.styled(theme::button_confirm())
|
.styled(theme::button_confirm())
|
||||||
@ -522,11 +515,10 @@ pub enum CancelConfirmMsg {
|
|||||||
Confirmed,
|
Confirmed,
|
||||||
}
|
}
|
||||||
|
|
||||||
type CancelInfoConfirm<T, F0, F1, F2> = FixedHeightBar<
|
type CancelInfoConfirm<F0, F1, F2> =
|
||||||
Split<MsgMap<Button<T>, F0>, Split<MsgMap<Button<T>, F1>, MsgMap<Button<T>, F2>>>,
|
FixedHeightBar<Split<MsgMap<Button, F0>, Split<MsgMap<Button, F1>, MsgMap<Button, F2>>>>;
|
||||||
>;
|
|
||||||
|
|
||||||
type CancelConfirm<T, F0, F1> = FixedHeightBar<Split<MsgMap<Button<T>, F0>, MsgMap<Button<T>, F1>>>;
|
type CancelConfirm<F0, F1> = FixedHeightBar<Split<MsgMap<Button, F0>, MsgMap<Button, F1>>>;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum CancelInfoConfirmMsg {
|
pub enum CancelInfoConfirmMsg {
|
||||||
@ -536,25 +528,23 @@ pub enum CancelInfoConfirmMsg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct IconText<T> {
|
pub struct IconText {
|
||||||
text: T,
|
text: &'static str,
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IconText<T>
|
impl IconText {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
const ICON_SPACE: i16 = 46;
|
const ICON_SPACE: i16 = 46;
|
||||||
const ICON_MARGIN: i16 = 4;
|
const ICON_MARGIN: i16 = 4;
|
||||||
const TEXT_MARGIN: i16 = 6;
|
const TEXT_MARGIN: i16 = 6;
|
||||||
|
|
||||||
pub fn new(text: T, icon: Icon) -> Self {
|
pub fn new(text: &'static str, icon: Icon) -> Self {
|
||||||
Self { text, icon }
|
Self { text, icon }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(&self, area: Rect, style: &ButtonStyle, baseline_offset: Offset) {
|
pub fn paint(&self, area: Rect, style: &ButtonStyle, baseline_offset: Offset) {
|
||||||
let width = style.font.text_width(self.text.as_ref());
|
let width = style.font.text_width(self.text);
|
||||||
|
let height = style.font.text_height();
|
||||||
|
|
||||||
let mut use_icon = false;
|
let mut use_icon = false;
|
||||||
let mut use_text = false;
|
let mut use_text = false;
|
||||||
@ -563,7 +553,8 @@ where
|
|||||||
area.top_left().x + ((Self::ICON_SPACE + Self::ICON_MARGIN) / 2),
|
area.top_left().x + ((Self::ICON_SPACE + Self::ICON_MARGIN) / 2),
|
||||||
area.center().y,
|
area.center().y,
|
||||||
);
|
);
|
||||||
let mut text_pos = area.left_center() + baseline_offset;
|
let mut text_pos =
|
||||||
|
area.center() + Offset::new(-width / 2, height / 2) + baseline_offset;
|
||||||
|
|
||||||
if area.width() > (Self::ICON_SPACE + Self::TEXT_MARGIN + width) {
|
if area.width() > (Self::ICON_SPACE + Self::TEXT_MARGIN + width) {
|
||||||
//display both icon and text
|
//display both icon and text
|
||||||
@ -581,7 +572,7 @@ where
|
|||||||
if use_text {
|
if use_text {
|
||||||
display::text_left(
|
display::text_left(
|
||||||
text_pos,
|
text_pos,
|
||||||
self.text.as_ref(),
|
self.text,
|
||||||
style.font,
|
style.font,
|
||||||
style.text_color,
|
style.text_color,
|
||||||
style.button_color,
|
style.button_color,
|
||||||
@ -629,7 +620,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if use_text {
|
if use_text {
|
||||||
shape::Text::new(text_pos, self.text.as_ref())
|
shape::Text::new(text_pos, self.text)
|
||||||
.with_fg(style.text_color)
|
.with_fg(style.text_color)
|
||||||
.render(target);
|
.render(target);
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,15 @@ use core::mem;
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
maybe_trace::MaybeTrace,
|
maybe_trace::MaybeTrace,
|
||||||
micropython::buffer::StrBuffer,
|
strutil::TString,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
base::Never, Bar, Child, Component, ComponentExt, Empty, Event, EventCtx, Label, Split,
|
base::Never, Bar, Child, Component, ComponentExt, Empty, Event, EventCtx, Label, Split,
|
||||||
},
|
},
|
||||||
constant,
|
|
||||||
display::loader::{loader_circular_uncompress, LoaderDimensions},
|
display::loader::{loader_circular_uncompress, LoaderDimensions},
|
||||||
geometry::{Insets, Offset, Rect},
|
geometry::{Insets, Offset, Rect},
|
||||||
|
model_mercury::constant,
|
||||||
shape,
|
shape,
|
||||||
shape::Renderer,
|
shape::Renderer,
|
||||||
util::animation_disabled,
|
util::animation_disabled,
|
||||||
@ -27,31 +27,22 @@ const LOADER_INNER: i16 = 28;
|
|||||||
const LOADER_OFFSET: i16 = -34;
|
const LOADER_OFFSET: i16 = -34;
|
||||||
const LOADER_SPEED: u16 = 5;
|
const LOADER_SPEED: u16 = 5;
|
||||||
|
|
||||||
pub struct CoinJoinProgress<T, U> {
|
pub struct CoinJoinProgress<U> {
|
||||||
value: u16,
|
value: u16,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
content: Child<Frame<Split<Empty, U>, StrBuffer>>,
|
content: Child<Frame<Split<Empty, U>>>,
|
||||||
// Label is not a child since circular loader paints large black rectangle which overlaps it.
|
// Label is not a child since circular loader paints large black rectangle which overlaps it.
|
||||||
// To work around this, draw label every time loader is drawn.
|
// To work around this, draw label every time loader is drawn.
|
||||||
label: Label<T>,
|
label: Label<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> CoinJoinProgress<T, U>
|
impl<U> CoinJoinProgress<U> {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
text: T,
|
text: TString<'static>,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
) -> Result<CoinJoinProgress<T, impl Component<Msg = Never> + MaybeTrace>, Error>
|
) -> Result<CoinJoinProgress<impl Component<Msg = Never> + MaybeTrace>, Error> {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
let style = theme::label_coinjoin_progress();
|
let style = theme::label_coinjoin_progress();
|
||||||
let label = Label::centered(
|
let label = Label::centered(TR::coinjoin__title_do_not_disconnect.into(), style)
|
||||||
TryInto::<StrBuffer>::try_into(TR::coinjoin__title_do_not_disconnect)?,
|
|
||||||
style,
|
|
||||||
)
|
|
||||||
.vertically_centered();
|
.vertically_centered();
|
||||||
let bg = Bar::new(style.background_color, theme::BG, 2);
|
let bg = Bar::new(style.background_color, theme::BG, 2);
|
||||||
let inner = (bg, label);
|
let inner = (bg, label);
|
||||||
@ -59,17 +50,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> CoinJoinProgress<T, U>
|
impl<U> CoinJoinProgress<U>
|
||||||
where
|
where
|
||||||
T: AsRef<str>,
|
|
||||||
U: Component<Msg = Never>,
|
U: Component<Msg = Never>,
|
||||||
{
|
{
|
||||||
pub fn with_background(text: T, inner: U, indeterminate: bool) -> Result<Self, Error> {
|
pub fn with_background(
|
||||||
|
text: TString<'static>,
|
||||||
|
inner: U,
|
||||||
|
indeterminate: bool,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
value: 0,
|
value: 0,
|
||||||
indeterminate,
|
indeterminate,
|
||||||
content: Frame::centered(
|
content: Frame::centered(
|
||||||
TR::coinjoin__title_progress.try_into()?,
|
TR::coinjoin__title_progress.into(),
|
||||||
Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner),
|
Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner),
|
||||||
)
|
)
|
||||||
.into_child(),
|
.into_child(),
|
||||||
@ -78,9 +72,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Component for CoinJoinProgress<T, U>
|
impl<U> Component for CoinJoinProgress<U>
|
||||||
where
|
where
|
||||||
T: AsRef<str>,
|
|
||||||
U: Component<Msg = Never>,
|
U: Component<Msg = Never>,
|
||||||
{
|
{
|
||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
@ -167,9 +160,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T, U> crate::trace::Trace for CoinJoinProgress<T, U>
|
impl<U> crate::trace::Trace for CoinJoinProgress<U>
|
||||||
where
|
where
|
||||||
T: AsRef<str>,
|
|
||||||
U: Component + crate::trace::Trace,
|
U: Component + crate::trace::Trace,
|
||||||
{
|
{
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::StringType,
|
strutil::TString,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
image::BlendedImage,
|
image::BlendedImage,
|
||||||
@ -97,18 +97,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(
|
||||||
@ -125,26 +124,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(
|
||||||
@ -171,9 +170,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>;
|
||||||
@ -219,9 +217,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) {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use crate::ui::{
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
component::{image::Image, Child, Component, Event, EventCtx, Label},
|
component::{image::Image, Child, Component, Event, EventCtx, Label},
|
||||||
display,
|
display,
|
||||||
geometry::{Insets, Rect},
|
geometry::{Insets, Rect},
|
||||||
@ -9,6 +11,7 @@ use crate::ui::{
|
|||||||
},
|
},
|
||||||
shape,
|
shape,
|
||||||
shape::Renderer,
|
shape::Renderer,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::CancelConfirmMsg;
|
use super::CancelConfirmMsg;
|
||||||
@ -25,10 +28,10 @@ pub enum FidoMsg {
|
|||||||
Cancelled,
|
Cancelled,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FidoConfirm<F: Fn(usize) -> T, T, U> {
|
pub struct FidoConfirm<F: Fn(usize) -> TString<'static>, U> {
|
||||||
page_swipe: Swipe,
|
page_swipe: Swipe,
|
||||||
app_name: Label<T>,
|
app_name: Label<'static>,
|
||||||
account_name: Label<T>,
|
account_name: Label<'static>,
|
||||||
icon: Child<Image>,
|
icon: Child<Image>,
|
||||||
/// Function/closure that will return appropriate page on demand.
|
/// Function/closure that will return appropriate page on demand.
|
||||||
get_account: F,
|
get_account: F,
|
||||||
@ -37,20 +40,19 @@ pub struct FidoConfirm<F: Fn(usize) -> T, T, U> {
|
|||||||
controls: U,
|
controls: U,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T, U> FidoConfirm<F, T, U>
|
impl<F, U> FidoConfirm<F, U>
|
||||||
where
|
where
|
||||||
F: Fn(usize) -> T,
|
F: Fn(usize) -> TString<'static>,
|
||||||
T: AsRef<str> + From<&'static str>,
|
|
||||||
U: Component<Msg = CancelConfirmMsg>,
|
U: Component<Msg = CancelConfirmMsg>,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
app_name: T,
|
app_name: TString<'static>,
|
||||||
get_account: F,
|
get_account: F,
|
||||||
page_count: usize,
|
page_count: usize,
|
||||||
icon_name: Option<T>,
|
icon_name: Option<TString<'static>>,
|
||||||
controls: U,
|
controls: U,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let icon_data = get_fido_icon_data(icon_name.as_ref());
|
let icon_data = get_fido_icon_data(icon_name);
|
||||||
|
|
||||||
// Preparing scrollbar and setting its page-count.
|
// Preparing scrollbar and setting its page-count.
|
||||||
let mut scrollbar = ScrollBar::horizontal();
|
let mut scrollbar = ScrollBar::horizontal();
|
||||||
@ -118,10 +120,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T, U> Component for FidoConfirm<F, T, U>
|
impl<F, U> Component for FidoConfirm<F, U>
|
||||||
where
|
where
|
||||||
F: Fn(usize) -> T,
|
F: Fn(usize) -> TString<'static>,
|
||||||
T: AsRef<str> + From<&'static str>,
|
|
||||||
U: Component<Msg = CancelConfirmMsg>,
|
U: Component<Msg = CancelConfirmMsg>,
|
||||||
{
|
{
|
||||||
type Msg = FidoMsg;
|
type Msg = FidoMsg;
|
||||||
@ -200,8 +201,8 @@ where
|
|||||||
// Account name is optional.
|
// Account name is optional.
|
||||||
// Showing it only if it differs from app name.
|
// Showing it only if it differs from app name.
|
||||||
// (Dummy requests usually have some text as both app_name and account_name.)
|
// (Dummy requests usually have some text as both app_name and account_name.)
|
||||||
let account_name = self.account_name.text().as_ref();
|
let account_name = self.account_name.text();
|
||||||
let app_name = self.app_name.text().as_ref();
|
let app_name = self.app_name.text();
|
||||||
if !account_name.is_empty() && account_name != app_name {
|
if !account_name.is_empty() && account_name != app_name {
|
||||||
self.account_name.paint();
|
self.account_name.paint();
|
||||||
}
|
}
|
||||||
@ -230,8 +231,8 @@ where
|
|||||||
// Account name is optional.
|
// Account name is optional.
|
||||||
// Showing it only if it differs from app name.
|
// Showing it only if it differs from app name.
|
||||||
// (Dummy requests usually have some text as both app_name and account_name.)
|
// (Dummy requests usually have some text as both app_name and account_name.)
|
||||||
let account_name = self.account_name.text().as_ref();
|
let account_name = self.account_name.text();
|
||||||
let app_name = self.app_name.text().as_ref();
|
let app_name = self.app_name.text();
|
||||||
if !account_name.is_empty() && account_name != app_name {
|
if !account_name.is_empty() && account_name != app_name {
|
||||||
self.account_name.render(target);
|
self.account_name.render(target);
|
||||||
}
|
}
|
||||||
@ -251,9 +252,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<F, T, U> crate::trace::Trace for FidoConfirm<F, T, U>
|
impl<F, T> crate::trace::Trace for FidoConfirm<F, T>
|
||||||
where
|
where
|
||||||
F: Fn(usize) -> T,
|
F: Fn(usize) -> TString<'static>,
|
||||||
{
|
{
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("FidoConfirm");
|
t.component("FidoConfirm");
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
//! do not edit manually!
|
//! do not edit manually!
|
||||||
|
|
||||||
|
|
||||||
|
use crate::strutil::TString;
|
||||||
|
|
||||||
|
|
||||||
const ICON_APPLE: &[u8] = include_res!("model_mercury/res/fido/icon_apple.toif");
|
const ICON_APPLE: &[u8] = include_res!("model_mercury/res/fido/icon_apple.toif");
|
||||||
const ICON_AWS: &[u8] = include_res!("model_mercury/res/fido/icon_aws.toif");
|
const ICON_AWS: &[u8] = include_res!("model_mercury/res/fido/icon_aws.toif");
|
||||||
const ICON_BINANCE: &[u8] = include_res!("model_mercury/res/fido/icon_binance.toif");
|
const ICON_BINANCE: &[u8] = include_res!("model_mercury/res/fido/icon_binance.toif");
|
||||||
@ -39,9 +42,9 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_mercury/res/fido/icon_webauthn.
|
|||||||
/// Translates icon name into its data.
|
/// Translates icon name into its data.
|
||||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||||
/// supplied.
|
/// supplied.
|
||||||
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] {
|
pub fn get_fido_icon_data(icon_name: Option<TString<'static>>) -> &'static [u8] {
|
||||||
if let Some(icon_name) = icon_name {
|
if let Some(icon_name) = icon_name {
|
||||||
match icon_name.as_ref() {
|
icon_name.map(|c| match c {
|
||||||
"apple" => ICON_APPLE,
|
"apple" => ICON_APPLE,
|
||||||
"aws" => ICON_AWS,
|
"aws" => ICON_AWS,
|
||||||
"binance" => ICON_BINANCE,
|
"binance" => ICON_BINANCE,
|
||||||
@ -73,7 +76,7 @@ pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8]
|
|||||||
"stripe" => ICON_STRIPE,
|
"stripe" => ICON_STRIPE,
|
||||||
"tutanota" => ICON_TUTANOTA,
|
"tutanota" => ICON_TUTANOTA,
|
||||||
_ => ICON_WEBAUTHN,
|
_ => ICON_WEBAUTHN,
|
||||||
}
|
})
|
||||||
} else {
|
} else {
|
||||||
ICON_WEBAUTHN
|
ICON_WEBAUTHN
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
//! (by running `make templates` in `core`)
|
//! (by running `make templates` in `core`)
|
||||||
//! do not edit manually!
|
//! do not edit manually!
|
||||||
|
|
||||||
|
|
||||||
|
use crate::strutil::TString;
|
||||||
|
|
||||||
<%
|
<%
|
||||||
icons: list[tuple[str, str]] = []
|
icons: list[tuple[str, str]] = []
|
||||||
for app in fido:
|
for app in fido:
|
||||||
@ -21,14 +24,14 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_mercury/res/fido/icon_webauthn.
|
|||||||
/// Translates icon name into its data.
|
/// Translates icon name into its data.
|
||||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||||
/// supplied.
|
/// supplied.
|
||||||
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] {
|
pub fn get_fido_icon_data(icon_name: Option<TString<'static>>) -> &'static [u8] {
|
||||||
if let Some(icon_name) = icon_name {
|
if let Some(icon_name) = icon_name {
|
||||||
match icon_name.as_ref() {
|
icon_name.map(|c| match c {
|
||||||
% for icon_name, var_name in icons:
|
% for icon_name, var_name in icons:
|
||||||
"${icon_name}" => ICON_${var_name},
|
"${icon_name}" => ICON_${var_name},
|
||||||
% endfor
|
% endfor
|
||||||
_ => ICON_WEBAUTHN,
|
_ => ICON_WEBAUTHN,
|
||||||
}
|
})
|
||||||
} else {
|
} else {
|
||||||
ICON_WEBAUTHN
|
ICON_WEBAUTHN
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_subtitle(mut self, subtitle: U) -> Self {
|
pub fn with_subtitle(mut self, subtitle: TString<'static>) -> Self {
|
||||||
let style = theme::TEXT_SUB;
|
let style = theme::TEXT_SUB;
|
||||||
self.title = Child::new(self.title.into_inner().top_aligned());
|
self.title = Child::new(self.title.into_inner().top_aligned());
|
||||||
self.subtitle = Some(Child::new(Label::new(
|
self.subtitle = Some(Child::new(Label::new(
|
||||||
|
@ -288,6 +288,13 @@ impl Component for Homescreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.label.map(|t| {
|
self.label.map(|t| {
|
||||||
|
let r = Rect::new(Point::new(6, 198), Point::new(234, 233));
|
||||||
|
shape::Bar::new(r)
|
||||||
|
.with_bg(Color::black())
|
||||||
|
.with_alpha(89)
|
||||||
|
.with_radius(3)
|
||||||
|
.render(target);
|
||||||
|
|
||||||
let style = theme::TEXT_DEMIBOLD;
|
let style = theme::TEXT_DEMIBOLD;
|
||||||
let pos = Point::new(self.pad.area.center().x, LABEL_Y);
|
let pos = Point::new(self.pad.area.center().x, LABEL_Y);
|
||||||
shape::Text::new(pos, t)
|
shape::Text::new(pos, t)
|
||||||
@ -355,15 +362,15 @@ impl crate::trace::Trace for Homescreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Lockscreen {
|
pub struct Lockscreen<'a> {
|
||||||
label: TString<'static>,
|
label: TString<'a>,
|
||||||
custom_image: Option<Gc<[u8]>>,
|
custom_image: Option<Gc<[u8]>>,
|
||||||
bootscreen: bool,
|
bootscreen: bool,
|
||||||
coinjoin_authorized: bool,
|
coinjoin_authorized: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lockscreen {
|
impl<'a> Lockscreen<'a> {
|
||||||
pub fn new(label: TString<'static>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
|
pub fn new(label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
|
||||||
Lockscreen {
|
Lockscreen {
|
||||||
label,
|
label,
|
||||||
custom_image: get_user_custom_image().ok(),
|
custom_image: get_user_custom_image().ok(),
|
||||||
@ -373,7 +380,7 @@ impl Lockscreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Lockscreen {
|
impl Component for Lockscreen<'_> {
|
||||||
type Msg = HomescreenMsg;
|
type Msg = HomescreenMsg;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
@ -470,7 +477,7 @@ impl Component for Lockscreen {
|
|||||||
shape::JpegImage::new(center, img_data)
|
shape::JpegImage::new(center, img_data)
|
||||||
.with_align(Alignment2D::CENTER)
|
.with_align(Alignment2D::CENTER)
|
||||||
.with_blur(4)
|
.with_blur(4)
|
||||||
.with_dim(130)
|
.with_dim(140)
|
||||||
.render(target);
|
.render(target);
|
||||||
} else if is_image_toif(img_data) {
|
} else if is_image_toif(img_data) {
|
||||||
shape::ToifImage::new(center, unwrap!(Toif::new(img_data)))
|
shape::ToifImage::new(center, unwrap!(Toif::new(img_data)))
|
||||||
@ -591,7 +598,7 @@ fn is_image_toif(buffer: &[u8]) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl crate::trace::Trace for Lockscreen {
|
impl crate::trace::Trace for Lockscreen<'_> {
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("Lockscreen");
|
t.component("Lockscreen");
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ use heapless::String;
|
|||||||
const MAX_LENGTH: usize = 8;
|
const MAX_LENGTH: usize = 8;
|
||||||
|
|
||||||
pub struct Bip39Input {
|
pub struct Bip39Input {
|
||||||
button: Button<&'static str>,
|
button: Button<>,
|
||||||
// used only to keep track of suggestion text color
|
// used only to keep track of suggestion text color
|
||||||
button_suggestion: Button<&'static str>,
|
button_suggestion: Button<>,
|
||||||
textbox: TextBox<MAX_LENGTH>,
|
textbox: TextBox<MAX_LENGTH>,
|
||||||
multi_tap: MultiTapKeyboard,
|
multi_tap: MultiTapKeyboard,
|
||||||
options_num: Option<usize>,
|
options_num: Option<usize>,
|
||||||
@ -228,7 +228,7 @@ impl Bip39Input {
|
|||||||
// Styling the input to reflect already filled word
|
// Styling the input to reflect already filled word
|
||||||
Self {
|
Self {
|
||||||
button: Button::with_icon(theme::ICON_LIST_CHECK).styled(theme::button_pin_confirm()),
|
button: Button::with_icon(theme::ICON_LIST_CHECK).styled(theme::button_pin_confirm()),
|
||||||
textbox: TextBox::new(String::from(word)),
|
textbox: TextBox::new(unwrap!(String::try_from(word))),
|
||||||
multi_tap: MultiTapKeyboard::new(),
|
multi_tap: MultiTapKeyboard::new(),
|
||||||
options_num: bip39::options_num(word),
|
options_num: bip39::options_num(word),
|
||||||
suggested_word: bip39::complete_word(word),
|
suggested_word: bip39::complete_word(word),
|
||||||
@ -310,7 +310,7 @@ impl Bip39Input {
|
|||||||
// Disabled button.
|
// Disabled button.
|
||||||
self.button.disable(ctx);
|
self.button.disable(ctx);
|
||||||
self.button.set_stylesheet(ctx, theme::button_pin());
|
self.button.set_stylesheet(ctx, theme::button_pin());
|
||||||
self.button.set_content(ctx, ButtonContent::Text(""));
|
self.button.set_content(ctx, ButtonContent::Text("".into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use crate::ui::{
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe},
|
component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe},
|
||||||
geometry::{Alignment2D, Grid, Offset, Rect},
|
geometry::{Alignment2D, Grid, Offset, Rect},
|
||||||
model_mercury::{
|
model_mercury::{
|
||||||
@ -6,6 +8,7 @@ use crate::ui::{
|
|||||||
theme,
|
theme,
|
||||||
},
|
},
|
||||||
shape::Renderer,
|
shape::Renderer,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MNEMONIC_KEY_COUNT: usize = 9;
|
pub const MNEMONIC_KEY_COUNT: usize = 9;
|
||||||
@ -15,27 +18,26 @@ pub enum MnemonicKeyboardMsg {
|
|||||||
Previous,
|
Previous,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MnemonicKeyboard<T, U> {
|
pub struct MnemonicKeyboard<T> {
|
||||||
/// Initial prompt, displayed on empty input.
|
/// Initial prompt, displayed on empty input.
|
||||||
prompt: Child<Maybe<Label<U>>>,
|
prompt: Child<Maybe<Label<'static>>>,
|
||||||
/// Backspace button.
|
/// Backspace button.
|
||||||
back: Child<Maybe<Button<&'static str>>>,
|
back: Child<Maybe<Button>>,
|
||||||
/// Input area, acting as the auto-complete and confirm button.
|
/// Input area, acting as the auto-complete and confirm button.
|
||||||
input: Child<Maybe<T>>,
|
input: Child<Maybe<T>>,
|
||||||
/// Key buttons.
|
/// Key buttons.
|
||||||
keys: [Child<Button<&'static str>>; MNEMONIC_KEY_COUNT],
|
keys: [Child<Button>; MNEMONIC_KEY_COUNT],
|
||||||
/// Swipe controller - allowing for going to the previous word.
|
/// Swipe controller - allowing for going to the previous word.
|
||||||
swipe: Swipe,
|
swipe: Swipe,
|
||||||
/// Whether going back is allowed (is not on the very first word).
|
/// Whether going back is allowed (is not on the very first word).
|
||||||
can_go_back: bool,
|
can_go_back: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> MnemonicKeyboard<T, U>
|
impl<T> MnemonicKeyboard<T>
|
||||||
where
|
where
|
||||||
T: MnemonicInput,
|
T: MnemonicInput,
|
||||||
U: AsRef<str>,
|
|
||||||
{
|
{
|
||||||
pub fn new(input: T, prompt: U, can_go_back: bool) -> Self {
|
pub fn new(input: T, prompt: TString<'static>, can_go_back: bool) -> Self {
|
||||||
// Input might be already pre-filled
|
// Input might be already pre-filled
|
||||||
let prompt_visible = input.is_empty();
|
let prompt_visible = input.is_empty();
|
||||||
|
|
||||||
@ -58,7 +60,7 @@ where
|
|||||||
)),
|
)),
|
||||||
input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)),
|
input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)),
|
||||||
keys: T::keys()
|
keys: T::keys()
|
||||||
.map(|t| Button::with_text(t).styled(theme::button_pin()))
|
.map(|t| Button::with_text(t.into()).styled(theme::button_pin()))
|
||||||
.map(Child::new),
|
.map(Child::new),
|
||||||
swipe: Swipe::new().right(),
|
swipe: Swipe::new().right(),
|
||||||
can_go_back,
|
can_go_back,
|
||||||
@ -100,10 +102,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Component for MnemonicKeyboard<T, U>
|
impl<T> Component for MnemonicKeyboard<T>
|
||||||
where
|
where
|
||||||
T: MnemonicInput,
|
T: MnemonicInput,
|
||||||
U: AsRef<str>,
|
|
||||||
{
|
{
|
||||||
type Msg = MnemonicKeyboardMsg;
|
type Msg = MnemonicKeyboardMsg;
|
||||||
|
|
||||||
@ -221,10 +222,9 @@ pub enum MnemonicInputMsg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T, U> crate::trace::Trace for MnemonicKeyboard<T, U>
|
impl<T> crate::trace::Trace for MnemonicKeyboard<T>
|
||||||
where
|
where
|
||||||
T: MnemonicInput + crate::trace::Trace,
|
T: MnemonicInput + crate::trace::Trace,
|
||||||
U: AsRef<str>,
|
|
||||||
{
|
{
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("MnemonicKeyboard");
|
t.component("MnemonicKeyboard");
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use crate::ui::{
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never,
|
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never,
|
||||||
},
|
},
|
||||||
@ -13,6 +15,7 @@ use crate::ui::{
|
|||||||
shape,
|
shape,
|
||||||
shape::Renderer,
|
shape::Renderer,
|
||||||
util::long_line_content_with_ellipsis,
|
util::long_line_content_with_ellipsis,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
@ -25,9 +28,9 @@ pub enum PassphraseKeyboardMsg {
|
|||||||
pub struct PassphraseKeyboard {
|
pub struct PassphraseKeyboard {
|
||||||
page_swipe: Swipe,
|
page_swipe: Swipe,
|
||||||
input: Child<Input>,
|
input: Child<Input>,
|
||||||
back: Child<Button<&'static str>>,
|
back: Child<Button>,
|
||||||
confirm: Child<Button<&'static str>>,
|
confirm: Child<Button>,
|
||||||
keys: [Child<Button<&'static str>>; KEY_COUNT],
|
keys: [Child<Button>; KEY_COUNT],
|
||||||
scrollbar: ScrollBar,
|
scrollbar: ScrollBar,
|
||||||
fade: Cell<bool>,
|
fade: Cell<bool>,
|
||||||
}
|
}
|
||||||
@ -71,20 +74,20 @@ impl PassphraseKeyboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_text(content: &ButtonContent<&'static str>) -> &'static str {
|
fn key_text(content: &ButtonContent) -> TString<'static> {
|
||||||
match content {
|
match content {
|
||||||
ButtonContent::Text(text) => text,
|
ButtonContent::Text(text) => *text,
|
||||||
ButtonContent::Icon(_) => " ",
|
ButtonContent::Icon(_) => " ".into(),
|
||||||
ButtonContent::IconAndText(_) => " ",
|
ButtonContent::IconAndText(_) => " ".into(),
|
||||||
ButtonContent::Empty => "",
|
ButtonContent::Empty => "".into(),
|
||||||
ButtonContent::IconBlend(_, _, _) => "",
|
ButtonContent::IconBlend(_, _, _) => "".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_content(text: &'static str) -> ButtonContent<&'static str> {
|
fn key_content(text: &'static str) -> ButtonContent {
|
||||||
match text {
|
match text {
|
||||||
" " => ButtonContent::Icon(theme::ICON_SPACE),
|
" " => ButtonContent::Icon(theme::ICON_SPACE),
|
||||||
t => ButtonContent::Text(t),
|
t => ButtonContent::Text(t.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +277,7 @@ impl Component for PassphraseKeyboard {
|
|||||||
// character in textbox. If not, let's just append the first character.
|
// character in textbox. If not, let's just append the first character.
|
||||||
let text = Self::key_text(btn.inner().content());
|
let text = Self::key_text(btn.inner().content());
|
||||||
self.input.mutate(ctx, |ctx, i| {
|
self.input.mutate(ctx, |ctx, i| {
|
||||||
let edit = i.multi_tap.click_key(ctx, key, text);
|
let edit = text.map(|c| i.multi_tap.click_key(ctx, key, c));
|
||||||
i.textbox.apply(ctx, edit);
|
i.textbox.apply(ctx, edit);
|
||||||
});
|
});
|
||||||
self.after_edit(ctx);
|
self.after_edit(ctx);
|
||||||
|
@ -2,6 +2,7 @@ use core::mem;
|
|||||||
use heapless::String;
|
use heapless::String;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
trezorhal::random,
|
trezorhal::random,
|
||||||
ui::{
|
ui::{
|
||||||
@ -41,32 +42,29 @@ const HEADER_PADDING: Insets = Insets::new(
|
|||||||
HEADER_PADDING_SIDE,
|
HEADER_PADDING_SIDE,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub struct PinKeyboard<T> {
|
pub struct PinKeyboard<'a> {
|
||||||
allow_cancel: bool,
|
allow_cancel: bool,
|
||||||
major_prompt: Child<Label<T>>,
|
major_prompt: Child<Label<'a>>,
|
||||||
minor_prompt: Child<Label<T>>,
|
minor_prompt: Child<Label<'a>>,
|
||||||
major_warning: Option<Child<Label<T>>>,
|
major_warning: Option<Child<Label<'a>>>,
|
||||||
textbox: Child<PinDots>,
|
textbox: Child<PinDots>,
|
||||||
textbox_pad: Pad,
|
textbox_pad: Pad,
|
||||||
erase_btn: Child<Maybe<Button<&'static str>>>,
|
erase_btn: Child<Maybe<Button>>,
|
||||||
cancel_btn: Child<Maybe<Button<&'static str>>>,
|
cancel_btn: Child<Maybe<Button>>,
|
||||||
confirm_btn: Child<Button<&'static str>>,
|
confirm_btn: Child<Button>,
|
||||||
digit_btns: [Child<Button<&'static str>>; DIGIT_COUNT],
|
digit_btns: [Child<Button>; DIGIT_COUNT],
|
||||||
warning_timer: Option<TimerToken>,
|
warning_timer: Option<TimerToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PinKeyboard<T>
|
impl<'a> PinKeyboard<'a> {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
// Label position fine-tuning.
|
// Label position fine-tuning.
|
||||||
const MAJOR_OFF: Offset = Offset::y(11);
|
const MAJOR_OFF: Offset = Offset::y(11);
|
||||||
const MINOR_OFF: Offset = Offset::y(11);
|
const MINOR_OFF: Offset = Offset::y(11);
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
major_prompt: T,
|
major_prompt: TString<'a>,
|
||||||
minor_prompt: T,
|
minor_prompt: TString<'a>,
|
||||||
major_warning: Option<T>,
|
major_warning: Option<TString<'a>>,
|
||||||
allow_cancel: bool,
|
allow_cancel: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Control buttons.
|
// Control buttons.
|
||||||
@ -104,12 +102,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_digit_buttons() -> [Child<Button<&'static str>>; DIGIT_COUNT] {
|
fn generate_digit_buttons() -> [Child<Button>; DIGIT_COUNT] {
|
||||||
// Generate a random sequence of digits from 0 to 9.
|
// Generate a random sequence of digits from 0 to 9.
|
||||||
let mut digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
let mut digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
||||||
random::shuffle(&mut digits);
|
random::shuffle(&mut digits);
|
||||||
digits
|
digits
|
||||||
.map(Button::with_text)
|
.map(|c| Button::with_text(c.into()))
|
||||||
.map(|b| b.styled(theme::button_pin()))
|
.map(|b| b.styled(theme::button_pin()))
|
||||||
.map(Child::new)
|
.map(Child::new)
|
||||||
}
|
}
|
||||||
@ -148,10 +146,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for PinKeyboard<T>
|
impl Component for PinKeyboard<'_> {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
type Msg = PinKeyboardMsg;
|
type Msg = PinKeyboardMsg;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
@ -240,7 +235,9 @@ where
|
|||||||
for btn in &mut self.digit_btns {
|
for btn in &mut self.digit_btns {
|
||||||
if let Some(Clicked) = btn.event(ctx, event) {
|
if let Some(Clicked) = btn.event(ctx, event) {
|
||||||
if let ButtonContent::Text(text) = btn.inner().content() {
|
if let ButtonContent::Text(text) = btn.inner().content() {
|
||||||
|
text.map(|text| {
|
||||||
self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text));
|
self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text));
|
||||||
|
});
|
||||||
self.pin_modified(ctx);
|
self.pin_modified(ctx);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -551,10 +548,7 @@ impl Component for PinDots {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T> crate::trace::Trace for PinKeyboard<T>
|
impl crate::trace::Trace for PinKeyboard<'_> {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("PinKeyboard");
|
t.component("PinKeyboard");
|
||||||
// So that debuglink knows the locations of the buttons
|
// So that debuglink knows the locations of the buttons
|
||||||
@ -562,7 +556,9 @@ where
|
|||||||
for btn in self.digit_btns.iter() {
|
for btn in self.digit_btns.iter() {
|
||||||
let btn_content = btn.inner().content();
|
let btn_content = btn.inner().content();
|
||||||
if let ButtonContent::Text(text) = btn_content {
|
if let ButtonContent::Text(text) = btn_content {
|
||||||
|
text.map(|text| {
|
||||||
unwrap!(digits_order.push_str(text));
|
unwrap!(digits_order.push_str(text));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.string("digits_order", digits_order.as_str().into());
|
t.string("digits_order", digits_order.as_str().into());
|
||||||
|
@ -30,7 +30,7 @@ use crate::{
|
|||||||
const MAX_LENGTH: usize = 8;
|
const MAX_LENGTH: usize = 8;
|
||||||
|
|
||||||
pub struct Slip39Input {
|
pub struct Slip39Input {
|
||||||
button: Button<&'static str>,
|
button: Button,
|
||||||
textbox: TextBox<MAX_LENGTH>,
|
textbox: TextBox<MAX_LENGTH>,
|
||||||
multi_tap: MultiTapKeyboard,
|
multi_tap: MultiTapKeyboard,
|
||||||
final_word: Option<&'static str>,
|
final_word: Option<&'static str>,
|
||||||
@ -360,7 +360,7 @@ impl Slip39Input {
|
|||||||
} else {
|
} else {
|
||||||
// Disabled button.
|
// Disabled button.
|
||||||
self.button.disable(ctx);
|
self.button.disable(ctx);
|
||||||
self.button.set_content(ctx, ButtonContent::Text(""));
|
self.button.set_content(ctx, ButtonContent::Text("".into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ const LABELS: [&str; 5] = ["12", "18", "20", "24", "33"];
|
|||||||
const CELLS: [(usize, usize); 5] = [(0, 0), (0, 2), (0, 4), (1, 0), (1, 2)];
|
const CELLS: [(usize, usize); 5] = [(0, 0), (0, 2), (0, 4), (1, 0), (1, 2)];
|
||||||
|
|
||||||
pub struct SelectWordCount {
|
pub struct SelectWordCount {
|
||||||
button: [Button<&'static str>; NUMBERS.len()],
|
button: [Button; NUMBERS.len()],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SelectWordCountMsg {
|
pub enum SelectWordCountMsg {
|
||||||
@ -23,7 +23,7 @@ pub enum SelectWordCountMsg {
|
|||||||
impl SelectWordCount {
|
impl SelectWordCount {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
SelectWordCount {
|
SelectWordCount {
|
||||||
button: LABELS.map(|t| Button::with_text(t).styled(theme::button_pin())),
|
button: LABELS.map(|t| Button::with_text(t.into()).styled(theme::button_pin())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
micropython::buffer::StrBuffer,
|
strutil::{self, StringType, TString},
|
||||||
strutil::{self, StringType},
|
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
@ -23,23 +22,22 @@ pub enum NumberInputDialogMsg {
|
|||||||
InfoRequested,
|
InfoRequested,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NumberInputDialog<T, F>
|
pub struct NumberInputDialog<F>
|
||||||
where
|
where
|
||||||
F: Fn(u32) -> T,
|
F: Fn(u32) -> TString<'static>,
|
||||||
{
|
{
|
||||||
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>,
|
||||||
confirm_button: Child<Button<StrBuffer>>,
|
confirm_button: Child<Button>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, F> NumberInputDialog<T, F>
|
impl<F> NumberInputDialog<F>
|
||||||
where
|
where
|
||||||
F: Fn(u32) -> T,
|
F: Fn(u32) -> TString<'static>,
|
||||||
T: StringType,
|
|
||||||
{
|
{
|
||||||
pub fn new(min: u32, max: u32, init_value: u32, description_func: F) -> Result<Self, Error> {
|
pub fn new(min: u32, max: u32, init_value: u32, description_func: F) -> Result<Self, Error> {
|
||||||
let text = description_func(init_value);
|
let text = description_func(init_value);
|
||||||
@ -49,8 +47,8 @@ where
|
|||||||
input: NumberInput::new(min, max, init_value).into_child(),
|
input: NumberInput::new(min, max, init_value).into_child(),
|
||||||
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, text)).into_child(),
|
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, text)).into_child(),
|
||||||
paragraphs_pad: Pad::with_background(theme::BG),
|
paragraphs_pad: Pad::with_background(theme::BG),
|
||||||
info_button: Button::with_text(TR::buttons__info.try_into()?).into_child(),
|
info_button: Button::with_text(TR::buttons__info.into()).into_child(),
|
||||||
confirm_button: Button::with_text(TR::buttons__continue.try_into()?)
|
confirm_button: Button::with_text(TR::buttons__continue.into())
|
||||||
.styled(theme::button_confirm())
|
.styled(theme::button_confirm())
|
||||||
.into_child(),
|
.into_child(),
|
||||||
})
|
})
|
||||||
@ -73,10 +71,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, F> Component for NumberInputDialog<T, F>
|
impl<F> Component for NumberInputDialog<F>
|
||||||
where
|
where
|
||||||
T: StringType,
|
F: Fn(u32) -> TString<'static>,
|
||||||
F: Fn(u32) -> T,
|
|
||||||
{
|
{
|
||||||
type Msg = NumberInputDialogMsg;
|
type Msg = NumberInputDialogMsg;
|
||||||
|
|
||||||
@ -143,10 +140,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T, F> crate::trace::Trace for NumberInputDialog<T, F>
|
impl<F> crate::trace::Trace for NumberInputDialog<F>
|
||||||
where
|
where
|
||||||
T: StringType,
|
F: Fn(u32) -> TString<'static>,
|
||||||
F: Fn(u32) -> T,
|
|
||||||
{
|
{
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("NumberInputDialog");
|
t.component("NumberInputDialog");
|
||||||
@ -163,8 +159,8 @@ pub enum NumberInputMsg {
|
|||||||
|
|
||||||
pub struct NumberInput {
|
pub struct NumberInput {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
dec: Child<Button<&'static str>>,
|
dec: Child<Button>,
|
||||||
inc: Child<Button<&'static str>>,
|
inc: Child<Button>,
|
||||||
min: u32,
|
min: u32,
|
||||||
max: u32,
|
max: u32,
|
||||||
value: u32,
|
value: u32,
|
||||||
@ -172,10 +168,10 @@ pub struct NumberInput {
|
|||||||
|
|
||||||
impl NumberInput {
|
impl NumberInput {
|
||||||
pub fn new(min: u32, max: u32, value: u32) -> Self {
|
pub fn new(min: u32, max: u32, value: u32) -> Self {
|
||||||
let dec = Button::with_text("-")
|
let dec = Button::with_text("-".into())
|
||||||
.styled(theme::button_counter())
|
.styled(theme::button_counter())
|
||||||
.into_child();
|
.into_child();
|
||||||
let inc = Button::with_text("+")
|
let inc = Button::with_text("+".into())
|
||||||
.styled(theme::button_counter())
|
.styled(theme::button_counter())
|
||||||
.into_child();
|
.into_child();
|
||||||
let value = value.clamp(min, max);
|
let value = value.clamp(min, max);
|
||||||
@ -226,7 +222,7 @@ impl Component for NumberInput {
|
|||||||
let mut buf = [0u8; 10];
|
let mut buf = [0u8; 10];
|
||||||
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
||||||
let digit_font = Font::DEMIBOLD;
|
let digit_font = Font::DEMIBOLD;
|
||||||
let y_offset = digit_font.text_height() / 2 + Button::<&str>::BASELINE_OFFSET.y;
|
let y_offset = digit_font.text_height() / 2 + Button::BASELINE_OFFSET.y;
|
||||||
display::rect_fill(self.area, theme::BG);
|
display::rect_fill(self.area, theme::BG);
|
||||||
display::text_center(
|
display::text_center(
|
||||||
self.area.center() + Offset::y(y_offset),
|
self.area.center() + Offset::y(y_offset),
|
||||||
@ -245,7 +241,7 @@ impl Component for NumberInput {
|
|||||||
|
|
||||||
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
||||||
let digit_font = Font::DEMIBOLD;
|
let digit_font = Font::DEMIBOLD;
|
||||||
let y_offset = digit_font.text_height() / 2 + Button::<&str>::BASELINE_OFFSET.y;
|
let y_offset = digit_font.text_height() / 2 + Button::BASELINE_OFFSET.y;
|
||||||
|
|
||||||
shape::Bar::new(self.area).with_bg(theme::BG).render(target);
|
shape::Bar::new(self.area).with_bg(theme::BG).render(target);
|
||||||
shape::Text::new(self.area.center() + Offset::y(y_offset), text)
|
shape::Text::new(self.area.center() + Offset::y(y_offset), text)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
micropython::buffer::StrBuffer,
|
strutil::TString,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
@ -22,7 +22,7 @@ use core::cell::Cell;
|
|||||||
|
|
||||||
/// Allows pagination of inner component. Shows scroll bar, confirm & cancel
|
/// Allows pagination of inner component. Shows scroll bar, confirm & cancel
|
||||||
/// buttons. Optionally handles hold-to-confirm with loader.
|
/// buttons. Optionally handles hold-to-confirm with loader.
|
||||||
pub struct ButtonPage<T, U> {
|
pub struct ButtonPage<T> {
|
||||||
/// Inner component.
|
/// Inner component.
|
||||||
content: T,
|
content: T,
|
||||||
/// Cleared when page changes.
|
/// Cleared when page changes.
|
||||||
@ -32,10 +32,10 @@ pub struct ButtonPage<T, U> {
|
|||||||
scrollbar: ScrollBar,
|
scrollbar: ScrollBar,
|
||||||
/// Hold-to-confirm mode whenever this is `Some(loader)`.
|
/// Hold-to-confirm mode whenever this is `Some(loader)`.
|
||||||
loader: Option<Loader>,
|
loader: Option<Loader>,
|
||||||
button_cancel: Option<Button<U>>,
|
button_cancel: Option<Button>,
|
||||||
button_confirm: Button<U>,
|
button_confirm: Button,
|
||||||
button_prev: Button<&'static str>,
|
button_prev: Button,
|
||||||
button_next: Button<&'static str>,
|
button_next: Button,
|
||||||
/// Show cancel button instead of back button.
|
/// Show cancel button instead of back button.
|
||||||
cancel_from_any_page: bool,
|
cancel_from_any_page: bool,
|
||||||
/// Whether to pass-through left swipe to parent component.
|
/// Whether to pass-through left swipe to parent component.
|
||||||
@ -46,24 +46,23 @@ pub struct ButtonPage<T, U> {
|
|||||||
fade: Cell<Option<u16>>,
|
fade: Cell<Option<u16>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ButtonPage<T, StrBuffer>
|
impl<T> ButtonPage<T>
|
||||||
where
|
where
|
||||||
T: Paginate,
|
T: Paginate,
|
||||||
T: Component,
|
T: Component,
|
||||||
{
|
{
|
||||||
pub fn with_hold(mut self) -> Result<Self, Error> {
|
pub fn with_hold(mut self) -> Result<Self, Error> {
|
||||||
self.button_confirm = Button::with_text(TR::buttons__hold_to_confirm.try_into()?)
|
self.button_confirm =
|
||||||
.styled(theme::button_confirm());
|
Button::with_text(TR::buttons__hold_to_confirm.into()).styled(theme::button_confirm());
|
||||||
self.loader = Some(Loader::new());
|
self.loader = Some(Loader::new());
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> ButtonPage<T, U>
|
impl<T> ButtonPage<T>
|
||||||
where
|
where
|
||||||
T: Paginate,
|
T: Paginate,
|
||||||
T: Component,
|
T: Component,
|
||||||
U: AsRef<str> + From<&'static str>,
|
|
||||||
{
|
{
|
||||||
pub fn new(content: T, background: Color) -> Self {
|
pub fn new(content: T, background: Color) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -88,13 +87,17 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_cancel_confirm(mut self, left: Option<U>, right: Option<U>) -> Self {
|
pub fn with_cancel_confirm(
|
||||||
|
mut self,
|
||||||
|
left: Option<TString<'static>>,
|
||||||
|
right: Option<TString<'static>>,
|
||||||
|
) -> Self {
|
||||||
let cancel = match left {
|
let cancel = match left {
|
||||||
Some(verb) => match verb.as_ref() {
|
Some(verb) => verb.map(|s| match s {
|
||||||
"^" => Button::with_icon(theme::ICON_UP),
|
"^" => Button::with_icon(theme::ICON_UP),
|
||||||
"<" => Button::with_icon(theme::ICON_BACK),
|
"<" => Button::with_icon(theme::ICON_BACK),
|
||||||
_ => Button::with_text(verb),
|
_ => Button::with_text(verb),
|
||||||
},
|
}),
|
||||||
_ => Button::with_icon(theme::ICON_CANCEL),
|
_ => Button::with_icon(theme::ICON_CANCEL),
|
||||||
};
|
};
|
||||||
let confirm = match right {
|
let confirm = match right {
|
||||||
@ -285,11 +288,10 @@ enum HandleResult<T> {
|
|||||||
Continue,
|
Continue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Component for ButtonPage<T, U>
|
impl<T> Component for ButtonPage<T>
|
||||||
where
|
where
|
||||||
T: Paginate,
|
T: Paginate,
|
||||||
T: Component,
|
T: Component,
|
||||||
U: AsRef<str> + From<&'static str>,
|
|
||||||
{
|
{
|
||||||
type Msg = PageMsg<T::Msg>;
|
type Msg = PageMsg<T::Msg>;
|
||||||
|
|
||||||
@ -297,7 +299,7 @@ where
|
|||||||
let small_left_button = match (&self.button_cancel, &self.button_confirm) {
|
let small_left_button = match (&self.button_cancel, &self.button_confirm) {
|
||||||
(None, _) => true,
|
(None, _) => true,
|
||||||
(Some(cancel), confirm) => match (cancel.content(), confirm.content()) {
|
(Some(cancel), confirm) => match (cancel.content(), confirm.content()) {
|
||||||
(ButtonContent::Text(t), _) => t.as_ref().len() <= 4,
|
(ButtonContent::Text(t), _) => t.len() <= 4,
|
||||||
(ButtonContent::Icon(_), ButtonContent::Icon(_)) => false,
|
(ButtonContent::Icon(_), ButtonContent::Icon(_)) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
},
|
},
|
||||||
@ -455,7 +457,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T, U> crate::trace::Trace for ButtonPage<T, U>
|
impl<T> crate::trace::Trace for ButtonPage<T>
|
||||||
where
|
where
|
||||||
T: crate::trace::Trace,
|
T: crate::trace::Trace,
|
||||||
{
|
{
|
||||||
@ -532,12 +534,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;
|
||||||
@ -567,10 +563,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn paragraphs_empty() {
|
fn paragraphs_empty() {
|
||||||
let mut page = ButtonPage::<_, &'static str>::new(
|
let mut page = ButtonPage::new(Paragraphs::<[Paragraph<'static>; 0]>::new([]), theme::BG);
|
||||||
Paragraphs::<[Paragraph<&'static str>; 0]>::new([]),
|
|
||||||
theme::BG,
|
|
||||||
);
|
|
||||||
page.place(SCREEN);
|
page.place(SCREEN);
|
||||||
|
|
||||||
let expected = serde_json::json!({
|
let expected = serde_json::json!({
|
||||||
@ -593,7 +586,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn paragraphs_single() {
|
fn paragraphs_single() {
|
||||||
let mut page = ButtonPage::<_, &'static str>::new(
|
let mut page = ButtonPage::new(
|
||||||
Paragraphs::new([
|
Paragraphs::new([
|
||||||
Paragraph::new(
|
Paragraph::new(
|
||||||
&theme::TEXT_NORMAL,
|
&theme::TEXT_NORMAL,
|
||||||
@ -631,7 +624,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn paragraphs_one_long() {
|
fn paragraphs_one_long() {
|
||||||
let mut page = ButtonPage::<_, &'static str>::new(
|
let mut page = ButtonPage::new(
|
||||||
Paragraphs::new(
|
Paragraphs::new(
|
||||||
Paragraph::new(
|
Paragraph::new(
|
||||||
&theme::TEXT_BOLD,
|
&theme::TEXT_BOLD,
|
||||||
@ -689,7 +682,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn paragraphs_three_long() {
|
fn paragraphs_three_long() {
|
||||||
let mut page = ButtonPage::<_, &'static str>::new(
|
let mut page = ButtonPage::new(
|
||||||
Paragraphs::new([
|
Paragraphs::new([
|
||||||
Paragraph::new(
|
Paragraph::new(
|
||||||
&theme::TEXT_BOLD,
|
&theme::TEXT_BOLD,
|
||||||
@ -789,7 +782,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn paragraphs_hard_break() {
|
fn paragraphs_hard_break() {
|
||||||
let mut page = ButtonPage::<_, &'static str>::new(
|
let mut page = ButtonPage::new(
|
||||||
Paragraphs::new([
|
Paragraphs::new([
|
||||||
Paragraph::new(&theme::TEXT_NORMAL, "Short one.").break_after(),
|
Paragraph::new(&theme::TEXT_NORMAL, "Short one.").break_after(),
|
||||||
Paragraph::new(&theme::TEXT_NORMAL, "Short two.").break_after(),
|
Paragraph::new(&theme::TEXT_NORMAL, "Short two.").break_after(),
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
strutil::TString,
|
||||||
strutil::StringType,
|
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
base::ComponentExt,
|
base::ComponentExt,
|
||||||
@ -21,27 +20,22 @@ use crate::{
|
|||||||
|
|
||||||
use super::theme;
|
use super::theme;
|
||||||
|
|
||||||
pub struct Progress<T> {
|
pub struct Progress {
|
||||||
title: Child<Label<T>>,
|
title: Child<Label<'static>>,
|
||||||
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 Progress {
|
||||||
where
|
|
||||||
T: StringType,
|
|
||||||
{
|
|
||||||
const AREA: Rect = constant::screen().inset(theme::borders());
|
const AREA: Rect = constant::screen().inset(theme::borders());
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
title: T,
|
title: TString<'static>,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
description: T,
|
description: TString<'static>,
|
||||||
update_description: fn(&str) -> Result<T, Error>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: Label::centered(title, theme::label_progress()).into_child(),
|
title: Label::centered(title, theme::label_progress()).into_child(),
|
||||||
@ -53,15 +47,11 @@ where
|
|||||||
)
|
)
|
||||||
.into_child(),
|
.into_child(),
|
||||||
description_pad: Pad::with_background(theme::BG),
|
description_pad: Pad::with_background(theme::BG),
|
||||||
update_description,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for Progress<T>
|
impl Component for Progress {
|
||||||
where
|
|
||||||
T: StringType,
|
|
||||||
{
|
|
||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
fn place(&mut self, _bounds: Rect) -> Rect {
|
fn place(&mut self, _bounds: Rect) -> Rect {
|
||||||
@ -70,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);
|
||||||
@ -92,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();
|
||||||
@ -172,10 +158,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T> crate::trace::Trace for Progress<T>
|
impl crate::trace::Trace for Progress {
|
||||||
where
|
|
||||||
T: StringType,
|
|
||||||
{
|
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("Progress");
|
t.component("Progress");
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
use super::theme;
|
use super::theme;
|
||||||
use crate::ui::{
|
use crate::{
|
||||||
|
micropython::buffer::StrBuffer,
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
component::{base::Component, Event, EventCtx},
|
component::{base::Component, Event, EventCtx},
|
||||||
display::Icon,
|
display::Icon,
|
||||||
geometry::Rect,
|
geometry::Rect,
|
||||||
model_mercury::component::button::{Button, ButtonMsg, IconText},
|
model_mercury::component::button::{Button, ButtonMsg, IconText},
|
||||||
shape::{Bar, Renderer},
|
shape::{Bar, Renderer},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum VerticalMenuChoiceMsg {
|
pub enum VerticalMenuChoiceMsg {
|
||||||
@ -27,39 +31,37 @@ const MENU_BUTTON_HEIGHT: i16 = 64;
|
|||||||
/// Fixed height of a separator.
|
/// Fixed height of a separator.
|
||||||
const MENU_SEP_HEIGHT: i16 = 2;
|
const MENU_SEP_HEIGHT: i16 = 2;
|
||||||
|
|
||||||
type VerticalMenuButtons<T> = Vec<Button<T>, N_ITEMS>;
|
type VerticalMenuButtons = Vec<Button, N_ITEMS>;
|
||||||
type AreasForSeparators = Vec<Rect, N_SEPS>;
|
type AreasForSeparators = Vec<Rect, N_SEPS>;
|
||||||
|
|
||||||
pub struct VerticalMenu<T> {
|
pub struct VerticalMenu {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
/// buttons placed vertically from top to bottom
|
/// buttons placed vertically from top to bottom
|
||||||
buttons: VerticalMenuButtons<T>,
|
buttons: VerticalMenuButtons,
|
||||||
/// areas for visual separators between buttons
|
/// areas for visual separators between buttons
|
||||||
areas_sep: AreasForSeparators,
|
areas_sep: AreasForSeparators,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> VerticalMenu<T>
|
impl VerticalMenu {
|
||||||
where
|
fn new(buttons: VerticalMenuButtons) -> Self {
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
fn new(buttons: VerticalMenuButtons<T>) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
area: Rect::zero(),
|
area: Rect::zero(),
|
||||||
buttons,
|
buttons,
|
||||||
areas_sep: AreasForSeparators::new(),
|
areas_sep: AreasForSeparators::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn select_word(words: [T; 3]) -> Self {
|
pub fn select_word(words: [StrBuffer; 3]) -> Self {
|
||||||
let mut buttons_vec = VerticalMenuButtons::new();
|
let mut buttons_vec = VerticalMenuButtons::new();
|
||||||
for word in words {
|
for word in words {
|
||||||
let button = Button::with_text(word).styled(theme::button_vertical_menu());
|
let button = Button::with_text(word.into()).styled(theme::button_vertical_menu());
|
||||||
unwrap!(buttons_vec.push(button));
|
unwrap!(buttons_vec.push(button));
|
||||||
}
|
}
|
||||||
Self::new(buttons_vec)
|
Self::new(buttons_vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_menu(options: [(T, Icon); 3]) -> Self {
|
pub fn context_menu(options: [(&'static str, Icon); 3]) -> Self {
|
||||||
// TODO: this is just POC
|
// TODO: this is just POC
|
||||||
|
// FIXME: args should be TString when IconText has TString
|
||||||
let mut buttons_vec = VerticalMenuButtons::new();
|
let mut buttons_vec = VerticalMenuButtons::new();
|
||||||
for opt in options {
|
for opt in options {
|
||||||
let button_theme;
|
let button_theme;
|
||||||
@ -79,10 +81,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for VerticalMenu<T>
|
impl Component for VerticalMenu {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
type Msg = VerticalMenuChoiceMsg;
|
type Msg = VerticalMenuChoiceMsg;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
@ -141,10 +140,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_debug")]
|
#[cfg(feature = "ui_debug")]
|
||||||
impl<T> crate::trace::Trace for VerticalMenu<T>
|
impl crate::trace::Trace for VerticalMenu {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("VerticalMenu");
|
t.component("VerticalMenu");
|
||||||
t.in_list("buttons", &|button_list| {
|
t.in_list("buttons", &|button_list| {
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user