mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-03 22:58:46 +00:00
feat(eckhart): set brightness screen
This commit is contained in:
parent
b686f0dd9e
commit
efc2511f26
@ -17,7 +17,7 @@ use super::firmware::{
|
|||||||
AllowedTextContent, ConfirmHomescreen, ConfirmHomescreenMsg, Homescreen, HomescreenMsg,
|
AllowedTextContent, ConfirmHomescreen, ConfirmHomescreenMsg, Homescreen, HomescreenMsg,
|
||||||
MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputScreen, NumberInputScreenMsg,
|
MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputScreen, NumberInputScreenMsg,
|
||||||
PinKeyboard, PinKeyboardMsg, SelectWordCountMsg, SelectWordCountScreen, SelectWordMsg,
|
PinKeyboard, PinKeyboardMsg, SelectWordCountMsg, SelectWordCountScreen, SelectWordMsg,
|
||||||
SelectWordScreen, TextScreen, TextScreenMsg,
|
SelectWordScreen, SetBrightnessMsg, SetBrightnessScreen, TextScreen, TextScreenMsg,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ComponentMsgObj for PinKeyboard<'_> {
|
impl ComponentMsgObj for PinKeyboard<'_> {
|
||||||
@ -128,3 +128,12 @@ impl ComponentMsgObj for ConfirmHomescreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComponentMsgObj for SetBrightnessScreen {
|
||||||
|
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
|
match msg {
|
||||||
|
SetBrightnessMsg::Confirmed => Ok(CONFIRMED.as_obj()),
|
||||||
|
SetBrightnessMsg::Cancelled => Ok(CANCELLED.as_obj()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,216 @@
|
|||||||
|
use crate::{
|
||||||
|
storage,
|
||||||
|
translations::TR,
|
||||||
|
trezorhal::display,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx},
|
||||||
|
event::TouchEvent,
|
||||||
|
geometry::{Alignment2D, Insets, Offset, Point, Rect},
|
||||||
|
shape::{Bar, Renderer},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
constant::SCREEN,
|
||||||
|
firmware::{Header, HeaderMsg},
|
||||||
|
theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SetBrightnessScreen {
|
||||||
|
header: Header,
|
||||||
|
slider: VerticalSlider,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SetBrightnessMsg {
|
||||||
|
Confirmed,
|
||||||
|
Cancelled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SetBrightnessScreen {
|
||||||
|
const SLIDER_HEIGHT: i16 = 392;
|
||||||
|
pub fn new(min: u16, max: u16, init_value: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
header: Header::new(TR::brightness__title.into()).with_close_button(),
|
||||||
|
slider: VerticalSlider::new(min, max, init_value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for SetBrightnessScreen {
|
||||||
|
type Msg = SetBrightnessMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
// assert full screen
|
||||||
|
debug_assert_eq!(bounds.height(), SCREEN.height());
|
||||||
|
debug_assert_eq!(bounds.width(), SCREEN.width());
|
||||||
|
|
||||||
|
let (header_area, rest) = bounds.split_top(Header::HEADER_HEIGHT);
|
||||||
|
let (slider_area, _) = rest.split_top(Self::SLIDER_HEIGHT);
|
||||||
|
|
||||||
|
self.header.place(header_area);
|
||||||
|
self.slider.place(slider_area);
|
||||||
|
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(HeaderMsg::Cancelled) = self.header.event(ctx, event) {
|
||||||
|
return Some(SetBrightnessMsg::Cancelled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(value) = self.slider.event(ctx, event) {
|
||||||
|
unwrap!(storage::set_brightness(value as _));
|
||||||
|
return Some(SetBrightnessMsg::Confirmed);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.header.render(target);
|
||||||
|
self.slider.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for SetBrightnessScreen {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("SetBrightnessScreen");
|
||||||
|
t.child("Header", &self.header);
|
||||||
|
t.child("Slider", &self.slider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VerticalSlider {
|
||||||
|
area: Rect,
|
||||||
|
touch_area: Rect,
|
||||||
|
min: u16,
|
||||||
|
max: u16,
|
||||||
|
value: u16,
|
||||||
|
val_pct: u16,
|
||||||
|
touching: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerticalSlider {
|
||||||
|
const SLIDER_WIDTH: i16 = 120;
|
||||||
|
|
||||||
|
pub fn new(min: u16, max: u16, value: u16) -> Self {
|
||||||
|
debug_assert!(min < max);
|
||||||
|
let value = value.clamp(min, max);
|
||||||
|
Self {
|
||||||
|
area: Rect::zero(),
|
||||||
|
touch_area: Rect::zero(),
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
value,
|
||||||
|
val_pct: 0,
|
||||||
|
touching: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_value(&mut self, pos: Point, ctx: &mut EventCtx) {
|
||||||
|
// Area where slider value is not saturated
|
||||||
|
let proportional_area = self.area.inset(Insets::new(
|
||||||
|
Self::SLIDER_WIDTH / 2,
|
||||||
|
0,
|
||||||
|
Self::SLIDER_WIDTH / 2,
|
||||||
|
0,
|
||||||
|
));
|
||||||
|
|
||||||
|
let filled = (proportional_area.y1 - pos.y).clamp(0, proportional_area.height());
|
||||||
|
let val_pct = (filled as u16 * 100) / proportional_area.height() as u16;
|
||||||
|
let val = (val_pct * (self.max - self.min)) / 100 + self.min;
|
||||||
|
|
||||||
|
if val != self.value {
|
||||||
|
ctx.request_paint();
|
||||||
|
self.value = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for VerticalSlider {
|
||||||
|
type Msg = u8;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.area = Rect::snap(
|
||||||
|
bounds.center(),
|
||||||
|
Offset::new(Self::SLIDER_WIDTH, bounds.height()),
|
||||||
|
Alignment2D::CENTER,
|
||||||
|
);
|
||||||
|
self.touch_area = self.area.outset(Insets::uniform(20));
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Event::Touch(touch_event) = event {
|
||||||
|
match touch_event {
|
||||||
|
TouchEvent::TouchStart(pos) if self.touch_area.contains(pos) => {
|
||||||
|
// Detect only touches inside the touch area
|
||||||
|
self.touching = true;
|
||||||
|
self.update_value(pos, ctx);
|
||||||
|
display::backlight(self.value as _);
|
||||||
|
ctx.request_paint();
|
||||||
|
}
|
||||||
|
TouchEvent::TouchMove(pos) if self.touching => {
|
||||||
|
self.update_value(pos, ctx);
|
||||||
|
// Update only if the touch started inside the touch area
|
||||||
|
display::backlight(self.value as _);
|
||||||
|
}
|
||||||
|
TouchEvent::TouchEnd(pos) if self.touching => {
|
||||||
|
self.touching = false;
|
||||||
|
self.update_value(pos, ctx);
|
||||||
|
ctx.request_paint();
|
||||||
|
// Confirm the value only if the touch ended inside the touch area
|
||||||
|
if self.touch_area.contains(pos) {
|
||||||
|
return Some(self.value as _);
|
||||||
|
} else {
|
||||||
|
display::backlight(self.value as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
let val_pct = ((100 * (self.value - self.min)) / (self.max - self.min)).clamp(0, 100);
|
||||||
|
|
||||||
|
// Square area for the slider
|
||||||
|
let (_, small_area) = self.area.split_bottom(Self::SLIDER_WIDTH);
|
||||||
|
|
||||||
|
// Background pad
|
||||||
|
Bar::new(self.area)
|
||||||
|
.with_radius(12)
|
||||||
|
.with_bg(theme::GREY_EXTRA_DARK)
|
||||||
|
.render(target);
|
||||||
|
|
||||||
|
// Moving slider
|
||||||
|
Bar::new(small_area.translate(
|
||||||
|
Offset::y(val_pct as i16 * (self.area.height() - Self::SLIDER_WIDTH) / 100).neg(),
|
||||||
|
))
|
||||||
|
.with_radius(4)
|
||||||
|
.with_bg(theme::GREY_LIGHT)
|
||||||
|
.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for VerticalSlider {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("VerticalSlider");
|
||||||
|
t.int("value", self.value as i64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{super::super::constant::SCREEN, *};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_component_heights_fit_screen() {
|
||||||
|
assert!(
|
||||||
|
SetBrightnessScreen::SLIDER_HEIGHT + Header::HEADER_HEIGHT <= SCREEN.height(),
|
||||||
|
"Components overflow the screen height",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
mod action_bar;
|
mod action_bar;
|
||||||
|
mod brightness_screen;
|
||||||
mod confirm_homescreen;
|
mod confirm_homescreen;
|
||||||
mod header;
|
mod header;
|
||||||
mod hint;
|
mod hint;
|
||||||
@ -14,6 +15,7 @@ mod vertical_menu;
|
|||||||
mod vertical_menu_screen;
|
mod vertical_menu_screen;
|
||||||
|
|
||||||
pub use action_bar::{ActionBar, ActionBarMsg};
|
pub use action_bar::{ActionBar, ActionBarMsg};
|
||||||
|
pub use brightness_screen::{SetBrightnessMsg, SetBrightnessScreen};
|
||||||
pub use confirm_homescreen::{ConfirmHomescreen, ConfirmHomescreenMsg};
|
pub use confirm_homescreen::{ConfirmHomescreen, ConfirmHomescreenMsg};
|
||||||
pub use header::{Header, HeaderMsg};
|
pub use header::{Header, HeaderMsg};
|
||||||
pub use hint::Hint;
|
pub use hint::Hint;
|
||||||
|
@ -34,7 +34,7 @@ use super::{
|
|||||||
firmware::{
|
firmware::{
|
||||||
ActionBar, Bip39Input, ConfirmHomescreen, Header, HeaderMsg, Hint, Homescreen,
|
ActionBar, Bip39Input, ConfirmHomescreen, Header, HeaderMsg, Hint, Homescreen,
|
||||||
MnemonicKeyboard, NumberInputScreen, PinKeyboard, SelectWordCountScreen, SelectWordScreen,
|
MnemonicKeyboard, NumberInputScreen, PinKeyboard, SelectWordCountScreen, SelectWordScreen,
|
||||||
Slip39Input, TextScreen,
|
SetBrightnessScreen, Slip39Input, TextScreen,
|
||||||
},
|
},
|
||||||
flow, fonts, theme, UIEckhart,
|
flow, fonts, theme, UIEckhart,
|
||||||
};
|
};
|
||||||
@ -650,8 +650,14 @@ impl FirmwareUI for UIEckhart {
|
|||||||
Ok(layout)
|
Ok(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_brightness(_current_brightness: Option<u8>) -> Result<impl LayoutMaybeTrace, Error> {
|
fn set_brightness(current_brightness: Option<u8>) -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
let content = SetBrightnessScreen::new(
|
||||||
|
theme::backlight::get_backlight_min() as u16,
|
||||||
|
theme::backlight::get_backlight_max() as u16,
|
||||||
|
current_brightness.unwrap_or(theme::backlight::get_backlight_normal()) as u16,
|
||||||
|
);
|
||||||
|
let layout = RootComponent::new(content);
|
||||||
|
Ok(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_address_details(
|
fn show_address_details(
|
||||||
|
Loading…
Reference in New Issue
Block a user