Skip to content

Update VerticalMenu swiping behavior for Eckhart #4908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -416,10 +416,13 @@ impl<'a> DeviceMenuScreen<'a> {
Subscreen::Submenu(ref mut submenu_index) => {
match self.submenus[*submenu_index].items[idx].action {
Some(Action::GoTo(menu)) => {
self.menu_screen.as_mut().unwrap().update_menu(ctx);
unwrap!(self.parent_subscreens.push(self.active_subscreen));
self.set_active_subscreen(menu);
self.place(self.bounds);
if let Some(screen) = self.menu_screen.as_mut() {
screen.initialize_screen(ctx);
}
return None;
}
Some(Action::Return(msg)) => return Some(msg),
None => {}
Expand All @@ -433,10 +436,13 @@ impl<'a> DeviceMenuScreen<'a> {
None
}

fn go_back(&mut self) -> Option<DeviceMenuMsg> {
fn go_back(&mut self, ctx: &mut EventCtx) -> Option<DeviceMenuMsg> {
if let Some(parent) = self.parent_subscreens.pop() {
self.set_active_subscreen(parent);
self.place(self.bounds);
if let Some(screen) = self.menu_screen.as_mut() {
screen.initialize_screen(ctx);
}
None
} else {
Some(DeviceMenuMsg::Close)
Expand Down Expand Up @@ -478,7 +484,7 @@ impl<'a> Component for DeviceMenuScreen<'a> {
}
}
Some(VerticalMenuScreenMsg::Back) => {
return self.go_back();
return self.go_back(ctx);
}
Some(VerticalMenuScreenMsg::Close) => {
return Some(DeviceMenuMsg::Close);
Expand All @@ -488,7 +494,7 @@ impl<'a> Component for DeviceMenuScreen<'a> {
}
Subscreen::AboutScreen => {
if let Some(TextScreenMsg::Cancelled) = self.about_screen.event(ctx, event) {
return self.go_back();
return self.go_back(ctx);
}
}
}
Expand Down
104 changes: 87 additions & 17 deletions core/embed/rust/src/ui/layout_eckhart/firmware/select_word_screen.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
use heapless::Vec;

use crate::{
strutil::TString,
ui::{
component::{Component, Event, EventCtx, Label},
geometry::{Alignment, Insets, Rect},
geometry::{Alignment, Insets, Offset, Rect},
shape,
shape::Renderer,
ui_firmware::MAX_WORD_QUIZ_ITEMS,
},
};

use super::super::{
component::Button,
component::{Button, ButtonMsg},
constant::SCREEN,
firmware::{Header, HeaderMsg, VerticalMenu, VerticalMenuMsg},
firmware::{Header, HeaderMsg},
theme,
};

pub struct SelectWordScreen {
header: Header,
description: Label<'static>,
menu: VerticalMenu,
menu: SelectWordButtons,
}

pub enum SelectWordMsg {
Expand All @@ -36,21 +39,11 @@ impl SelectWordScreen {
share_words_vec: [TString<'static>; MAX_WORD_QUIZ_ITEMS],
description: TString<'static>,
) -> Self {
let mut menu = VerticalMenu::empty().with_separators().with_fit_area();

for word in share_words_vec {
menu = menu.item(
Button::with_text(word)
.styled(theme::button_select_word())
.with_radius(12),
);
}

Self {
header: Header::new(TString::empty()),
description: Label::new(description, Alignment::Start, theme::TEXT_MEDIUM)
.top_aligned(),
menu,
menu: SelectWordButtons::new(share_words_vec),
}
}

Expand Down Expand Up @@ -87,7 +80,7 @@ impl Component for SelectWordScreen {
return Some(SelectWordMsg::Cancelled);
}

if let Some(VerticalMenuMsg::Selected(i)) = self.menu.event(ctx, event) {
if let Some(i) = self.menu.event(ctx, event) {
return Some(SelectWordMsg::Selected(i));
}

Expand All @@ -101,13 +94,90 @@ impl Component for SelectWordScreen {
}
}

struct SelectWordButtons {
buttons: Vec<Button, MAX_WORD_QUIZ_ITEMS>,
}

impl SelectWordButtons {
const SEPARATOR_PADDING: i16 = 12;
fn new(share_words_vec: [TString<'static>; MAX_WORD_QUIZ_ITEMS]) -> Self {
let mut buttons = Vec::new();
for word in share_words_vec {
unwrap!(buttons.push(
Button::with_text(word)
.styled(theme::button_select_word())
.with_radius(12)
.with_text_align(Alignment::Center),
));
}
Self { buttons }
}

fn render_separators<'s>(&'s self, target: &mut impl Renderer<'s>) {
for i in 1..self.buttons.len() {
let button = &self.buttons[i];
let button_prev = &self.buttons[i - 1];

if !button.is_pressed() && !button_prev.is_pressed() {
let separator = Rect::from_top_left_and_size(
button
.area()
.top_left()
.ofs(Offset::x(Self::SEPARATOR_PADDING)),
Offset::new(button.area().width() - 2 * Self::SEPARATOR_PADDING, 1),
);
shape::Bar::new(separator)
.with_fg(theme::GREY_EXTRA_DARK)
.render(target);
}
}
}
}

impl Component for SelectWordButtons {
type Msg = usize;

fn place(&mut self, bounds: Rect) -> Rect {
let button_height = bounds.height() / self.buttons.len() as i16;
for (i, button) in self.buttons.iter_mut().enumerate() {
let top_left = bounds.top_left().ofs(Offset::y(i as i16 * button_height));
let button_bounds =
Rect::from_top_left_and_size(top_left, Offset::new(bounds.width(), button_height));
button.place(button_bounds);
}

bounds
}

fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
for (i, button) in self.buttons.iter_mut().enumerate() {
if let Some(ButtonMsg::Clicked) = button.event(ctx, event) {
return Some(i);
}
}

None
}

fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
for button in &self.buttons {
button.render(target);
}
self.render_separators(target);
}
}

#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for SelectWordScreen {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("SelectWordScreen");
t.child("Header", &self.header);
t.child("subtitle", &self.description);
t.child("Content", &self.menu);
t.in_list("buttons", &|button_list| {
for button in &self.menu.buttons {
button_list.child(button);
}
});
}
}

Expand Down
Loading
Loading