You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/embed/rust/src/ui/model_t1/layout.rs

147 lines
4.3 KiB

use core::convert::{TryFrom, TryInto};
use crate::{
error::Error,
micropython::{buffer::Buffer, map::Map, obj::Obj, qstr::Qstr},
ui::{
component::{Child, Text},
display,
layout::obj::LayoutObj,
},
util,
};
use super::{
component::{Button, Dialog, DialogMsg},
theme,
};
impl<T> TryFrom<DialogMsg<T>> for Obj
where
Obj: TryFrom<T>,
Error: From<<T as TryInto<Obj>>::Error>,
{
type Error = Error;
fn try_from(val: DialogMsg<T>) -> Result<Self, Self::Error> {
match val {
DialogMsg::Content(c) => Ok(c.try_into()?),
DialogMsg::LeftClicked => 1.try_into(),
DialogMsg::RightClicked => 2.try_into(),
}
}
}
#[no_mangle]
extern "C" fn ui_layout_new_confirm_action(
n_args: usize,
args: *const Obj,
kwargs: *const Map,
) -> Obj {
let block = |_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 verb_cancel: Option<Buffer> =
kwargs.get(Qstr::MP_QSTR_verb_cancel)?.try_into_option()?;
let reverse: bool = kwargs.get(Qstr::MP_QSTR_reverse)?.try_into()?;
let format = match (&action, &description, reverse) {
(Some(_), Some(_), false) => "{bold}{action}\n\r{normal}{description}",
(Some(_), Some(_), true) => "{normal}{description}\n\r{bold}{action}",
(Some(_), None, _) => "{bold}{action}",
(None, Some(_), _) => "{normal}{description}",
_ => "",
};
let left = verb_cancel
.map(|label| |area, pos| Button::with_text(area, pos, label, theme::button_cancel()));
let right = verb
.map(|label| |area, pos| Button::with_text(area, pos, label, theme::button_default()));
let obj = LayoutObj::new(Child::new(Dialog::new(
display::screen(),
|area| {
Text::new::<theme::T1DefaultText>(area, format)
.with(b"action", action.unwrap_or("".into()))
.with(b"description", description.unwrap_or("".into()))
},
left,
right,
Some(title),
)))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
#[cfg(test)]
mod tests {
use crate::trace::{Trace, Tracer};
use super::*;
impl Tracer for Vec<u8> {
fn bytes(&mut self, b: &[u8]) {
self.extend(b)
}
fn string(&mut self, s: &str) {
self.extend(s.as_bytes())
}
fn symbol(&mut self, name: &str) {
self.extend(name.as_bytes())
}
fn open(&mut self, name: &str) {
self.extend(b"<");
self.extend(name.as_bytes());
self.extend(b" ");
}
fn field(&mut self, name: &str, value: &dyn Trace) {
self.extend(name.as_bytes());
self.extend(b":");
value.trace(self);
self.extend(b" ");
}
fn close(&mut self) {
self.extend(b">")
}
}
fn trace(val: &impl Trace) -> String {
let mut t = Vec::new();
val.trace(&mut t);
String::from_utf8(t).unwrap()
}
#[test]
fn trace_example_layout() {
let layout = Child::new(Dialog::new(
display::screen(),
|area| {
Text::new(
area,
"Testing text layout, with some text, and some more text. And {param}",
)
.with(b"param", b"parameters!")
},
Some(|area, pos| Button::with_text(area, pos, "Left", theme::button_cancel())),
Some(|area, pos| Button::with_text(area, pos, "Right", theme::button_default())),
None,
));
assert_eq!(
trace(&layout),
r#"<Dialog content:<Text content:Testing text layout,
with some text, and
some more text. And p-
arameters! > left:<Button text:Left > right:<Button text:Right > >"#
)
}
}