mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-24 21:32:03 +00:00
feat(eckhart): full-screen select word component
This commit is contained in:
parent
3b2085e27e
commit
7434524d2a
@ -6,6 +6,7 @@ mod header;
|
|||||||
mod hint;
|
mod hint;
|
||||||
mod hold_to_confirm;
|
mod hold_to_confirm;
|
||||||
mod result;
|
mod result;
|
||||||
|
mod select_word_screen;
|
||||||
mod share_words;
|
mod share_words;
|
||||||
mod text_screen;
|
mod text_screen;
|
||||||
mod vertical_menu;
|
mod vertical_menu;
|
||||||
@ -19,6 +20,7 @@ pub use header::{Header, HeaderMsg};
|
|||||||
pub use hint::Hint;
|
pub use hint::Hint;
|
||||||
pub use hold_to_confirm::HoldToConfirmAnim;
|
pub use hold_to_confirm::HoldToConfirmAnim;
|
||||||
pub use result::{ResultFooter, ResultScreen, ResultStyle};
|
pub use result::{ResultFooter, ResultScreen, ResultStyle};
|
||||||
|
pub use select_word_screen::{SelectWordMsg, SelectWordScreen};
|
||||||
#[cfg(feature = "translations")]
|
#[cfg(feature = "translations")]
|
||||||
pub use share_words::{ShareWordsScreen, ShareWordsScreenMsg};
|
pub use share_words::{ShareWordsScreen, ShareWordsScreenMsg};
|
||||||
pub use text_screen::{AllowedTextContent, TextScreen, TextScreenMsg};
|
pub use text_screen::{AllowedTextContent, TextScreen, TextScreenMsg};
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx, Label},
|
||||||
|
geometry::{Alignment, Insets, Rect},
|
||||||
|
shape::Renderer,
|
||||||
|
ui_firmware::MAX_WORD_QUIZ_ITEMS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
component::{Button, Header, HeaderMsg, VerticalMenu, VerticalMenuMsg},
|
||||||
|
constant::SCREEN,
|
||||||
|
theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SelectWordScreen {
|
||||||
|
header: Header,
|
||||||
|
description: Label<'static>,
|
||||||
|
menu: VerticalMenu,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SelectWordMsg {
|
||||||
|
Selected(usize),
|
||||||
|
/// Right header button clicked
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectWordScreen {
|
||||||
|
const INSET: i16 = 24;
|
||||||
|
const DESCRIPTION_HEIGHT: i16 = 52;
|
||||||
|
const BUTTON_RADIUS: u8 = 12;
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
share_words_vec: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
|
||||||
|
description: TString<'static>,
|
||||||
|
) -> Self {
|
||||||
|
let mut menu = VerticalMenu::empty().with_separators().with_fit_area();
|
||||||
|
|
||||||
|
for word in share_words_vec {
|
||||||
|
menu = menu.item(
|
||||||
|
Button::with_text(word)
|
||||||
|
.styled(theme::button_select_word())
|
||||||
|
.with_radius(Self::BUTTON_RADIUS),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
header: Header::new(TString::empty()),
|
||||||
|
description: Label::new(description, Alignment::Start, theme::TEXT_MEDIUM)
|
||||||
|
.vertically_centered(),
|
||||||
|
menu,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_header(mut self, header: Header) -> Self {
|
||||||
|
self.header = header;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for SelectWordScreen {
|
||||||
|
type Msg = SelectWordMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
// assert full screen
|
||||||
|
debug_assert_eq!(bounds.height(), SCREEN.height());
|
||||||
|
debug_assert_eq!(bounds.width(), SCREEN.width());
|
||||||
|
|
||||||
|
let (header_area, rest) = bounds.split_top(Header::HEADER_HEIGHT);
|
||||||
|
let (description_area, rest) = rest.split_top(Self::DESCRIPTION_HEIGHT);
|
||||||
|
let (_, rest) = rest.split_top(Self::INSET);
|
||||||
|
let (menu_area, _) = rest.split_bottom(Self::INSET);
|
||||||
|
|
||||||
|
let description_area = description_area.inset(Insets::sides(Self::INSET));
|
||||||
|
|
||||||
|
self.menu.place(menu_area);
|
||||||
|
self.description.place(description_area);
|
||||||
|
self.header.place(header_area);
|
||||||
|
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(HeaderMsg::Cancelled) = self.header.event(ctx, event) {
|
||||||
|
return Some(SelectWordMsg::Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(VerticalMenuMsg::Selected(i)) = self.menu.event(ctx, event) {
|
||||||
|
return Some(SelectWordMsg::Selected(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.header.render(target);
|
||||||
|
self.description.render(target);
|
||||||
|
self.menu.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for SelectWordScreen {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("SelectWordScreen");
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,9 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::component::{AllowedTextContent, TextScreen, TextScreenMsg};
|
use super::component::{
|
||||||
|
AllowedTextContent, SelectWordMsg, SelectWordScreen, TextScreen, TextScreenMsg,
|
||||||
|
};
|
||||||
|
|
||||||
// Clippy/compiler complains about conflicting implementations
|
// Clippy/compiler complains about conflicting implementations
|
||||||
// TODO move the common impls to a common module
|
// TODO move the common impls to a common module
|
||||||
@ -50,3 +52,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComponentMsgObj for SelectWordScreen {
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
SelectWordMsg::Selected(i) => i.try_into(),
|
||||||
|
SelectWordMsg::Cancelled => Ok(CANCELLED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,7 +24,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
component::{ActionBar, Button, Header, HeaderMsg, Hint, TextScreen},
|
component::{ActionBar, Button, Header, HeaderMsg, Hint, SelectWordScreen, TextScreen},
|
||||||
flow, fonts, theme, UIEckhart,
|
flow, fonts, theme, UIEckhart,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -389,11 +389,18 @@ impl FirmwareUI for UIEckhart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn select_word(
|
fn select_word(
|
||||||
_title: TString<'static>,
|
title: TString<'static>,
|
||||||
_description: TString<'static>,
|
description: TString<'static>,
|
||||||
_words: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
|
words: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
|
||||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
let component = SelectWordScreen::new(words, description).with_header(
|
||||||
|
Header::new(title)
|
||||||
|
.with_right_button(Button::with_icon(theme::ICON_MENU), HeaderMsg::Cancelled),
|
||||||
|
);
|
||||||
|
|
||||||
|
let layout = RootComponent::new(component);
|
||||||
|
|
||||||
|
Ok(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_word_count(_recovery_type: RecoveryType) -> Result<impl LayoutMaybeTrace, Error> {
|
fn select_word_count(_recovery_type: RecoveryType) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
Loading…
Reference in New Issue
Block a user