1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-24 15:28:10 +00:00

refactor(core/ui): optimize brightness settings for Mercury

This commit is contained in:
Ioan Bizău 2024-07-02 18:43:13 +02:00 committed by Ioan Bizău
parent 2994317dcd
commit 7404436305
10 changed files with 149 additions and 129 deletions

View File

@ -0,0 +1 @@
[T3T1] Improved screen brightness settings.

View File

@ -1330,7 +1330,7 @@ pub enum TranslatedString {
words__settings = 929, // "Settings"
words__try_again = 930, // "Try again."
reset__slip39_checklist_num_groups_x_template = 931, // "Number of groups: {0}"
brightness__title = 932, // "Set brightness"
brightness__title = 932, // "Change display brightness"
recovery__title_unlock_repeated_backup = 933, // "Multi-share backup"
recovery__unlock_repeated_backup = 934, // "Create additional backup?"
recovery__unlock_repeated_backup_verb = 935, // "Create backup"
@ -2673,7 +2673,7 @@ impl TranslatedString {
Self::words__settings => "Settings",
Self::words__try_again => "Try again.",
Self::reset__slip39_checklist_num_groups_x_template => "Number of groups: {0}",
Self::brightness__title => "Set brightness",
Self::brightness__title => "Change display brightness",
Self::recovery__title_unlock_repeated_backup => "Multi-share backup",
Self::recovery__unlock_repeated_backup => "Create additional backup?",
Self::recovery__unlock_repeated_backup_verb => "Create backup",

View File

@ -27,8 +27,6 @@ mod prompt_screen;
mod result;
mod scroll;
#[cfg(feature = "translations")]
mod set_brightness;
#[cfg(feature = "translations")]
mod share_words;
mod status_screen;
mod swipe_content;
@ -74,8 +72,6 @@ pub use prompt_screen::PromptScreen;
pub use result::{ResultFooter, ResultScreen, ResultStyle};
pub use scroll::ScrollBar;
#[cfg(feature = "translations")]
pub use set_brightness::SetBrightnessDialog;
#[cfg(feature = "translations")]
pub use share_words::ShareWords;
pub use status_screen::StatusScreen;
pub use swipe_content::{InternallySwipable, InternallySwipableContent, SwipeContent};

View File

@ -5,25 +5,21 @@ use crate::{
constant::screen,
display,
event::TouchEvent,
geometry::{Alignment, Grid, Insets, Point, Rect},
geometry::{Alignment, Insets, Point, Rect},
shape::{self, Renderer},
},
};
use super::{theme, Button, ButtonMsg};
use super::theme;
pub enum NumberInputSliderDialogMsg {
Changed(u16),
Confirmed,
Cancelled,
}
pub struct NumberInputSliderDialog {
area: Rect,
text_area: Rect,
input: Child<NumberInputSlider>,
cancel_button: Child<Button>,
confirm_button: Child<Button>,
min: u16,
max: u16,
val: u16,
@ -35,12 +31,6 @@ impl NumberInputSliderDialog {
area: Rect::zero(),
text_area: Rect::zero(),
input: NumberInputSlider::new(min, max, init_value).into_child(),
cancel_button: Button::with_text("CANCEL".into())
.styled(theme::button_cancel())
.into_child(),
confirm_button: Button::with_text("CONFIRM".into())
.styled(theme::button_confirm())
.into_child(),
min,
max,
val: init_value,
@ -57,18 +47,14 @@ impl Component for NumberInputSliderDialog {
fn place(&mut self, bounds: Rect) -> Rect {
self.area = bounds;
let button_height = theme::BUTTON_HEIGHT;
let content_area = self.area.inset(Insets::top(2 * theme::BUTTON_SPACING));
let (_, content_area) = content_area.split_top(30);
let (input_area, _) = content_area.split_top(15);
let (text_area, button_area) = content_area.split_bottom(button_height);
let (text_area, _) = content_area.split_bottom(theme::BUTTON_HEIGHT);
self.text_area = text_area;
let grid = Grid::new(button_area, 1, 2).with_spacing(theme::KEYBOARD_SPACING);
self.input.place(input_area.inset(Insets::sides(20)));
self.cancel_button.place(grid.row_col(0, 0));
self.confirm_button.place(grid.row_col(0, 1));
bounds
}
@ -77,19 +63,11 @@ impl Component for NumberInputSliderDialog {
self.val = value;
return Some(Self::Msg::Changed(value));
}
if let Some(ButtonMsg::Clicked) = self.cancel_button.event(ctx, event) {
return Some(Self::Msg::Cancelled);
}
if let Some(ButtonMsg::Clicked) = self.confirm_button.event(ctx, event) {
return Some(Self::Msg::Confirmed);
};
None
}
fn paint(&mut self) {
self.input.paint();
self.cancel_button.paint();
self.confirm_button.paint();
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
@ -105,9 +83,6 @@ impl Component for NumberInputSliderDialog {
.with_fg(theme::TEXT_NORMAL.text_color)
.with_align(Alignment::Center)
.render(target);
self.cancel_button.render(target);
self.confirm_button.render(target);
}
}
@ -116,8 +91,6 @@ impl crate::trace::Trace for NumberInputSliderDialog {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("NumberInputSliderDialog");
t.child("input", &self.input);
t.child("cancel_button", &self.cancel_button);
t.child("confirm_button", &self.confirm_button);
}
}

View File

@ -1,69 +0,0 @@
use crate::{
storage,
trezorhal::display,
ui::{
component::{Component, Event, EventCtx},
geometry::Rect,
shape::Renderer,
},
};
use super::{
super::theme,
number_input_slider::{NumberInputSliderDialog, NumberInputSliderDialogMsg},
CancelConfirmMsg,
};
pub struct SetBrightnessDialog(NumberInputSliderDialog);
impl SetBrightnessDialog {
pub fn new(current: Option<u16>) -> Self {
let current = current.unwrap_or(theme::backlight::get_backlight_normal());
Self(NumberInputSliderDialog::new(
theme::backlight::get_backlight_min(),
theme::backlight::get_backlight_max(),
current,
))
}
}
impl Component for SetBrightnessDialog {
type Msg = CancelConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.0.place(bounds)
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
match self.0.event(ctx, event) {
Some(NumberInputSliderDialogMsg::Changed(value)) => {
display::backlight(value as _);
None
}
Some(NumberInputSliderDialogMsg::Cancelled) => Some(CancelConfirmMsg::Cancelled),
Some(NumberInputSliderDialogMsg::Confirmed) => {
match storage::set_brightness(self.0.value() as _) {
Ok(_) => Some(CancelConfirmMsg::Confirmed),
Err(_) => Some(CancelConfirmMsg::Cancelled), // TODO: handle error
}
}
None => None,
}
}
fn paint(&mut self) {
self.0.paint()
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.0.render(target);
}
}
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for SetBrightnessDialog {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("SetBrightnessDialog");
t.child("input", &self.0);
}
}

View File

@ -7,6 +7,7 @@ pub mod confirm_summary;
pub mod get_address;
pub mod prompt_backup;
pub mod request_number;
pub mod set_brightness;
pub mod show_share_words;
pub mod show_tutorial;
pub mod warning_hi_prio;
@ -22,6 +23,7 @@ pub use confirm_summary::new_confirm_summary;
pub use get_address::GetAddress;
pub use prompt_backup::PromptBackup;
pub use request_number::RequestNumber;
pub use set_brightness::SetBrightness;
pub use show_share_words::ShowShareWords;
pub use show_tutorial::ShowTutorial;
pub use warning_hi_prio::WarningHiPrio;

View File

@ -0,0 +1,138 @@
use core::sync::atomic::{AtomicU16, Ordering};
use crate::{
error::Error,
micropython::{map::Map, obj::Obj, qstr::Qstr, util},
storage,
translations::TR,
trezorhal::display,
ui::{
component::{base::ComponentExt, swipe_detect::SwipeSettings, SwipeDirection},
flow::{
base::{Decision, FlowMsg},
flow_store, FlowState, FlowStore, SwipeFlow,
},
layout::obj::LayoutObj,
model_mercury::component::{
number_input_slider::{NumberInputSliderDialog, NumberInputSliderDialogMsg},
SwipeContent,
},
},
};
use super::super::{
component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg},
theme,
};
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
pub enum SetBrightness {
Slider,
Menu,
Confirm,
}
impl FlowState for SetBrightness {
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
match (self, direction) {
(SetBrightness::Menu, SwipeDirection::Right) => {
Decision::Goto(SetBrightness::Slider, direction)
}
(SetBrightness::Slider, SwipeDirection::Up) => {
Decision::Goto(SetBrightness::Confirm, direction)
}
(SetBrightness::Confirm, SwipeDirection::Down) => {
Decision::Goto(SetBrightness::Slider, direction)
}
(SetBrightness::Confirm, SwipeDirection::Left) => {
Decision::Goto(SetBrightness::Menu, direction)
}
_ => Decision::Nothing,
}
}
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
match (self, msg) {
(SetBrightness::Slider, FlowMsg::Info) => {
Decision::Goto(SetBrightness::Menu, SwipeDirection::Left)
}
(SetBrightness::Menu, FlowMsg::Cancelled) => {
Decision::Goto(SetBrightness::Slider, SwipeDirection::Right)
}
(SetBrightness::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled),
(SetBrightness::Confirm, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Confirmed),
(SetBrightness::Confirm, FlowMsg::Info) => {
Decision::Goto(SetBrightness::Menu, SwipeDirection::Left)
}
_ => Decision::Nothing,
}
}
}
static BRIGHTNESS: AtomicU16 = AtomicU16::new(0);
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn new_set_brightness(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, SetBrightness::new_obj) }
}
impl SetBrightness {
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, Error> {
let current: Option<u16> = kwargs.get(Qstr::MP_QSTR_current)?.try_into_option()?;
let content_slider = Frame::left_aligned(
TR::brightness__title.into(),
NumberInputSliderDialog::new(
theme::backlight::get_backlight_min(),
theme::backlight::get_backlight_max(),
current.unwrap_or(theme::backlight::get_backlight_normal()),
),
)
.with_menu_button()
.with_footer(TR::instructions__swipe_up.into(), None)
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
.map(|msg| match msg {
FrameMsg::Content(NumberInputSliderDialogMsg::Changed(n)) => {
display::backlight(n as _);
BRIGHTNESS.store(n, Ordering::Relaxed);
None
}
FrameMsg::Button(_) => Some(FlowMsg::Info),
});
let content_menu = Frame::left_aligned(
"".into(),
VerticalMenu::empty().danger(theme::ICON_CANCEL, TR::buttons__cancel.into()),
)
.with_cancel_button()
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
.map(move |msg| match msg {
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
});
let content_confirm = Frame::left_aligned(
TR::brightness__title.into(),
SwipeContent::new(PromptScreen::new_tap_to_confirm()),
)
.with_footer(TR::instructions__tap_to_confirm.into(), None)
.with_menu_button()
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
.with_swipe(SwipeDirection::Left, SwipeSettings::default())
.map(move |msg| match msg {
FrameMsg::Content(()) => {
let _ = storage::set_brightness(BRIGHTNESS.load(Ordering::Relaxed) as u8);
Some(FlowMsg::Confirmed)
}
FrameMsg::Button(_) => Some(FlowMsg::Info),
});
let store = flow_store()
.add(content_slider)?
.add(content_menu)?
.add(content_confirm)?;
let res = SwipeFlow::new(SetBrightness::Slider, store)?;
Ok(LayoutObj::new(res)?.into())
}
}

View File

@ -217,15 +217,6 @@ where
}
}
impl ComponentMsgObj for SetBrightnessDialog {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg {
CancelConfirmMsg::Confirmed => Ok(CONFIRMED.as_obj()),
CancelConfirmMsg::Cancelled => Ok(CANCELLED.as_obj()),
}
}
}
impl ComponentMsgObj for Progress {
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
unreachable!()
@ -1025,18 +1016,6 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_set_brightness(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let current: Option<u16> = kwargs.get(Qstr::MP_QSTR_current)?.try_into_option()?;
let obj = LayoutObj::new(Frame::centered(
TR::brightness__title.into(),
SetBrightnessDialog::new(current),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
@ -1734,7 +1713,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// current: int | None = None
/// ) -> LayoutObj[UiResult]:
/// """Show the brightness configuration dialog."""
Qstr::MP_QSTR_set_brightness => obj_fn_kw!(0, new_set_brightness).as_obj(),
Qstr::MP_QSTR_set_brightness => obj_fn_kw!(0, flow::set_brightness::new_set_brightness).as_obj(),
/// def show_checklist(
/// *,

View File

@ -79,7 +79,7 @@ class TR:
bitcoin__unverified_external_inputs: str = "The transaction contains unverified external inputs."
bitcoin__valid_signature: str = "The signature is valid."
bitcoin__voting_rights: str = "Voting rights to:"
brightness__title: str = "Set brightness"
brightness__title: str = "Change display brightness"
buttons__abort: str = "Abort"
buttons__access: str = "Access"
buttons__again: str = "Again"

View File

@ -81,7 +81,7 @@
"bitcoin__unverified_external_inputs": "The transaction contains unverified external inputs.",
"bitcoin__valid_signature": "The signature is valid.",
"bitcoin__voting_rights": "Voting rights to:",
"brightness__title": "Set brightness",
"brightness__title": "Change display brightness",
"buttons__abort": "Abort",
"buttons__access": "Access",
"buttons__again": "Again",