mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-26 07:11:25 +00:00
feat(core/rust/ui): implement confirm_action
[no changelog]
This commit is contained in:
parent
6d1227d839
commit
b64c69c3ff
@ -23,12 +23,6 @@
|
||||
|
||||
#include "librust.h"
|
||||
|
||||
#if TREZOR_MODEL == T
|
||||
/// def layout_new_example(text: str) -> None:
|
||||
/// """Example layout."""
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorui2_layout_new_example_obj,
|
||||
ui_layout_new_example);
|
||||
#elif TREZOR_MODEL == 1
|
||||
/// def layout_new_confirm_action(
|
||||
/// *,
|
||||
/// title: str,
|
||||
@ -38,30 +32,37 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorui2_layout_new_example_obj,
|
||||
/// verb_cancel: str | None,
|
||||
/// hold: bool | None,
|
||||
/// reverse: bool,
|
||||
/// ) -> int:
|
||||
/// ) -> object:
|
||||
/// """Example layout."""
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_action_obj,
|
||||
0, ui_layout_new_confirm_action);
|
||||
|
||||
#if TREZOR_MODEL == T
|
||||
/// def layout_new_example(text: str) -> object:
|
||||
/// """Example layout."""
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorui2_layout_new_example_obj,
|
||||
ui_layout_new_example);
|
||||
#elif TREZOR_MODEL == 1
|
||||
/// def layout_new_confirm_text(
|
||||
/// *,
|
||||
/// title: str,
|
||||
/// data: str,
|
||||
/// description: str | None,
|
||||
/// ) -> int:
|
||||
/// ) -> object:
|
||||
/// """Example layout."""
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_text_obj, 0,
|
||||
ui_layout_new_confirm_text);
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_trezorui2_globals_table[] = {
|
||||
#if TREZOR_MODEL == T
|
||||
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorui2)},
|
||||
|
||||
{MP_ROM_QSTR(MP_QSTR_layout_new_confirm_action),
|
||||
MP_ROM_PTR(&mod_trezorui2_layout_new_confirm_action_obj)},
|
||||
#if TREZOR_MODEL == T
|
||||
{MP_ROM_QSTR(MP_QSTR_layout_new_example),
|
||||
MP_ROM_PTR(&mod_trezorui2_layout_new_example_obj)},
|
||||
#elif TREZOR_MODEL == 1
|
||||
{MP_ROM_QSTR(MP_QSTR_layout_new_confirm_action),
|
||||
MP_ROM_PTR(&mod_trezorui2_layout_new_confirm_action_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_layout_new_confirm_text),
|
||||
MP_ROM_PTR(&mod_trezorui2_layout_new_confirm_text_obj)},
|
||||
#endif
|
||||
|
@ -9,6 +9,7 @@ use crate::ui::{
|
||||
use super::layout::{DefaultTextTheme, LayoutFit, TextLayout, TextNoOp, TextRenderer};
|
||||
|
||||
pub const MAX_PARAGRAPHS: usize = 6;
|
||||
pub const DEFAULT_SPACING: i32 = 3;
|
||||
|
||||
pub struct Paragraphs<T> {
|
||||
area: Rect,
|
||||
@ -26,7 +27,9 @@ where
|
||||
Self {
|
||||
area,
|
||||
list: Vec::new(),
|
||||
layout: LinearLayout::vertical().align_at_center().with_spacing(10),
|
||||
layout: LinearLayout::vertical()
|
||||
.align_at_center()
|
||||
.with_spacing(DEFAULT_SPACING),
|
||||
para_offset: 0,
|
||||
char_offset: 0,
|
||||
}
|
||||
@ -37,6 +40,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_spacing(mut self, spacing: i32) -> Self {
|
||||
self.layout = self.layout.with_spacing(spacing);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add<D: DefaultTextTheme>(mut self, text_font: Font, content: T) -> Self {
|
||||
if content.as_ref().is_empty() {
|
||||
return self;
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
typ::Type,
|
||||
},
|
||||
time::Duration,
|
||||
ui::component::{Child, Component, Event, EventCtx, Never, TimerToken},
|
||||
ui::component::{Child, Component, Event, EventCtx, Never, PageMsg, TimerToken},
|
||||
util,
|
||||
};
|
||||
|
||||
@ -277,6 +277,17 @@ impl TryFrom<Duration> for Obj {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<PageMsg<T, bool>> for Obj {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(val: PageMsg<T, bool>) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
PageMsg::Content(_) => Err(Error::TypeError),
|
||||
PageMsg::Controls(c) => Ok(c.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Never> for Obj {
|
||||
fn from(_: Never) -> Self {
|
||||
unreachable!()
|
||||
|
@ -1,10 +1,9 @@
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use core::convert::TryInto;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
|
||||
ui::{
|
||||
component::{text::paragraphs::Paragraphs, Child, FormattedText, PageMsg},
|
||||
component::{text::paragraphs::Paragraphs, Child, FormattedText},
|
||||
display,
|
||||
layout::obj::LayoutObj,
|
||||
},
|
||||
@ -16,17 +15,6 @@ use super::{
|
||||
theme,
|
||||
};
|
||||
|
||||
impl<T> TryFrom<PageMsg<T, bool>> for Obj {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(val: PageMsg<T, bool>) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
PageMsg::Content(_) => 2.try_into(),
|
||||
PageMsg::Controls(c) => Ok(c.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn ui_layout_new_confirm_action(
|
||||
n_args: usize,
|
||||
@ -106,7 +94,8 @@ extern "C" fn ui_layout_new_confirm_text(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
trace::{Trace, Tracer},
|
||||
error::Error,
|
||||
trace::Trace,
|
||||
ui::model_t1::component::{Dialog, DialogMsg},
|
||||
};
|
||||
|
||||
|
@ -2,9 +2,9 @@ use core::convert::{TryFrom, TryInto};
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::obj::Obj,
|
||||
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
|
||||
ui::{
|
||||
component::{Child, FormattedText},
|
||||
component::{base::ComponentExt, text::paragraphs::Paragraphs, Child, FormattedText},
|
||||
display,
|
||||
layout::obj::LayoutObj,
|
||||
},
|
||||
@ -12,7 +12,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
component::{ButtonMsg, DialogMsg, HoldToConfirm, HoldToConfirmMsg},
|
||||
component::{Button, ButtonMsg, DialogMsg, Frame, HoldToConfirm, HoldToConfirmMsg, SwipePage},
|
||||
theme,
|
||||
};
|
||||
|
||||
@ -64,6 +64,61 @@ extern "C" fn ui_layout_new_example(_param: Obj) -> Obj {
|
||||
unsafe { util::try_or_raise(block) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn ui_layout_new_confirm_action(
|
||||
n_args: usize,
|
||||
args: *const Obj,
|
||||
kwargs: *const Map,
|
||||
) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: Buffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let action: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
|
||||
let description: Option<Buffer> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let verb: Option<Buffer> = kwargs.get(Qstr::MP_QSTR_verb)?.try_into_option()?;
|
||||
let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?;
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Frame::new(theme::borders(), title, |area| {
|
||||
SwipePage::new(
|
||||
area,
|
||||
theme::BG,
|
||||
|area| {
|
||||
let action = action.unwrap_or("".into());
|
||||
let description = description.unwrap_or("".into());
|
||||
let mut para = Paragraphs::new(area);
|
||||
if !reverse {
|
||||
para = para
|
||||
.add::<theme::TTDefaultText>(theme::FONT_BOLD, action)
|
||||
.add::<theme::TTDefaultText>(theme::FONT_NORMAL, description);
|
||||
} else {
|
||||
para = para
|
||||
.add::<theme::TTDefaultText>(theme::FONT_NORMAL, description)
|
||||
.add::<theme::TTDefaultText>(theme::FONT_BOLD, action);
|
||||
}
|
||||
para
|
||||
},
|
||||
|area| {
|
||||
Button::array2(
|
||||
area,
|
||||
|area| Button::with_icon(area, theme::ICON_CANCEL),
|
||||
|msg| (matches!(msg, ButtonMsg::Clicked)).then(|| false),
|
||||
|area| {
|
||||
Button::with_text(area, verb.unwrap_or("CONFIRM".into()))
|
||||
.styled(theme::button_confirm())
|
||||
},
|
||||
|msg| (matches!(msg, ButtonMsg::Clicked)).then(|| true),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.into_child(),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
@ -130,9 +185,7 @@ mod tests {
|
||||
));
|
||||
assert_eq!(
|
||||
trace(&layout),
|
||||
r#"<Dialog content:<Text content:Testing text layout, with
|
||||
some text, and some more
|
||||
text. And parameters! > left:<Button text:Left > right:<Button text:Right > >"#
|
||||
"<Dialog content:<Text content:Testing text layout, with\nsome text, and some more\ntext. And parameters! > left:<Button text:Left > right:<Button text:Right > >",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -53,37 +53,65 @@ pub fn label_default() -> LabelStyle {
|
||||
pub fn button_default() -> ButtonStyleSheet {
|
||||
ButtonStyleSheet {
|
||||
normal: &ButtonStyle {
|
||||
font: FONT_NORMAL,
|
||||
font: FONT_BOLD,
|
||||
text_color: FG,
|
||||
button_color: GREY_DARK,
|
||||
background_color: BG,
|
||||
border_color: BG,
|
||||
border_radius: RADIUS,
|
||||
border_width: 2,
|
||||
border_width: 1,
|
||||
},
|
||||
active: &ButtonStyle {
|
||||
font: FONT_NORMAL,
|
||||
font: FONT_BOLD,
|
||||
text_color: BG,
|
||||
button_color: FG,
|
||||
background_color: BG,
|
||||
border_color: FG,
|
||||
border_radius: RADIUS,
|
||||
border_width: 2,
|
||||
border_width: 1,
|
||||
},
|
||||
disabled: &ButtonStyle {
|
||||
font: FONT_NORMAL,
|
||||
font: FONT_BOLD,
|
||||
text_color: GREY_LIGHT,
|
||||
button_color: GREY_DARK,
|
||||
background_color: BG,
|
||||
border_color: BG,
|
||||
border_radius: RADIUS,
|
||||
border_width: 2,
|
||||
border_width: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn button_confirm() -> ButtonStyleSheet {
|
||||
button_default()
|
||||
ButtonStyleSheet {
|
||||
normal: &ButtonStyle {
|
||||
font: FONT_BOLD,
|
||||
text_color: FG,
|
||||
button_color: GREEN,
|
||||
background_color: BG,
|
||||
border_color: BG,
|
||||
border_radius: RADIUS,
|
||||
border_width: 1,
|
||||
},
|
||||
active: &ButtonStyle {
|
||||
font: FONT_BOLD,
|
||||
text_color: BG,
|
||||
button_color: FG,
|
||||
background_color: BG,
|
||||
border_color: FG,
|
||||
border_radius: RADIUS,
|
||||
border_width: 1,
|
||||
},
|
||||
disabled: &ButtonStyle {
|
||||
font: FONT_BOLD,
|
||||
text_color: GREY_LIGHT,
|
||||
button_color: GREEN,
|
||||
background_color: BG,
|
||||
border_color: BG,
|
||||
border_radius: RADIUS,
|
||||
border_width: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn button_cancel() -> ButtonStyleSheet {
|
||||
@ -125,3 +153,20 @@ impl DefaultTextTheme for TTDefaultText {
|
||||
const BOLD_FONT: Font = FONT_BOLD;
|
||||
const MONO_FONT: Font = FONT_MONO;
|
||||
}
|
||||
|
||||
pub const CONTENT_BORDER: i32 = 5;
|
||||
|
||||
/// +----------+
|
||||
/// | 13 |
|
||||
/// | +----+ |
|
||||
/// |10| | 5|
|
||||
/// | +----+ |
|
||||
/// | 14 |
|
||||
/// +----------+
|
||||
pub fn borders() -> Rect {
|
||||
let (_left_border, area) = display::screen().vsplit(10);
|
||||
let (area, _right_area) = area.vsplit(-5);
|
||||
let (_top_border, area) = area.hsplit(13);
|
||||
let (area, _bottom_border) = area.hsplit(-14);
|
||||
area
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user