fix(core/ui): style update: frame and corner button

[no changelog]
pull/2905/head
Martin Milata 1 year ago
parent 670cbd7a1d
commit afe965687f

@ -7,13 +7,13 @@ use crate::{
text::paragraphs::{
Paragraph, ParagraphSource, ParagraphStrType, ParagraphVecShort, Paragraphs, VecExt,
},
Component, Event, EventCtx, Never, Paginate, Qr,
Component, Event, EventCtx, Paginate, Qr,
},
geometry::Rect,
},
};
use super::{theme, Frame};
use super::{theme, Frame, FrameMsg};
const MAX_XPUBS: usize = 16;
@ -57,18 +57,21 @@ where
"RECEIVE ADDRESS".into(),
Qr::new(qr_address, case_sensitive)?.with_border(7),
)
.with_cancel_button()
.with_border(theme::borders_horizontal_scroll()),
details: Frame::left_aligned(
theme::label_title(),
"RECEIVING TO".into(),
para.into_paragraphs(),
)
.with_cancel_button()
.with_border(theme::borders_horizontal_scroll()),
xpub_view: Frame::left_aligned(
theme::label_title(),
" \n ".into(),
Paragraph::new(&theme::TEXT_MONO, "".into()).into_paragraphs(),
)
.with_cancel_button()
.with_border(theme::borders_horizontal_scroll()),
xpubs: Vec::new(),
xpub_page_count: Vec::new(),
@ -142,7 +145,7 @@ impl<T> Component for AddressDetails<T>
where
T: ParagraphStrType + Clone,
{
type Msg = Never;
type Msg = ();
fn place(&mut self, bounds: Rect) -> Rect {
self.qr_code.place(bounds);
@ -159,10 +162,14 @@ where
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match self.current_page {
let msg = match self.current_page {
0 => self.qr_code.event(ctx, event),
1 => self.details.event(ctx, event),
_ => self.xpub_view.event(ctx, event),
};
match msg {
Some(FrameMsg::Button(_)) => Some(()),
_ => None,
}
}

@ -2,8 +2,7 @@ use crate::{
time::Duration,
ui::{
component::{
Component, ComponentExt, Event, EventCtx, FixedHeightBar, Floating, Map, Paginate,
Split, TimerToken,
Component, ComponentExt, Event, EventCtx, FixedHeightBar, Map, Split, TimerToken,
},
display::{self, toif::Icon, Color, Font},
event::TouchEvent,
@ -514,6 +513,7 @@ type CancelInfoConfirm<T, F0, F1, F2> =
type CancelConfirm<T, F0, F1> = FixedHeightBar<Split<Map<Button<T>, F0>, Map<Button<T>, F1>>>;
#[derive(Clone, Copy)]
pub enum CancelInfoConfirmMsg {
Cancelled,
Info,
@ -582,89 +582,3 @@ impl IconText {
}
}
}
pub struct FloatingButton<T> {
inner: T,
button: Floating<Button<&'static str>>,
}
pub enum FloatingButtonMsg<T> {
ButtonClicked,
Content(T),
}
impl<T> FloatingButton<T>
where
T: Component,
{
pub const fn top_right_corner(icon: Icon, inner: T) -> Self {
Self {
inner,
button: Floating::top_right(
theme::CORNER_BUTTON_SIDE,
theme::CORNER_BUTTON_SPACING,
Button::with_icon(icon)
.with_expanded_touch_area(Insets::uniform(theme::CORNER_BUTTON_SPACING))
.styled(theme::button_moreinfo()),
),
}
}
pub fn inner(&self) -> &T {
&self.inner
}
}
impl<T> Component for FloatingButton<T>
where
T: Component,
{
type Msg = FloatingButtonMsg<T::Msg>;
fn place(&mut self, bounds: Rect) -> Rect {
self.button.place(bounds);
self.inner.place(bounds)
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if let Some(ButtonMsg::Clicked) = self.button.event(ctx, event) {
return Some(FloatingButtonMsg::ButtonClicked);
}
self.inner.event(ctx, event).map(FloatingButtonMsg::Content)
}
fn paint(&mut self) {
self.inner.paint();
self.button.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.inner.bounds(sink);
self.button.bounds(sink);
}
}
impl<T> Paginate for FloatingButton<T>
where
T: Paginate,
{
fn page_count(&mut self) -> usize {
self.inner.page_count()
}
fn change_page(&mut self, to_page: usize) {
self.inner.change_page(to_page)
}
}
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for FloatingButton<T>
where
T: Component + crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("FloatingButton");
t.field("inner", self.inner());
t.close();
}
}

@ -3,16 +3,24 @@ use crate::ui::{
component::{
base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx,
},
display::{self, Color, Font},
display::{self, Color, Font, Icon},
geometry::{Alignment, Insets, Offset, Rect},
model_tt::component::{Button, ButtonMsg, CancelInfoConfirmMsg},
};
pub struct Frame<T, U> {
border: Insets,
title: Child<Label<U>>,
button: Option<Child<Button<&'static str>>>,
button_msg: CancelInfoConfirmMsg,
content: Child<T>,
}
pub enum FrameMsg<T> {
Content(T),
Button(CancelInfoConfirmMsg),
}
impl<T, U> Frame<T, U>
where
T: Component,
@ -21,7 +29,9 @@ where
pub fn new(style: TextStyle, alignment: Alignment, title: U, content: T) -> Self {
Self {
title: Child::new(Label::new(title, alignment, style)),
border: theme::borders_scroll(),
border: theme::borders(),
button: None,
button_msg: CancelInfoConfirmMsg::Cancelled,
content: Child::new(content),
}
}
@ -43,6 +53,35 @@ where
self
}
fn with_button(mut self, icon: Icon, msg: CancelInfoConfirmMsg) -> Self {
let touch_area = Insets {
left: self.border.left * 4,
bottom: self.border.bottom * 4,
..self.border
};
self.button = Some(Child::new(
Button::with_icon(icon)
.with_expanded_touch_area(touch_area)
.styled(theme::button_moreinfo()),
));
self.button_msg = msg;
self
}
pub fn with_cancel_button(self) -> Self {
self.with_button(
Icon::new(theme::ICON_CORNER_CANCEL),
CancelInfoConfirmMsg::Cancelled,
)
}
pub fn with_info_button(self) -> Self {
self.with_button(
Icon::new(theme::ICON_CORNER_INFO),
CancelInfoConfirmMsg::Info,
)
}
pub fn inner(&self) -> &T {
self.content.inner()
}
@ -71,31 +110,51 @@ where
T: Component,
U: AsRef<str>,
{
type Msg = T::Msg;
type Msg = FrameMsg<T::Msg>;
fn place(&mut self, bounds: Rect) -> Rect {
const TITLE_SPACE: i16 = theme::BUTTON_SPACING;
let bounds = bounds.inset(self.border);
let title_area = bounds.inset(Insets::sides(theme::CONTENT_BORDER));
let title_area = self.title.place(title_area);
let content_area = bounds.inset(Insets::top(title_area.height() + TITLE_SPACE));
self.content.place(content_area);
if let Some(b) = &mut self.button {
let button_side = theme::CORNER_BUTTON_SIDE;
let (title_area, button_area) = bounds.split_right(button_side);
let (button_area, _) = button_area.split_top(button_side);
b.place(button_area);
let title_area = self.title.place(title_area);
let title_height = title_area.height();
let height = title_area.height().max(button_side);
if title_height < button_side {
self.title
.place(title_area.translate(Offset::y((button_side - title_height) / 2)));
}
let content_area = bounds.inset(Insets::top(height + TITLE_SPACE));
self.content.place(content_area);
} else {
let title_area = self.title.place(bounds);
let content_area = bounds.inset(Insets::top(title_area.height() + TITLE_SPACE));
self.content.place(content_area);
}
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
self.title.event(ctx, event);
self.content.event(ctx, event)
if let Some(ButtonMsg::Clicked) = self.button.event(ctx, event) {
return Some(FrameMsg::Button(self.button_msg));
}
self.content.event(ctx, event).map(FrameMsg::Content)
}
fn paint(&mut self) {
self.title.paint();
self.button.paint();
self.content.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.title.bounds(sink);
self.button.bounds(sink);
self.content.bounds(sink);
}
}
@ -109,6 +168,9 @@ where
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("Frame");
t.field("title", &self.title);
if let Some(b) = &self.button {
t.field("button", b);
}
t.field("content", &self.content);
t.close();
}
@ -150,7 +212,7 @@ where
display::text_center(
area.center() + Offset::y((font.text_max_height() - font.text_baseline()) / 2),
title,
Font::BOLD,
font,
theme::FG,
color,
);

@ -22,11 +22,11 @@ mod welcome_screen;
pub use address_details::AddressDetails;
pub use button::{
Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet, CancelConfirmMsg,
CancelInfoConfirmMsg, FloatingButton, FloatingButtonMsg, IconText, SelectWordMsg,
CancelInfoConfirmMsg, IconText, SelectWordMsg,
};
pub use dialog::{Dialog, DialogMsg, IconDialog};
pub use fido::{FidoConfirm, FidoMsg};
pub use frame::{Frame, NotificationFrame};
pub use frame::{Frame, FrameMsg, NotificationFrame};
pub use hold_to_confirm::{HoldToConfirm, HoldToConfirmMsg};
#[cfg(feature = "dma2d")]
pub use homescreen::{Homescreen, HomescreenMsg, Lockscreen};

@ -46,13 +46,12 @@ use crate::{
use super::{
component::{
AddressDetails, Bip39Input, Button, ButtonMsg, ButtonStyleSheet, CancelConfirmMsg,
CancelInfoConfirmMsg, Dialog, DialogMsg, FidoConfirm, FidoMsg, FloatingButton,
FloatingButtonMsg, Frame, HoldToConfirm, HoldToConfirmMsg, Homescreen, HomescreenMsg,
HorizontalPage, IconDialog, Lockscreen, MnemonicInput, MnemonicKeyboard,
MnemonicKeyboardMsg, NotificationFrame, NumberInputDialog, NumberInputDialogMsg,
PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress,
SelectWordCount, SelectWordCountMsg, SelectWordMsg, Slip39Input, SwipeHoldPage, SwipePage,
WelcomeScreen,
CancelInfoConfirmMsg, Dialog, DialogMsg, FidoConfirm, FidoMsg, Frame, FrameMsg,
HoldToConfirm, HoldToConfirmMsg, Homescreen, HomescreenMsg, HorizontalPage, IconDialog,
Lockscreen, MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, NotificationFrame,
NumberInputDialog, NumberInputDialogMsg, PassphraseKeyboard, PassphraseKeyboardMsg,
PinKeyboard, PinKeyboardMsg, Progress, SelectWordCount, SelectWordCountMsg, SelectWordMsg,
Slip39Input, SwipeHoldPage, SwipePage, WelcomeScreen,
},
constant, theme,
};
@ -200,7 +199,10 @@ where
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
self.inner().msg_try_into_obj(msg)
match msg {
FrameMsg::Content(c) => self.inner().msg_try_into_obj(c),
FrameMsg::Button(b) => b.try_into(),
}
}
}
@ -340,18 +342,6 @@ impl ComponentMsgObj for Qr {
}
}
impl<T> ComponentMsgObj for FloatingButton<T>
where
T: ComponentMsgObj,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
FloatingButtonMsg::ButtonClicked => Ok(INFO.as_obj()),
FloatingButtonMsg::Content(c) => self.inner().msg_try_into_obj(c),
}
}
}
impl<T> ComponentMsgObj for HorizontalPage<T>
where
T: ComponentMsgObj + Paginate,
@ -371,7 +361,7 @@ where
T: ParagraphStrType + Clone,
{
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
unreachable!();
Ok(CANCELLED.as_obj())
}
}
@ -508,16 +498,16 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
.into_paragraphs();
let buttons = Button::cancel_confirm_text(None, Some("CONFIRM"));
let obj = LayoutObj::new(FloatingButton::top_right_corner(
Icon::new(theme::ICON_CORNER_INFO),
let obj = LayoutObj::new(
Frame::left_aligned(
theme::label_title(),
title,
SwipePage::new(paragraphs, buttons, theme::BG)
.with_swipe_left()
.with_cancel_on_first_page(),
),
))?;
)
.with_info_button(),
)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -572,14 +562,11 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
};
let buttons = Button::cancel_confirm_text(None, Some("CONFIRM"));
let obj = LayoutObj::new(
Frame::centered(
theme::label_title(),
title,
Dialog::new(painter::jpeg_painter(buffer_func, size, 1), buttons),
)
.with_border(theme::borders()),
)?;
let obj = LayoutObj::new(Frame::centered(
theme::label_title(),
title,
Dialog::new(painter::jpeg_painter(buffer_func, size, 1), buttons),
))?;
Ok(obj.into())
};
@ -606,14 +593,11 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
Button::with_text(button).styled(theme::button_confirm()),
true,
);
let obj = LayoutObj::new(
Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, buttons),
)
.with_border(theme::borders()),
)?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, buttons),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -632,14 +616,11 @@ extern "C" fn new_show_qr(n_args: usize, args: *const Obj, kwargs: *mut Map) ->
false,
);
let obj = LayoutObj::new(
Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(Qr::new(address, case_sensitive)?.with_border(4), buttons),
)
.with_border(theme::borders()),
)?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(Qr::new(address, case_sensitive)?.with_border(4), buttons),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -663,13 +644,7 @@ extern "C" fn new_show_address_details(n_args: usize, args: *const Obj, kwargs:
ad.add_xpub(xtitle, text)?;
}
let obj = LayoutObj::new(
HorizontalPage::new(
FloatingButton::top_right_corner(Icon::new(theme::ICON_CORNER_CANCEL), ad),
theme::BG,
)
.with_swipe_right_to_go_back(),
)?;
let obj = LayoutObj::new(HorizontalPage::new(ad, theme::BG).with_swipe_right_to_go_back())?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -889,9 +864,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls);
let obj = LayoutObj::new(
Frame::centered(theme::label_title(), title, fido_page).with_border(theme::borders()),
)?;
let obj = LayoutObj::new(Frame::centered(theme::label_title(), title, fido_page))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1043,14 +1016,11 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
let buttons = Button::cancel_info_confirm(button, info_button);
let obj = LayoutObj::new(
Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs.into_paragraphs(), buttons),
)
.with_border(theme::borders()),
)?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs.into_paragraphs(), buttons),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1216,14 +1186,11 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
.unwrap()
};
let obj = LayoutObj::new(
Frame::left_aligned(
theme::label_title(),
title,
NumberInputDialog::new(min_count, max_count, count, callback),
)
.with_border(theme::borders()),
)?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
NumberInputDialog::new(min_count, max_count, count, callback),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1249,26 +1216,23 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
paragraphs.add(Paragraph::new(style, text));
}
let obj = LayoutObj::new(
Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(
Checklist::from_paragraphs(
Icon::new(theme::ICON_LIST_CURRENT),
Icon::new(theme::ICON_LIST_CHECK),
active,
paragraphs
.into_paragraphs()
.with_spacing(theme::CHECKLIST_SPACING),
),
theme::button_bar(Button::with_text(button).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})),
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(
Checklist::from_paragraphs(
Icon::new(theme::ICON_LIST_CURRENT),
Icon::new(theme::ICON_LIST_CHECK),
active,
paragraphs
.into_paragraphs()
.with_spacing(theme::CHECKLIST_SPACING),
),
)
.with_border(theme::borders()),
)?;
theme::button_bar(Button::with_text(button).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})),
),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1346,14 +1310,11 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
.centered(),
);
let obj = LayoutObj::new(
Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, SelectWordCount::new()),
)
.with_border(theme::borders()),
)?;
let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title,
Dialog::new(paragraphs, SelectWordCount::new()),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }

@ -553,22 +553,18 @@ pub const fn button_bar<T>(inner: T) -> FixedHeightBar<T> {
}
/// +----------+
/// | 13 |
/// | 6 |
/// | +----+ |
/// |10| |10|
/// | 6| | 6|
/// | +----+ |
/// | 14 |
/// | 6 |
/// +----------+
pub const fn borders() -> Insets {
Insets::new(6, 6, 6, 6)
}
pub const fn borders_scroll() -> Insets {
Insets::new(6, 6, 6, 6)
}
pub const fn borders_horizontal_scroll() -> Insets {
Insets::new(13, 10, 0, 10)
Insets::new(6, 6, 0, 6)
}
pub const fn borders_notification() -> Insets {

Loading…
Cancel
Save