mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-22 20:42:03 +00:00
feat(eckhart): full-screen select word component
This commit is contained in:
parent
ee1a3112c9
commit
b84a983678
@ -5,6 +5,7 @@ mod error;
|
||||
mod header;
|
||||
mod hint;
|
||||
mod result;
|
||||
mod select_word_screen;
|
||||
mod share_words;
|
||||
mod text_screen;
|
||||
mod vertical_menu;
|
||||
@ -17,6 +18,7 @@ pub use error::ErrorScreen;
|
||||
pub use header::{Header, HeaderMsg};
|
||||
pub use hint::Hint;
|
||||
pub use result::{ResultFooter, ResultScreen, ResultStyle};
|
||||
pub use select_word_screen::{SelectWordMsg, SelectWordScreen};
|
||||
#[cfg(feature = "translations")]
|
||||
pub use share_words::{ShareWordsScreen, ShareWordsScreenMsg};
|
||||
pub use text_screen::{AllowedTextContent, TextScreen, TextScreenMsg};
|
||||
|
@ -0,0 +1,107 @@
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx, Label},
|
||||
geometry::{Alignment, Insets, Rect},
|
||||
layout_eckhart::{
|
||||
component::{Button, Header, HeaderMsg, VerticalMenu, VerticalMenuMsg},
|
||||
constant::SCREEN,
|
||||
theme,
|
||||
},
|
||||
shape::Renderer,
|
||||
ui_firmware::MAX_WORD_QUIZ_ITEMS,
|
||||
},
|
||||
};
|
||||
|
||||
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
|
||||
// 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::{
|
||||
component::{ActionBar, Button, Header, HeaderMsg, Hint, TextScreen},
|
||||
component::{ActionBar, Button, Header, HeaderMsg, Hint, SelectWordScreen, TextScreen},
|
||||
flow, fonts, theme, UIEckhart,
|
||||
};
|
||||
|
||||
@ -389,11 +389,18 @@ impl FirmwareUI for UIEckhart {
|
||||
}
|
||||
|
||||
fn select_word(
|
||||
_title: TString<'static>,
|
||||
_description: TString<'static>,
|
||||
_words: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
|
||||
title: TString<'static>,
|
||||
description: TString<'static>,
|
||||
words: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
|
||||
) -> 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> {
|
||||
|
Loading…
Reference in New Issue
Block a user