refactor(core/caesar): simplify button code
What changed, and why it matters
This is a code cleanup (refactor) of the on-screen button drawing and touch/hold logic for the Trezor hardware wallet's Caesar UI layout. It removes unused style-sheet abstractions, simplifies how buttons are described, and makes the hold-to-confirm component always assume it sits on the right side of the screen. The commit is tagged [no changelog] and contains no explicit security claims. The only behavior-visible change is a small simplification in the page component: when the left button is triggered on the first page, it now always returns a Cancel message instead of first checking whether a cancel button was configured. That change is a simplification based on an existing invariant, not a fix for a known vulnerability.
No security action required. Treat as normal code-quality review. If reviewing for product safety, verify that the page.rs invariant (left-slot Triggered on first page implies a rendered cancel button) holds across all ButtonLayout configurations, and that HoldToConfirm's new right-side-only placement matches all intended usage sites.
Security signals we found
Refactor-only commit with [no changelog] tag
No changes to cryptographic, storage, or communication code
No explicit security relevance stated by vendor
Behavior change in page.rs is a simplification based on existing UI invariant
Hold-to-confirm component loses position parameter and assumes right-side placement
Evidence from the diff
The patch refactors core/embed/rust/src/ui/layout_caesar/component/button.rs and related files. Key changes: ButtonStyleSheet/ButtonStyle are removed and visual fields are inlined into Button; ButtonType::Nothing is replaced by Option
Changed components
core/embed/rust/src/ui/layout_caesar/component/button.rscore/embed/rust/src/ui/layout_caesar/component/button_controller.rscore/embed/rust/src/ui/layout_caesar/component/hold_to_confirm.rscore/embed/rust/src/ui/layout_caesar/component/mod.rscore/embed/rust/src/ui/layout_caesar/component/page.rsInspect captured patch +105 / −275
diff --git a/core/embed/rust/src/ui/layout_caesar/component/button.rs b/core/embed/rust/src/ui/layout_caesar/component/button.rs
index 750b78e9..7ac89e1c 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/button.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/button.rs
@@ -4,7 +4,7 @@ use crate::{
ui::{
component::{Component, Event, EventCtx, Never},
constant,
- display::{Color, Font, Icon},
+ display::{Font, Icon},
event::PhysicalButton,
geometry::{Alignment2D, Offset, Point, Rect},
shape,
@@ -37,49 +37,31 @@ pub struct Button {
bounds: Rect,
pos: ButtonPos,
content: ButtonContent,
- styles: ButtonStyleSheet,
+ font: Font,
+ decoration: Option<Decoration>,
+ fixed_width: Option<i16>,
+ offset: Offset,
state: State,
}
impl Button {
- pub fn new(pos: ButtonPos, content: ButtonContent, styles: ButtonStyleSheet) -> Self {
+ pub fn new(pos: ButtonPos, btn_details: ButtonDetails) -> Self {
Self {
pos,
- content,
- styles,
+ content: btn_details.content,
+ font: btn_details.font,
+ decoration: btn_details.decoration,
+ fixed_width: btn_details.fixed_width,
+ offset: btn_details.offset,
bounds: Rect::zero(),
state: State::Released,
}
}
- pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails) -> Self {
- // Deciding between text and icon
- let style = btn_details.style();
- match btn_details.content {
- ButtonContent::Text(text) => Self::with_text(pos, text, style),
- ButtonContent::Icon(icon) => Self::with_icon(pos, icon, style),
- }
- }
-
- pub fn with_text(pos: ButtonPos, text: TString<'static>, styles: ButtonStyleSheet) -> Self {
- Self::new(pos, ButtonContent::Text(text), styles)
- }
-
- pub fn with_icon(pos: ButtonPos, image: Icon, styles: ButtonStyleSheet) -> Self {
- Self::new(pos, ButtonContent::Icon(image), styles)
- }
-
pub fn content(&self) -> &ButtonContent {
&self.content
}
- fn style(&self) -> &ButtonStyle {
- match self.state {
- State::Released => &self.styles.normal,
- State::Pressed => &self.styles.active,
- }
- }
-
/// Changing the icon content of the button.
pub fn set_icon(&mut self, image: Icon) {
self.content = ButtonContent::Icon(image);
@@ -111,26 +93,22 @@ impl Button {
/// Return the full area of the button according
/// to its current style, content and position.
fn get_current_area(&self) -> Rect {
- let style = self.style();
-
// Button width may be forced. Otherwise calculate it.
- let button_width = if let Some(width) = style.fixed_width {
+ let button_width = if let Some(width) = self.fixed_width {
width
} else {
match &self.content {
ButtonContent::Text(text) => {
- let text_width = text.map(|t| style.font.visible_text_width(t));
- if style.with_outline {
- text_width + 2 * theme::BUTTON_OUTLINE
- } else if style.with_arms {
- text_width + 2 * theme::ARMS_MARGIN
- } else {
- text_width
+ let text_width = text.map(|t| self.font.visible_text_width(t));
+ match self.decoration {
+ Some(Decoration::Outline) => text_width + 2 * theme::BUTTON_OUTLINE,
+ Some(Decoration::Arms) => text_width + 2 * theme::ARMS_MARGIN,
+ None => text_width,
}
}
ButtonContent::Icon(icon) => {
// When Icon does not have outline, hardcode its width
- if style.with_outline {
+ if matches!(self.decoration, Some(Decoration::Outline)) {
icon.toif.width() + 2 * theme::BUTTON_OUTLINE
} else {
theme::BUTTON_ICON_WIDTH
@@ -140,7 +118,7 @@ impl Button {
};
// Arms should connect to the center, therefore decreasing the height
- let button_height = if style.with_arms {
+ let button_height = if matches!(self.decoration, Some(Decoration::Arms)) {
theme::BUTTON_HEIGHT - 2
} else {
theme::BUTTON_HEIGHT
@@ -154,21 +132,19 @@ impl Button {
}
/// Determine baseline point for the text.
- fn get_text_baseline(&self, style: &ButtonStyle) -> Point {
+ fn get_text_baseline(&self) -> Point {
// Arms and outline require the text to be elevated.
// Moving text to the right and elevating it for arms and outline.
- let (mut offset_x, offset_y) = if style.with_outline {
- (theme::BUTTON_OUTLINE, theme::BUTTON_OUTLINE)
- } else if style.with_arms {
- (theme::ARMS_MARGIN, theme::ARMS_MARGIN)
- } else {
- (0, 0)
+ let (mut offset_x, offset_y) = match self.decoration {
+ Some(Decoration::Outline) => (theme::BUTTON_OUTLINE, theme::BUTTON_OUTLINE),
+ Some(Decoration::Arms) => (theme::ARMS_MARGIN, theme::ARMS_MARGIN),
+ None => (0, 0),
};
// Centering the text in case of fixed width.
if let ButtonContent::Text(text) = &self.content {
- if let Some(fixed_width) = style.fixed_width {
- let diff = fixed_width - text.map(|t| style.font.visible_text_width(t));
+ if let Some(fixed_width) = self.fixed_width {
+ let diff = fixed_width - text.map(|t| self.font.visible_text_width(t));
offset_x = diff / 2;
}
}
@@ -191,8 +167,10 @@ impl Component for Button {
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
- let style = self.style();
- let fg_color = style.text_color;
+ let fg_color = match self.state {
+ State::Released => theme::FG,
+ State::Pressed => theme::BG,
+ };
let bg_color = fg_color.negate();
let area = self.get_current_area();
let inversed_colors = bg_color != theme::BG;
@@ -203,7 +181,7 @@ impl Component for Button {
.with_radius(3)
.with_bg(bg_color)
.render(target);
- } else if style.with_outline {
+ } else if matches!(self.decoration, Some(Decoration::Outline)) {
shape::Bar::new(area)
.with_radius(3)
.with_fg(fg_color)
@@ -214,7 +192,7 @@ impl Component for Button {
// Optionally display "arms" at both sides of content - always in FG and BG
// colors (they are not inverted).
- if style.with_arms {
+ if matches!(self.decoration, Some(Decoration::Arms)) {
shape::ToifImage::new(area.left_center(), theme::ICON_ARM_LEFT.toif)
.with_align(Alignment2D::TOP_RIGHT)
.with_fg(theme::FG)
@@ -230,17 +208,17 @@ impl Component for Button {
match &self.content {
ButtonContent::Text(text) => text.map(|t| {
shape::Text::new(
- self.get_text_baseline(style) - Offset::x(style.font.start_x_bearing(t)),
+ self.get_text_baseline() - Offset::x(self.font.start_x_bearing(t)),
t,
- style.font,
+ self.font,
)
.with_fg(fg_color)
.render(target);
}),
ButtonContent::Icon(icon) => {
// Allowing for possible offset of the area from current style
- let icon_area = area.translate(style.offset);
- if style.with_outline {
+ let icon_area = area.translate(self.offset);
+ if matches!(self.decoration, Some(Decoration::Outline)) {
shape::ToifImage::new(icon_area.center(), icon.toif)
.with_align(Alignment2D::CENTER)
.with_fg(fg_color)
@@ -279,84 +257,25 @@ enum State {
Pressed,
}
+#[derive(Copy, Clone)]
+pub enum Decoration {
+ Outline,
+ Arms,
+}
+
#[derive(Clone)]
pub enum ButtonContent {
Text(TString<'static>),
Icon(Icon),
}
-pub struct ButtonStyleSheet {
- pub normal: ButtonStyle,
- pub active: ButtonStyle,
-}
-
-pub struct ButtonStyle {
- pub font: Font,
- pub text_color: Color,
- pub with_outline: bool,
- pub with_arms: bool,
- pub fixed_width: Option<i16>,
- pub offset: Offset,
-}
-
-impl ButtonStyleSheet {
- pub fn new(
- font: Font,
- normal_color: Color,
- active_color: Color,
- with_outline: bool,
- with_arms: bool,
- fixed_width: Option<i16>,
- offset: Offset,
- ) -> Self {
- Self {
- normal: ButtonStyle {
- font,
- text_color: normal_color,
- with_outline,
- with_arms,
- fixed_width,
- offset,
- },
- active: ButtonStyle {
- font,
- text_color: active_color,
- with_outline,
- with_arms,
- fixed_width,
- offset,
- },
- }
- }
-
- // White text in normal mode.
- pub fn default(
- font: Font,
- with_outline: bool,
- with_arms: bool,
- fixed_width: Option<i16>,
- offset: Offset,
- ) -> Self {
- Self::new(
- font,
- theme::FG,
- theme::BG,
- with_outline,
- with_arms,
- fixed_width,
- offset,
- )
- }
-}
-
/// Describing the button on the screen - only visuals.
#[derive(Clone)]
pub struct ButtonDetails {
pub content: ButtonContent,
font: Font,
pub duration: Option<Duration>,
- with_outline: bool,
- with_arms: bool,
+ decoration: Option<Decoration>,
fixed_width: Option<i16>,
offset: Offset,
pub send_long_press: bool,
@@ -369,8 +288,7 @@ impl ButtonDetails {
content: ButtonContent::Text(text),
font: fonts::FONT_NORMAL_UPPER,
duration: None,
- with_outline: true,
- with_arms: false,
+ decoration: Some(Decoration::Outline),
fixed_width: None,
offset: Offset::zero(),
send_long_press: false,
@@ -383,8 +301,7 @@ impl ButtonDetails {
content: ButtonContent::Icon(icon),
font: fonts::FONT_NORMAL_UPPER,
duration: None,
- with_outline: false,
- with_arms: false,
+ decoration: None,
fixed_width: None,
offset: Offset::zero(),
send_long_press: false,
@@ -445,20 +362,20 @@ impl ButtonDetails {
/// Down arrow to signal paginating forward. Takes half the screen's width
pub fn down_arrow_icon_wide() -> Self {
Self::icon(theme::ICON_ARROW_DOWN)
- .with_outline(true)
+ .with_outline()
.with_fixed_width(HALF_SCREEN_BUTTON_WIDTH)
}
/// Up arrow to signal paginating back. Takes half the screen's width
pub fn up_arrow_icon_wide() -> Self {
Self::icon(theme::ICON_ARROW_UP)
- .with_outline(true)
+ .with_outline()
.with_fixed_width(HALF_SCREEN_BUTTON_WIDTH)
}
- /// Possible outline around the button.
- pub fn with_outline(mut self, outline: bool) -> Self {
- self.with_outline = outline;
+ /// Outline around the button.
+ pub fn with_outline(mut self) -> Self {
+ self.decoration = Some(Decoration::Outline);
self
}
@@ -471,10 +388,8 @@ impl ButtonDetails {
}
/// Left and right "arms" around the button.
- /// Automatically disabling the outline.
pub fn with_arms(mut self) -> Self {
- self.with_arms = true;
- self.with_outline = false;
+ self.decoration = Some(Decoration::Arms);
self
}
@@ -501,17 +416,6 @@ impl ButtonDetails {
self.font = font;
self
}
-
- /// Button style that should be applied.
- pub fn style(&self) -> ButtonStyleSheet {
- ButtonStyleSheet::default(
- self.font,
- self.with_outline,
- self.with_arms,
- self.fixed_width,
- self.offset,
- )
- }
}
/// Holding the button details for all three possible buttons.
@@ -774,15 +678,6 @@ impl ButtonLayout {
Self::new(None, Some(ButtonDetails::armed_text(text)), None)
}
- /// HTC on both sides.
- pub fn htc_none_htc(left: TString<'static>, right: TString<'static>) -> Self {
- Self::new(
- Some(ButtonDetails::text(left).with_default_duration()),
- None,
- Some(ButtonDetails::text(right).with_default_duration()),
- )
- }
-
/// Only left arrow.
pub fn arrow_none_none() -> Self {
Self::new(Some(ButtonDetails::left_arrow_icon()), None, None)
diff --git a/core/embed/rust/src/ui/layout_caesar/component/button_controller.rs b/core/embed/rust/src/ui/layout_caesar/component/button_controller.rs
index 394afc60..8547d836 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/button_controller.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/button_controller.rs
@@ -55,19 +55,14 @@ pub enum ButtonControllerMsg {
pub enum ButtonType {
Button(Button),
HoldToConfirm(HoldToConfirm),
- Nothing,
}
impl ButtonType {
- pub fn from_button_details(pos: ButtonPos, btn_details: Option<ButtonDetails>) -> Self {
- if let Some(btn_details) = btn_details {
- if btn_details.duration.is_some() {
- Self::HoldToConfirm(HoldToConfirm::from_button_details(pos, btn_details))
- } else {
- Self::Button(Button::from_button_details(pos, btn_details))
- }
+ pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails) -> Self {
+ if btn_details.duration.is_some() {
+ Self::HoldToConfirm(HoldToConfirm::from_button_details(btn_details))
} else {
- Self::Nothing
+ Self::Button(Button::new(pos, btn_details))
}
}
@@ -79,7 +74,6 @@ impl ButtonType {
Self::HoldToConfirm(htc) => {
htc.place(button_area);
}
- Self::Nothing => {}
}
}
@@ -91,7 +85,6 @@ impl ButtonType {
Self::HoldToConfirm(htc) => {
htc.render(target);
}
- Self::Nothing => {}
}
}
}
@@ -103,7 +96,7 @@ impl ButtonType {
/// `button_type` specified what from those two is used, if anything.
pub struct ButtonContainer {
pos: ButtonPos,
- button_type: ButtonType,
+ button_type: Option<ButtonType>,
/// Holds the timestamp of when the button was pressed.
pressed_since: Option<Instant>,
/// How long the button should be pressed to send `long_press=true` in
@@ -124,7 +117,7 @@ impl ButtonContainer {
let send_long_press = btn_details.as_ref().is_some_and(|btn| btn.send_long_press);
Self {
pos,
- button_type: ButtonType::from_button_details(pos, btn_details),
+ button_type: btn_details.map(|d| ButtonType::from_button_details(pos, d)),
pressed_since: None,
long_press_ms: DEFAULT_LONG_PRESS_MS,
long_pressed_timer: Timer::new(),
@@ -137,22 +130,28 @@ impl ButtonContainer {
/// Passing `None` as `btn_details` will mark the button as inactive.
pub fn set(&mut self, btn_details: Option<ButtonDetails>, button_area: Rect) {
self.send_long_press = btn_details.as_ref().is_some_and(|btn| btn.send_long_press);
- self.button_type = ButtonType::from_button_details(self.pos, btn_details);
- self.button_type.place(button_area);
+ self.button_type = btn_details.map(|d| ButtonType::from_button_details(self.pos, d));
+ if let Some(button_type) = &mut self.button_type {
+ button_type.place(button_area);
+ }
}
/// Placing the possible component.
pub fn place(&mut self, bounds: Rect) {
- self.button_type.place(bounds);
+ if let Some(button_type) = &mut self.button_type {
+ button_type.place(bounds);
+ }
}
pub fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
- self.button_type.render(target);
+ if let Some(button_type) = &self.button_type {
+ button_type.render(target);
+ }
}
/// Setting the visual state of the button - released/pressed.
pub fn set_pressed(&mut self, ctx: &mut EventCtx, is_pressed: bool) {
- if let ButtonType::Button(btn) = &mut self.button_type {
+ if let Some(ButtonType::Button(btn)) = &mut self.button_type {
btn.set_pressed(ctx, is_pressed);
}
}
@@ -162,7 +161,7 @@ impl ButtonContainer {
/// a Triggered message. If it is a hold-to-confirm button, it ends the
/// hold.
pub fn maybe_trigger(&mut self, ctx: &mut EventCtx) -> Option<ButtonControllerMsg> {
- match self.button_type {
+ match self.button_type.as_ref()? {
ButtonType::Button(_) => {
// Finding out whether the button was long-pressed
let long_press = self.pressed_since.is_some_and(|since| {
@@ -173,21 +172,19 @@ impl ButtonContainer {
Some(ButtonControllerMsg::Triggered(self.pos, long_press))
}
ButtonType::HoldToConfirm(_) => {
- self.hold_ended(ctx);
+ self.forward_hold(ctx, ButtonEvent::HoldEnded);
Some(ButtonControllerMsg::ReleasedWithoutLongPress(self.pos))
}
- _ => None,
}
}
/// Find out whether hold-to-confirm was triggered.
pub fn htc_got_triggered(&mut self, ctx: &mut EventCtx, event: Event) -> bool {
- if let ButtonType::HoldToConfirm(htc) = &mut self.button_type {
- if matches!(htc.event(ctx, event), Some(HoldToConfirmMsg::Confirmed)) {
- return true;
- }
+ if let Some(ButtonType::HoldToConfirm(htc)) = &mut self.button_type {
+ matches!(htc.event(ctx, event), Some(HoldToConfirmMsg::Confirmed))
+ } else {
+ false
}
- false
}
/// Saving the timestamp of when the button was pressed.
@@ -211,24 +208,11 @@ impl ButtonContainer {
self.long_pressed_timer.expire(event)
}
- /// Registering the hold event
- pub fn hold_started(&mut self, ctx: &mut EventCtx) {
- if let ButtonType::HoldToConfirm(htc) = &mut self.button_type {
- htc.event(ctx, Event::Button(ButtonEvent::HoldStarted));
- }
- }
-
- /// Ending the hold event by releasing the button held
- pub fn hold_ended(&mut self, ctx: &mut EventCtx) {
- if let ButtonType::HoldToConfirm(htc) = &mut self.button_type {
- htc.event(ctx, Event::Button(ButtonEvent::HoldEnded));
- }
- }
-
- /// Canceling the hold event
- pub fn hold_canceled(&mut self, ctx: &mut EventCtx) {
- if let ButtonType::HoldToConfirm(htc) = &mut self.button_type {
- htc.event(ctx, Event::Button(ButtonEvent::HoldCanceled));
+ /// Forward a hold lifecycle event (started/ended/canceled) to the
+ /// hold-to-confirm component, if any.
+ pub fn forward_hold(&mut self, ctx: &mut EventCtx, event: ButtonEvent) {
+ if let Some(ButtonType::HoldToConfirm(htc)) = &mut self.button_type {
+ htc.event(ctx, Event::Button(event));
}
}
}
@@ -313,9 +297,9 @@ impl ButtonController {
/// Handle middle button hold-to-confirm start.
/// We need to cancel possible holds in both other buttons.
fn middle_hold_started(&mut self, ctx: &mut EventCtx) {
- self.left_btn.hold_ended(ctx);
- self.middle_btn.hold_started(ctx);
- self.right_btn.hold_ended(ctx);
+ self.left_btn.forward_hold(ctx, ButtonEvent::HoldEnded);
+ self.middle_btn.forward_hold(ctx, ButtonEvent::HoldStarted);
+ self.right_btn.forward_hold(ctx, ButtonEvent::HoldEnded);
}
/// Handling the expiration of HTC elements.
@@ -416,13 +400,13 @@ impl Component for ButtonController {
// ▼ *
PhysicalButton::Left => {
self.got_pressed(ctx, ButtonPos::Left);
- self.left_btn.hold_started(ctx);
+ self.left_btn.forward_hold(ctx, ButtonEvent::HoldStarted);
Some(ButtonControllerMsg::Pressed(ButtonPos::Left))
}
// * ▼
PhysicalButton::Right => {
self.got_pressed(ctx, ButtonPos::Right);
- self.right_btn.hold_started(ctx);
+ self.right_btn.forward_hold(ctx, ButtonEvent::HoldStarted);
Some(ButtonControllerMsg::Pressed(ButtonPos::Right))
}
_ => None,
@@ -476,10 +460,10 @@ impl Component for ButtonController {
// cancel the hold and do not register the press.
match which_down {
PhysicalButton::Left => {
- self.left_btn.hold_canceled(ctx);
+ self.left_btn.forward_hold(ctx, ButtonEvent::HoldCanceled);
}
PhysicalButton::Right => {
- self.right_btn.hold_canceled(ctx);
+ self.right_btn.forward_hold(ctx, ButtonEvent::HoldCanceled);
}
_ => {}
}
@@ -493,7 +477,7 @@ impl Component for ButtonController {
ButtonState::BothDown => match button_event {
// ▲ * | * ▲
ButtonEvent::ButtonReleased(b) => {
- self.middle_btn.hold_ended(ctx);
+ self.middle_btn.forward_hold(ctx, ButtonEvent::HoldEnded);
// _ ↓ | ↓ _
if self.handle_middle_button {
(ButtonState::OneReleased(b), None)
@@ -786,10 +770,10 @@ impl Component for AutomaticMover {
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ButtonContainer {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
- if let ButtonType::Button(btn) = &self.button_type {
- btn.trace(t);
- } else if let ButtonType::HoldToConfirm(htc) = &self.button_type {
- htc.trace(t);
+ match &self.button_type {
+ Some(ButtonType::Button(btn)) => btn.trace(t),
+ Some(ButtonType::HoldToConfirm(htc)) => htc.trace(t),
+ None => {}
}
}
}
diff --git a/core/embed/rust/src/ui/layout_caesar/component/hold_to_confirm.rs b/core/embed/rust/src/ui/layout_caesar/component/hold_to_confirm.rs
index 34784756..f341dd8e 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/hold_to_confirm.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/hold_to_confirm.rs
@@ -1,5 +1,4 @@
use crate::{
- strutil::TString,
time::{Duration, Instant},
ui::{
component::{Component, Event, EventCtx},
@@ -11,87 +10,43 @@ use crate::{
use super::{
loader::{Loader, DEFAULT_DURATION_MS},
- theme, ButtonContent, ButtonDetails, ButtonPos, LoaderMsg, LoaderStyleSheet,
+ theme, ButtonContent, ButtonDetails, LoaderMsg, LoaderStyleSheet,
};
pub enum HoldToConfirmMsg {
Confirmed,
- FailedToConfirm,
}
pub struct HoldToConfirm {
- pos: ButtonPos,
loader: Loader,
text_width: i16,
}
impl HoldToConfirm {
- pub fn text<T: Into<TString<'static>>>(
- pos: ButtonPos,
- text: T,
- styles: LoaderStyleSheet,
- duration: Duration,
- ) -> Self {
- let text = text.into();
- let text_width = text.map(|t| styles.normal.font.visible_text_width(t));
- Self {
- pos,
- loader: Loader::text(text, styles).with_growing_duration(duration),
- text_width,
- }
- }
-
- pub fn from_button_details(pos: ButtonPos, btn_details: ButtonDetails) -> Self {
+ pub fn from_button_details(btn_details: ButtonDetails) -> Self {
let duration = btn_details
.duration
.unwrap_or_else(|| Duration::from_millis(DEFAULT_DURATION_MS));
match btn_details.content {
ButtonContent::Text(text) => {
- Self::text(pos, text, LoaderStyleSheet::default_loader(), duration)
+ let styles = LoaderStyleSheet::default_loader();
+ let text_width = text.map(|t| styles.normal.font.visible_text_width(t));
+ Self {
+ loader: Loader::text(text, styles).with_growing_duration(duration),
+ text_width,
+ }
}
ButtonContent::Icon(_) => fatal_error!("Icon is not supported"),
}
}
-
- /// Updating the text of the component and re-placing it.
- pub fn set_text<T: Into<TString<'static>>>(&mut self, text: T, button_area: Rect) {
- let text = text.into();
- self.text_width = self.loader.get_text_width(&text);
- self.loader.set_text(text);
- self.place(button_area);
- }
-
- pub fn reset(&mut self) {
- self.loader.reset();
- }
-
- pub fn set_duration(&mut self, duration: Duration) {
- self.loader.set_duration(duration);
- }
-
- pub fn get_duration(&self) -> Duration {
- self.loader.get_duration()
- }
-
- pub fn get_text(&self) -> TString<'static> {
- self.loader.get_text()
- }
-
- fn placement(&mut self, area: Rect, pos: ButtonPos) -> Rect {
- let button_width = self.text_width + 2 * theme::BUTTON_OUTLINE;
- match pos {
- ButtonPos::Left => area.split_left(button_width).0,
- ButtonPos::Right => area.split_right(button_width).1,
- ButtonPos::Middle => area.split_center(button_width).1,
- }
- }
}
impl Component for HoldToConfirm {
type Msg = HoldToConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect {
- let loader_area = self.placement(bounds, self.pos);
+ let button_width = self.text_width + 2 * theme::BUTTON_OUTLINE;
+ let loader_area = bounds.split_right(button_width).1;
self.loader.place(loader_area)
}
@@ -114,9 +69,6 @@ impl Component for HoldToConfirm {
if let Some(LoaderMsg::GrownCompletely) = msg {
return Some(HoldToConfirmMsg::Confirmed);
}
- if let Some(LoaderMsg::ShrunkCompletely) = msg {
- return Some(HoldToConfirmMsg::FailedToConfirm);
- }
None
}
diff --git a/core/embed/rust/src/ui/layout_caesar/component/mod.rs b/core/embed/rust/src/ui/layout_caesar/component/mod.rs
index b4b3b9e8..7e5c0c04 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/mod.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/mod.rs
@@ -11,7 +11,6 @@ mod welcome_screen;
use super::{common_messages, constant, theme};
pub use button::{
Button, ButtonAction, ButtonActions, ButtonContent, ButtonDetails, ButtonLayout, ButtonPos,
- ButtonStyle, ButtonStyleSheet,
};
pub use button_controller::{AutomaticMover, ButtonController, ButtonControllerMsg};
pub use common_messages::CancelConfirmMsg;
diff --git a/core/embed/rust/src/ui/layout_caesar/component/page.rs b/core/embed/rust/src/ui/layout_caesar/component/page.rs
index 643731c9..fae06668 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/page.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/page.rs
@@ -163,11 +163,11 @@ where
// Clicked BACK. Scroll up.
self.prev_page();
self.change_page(ctx);
- } else if self.cancel_btn_details.is_some() {
- // Clicked CANCEL. Send result.
- return Some(PageMsg::Cancelled);
} else {
- return None;
+ // First page — left slot can only be the cancel button
+ // (otherwise the layout would not have rendered it and
+ // no Triggered would arrive here).
+ return Some(PageMsg::Cancelled);
}
}
ButtonPos::Middle => {
Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.