1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-24 23:38:09 +00:00

refactor(core/rust/ui): arbitrary controls for Dialog component

[no changelog]
This commit is contained in:
Martin Milata 2022-05-06 21:14:00 +02:00
parent 62ab48d2e6
commit 9f0ebf6d1a
4 changed files with 72 additions and 45 deletions

View File

@ -210,6 +210,39 @@ where
} }
} }
impl<T> Component for Option<T>
where
T: Component,
{
type Msg = T::Msg;
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match self {
Some(ref mut c) => c.event(ctx, event),
_ => None,
}
}
fn paint(&mut self) {
if let Some(ref mut c) = self {
c.paint()
}
}
fn place(&mut self, bounds: Rect) -> Rect {
match self {
Some(ref mut c) => c.place(bounds),
_ => bounds.with_size(Offset::zero()),
}
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
if let Some(ref c) = self {
c.bounds(sink)
}
}
}
pub trait ComponentExt: Sized { pub trait ComponentExt: Sized {
fn map<F>(self, func: F) -> Map<Self, F>; fn map<F>(self, func: F) -> Map<Self, F>;
fn into_child(self) -> Child<Self>; fn into_child(self) -> Child<Self>;

View File

@ -52,8 +52,9 @@ where
let layout = DialogLayout::middle(bounds); let layout = DialogLayout::middle(bounds);
self.loader.place(layout.content); self.loader.place(layout.content);
self.content.place(layout.content); self.content.place(layout.content);
self.cancel.place(layout.left); let (left, right) = layout.controls.split_left(layout.controls.size().x);
self.confirm.place(layout.right); self.cancel.place(left);
self.confirm.place(right);
bounds bounds
} }

View File

@ -3,29 +3,27 @@ use crate::ui::{
geometry::{Grid, Rect}, geometry::{Grid, Rect},
}; };
pub enum DialogMsg<T, L, R> { use super::{theme, Button};
pub enum DialogMsg<T, U> {
Content(T), Content(T),
Left(L), Controls(U),
Right(R),
} }
pub struct Dialog<T, L, R> { pub struct Dialog<T, U> {
content: Child<T>, content: Child<T>,
left: Child<L>, controls: Child<U>,
right: Child<R>,
} }
impl<T, L, R> Dialog<T, L, R> impl<T, U> Dialog<T, U>
where where
T: Component, T: Component,
L: Component, U: Component,
R: Component,
{ {
pub fn new(content: T, left: L, right: R) -> Self { pub fn new(content: T, controls: U) -> Self {
Self { Self {
content: Child::new(content), content: Child::new(content),
left: Child::new(left), controls: Child::new(controls),
right: Child::new(right),
} }
} }
@ -34,19 +32,17 @@ where
} }
} }
impl<T, L, R> Component for Dialog<T, L, R> impl<T, U> Component for Dialog<T, U>
where where
T: Component, T: Component,
L: Component, U: Component,
R: Component,
{ {
type Msg = DialogMsg<T::Msg, L::Msg, R::Msg>; type Msg = DialogMsg<T::Msg, U::Msg>;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
let layout = DialogLayout::middle(bounds); let layout = DialogLayout::middle(bounds);
self.content.place(layout.content); self.content.place(layout.content);
self.left.place(layout.left); self.controls.place(layout.controls);
self.right.place(layout.right);
bounds bounds
} }
@ -54,55 +50,48 @@ where
self.content self.content
.event(ctx, event) .event(ctx, event)
.map(Self::Msg::Content) .map(Self::Msg::Content)
.or_else(|| self.left.event(ctx, event).map(Self::Msg::Left)) .or_else(|| self.controls.event(ctx, event).map(Self::Msg::Controls))
.or_else(|| self.right.event(ctx, event).map(Self::Msg::Right))
} }
fn paint(&mut self) { fn paint(&mut self) {
self.content.paint(); self.content.paint();
self.left.paint(); self.controls.paint();
self.right.paint();
} }
fn bounds(&self, sink: &mut dyn FnMut(Rect)) { fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.content.bounds(sink); self.content.bounds(sink);
self.left.bounds(sink); self.controls.bounds(sink);
self.right.bounds(sink);
} }
} }
pub struct DialogLayout { pub struct DialogLayout {
pub content: Rect, pub content: Rect,
pub left: Rect, pub controls: Rect,
pub right: Rect,
} }
impl DialogLayout { impl DialogLayout {
pub fn middle(area: Rect) -> Self { pub fn middle(area: Rect) -> Self {
let grid = Grid::new(area, 5, 2); let grid = Grid::new(area, 5, 1);
Self { Self {
content: Rect::new( content: Rect::new(
grid.row_col(0, 0).top_left(), grid.row_col(0, 0).top_left(),
grid.row_col(3, 1).bottom_right(), grid.row_col(3, 0).bottom_right(),
), ),
left: grid.row_col(4, 0), controls: grid.row_col(4, 0),
right: grid.row_col(4, 1),
} }
} }
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, L, R> crate::trace::Trace for Dialog<T, L, R> impl<T, U> crate::trace::Trace for Dialog<T, U>
where where
T: crate::trace::Trace, T: crate::trace::Trace,
L: crate::trace::Trace, U: crate::trace::Trace,
R: crate::trace::Trace,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Dialog"); t.open("Dialog");
t.field("content", &self.content); t.field("content", &self.content);
t.field("left", &self.left); t.field("controls", &self.controls);
t.field("right", &self.right);
t.close(); t.close();
} }
} }

View File

@ -26,17 +26,16 @@ use super::{
theme, theme,
}; };
impl<T, U> ComponentMsgObj for Dialog<T, Button<U>, Button<U>> impl<T, U> ComponentMsgObj for Dialog<T, U>
where where
T: ComponentMsgObj, T: ComponentMsgObj,
U: AsRef<str>, U: Component<Msg = bool>,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
DialogMsg::Content(c) => Ok(self.inner().msg_try_into_obj(c)?), DialogMsg::Content(c) => Ok(self.inner().msg_try_into_obj(c)?),
DialogMsg::Left(ButtonMsg::Clicked) => Ok(CANCELLED.as_obj()), DialogMsg::Controls(false) => Ok(CANCELLED.as_obj()),
DialogMsg::Right(ButtonMsg::Clicked) => Ok(CONFIRMED.as_obj()), DialogMsg::Controls(true) => Ok(CONFIRMED.as_obj()),
_ => Ok(Obj::const_none()),
} }
} }
} }
@ -283,18 +282,23 @@ mod tests {
#[test] #[test]
fn trace_example_layout() { fn trace_example_layout() {
let buttons = Button::left_right(
Button::with_text("Left"),
|msg| (matches!(msg, ButtonMsg::Clicked)).then(|| false),
Button::with_text("Right"),
|msg| (matches!(msg, ButtonMsg::Clicked)).then(|| true),
);
let mut layout = Dialog::new( let mut layout = Dialog::new(
FormattedText::new::<theme::TTDefaultText>( FormattedText::new::<theme::TTDefaultText>(
"Testing text layout, with some text, and some more text. And {param}", "Testing text layout, with some text, and some more text. And {param}",
) )
.with("param", "parameters!"), .with("param", "parameters!"),
Button::with_text("Left"), buttons,
Button::with_text("Right"),
); );
layout.place(SCREEN); layout.place(SCREEN);
assert_eq!( assert_eq!(
trace(&layout), trace(&layout),
"<Dialog content:<Text content:Testing text layout, with\nsome text, and some more\ntext. 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! > controls:<Tuple 0:<GridPlaced inner:<Button text:Left > > 1:<GridPlaced inner:<Button text:Right > > > >",
) )
} }
} }