mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
fixup! refactor(core/rust): make choice page more reusable by allowing custom content
This commit is contained in:
parent
3a93a28882
commit
1ce85119c3
@ -13,9 +13,6 @@ use super::{
|
||||
};
|
||||
use heapless::{String, Vec};
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::trace::Tracer;
|
||||
|
||||
pub enum Bip39EntryMsg {
|
||||
ResultWord(String<15>),
|
||||
}
|
||||
@ -99,19 +96,11 @@ impl ChoiceFactoryBIP39 {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, choice_index: u8) -> ChoiceItem {
|
||||
if self.letter_choices.is_some() {
|
||||
self.get_letter_item(choice_index)
|
||||
} else if self.word_choices.is_some() {
|
||||
self.get_word_item(choice_index)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChoiceFactory for ChoiceFactoryBIP39 {
|
||||
type Item = ChoiceItem;
|
||||
|
||||
fn count(&self) -> u8 {
|
||||
if let Some(letter_choices) = &self.letter_choices {
|
||||
letter_choices.len() as u8
|
||||
@ -122,29 +111,14 @@ impl ChoiceFactory for ChoiceFactoryBIP39 {
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_center(&self, choice_index: u8, area: Rect, inverse: bool) {
|
||||
self.get(choice_index).paint_center(area, inverse);
|
||||
}
|
||||
|
||||
fn width_center(&self, choice_index: u8) -> i16 {
|
||||
self.get(choice_index).width_center()
|
||||
}
|
||||
|
||||
fn paint_left(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_left(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn paint_right(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_right(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn btn_layout(&self, choice_index: u8) -> ButtonLayout<&'static str> {
|
||||
self.get(choice_index).btn_layout()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
fn trace(&self, t: &mut dyn Tracer, name: &str, choice_index: u8) {
|
||||
t.field(name, &self.get(choice_index));
|
||||
fn get(&self, choice_index: u8) -> ChoiceItem {
|
||||
if self.letter_choices.is_some() {
|
||||
self.get_letter_item(choice_index)
|
||||
} else if self.word_choices.is_some() {
|
||||
self.get_word_item(choice_index)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::trace::Trace;
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Pad},
|
||||
geometry::Rect,
|
||||
@ -15,6 +17,22 @@ pub enum ChoicePageMsg {
|
||||
const DEFAULT_ITEMS_DISTANCE: i16 = 10;
|
||||
const DEFAULT_Y_BASELINE: i16 = 20;
|
||||
|
||||
pub trait Choice {
|
||||
fn paint_center(&self, area: Rect, inverse: bool);
|
||||
fn width_center(&self) -> i16 {
|
||||
0
|
||||
}
|
||||
fn paint_left(&self, _area: Rect, _show_incomplete: bool) -> Option<i16> {
|
||||
None
|
||||
}
|
||||
fn paint_right(&self, _area: Rect, _show_incomplete: bool) -> Option<i16> {
|
||||
None
|
||||
}
|
||||
fn btn_layout(&self) -> ButtonLayout<&'static str> {
|
||||
ButtonLayout::default_three_icons()
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface for a specific component efficiently giving
|
||||
/// `ChoicePage` all the information it needs to render
|
||||
/// all the choice pages.
|
||||
@ -25,23 +43,12 @@ const DEFAULT_Y_BASELINE: i16 = 20;
|
||||
/// items only when they are needed, one-by-one.
|
||||
/// This way, no more than one item is stored in memory at any time.
|
||||
pub trait ChoiceFactory {
|
||||
fn count(&self) -> u8;
|
||||
fn paint_center(&self, choice_index: u8, area: Rect, inverse: bool);
|
||||
fn width_center(&self, _choice_index: u8) -> i16 {
|
||||
0
|
||||
}
|
||||
fn paint_left(&self, _choice_index: u8, _area: Rect, _show_incomplete: bool) -> Option<i16> {
|
||||
None
|
||||
}
|
||||
fn paint_right(&self, _choice_index: u8, _area: Rect, _show_incomplete: bool) -> Option<i16> {
|
||||
None
|
||||
}
|
||||
fn btn_layout(&self, _choice_index: u8) -> ButtonLayout<&'static str> {
|
||||
ButtonLayout::default_three_icons()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer, name: &str, choice_index: u8);
|
||||
type Item: Choice + Trace;
|
||||
#[cfg(not(feature = "ui_debug"))]
|
||||
type Item: Choice;
|
||||
fn count(&self) -> u8;
|
||||
fn get(&self, index: u8) -> Self::Item;
|
||||
}
|
||||
|
||||
/// General component displaying a set of items on the screen
|
||||
@ -86,7 +93,7 @@ where
|
||||
F: ChoiceFactory,
|
||||
{
|
||||
pub fn new(choices: F) -> Self {
|
||||
let initial_btn_layout = choices.btn_layout(0);
|
||||
let initial_btn_layout = choices.get(0).btn_layout();
|
||||
|
||||
Self {
|
||||
choices,
|
||||
@ -184,7 +191,7 @@ where
|
||||
|
||||
// Getting the remaining left and right areas.
|
||||
let (left_area, _center_area, right_area) =
|
||||
available_area.split_center(self.choices.width_center(self.page_counter as u8));
|
||||
available_area.split_center(self.choices.get(self.page_counter as u8).width_center());
|
||||
|
||||
// Possibly drawing on the left side.
|
||||
if self.has_previous_choice() || self.is_carousel {
|
||||
@ -227,7 +234,8 @@ where
|
||||
/// Display the current choice in the middle.
|
||||
fn show_current_choice(&mut self, area: Rect) {
|
||||
self.choices
|
||||
.paint_center(self.page_counter, area, self.inverse_selected_item);
|
||||
.get(self.page_counter)
|
||||
.paint_center(area, self.inverse_selected_item);
|
||||
|
||||
// Color inversion is just one-time thing.
|
||||
if self.inverse_selected_item {
|
||||
@ -254,9 +262,10 @@ where
|
||||
|
||||
let current_area = area.split_right(x_offset + self.items_distance).0;
|
||||
|
||||
if let Some(width) =
|
||||
self.choices
|
||||
.paint_left(page_index as u8, current_area, self.show_incomplete)
|
||||
if let Some(width) = self
|
||||
.choices
|
||||
.get(page_index as u8)
|
||||
.paint_left(current_area, self.show_incomplete)
|
||||
{
|
||||
// Updating loop variables.
|
||||
x_offset += width + self.items_distance;
|
||||
@ -285,13 +294,14 @@ where
|
||||
}
|
||||
let current_area = area.split_left(x_offset + self.items_distance).1;
|
||||
|
||||
if let Some(width) =
|
||||
self.choices
|
||||
.paint_right(page_index as u8, current_area, self.show_incomplete)
|
||||
if let Some(width) = self
|
||||
.choices
|
||||
.get(page_index as u8)
|
||||
.paint_right(current_area, self.show_incomplete)
|
||||
{
|
||||
// Updating loop variables.
|
||||
x_offset += width + self.items_distance;
|
||||
page_index -= 1;
|
||||
page_index += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -338,7 +348,7 @@ where
|
||||
// and Cancel as HTC. PIN client would check if the PIN is empty/not and
|
||||
// adjust the HTC/not.
|
||||
|
||||
let btn_layout = self.choices.btn_layout(self.page_counter);
|
||||
let btn_layout = self.choices.get(self.page_counter).btn_layout();
|
||||
self.buttons.mutate(ctx, |_ctx, buttons| {
|
||||
buttons.set(btn_layout);
|
||||
});
|
||||
@ -429,21 +439,21 @@ where
|
||||
t.kw_pair("is_carousel", booltostr!(self.is_carousel));
|
||||
|
||||
if self.has_previous_choice() {
|
||||
self.choices.trace(t, "prev_choice", self.page_counter - 1);
|
||||
t.field("prev_choice", &self.choices.get(self.page_counter - 1));
|
||||
} else if self.is_carousel {
|
||||
// In case of carousel going to the left end.
|
||||
self.choices.trace(t, "prev_choice", self.last_page_index());
|
||||
t.field("prev_choice", &self.choices.get(self.last_page_index()));
|
||||
} else {
|
||||
t.string("prev_choice");
|
||||
t.symbol("None");
|
||||
}
|
||||
self.choices.trace(t, "current_choice", self.page_counter);
|
||||
t.field("current_choice", &self.choices.get(self.page_counter));
|
||||
|
||||
if self.has_next_choice() {
|
||||
self.choices.trace(t, "next_choice", self.page_counter + 1);
|
||||
t.field("next_choice", &self.choices.get(self.page_counter + 1));
|
||||
} else if self.is_carousel {
|
||||
// In case of carousel going to the very left.
|
||||
self.choices.trace(t, "next_choice", 0);
|
||||
t.field("next_choice", &self.choices.get(0));
|
||||
} else {
|
||||
t.string("next_choice");
|
||||
t.symbol("None");
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::ui::{
|
||||
display::{rect_fill, rect_fill_corners, rect_outline_rounded, Font, Icon},
|
||||
geometry::{Offset, Rect},
|
||||
model_tr::theme,
|
||||
model_tr::{component::choice::Choice, theme},
|
||||
};
|
||||
use heapless::String;
|
||||
|
||||
@ -49,17 +49,6 @@ impl ChoiceItem {
|
||||
self.font.text_width(&self.text)
|
||||
}
|
||||
|
||||
/// Getting the overall width in pixels when displayed in center.
|
||||
/// That means both the icon and text will be shown.
|
||||
pub fn width_center(&self) -> i16 {
|
||||
let icon_width = if let Some(icon) = self.icon {
|
||||
icon.width() + 2
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.text_width() + icon_width
|
||||
}
|
||||
|
||||
/// Getting the non-central width in pixels.
|
||||
/// It will show an icon if defined, otherwise the text, not both.
|
||||
pub fn width_side(&self) -> i16 {
|
||||
@ -95,25 +84,6 @@ impl ChoiceItem {
|
||||
}
|
||||
}
|
||||
|
||||
/// Painting the item as the main choice in the middle.
|
||||
/// Showing both the icon and text, if the icon is available.
|
||||
pub fn paint_center(&self, area: Rect, inverse: bool) {
|
||||
self.paint_rounded_highlight(area, inverse);
|
||||
|
||||
let mut baseline = area.bottom_center() + Offset::new(-self.width_center() / 2, 0);
|
||||
if let Some(icon) = self.icon {
|
||||
let fg_color = if inverse { theme::BG } else { theme::FG };
|
||||
let bg_color = if inverse { theme::FG } else { theme::BG };
|
||||
icon.draw_bottom_left(baseline, fg_color, bg_color);
|
||||
baseline = baseline + Offset::new(icon.width() + 2, 0);
|
||||
}
|
||||
if inverse {
|
||||
display_inverse(baseline, &self.text, self.font);
|
||||
} else {
|
||||
display(baseline, &self.text, self.font);
|
||||
}
|
||||
}
|
||||
|
||||
/// Painting the item as a choice on the left side from center.
|
||||
/// Showing only the icon, if available, otherwise the text.
|
||||
pub fn render_left(&self, area: Rect) {
|
||||
@ -124,24 +94,6 @@ impl ChoiceItem {
|
||||
}
|
||||
}
|
||||
|
||||
/// Painting item on the side if it fits, otherwise paint incomplete if
|
||||
/// allowed
|
||||
pub fn paint_left(&self, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
// When the item does not fit, we stop.
|
||||
// Rendering the item anyway if the incomplete items are allowed.
|
||||
if !self.fits(area) {
|
||||
if show_incomplete {
|
||||
self.render_left(area);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Rendering the item.
|
||||
self.render_left(area);
|
||||
|
||||
Some(self.width_side())
|
||||
}
|
||||
|
||||
/// Painting the item as a choice on the right side from center.
|
||||
/// Showing only the icon, if available, otherwise the text.
|
||||
pub fn render_right(&self, area: Rect) {
|
||||
@ -152,29 +104,6 @@ impl ChoiceItem {
|
||||
}
|
||||
}
|
||||
|
||||
/// Painting item on the side if it fits, otherwise paint incomplete if
|
||||
/// allowed
|
||||
pub fn paint_right(&self, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
// When the item does not fit, we stop.
|
||||
// Rendering the item anyway if the incomplete items are allowed.
|
||||
if !self.fits(area) {
|
||||
if show_incomplete {
|
||||
self.render_right(area);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Rendering the item.
|
||||
self.render_right(area);
|
||||
|
||||
Some(self.width_side())
|
||||
}
|
||||
|
||||
/// Getting current button layout.
|
||||
pub fn btn_layout(&self) -> ButtonLayout<&'static str> {
|
||||
self.btn_layout.clone()
|
||||
}
|
||||
|
||||
/// Setting left button.
|
||||
pub fn set_left_btn(&mut self, btn_left: Option<ButtonDetails<&'static str>>) {
|
||||
self.btn_layout.btn_left = btn_left;
|
||||
@ -196,6 +125,78 @@ impl ChoiceItem {
|
||||
}
|
||||
}
|
||||
|
||||
impl Choice for ChoiceItem {
|
||||
/// Painting the item as the main choice in the middle.
|
||||
/// Showing both the icon and text, if the icon is available.
|
||||
fn paint_center(&self, area: Rect, inverse: bool) {
|
||||
self.paint_rounded_highlight(area, inverse);
|
||||
|
||||
let mut baseline = area.bottom_center() + Offset::new(-self.width_center() / 2, 0);
|
||||
if let Some(icon) = self.icon {
|
||||
let fg_color = if inverse { theme::BG } else { theme::FG };
|
||||
let bg_color = if inverse { theme::FG } else { theme::BG };
|
||||
icon.draw_bottom_left(baseline, fg_color, bg_color);
|
||||
baseline = baseline + Offset::new(icon.width() + 2, 0);
|
||||
}
|
||||
if inverse {
|
||||
display_inverse(baseline, &self.text, self.font);
|
||||
} else {
|
||||
display(baseline, &self.text, self.font);
|
||||
}
|
||||
}
|
||||
|
||||
/// Getting the overall width in pixels when displayed in center.
|
||||
/// That means both the icon and text will be shown.
|
||||
fn width_center(&self) -> i16 {
|
||||
let icon_width = if let Some(icon) = self.icon {
|
||||
icon.width() + 2
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.text_width() + icon_width
|
||||
}
|
||||
|
||||
/// Painting item on the side if it fits, otherwise paint incomplete if
|
||||
/// allowed
|
||||
fn paint_left(&self, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
// When the item does not fit, we stop.
|
||||
// Rendering the item anyway if the incomplete items are allowed.
|
||||
if !self.fits(area) {
|
||||
if show_incomplete {
|
||||
self.render_left(area);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Rendering the item.
|
||||
self.render_left(area);
|
||||
|
||||
Some(self.width_side())
|
||||
}
|
||||
/// Painting item on the side if it fits, otherwise paint incomplete if
|
||||
/// allowed
|
||||
fn paint_right(&self, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
// When the item does not fit, we stop.
|
||||
// Rendering the item anyway if the incomplete items are allowed.
|
||||
if !self.fits(area) {
|
||||
if show_incomplete {
|
||||
self.render_right(area);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Rendering the item.
|
||||
self.render_right(area);
|
||||
|
||||
Some(self.width_side())
|
||||
}
|
||||
|
||||
/// Getting current button layout.
|
||||
fn btn_layout(&self) -> ButtonLayout<&'static str> {
|
||||
self.btn_layout.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for ChoiceItem {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -44,7 +44,7 @@ pub use confirm::{HoldToConfirm, HoldToConfirmMsg};
|
||||
pub use button_controller::{ButtonController, ButtonControllerMsg};
|
||||
#[cfg(feature = "micropython")]
|
||||
pub use changing_text::ChangingTextLine;
|
||||
pub use choice::{ChoiceFactory, ChoicePage, ChoicePageMsg};
|
||||
pub use choice::{Choice, ChoiceFactory, ChoicePage, ChoicePageMsg};
|
||||
pub use choice_item::ChoiceItem;
|
||||
pub use dialog::{Dialog, DialogMsg};
|
||||
#[cfg(feature = "micropython")]
|
||||
|
@ -1,5 +1,3 @@
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::trace::Tracer;
|
||||
use crate::{
|
||||
time::Duration,
|
||||
ui::{
|
||||
@ -146,15 +144,10 @@ impl ChoiceFactoryPassphrase {
|
||||
ChoiceItem::new(char_to_string::<1>(ch), ButtonLayout::default_three_icons())
|
||||
}
|
||||
}
|
||||
fn get(&self, choice_index: u8) -> ChoiceItem {
|
||||
match self.current_category {
|
||||
ChoiceCategory::Menu => self.get_menu_item(choice_index),
|
||||
_ => self.get_character_item(choice_index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChoiceFactory for ChoiceFactoryPassphrase {
|
||||
type Item = ChoiceItem;
|
||||
fn count(&self) -> u8 {
|
||||
let length = get_category_length(&self.current_category);
|
||||
// All non-MENU categories have an extra item for returning back to MENU
|
||||
@ -163,29 +156,11 @@ impl ChoiceFactory for ChoiceFactoryPassphrase {
|
||||
_ => length + 1,
|
||||
}
|
||||
}
|
||||
fn paint_center(&self, choice_index: u8, area: Rect, inverse: bool) {
|
||||
self.get(choice_index).paint_center(area, inverse);
|
||||
}
|
||||
|
||||
fn width_center(&self, choice_index: u8) -> i16 {
|
||||
self.get(choice_index).width_center()
|
||||
}
|
||||
|
||||
fn paint_left(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_left(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn paint_right(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_right(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn btn_layout(&self, choice_index: u8) -> ButtonLayout<&'static str> {
|
||||
self.get(choice_index).btn_layout()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
fn trace(&self, t: &mut dyn Tracer, name: &str, choice_index: u8) {
|
||||
t.field(name, &self.get(choice_index));
|
||||
fn get(&self, choice_index: u8) -> ChoiceItem {
|
||||
match self.current_category {
|
||||
ChoiceCategory::Menu => self.get_menu_item(choice_index),
|
||||
_ => self.get_character_item(choice_index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ use super::{
|
||||
ButtonDetails, ButtonLayout, ChangingTextLine, ChoiceFactory, ChoiceItem, ChoicePage,
|
||||
ChoicePageMsg,
|
||||
};
|
||||
use crate::trace::Tracer;
|
||||
use heapless::String;
|
||||
|
||||
pub enum PinEntryMsg {
|
||||
@ -53,6 +52,10 @@ impl ChoiceFactoryPIN {
|
||||
fn new(prompt: StrBuffer) -> Self {
|
||||
Self { prompt }
|
||||
}
|
||||
}
|
||||
|
||||
impl ChoiceFactory for ChoiceFactoryPIN {
|
||||
type Item = ChoiceItem;
|
||||
|
||||
fn get(&self, choice_index: u8) -> ChoiceItem {
|
||||
let choice_str = CHOICES[choice_index as usize];
|
||||
@ -76,37 +79,10 @@ impl ChoiceFactoryPIN {
|
||||
|
||||
choice_item
|
||||
}
|
||||
}
|
||||
|
||||
impl ChoiceFactory for ChoiceFactoryPIN {
|
||||
fn count(&self) -> u8 {
|
||||
CHOICE_LENGTH as u8
|
||||
}
|
||||
|
||||
fn paint_center(&self, choice_index: u8, area: Rect, inverse: bool) {
|
||||
self.get(choice_index).paint_center(area, inverse);
|
||||
}
|
||||
|
||||
fn width_center(&self, choice_index: u8) -> i16 {
|
||||
self.get(choice_index).width_center()
|
||||
}
|
||||
|
||||
fn paint_left(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_left(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn paint_right(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_right(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn btn_layout(&self, choice_index: u8) -> ButtonLayout<&'static str> {
|
||||
self.get(choice_index).btn_layout()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
fn trace(&self, t: &mut dyn Tracer, name: &str, choice_index: u8) {
|
||||
t.field(name, &self.get(choice_index));
|
||||
}
|
||||
}
|
||||
|
||||
/// Component for entering a PIN.
|
||||
|
@ -6,9 +6,6 @@ use crate::ui::{
|
||||
use super::{ButtonLayout, ChoiceFactory, ChoiceItem, ChoicePage, ChoicePageMsg};
|
||||
use heapless::{String, Vec};
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use crate::trace::Tracer;
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
use super::{ButtonAction, ButtonPos};
|
||||
|
||||
@ -28,6 +25,17 @@ where
|
||||
fn new(choices: Vec<T, N>, carousel: bool) -> Self {
|
||||
Self { choices, carousel }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> ChoiceFactory for ChoiceFactorySimple<T, N>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
type Item = ChoiceItem;
|
||||
|
||||
fn count(&self) -> u8 {
|
||||
N as u8
|
||||
}
|
||||
|
||||
fn get(&self, choice_index: u8) -> ChoiceItem {
|
||||
let text = &self.choices[choice_index as usize];
|
||||
@ -46,40 +54,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> ChoiceFactory for ChoiceFactorySimple<T, N>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn count(&self) -> u8 {
|
||||
N as u8
|
||||
}
|
||||
|
||||
fn paint_center(&self, choice_index: u8, area: Rect, inverse: bool) {
|
||||
self.get(choice_index).paint_center(area, inverse);
|
||||
}
|
||||
|
||||
fn width_center(&self, choice_index: u8) -> i16 {
|
||||
self.get(choice_index).width_center()
|
||||
}
|
||||
|
||||
fn paint_left(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_left(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn paint_right(&self, choice_index: u8, area: Rect, show_incomplete: bool) -> Option<i16> {
|
||||
self.get(choice_index).paint_right(area, show_incomplete)
|
||||
}
|
||||
|
||||
fn btn_layout(&self, choice_index: u8) -> ButtonLayout<&'static str> {
|
||||
self.get(choice_index).btn_layout()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
fn trace(&self, t: &mut dyn Tracer, name: &str, choice_index: u8) {
|
||||
t.field(name, &self.get(choice_index));
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple wrapper around `ChoicePage` that allows for
|
||||
/// inputting a list of values and receiving the chosen one.
|
||||
pub struct SimpleChoice<T, const N: usize>
|
||||
|
Loading…
Reference in New Issue
Block a user