mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-28 00:01:31 +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"
|
#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(
|
/// def layout_new_confirm_action(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str,
|
/// title: str,
|
||||||
@ -38,30 +32,37 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorui2_layout_new_example_obj,
|
|||||||
/// verb_cancel: str | None,
|
/// verb_cancel: str | None,
|
||||||
/// hold: bool | None,
|
/// hold: bool | None,
|
||||||
/// reverse: bool,
|
/// reverse: bool,
|
||||||
/// ) -> int:
|
/// ) -> object:
|
||||||
/// """Example layout."""
|
/// """Example layout."""
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_action_obj,
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_action_obj,
|
||||||
0, ui_layout_new_confirm_action);
|
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(
|
/// def layout_new_confirm_text(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str,
|
/// title: str,
|
||||||
/// data: str,
|
/// data: str,
|
||||||
/// description: str | None,
|
/// description: str | None,
|
||||||
/// ) -> int:
|
/// ) -> object:
|
||||||
/// """Example layout."""
|
/// """Example layout."""
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_text_obj, 0,
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_trezorui2_layout_new_confirm_text_obj, 0,
|
||||||
ui_layout_new_confirm_text);
|
ui_layout_new_confirm_text);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t mp_module_trezorui2_globals_table[] = {
|
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___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_QSTR(MP_QSTR_layout_new_example),
|
||||||
MP_ROM_PTR(&mod_trezorui2_layout_new_example_obj)},
|
MP_ROM_PTR(&mod_trezorui2_layout_new_example_obj)},
|
||||||
#elif TREZOR_MODEL == 1
|
#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_QSTR(MP_QSTR_layout_new_confirm_text),
|
||||||
MP_ROM_PTR(&mod_trezorui2_layout_new_confirm_text_obj)},
|
MP_ROM_PTR(&mod_trezorui2_layout_new_confirm_text_obj)},
|
||||||
#endif
|
#endif
|
||||||
|
@ -9,6 +9,7 @@ use crate::ui::{
|
|||||||
use super::layout::{DefaultTextTheme, LayoutFit, TextLayout, TextNoOp, TextRenderer};
|
use super::layout::{DefaultTextTheme, LayoutFit, TextLayout, TextNoOp, TextRenderer};
|
||||||
|
|
||||||
pub const MAX_PARAGRAPHS: usize = 6;
|
pub const MAX_PARAGRAPHS: usize = 6;
|
||||||
|
pub const DEFAULT_SPACING: i32 = 3;
|
||||||
|
|
||||||
pub struct Paragraphs<T> {
|
pub struct Paragraphs<T> {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
@ -26,7 +27,9 @@ where
|
|||||||
Self {
|
Self {
|
||||||
area,
|
area,
|
||||||
list: Vec::new(),
|
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,
|
para_offset: 0,
|
||||||
char_offset: 0,
|
char_offset: 0,
|
||||||
}
|
}
|
||||||
@ -37,6 +40,11 @@ where
|
|||||||
self
|
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 {
|
pub fn add<D: DefaultTextTheme>(mut self, text_font: Font, content: T) -> Self {
|
||||||
if content.as_ref().is_empty() {
|
if content.as_ref().is_empty() {
|
||||||
return self;
|
return self;
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
typ::Type,
|
typ::Type,
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
ui::component::{Child, Component, Event, EventCtx, Never, TimerToken},
|
ui::component::{Child, Component, Event, EventCtx, Never, PageMsg, TimerToken},
|
||||||
util,
|
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 {
|
impl From<Never> for Obj {
|
||||||
fn from(_: Never) -> Self {
|
fn from(_: Never) -> Self {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use core::convert::{TryFrom, TryInto};
|
use core::convert::TryInto;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
|
||||||
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
|
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
|
||||||
ui::{
|
ui::{
|
||||||
component::{text::paragraphs::Paragraphs, Child, FormattedText, PageMsg},
|
component::{text::paragraphs::Paragraphs, Child, FormattedText},
|
||||||
display,
|
display,
|
||||||
layout::obj::LayoutObj,
|
layout::obj::LayoutObj,
|
||||||
},
|
},
|
||||||
@ -16,17 +15,6 @@ use super::{
|
|||||||
theme,
|
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]
|
#[no_mangle]
|
||||||
extern "C" fn ui_layout_new_confirm_action(
|
extern "C" fn ui_layout_new_confirm_action(
|
||||||
n_args: usize,
|
n_args: usize,
|
||||||
@ -106,7 +94,8 @@ extern "C" fn ui_layout_new_confirm_text(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
trace::{Trace, Tracer},
|
error::Error,
|
||||||
|
trace::Trace,
|
||||||
ui::model_t1::component::{Dialog, DialogMsg},
|
ui::model_t1::component::{Dialog, DialogMsg},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ use core::convert::{TryFrom, TryInto};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
micropython::obj::Obj,
|
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
|
||||||
ui::{
|
ui::{
|
||||||
component::{Child, FormattedText},
|
component::{base::ComponentExt, text::paragraphs::Paragraphs, Child, FormattedText},
|
||||||
display,
|
display,
|
||||||
layout::obj::LayoutObj,
|
layout::obj::LayoutObj,
|
||||||
},
|
},
|
||||||
@ -12,7 +12,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
component::{ButtonMsg, DialogMsg, HoldToConfirm, HoldToConfirmMsg},
|
component::{Button, ButtonMsg, DialogMsg, Frame, HoldToConfirm, HoldToConfirmMsg, SwipePage},
|
||||||
theme,
|
theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,6 +64,61 @@ extern "C" fn ui_layout_new_example(_param: Obj) -> Obj {
|
|||||||
unsafe { util::try_or_raise(block) }
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -130,9 +185,7 @@ mod tests {
|
|||||||
));
|
));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
trace(&layout),
|
trace(&layout),
|
||||||
r#"<Dialog content:<Text content:Testing text layout, with
|
"<Dialog content:<Text content:Testing text layout, with\nsome text, and some more\ntext. And parameters! > left:<Button text:Left > right:<Button text:Right > >",
|
||||||
some text, and some more
|
|
||||||
text. And parameters! > left:<Button text:Left > right:<Button text:Right > >"#
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,37 +53,65 @@ pub fn label_default() -> LabelStyle {
|
|||||||
pub fn button_default() -> ButtonStyleSheet {
|
pub fn button_default() -> ButtonStyleSheet {
|
||||||
ButtonStyleSheet {
|
ButtonStyleSheet {
|
||||||
normal: &ButtonStyle {
|
normal: &ButtonStyle {
|
||||||
font: FONT_NORMAL,
|
font: FONT_BOLD,
|
||||||
text_color: FG,
|
text_color: FG,
|
||||||
button_color: GREY_DARK,
|
button_color: GREY_DARK,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: BG,
|
border_color: BG,
|
||||||
border_radius: RADIUS,
|
border_radius: RADIUS,
|
||||||
border_width: 2,
|
border_width: 1,
|
||||||
},
|
},
|
||||||
active: &ButtonStyle {
|
active: &ButtonStyle {
|
||||||
font: FONT_NORMAL,
|
font: FONT_BOLD,
|
||||||
text_color: BG,
|
text_color: BG,
|
||||||
button_color: FG,
|
button_color: FG,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: FG,
|
border_color: FG,
|
||||||
border_radius: RADIUS,
|
border_radius: RADIUS,
|
||||||
border_width: 2,
|
border_width: 1,
|
||||||
},
|
},
|
||||||
disabled: &ButtonStyle {
|
disabled: &ButtonStyle {
|
||||||
font: FONT_NORMAL,
|
font: FONT_BOLD,
|
||||||
text_color: GREY_LIGHT,
|
text_color: GREY_LIGHT,
|
||||||
button_color: GREY_DARK,
|
button_color: GREY_DARK,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: BG,
|
border_color: BG,
|
||||||
border_radius: RADIUS,
|
border_radius: RADIUS,
|
||||||
border_width: 2,
|
border_width: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn button_confirm() -> ButtonStyleSheet {
|
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 {
|
pub fn button_cancel() -> ButtonStyleSheet {
|
||||||
@ -125,3 +153,20 @@ impl DefaultTextTheme for TTDefaultText {
|
|||||||
const BOLD_FONT: Font = FONT_BOLD;
|
const BOLD_FONT: Font = FONT_BOLD;
|
||||||
const MONO_FONT: Font = FONT_MONO;
|
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