예제 #1
0
    def test_size(self) -> None:
        """
        Test size.
        """
        menu = MenuUtils.generic_menu(title='menu', theme=TEST_THEME.copy())
        menu.render()
        self.assertEqual(menu.get_height(widget=True), 0)

        # Adds a button, hide it, then the height should be 0 as well
        btn = menu.add.button('hidden')
        btn.hide()
        self.assertEqual(menu.get_height(widget=True), 0)
        menu.render()

        # Get the size of the scrollarea
        sa = menu.get_scrollarea()

        sa_height = menu.get_height() - menu.get_menubar().get_height()
        sa_width = menu.get_width()
        self.assertEqual(sa.get_world_size()[0], sa_width)
        self.assertEqual(sa.get_world_size()[1], sa_height)
        rect = sa.get_view_rect()
        self.assertEqual(rect.x, 0)
        self.assertEqual(rect.y, 155)
        self.assertEqual(rect.width, sa_width)
        self.assertEqual(rect.height, sa_height)
        self.assertEqual(sa.get_hidden_width(), 0)
        self.assertEqual(sa.get_hidden_height(), 0)

        rect = sa.to_world_position(btn.get_rect())
        self.assertEqual(rect.x, 0)
        self.assertEqual(rect.y, -155)
        self.assertEqual(rect.width, btn.get_width())
        self.assertEqual(rect.height, btn.get_height())

        pos_rect = sa.to_world_position((10, 10))
        self.assertEqual(pos_rect, (10, -145))

        self.assertFalse(sa.is_scrolling())
        self.assertEqual(sa.get_menu(), menu)

        sa._on_vertical_scroll(50)
        sa._on_horizontal_scroll(50)

        # Remove the world of surface
        world = sa._world
        sa._world = None
        self.assertEqual(sa.get_world_size(), (0, 0))
        sa._world = world

        # Test collide
        event = PygameEventUtils.mouse_click(100, 100, inlist=False)
        self.assertFalse(sa.collide(btn, event))

        # Create virtual rect from button
        rect_virtual = sa.to_real_position(btn.get_rect())
        event_click_widget = PygameEventUtils.middle_rect_click(rect_virtual,
                                                                inlist=False)
        self.assertTrue(sa.collide(btn, event_click_widget))
예제 #2
0
    def test_size(self) -> None:
        """
        Test size.
        """
        if SYS_PLATFORM_OSX:
            return

        menu = MenuUtils.generic_menu(title='menu', theme=TEST_THEME.copy())
        menu.render()
        self.assertEqual(menu.get_height(widget=True), 0)

        # Adds a button, hide it, then the height should be 0 as well
        btn = menu.add.button('hidden')
        btn.hide()
        self.assertEqual(menu.get_height(widget=True), 0)
        menu.render()

        # Get the size of the scrollarea
        sa = menu.get_scrollarea()

        sa_height = menu.get_height() - menu.get_menubar().get_height()
        sa_width = menu.get_width()
        self.assertEqual(sa.get_world_size()[0], sa_width)
        self.assertEqual(sa.get_world_size()[1], sa_height)
        rect = sa.get_view_rect()
        self.assertEqual(rect.x, 0)
        self.assertEqual(rect.y, 155)
        self.assertEqual(rect.width, sa_width)
        self.assertEqual(rect.height, sa_height)
        self.assertEqual(sa.get_hidden_width(), 0)
        self.assertEqual(sa.get_hidden_height(), 0)

        rect = sa.to_world_position(btn.get_rect())
        self.assertEqual(rect.x, 0)
        self.assertEqual(rect.y, -155)
        self.assertEqual(rect.width, btn.get_width())
        self.assertEqual(rect.height, btn.get_height())

        pos_rect = sa.to_world_position((10, 10))
        self.assertEqual(pos_rect, (10, -145))

        self.assertFalse(sa.is_scrolling())
        self.assertEqual(sa.get_menu(), menu)

        sa._on_vertical_scroll(50)
        sa._on_horizontal_scroll(50)

        # Remove the world of surface
        world = sa._world
        sa._world = None
        self.assertEqual(sa.get_world_size(), (0, 0))
        sa._world = world

        # Test collide
        event = PygameEventUtils.mouse_click(100, 100, inlist=False)
        self.assertFalse(sa.collide(btn, event))

        # Create virtual rect from button
        rect_virtual = sa.to_real_position(btn.get_rect())
        event_click_widget = PygameEventUtils.middle_rect_click(rect_virtual, inlist=False)
        self.assertTrue(sa.collide(btn, event_click_widget))
        self.assertEqual(sa.get_world_rect(absolute=True),
                         pygame.Rect(0, 0, 600, 345))
        self.assertEqual(sa.get_size(), (600, 345))
        self.assertIsInstance(sa.mouse_is_over(), bool)

        # Append many items within menu, thus, scrollbars will appear
        self.assertEqual(sa.get_scrollbar_thickness(ORIENTATION_VERTICAL), 0)
        self.assertEqual(sa.get_scrollbar_thickness(ORIENTATION_HORIZONTAL), 0)

        sa._scrollbar_positions = (POSITION_NORTH, POSITION_EAST, POSITION_WEST, POSITION_SOUTH)
        self.assertEqual(sa.get_view_rect(), pygame.Rect(0, 155, 600, 345))

        for i in range(10):
            menu.add.button('b{0}'.format(i)).scale(20, 1)

        self.assertEqual(sa.get_scrollbar_thickness(ORIENTATION_VERTICAL), 20)
        self.assertEqual(sa.get_scrollbar_thickness(ORIENTATION_HORIZONTAL), 20)

        sa._on_vertical_scroll(0.5)
        sa._on_horizontal_scroll(0.5)

        self.assertEqual(sa.to_real_position((10, 10)), (10, 165))
        sa.get_view_rect()

        # Test rect
        sa = pygame_menu._scrollarea.ScrollArea(100, 100)
        self.assertEqual(sa.get_rect(to_real_position=True),
                         pygame.Rect(0, 0, 100, 100))
        self.assertEqual(sa.get_scrollbar_thickness(ORIENTATION_VERTICAL, visible=False), 0)
        self.assertEqual(sa.get_scrollbar_thickness(ORIENTATION_HORIZONTAL, visible=False), 0)
        self.assertEqual(sa.get_scrollbar_thickness(ORIENTATION_VERTICAL, visible=False), 0)
        self.assertRaises(AssertionError, lambda: sa.get_scrollbar_thickness('fake', visible=False))
    def test_textinput(self) -> None:
        """
        Test TextInput widget.
        """
        menu = MenuUtils.generic_menu()

        # Assert bad settings
        self.assertRaises(
            ValueError, lambda: menu.add.text_input('title',
                                                    input_type=pygame_menu.
                                                    locals.INPUT_FLOAT,
                                                    default='bad'))
        self.assertRaises(
            ValueError,  # Default and password cannot coexist
            lambda: menu.add.text_input('title', password=True, default='bad'))

        # Create text input widget
        textinput = menu.add.text_input('title', input_underline='_')
        textinput.set_value('new_value')  # No error
        textinput._selected = False
        textinput.draw(surface)
        textinput.select(update_menu=True)
        textinput.draw(surface)
        self.assertEqual(textinput.get_value(), 'new_value')
        textinput.clear()
        self.assertEqual(textinput.get_value(), '')

        # Create selection box
        string = 'the text'
        textinput._cursor_render = True
        textinput.set_value(string)
        textinput._select_all()
        self.assertEqual(textinput._get_selected_text(), 'the text')
        textinput.draw(surface)
        textinput._unselect_text()
        textinput.draw(surface)

        # Assert events
        textinput.update(PygameEventUtils.key(0, keydown=True, testmode=False))
        PygameEventUtils.test_widget_key_press(textinput)
        textinput.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        textinput.update(PygameEventUtils.key(pygame.K_LSHIFT, keydown=True))
        textinput.clear()

        # Type
        textinput.update(
            PygameEventUtils.key(pygame.K_t, keydown=True, char='t'))
        textinput.update(
            PygameEventUtils.key(pygame.K_e, keydown=True, char='e'))
        textinput.update(
            PygameEventUtils.key(pygame.K_s, keydown=True, char='s'))
        textinput.update(
            PygameEventUtils.key(pygame.K_t, keydown=True, char='t'))

        # Keyup
        textinput.update(PygameEventUtils.key(pygame.K_a, keyup=True,
                                              char='a'))
        self.assertEqual(textinput.get_value(), 'test')  # The text we typed

        # Ctrl events
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_c))  # copy
        textinput.update(PygameEventUtils.keydown_mod_ctrl(
            pygame.K_v))  # paste
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_z))  # undo
        self.assertEqual(textinput.get_value(), 'tes')
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_y))  # redo
        self.assertEqual(textinput.get_value(), 'test')
        textinput._select_all()
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_x))  # cut
        self.assertEqual(textinput.get_value(), '')
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_z))  # undo
        self.assertEqual(textinput.get_value(), 'test')
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_y))  # redo
        self.assertEqual(textinput.get_value(), '')
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_z))  # undo
        self.assertEqual(textinput.get_value(), 'test')

        # Test ignore ctrl events
        textinput._copy_paste_enabled = False
        self.assertFalse(
            textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_c)))
        self.assertFalse(
            textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_v)))
        max_history = textinput._max_history
        textinput._max_history = 0
        self.assertFalse(
            textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_z)))
        self.assertFalse(
            textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_y)))
        self.assertFalse(
            textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_x)))
        textinput._selection_enabled = False
        self.assertFalse(
            textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_a)))
        self.assertFalse(
            textinput.update(PygameEventUtils.keydown_mod_ctrl(
                pygame.K_r)))  # invalid

        # Reset
        textinput._copy_paste_enabled = True
        textinput._max_history = max_history
        textinput._selection_enabled = True

        # Test selection, if user selects all and types anything the selected
        # text must be destroyed
        textinput.update(PygameEventUtils.keydown_mod_ctrl(
            pygame.K_a))  # select all
        textinput._unselect_text()
        self.assertEqual(textinput._get_selected_text(), '')
        textinput._select_all()
        self.assertEqual(textinput._get_selected_text(), 'test')
        textinput._unselect_text()
        self.assertEqual(textinput._get_selected_text(), '')
        textinput.update(PygameEventUtils.keydown_mod_ctrl(pygame.K_a))
        self.assertEqual(textinput._get_selected_text(), 'test')
        textinput.update(
            PygameEventUtils.key(pygame.K_t, keydown=True, char='t'))
        textinput._select_all()
        self.assertTrue(
            textinput.update(
                PygameEventUtils.key(pygame.K_ESCAPE, keydown=True)))
        textinput._select_all()
        self.assertTrue(
            textinput.update(
                PygameEventUtils.key(pygame.K_BACKSPACE, keydown=True)))
        self.assertEqual(textinput.get_value(), '')
        textinput.set_value('t')

        # Releasing shift disable selection
        textinput._selection_active = True
        textinput.update(PygameEventUtils.key(pygame.K_LSHIFT, keyup=True))
        self.assertFalse(textinput._selection_active)

        # Arrows while selection
        textinput._select_all()
        self.assertIsNotNone(textinput._selection_surface)
        textinput.update(PygameEventUtils.key(pygame.K_LEFT, keydown=True))
        self.assertIsNone(textinput._selection_surface)
        textinput._select_all()
        self.assertIsNotNone(textinput._selection_surface)
        textinput.update(PygameEventUtils.key(pygame.K_RIGHT, keydown=True))
        self.assertIsNone(textinput._selection_surface)

        textinput._select_all()
        textinput._selection_active = True
        self.assertEqual(textinput._selection_box, [0, 1])
        textinput.update(PygameEventUtils.key(pygame.K_LEFT, keydown=True))
        self.assertEqual(textinput._selection_box, [0, 0])
        textinput._select_all()
        textinput._selection_active = True
        textinput.update(PygameEventUtils.key(pygame.K_RIGHT, keydown=True))
        self.assertEqual(textinput._selection_box, [0, 1])

        # Remove while selection
        textinput._select_all()
        textinput.update(PygameEventUtils.key(pygame.K_DELETE, keydown=True))
        self.assertEqual(textinput.get_value(), '')
        textinput.set_value('t')

        # Now the value must be t
        self.assertEqual(textinput._get_selected_text(), '')
        self.assertEqual(textinput.get_value(), 't')

        # Test readonly
        textinput.update(
            PygameEventUtils.key(pygame.K_t, keydown=True, char='k'))
        self.assertEqual(textinput.get_value(), 'tk')
        textinput.readonly = True
        textinput.update(
            PygameEventUtils.key(pygame.K_t, keydown=True, char='k'))
        self.assertEqual(textinput.get_value(), 'tk')
        textinput.readonly = False

        # Test keyup
        self.assertIn(pygame.K_t, textinput._keyrepeat_counters.keys())
        self.assertFalse(
            textinput.update(
                PygameEventUtils.key(pygame.K_t, keyup=True, char='1')))
        self.assertNotIn(pygame.K_t, textinput._keyrepeat_counters.keys())

        # Test tab
        self.assertEqual(textinput._tab_size, 4)
        textinput.update(PygameEventUtils.key(pygame.K_TAB, keydown=True))
        self.assertEqual(textinput.get_value(), 'tk    ')

        # Test invalid unicode
        self.assertFalse(
            textinput.update(PygameEventUtils.key(pygame.K_1, keydown=True)))

        # Up/Down disable active status
        textinput.active = True
        textinput.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        self.assertFalse(textinput.active)
        textinput.active = True
        textinput.update(PygameEventUtils.key(ctrl.KEY_MOVE_DOWN,
                                              keydown=True))
        self.assertFalse(textinput.active)
        textinput.active = True
        self.assertTrue(
            textinput.update(
                PygameEventUtils.key(pygame.K_ESCAPE, keydown=True)))
        self.assertFalse(textinput.active)

        # Test mouse
        textinput._selected = True
        textinput._selection_time = 0
        textinput.update(PygameEventUtils.middle_rect_click(textinput))
        self.assertTrue(textinput._cursor_visible)
        textinput._select_all()
        textinput._selection_active = True
        self.assertEqual(textinput._cursor_position, 6)
        self.assertEqual(textinput._selection_box, [0, 6])
        textinput.update(
            PygameEventUtils.middle_rect_click(textinput,
                                               evtype=pygame.MOUSEBUTTONDOWN))
        self.assertEqual(textinput._selection_box, [0, 0])

        # Check click pos
        textinput._check_mouse_collide_input(
            PygameEventUtils.middle_rect_click(textinput)[0].pos)
        self.assertEqual(textinput._cursor_position, 6)

        # Test touch
        textinput._cursor_position = 0
        textinput._check_touch_collide_input(
            PygameEventUtils.middle_rect_click(textinput)[0].pos)
        self.assertEqual(textinput._cursor_position, 6)

        # Update mouse
        for i in range(50):
            textinput.update(
                PygameEventUtils.key(pygame.K_t, keydown=True, char='t'))
        textinput._update_cursor_mouse(50)
        textinput._cursor_render = True
        textinput._render_cursor()

        # Test multiple are selected
        menu.add.text_input('title', password=True,
                            input_underline='_').select()
        self.assertRaises(
            pygame_menu.menu._MenuMultipleSelectedWidgetsException,
            lambda: menu.draw(surface))
        textinput.clear()
        textinput.select(update_menu=True)
        menu.draw(surface)

        # Clear the menu
        self.assertEqual(menu._stats.removed_widgets, 0)
        self.assertEqual(textinput.get_menu(), menu)
        menu.clear()
        self.assertIsNone(textinput.get_menu())
        self.assertEqual(menu._stats.removed_widgets, 3)
        menu.add.generic_widget(textinput)
        self.assertEqual(textinput.get_menu(), menu)
        menu.clear()
        self.assertEqual(menu._stats.removed_widgets, 4)
    def test_scrollbar(self) -> None:
        """
        Test ScrollBar widget.
        """
        screen_size = surface.get_size()
        world = pygame.Surface((WINDOW_SIZE[0] * 2, WINDOW_SIZE[1] * 3))
        world.fill((200, 200, 200))
        for x in range(100, world.get_width(), 200):
            for y in range(100, world.get_height(), 200):
                pygame.draw.circle(world, (225, 34, 43), (x, y), 100, 10)

        # Vertical right scrollbar
        thick = 80
        length = screen_size[1]
        world_range = (50, world.get_height())
        x, y = screen_size[0] - thick, 0

        sb = ScrollBar(
            length, world_range, 'sb2', ORIENTATION_VERTICAL,
            slider_pad=2,
            slider_color=(210, 120, 200),
            page_ctrl_thick=thick,
            page_ctrl_color=(235, 235, 230)
        )
        self.assertEqual(sb.get_thickness(), 80)
        self.assertIsNone(sb.get_scrollarea())

        sb.set_shadow(color=(245, 245, 245), position=POSITION_SOUTHEAST)
        self.assertFalse(sb._font_shadow)

        sb.set_position(x, y)

        self.assertEqual(sb._orientation, 1)
        self.assertEqual(sb.get_orientation(), ORIENTATION_VERTICAL)
        self.assertEqual(sb.get_minimum(), world_range[0])
        self.assertEqual(sb.get_maximum(), world_range[1])

        sb.set_value(80)
        self.assertAlmostEqual(sb.get_value(), 80, delta=2)  # Scaling delta

        sb.update(PygameEventUtils.mouse_click(x + thick / 2, y + 2, evtype=pygame.MOUSEBUTTONDOWN))
        self.assertEqual(sb.get_value(), 50)
        self.assertEqual(sb.get_value_percentage(), 0)

        sb.set_page_step(length)
        self.assertAlmostEqual(sb.get_page_step(), length, delta=2)  # Scaling delta

        sb.draw(surface)

        # Test events
        sb.update(PygameEventUtils.key(pygame.K_PAGEDOWN, keydown=True))
        self.assertEqual(sb.get_value(), 964)
        sb.update(PygameEventUtils.key(pygame.K_PAGEUP, keydown=True))
        self.assertEqual(sb.get_value(), 50)
        self.assertEqual(sb._last_mouse_pos, (-1, -1))
        sb.update(PygameEventUtils.enter_window())
        self.assertEqual(sb._last_mouse_pos, (-1, -1))
        sb.update(PygameEventUtils.leave_window())
        self.assertEqual(sb._last_mouse_pos, pygame.mouse.get_pos())
        self.assertFalse(sb.scrolling)
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), evtype=pygame.MOUSEBUTTONDOWN))
        self.assertTrue(sb.scrolling)
        sb.update(PygameEventUtils.mouse_click(1, 1))
        self.assertFalse(sb.scrolling)
        self.assertEqual(sb.get_value(), 50)
        sb.update(PygameEventUtils.middle_rect_click(sb.get_rect(to_absolute_position=True),
                                                     evtype=pygame.MOUSEBUTTONDOWN))
        self.assertEqual(sb.get_value(), 964)
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), evtype=pygame.MOUSEBUTTONDOWN))
        self.assertTrue(sb.scrolling)
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), button=4,
                                                     evtype=pygame.MOUSEBUTTONDOWN))
        self.assertEqual(sb.get_value(), 875)
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), button=5,
                                                     evtype=pygame.MOUSEBUTTONDOWN))
        self.assertEqual(sb.get_value(), 964)
        self.assertEqual(sb.get_value_percentage(), 0.522)

        # Test mouse motion while scrolling
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), button=5, delta=(0, 50), rel=(0, 10),
                                                     evtype=pygame.MOUSEMOTION))
        self.assertEqual(sb.get_value_percentage(), 0.547)
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), button=5, delta=(0, 50), rel=(0, -10),
                                                     evtype=pygame.MOUSEMOTION))
        self.assertEqual(sb.get_value_percentage(), 0.522)
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), button=5, delta=(0, 50), rel=(0, 999),
                                                     evtype=pygame.MOUSEMOTION))
        self.assertEqual(sb.get_value_percentage(), 1)
        sb.readonly = True
        self.assertFalse(sb.update([]))

        # Ignore events if mouse outside the region
        sb.update(PygameEventUtils.middle_rect_click(sb.get_slider_rect(), button=5, delta=(0, 999), rel=(0, -10),
                                                     evtype=pygame.MOUSEMOTION))
        self.assertIn(sb.get_value_percentage(), (0.976, 1))

        # Test remove onreturn
        sb = ScrollBar(length, world_range, 'sb', ORIENTATION_VERTICAL, onreturn=-1)
        self.assertIsNone(sb._onreturn)
        self.assertTrue(sb._kwargs.get('onreturn', 0))

        # Scrollbar ignores scaling
        self.assertRaises(WidgetTransformationNotImplemented, lambda: sb.scale(2, 2))
        self.assertFalse(sb._scale[0])
        self.assertRaises(WidgetTransformationNotImplemented, lambda: sb.resize(2, 2))
        self.assertFalse(sb._scale[0])
        self.assertRaises(WidgetTransformationNotImplemented, lambda: sb.set_max_width(10))
        self.assertIsNone(sb._max_width[0])
        self.assertRaises(WidgetTransformationNotImplemented, lambda: sb.set_max_height(10))
        self.assertIsNone(sb._max_height[0])
        sb._apply_font()
        sb.set_padding(10)
        self.assertEqual(sb._padding[0], 0)
        self.assertRaises(WidgetTransformationNotImplemented, lambda: sb.rotate(10))
        self.assertEqual(sb._angle, 0)
        self.assertRaises(WidgetTransformationNotImplemented, lambda: sb.flip(True, True))
        self.assertFalse(sb._flip[0])
        self.assertFalse(sb._flip[1])

        # Set minimum
        sb.set_minimum(0.5 * sb._values_range[1])

        # Test hide
        sb._mouseover = True
        sb.hide()
예제 #5
0
    def test_double(self) -> None:
        """
        Test double range slider.
        """
        menu = MenuUtils.generic_menu()

        # Double slider
        slider = pygame_menu.widgets.RangeSlider(
            'Range',
            range_text_value_tick_number=3,
            default_value=(0.2, 1.0),
            slider_text_value_font=pygame_menu.font.FONT_BEBAS,
            range_text_value_font=pygame_menu.font.FONT_8BIT,
            slider_text_value_triangle=False)
        slider._slider_text_value_vmargin = -2
        menu.add.generic_widget(slider, True)
        slider.draw(surface)
        slider.draw_after_if_selected(surface)

        self.assertEqual(slider.get_value(), (0.2, 1.0))
        self.assertRaises(AssertionError, lambda: slider.set_value(0.2))
        self.assertRaises(AssertionError, lambda: slider.set_value((0.2, 0.2)))
        self.assertRaises(AssertionError, lambda: slider.set_value((1.0, 0.2)))
        self.assertRaises(AssertionError, lambda: slider.set_value(
            (0.2, 0.5, 1.0)))

        # Test slider selection
        self.assertTrue(slider._slider_selected[0])
        self.assertTrue(
            slider.update(PygameEventUtils.key(ctrl.KEY_TAB, keydown=True)))
        self.assertFalse(slider._slider_selected[0])
        slider.draw(surface)
        slider.draw_after_if_selected(surface)

        # Test click sliders
        slider_rect = slider._get_slider_inflate_rect(0, to_real_position=True)
        self.assertTrue(
            slider.update(
                PygameEventUtils.middle_rect_click(
                    slider_rect, evtype=pygame.MOUSEBUTTONDOWN)))
        self.assertTrue(slider._slider_selected[0])
        self.assertFalse(
            slider.update(  # Slider already selected
                PygameEventUtils.middle_rect_click(
                    slider_rect, evtype=pygame.MOUSEBUTTONDOWN)))

        # Click if sliders are colliding
        slider.set_value((0.5, 0.50000001))
        slider_rect = slider._get_slider_inflate_rect(1, to_real_position=True)
        self.assertFalse(
            slider.update(
                PygameEventUtils.middle_rect_click(
                    slider_rect, evtype=pygame.MOUSEBUTTONDOWN)))
        self.assertTrue(slider._slider_selected[0])
        slider.set_value((0.5, 0.7))
        slider_rect = slider._get_slider_inflate_rect(1, to_real_position=True)
        self.assertTrue(
            slider.update(
                PygameEventUtils.middle_rect_click(
                    slider_rect, evtype=pygame.MOUSEBUTTONDOWN)))
        self.assertTrue(slider._slider_selected[1])

        # Test left slider
        pos = slider._test_get_pos_value(0.5)
        pos2 = slider._test_get_pos_value(0.6)
        slider_rect = slider._get_slider_inflate_rect(0, to_real_position=True)
        self.assertEqual(slider_rect, pygame.Rect(344, 311, 15, 28))
        slider.update(
            PygameEventUtils.middle_rect_click(slider_rect,
                                               evtype=pygame.MOUSEBUTTONDOWN))
        self.assertTrue(slider._slider_selected[0])
        self.assertTrue(slider._scrolling)
        self.assertTrue(slider._selected_mouse)
        dx = pos[0] - pos2[0]
        slider.update(
            PygameEventUtils.mouse_motion(slider_rect,
                                          rel=(-dx, pos[1]),
                                          update_mouse=True))
        self.assertEqual(slider.get_value(), (0.6, 0.7))

        # As slider moved, ignore this
        slider.update(
            PygameEventUtils.mouse_motion(slider_rect,
                                          rel=(-dx, pos[1]),
                                          update_mouse=True))
        self.assertEqual(slider.get_value(), (0.6, 0.7))
        self.assertTrue(slider._scrolling)
        self.assertTrue(slider._slider_selected[0])

        # Move to 0
        self.assertTrue(slider._selected_mouse)
        pos = slider._test_get_pos_value(0)
        pos2 = slider._test_get_pos_value(0.6)
        dx = pos[0] - pos2[0]
        slider.update(
            PygameEventUtils.mouse_motion(slider_rect,
                                          rel=(dx, pos[1]),
                                          update_mouse=True))
        self.assertEqual(slider.get_value(), (0, 0.7))

        # Move more than 0.7
        pos = slider._test_get_pos_value(0)
        pos2 = slider._test_get_pos_value(0.75)
        slider_rect = slider._get_slider_inflate_rect(0, to_real_position=True)
        dx = pos[0] - pos2[0]
        slider.update(
            PygameEventUtils.mouse_motion(slider_rect,
                                          rel=(-dx, pos[1]),
                                          update_mouse=True))
        self.assertEqual(slider.get_value(), (0, 0.7))

        # Move to 0.7 - eps
        pos = slider._test_get_pos_value(0)
        pos2 = slider._test_get_pos_value(0.7 - 1e-6)
        slider_rect = slider._get_slider_inflate_rect(0, to_real_position=True)
        dx = pos[0] - pos2[0]
        slider.update(
            PygameEventUtils.mouse_motion(slider_rect,
                                          rel=(-dx, pos[1]),
                                          update_mouse=True))
        self.assertAlmostEqual(slider.get_value()[0], 0.7 - 1e-7, places=1)

        # Ignore if move 0.7 + eps
        self.assertFalse(
            slider.update(
                PygameEventUtils.mouse_motion(slider_rect,
                                              rel=(1, pos[1]),
                                              update_mouse=True)))

        # Change to right
        slider_rect = slider._get_slider_inflate_rect(1, to_real_position=True)
        self.assertTrue(
            slider.update(PygameEventUtils.key(ctrl.KEY_TAB, keydown=True)))
        pos = slider._test_get_pos_value(0.7)
        pos2 = slider._test_get_pos_value(0.8)
        dx = pos[0] - pos2[0]
        self.assertFalse(
            slider.update(
                PygameEventUtils.mouse_motion(slider_rect,
                                              rel=(-1, pos[1]),
                                              update_mouse=True)))
        self.assertTrue(
            slider.update(
                PygameEventUtils.mouse_motion(slider_rect,
                                              rel=(-dx, pos[1]),
                                              update_mouse=True)))
        self.assertAlmostEqual(slider.get_value()[1], 0.8)

        # Test left/right
        slider.set_value((0.7, 0.8))
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        slider.set_value((0.7, 0.8))  # Ignored
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True))
        slider.set_value((0.7, 0.9))
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True))
        slider.set_value((0.7, 1.0))
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True))
        slider.set_value((0.7, 1.0))
        slider.set_value((0.7, 0.8))
        slider.update(PygameEventUtils.key(ctrl.KEY_TAB, keydown=True))
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True))
        slider.set_value((0.7, 0.8))
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        slider.set_value((0.6, 0.8))

        # Reset value
        slider.reset_value()
        self.assertEqual(slider.get_value(), (0.2, 1.0))
예제 #6
0
    def test_single_rangeslider(self) -> None:
        """
        Test single range slider.
        """
        menu = MenuUtils.generic_menu()

        # Single slider
        slider = pygame_menu.widgets.RangeSlider('Range S')

        test = [0, 0]

        def onchange(x: float) -> None:
            """
            Change slider.
            """
            # noinspection PyTypeChecker
            test[0] = x

        def onreturn(x: float) -> None:
            """
            Return slider.
            """
            # noinspection PyTypeChecker
            test[1] = x

        slider.set_onchange(onchange)
        slider.set_onreturn(onreturn)

        menu.add.generic_widget(slider, True)
        self.assertEqual(slider.get_value(), 0)
        slider.set_value(0.5)
        self.assertEqual(slider.get_value(), 0.5)
        self.assertEqual(slider._value, [0.5, 0])

        self.assertEqual(test[0], 0)
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        self.assertEqual(test[0], 0.4)
        self.assertEqual(slider.get_value(), 0.4)
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        self.assertAlmostEqual(slider.get_value(), 0.3)
        for _ in range(10):
            slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        self.assertEqual(slider.get_value(), 0)
        self.assertTrue(
            slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True)))
        self.assertEqual(slider.get_value(), 0.1)
        self.assertEqual(test[1], 0)
        slider.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertEqual(test[1], 0.1)

        # Ignore invalid key
        self.assertFalse(
            slider.update(
                PygameEventUtils.key(ctrl.KEY_LEFT,
                                     keydown=True,
                                     testmode=False)))

        # Ignore for readonly
        slider.draw(surface)
        slider.draw_after_if_selected(surface)
        slider.readonly = True
        self.assertFalse(
            slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True)))
        self.assertEqual(slider.get_value(), 0.1)
        slider._update_value(0)
        self.assertEqual(slider.get_value(), 0.1)
        slider.readonly = False

        # Test invalid values
        self.assertRaises(AssertionError, lambda: slider.set_value(-1))
        self.assertRaises(AssertionError, lambda: slider.set_value([0.4, 0.5]))

        # Test invalid transforms
        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: slider.rotate())
        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: slider.flip())
        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: slider.scale())
        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: slider.resize())
        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: slider.set_max_width())
        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: slider.set_max_height())

        # Test mouse click
        self.assertFalse(slider._selected_mouse)
        pos = slider._test_get_pos_value(0.5)
        slider.update(
            PygameEventUtils.middle_rect_click(pos,
                                               evtype=pygame.MOUSEBUTTONDOWN))
        self.assertEqual(slider.get_value(), 0.1)
        self.assertTrue(slider._selected_mouse)
        self.assertFalse(slider._scrolling)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertFalse(slider._scrolling)
        self.assertEqual(slider.get_value(), 0.5)
        self.assertFalse(slider._selected_mouse)

        # Mouse click out of range
        slider._selected_mouse = True
        pos = slider._test_get_pos_value(1, dx=100)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertEqual(slider.get_value(), 0.5)
        self.assertFalse(slider._selected_mouse)

        slider._selected_mouse = True
        pos = slider._test_get_pos_value(0, dx=-100)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertEqual(slider.get_value(), 0.5)
        self.assertFalse(slider._selected_mouse)

        # Test extremes
        slider._selected_mouse = True
        pos = slider._test_get_pos_value(0)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertEqual(slider.get_value(), 0)
        slider._selected_mouse = True
        pos = slider._test_get_pos_value(1)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertEqual(slider.get_value(), 1)

        # Scroll to 0.5
        pos2 = slider._test_get_pos_value(0.5)
        self.assertFalse(slider._scrolling)
        slider_rect = slider._get_slider_inflate_rect(0, to_real_position=True)
        slider.update(
            PygameEventUtils.middle_rect_click(slider_rect,
                                               evtype=pygame.MOUSEBUTTONDOWN))
        self.assertTrue(slider._scrolling)
        self.assertTrue(slider._selected_mouse)
        dx = pos[0] - pos2[0]
        slider.update(
            PygameEventUtils.mouse_motion(slider_rect,
                                          rel=(-dx, pos[1]),
                                          update_mouse=True))
        self.assertEqual(slider.get_value(), 0.5)
        self.assertTrue(slider._scrolling)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertFalse(slider._scrolling)

        # Check invalid constructor for single slider
        self.assertRaises(
            AssertionError, lambda: pygame_menu.widgets.RangeSlider(
                'Range S', default_value=2))
        self.assertRaises(
            AssertionError, lambda: pygame_menu.widgets.RangeSlider(
                'Range S', default_value=1, range_values=[1, 0]))
        self.assertRaises(
            AssertionError, lambda: pygame_menu.widgets.RangeSlider(
                'Range S', default_value=1, range_values=[1, 1]))
        self.assertRaises(
            AssertionError,
            lambda: pygame_menu.widgets.RangeSlider('Range S',
                                                    default_value='a'))

        # Ignore tabs
        self.assertFalse(
            slider.update(PygameEventUtils.key(ctrl.KEY_TAB, keydown=True)))

        # Check LEFT key in repeat
        self.assertIn(ctrl.KEY_RIGHT, slider._keyrepeat_counters.keys())
        self.assertEqual(slider.get_value(), 0.5)

        # Make left repeat
        slider._keyrepeat_counters[ctrl.KEY_RIGHT] += 1e4
        self.assertEqual(len(slider._events), 0)
        self.assertFalse(slider.update([]))
        self.assertEqual(len(slider._events), 1)
        self.assertFalse(slider.update(
            []))  # As key is not pressed, event continues
        self.assertEqual(len(slider._events), 0)

        # Keyup, removes counters
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keyup=True))
        self.assertNotIn(ctrl.KEY_RIGHT, slider._keyrepeat_counters.keys())
        self.assertFalse(hasattr(slider, '_range_box'))

        # Single slider with range box
        slider_rb = pygame_menu.widgets.RangeSlider(
            'Range', range_box_single_slider=True)
        menu.add.generic_widget(slider_rb, True)
        slider_rb.draw(surface)
        self.assertTrue(hasattr(slider_rb, '_range_box'))
        self.assertEqual(slider_rb._range_box.get_width(), 0)
        slider_rb.set_value(1)
        self.assertEqual(slider_rb._range_box.get_width(), 150)
예제 #7
0
    def test_single_discrete(self) -> None:
        """
        Test single range slider with discrete values.
        """
        menu = MenuUtils.generic_menu()

        # Single slider with discrete values
        rv = [0, 1, 2, 3, 4, 5]
        slider = pygame_menu.widgets.RangeSlider('Range', range_values=rv)
        menu.add.generic_widget(slider, True)

        self.assertRaises(
            AssertionError, lambda: pygame_menu.widgets.RangeSlider(
                'Range', default_value=0.5, range_values=rv))
        self.assertRaises(
            AssertionError, lambda: pygame_menu.widgets.RangeSlider(
                'Range', default_value=-1, range_values=rv))

        self.assertRaises(AssertionError, lambda: slider.set_value(-1))
        self.assertRaises(AssertionError, lambda: slider.set_value([0, 1]))
        self.assertRaises(AssertionError, lambda: slider.set_value((0, 1)))

        # Test key events
        self.assertFalse(
            slider.update(PygameEventUtils.key(ctrl.KEY_TAB, keydown=True)))
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True))
        self.assertEqual(slider.get_value(), 1)
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True))
        self.assertEqual(slider.get_value(), 2)
        slider._increment = 0
        slider.update(PygameEventUtils.key(ctrl.KEY_RIGHT, keydown=True))
        self.assertEqual(slider.get_value(), 3)
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        self.assertEqual(slider.get_value(), 2)
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        self.assertEqual(slider.get_value(), 1)
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        self.assertEqual(slider.get_value(), 0)
        slider.update(PygameEventUtils.key(ctrl.KEY_LEFT, keydown=True))
        self.assertEqual(slider.get_value(), 0)

        # Test click mouse
        slider._selected_mouse = True
        pos = slider._test_get_pos_value(2)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertEqual(slider.get_value(), 2)
        self.assertFalse(slider._selected_mouse)

        # Test invalid click
        slider._selected_mouse = True
        pos = slider._test_get_pos_value(2, dx=1000)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertEqual(slider.get_value(), 2)
        self.assertFalse(slider._selected_mouse)

        # Scroll to 4
        pos = slider._test_get_pos_value(2)
        pos2 = slider._test_get_pos_value(4)
        self.assertFalse(slider._scrolling)
        slider_rect = slider._get_slider_inflate_rect(0, to_real_position=True)
        slider.update(
            PygameEventUtils.middle_rect_click(slider_rect,
                                               evtype=pygame.MOUSEBUTTONDOWN))
        self.assertTrue(slider._scrolling)
        self.assertTrue(slider._selected_mouse)
        dx = pos[0] - pos2[0]
        slider.update(
            PygameEventUtils.mouse_motion(slider_rect,
                                          rel=(-dx, pos[1]),
                                          update_mouse=True))
        self.assertEqual(slider.get_value(), 4)
        self.assertTrue(slider._scrolling)
        slider.update(PygameEventUtils.middle_rect_click(pos))
        self.assertFalse(slider._scrolling)

        # Back to 2
        slider.set_value(2)

        # Invalid scrolling if clicked outside the slider
        slider.update(
            PygameEventUtils.middle_rect_click(slider._test_get_pos_value(0),
                                               evtype=pygame.MOUSEBUTTONDOWN))
        self.assertFalse(slider._scrolling)
예제 #8
0
    def test_menubar(self) -> None:
        """
        Test menubar widget.
        """
        menu = MenuUtils.generic_menu()
        for mode in (MENUBAR_STYLE_ADAPTIVE, MENUBAR_STYLE_NONE,
                     MENUBAR_STYLE_SIMPLE, MENUBAR_STYLE_UNDERLINE,
                     MENUBAR_STYLE_UNDERLINE_TITLE, MENUBAR_STYLE_TITLE_ONLY,
                     MENUBAR_STYLE_TITLE_ONLY_DIAGONAL):
            mb = MenuBar('Menu', 500, (0, 0, 0), back_box=True, mode=mode)
            menu.add.generic_widget(mb)
        mb = MenuBar('Menu', 500, (0, 0, 0), back_box=True)
        mb.set_backbox_border_width(2)
        self.assertRaises(AssertionError,
                          lambda: mb.set_backbox_border_width(1.5))
        self.assertRaises(AssertionError,
                          lambda: mb.set_backbox_border_width(0))
        self.assertRaises(AssertionError,
                          lambda: mb.set_backbox_border_width(-1))
        self.assertEqual(mb._backbox_border_width, 2)
        menu.draw(surface)
        menu.disable()

        # Test unknown mode
        mb = MenuBar('Menu', 500, (0, 0, 0), back_box=True, mode='unknown')
        mb.set_menu(menu)
        self.assertRaises(ValueError, lambda: mb._render())

        # Check margins
        mb = MenuBar('Menu',
                     500, (0, 0, 0),
                     back_box=True,
                     mode=MENUBAR_STYLE_ADAPTIVE)
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 0)))
        mb.set_menu(menu)
        mb._render()
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTHWEST),
                         (0, (0, 0)))

        # Test displacements
        theme = pygame_menu.themes.THEME_DEFAULT.copy()
        theme.title_bar_style = MENUBAR_STYLE_TITLE_ONLY
        menu = MenuUtils.generic_menu(theme=theme, title='my title')
        mb = menu.get_menubar()
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (-55, (0, 55)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 55)))

        # Test with close button
        menu = MenuUtils.generic_menu(theme=theme,
                                      title='my title',
                                      onclose=pygame_menu.events.CLOSE,
                                      touchscreen=True)
        theme.widget_border_inflate = 0
        mb = menu.get_menubar()
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (-33, (0, 33)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (-55, (0, 55)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 55)))

        # Hide the title, and check
        mb.hide()
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 0)))

        mb.show()
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (-33, (0, 33)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (-55, (0, 55)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 55)))

        # Floating
        mb.set_float(True)
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 0)))

        mb.set_float(False)
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (-33, (0, 33)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (-55, (0, 55)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 55)))

        # Fixed
        mb.fixed = False
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_SOUTH),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_EAST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_WEST),
                         (0, (0, 0)))
        self.assertEqual(mb.get_scrollbar_style_change(POSITION_NORTH),
                         (0, (0, 0)))

        # Test menubar
        self.assertFalse(
            mb.update(PygameEventUtils.middle_rect_click(mb._rect)))
        self.assertTrue(
            mb.update(PygameEventUtils.middle_rect_click(mb._backbox_rect)))
        self.assertTrue(
            mb.update(
                PygameEventUtils.middle_rect_click(mb._backbox_rect,
                                                   evtype=pygame.FINGERUP,
                                                   menu=menu)))
        self.assertFalse(
            mb.update(
                PygameEventUtils.middle_rect_click(
                    mb._backbox_rect, evtype=pygame.MOUSEBUTTONDOWN)))
        self.assertTrue(
            mb.update(PygameEventUtils.joy_button(ctrl.JOY_BUTTON_BACK)))
        mb.readonly = True
        self.assertFalse(
            mb.update(PygameEventUtils.joy_button(ctrl.JOY_BUTTON_BACK)))
        mb.readonly = False

        # Test none methods
        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: mb.rotate(10))
        self.assertEqual(mb._angle, 0)

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: mb.resize(10, 10))
        self.assertFalse(mb._scale[0])
        self.assertEqual(mb._scale[1], 1)
        self.assertEqual(mb._scale[2], 1)

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: mb.scale(100, 100))
        self.assertFalse(mb._scale[0])
        self.assertEqual(mb._scale[1], 1)
        self.assertEqual(mb._scale[2], 1)

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: mb.flip(True, True))
        self.assertFalse(mb._flip[0])
        self.assertFalse(mb._flip[1])

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: mb.set_max_width(100))
        self.assertIsNone(mb._max_width[0])

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: mb.set_max_height(100))
        self.assertIsNone(mb._max_height[0])

        # Ignore others
        mb.set_padding()
        mb.set_border()
        mb.set_selection_effect()
        menu.add.button('nice')
예제 #9
0
    def test_dropselect_multiple(self) -> None:
        """
        Test dropselect multiple widget.
        """
        theme = pygame_menu.themes.THEME_DEFAULT.copy()
        theme.widget_font_size = 25
        menu = MenuUtils.generic_menu(mouse_motion_selection=True, theme=theme)
        items = [('This is a really long selection item', 1), ('epic', 2)]
        for i in range(10):
            items.append(('item{}'.format(i + 1), i + 1))
        # noinspection SpellCheckingInspection
        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 if not SYS_PLATFORM_OSX else 226)

        # Check drop is empty
        self.assertEqual(drop.get_value(), ([], []))
        self.assertEqual(drop.get_index(), [])
        self.assertEqual(drop._get_current_selected_text(), 'Select an option')

        # Check events
        self.assertFalse(drop.active)
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertTrue(drop.active)
        self.assertEqual(drop._index, -1)
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY,
                                         keydown=True))  # Index is -1
        self.assertFalse(drop.active)
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        self.assertEqual(drop._index, 0)
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        self.assertEqual(drop._index, 1)

        # Apply on current
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertEqual(drop.get_value(), ([('epic', 2)], [1]))
        self.assertEqual(drop.get_index(), [1])
        self.assertEqual(drop._get_current_selected_text(), '1 selected')
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertEqual(drop.get_value(), ([('epic', 2),
                                             ('item2', 2)], [1, 3]))
        self.assertEqual(drop._get_current_selected_text(), '2 selected')

        # Change selection type
        drop._selection_placeholder_format = DROPSELECT_MULTIPLE_SFORMAT_LIST_COMMA
        self.assertEqual(drop._get_current_selected_text(),
                         'epic,item2 selected')
        drop._selection_placeholder_format = DROPSELECT_MULTIPLE_SFORMAT_LIST_HYPHEN
        self.assertEqual(drop._get_current_selected_text(),
                         'epic-item2 selected')
        drop._selection_placeholder_format = '+'
        self.assertEqual(drop._get_current_selected_text(),
                         'epic+item2 selected')

        def format_string_list(items_list) -> str:
            """
            Receives the items list string and returns a function.

            :param items_list: Items list
            :return: Join string
            """
            if len(items_list) == 1:
                return items_list[0]
            elif len(items_list) == 2:
                return items_list[0] + ' and ' + items_list[1]
            return 'overflow'

        drop._selection_placeholder_format = format_string_list
        self.assertEqual(drop._get_current_selected_text(),
                         'epic and item2 selected')

        # Invalid format
        drop._selection_placeholder_format = 1
        self.assertRaises(ValueError,
                          lambda: drop._get_current_selected_text())
        drop._selection_placeholder_format = lambda: print('nice')
        self.assertRaises(ValueError,
                          lambda: drop._get_current_selected_text())
        drop._selection_placeholder_format = lambda x: 1
        self.assertRaises(AssertionError,
                          lambda: drop._get_current_selected_text())

        # Back to default
        drop._selection_placeholder_format = DROPSELECT_MULTIPLE_SFORMAT_TOTAL

        # Click item 2, this should unselect
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.middle_rect_click(
            drop._option_buttons[3]))
        self.assertEqual(drop.get_value(), ([('epic', 2)], [1]))
        self.assertEqual(drop._get_current_selected_text(), '1 selected')
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        self.assertEqual(drop._index, 4)
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertEqual(drop.get_value(), ([('epic', 2),
                                             ('item3', 3)], [1, 4]))
        self.assertEqual(drop._get_current_selected_text(), '2 selected')

        # Close
        drop.update(PygameEventUtils.key(pygame.K_ESCAPE, keydown=True))
        self.assertFalse(drop.active)
        self.assertEqual(drop.get_value(), ([('epic', 2),
                                             ('item3', 3)], [1, 4]))
        self.assertEqual(drop._get_current_selected_text(), '2 selected')

        # Change drop selected text method

        # Set max limit
        drop._max_selected = 3
        self.assertEqual(drop.get_total_selected(), 2)
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertEqual(drop.get_total_selected(), 3)
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertEqual(drop.get_total_selected(), 3)  # Limit reached
        self.assertEqual(drop.get_value(), ([('epic', 2), ('item3', 3),
                                             ('item4', 4)], [1, 4, 5]))
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_DOWN, keydown=True))
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY,
                                         keydown=True))  # Unselect previous
        self.assertEqual(drop.get_total_selected(), 2)  # Limit reached
        self.assertEqual(drop.get_value(), ([('epic', 2),
                                             ('item3', 3)], [1, 4]))

        # Update elements
        drop.update_items([('This is a really long selection item', 1),
                           ('epic', 2)])
        self.assertEqual(drop.get_value(), ([], []))
        self.assertEqual(drop._get_current_selected_text(), 'Select an option')
        drop.set_value(1, process_index=True)
        self.assertEqual(drop.get_value(), ([('epic', 2)], [1]))
        drop.set_value('This is a really long selection item',
                       process_index=True)
        self.assertEqual(drop.get_value(),
                         ([('This is a really long selection item', 1),
                           ('epic', 2)], [0, 1]))
        self.assertEqual(drop._get_current_selected_text(), '2 selected')
        drop.set_default_value(1)
        self.assertEqual(drop.get_value(), ([('epic', 2)], [1]))
        self.assertEqual(drop._get_current_selected_text(), '1 selected')

        # 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 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])
        self.assertEqual(drop2._get_current_selected_text(), 'nice 1')
        drop2.set_value(0, process_index=True)
        self.assertEqual(drop2.get_index(), [1])
        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()
        self.assertEqual(drop2._get_current_selected_text(), 'epic')
        self.assertEqual(drop2._default_value, [])
        self.assertEqual(drop2._index, -1)
        self.assertEqual(drop2.get_index(), [])
        self.assertNotEqual(id(drop2._default_value),
                            id(drop2._selected_indices))

        menu.select_widget(drop2)
        self.assertTrue(
            drop2.update(PygameEventUtils.key(pygame.K_TAB, keydown=True)))

        # Test hide
        self.assertTrue(drop2._drop_frame.is_visible())
        self.assertTrue(drop2.active)
        drop2.hide()  # Hiding selects the other widget
        self.assertEqual(menu.get_selected_widget(), drop)
        self.assertFalse(drop2._drop_frame.is_visible())
        self.assertFalse(drop2.active)
        drop2.show()
        self.assertFalse(drop2._drop_frame.is_visible())
        self.assertFalse(drop2.active)
        self.assertEqual(menu.get_selected_widget(), drop)
        menu.select_widget(drop2)
        drop2._toggle_drop()
        self.assertTrue(drop2.active)
        self.assertTrue(drop2._drop_frame.is_visible())

        # Test change
        test = [-1]

        def onchange(value) -> None:
            """
            Test onchange.
            """
            test[0] = value[1]

        drop2.set_onchange(onchange)

        # Pick any option
        menu.render()
        self.assertEqual(test, [-1])
        drop2._option_buttons[0].apply()
        self.assertEqual(test[0], [0])
        drop2._option_buttons[0].apply()
        self.assertEqual(test[0], [])
        drop2._option_buttons[0].apply()
        drop2._option_buttons[1].apply()
        self.assertEqual(test[0], [0])  # As max selected is only 1
        drop2._max_selected = 2
        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)

        # Test format option from manager
        menu._theme.widget_background_inflate_to_selection = True
        menu._theme.widget_background_inflate = 0
        # menu._theme.widget_border_inflate = 0
        menu._theme.widget_margin = 0
        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,
            selection_placeholder_format=lambda x: 'not EPIC')
        self.assertEqual(drop2._get_current_selected_text(), 'epic')
        drop2.set_value('epic', process_index=True)
        self.assertEqual(drop2._get_current_selected_text(), 'nice not EPIC')
        self.assertEqual(drop2.get_margin(), (0, 0))
        self.assertEqual(drop2._background_inflate, (0, 0))
        self.assertEqual(drop2._border_inflate, (0, 0))
        menu._theme.widget_background_inflate_to_selection = False

        # Process index
        drop2._index = -1
        drop2._process_index()
예제 #10
0
    def test_dropselect(self) -> None:
        """
        Test dropselect widget.
        """
        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):
            items.append(('item{}'.format(i + 1), i + 1))
        # noinspection SpellCheckingInspection
        drop = pygame_menu.widgets.DropSelect(
            'dropsel',
            items,
            selection_option_font_size=int(0.75 *
                                           menu._theme.widget_font_size),
            placeholder_add_to_selection_box=False,
            selection_option_font=menu._theme.widget_font)
        menu.add.generic_widget(drop, configure_defaults=True)
        self.assertEqual(
            drop._selection_box_width,
            (207 if PYGAME_V2 else 208) if not SYS_PLATFORM_OSX else 209)
        self.assertEqual(drop.get_frame_depth(), 0)
        drop.render()
        self.assertTrue(drop._drop_frame.is_scrollable)
        drop_frame = drop._drop_frame

        self.assertIn(drop_frame, menu._update_frames)
        if PYGAME_V2:
            # noinspection SpellCheckingInspection
            self.assertEqual(
                menu._test_widgets_status(),
                ((('DropSelect-dropsel',
                   (0, 0, 0, 123, 149, 354, 49, 123, 304, 123, 149),
                   (1, 0, 1, 1, 0, 0, 0),
                   ('Frame',
                    (-1, -1, -1, 261, 193, 207, 136, 261, 348, 261, 193),
                    (0, 0, 0, 0, 1, 0, 0),
                    (-1, -1)), ('Button-This is a really long selection item',
                                (-1, -1, -1, 0, -1, 356, 40, 261, 348, 0, 154),
                                (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-epic',
                    (-1, -1, -1, 0, 38, 356, 40, 261, 386, 0, 193),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item1',
                    (-1, -1, -1, 0, 77, 356, 40, 261, 425, 0, 232),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item2',
                    (-1, -1, -1, 0, 116, 356, 40, 261, 348, 0, 271),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item3',
                    (-1, -1, -1, 0, 155, 356, 40, 261, 348, 0, 310),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item4',
                    (-1, -1, -1, 0, 194, 356, 40, 261, 348, 0, 349),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item5',
                    (-1, -1, -1, 0, 233, 356, 40, 261, 348, 0, 388),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item6',
                    (-1, -1, -1, 0, 272, 356, 40, 261, 348, 0, 427),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item7',
                    (-1, -1, -1, 0, 311, 356, 40, 261, 348, 0, 466),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item8',
                    (-1, -1, -1, 0, 350, 356, 40, 261, 348, 0, 505),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item9',
                    (-1, -1, -1, 0, 389, 356, 40, 261, 348, 0, 544),
                    (1, 0, 0, 0, 0, 1, 1)),
                   ('Button-item10',
                    (-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) 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)
        self.assertFalse(drop._drop_frame.is_visible())
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertTrue(drop.active)
        self.assertTrue(drop._drop_frame.is_visible())
        self.assertEqual(drop.get_index(), -1)
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
        self.assertEqual(drop.get_index(), 0)
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        self.assertFalse(
            drop.update(
                PygameEventUtils.key(ctrl.KEY_APPLY,
                                     keydown=True,
                                     testmode=False)))
        self.assertFalse(drop.active)
        self.assertFalse(drop._drop_frame.is_visible())
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_DOWN, keydown=True))
        self.assertEqual(drop.get_index(), 0)
        self.assertFalse(drop.active)
        drop.update(PygameEventUtils.key(pygame.K_TAB, keydown=True))  # Enable
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.key(pygame.K_TAB, keydown=True))
        self.assertFalse(drop.active)
        drop.update(PygameEventUtils.key(pygame.K_TAB, keydown=True))  # Enable
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_DOWN,
                                         keydown=True))  # Not infinite
        self.assertEqual(drop.get_index(), 0)
        scroll_values = [
            -1, 0, 0, 0.114, 0.228, 0.33, 0.447, 0.561, 0.664, 0.778, 0.895,
            0.997
        ]
        for i in range(1, 12):
            drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP, keydown=True))
            self.assertEqual(drop.get_index(), i)
            if PYGAME_V2:
                self.assertEqual(
                    drop.get_scroll_value_percentage(ORIENTATION_VERTICAL),
                    scroll_values[i])

        drop.update(PygameEventUtils.key(ctrl.KEY_MOVE_UP,
                                         keydown=True))  # Not infinite
        self.assertEqual(drop.get_index(), 11)  # Not infinite
        if PYGAME_V2:
            self.assertEqual(
                drop.get_scroll_value_percentage(ORIENTATION_VERTICAL), 0.997)

        # Mouseup over rect returns True updated status
        self.assertTrue(drop.active)
        self.assertTrue(
            drop.update(
                PygameEventUtils.middle_rect_click(
                    drop.get_focus_rect(), evtype=pygame.MOUSEBUTTONDOWN)))
        self.assertTrue(drop.active)

        # Touch also does the same trick
        if PYGAME_V2:
            drop._touchscreen_enabled = True
            self.assertTrue(
                drop.update(
                    PygameEventUtils.middle_rect_click(drop.get_focus_rect(),
                                                       menu=menu,
                                                       evtype=FINGERDOWN)))
            self.assertTrue(drop.active)

        # Scroll to bottom and close, then open again, this should scroll to current selected
        drop.scrollh(0)
        drop.scrollv(0)
        self.assertEqual(
            drop._drop_frame.get_scroll_value_percentage(ORIENTATION_VERTICAL),
            0)
        drop._toggle_drop()
        drop._toggle_drop()
        if PYGAME_V2:
            self.assertEqual(
                drop._drop_frame.get_scroll_value_percentage(
                    ORIENTATION_VERTICAL), 0.997)

        # Click drop box should toggle it
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.middle_rect_click(drop))
        self.assertFalse(drop.active)
        drop.update(PygameEventUtils.middle_rect_click(drop))
        self.assertTrue(drop.active)

        # Click middle option
        drop.update(PygameEventUtils.middle_rect_click(drop.get_focus_rect()))
        self.assertEqual(drop.get_index(), 10)
        self.assertFalse(drop.active)

        # Touch middle
        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:
            drop.update(PygameEventUtils.key(ctrl.KEY_APPLY, keydown=True))
        if PYGAME_V2:
            self.assertEqual(
                menu._draw_focus_widget(surface, drop), {
                    1: ((0, 0), (600, 0), (600, 307), (0, 307)),
                    2: ((0, 308), (260, 308), (260, 483), (0, 483)),
                    3: ((468, 308), (600, 308), (600, 483), (468, 483)),
                    4: ((0, 484), (600, 484), (600, 600), (0, 600))
                })
        else:
            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_frame = drop._drop_frame
        drop._drop_frame = None
        self.assertEqual(drop.get_scroll_value_percentage('any'), -1)
        self.assertRaises(
            pygame_menu.widgets.widget.dropselect.
            _SelectionDropNotMadeException, lambda: drop._check_drop_made())
        drop._drop_frame = drop_frame
        drop.update_items([])
        drop._make_selection_drop()
        self.assertEqual(drop._drop_frame.get_attribute('height'), 0)
        self.assertEqual(drop._drop_frame.get_attribute('width'), 0)
        self.assertFalse(drop.active)
        drop._toggle_drop()
        self.assertFalse(drop.active)
        fr = drop.get_focus_rect()
        r = drop.get_rect(apply_padding=False, to_real_position=True)
        self.assertEqual(fr.x, r.x)
        self.assertEqual(fr.y, r.y)
        self.assertEqual(fr.width + drop._selection_box_border_width, r.width)
        self.assertEqual(fr.height, r.height)
        self.assertEqual(drop.get_index(), -1)
        self.assertRaises(ValueError, lambda: drop.get_value())
        drop._up()
        self.assertEqual(drop.get_index(), -1)
        drop._down()
        self.assertEqual(drop.get_index(), -1)

        # Check previous frame not in scrollable frames
        self.assertFalse(drop._drop_frame.is_scrollable)
        self.assertNotIn(drop_frame, menu._update_frames)

        # Restore previous values
        drop.update_items(items)
        self.assertEqual(drop.get_index(), -1)

        # Apply transforms
        drop.translate(1, 1)
        self.assertEqual(drop.get_translate(), (1, 1))
        drop.translate(0, 0)

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: drop.rotate(10))
        self.assertEqual(drop._angle, 0)

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: drop.resize(10, 10))
        self.assertFalse(drop._scale[0])
        self.assertEqual(drop._scale[1], 1)
        self.assertEqual(drop._scale[2], 1)

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: drop.scale(100, 100))
        self.assertFalse(drop._scale[0])
        self.assertEqual(drop._scale[1], 1)
        self.assertEqual(drop._scale[2], 1)

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: drop.flip(True, True))
        self.assertFalse(drop._flip[0])
        self.assertFalse(drop._flip[1])

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: drop.set_max_width(100))
        self.assertIsNone(drop._max_width[0])

        self.assertRaises(WidgetTransformationNotImplemented,
                          lambda: drop.set_max_height(100))
        self.assertIsNone(drop._max_height[0])
        self.assertFalse(drop.active)

        # Add margin
        vm = menu.add.vertical_margin(500)
        self.assertEqual(vm.get_height(), 500)

        # 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)
        for btn in drop2._option_buttons:
            self.assertEqual(btn._tab_size, menu._theme.widget_tab_size)
        self.assertEqual(drop2._drop_frame._tab_size, 4)
        self.assertEqual(drop2.get_id(), '2')
        self.assertEqual(
            menu.get_scrollarea().get_scroll_value_percentage(
                ORIENTATION_VERTICAL), 0)
        self.assertTrue(drop._open_bottom)
        self.assertFalse(drop2._open_bottom)

        # Move to bottom
        menu.get_scrollarea().scroll_to(ORIENTATION_VERTICAL, 1)
        menu.render()
        self.assertTrue(drop._open_bottom)
        self.assertFalse(drop2._open_bottom)
        menu.select_widget(drop2)
        drop2._toggle_drop()

        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)
        self.assertEqual(drop2.get_index(), -1)
        drop2._down()
        self.assertEqual(drop2.get_index(), 11)
        drop2.draw(surface)
        drop._index = -1
        drop2._up()
        self.assertEqual(drop2.get_index(), 0)
        drop2._up()
        self.assertEqual(drop2.get_index(), 1)
        drop2._down()
        self.assertEqual(drop2.get_index(), 0)
        drop2._down()
        self.assertEqual(drop2.get_index(), 11)
        drop2._up()
        self.assertEqual(drop2.get_index(), 0)
        drop2.set_value('item6')
        self.assertEqual(drop2.get_index(), 7)

        drop2.readonly = True
        drop2._up()
        self.assertEqual(drop2.get_index(), 7)
        drop2._down()
        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) if not SYS_PLATFORM_OSX else 0.609)
        drop2.reset_value()
        self.assertTrue(drop2.active)
        self.assertEqual(drop2.get_index(), -1)
        drop2.set_scrollarea(drop2.get_scrollarea())

        if PYGAME_V2:
            self.assertEqual(
                menu._draw_focus_widget(surface, drop2), {
                    1: ((0, 0), (600, 0), (600, 338), (0, 338)),
                    2: ((0, 339), (239, 339), (239, 496), (0, 496)),
                    3: ((447, 339), (600, 339), (600, 496), (447, 496)),
                    4: ((0, 497), (600, 497), (600, 600), (0, 600))
                })

        menu.draw(surface)
        self.assertIsNone(drop2.get_frame())
        self.assertEqual(drop2.last_surface, menu._widgets_surface
                         )  # Outside frame, must be the widgets surface

        # Add drop inside frame
        f = menu.add.frame_v(400,
                             500,
                             max_height=200,
                             background_color=(0, 0, 255))
        f.pack(drop2)
        self.assertEqual(drop2.get_scrollarea(), f.get_scrollarea(inner=True))
        self.assertEqual(drop2._drop_frame.get_scrollarea(),
                         f.get_scrollarea(inner=True))
        self.assertEqual(drop2.get_scrollarea().get_parent(),
                         menu.get_scrollarea())
        self.assertEqual(drop2._drop_frame.get_scrollarea().get_parent(),
                         menu.get_scrollarea())
        drop2.update_items([('optionA', 1), ('optionB', 2)])

        if PYGAME_V2:
            self.assertEqual(
                drop2._get_status(),
                (('DropSelect-drop2',
                  (0, 2, 3, 0, 0, 332, 49, 88, 308, 0, -242),
                  (1, 0, 1, 1, 0, 1, 1),
                  ('Frame',
                   (-1, -1, -1, 116, 44, 207, 100, 204, 352, 116, -198),
                   (0, 0, 0, 0, 0, 1, 1), (-1, -1)),
                  ('Button-optionA',
                   (-1, -1, -1, 116, 77, 207, 34, 204, 385, 116, -165),
                   (1, 0, 0, 0, 0, 1, 2)),
                  ('Button-optionB',
                   (-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) 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) if not SYS_PLATFORM_OSX else
            (0, 0.997))
        self.assertTrue(drop2._open_bottom)

        # Test onchange
        test = [-1, False]

        drop2.set_default_value(0)

        def test_change(item, v) -> None:
            """
            Test change.
            """
            assert item[0][1] == v
            test[0] = item[0][0]

        def test_apply(item, v) -> None:
            """
            Test apply.
            """
            assert item[0][1] == v
            test[1] = not test[1]

        drop2.set_onchange(test_change)
        drop2.set_onreturn(test_apply)
        drop2._toggle_drop()
        self.assertEqual(drop2.get_index(), -1)
        self.assertEqual(test[0], -1)
        drop2._up()
        self.assertEqual(test[0], 'optionA')
        drop2._up()
        self.assertEqual(test[0], 'optionB')
        drop2._up()
        self.assertEqual(test[0], 'optionA')
        self.assertFalse(test[1])
        drop2.update(PygameEventUtils.key(ctrl.KEY_APPLY,
                                          keydown=True))  # Now it's closed
        self.assertTrue(test[1])
        self.assertFalse(drop2.active)
        drop2.update(PygameEventUtils.key(
            ctrl.KEY_APPLY, keydown=True))  # This only opens but not apply
        self.assertTrue(test[1])
        self.assertTrue(drop2.active)
        menu.draw(surface)
        self.assertEqual(drop2.get_frame(), f)
        self.assertEqual(
            drop2.last_surface,
            f.get_surface())  # Frame surface as drop is not in middle
        drop2.update(PygameEventUtils.key(ctrl.KEY_APPLY,
                                          keydown=True))  # Now applies
        self.assertFalse(test[1])
        self.assertFalse(drop2.active)

        # Unpack from frame
        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) 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)
        drop._toggle_drop()
        self.assertTrue(drop.active)
        drop.update(PygameEventUtils.mouse_click(0, 0))
        self.assertFalse(drop.active)

        # Set drop in middle
        if PYGAME_V2:
            self.assertEqual(drop._drop_frame.get_position(), (251, 45))
            self.assertEqual(drop.get_focus_rect(),
                             pygame.Rect(121, 160, 337, 41))

        drop._open_middle = True
        menu.render()

        # For this test, hide all widgets except drop
        # for w in menu.get_widgets():
        #     w.hide()
        # drop.show()

        if PYGAME_V2:
            self.assertEqual(drop._drop_frame.get_position(), (196, 105))
            self.assertEqual(drop.get_focus_rect(),
                             pygame.Rect(121, 160, 337, 41))

        scr = drop._drop_frame.get_scrollarea()
        sfr = drop._drop_frame.get_frame()

        # Add drop to frame
        f.pack(drop)
        menu.render()
        self.assertEqual(drop._drop_frame.get_scrollarea(), scr)
        self.assertEqual(drop._drop_frame.get_frame(), sfr)
        if PYGAME_V2:
            self.assertEqual(drop._drop_frame.get_position(), (196, 453))
            self.assertEqual(drop.get_focus_rect(),
                             pygame.Rect(96, 312, 337, 41))

        self.assertFalse(drop.active)
        drop._toggle_drop()
        menu.render()
        menu.draw(surface)
        self.assertEqual(
            drop.last_surface,
            menu._widgets_surface)  # Menu surface as drop is in middle
        if PYGAME_V2:
            self.assertEqual(
                menu._draw_focus_widget(surface, drop), {
                    1: ((0, 0), (600, 0), (600, 259), (0, 259)),
                    2: ((0, 260), (195, 260), (195, 394), (0, 394)),
                    3: ((403, 260), (600, 260), (600, 394), (403, 394)),
                    4: ((0, 395), (600, 395), (600, 600), (0, 600))
                })

        drop._toggle_drop()

        drop2._open_middle = True
        menu.render()
        menu.select_widget(drop2)
        drop2._toggle_drop()
        menu.draw(surface)
        self.assertEqual(drop2.last_surface, menu._widgets_surface)
        if PYGAME_V2:
            self.assertEqual(drop2._drop_frame.get_position(), (196, 519))
            self.assertEqual(drop2.get_focus_rect(),
                             pygame.Rect(196, 277, 207, 99))

        # Disable focus
        menu._mouse_motion_selection = False

        # As drop1 is scrollable, remove from menu, this should remove the widget too
        drop_frame = drop._drop_frame
        self.assertIn(drop_frame, menu._update_frames)
        menu.remove_widget(drop)
        self.assertNotIn(drop_frame, menu._update_frames)

        def draw_rect() -> None:
            """
            Draw absolute rect on surface for testing purposes.
            """
            # surface.fill((255, 0, 0), drop2.get_focus_rect())
            # surface.fill((0, 0, 255), drop2.get_scrollarea().get_absolute_view_rect())
            # surface.fill((255, 255, 0), drop2.get_scrollarea().get_absolute_view_rect().clip(drop.get_focus_rect()))
            return

        menu.get_decorator().add_callable(draw_rect,
                                          prev=False,
                                          pass_args=False)

        # Test active with different menu settings
        menu_theme = pygame_menu.themes.THEME_ORANGE.copy()
        menu_theme.title_fixed = False
        menu_theme.title_offset = (5, -2)
        menu_theme.widget_alignment = pygame_menu.locals.ALIGN_LEFT
        menu_theme.widget_font = pygame_menu.font.FONT_OPEN_SANS_LIGHT
        menu_theme.widget_font_size = 20

        menu2 = MenuUtils.generic_menu(theme=menu_theme, width=400)
        menu2.add.vertical_margin(1000)

        drop3 = menu2.add.dropselect_multiple(
            title='Pick 3 colors',
            items=[('Black', (0, 0, 0)), ('Blue', (0, 0, 255)),
                   ('Cyan', (0, 255, 255)), ('Fuchsia', (255, 0, 255)),
                   ('Green', (0, 255, 0)), ('Red', (255, 0, 0)),
                   ('White', (255, 255, 255)), ('Yellow', (255, 255, 0))],
            dropselect_multiple_id='pickcolors',
            open_middle=True,
            max_selected=3)
        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, rh))
        menu2.translate(100, 150)
        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, rh))

        # Test update list
        def remove_selection_item(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)
            print('removed {} left'.format(len(_items)))

        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', remove_selection_item, 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

        select1.active = True
        select1.show()

        # Test configured
        select1.configured = False
        self.assertRaises(RuntimeError, lambda: select1._make_selection_drop())
        select1.configured = True
        select1.readonly = True
        self.assertFalse(select1.update([]))

        # Test touchscreen support
        if not PYGAME_V2:
            return

        menu = MenuUtils.generic_menu(touchscreen=True)
        sel = menu.add.dropselect('Subject Id',
                                  items=[('a', ), ('b', ), ('c', ), ('d', ),
                                         ('e', ), ('f', )],
                                  dropselect_id='s0')
        menu.add.button('One', remove_selection_item, sel)

        # Select by touch
        touch_sel = sel.get_rect(to_real_position=True).center
        self.assertEqual(sel.get_index(), -1)
        self.assertFalse(sel.active)

        sel.update(
            PygameEventUtils.touch_click(touch_sel[0], touch_sel[1],
                                         menu=menu))
        self.assertTrue(sel.active)

        # Touch null option
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 40,
                                         menu=menu))
        self.assertTrue(sel.active)

        # Select option a
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 80,
                                         menu=menu))
        self.assertEqual(sel.get_index(), 0)
        self.assertFalse(sel.active)

        # Touch outside
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 400,
                                         menu=menu))
        self.assertFalse(sel.active)

        # Touch button
        menu.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 40,
                                         menu=menu,
                                         evtype=FINGERDOWN))
        menu.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 40,
                                         menu=menu))

        # Touch again outside
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 400,
                                         menu=menu))
        self.assertFalse(sel.active)

        # Select
        sel.select(update_menu=True)
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0], touch_sel[1],
                                         menu=menu))
        self.assertTrue(sel.active)

        # Touch but by menu events, unselect current and force selection
        sel.active = False
        menu._widget_selected_update = False
        menu.update(
            PygameEventUtils.touch_click(touch_sel[0], touch_sel[1],
                                         menu=menu))
        self.assertTrue(sel.active)
        menu.update(
            PygameEventUtils.touch_click(touch_sel[0], touch_sel[1],
                                         menu=menu))
        self.assertFalse(sel.active)

        # Mouse click
        menu.update(PygameEventUtils.mouse_click(touch_sel[0], touch_sel[1]))
        self.assertTrue(sel.active)
        menu.update(PygameEventUtils.mouse_click(touch_sel[0], touch_sel[1]))
        self.assertFalse(sel.active)
        menu._widget_selected_update = True

        # Update the number of items
        sel.update_items([('a', ), ('b', )])
        self.assertFalse(sel.active)
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0], touch_sel[1],
                                         menu=menu))
        self.assertTrue(sel.active)
        self.assertEqual(sel.get_index(), -1)
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 80,
                                         menu=menu))
        self.assertEqual(sel.get_index(), 0)
        self.assertFalse(sel.active)
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0], touch_sel[1],
                                         menu=menu))
        self.assertTrue(sel.active)
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0],
                                         touch_sel[1] + 80,
                                         menu=menu,
                                         evtype=pygame.FINGERDOWN))
        sel.update(
            PygameEventUtils.touch_click(touch_sel[0], touch_sel[1],
                                         menu=menu))
        self.assertFalse(sel.active)

        # Ignore buttons if not active
        self.assertFalse(
            sel.update(
                PygameEventUtils.key(pygame_menu.controls.KEY_MOVE_UP,
                                     keydown=True)))