diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs index f6426c3f6d..36d68f53ec 100644 --- a/core/embed/rust/src/ui/api/firmware_micropython.rs +++ b/core/embed/rust/src/ui/api/firmware_micropython.rs @@ -807,7 +807,7 @@ extern "C" fn new_show_device_menu(n_args: usize, args: *const Obj, kwargs: *mut let failed_backup: bool = kwargs.get(Qstr::MP_QSTR_failed_backup)?.try_into()?; let battery_percentage: usize = kwargs.get_or(Qstr::MP_QSTR_battery_percentage, 0)?; let paired_devices: Obj = kwargs.get(Qstr::MP_QSTR_paired_devices)?; - let paired_devices: Vec = util::iter_into_vec(paired_devices)?; + let paired_devices: Vec = util::iter_into_vec(paired_devices)?; let layout = ModelUI::show_device_menu(failed_backup, battery_percentage, paired_devices)?; let layout_obj = LayoutObj::new_root(layout)?; Ok(layout_obj.into()) diff --git a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs index d6253dba1c..3d1e35640e 100644 --- a/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_bolt/ui_firmware.rs @@ -870,7 +870,7 @@ impl FirmwareUI for UIBolt { Ok(layout) } - fn show_device_menu(_failed_backup: bool, _battery_percentage: usize, _paired_devices: Vec, 10>) -> Result { + fn show_device_menu(_failed_backup: bool, _battery_percentage: usize, _paired_devices: Vec, 1>) -> Result { Err::, Error>(Error::ValueError(c"show_device_menu not supported")) } diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs index 41bbb78f37..1474747dee 100644 --- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs @@ -1036,7 +1036,7 @@ impl FirmwareUI for UICaesar { Ok(layout) } - fn show_device_menu(_failed_backup: bool, _battery_percentage: usize, _paired_devices: Vec, 10>) -> Result { + fn show_device_menu(_failed_backup: bool, _battery_percentage: usize, _paired_devices: Vec, 1>) -> Result { Err::, Error>(Error::ValueError(c"show_device_menu not supported")) } diff --git a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs index 4c19f0c693..1c57667bcb 100644 --- a/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_delizia/ui_firmware.rs @@ -888,7 +888,7 @@ impl FirmwareUI for UIDelizia { Ok(layout) } - fn show_device_menu(_failed_backup: bool, _battery_percentage: usize, _paired_devices: Vec, 10>) -> Result { + fn show_device_menu(_failed_backup: bool, _battery_percentage: usize, _paired_devices: Vec, 1>) -> Result { Err::, Error>(Error::ValueError(c"show_device_menu not supported")) } diff --git a/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs b/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs index c8dbc0741b..5c0f44cbd1 100644 --- a/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs +++ b/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs @@ -139,9 +139,9 @@ impl ComponentMsgObj for SetBrightnessScreen { impl<'a> ComponentMsgObj for DeviceMenuScreen<'a> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { match msg { - DeviceMenuMsg::NotImplemented => "NotImplemented".try_into(), DeviceMenuMsg::BackupFailed => "BackupFailed".try_into(), - DeviceMenuMsg::PairNewDevice => "PairNewDevice".try_into(), + DeviceMenuMsg::DevicePair => "DevicePair".try_into(), + DeviceMenuMsg::DeviceDisconnect(_) => "DeviceDisconnect".try_into(), DeviceMenuMsg::CheckBackup => "CheckBackup".try_into(), DeviceMenuMsg::WipeDevice => "WipeDevice".try_into(), DeviceMenuMsg::ScreenBrightness => "ScreenBrightness".try_into(), 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 ed70ed7306..1a6f96f966 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 @@ -8,7 +8,7 @@ use crate::{ }, geometry::Rect, layout_eckhart::{ - component::Button, + component::{Button, ButtonStyleSheet}, constant::SCREEN, firmware::{ Header, HeaderMsg, TextScreen, TextScreenMsg, VerticalMenu, VerticalMenuScreen, @@ -23,38 +23,29 @@ use super::theme; use heapless::Vec; const MAX_DEPTH: usize = 5; -const MAX_SUBMENUS: usize = 10; +const MAX_SUBSCREENS: usize = 10; -#[derive(Clone, Debug)] +const DISCONNECT_DEVICE_MENU_INDEX: usize = 1; + +#[derive(Clone)] enum Action { - // Go to another registered child screen + // Go to another registered subscreen GoTo(usize), // Return a DeviceMenuMsg to the caller Return(DeviceMenuMsg), } -struct SubmenuScreen { - pub screen: VerticalMenuScreen, - - // actions for the menu items in the VerticalMenuScreen, in order - pub actions: Vec, MENU_MAX_ITEMS>, -} - -enum Subscreen { - Submenu(SubmenuScreen), - AboutScreen, -} - -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone)] pub enum DeviceMenuMsg { - NotImplemented, - // Root menu BackupFailed, // "Pair & Connect" - PairNewDevice, + DevicePair, // pair a new device + DeviceDisconnect( + usize, /* which device to disconnect, index in the list of devices */ + ), // Security menu CheckBackup, @@ -63,20 +54,99 @@ pub enum DeviceMenuMsg { // Device menu ScreenBrightness, + // nothing selected Close, } +struct MenuItem { + text: TString<'static>, + subtext: Option>, + stylesheet: ButtonStyleSheet, + green_subtext: bool, + action: Option, +} + +impl MenuItem { + pub fn new(text: TString<'static>, action: Option) -> Self { + Self { + text, + subtext: None, + stylesheet: theme::menu_item_title(), + green_subtext: false, + action, + } + } + + pub fn with_subtext(mut self, subtext: Option>) -> Self { + self.subtext = subtext; + self + } + + pub fn with_stylesheet(mut self, stylesheet: ButtonStyleSheet) -> Self { + self.stylesheet = stylesheet; + self + } + + pub fn with_green_subtext(mut self) -> Self { + self.green_subtext = true; + self + } +} + +struct SubmenuScreen { + header_text: TString<'static>, + show_battery: bool, + items: Vec, +} + +impl SubmenuScreen { + pub fn new(header_text: TString<'static>, items: Vec) -> Self { + Self { + header_text, + show_battery: false, + items, + } + } + + pub fn with_battery(mut self) -> Self { + self.show_battery = true; + self + } +} + +// Each subscreen of the DeviceMenuScreen is one of these +enum Subscreen { + // A menu, with associated items and actions + Submenu(SubmenuScreen), + + // A screen allowing the user to to disconnect a device + DeviceScreen( + TString<'static>, /* device name */ + usize, /* index in the list of devices */ + ), + + // The about screen + AboutScreen, +} + pub struct DeviceMenuScreen<'a> { bounds: Rect, - about_screen: TextScreen; 2]>>, + battery_percentage: usize, - // all the subscreens in the DeviceMenuScreen - // which can be either VerticalMenuScreens with associated Actions - // or some predefined TextScreens, such as "About" - subscreens: Vec, + // These correspond to the currently active subscreen, + // which is one of the possible kinds of subscreens + // as defined by `enum Subscreen` + // The active one will be Some(...) and the other two will be None. + // This way we only need to keep one screen at any time in memory. + menu_screen: Option, + paired_device_screen: Option, + about_screen: Option; 2]>>>, - // the index of the current subscreen in the list of subscreens + // Information needed to construct any subscreen on demand + subscreens: Vec, + + // index of the current subscreen in the list of subscreens active_subscreen: usize, // stack of parents that led to the current subscreen @@ -87,19 +157,18 @@ impl<'a> DeviceMenuScreen<'a> { pub fn new( failed_backup: bool, battery_percentage: usize, - paired_devices: Vec, 10>, + // NB: we currently only support one device at a time. + // if we ever increase this size, we will need a way to return the correct + // device index on Disconnect back to uPy + // (see component_msg_obj.rs, which currently just returns "DeviceDisconnect" with no index!) + paired_devices: Vec, 1>, ) -> Self { - let about_content = Paragraphs::new([ - Paragraph::new(&theme::firmware::TEXT_REGULAR, "Firmware version"), - Paragraph::new(&theme::firmware::TEXT_REGULAR, "2.3.1"), // TODO - ]); - - let about_screen = TextScreen::new(about_content) - .with_header(Header::new("About".into()).with_close_button()); - let mut screen = Self { bounds: Rect::zero(), - about_screen, + battery_percentage, + menu_screen: None, + paired_device_screen: None, + about_screen: None, active_subscreen: 0, subscreens: Vec::new(), parent_subscreens: Vec::new(), @@ -110,220 +179,147 @@ impl<'a> DeviceMenuScreen<'a> { let device = screen.add_device_menu("My device".into(), about); // TODO: device name let settings = screen.add_settings_menu(security, device); - let mut paired_device_indices: Vec = Vec::new(); - for device in &paired_devices { - unwrap!(paired_device_indices.push(screen.add_paired_device_menu(*device))); + let mut paired_device_indices: Vec = Vec::new(); + for (i, device) in paired_devices.iter().enumerate() { + unwrap!(paired_device_indices + .push(screen.add_subscreen(Subscreen::DeviceScreen(*device, i)))); } let devices = screen.add_paired_devices_menu(paired_devices, paired_device_indices); let pair_and_connect = screen.add_pair_and_connect_menu(devices); - let root = screen.add_root_menu( - failed_backup, - battery_percentage, - pair_and_connect, - settings, - ); + let root = screen.add_root_menu(failed_backup, pair_and_connect, settings); screen.set_active_subscreen(root); screen } - fn add_paired_device_menu(&mut self, device: TString<'static>) -> usize { - let mut actions: Vec, MENU_MAX_ITEMS> = Vec::new(); - let mut menu = VerticalMenu::empty().with_separators(); - menu = menu.item(Button::new_menu_item( - device.into(), - None, - theme::menu_item_title(), - )); - unwrap!(actions.push(None)); - - let screen = VerticalMenuScreen::new(menu).with_header( - Header::new("Manage".into()) - .with_left_button(Button::with_icon(theme::ICON_CHEVRON_LEFT), HeaderMsg::Back), - ); - - self.add_subscreen(Subscreen::Submenu(SubmenuScreen { screen, actions })) - } - fn add_paired_devices_menu( &mut self, - paired_devices: Vec, 10>, - paired_device_indices: Vec, + paired_devices: Vec, 1>, + paired_device_indices: Vec, ) -> usize { - let mut actions: Vec, MENU_MAX_ITEMS> = Vec::new(); - let mut menu = VerticalMenu::empty().with_separators(); + let mut items: Vec = Vec::new(); for (device, idx) in paired_devices.iter().zip(paired_device_indices) { - menu = menu.item(Button::new_menu_item( - (*device).into(), - None, - theme::menu_item_title(), + unwrap!(items.push( + MenuItem::new((*device).into(), Some(Action::GoTo(idx))) + .with_subtext(Some("Connected".into())) // TODO: this should be a boolean feature of the device + .with_green_subtext() )); - unwrap!(actions.push(Some(Action::GoTo(idx)))); } - let screen = VerticalMenuScreen::new(menu).with_header( - Header::new("Manage paired devices".into()) - .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled) - .with_left_button(Button::with_icon(theme::ICON_CHEVRON_LEFT), HeaderMsg::Back), - ); - - self.add_subscreen(Subscreen::Submenu(SubmenuScreen { screen, actions })) + self.add_subscreen(Subscreen::Submenu(SubmenuScreen::new( + "Manage paired devices".into(), + items, + ))) } fn add_pair_and_connect_menu(&mut self, manage_devices_index: usize) -> usize { - let mut actions: Vec, MENU_MAX_ITEMS> = Vec::new(); - let mut menu = VerticalMenu::empty().with_separators(); - menu = menu.item( - Button::new_menu_item( + let mut items: Vec = Vec::new(); + unwrap!(items.push( + MenuItem::new( "Manage paired devices".into(), - Some("1 device connected".into()), // TODO - theme::menu_item_title(), + Some(Action::GoTo(manage_devices_index)), ) - .subtext_green(), - ); - unwrap!(actions.push(Some(Action::GoTo(manage_devices_index)))); - menu = menu.item(Button::new_menu_item( - "Pair new device".into(), - None, - theme::menu_item_title(), + .with_subtext(Some("1 device connected".into())) + .with_green_subtext(), )); - unwrap!(actions.push(Some(Action::Return(DeviceMenuMsg::PairNewDevice)))); + unwrap!(items.push(MenuItem::new( + "Pair new device".into(), + Some(Action::Return(DeviceMenuMsg::DevicePair)), + ))); - let screen = VerticalMenuScreen::new(menu).with_header( - Header::new("Pair & Connect".into()) - .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled) - .with_left_button(Button::with_icon(theme::ICON_CHEVRON_LEFT), HeaderMsg::Back), - ); - - self.add_subscreen(Subscreen::Submenu(SubmenuScreen { screen, actions })) + self.add_subscreen(Subscreen::Submenu(SubmenuScreen::new( + "Pair & connect".into(), + items, + ))) } fn add_settings_menu(&mut self, security_index: usize, device_index: usize) -> usize { - let mut actions: Vec, MENU_MAX_ITEMS> = Vec::new(); - let mut menu = VerticalMenu::empty().with_separators(); - menu = menu.item(Button::new_menu_item( + let mut items: Vec = Vec::new(); + unwrap!(items.push(MenuItem::new( "Security".into(), - None, - theme::menu_item_title(), - )); - unwrap!(actions.push(Some(Action::GoTo(security_index)))); - menu = menu.item(Button::new_menu_item( + Some(Action::GoTo(security_index)) + ))); + unwrap!(items.push(MenuItem::new( "Device".into(), - None, - theme::menu_item_title(), - )); - unwrap!(actions.push(Some(Action::GoTo(device_index)))); + Some(Action::GoTo(device_index)) + ))); - let screen = VerticalMenuScreen::new(menu).with_header( - Header::new("Settings".into()) - .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled) - .with_left_button(Button::with_icon(theme::ICON_CHEVRON_LEFT), HeaderMsg::Back), - ); - - self.add_subscreen(Subscreen::Submenu(SubmenuScreen { screen, actions })) + self.add_subscreen(Subscreen::Submenu(SubmenuScreen::new( + "Settings".into(), + items, + ))) } fn add_security_menu(&mut self) -> usize { - let mut actions: Vec, MENU_MAX_ITEMS> = Vec::new(); - let mut menu = VerticalMenu::empty().with_separators(); - menu = menu.item(Button::new_menu_item( + let mut items: Vec = Vec::new(); + unwrap!(items.push(MenuItem::new( "Check backup".into(), - None, - theme::menu_item_title(), - )); - unwrap!(actions.push(Some(Action::Return(DeviceMenuMsg::CheckBackup)))); - menu = menu.item(Button::new_menu_item( + Some(Action::Return(DeviceMenuMsg::CheckBackup)), + ))); + unwrap!(items.push(MenuItem::new( "Wipe device".into(), - None, - theme::menu_item_title(), - )); - unwrap!(actions.push(Some(Action::Return(DeviceMenuMsg::WipeDevice)))); + Some(Action::Return(DeviceMenuMsg::WipeDevice)) + ))); - let screen = VerticalMenuScreen::new(menu).with_header( - Header::new("Security".into()) - .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled) - .with_left_button(Button::with_icon(theme::ICON_CHEVRON_LEFT), HeaderMsg::Back), - ); - - self.add_subscreen(Subscreen::Submenu(SubmenuScreen { screen, actions })) + self.add_subscreen(Subscreen::Submenu(SubmenuScreen::new( + "Security".into(), + items, + ))) } fn add_device_menu(&mut self, device_name: TString<'static>, about_index: usize) -> usize { - let mut actions: Vec, MENU_MAX_ITEMS> = Vec::new(); - let mut menu = VerticalMenu::empty().with_separators(); - menu = menu.item(Button::new_menu_item( - "Name".into(), - Some(device_name), - theme::menu_item_title(), - )); - unwrap!(actions.push(None)); - menu = menu.item(Button::new_menu_item( + let mut items: Vec = Vec::new(); + unwrap!(items.push(MenuItem::new("Name".into(), None).with_subtext(Some(device_name)))); + unwrap!(items.push(MenuItem::new( "Screen brightness".into(), - None, - theme::menu_item_title(), - )); - unwrap!(actions.push(Some(Action::Return(DeviceMenuMsg::ScreenBrightness)))); - menu = menu.item(Button::new_menu_item( + Some(Action::Return(DeviceMenuMsg::ScreenBrightness)), + ))); + unwrap!(items.push(MenuItem::new( "About".into(), - None, - theme::menu_item_title(), - )); - unwrap!(actions.push(Some(Action::GoTo(about_index)))); + Some(Action::GoTo(about_index)) + ))); - let screen = VerticalMenuScreen::new(menu).with_header( - Header::new("Security".into()) - .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled) - .with_left_button(Button::with_icon(theme::ICON_CHEVRON_LEFT), HeaderMsg::Back), - ); - - self.add_subscreen(Subscreen::Submenu(SubmenuScreen { screen, actions })) + self.add_subscreen(Subscreen::Submenu(SubmenuScreen::new( + "Device".into(), + items, + ))) } fn add_root_menu( &mut self, failed_backup: bool, - battery_percentage: usize, pair_and_connect_index: usize, settings_index: usize, ) -> usize { - let mut actions: Vec, MENU_MAX_ITEMS> = Vec::new(); - let mut menu = VerticalMenu::empty().with_separators(); + let mut items: Vec = Vec::new(); if failed_backup { - menu = menu.item(Button::new_menu_item( - "Backup failed".into(), - Some("Review".into()), - theme::menu_item_title_red(), + unwrap!(items.push( + MenuItem::new( + "Backup failed".into(), + Some(Action::Return(DeviceMenuMsg::BackupFailed)), + ) + .with_subtext(Some("Review".into())) + .with_stylesheet(theme::menu_item_title_red()), )); - unwrap!(actions.push(Some(Action::Return(DeviceMenuMsg::BackupFailed)))); } - - menu = menu.item( - Button::new_menu_item( + unwrap!(items.push( + MenuItem::new( "Pair & connect".into(), - Some("1 device connected".into()), // TODO - theme::menu_item_title(), + Some(Action::GoTo(pair_and_connect_index)), ) - .subtext_green(), - ); - unwrap!(actions.push(Some(Action::GoTo(pair_and_connect_index)))); - - menu = menu.item(Button::new_menu_item( - "Settings".into(), - None, - theme::menu_item_title(), + .with_subtext(Some("1 device connected".into())) + .with_green_subtext(), )); - unwrap!(actions.push(Some(Action::GoTo(settings_index)))); - - let screen = VerticalMenuScreen::new(menu).with_header( - Header::new("".into()) - .with_battery(battery_percentage) - .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled), - ); - - self.add_subscreen(Subscreen::Submenu(SubmenuScreen { screen, actions })) + unwrap!(items.push(MenuItem::new( + "Settings".into(), + Some(Action::GoTo(settings_index)), + ))); + self.add_subscreen(Subscreen::Submenu( + SubmenuScreen::new("".into(), items).with_battery(), + )) } fn add_subscreen(&mut self, screen: Subscreen) -> usize { @@ -334,16 +330,87 @@ impl<'a> DeviceMenuScreen<'a> { fn set_active_subscreen(&mut self, idx: usize) { assert!(idx < self.subscreens.len()); self.active_subscreen = idx; + self.build_active_subscreen(); + } + + fn build_active_subscreen(&mut self) { + match self.subscreens[self.active_subscreen] { + Subscreen::Submenu(ref mut submenu) => { + self.paired_device_screen = None; + self.about_screen = None; + let mut menu = VerticalMenu::empty().with_separators(); + for item in &submenu.items { + let mut button = + Button::new_menu_item(item.text, item.subtext, item.stylesheet); + if item.green_subtext { + button = button.subtext_green(); + } + menu = menu.item(button); + } + let mut header = Header::new(submenu.header_text) + .with_right_button(Button::with_icon(theme::ICON_CROSS), HeaderMsg::Cancelled); + if submenu.show_battery { + header = header.with_battery(self.battery_percentage); + } else { + header = header.with_left_button( + Button::with_icon(theme::ICON_CHEVRON_LEFT), + HeaderMsg::Back, + ); + } + self.menu_screen = Some(VerticalMenuScreen::new(menu).with_header(header)); + } + Subscreen::DeviceScreen(device, _) => { + self.menu_screen = None; + self.about_screen = None; + let mut menu = VerticalMenu::empty().with_separators(); + menu = menu.item(Button::new_menu_item( + device, + None, + theme::menu_item_title(), + )); + menu = menu.item(Button::new_menu_item( + "Disconnect".into(), + None, + theme::menu_item_title_red(), + )); + self.paired_device_screen = Some( + VerticalMenuScreen::new(menu).with_header( + Header::new("Manage".into()) + .with_right_button( + Button::with_icon(theme::ICON_CROSS), + HeaderMsg::Cancelled, + ) + .with_left_button( + Button::with_icon(theme::ICON_CHEVRON_LEFT), + HeaderMsg::Back, + ), + ), + ); + } + Subscreen::AboutScreen => { + self.menu_screen = None; + self.paired_device_screen = None; + let about_content = Paragraphs::new([ + Paragraph::new(&theme::firmware::TEXT_REGULAR, "Firmware version"), + Paragraph::new(&theme::firmware::TEXT_REGULAR, "2.3.1"), // TODO + ]); + + self.about_screen = Some( + TextScreen::new(about_content) + .with_header(Header::new("About".into()).with_close_button()), + ); + } + } } fn handle_submenu(&mut self, ctx: &mut EventCtx, idx: usize) -> Option { match self.subscreens[self.active_subscreen] { Subscreen::Submenu(ref mut menu_screen) => { - match menu_screen.actions[idx] { + match menu_screen.items[idx].action { Some(Action::GoTo(menu)) => { - menu_screen.screen.update_menu(ctx); + self.menu_screen.as_mut().unwrap().update_menu(ctx); unwrap!(self.parent_subscreens.push(self.active_subscreen)); - self.active_subscreen = menu; + self.set_active_subscreen(menu); self.place(self.bounds); } Some(Action::Return(msg)) => return Some(msg), @@ -360,7 +427,7 @@ impl<'a> DeviceMenuScreen<'a> { fn go_back(&mut self) -> Option { if let Some(parent) = self.parent_subscreens.pop() { - self.active_subscreen = parent; + self.set_active_subscreen(parent); self.place(self.bounds); None } else { @@ -380,7 +447,8 @@ impl<'a> Component for DeviceMenuScreen<'a> { self.bounds = bounds; match self.subscreens[self.active_subscreen] { - Subscreen::Submenu(ref mut menu_screen) => menu_screen.screen.place(bounds), + Subscreen::Submenu(..) => self.menu_screen.place(bounds), + Subscreen::DeviceScreen(..) => self.paired_device_screen.place(bounds), Subscreen::AboutScreen => self.about_screen.place(bounds), }; @@ -393,7 +461,7 @@ impl<'a> Component for DeviceMenuScreen<'a> { // Handle the event for the active menu match self.subscreens[self.active_subscreen] { - Subscreen::Submenu(ref mut menu_screen) => match menu_screen.screen.event(ctx, event) { + Subscreen::Submenu(..) => match self.menu_screen.event(ctx, event) { Some(VerticalMenuScreenMsg::Selected(index)) => { return self.handle_submenu(ctx, index); } @@ -405,6 +473,20 @@ impl<'a> Component for DeviceMenuScreen<'a> { } _ => {} }, + Subscreen::DeviceScreen(_, i) => match self.paired_device_screen.event(ctx, event) { + Some(VerticalMenuScreenMsg::Selected(index)) => { + if index == DISCONNECT_DEVICE_MENU_INDEX { + return Some(DeviceMenuMsg::DeviceDisconnect(i)); + } + } + Some(VerticalMenuScreenMsg::Back) => { + return self.go_back(); + } + Some(VerticalMenuScreenMsg::Close) => { + return Some(DeviceMenuMsg::Close); + } + _ => {} + }, Subscreen::AboutScreen => match self.about_screen.event(ctx, event) { Some(TextScreenMsg::Cancelled) => { return self.go_back(); @@ -418,7 +500,8 @@ impl<'a> Component for DeviceMenuScreen<'a> { fn render<'s>(&'s self, target: &mut impl Renderer<'s>) { match &self.subscreens[self.active_subscreen] { - Subscreen::Submenu(ref menu_screen) => menu_screen.screen.render(target), + Subscreen::Submenu(..) => self.menu_screen.render(target), + Subscreen::DeviceScreen(..) => self.paired_device_screen.render(target), Subscreen::AboutScreen => self.about_screen.render(target), } } diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs index 518e581821..c8261fe637 100644 --- a/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs +++ b/core/embed/rust/src/ui/layout_eckhart/ui_firmware.rs @@ -783,7 +783,7 @@ impl FirmwareUI for UIEckhart { Ok(layout) } - fn show_device_menu(failed_backup: bool, battery_percentage: usize, paired_devices: Vec, 10>) -> Result { + fn show_device_menu(failed_backup: bool, battery_percentage: usize, paired_devices: Vec, 1>) -> Result { let layout = RootComponent::new(DeviceMenuScreen::new(failed_backup, battery_percentage, paired_devices)); Ok(layout) } diff --git a/core/embed/rust/src/ui/ui_firmware.rs b/core/embed/rust/src/ui/ui_firmware.rs index 8c8b87d052..bafe476d01 100644 --- a/core/embed/rust/src/ui/ui_firmware.rs +++ b/core/embed/rust/src/ui/ui_firmware.rs @@ -304,7 +304,7 @@ pub trait FirmwareUI { fn show_device_menu( failed_backup: bool, battery_percentage: usize, - paired_devices: Vec, 10>, + paired_devices: Vec, 1>, ) -> Result; fn show_info(