From d73f0dc79d8cdac4ced119fc2691341bb91ec797 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Wed, 21 May 2025 11:48:06 +0200 Subject: [PATCH] perf(core/eckhart): constructing MenuItems - self-referential builder pattern is easier on the stack memory usage than consuming builder pattern - store ButtonStyleSheet as a static ref --- .../firmware/device_menu_screen.rs | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs index 8b62b1bdd7..4b6f527adc 100644 --- a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs @@ -70,26 +70,30 @@ pub enum DeviceMenuMsg { struct MenuItem { text: TString<'static>, subtext: Option<(TString<'static>, Option)>, - stylesheet: ButtonStyleSheet, + stylesheet: &'static ButtonStyleSheet, action: Option, } +const MENU_ITEM_TITLE_STYLE_SHEET: &ButtonStyleSheet = &theme::menu_item_title(); impl MenuItem { pub fn new(text: TString<'static>, action: Option) -> Self { Self { text, subtext: None, - stylesheet: theme::menu_item_title(), + stylesheet: MENU_ITEM_TITLE_STYLE_SHEET, action, } } - pub fn with_subtext(mut self, subtext: Option<(TString<'static>, Option)>) -> Self { + pub fn with_subtext( + &mut self, + subtext: Option<(TString<'static>, Option)>, + ) -> &mut Self { self.subtext = subtext; self } - pub fn with_stylesheet(mut self, stylesheet: ButtonStyleSheet) -> Self { + pub fn with_stylesheet(&mut self, stylesheet: &'static ButtonStyleSheet) -> &mut Self { self.stylesheet = stylesheet; self } @@ -221,12 +225,13 @@ impl<'a> DeviceMenuScreen<'a> { ) -> usize { let mut items: Vec = Vec::new(); for (device, idx) in paired_devices.iter().zip(paired_device_indices) { - unwrap!(items.push( - MenuItem::new(*device, Some(Action::GoTo(idx))).with_subtext(Some(( - "Connected".into(), - Some(Button::SUBTEXT_STYLE_GREEN) - ))) // TODO: this should be a boolean feature of the device - )); + let mut item_device = MenuItem::new(*device, Some(Action::GoTo(idx))); + // TODO: this should be a boolean feature of the device + item_device.with_subtext(Some(( + "Connected".into(), + Some(Button::SUBTEXT_STYLE_GREEN), + ))); + unwrap!(items.push(item_device)); } let submenu_index = self.add_submenu(Submenu::new("Manage paired devices".into(), items)); @@ -235,16 +240,15 @@ impl<'a> DeviceMenuScreen<'a> { fn add_pair_and_connect_menu(&mut self, manage_devices_index: usize) -> usize { let mut items: Vec = Vec::new(); - unwrap!(items.push( - MenuItem::new( - "Manage paired devices".into(), - Some(Action::GoTo(manage_devices_index)), - ) - .with_subtext(Some(( - "1 device connected".into(), - Some(Button::SUBTEXT_STYLE_GREEN) - ))) - )); + let mut manage_paired_item = MenuItem::new( + "Manage paired devices".into(), + Some(Action::GoTo(manage_devices_index)), + ); + manage_paired_item.with_subtext(Some(( + "1 device connected".into(), + Some(Button::SUBTEXT_STYLE_GREEN), + ))); + unwrap!(items.push(manage_paired_item)); unwrap!(items.push(MenuItem::new( "Pair new device".into(), Some(Action::Return(DeviceMenuMsg::DevicePair)), @@ -291,22 +295,21 @@ impl<'a> DeviceMenuScreen<'a> { auto_lock_delay: TString<'static>, ) -> usize { let mut items: Vec = Vec::new(); - unwrap!( - items.push(MenuItem::new("Name".into(), None).with_subtext(Some((device_name, None)))) - ); + let mut item_device_name = MenuItem::new("Name".into(), None); + item_device_name.with_subtext(Some((device_name, None))); + unwrap!(items.push(item_device_name)); unwrap!(items.push(MenuItem::new( "Screen brightness".into(), Some(Action::Return(DeviceMenuMsg::ScreenBrightness)), ))); if has_pin() { - unwrap!(items.push( - MenuItem::new( - "Auto-lock delay".into(), - Some(Action::Return(DeviceMenuMsg::AutoLockDelay)), - ) - .with_subtext(Some((auto_lock_delay, None))) - )); + let mut autolock_delay_item = MenuItem::new( + "Auto-lock delay".into(), + Some(Action::Return(DeviceMenuMsg::AutoLockDelay)), + ); + autolock_delay_item.with_subtext(Some((auto_lock_delay, None))); + unwrap!(items.push(autolock_delay_item)); } unwrap!(items.push(MenuItem::new( @@ -326,25 +329,23 @@ impl<'a> DeviceMenuScreen<'a> { ) -> usize { let mut items: Vec = Vec::new(); if failed_backup { - unwrap!(items.push( - MenuItem::new( - "Backup failed".into(), - Some(Action::Return(DeviceMenuMsg::BackupFailed)), - ) - .with_subtext(Some(("Review".into(), None))) - .with_stylesheet(theme::menu_item_title_red()), - )); + let mut item_backup_failed = MenuItem::new( + "Backup failed".into(), + Some(Action::Return(DeviceMenuMsg::BackupFailed)), + ); + item_backup_failed.with_subtext(Some(("Review".into(), None))); + item_backup_failed.with_stylesheet(MENU_ITEM_TITLE_STYLE_SHEET); + unwrap!(items.push(item_backup_failed)); } - unwrap!(items.push( - MenuItem::new( - "Pair & connect".into(), - Some(Action::GoTo(pair_and_connect_index)), - ) - .with_subtext(Some(( - "1 device connected".into(), - Some(Button::SUBTEXT_STYLE_GREEN) - ))) - )); + let mut item_pair_and_connect = MenuItem::new( + "Pair & connect".into(), + Some(Action::GoTo(pair_and_connect_index)), + ); + item_pair_and_connect.with_subtext(Some(( + "1 device connected".into(), + Some(Button::SUBTEXT_STYLE_GREEN), + ))); + unwrap!(items.push(item_pair_and_connect)); unwrap!(items.push(MenuItem::new( "Settings".into(), Some(Action::GoTo(settings_index)), @@ -379,12 +380,12 @@ impl<'a> DeviceMenuScreen<'a> { let button = if let Some((subtext, subtext_style)) = item.subtext { Button::new_menu_item_with_subtext( item.text, - item.stylesheet, + *item.stylesheet, subtext, subtext_style, ) } else { - Button::new_menu_item(item.text, item.stylesheet) + Button::new_menu_item(item.text, *item.stylesheet) }; menu.item(button); }