mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-21 09:39:02 +00:00
feat(eckhart): set brightness screen
This commit is contained in:
parent
ed43fd8dae
commit
34af0db250
@ -17,7 +17,7 @@ use super::firmware::{
|
||||
AllowedTextContent, ConfirmHomescreen, ConfirmHomescreenMsg, Homescreen, HomescreenMsg,
|
||||
MnemonicInput, MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputScreen, NumberInputScreenMsg,
|
||||
PinKeyboard, PinKeyboardMsg, SelectWordCountMsg, SelectWordCountScreen, SelectWordMsg,
|
||||
SelectWordScreen, TextScreen, TextScreenMsg,
|
||||
SelectWordScreen, SetBrightnessMsg, SetBrightnessScreen, TextScreen, TextScreenMsg,
|
||||
};
|
||||
|
||||
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 brightness_screen;
|
||||
mod confirm_homescreen;
|
||||
mod header;
|
||||
mod hint;
|
||||
@ -14,6 +15,7 @@ mod vertical_menu;
|
||||
mod vertical_menu_screen;
|
||||
|
||||
pub use action_bar::{ActionBar, ActionBarMsg};
|
||||
pub use brightness_screen::{SetBrightnessMsg, SetBrightnessScreen};
|
||||
pub use confirm_homescreen::{ConfirmHomescreen, ConfirmHomescreenMsg};
|
||||
pub use header::{Header, HeaderMsg};
|
||||
pub use hint::Hint;
|
||||
|
@ -34,7 +34,7 @@ use super::{
|
||||
firmware::{
|
||||
ActionBar, Bip39Input, ConfirmHomescreen, Header, HeaderMsg, Hint, Homescreen,
|
||||
MnemonicKeyboard, NumberInputScreen, PinKeyboard, SelectWordCountScreen, SelectWordScreen,
|
||||
Slip39Input, TextScreen,
|
||||
SetBrightnessScreen, Slip39Input, TextScreen,
|
||||
},
|
||||
flow, fonts, theme, UIEckhart,
|
||||
};
|
||||
@ -648,8 +648,14 @@ impl FirmwareUI for UIEckhart {
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
fn set_brightness(_current_brightness: Option<u8>) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::ValueError(c"not implemented"))
|
||||
fn set_brightness(current_brightness: Option<u8>) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
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(
|
||||
|
Loading…
Reference in New Issue
Block a user