Repository: ppizarror/pygame-menu

Issue:
dropselect doesn't update index correctly
**Environment information**
Describe your environment information, such as:
- SO: linux
- python version: v3.8
- pygame version: v2.0.1
- pygame-menu version: v4.0.6

**Describe the bug**
Updating a `dropselect` item list with fewer elements might raise an exception when the selected index is higher than new list lenght. In the following example the button deletes the item selected, but fails if it is the last item.

**To Reproduce**
```
import pygame, pygame_menu

SCREENWIDTH  = 800
SCREENHEIGHT = 480

def main():
    pygame.init()

    surface = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))

    def test_1(select):
        sVal = select.get_value()
        items = select._items
        items.pop(items.index(sVal[0]))
        #select._index = -1
        select.update_items(items)
        select.make_selection_drop()

    menu = pygame_menu.Menu('Test', SCREENWIDTH, SCREENHEIGHT,
                            theme = pygame_menu.themes.THEME_DARK,
                            onclose = pygame_menu.events.RESET,
                            touchscreen = False,
                            joystick_enabled = False,
                            mouse_enabled = True,
                            )

    S1 = menu.add.dropselect('Subject Id', items=[('a',),('b',),('c',),('d',),('e',),('f',)], dropselect_id='s0')
    menu.add.button('One',test_1, S1)

    # Allows menu to be run
    menu.mainloop(surface)
    print(menu.get_input_data())
    return

if __name__ == "__main__":
    main()
```

**Expected behavior**
The documentation says that if the new list has different size than the actual list, the new index will be -1, but that is not happening.
https://pygame-menu.readthedocs.io/en/4.0.6/_modules/pygame_menu/widgets/widget/dropselect.html#DropSelect.update_items
As a workaround, uncommenting the line (15) that sets `_index` to -1, helps to avoid the exception.

Also, I don't understand what `select.make_selection_drop()` but there is an exception if I don't call it.

**Additional context**
I belived this verification
https://github.com/ppizarror/pygame-menu/blob/3d8ef71b882a6e7bcbe7299af8d16fbcc0875d8f/pygame_menu/widgets/widget/dropselect.py#L1000
is intended to be before
https://github.com/ppizarror/pygame-menu/blob/3d8ef71b882a6e7bcbe7299af8d16fbcc0875d8f/pygame_menu/widgets/widget/dropselect.py#L992

Discussion:

Patch:
diff --git a/codecov.yml b/codecov.yml
index 517b8883..a444c199 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -1,3 +1,4 @@
 ignore:
+  - "docs/*.py"
   - "pygame_menu/__pyinstaller/*py"
   - "test/*.py"
\ No newline at end of file
diff --git a/pygame_menu/sound.py b/pygame_menu/sound.py
index 23dfc2e8..dc8d845f 100644
--- a/pygame_menu/sound.py
+++ b/pygame_menu/sound.py
@@ -112,7 +112,7 @@ SOUND_EXAMPLES = (SOUND_EXAMPLE_CLICK_MOUSE, SOUND_EXAMPLE_CLOSE_MENU, SOUND_EXA
                   SOUND_EXAMPLE_KEY_DELETE, SOUND_EXAMPLE_OPEN_MENU, SOUND_EXAMPLE_WIDGET_SELECTION)
 
 # Stores global reference that marks sounds as initialized
-SOUND_INITIALIZED = [False]
+SOUND_INITIALIZED = [False, True]
 
 
 class Sound(Base):
@@ -168,6 +168,7 @@ class Sound(Base):
         if mixer_missing:
             warn('pygame mixer module could not be found, NotImplementedError'
                  'has been raised. Sound support is disabled')
+            SOUND_INITIALIZED[1] = False
 
         # Initialize sounds if not initialized
         if not mixer_missing and \
@@ -325,7 +326,7 @@ class Sound(Base):
             raise ValueError('sound type not valid, check the manual')
 
         # If file is none disable the sound
-        if sound_file is None:
+        if sound_file is None or not SOUND_INITIALIZED[1]:
             self._sound[sound_type] = {}
             return False
 
diff --git a/pygame_menu/widgets/widget/dropselect.py b/pygame_menu/widgets/widget/dropselect.py
index f4aaddd7..059b6d19 100644
--- a/pygame_menu/widgets/widget/dropselect.py
+++ b/pygame_menu/widgets/widget/dropselect.py
@@ -323,7 +323,9 @@ class DropSelect(Widget):
 
     def make_selection_drop(self, **kwargs) -> 'DropSelect':
         """
-        Make selection drop box.
+        Make the selection drop box. This function creates the box UI and adds
+        the selection options, and should be called after updating the selection
+        options.
 
         kwargs (Optional)
             - ``scrollbar_color``               (tuple, list, str, int, :py:class:`pygame.Color`) – Scrollbar color
@@ -758,8 +760,9 @@ class DropSelect(Widget):
         current_rect_bg.y += int((self._selection_box_inflate[1]) / 2
                                  + vi + self._selection_box_margin[1])
         current_rect_bg.width = self._selection_box_width
-        current_rect_bg = current_rect_bg.inflate((self._selection_box_inflate[0],
-                                                   self._selection_box_inflate[1] + 2 * vi))
+        current_rect_bg = current_rect_bg.inflate(
+            (int(self._selection_box_inflate[0]), int(self._selection_box_inflate[1] + 2 * vi))
+        )
 
         # Compute delta title if height is lower than selection box
         h = title.get_height()
@@ -767,9 +770,13 @@ class DropSelect(Widget):
 
         # Create arrows
         arrow = pygame.Rect(
-            int(title.get_width() + self._selection_box_margin[0] + self._selection_box_width - h),
-            int(self._selection_box_arrow_margin[2] + (self._selection_box_inflate[1] + vi / 2) / 2
-                + delta_title_height + self._selection_box_margin[1]),
+            int(title.get_width()
+                + self._selection_box_margin[0]
+                + self._selection_box_width - h),
+            int(self._selection_box_arrow_margin[2]
+                + (self._selection_box_inflate[1] + vi / 2) / 2
+                + delta_title_height
+                + self._selection_box_margin[1]),
             h,
             h
         )
@@ -797,10 +804,10 @@ class DropSelect(Widget):
         else:
             self.force_menu_draw_focus = False
 
+        delta_title_height_dh = math.ceil((current_rect_bg.height - h) / 2) + \
+                                2 * self._selection_box_border_width
         if delta_title_height != 0:
-            self.set_attribute('delta_title_height',
-                               math.ceil((current_rect_bg.height - h) / 2)
-                               + 2 * self._selection_box_border_width)
+            self.set_attribute('delta_title_height', delta_title_height_dh)
 
         arrow_up = (
             (int(arrow.right - w + h / 2 - h / 16), int(arrow.centery - h / 6 - h / 20)),
@@ -823,8 +830,10 @@ class DropSelect(Widget):
             else:
                 arrow_right_pos = arrow_down
 
-        self._surface = make_surface(title.get_width() + self._selection_box_margin[0]
-                                     + self._selection_box_width + self._selection_box_inflate[0] / 2
+        self._surface = make_surface(title.get_width()
+                                     + self._selection_box_margin[0]
+                                     + self._selection_box_width
+                                     + self._selection_box_inflate[0] / 2
                                      + self._selection_box_border_width,
                                      max(title.get_height() + self._selection_box_inflate[1],
                                          current_rect_bg.height))
@@ -847,9 +856,11 @@ class DropSelect(Widget):
         # new_current.fill((0, 0, 0))
         self._surface.blit(new_current,
                            (
-                               int(title.get_width() + self._selection_box_margin[0]
+                               int(title.get_width()
+                                   + self._selection_box_margin[0]
                                    + self._selection_box_text_margin),
-                               int(self._selection_box_inflate[1] / 2 + vi - 1
+                               int(self._selection_box_inflate[1] / 2
+                                   + vi - 1
                                    + self._selection_box_margin[1])
                            ))
         if len(self._items) > 0:
@@ -935,7 +946,7 @@ class DropSelect(Widget):
         text if ``item`` is a string, or the index if ``item`` is an integer.
         This method raises ``ValueError`` if no item found.
 
-        For example, if widget item list is ``[['a',0],['b',1],['a',2]]``:
+        For example, if widget item list is ``[['a', 0], ['b', 1], ['a', 2]]``:
 
         - *widget*.set_value('a') -> Widget selects the first item (index 0)
         - *widget*.set_value(2) -> Widget selects the third item (index 2)
@@ -969,7 +980,9 @@ class DropSelect(Widget):
             btn = self._option_buttons[b_ind_x]
             if b_ind_x == self._index:
                 btn.set_background_color(self._selection_option_selected_bgcolor)
-                btn.update_font({'color': self._selection_option_font_style['color_selected']})
+                btn.update_font({
+                    'color': self._selection_option_font_style['color_selected']
+                })
                 if not self._drop_frame.has_attribute('ignorescroll'):
                     btn.scroll_to_widget(scroll_parent=False)
             else:
@@ -983,7 +996,8 @@ class DropSelect(Widget):
         .. note::
 
             If the length of the list is different than the previous one, the new
-            index of the select will be the first item of the list.
+            index of the select will be the ``-1``, that is, same as the unselected
+            status.
 
         :param items: New drop select items; format ``[('Item1', a, b, c...), ('Item2', d, e, f...)]``
         :return: None
@@ -991,18 +1005,11 @@ class DropSelect(Widget):
         assert isinstance(items, list)
         if len(items) > 0:
             check_selector_items(items)
-        if self._index != -1:
-            selected_item = self._items[self._index]
-        else:
-            selected_item = None
-        self._items = items
-        if selected_item is not None:
-            try:
-                self._index = self._items.index(selected_item)
-            except ValueError:
-                if self._index >= len(self._items):
-                    self._index = -1
-                    self._default_value = -1
+        if self._index != -1 and len(items) != len(self._items) or \
+                (self._index + 1) > len(items):
+            self._index = -1
+            self._default_value = -1
+        self._items = items.copy()
         if self._drop_frame is not None:
             self._drop_frame.set_menu(None)
         self._drop_frame = None
@@ -1020,6 +1027,14 @@ class DropSelect(Widget):
                 'for avoiding this exception'.format(self.get_class_id())
             )
 
+    def get_items(self) -> Union[List[Tuple[Any, ...]], List[str]]:
+        """
+        Return a copy of the select items.
+
+        :return: Select items list
+        """
+        return self._items.copy()
+
     def _toggle_drop(self) -> None:
         """
         Open drop selection.
@@ -1082,8 +1097,8 @@ class DropSelect(Widget):
                           - self._drop_frame.get_attribute('extra_margin')
             if self._open_middle:
                 x, y = self._compute_position_middle(add_offset=False)
-                rect.x = x + self._menu.get_position()[0]
-                rect.y = y + self._menu.get_scrollarea().get_position()[1]
+                rect.x = int(x + self._menu.get_position()[0])
+                rect.y = int(y + self._menu.get_scrollarea().get_position()[1])
                 rect.height -= self._rect.height
         else:
             rect.width -= self._selection_box_border_width
diff --git a/pygame_menu/widgets/widget/dropselect_multiple.py b/pygame_menu/widgets/widget/dropselect_multiple.py
index c6fb32b4..710e201e 100644
--- a/pygame_menu/widgets/widget/dropselect_multiple.py
+++ b/pygame_menu/widgets/widget/dropselect_multiple.py
@@ -315,7 +315,8 @@ class DropSelectMultiple(DropSelect):
         .. note::
 
             If the length of the list is different than the previous one, the new
-            index of the select will be the first item of the list.
+            index of the select will be the ``-1``, that is, same as the unselected
+            status.
 
         :param items: New drop select items; format ``[('Item1', a, b, c...), ('Item2', d, e, f...)]``
         :return: None
@@ -354,7 +355,7 @@ class DropSelectMultiple(DropSelect):
         the text if ``item`` is a string, or the index if ``item`` is an integer.
         This method raises ``ValueError`` if no item found.
 
-        For example, if widget item list is ``[['a',0],['b',1],['a',2]]``:
+        For example, if widget item list is ``[['a', 0], ['b', 1], ['a', 2]]``:
 
         - *widget*.set_value('a') -> Widget selects the first item (index 0)
         - *widget*.set_value(2) -> Widget selects the third item (index 2)
@@ -380,7 +381,8 @@ class DropSelectMultiple(DropSelect):
                     found = True
                     break
             if not found:
-                raise ValueError('no value "{}" found in drop select multiple'.format(item))
+                raise ValueError('no value "{}" found in drop select multiple'
+                                 ''.format(item))
         elif isinstance(item, int):
             assert -1 <= item < len(self._items), \
                 'item index must be greater than zero and lower than the number ' \
diff --git a/pygame_menu/widgets/widget/selector.py b/pygame_menu/widgets/widget/selector.py
index 45f348dd..f3b4f6ab 100644
--- a/pygame_menu/widgets/widget/selector.py
+++ b/pygame_menu/widgets/widget/selector.py
@@ -321,7 +321,7 @@ class Selector(Widget):
             self._surface.blit(title, (0, int(self._style_fancy_box_inflate[1] / 2)))
             current_rect_bg = current.get_rect()
             current_rect_bg.x += title.get_width() + self._style_fancy_box_margin[0]
-            current_rect_bg.y += int(self._style_fancy_box_inflate[1] / 2 \
+            current_rect_bg.y += int(self._style_fancy_box_inflate[1] / 2
                                      + self._style_fancy_box_margin[1])
             current_rect_bg.width += 2 * (self._style_fancy_arrow_margin[0]
                                           + self._style_fancy_arrow_margin[1]
@@ -353,6 +353,14 @@ class Selector(Widget):
         """
         return self._index
 
+    def get_items(self) -> Union[List[Tuple[Any, ...]], List[str]]:
+        """
+        Return a copy of the select items.
+
+        :return: Select items list
+        """
+        return self._items.copy()
+
     def get_value(self) -> Tuple[Union[Tuple[Any, ...], str], int]:
         """
         Return the current value of the selected index.
@@ -391,7 +399,7 @@ class Selector(Widget):
         the text if ``item`` is a string, or the index if ``item`` is an integer.
         This method raises ``ValueError`` if no item found.
 
-        For example, if widget item list is ``[['a',0],['b',1],['a',2]]``:
+        For example, if widget item list is ``[['a', 0], ['b', 1], ['a', 2]]``:
 
         - *widget*.set_value('a') -> Widget selects the first item (index 0)
         - *widget*.set_value(2) -> Widget selects the third item (index 2)


Test patch:
diff --git a/test/test_sound.py b/test/test_sound.py
index 5dd4f217..4712d684 100644
--- a/test/test_sound.py
+++ b/test/test_sound.py
@@ -96,6 +96,8 @@ class SoundTest(unittest.TestCase):
         """
         Test load sounds.
         """
+        if SYS_PLATFORM_OSX:
+            return
         self.assertFalse(self.sound.set_sound(pygame_menu.sound.SOUND_TYPE_CLICK_MOUSE, None))
         self.assertRaises(ValueError, lambda: self.sound.set_sound('none', None))
         self.assertRaises(IOError,
diff --git a/test/test_widgets.py b/test/test_widgets.py
index da424675..23d1b2bd 100644
--- a/test/test_widgets.py
+++ b/test/test_widgets.py
@@ -634,7 +634,7 @@ class WidgetsTest(unittest.TestCase):
         self.assertEqual(selector.get_value()[0][0], '4 - Easy')
 
         # Test fancy selector
-        menu.add.selector(
+        sel_fancy = menu.add.selector(
             'Fancy ',
             [('1 - Easy', 'EASY'),
              ('2 - Medium', 'MEDIUM'),
@@ -642,6 +642,9 @@ class WidgetsTest(unittest.TestCase):
             default=1,
             style=pygame_menu.widgets.widget.selector.SELECTOR_STYLE_FANCY
         )
+        self.assertEqual(sel_fancy.get_items(), [('1 - Easy', 'EASY'),
+                                                 ('2 - Medium', 'MEDIUM'),
+                                                 ('3 - Hard', 'HARD')])
 
     # noinspection PyArgumentEqualDefault,PyTypeChecker
     def test_colorinput(self) -> None:
@@ -1597,8 +1600,6 @@ class WidgetsTest(unittest.TestCase):
         """
         Test dropselect multiple widget.
         """
-        if SYS_PLATFORM_OSX:
-            return
         theme = pygame_menu.themes.THEME_DEFAULT.copy()
         theme.widget_font_size = 25
         menu = MenuUtils.generic_menu(mouse_motion_selection=True, theme=theme)
@@ -1609,7 +1610,7 @@ class WidgetsTest(unittest.TestCase):
         drop = pygame_menu.widgets.DropSelectMultiple('dropsel', items, open_middle=True, selection_box_height=5)
         self.assertNotEqual(id(items), id(drop._items))
         menu.add.generic_widget(drop, configure_defaults=True)
-        self.assertEqual(drop._selection_box_width, 225)
+        self.assertEqual(drop._selection_box_width, 225 if not SYS_PLATFORM_OSX else 226)
         drop.make_selection_drop()
 
         # Check drop is empty
@@ -1695,7 +1696,7 @@ class WidgetsTest(unittest.TestCase):
         # Use manager
         drop2 = menu.add.dropselect_multiple('nice', [('This is a really long selection item', 1), ('epic', 2)],
                                              placeholder_selected='nice {0}', placeholder='epic', max_selected=1)
-        self.assertEqual(drop2._selection_box_width, 134)
+        self.assertEqual(drop2._selection_box_width, 134 if not SYS_PLATFORM_OSX else 135)
         self.assertEqual(drop2._get_current_selected_text(), 'epic')
         drop2.set_value('epic', process_index=True)
         self.assertEqual(drop2.get_index(), [1])
@@ -1705,6 +1706,7 @@ class WidgetsTest(unittest.TestCase):
         self.assertEqual(drop2._get_current_selected_text(), 'nice 1')
         self.assertEqual(drop2._default_value, [])
         self.assertEqual(drop2._index, 0)
+        self.assertRaises(ValueError, lambda: drop2.set_value('not epic'))
 
         # Reset
         drop2.reset_value()
@@ -1758,12 +1760,14 @@ class WidgetsTest(unittest.TestCase):
         drop2._option_buttons[1].apply()
         self.assertEqual(test[0], [0, 1])
 
+        # Test none drop frame
+        drop2._drop_frame = None
+        self.assertEqual(drop2.get_scroll_value_percentage('any'), -1)
+
     def test_dropselect(self) -> None:
         """
         Test dropselect widget.
         """
-        if SYS_PLATFORM_OSX:
-            return
         menu = MenuUtils.generic_menu(mouse_motion_selection=True, theme=THEME_NON_FIXED_TITLE)
         items = [('This is a really long selection item', 1), ('epic', 2)]
         for i in range(10):
@@ -1773,7 +1777,8 @@ class WidgetsTest(unittest.TestCase):
                                               selection_option_font_size=int(0.75 * menu._theme.widget_font_size),
                                               placeholder_add_to_selection_box=False)
         menu.add.generic_widget(drop, configure_defaults=True)
-        self.assertEqual(drop._selection_box_width, 207 if PYGAME_V2 else 208)
+        self.assertEqual(drop._selection_box_width,
+                         (207 if PYGAME_V2 else 208) if not SYS_PLATFORM_OSX else 209)
         drop.make_selection_drop()
         self.assertEqual(drop.get_frame_depth(), 0)
         drop.render()
@@ -1828,8 +1833,10 @@ class WidgetsTest(unittest.TestCase):
                    (-1, -1, -1, 0, 428, 356, 40, 261, 348, 0, 583),
                    (1, 0, 0, 0, 0, 1, 1))),)
             ))
-        self.assertEqual(drop._drop_frame.get_attribute('height'), 135 if PYGAME_V2 else 138)
-        self.assertEqual(drop._drop_frame.get_attribute('width'), 187 if PYGAME_V2 else 188)
+        self.assertEqual(drop._drop_frame.get_attribute('height'),
+                         (135 if PYGAME_V2 else 138) if not SYS_PLATFORM_OSX else 141)
+        self.assertEqual(drop._drop_frame.get_attribute('width'),
+                         (187 if PYGAME_V2 else 188) if not SYS_PLATFORM_OSX else 189)
 
         # Test events
         self.assertFalse(drop.active)
@@ -1902,10 +1909,11 @@ class WidgetsTest(unittest.TestCase):
         self.assertFalse(drop.active)
 
         # Touch middle
-        self.assertTrue(drop._touchscreen_enabled)
-        drop.update(PygameEventUtils.middle_rect_click(
-            drop.get_focus_rect(), evtype=pygame.FINGERUP, menu=drop.get_menu()))
-        self.assertTrue(drop.active)
+        if not SYS_PLATFORM_OSX:
+            self.assertTrue(drop._touchscreen_enabled)
+            drop.update(PygameEventUtils.middle_rect_click(
+                drop.get_focus_rect(), evtype=pygame.FINGERUP, menu=drop.get_menu()))
+            self.assertTrue(drop.active)
 
         # Test focus
         if not drop.active:
@@ -1918,12 +1926,20 @@ class WidgetsTest(unittest.TestCase):
                               4: ((0, 484), (600, 484), (600, 600), (0, 600))}
                              )
         else:
-            self.assertEqual(menu._draw_focus_widget(surface, drop),
-                             {1: ((0, 0), (600, 0), (600, 306), (0, 306)),
-                              2: ((0, 307), (259, 307), (259, 486), (0, 486)),
-                              3: ((468, 307), (600, 307), (600, 486), (468, 486)),
-                              4: ((0, 487), (600, 487), (600, 600), (0, 600))}
-                             )
+            if not SYS_PLATFORM_OSX:
+                self.assertEqual(menu._draw_focus_widget(surface, drop),
+                                 {1: ((0, 0), (600, 0), (600, 306), (0, 306)),
+                                  2: ((0, 307), (259, 307), (259, 486), (0, 486)),
+                                  3: ((468, 307), (600, 307), (600, 486), (468, 486)),
+                                  4: ((0, 487), (600, 487), (600, 600), (0, 600))}
+                                 )
+            else:
+                self.assertEqual(menu._draw_focus_widget(surface, drop),
+                                 {1: ((0, 0), (600, 0), (600, 305), (0, 305)),
+                                  2: ((0, 306), (259, 306), (259, 489), (0, 489)),
+                                  3: ((469, 306), (600, 306), (600, 489), (469, 489)),
+                                  4: ((0, 490), (600, 490), (600, 600), (0, 600))}
+                                 )
 
         # Test change items
         drop.update_items([])
@@ -1990,7 +2006,7 @@ class WidgetsTest(unittest.TestCase):
         vm = menu.add.vertical_margin(500)
         self.assertEqual(vm.get_height(), 500)
 
-        # Add drop from widgetmanager, this has the placeholder button
+        # Add drop from widget manager, this has the placeholder button
         drop2 = menu.add.dropselect('drop2', items, dropselect_id='2', selection_infinite=True,
                                     selection_option_font_size=int(0.75 * menu._theme.widget_font_size))
         self.assertEqual(drop2._tab_size, menu._theme.widget_tab_size)
@@ -2010,9 +2026,12 @@ class WidgetsTest(unittest.TestCase):
         menu.select_widget(drop2)
         drop2._toggle_drop()
 
-        self.assertEqual(drop2.get_position(), (132, 554) if PYGAME_V2 else (131, 555))
-        self.assertEqual(drop2._drop_frame.get_attribute('height'), 117 if PYGAME_V2 else 120)
-        self.assertEqual(drop2._drop_frame.get_attribute('width'), 187 if PYGAME_V2 else 188)
+        self.assertEqual(drop2.get_position(),
+                         ((132, 554) if PYGAME_V2 else (131, 555)) if not SYS_PLATFORM_OSX else (131, 556))
+        self.assertEqual(drop2._drop_frame.get_attribute('height'),
+                         (117 if PYGAME_V2 else 120) if not SYS_PLATFORM_OSX else 123)
+        self.assertEqual(drop2._drop_frame.get_attribute('width'),
+                         (187 if PYGAME_V2 else 188) if not SYS_PLATFORM_OSX else 189)
 
         # Test infinite
         self.assertTrue(drop2.active)
@@ -2041,7 +2060,8 @@ class WidgetsTest(unittest.TestCase):
         self.assertEqual(drop2.get_index(), 7)
         drop2.readonly = False
         menu.render()
-        self.assertEqual(drop2.get_scroll_value_percentage(ORIENTATION_VERTICAL), 0.606 if PYGAME_V2 else 0.603)
+        self.assertEqual(drop2.get_scroll_value_percentage(ORIENTATION_VERTICAL),
+                         (0.606 if PYGAME_V2 else 0.603) if not SYS_PLATFORM_OSX else 0.609)
         drop2.reset_value()
         self.assertTrue(drop2.active)
         self.assertEqual(drop2.get_index(), -1)
@@ -2086,10 +2106,12 @@ class WidgetsTest(unittest.TestCase):
                   (-1, -1, -1, 116, 110, 207, 34, 204, 418, 116, -132),
                   (1, 0, 0, 0, 0, 1, 2)))
             ))
-        self.assertEqual(drop2._drop_frame.get_attribute('height'), 100 if PYGAME_V2 else 103)
-        self.assertEqual(drop2._drop_frame.get_attribute('width'), 207 if PYGAME_V2 else 208)
+        self.assertEqual(drop2._drop_frame.get_attribute('height'),
+                         (100 if PYGAME_V2 else 103) if not SYS_PLATFORM_OSX else 106)
+        self.assertEqual(drop2._drop_frame.get_attribute('width'),
+                         (207 if PYGAME_V2 else 208) if not SYS_PLATFORM_OSX else 209)
         self.assertEqual(drop2.get_scrollarea().get_parent_scroll_value_percentage(ORIENTATION_VERTICAL),
-                         (0, 1))
+                         (0, 1) if not SYS_PLATFORM_OSX else (0, 0.997))
         self.assertTrue(drop2._open_bottom)
 
         # Test onchange
@@ -2140,8 +2162,10 @@ class WidgetsTest(unittest.TestCase):
         f.unpack(drop2)
         self.assertTrue(drop2.is_floating())
         drop2.set_float(False)
-        self.assertEqual(drop2._drop_frame.get_attribute('height'), 100 if PYGAME_V2 else 103)
-        self.assertEqual(drop2._drop_frame.get_attribute('width'), 207 if PYGAME_V2 else 208)
+        self.assertEqual(drop2._drop_frame.get_attribute('height'),
+                         (100 if PYGAME_V2 else 103) if not SYS_PLATFORM_OSX else 106)
+        self.assertEqual(drop2._drop_frame.get_attribute('width'),
+                         (207 if PYGAME_V2 else 208) if not SYS_PLATFORM_OSX else 209)
 
         # Test close if mouse clicks outside
         menu.select_widget(drop)
@@ -2232,7 +2256,7 @@ class WidgetsTest(unittest.TestCase):
         menu_theme.widget_font_size = 20
 
         menu2 = MenuUtils.generic_menu(theme=menu_theme, width=400)
-        menu2.add_vertical_margin(1000)
+        menu2.add.vertical_margin(1000)
 
         drop3 = menu2.add.dropselect_multiple(
             title='Pick 3 colors',
@@ -2248,15 +2272,53 @@ class WidgetsTest(unittest.TestCase):
             open_middle=True,
             max_selected=3
         )
-        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108, 468, 320, 28))
+        rh = 28 if not SYS_PLATFORM_OSX else 29
+
+        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108, 468, 320, rh))
 
         # Translate the menu, this should also modify focus
         menu2.translate(100, 50)
-        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108 + 100, 468 + 50, 320, 28))
+        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108 + 100, 468 + 50, 320, rh))
         menu2.translate(100, 150)
-        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108 + 100, 468 + 150, 320, 28))
+        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108 + 100, 468 + 150, 320, rh))
         menu2.translate(0, 0)
-        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108, 468, 320, 28))
+        self.assertEqual(drop3.get_focus_rect(), pygame.Rect(108, 468, 320, rh))
+
+        # Test update list
+        def test_update(select: 'pygame_menu.widgets.DropSelect'):
+            """
+            Update list event.
+            """
+            if select.get_index() == -1:
+                return
+            s_val = select.get_value()
+            _items = select.get_items()
+            _items.pop(_items.index(s_val[0]))
+            select.update_items(_items)
+            self.assertEqual(select.get_scroll_value_percentage('any'), -1)
+            select.make_selection_drop()
+
+        menu = MenuUtils.generic_menu()
+
+        select1 = menu.add.dropselect('Subject Id',
+                                      items=[('a',), ('b',), ('c',), ('d',), ('e',), ('f',)],
+                                      dropselect_id='s0')
+        b_sel = menu.add.button('One', test_update, select1)
+        b_sel.apply()
+        select1.set_value(0)
+        self.assertEqual(select1.get_value(), (('a',), 0))
+        b_sel.apply()
+        self.assertEqual(select1.get_index(), -1)
+        self.assertEqual(select1.get_items(), [('b',), ('c',), ('d',), ('e',), ('f',)])
+        b_sel.apply()
+
+        # Update by value
+        select1.set_value('b')
+        self.assertEqual(select1.get_index(), 0)
+        select1.set_value('e')
+        self.assertEqual(select1.get_index(), 3)
+        self.assertRaises(ValueError, lambda: select1.set_value('unknown'))
+        b_sel.apply()  # to -1
 
     def test_none(self) -> None:
         """