Skip to content
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

[BUG] Panic when opening empty menubar subtree #776

Open
DvvCz opened this issue Mar 27, 2024 · 2 comments
Open

[BUG] Panic when opening empty menubar subtree #776

DvvCz opened this issue Mar 27, 2024 · 2 comments
Labels

Comments

@DvvCz
Copy link

DvvCz commented Mar 27, 2024

Describe the bug
When creating a menubar with an empty subtree, panics when you select it in the menu.
Dealing with this as I want to create a dynamic menubar to select a file, although maybe this isn't the best approach.

To Reproduce

let mut siv = cursive::default();
siv.menubar().add_subtree("foo", Tree::new());
siv.add_global_callback(Key::Esc, |s| s.select_menubar());
siv.run();
// Then press escape, go to foo and press enter.

Expected behavior
Expected to not panic

Environment

  • OS: Linux (Nobara 39)
  • Backend used: ncurses
  • Current locale: en_US
  • Cursive version: 0.20.0
  • Nightly rust in debug build

Additional context
Assuming index OOB error "len is 0 but index is 0":

let item = &s.menu.children[i];

As panic points to around there (line 335 specifically).

@DvvCz DvvCz added the bug label Mar 27, 2024
@gyscos
Copy link
Owner

gyscos commented Mar 27, 2024

Thanks for the report!

Indeed, not panicking sounds like a better behavior :)

@correabuscar
Copy link
Contributor

Tentative fix would be:

diff --git a/cursive-core/src/views/menu_popup.rs b/cursive-core/src/views/menu_popup.rs
index d3050ba..9fe5698 100644
--- a/cursive-core/src/views/menu_popup.rs
+++ b/cursive-core/src/views/menu_popup.rs
@@ -130,7 +130,11 @@ impl MenuPopup {
             } else if cycle {
                 // Only cycle once to prevent endless loop
                 cycle = false;
-                self.focus = self.menu.children.len() - 1;
+                let len = self.menu.children.len();
+                if len == 0 {
+                    break;
+                }
+                self.focus = len - 1;
             } else {
                 break;
             }
@@ -143,12 +147,16 @@ impl MenuPopup {
 
     fn scroll_down(&mut self, mut n: usize, mut cycle: bool) {
         while n > 0 {
-            if self.focus + 1 < self.menu.children.len() {
+            let len = self.menu.children.len();
+            if self.focus + 1 < len {
                 self.focus += 1;
             } else if cycle {
                 // Only cycle once to prevent endless loop
                 cycle = false;
                 self.focus = 0;
+                if len == 0 {
+                    break;
+                }
             } else {
                 // Stop if we're at the bottom.
                 break;
@@ -236,13 +244,19 @@ impl MenuPopup {
             Event::Key(Key::Home) => self.focus = 0,
             Event::Key(Key::End) => self.focus = self.menu.children.len().saturating_sub(1),
 
-            Event::Key(Key::Right) if self.menu.children[self.focus].is_subtree() => {
+            Event::Key(Key::Right)
+                if (self.focus < self.menu.children.len()
+                    && self.menu.children[self.focus].is_subtree()) =>
+            {
                 return match self.menu.children[self.focus] {
                     menu::Item::Subtree { ref tree, .. } => self.make_subtree_cb(tree),
                     _ => unreachable!("Child is a subtree"),
                 };
             }
-            Event::Key(Key::Enter) if self.menu.children[self.focus].is_enabled() => {
+            Event::Key(Key::Enter)
+                if (self.focus < self.menu.children.len()
+                    && self.menu.children[self.focus].is_enabled()) =>
+            {
                 return self.submit();
             }
             Event::Mouse {
@@ -263,7 +277,8 @@ impl MenuPopup {
                 event: MouseEvent::Release(MouseButton::Left),
                 position,
                 offset,
-            } if self.menu.children[self.focus].is_enabled()
+            } if (self.focus < self.menu.children.len()
+                && self.menu.children[self.focus].is_enabled())
                 && position
                     .checked_sub(offset)
                     .map(|position| position.y == self.focus)
@@ -320,7 +335,13 @@ impl View for MenuPopup {
         scroll::draw_box_frame(
             self,
             printer,
-            |s, y| s.menu.children[y].is_delimiter(),
+            |s, y| {
+                if s.menu.children.len() > y {
+                    s.menu.children[y].is_delimiter()
+                } else {
+                    false
+                }
+            },
             |_s, _x| false,
         );
 

But maybe this isn't the proper way to do it. Like, some indirection(via some new function) might be wanted instead.

correabuscar added a commit to correabuscar/cursive that referenced this issue Jun 11, 2024
correabuscar added a commit to correabuscar/cursive that referenced this issue Jun 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants