1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-24 15:28:10 +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 {
fn map<F>(self, func: F) -> Map<Self, F>;
fn into_child(self) -> Child<Self>;

View File

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

View File

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

View File

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