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:
parent
62ab48d2e6
commit
9f0ebf6d1a
@ -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>;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 > > > >",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user