Skip to content

Commit

Permalink
Added remember_selection to Menu constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
ppizarror committed Oct 25, 2024
1 parent fc47c9e commit 7a200ac
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 4 deletions.
10 changes: 8 additions & 2 deletions pygame_menu/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class Menu(Base):
:param onreset: Function executed when resetting the Menu. The function must be non-argument or single argument (Menu instance)
:param overflow: Enables overflow on x/y axes. If ``False`` then scrollbars will not work and the maximum width/height of the scrollarea is the same as the Menu container. Style: (overflow_x, overflow_y). If ``False`` or ``True`` the value will be set on both axis
:param position: Position on x-axis and y-axis. If the value is only 2 elements, the position is relative to the window width (thus, values must be 0-100%); else, the third element defines if the position is relative or not. If ``(x, y, False)`` the values of ``(x, y)`` are in px
:param remember_selection: Menu remembers selection when moving through submenus. By default it is false, so, when moving back the selected widget will be the first one of the Menu
:param rows: Number of rows of each column, if there's only 1 column ``None`` can be used for no-limit. Also, a tuple can be provided for defining different number of rows for each column, for example ``rows=10`` (each column can have a maximum 10 widgets), or ``rows=[2, 3, 5]`` (first column has 2 widgets, second 3, and third 5)
:param screen_dimension: List/Tuple representing the dimensions the Menu should reference for sizing/positioning (width, height), if ``None`` pygame is queried for the display mode. This value defines the ``window_size`` of the Menu
:param surface: The surface that contains the Menu. By default the Menu always considers that it is drawn on a surface that uses all window width/height. However, if a sub-surface is used the ``surface`` value will be used instead to retrieve the offset. Also, if ``surface`` is provided the menu can be drawn without providing a surface object while calling ``Menu.draw()``
Expand Down Expand Up @@ -153,6 +154,7 @@ class Menu(Base):
_position_default: Tuple2IntType
_position_relative: bool
_prev: Optional[List[Union['Menu', List['Menu']]]]
_remember_selection: bool
_runtime_errors: '_MenuRuntimeErrorConfig'
_scrollarea: 'ScrollArea'
_scrollarea_margin: List[int]
Expand Down Expand Up @@ -207,6 +209,7 @@ def __init__(
onreset: Optional[Union[Callable[['Menu'], Any], CallableNoArgsType]] = None,
overflow: Union[Vector2BoolType, bool] = (True, True),
position: Union[Vector2NumberType, Tuple[NumberType, NumberType, bool]] = (50, 50, True),
remember_selection: bool = False,
rows: MenuRowsType = None,
screen_dimension: Optional[Vector2IntType] = None,
surface: Optional['pygame.Surface'] = None,
Expand All @@ -229,6 +232,7 @@ def __init__(
assert isinstance(mouse_visible, bool)
assert isinstance(mouse_visible_update, bool)
assert isinstance(overflow, (VectorInstance, bool))
assert isinstance(remember_selection, bool)
assert isinstance(rows, (int, type(None), VectorInstance))
assert isinstance(surface, (pygame.Surface, type(None)))
assert isinstance(theme, Theme), \
Expand Down Expand Up @@ -369,6 +373,7 @@ def __init__(
self._last_selected_type = '' # Last type selection, used for test purposes
self._mainloop = False # Menu is in mainloop state
self._onclose = None # Function or event called on Menu close
self._remember_selection = remember_selection
self._render_enabled = True
self._sound = Sound(verbose=verbose)
self._stats = _MenuStats()
Expand Down Expand Up @@ -3237,8 +3242,9 @@ def _open(self, menu: 'Menu') -> None:
self._top._current = menu._current
self._top._prev = [self._top._prev, current]

# Select the first widget
self._current._select(0, 1, SELECT_OPEN, False, update_mouse_position=False)
# Select the first widget (if not remember the selection)
if not self._current._remember_selection:
self._current._select(0, 1, SELECT_OPEN, False, update_mouse_position=False)

# Call event
if menu._onbeforeopen is not None:
Expand Down
48 changes: 46 additions & 2 deletions test/test_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -2599,7 +2599,7 @@ def test_subsurface_offset(self) -> None:
"""
main_surface = surface
w, h = surface.get_size()
left_surf_w, left_surf_h = 300, h
left_surf_w, _ = 300, h
menu_w, menu_h = w - left_surf_w, h
# left_surface = main_surface.subsurface((0, 0, left_surf_w, left_surf_h))
menu_surface = main_surface.subsurface((300, 0, menu_w, menu_h))
Expand Down Expand Up @@ -2651,11 +2651,55 @@ def test_inheritance(self) -> None:
class SubMenu(pygame_menu.Menu):
def __init__(self) -> None:
super().__init__(title='Test', width=150, height=200, theme=pygame_menu.themes.THEME_DARK.copy())
help_menu = pygame_menu.Menu(title='Help', width=150, height=200)
help_menu = MenuUtils.generic_menu()
self.add.button(help_menu.get_title(), help_menu)
self.enable()

self.assertEqual(len(SubMenu().get_widgets()), 1)
main_menu = SubMenu()
test_menu = pygame_menu.Menu('test', 500, 400)
self.assertEqual(main_menu.add.menu_link(test_menu).get_menu(), main_menu)

def test_selection(self) -> None:
"""
Test menu widget selection. Based on #471.
"""
menu, sub, sub2 = MenuUtils.generic_menu(), MenuUtils.generic_menu(), MenuUtils.generic_menu()

# Add "sub" as a link within "menu"
sub_link = menu.add.menu_link(sub)
btn_back = sub.add.button('Back', pygame_menu.events.BACK)

# Add "sub2" as a link within "sub"
sub2_link = sub.add.menu_link(sub2)
btn_back_2 = sub2.add.button('Back', pygame_menu.events.BACK)

btn = menu.add.button('No-op Button')
btn2 = menu.add.button('Sub', sub_link.open)
btn3 = sub.add.button('Sub2', sub2_link.open)

menu.render()
self.assertEqual(menu.get_selected_widget(), btn)

# Now, we test selection preservation on return. By default, menu does not
# keep previous selection. So, selecting widget 2 (that opens the new menu
# sub2), and moving back, the index should be 0
menu.select_widget(btn2).get_selected_widget().apply()
self.assertEqual(menu.get_current(), sub)
self.assertEqual(menu.get_current().get_selected_widget(), btn_back)
# Now we select the btn3 and apply
menu.get_current().select_widget(btn3).get_selected_widget().apply()
btn_back_2.apply()
self.assertEqual(menu.get_current().get_selected_widget(), btn_back)

menu.get_current()._remember_selection = True
menu.get_current().select_widget(btn3).get_selected_widget().apply()
btn_back_2.apply()
self.assertEqual(menu.get_current().get_selected_widget(), btn3)

# Disable the feature, and now see what happens if trying to select first index if menu is empty
menu.get_current()._remember_selection = True
btn3.apply()
self.assertEqual(menu.get_current(), sub2)
sub.clear(reset=False)
self.assertEqual(menu.get_current(), sub2)

0 comments on commit 7a200ac

Please sign in to comment.