Repository: ppizarror/pygame-menu

Issue:
Allow Path object as image_path for BaseImage class
**Is your feature request related to a problem? Please describe.**
I like working with pathlib, but BaseImage only accepts a string as image_path, so I always have to put `as_posix` behind my image paths.

**Describe the solution you'd like**
Allow `image_path` to be a string OR a pathlib.Path

Discussion:

Patch:
diff --git a/docs/_source/migration_guide_3_to_4.rst b/docs/_source/migration_guide_3_to_4.rst
index 1252973c..48dbef3d 100644
--- a/docs/_source/migration_guide_3_to_4.rst
+++ b/docs/_source/migration_guide_3_to_4.rst
@@ -8,4 +8,6 @@ Migration Guide - v3 to v4
 - Menu Column/Row positioning has changed, now ``column_max_width`` has a different behaviour. For setting the minimum width of columns use ``column_min_width``.
 - Widget ``VMargin`` now inherits from ``NoneWidget``.
 - Widget ``shadow_offset`` now cannot be ``None``, only ``int`` or ``float`` allowed.
-- Renamed ``touchscreen_enabled`` to ``touchscreen`` in ``Menu`` constructor.
\ No newline at end of file
+- Renamed ``touchscreen_enabled`` to ``touchscreen`` in ``Menu`` constructor.
+- Renamed ``Widget`` method from ``set_selected(selected=True)`` to ``select(status=True, update_menu=False)``.
+- Menu method ``get_width()`` changes to``get_width(inner=False, widget=False)``.
\ No newline at end of file
diff --git a/pygame_menu/baseimage.py b/pygame_menu/baseimage.py
index 45370aea..275c2ba0 100644
--- a/pygame_menu/baseimage.py
+++ b/pygame_menu/baseimage.py
@@ -34,6 +34,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 import os.path as path
 import math
 
+try:
+    # noinspection PyCompatibility
+    from pathlib import Path as _Path
+except ImportError:
+    _Path = None
+
 import pygame
 from pygame_menu.utils import assert_vector2
 
@@ -64,8 +70,8 @@ class BaseImage(object):
     Object that loads an image, stores as a surface, transform it and
     let write the image to an surface.
 
-    :param image_path: Path of the image to be loaded
-    :type image_path: str
+    :param image_path: Path of the image to be loaded. It can be a string or :py:class:`pathlib.Path` on ``Python 3+``
+    :type image_path: str, :py:class:`pathlib.Path`
     :param drawing_mode: Drawing mode of the image
     :type drawing_mode: int
     :param drawing_offset: Offset of the image in drawing method
@@ -80,7 +86,12 @@ class BaseImage(object):
                  drawing_offset=(0, 0),
                  load_from_file=True
                  ):
-        assert isinstance(image_path, str)
+        if _Path is None:
+            assert isinstance(image_path, str)
+        else:
+            assert isinstance(image_path, (str, _Path))
+            if isinstance(image_path, _Path):
+                image_path = str(image_path)
         assert isinstance(load_from_file, bool)
 
         _, file_extension = path.splitext(image_path)
diff --git a/pygame_menu/menu.py b/pygame_menu/menu.py
index 1efbe6df..622007e1 100644
--- a/pygame_menu/menu.py
+++ b/pygame_menu/menu.py
@@ -79,7 +79,7 @@ class Menu(object):
     :type menu_position: tuple, list
     :param mouse_enabled: Enable/disable mouse click inside the Menu
     :type mouse_enabled: bool
-    :param mouse_motion_selection: Select widgets using mouse motion
+    :param mouse_motion_selection: Select widgets using mouse motion. If ``True`` menu draws a ``focus`` on the selected widget
     :type mouse_motion_selection: bool
     :param mouse_visible: Set mouse visible on Menu
     :type mouse_visible: bool
@@ -95,7 +95,7 @@ class Menu(object):
     :type theme: :py:class:`pygame_menu.themes.Theme`
     :param touchscreen: Enable/disable touch action inside the Menu. Only available on pygame 2
     :type touchscreen: bool
-    :param touchscreen_motion_selection: Select widgets using touchscreen motion
+    :param touchscreen_motion_selection: Select widgets using touchscreen motion. If ``True`` menu draws a ``focus`` on the selected widget
     :type touchscreen_motion_selection: bool
     """
 
@@ -282,6 +282,7 @@ class Menu(object):
         self._index = -1  # Selected index, if -1 the widget does not have been selected yet
         self._onclose = None  # Function or event called on Menu close
         self._sounds = Sound()  # type: Sound
+        self._stats = _MenuStats()
         self._submenus = []  # type: list
         self._theme = theme
         self._width = float(width)
@@ -321,10 +322,14 @@ class Menu(object):
         if abs(self._widget_offset[1]) < 1:
             self._widget_offset[1] *= self._height
 
+        # Cast to int offset
+        self._widget_offset[0] = int(self._widget_offset[0])
+        self._widget_offset[1] = int(self._widget_offset[1])
+
         # If centering is enabled, but widget offset in the vertical is different than zero a warning is raised
         if self._center_content and self._widget_offset[1] != 0:
             msg = 'menu (title "{0}") is vertically centered (center_content=True), but widget offset (from theme) is different than zero ({1}px). Auto-centering has been disabled'
-            msg = msg.format(title, round(self._widget_offset[1], 3))
+            msg = msg.format(title, self._widget_offset[1])
             warnings.warn(msg)
             self._center_content = False
 
@@ -633,6 +638,7 @@ class Menu(object):
         self._configure_widget(widget=widget, **attributes)
         widget.set_selection_callback(onselect)
         self._append_widget(widget)
+        self._stats.add_button += 1
 
         return widget
 
@@ -745,6 +751,7 @@ class Menu(object):
         self._configure_widget(widget=widget, **attributes)
         widget.set_default_value(default)
         self._append_widget(widget)
+        self._stats.add_color_input += 1
 
         return widget
 
@@ -828,6 +835,7 @@ class Menu(object):
         self._check_kwargs(kwargs)
         self._configure_widget(widget=widget, **attributes)
         self._append_widget(widget)
+        self._stats.add_image += 1
 
         return widget
 
@@ -919,6 +927,7 @@ class Menu(object):
             self._check_kwargs(kwargs)
             self._configure_widget(widget=widget, **attributes)
             self._append_widget(widget)
+            self._stats.add_label += 1
 
         else:
 
@@ -1039,6 +1048,7 @@ class Menu(object):
 
         self._configure_widget(widget=widget, **attributes)
         self._append_widget(widget)
+        self._stats.add_selector += 1
 
         return widget
 
@@ -1177,6 +1187,7 @@ class Menu(object):
         self._configure_widget(widget=widget, **attributes)
         widget.set_default_value(default)
         self._append_widget(widget)
+        self._stats.add_text_input += 1
 
         return widget
 
@@ -1205,6 +1216,7 @@ class Menu(object):
         widget = _widgets.VMargin(widget_id=margin_id)
         self._configure_widget(widget=widget, **attributes)
         self._append_widget(widget)
+        self._stats.add_vertical_margin += 1
 
         return widget
 
@@ -1231,9 +1243,11 @@ class Menu(object):
         :rtype: :py:class:`pygame_menu.widgets.NoneWidget`
         """
         attributes = self._filter_widget_attributes({})
+
         widget = _widgets.NoneWidget(widget_id=widget_id)
         self._configure_widget(widget=widget, **attributes)
         self._append_widget(widget)
+        self._stats.add_none_widget += 1
 
         return widget
 
@@ -1279,8 +1293,8 @@ class Menu(object):
         self._check_id_duplicated(widget.get_id())
 
         widget.set_controls(self._joystick, self._mouse, self._touchscreen)
-
         self._append_widget(widget)
+        self._stats.add_generic_widget += 1
 
     def _filter_widget_attributes(self, kwargs):
         """
@@ -1438,11 +1452,10 @@ class Menu(object):
         assert widget.get_menu() == self, 'widget cannot have a different instance of menu'
         self._widgets.append(widget)
         if self._index < 0 and widget.is_selectable:
-            widget.set_selected()
+            widget.select()
             self._index = len(self._widgets) - 1
+        self._stats.added_widgets += 1
         self._widgets_surface = None  # If added on execution time forces the update of the surface
-        if self._center_content:
-            self.center_content()
         self._render()
 
     def select_widget(self, widget):
@@ -1487,6 +1500,7 @@ class Menu(object):
         :return: None
         """
         assert isinstance(widget, _widgets.core.Widget)
+
         try:
             index = self._widgets.index(widget)  # If not exists this raises ValueError
         except ValueError:
@@ -1494,6 +1508,7 @@ class Menu(object):
                              'with menu.get_current().remove_widget(widget)')
         self._widgets.pop(index)
         self._update_after_remove_or_hidden(index)
+        self._stats.removed_widgets += 1
         widget.set_menu(None)  # Removes Menu reference from widget
 
     def _update_after_remove_or_hidden(self, index, update_surface=True):
@@ -1528,8 +1543,6 @@ class Menu(object):
                 self._select(self._index)
         self._update_widget_position()
         if update_surface:
-            if self._center_content:
-                self.center_content()
             self._widgets_surface = None  # If added on execution time forces the update of the surface
 
     def _back(self):
@@ -1543,6 +1556,21 @@ class Menu(object):
         else:
             self._close()
 
+    def _update_selection_if_hidden(self):
+        """
+        Updates Menu widget selection if a widget was hidden.
+
+        :return: None
+        """
+        if len(self._widgets) > 0:
+            if self._index != -1:
+                selected_widget = self._widgets[self._index % len(self._widgets)]  # type: _widgets.core.Widget
+                if not selected_widget.visible:
+                    selected_widget.select(False)  # Unselect
+                    self._update_after_remove_or_hidden(-1, update_surface=False)
+            else:
+                self._update_after_remove_or_hidden(0, update_surface=False)
+
     def _update_widget_position(self):
         """
         Update the position dict for each widget.
@@ -1681,10 +1709,9 @@ class Menu(object):
         self._menubar.set_position(self._pos_x, self._pos_y)
 
         # Widget max/min position
-        max_x = -1e8
-        max_y = -1e8
-        min_x = 1e8
-        min_y = 1e8
+        min_max_updated = False
+        max_x, max_y = -1e8, -1e8
+        min_x, min_y = 1e8, 1e8
 
         # Update appended widgets
         for index in range(len(self._widgets)):
@@ -1718,47 +1745,48 @@ class Menu(object):
             x_coord += max(0, self._widget_offset[0])
 
             # Calculate Y position
-            ysum = 0  # Compute the total height from the current row position to the top of the column
+            ysum = 1  # Compute the total height from the current row position to the top of the column
             for rwidget in self._widget_columns[col]:  # type: _widgets.core.Widget
                 _, r, _ = rwidget.get_col_row_index()
                 if r >= row:
                     break
                 if rwidget.visible and not rwidget.floating:
+                    if r == 0 and self._widget_offset[1] == 0:  # No widget is before
+                        if rwidget.is_selectable:
+                            ysum += rwidget.get_selection_effect().get_margin()[0]
                     ysum += widget_rects[rwidget.get_id()].height  # Height
                     ysum += rwidget.get_margin()[1]  # Vertical margin (bottom)
-            y_coord = max(0, self._widget_offset[1]) + ysum + widget.get_padding()[0]
 
             # If the widget offset is zero, then add the selection effect to the height
             # of the widget to avoid visual glitches
-            if self._widget_offset[1] == 0:
-                y_coord += selection_effect.get_margin()[0] + 1  # add 1px of linewidth
+            if ysum == 1 and self._widget_offset[1] == 0:  # No widget is before
+                if widget.is_selectable:  # Add top margin
+                    ysum += widget.get_selection_effect().get_margin()[0]
+
+            y_coord = max(0, self._widget_offset[1]) + ysum + widget.get_padding()[0]
 
             # Update the position of the widget
-            widget.set_position(x_coord, y_coord)
+            widget.set_position(int(x_coord), int(y_coord))
 
             # Update max/min position
-            x, y = widget.get_rect().bottomright
+            min_max_updated = True
+            widget_rect = widget.get_rect()
+            x, y = widget_rect.bottomright
             max_x = max(max_x, x)
             max_y = max(max_y, y)
-            x, y = widget.get_rect().topleft
+            x, y = widget_rect.topleft
             min_x = min(min_x, x)
             min_y = min(min_y, y)
 
-        # Save max/min position
-        self._widget_max_position = (max_x, max_y)
-        self._widget_min_position = (min_x, min_y)
-
-    def _update_selection_if_hidden(self):
-        """
-        Updates Menu widget selection if a widget was hidden.
+        # Update position
+        if min_max_updated:
+            self._widget_max_position = (max_x, max_y)
+            self._widget_min_position = (min_x, min_y)
+        else:
+            self._widget_max_position = (0, 0)
+            self._widget_min_position = (0, 0)
 
-        :return: None
-        """
-        if len(self._widgets) > 0:
-            selected_widget = self._widgets[self._index % len(self._widgets)]  # type: _widgets.core.Widget
-            if not selected_widget.visible:
-                selected_widget.set_selected(False)  # Unselect
-                self._update_after_remove_or_hidden(-1, update_surface=False)
+        self._stats.position_update += 1
 
     def _build_widget_surface(self):
         """
@@ -1812,6 +1840,7 @@ class Menu(object):
         self._widgets_surface = _utils.make_surface(width, height)
         self._scroll.set_world(self._widgets_surface)
         self._scroll.set_position(self._pos_x, self._pos_y + menubar_height)
+        self._stats.build_surface += 1
 
     def _check_id_duplicated(self, widget_id):
         """
@@ -1930,7 +1959,7 @@ class Menu(object):
 
     def center_content(self):
         """
-        Centers the content of the Menu vertically. This action rewrites theme ``widget_offset``.
+        Centers the content of the Menu vertically. This action rewrites ``widget_offset``.
 
         .. note::
 
@@ -1948,19 +1977,53 @@ class Menu(object):
         if len(self._widgets) == 0:  # If this happen, get_widget_max returns an immense value
             self._widget_offset[1] = 0
             return
-        self._build_widget_surface()  # For position
+        if self._widgets_surface is None:
+            self._build_widget_surface()  # For position (max/min)
         available = self.get_height(inner=True)
         widget_height = self.get_height(widget=True)
-        self._widget_offset[1] = max(float(available - widget_height) / 2, 0)
-        self._widgets_surface = None  # Rebuild on the next draw
+        new_offset = int(max(float(available - widget_height) / 2, 0))
+        if abs(new_offset - self._widget_offset[1]) > 1:
+            self._widget_offset[1] = new_offset
+            self._widgets_surface = None  # Rebuild on the next draw
+        self._stats.center_content += 1
+
+    def get_width(self, inner=False, widget=False):
+        """
+        Get menu width.
+
+        .. note::
+
+            This is applied only to the base Menu (not the currently displayed,
+            stored in ``_current`` pointer); for such behaviour apply
+            to :py:meth:`pygame_menu.Menu.get_current` object.
+
+        :param inner: If ``True`` returns the available woith (menu width minus scroll if visible)
+        :type inner: bool
+        :param widget: If ``True```returns the total width used by the widgets
+        :type widget: bool
+        :return: Width in px
+        :rtype: int, float
+        """
+        if widget:
+            return self._widget_max_position[0] - self._widget_min_position[0]
+        if not inner:
+            return self._width
+        vertical_scroll = self._scroll.get_scrollbar_thickness(_locals.ORIENTATION_VERTICAL)
+        return self._width - vertical_scroll
 
     def get_height(self, inner=False, widget=False):
         """
         Get menu height.
 
+        .. note::
+
+            This is applied only to the base Menu (not the currently displayed,
+            stored in ``_current`` pointer); for such behaviour apply
+            to :py:meth:`pygame_menu.Menu.get_current` object.
+
         :param inner: If ``True`` returns the available height (menu height minus scroll and menubar)
         :type inner: bool
-        :param widget: If ``True`` returns the height of the drawn widgets
+        :param widget: If ``True`` returns the total height used by the widgets
         :type widget: bool
         :return: Height in px
         :rtype: int, float
@@ -1972,6 +2035,25 @@ class Menu(object):
         horizontal_scroll = self._scroll.get_scrollbar_thickness(_locals.ORIENTATION_HORIZONTAL)
         return self._height - self._menubar.get_height() - horizontal_scroll
 
+    def get_size(self, inner=False, widget=False):
+        """
+        Return the Menu size (px) as a tuple of *(width, height)*.
+
+        .. note::
+
+            This is applied only to the base Menu (not the currently displayed,
+            stored in ``_current`` pointer); for such behaviour apply
+            to :py:meth:`pygame_menu.Menu.get_current` object.
+
+        :param inner: If ``True`` returns the available *(width, height)* (menu height minus scroll and menubar)
+        :type inner: bool
+        :param widget: If ``True`` returns the total *(width, height)* used by the widgets
+        :type widget: bool
+        :return: Height in px
+        :rtype: int, float
+        """
+        return self.get_width(inner=inner, widget=widget), self.get_height(inner=inner, widget=widget)
+
     def render(self):
         """
         Force **current** Menu rendering. Useful to force widget update.
@@ -1990,6 +2072,7 @@ class Menu(object):
         """
         self._current._widgets_surface = None
         self._current._render()
+        self._current._stats.render_public += 1
 
     def _render(self):
         """
@@ -2001,6 +2084,7 @@ class Menu(object):
             if self._center_content:
                 self.center_content()
             self._build_widget_surface()
+            self._stats.render_private += 1
 
     def draw(self, surface, clear_surface=False):
         """
@@ -2050,29 +2134,36 @@ class Menu(object):
         self._current._scroll.draw(surface)
         self._current._menubar.draw(surface)
 
-        # Focus on selected if the widget is active
+        # Draw focus on selected if the widget is active
         self._current._draw_focus_widget(surface, selected_widget)
 
+        self._current._stats.draw += 1
+
     def _draw_focus_widget(self, surface, widget):
         """
-        Draw the focus background from a given widget.
+        Draw the focus background from a given widget. Widget must be selectable,
+        active, selected. Not all widgets requests the active status, then focus may not
+        be drawn.
 
         :param surface: Pygame surface to draw the Menu
         :type surface: :py:class:`pygame.Surface`
         :param widget: Focused widget
         :type widget: :py:class:`pygame_menu.widgets.core.widget.Widget`, None
-        :return: None
+        :return: Returns the focus region, None if the focus could not be possible
+        :rtype: dict, None
         """
         assert isinstance(surface, pygame.Surface)
         assert isinstance(widget, (_widgets.core.Widget, type(None)))
 
-        if widget is None or not widget.active or not self._mouse_motion_selection:
+        if widget is None or not widget.active or not widget.is_selectable or not widget.selected or \
+                not (self._mouse_motion_selection or self._touchscreen_motion_selection) or not widget.visible:
             return
         window_width, window_height = self._window_size
 
         rect = widget.get_rect()  # type: pygame.Rect
-        if widget.selected and widget.get_selection_effect():
-            rect = widget.get_selection_effect().inflate(rect)
+
+        # Apply selection effect
+        rect = widget.get_selection_effect().inflate(rect)
         rect = self._scroll.to_real_position(rect, visible=True)
 
         if rect.width == 0 or rect.height == 0:
@@ -2086,32 +2177,32 @@ class Menu(object):
         x2 = int(x2)
         y2 = int(y2)
 
-        # Draw 4 areas:
-        # .------------------.
-        # |________1_________|
-        # |  2  |XXXXXX|  3  |
-        # |_____|XXXXXX|_____|
-        # |        4         |
-        # .------------------.
+        coords = {}
 
         if abs(y1 - y2) <= 4 or abs(x1 - x2) <= 4:
-            gfxdraw.filled_polygon(surface,
-                                   [(0, 0), (window_width, 0), (window_width, window_height), (0, window_height)],
-                                   self._theme.focus_background_color)
-            return
-
-        gfxdraw.filled_polygon(surface,
-                               [(0, 0), (window_width, 0), (window_width, y1 + 1), (0, y1 + 1)],
-                               self._theme.focus_background_color)  # 1
-        gfxdraw.filled_polygon(surface,
-                               [(0, y1 + 2), (x1 + 1, y1 + 2), (x1 + 1, y2 - 2), (0, y2 - 2)],
-                               self._theme.focus_background_color)  # 2
-        gfxdraw.filled_polygon(surface,
-                               [(x2 - 1, y1 + 2), (window_width, y1 + 2), (window_width, y2 - 2), (x2 - 1, y2 - 2)],
-                               self._theme.focus_background_color)  # 3
-        gfxdraw.filled_polygon(surface,
-                               [(0, y2 - 1), (window_width, y2 - 1), (window_width, window_height), (0, window_height)],
-                               self._theme.focus_background_color)  # 4
+            # If the area of the selected widget is too small, draw focus over the entire menu
+            # .------------------.
+            # |                  |
+            # |        1         |
+            # |                  |
+            # .------------------.
+            coords[1] = (0, 0), (window_width, 0), (window_width, window_height), (0, window_height)
+        else:
+            # Draw 4 areas:
+            # .------------------.
+            # |________1_________|
+            # |  2  |XXXXXX|  3  |
+            # |_____|XXXXXX|_____|
+            # |        4         |
+            # .------------------.
+            coords[1] = (0, 0), (window_width, 0), (window_width, y1 - 1), (0, y1 - 1)
+            coords[2] = (0, y1), (x1 - 1, y1), (x1 - 1, y2 - 1), (0, y2 - 1)
+            coords[3] = (x2, y1), (window_width, y1), (window_width, y2 - 1), (x2, y2 - 1)
+            coords[4] = (0, y2), (window_width, y2), (window_width, window_height), (0, window_height)
+
+        for area in coords:
+            gfxdraw.filled_polygon(surface, coords[area], self._theme.focus_background_color)
+        return coords
 
     def enable(self):
         """
@@ -2428,6 +2519,8 @@ class Menu(object):
         if not self.is_enabled():
             updated = True
 
+        self._current._stats.update += 1
+
         return updated
 
     def mainloop(self, surface, bgfun=None, disable_loop=False, fps_limit=30):
@@ -2475,6 +2568,7 @@ class Menu(object):
         self._current._background_function = bgfun
 
         while True:
+            self._current._stats.loop += 1
             self._current._clock.tick(fps_limit)
 
             self.draw(surface=surface, clear_surface=True)
@@ -2653,6 +2747,7 @@ class Menu(object):
         del self._widgets[:]
         del self._submenus[:]
         self._index = 0
+        self._stats.clear += 1
 
     def _open(self, menu):
         """
@@ -2707,6 +2802,7 @@ class Menu(object):
                 break
 
         self._current._select(self._top._current._index)
+        self._current._stats.reset += 1
 
     def _select(self, new_index, dwidget=0):
         """
@@ -2740,7 +2836,7 @@ class Menu(object):
         # Get both widgets
         if curr_widget._index >= total_curr_widgets:  # The length of the Menu changed during execution time
             for i in range(total_curr_widgets):  # Unselect all posible candidates
-                curr_widget._widgets[i].set_selected(False)
+                curr_widget._widgets[i].select(False)
             curr_widget._index = 0
 
         old_widget = curr_widget._widgets[curr_widget._index]  # type: _widgets.core.Widget
@@ -2748,16 +2844,16 @@ class Menu(object):
 
         # If new widget is not selectable or visible
         if not new_widget.is_selectable or not new_widget.visible:
-            if curr_widget._index >= 0:  # There's at least 1 selectable option (if only text this would be false)
+            if curr_widget._index >= 0:  # There's at least 1 selectable option
                 curr_widget._select(new_index + dwidget, dwidget)
                 return
             else:  # No selectable options, quit
                 return
 
         # Selecting widgets forces rendering
-        old_widget.set_selected(False)
+        old_widget.select(False)
         curr_widget._index = new_index  # Update selected index
-        new_widget.set_selected()
+        new_widget.select()
 
         # Scroll to rect
         rect = new_widget.get_rect()
@@ -2778,6 +2874,7 @@ class Menu(object):
 
         # Play widget selection sound
         self._sounds.play_widget_selection()
+        self._stats.select += 1
 
     def get_id(self):
         """
@@ -2810,21 +2907,6 @@ class Menu(object):
         w, h = self._window_size
         return w, h
 
-    def get_size(self):
-        """
-        Return the Menu size (px) as a tuple of *(width, height)*.
-
-        .. note::
-
-            This is applied only to the base Menu (not the currently displayed,
-            stored in ``_current`` pointer); for such behaviour apply
-            to :py:meth:`pygame_menu.Menu.get_current` object.
-
-        :return: Menu size in px
-        :rtype: tuple
-        """
-        return self._width, self._height
-
     def get_widgets(self):
         """
         Return the Menu widgets as a tuple.
@@ -3131,3 +3213,46 @@ class Menu(object):
         if not self.has_attribute(key):
             raise IndexError('attribute "{0}" does not exists on menu'.format(key))
         del self._attributes[key]
+
+
+class _MenuStats(object):
+    """
+    Stores menu stats.
+    """
+
+    def __init__(self):
+        """
+        Constructor.
+        """
+
+        # Widget addition
+        self.add_button = 0
+        self.add_color_input = 0
+        self.add_generic_widget = 0
+        self.add_image = 0
+        self.add_label = 0
+        self.add_none_widget = 0
+        self.add_selector = 0
+        self.add_text_input = 0
+        self.add_vertical_margin = 0
+
+        # Widget update
+        self.added_widgets = 0
+        self.removed_widgets = 0
+
+        # Widget position
+        self.build_surface = 0
+        self.position_update = 0
+        self.center_content = 0
+
+        # Render
+        self.render_private = 0
+        self.render_public = 0
+
+        # Other
+        self.clear = 0
+        self.draw = 0
+        self.loop = 0
+        self.reset = 0
+        self.select = 0
+        self.update = 0
diff --git a/pygame_menu/widgets/core/widget.py b/pygame_menu/widgets/core/widget.py
index 0fcdb603..5e98eeda 100644
--- a/pygame_menu/widgets/core/widget.py
+++ b/pygame_menu/widgets/core/widget.py
@@ -167,11 +167,11 @@ class Widget(object):
         self.lock_position = False  # If True, locks position after first call to .set_position(x,y) method
         self.floating = False  # If True, the widget don't contribute width/height to the Menu widget positioning computation. Use .set_float() to modify this status
         self.mouse_enabled = True
-        self.selected = False
+        self.selected = False  # Use select() to modify this status
         self.selection_expand_background = False  # If True, the widget background will inflate to match selection margin if selected
         self.sound = Sound()  # type: Sound
         self.touchscreen_enabled = True
-        self.visible = True  # Use .show() or .hide() to modify this status
+        self.visible = True  # Use show() or hide() to modify this status
 
     def _force_render(self):
         """
@@ -782,7 +782,8 @@ class Widget(object):
 
     def _apply_transforms(self):
         """
-        Apply surface transforms.
+        Apply surface transforms: angle, flip and scalling.
+        Translation is applied on widget positioning.
 
         :return: None
         """
@@ -1036,8 +1037,8 @@ class Widget(object):
 
     def flip(self, x, y):
         """
-        This method can flip the widget either vertically, horizontally, or both.
-        Flipping a widget is non-destructive and does not change the dimensions.
+        Transformation: This method can flip the widget either vertically, horizontally,
+        or both. Flipping a widget is non-destructive and does not change the dimensions.
 
         .. note::
 
@@ -1057,8 +1058,8 @@ class Widget(object):
 
     def set_max_width(self, width, scale_height=False, smooth=True):
         """
-        Set the widget max width, it applies an scalling factor if the widget width is
-        greater than the limit.
+        Transformation: Set the widget max width, it applies an scalling factor
+        if the widget width is greater than the limit.
 
         .. note::
 
@@ -1111,8 +1112,8 @@ class Widget(object):
 
     def set_max_height(self, height, scale_width=False, smooth=True):
         """
-        Set the widget max height, it applies an scalling factor if the widget height is
-        greater than the limit.
+        Transformation: Set the widget max height, it applies an scalling factor
+        if the widget height is greater than the limit.
 
         .. note::
 
@@ -1173,7 +1174,7 @@ class Widget(object):
 
     def scale(self, width, height, smooth=True):
         """
-        Scale the widget to a desired width and height factor.
+        Transformation: Scale the widget to a desired width and height factor.
 
         .. note::
 
@@ -1225,7 +1226,7 @@ class Widget(object):
 
     def resize(self, width, height, smooth=True):
         """
-        Set the widget size to another size.
+        Transformation: Set the widget size to another size.
 
         .. note::
 
@@ -1264,7 +1265,7 @@ class Widget(object):
 
     def translate(self, x, y):
         """
-        Translate to *(+x, +y)* according to the default position.
+        Transformation: Translate to *(+x, +y)* according to the default position.
 
         .. note::
 
@@ -1288,7 +1289,7 @@ class Widget(object):
 
     def rotate(self, angle):
         """
-        Unfiltered counterclockwise rotation. The angle argument represents degrees
+        Transformation: Unfiltered counterclockwise rotation. The angle argument represents degrees
         and can be any floating point value. Negative angle amounts will rotate clockwise.
 
         .. note::
@@ -1335,7 +1336,7 @@ class Widget(object):
         """
         return self._alignment
 
-    def set_selected(self, selected=True):
+    def select(self, status=True, update_menu=False):
         """
         Mark the widget as selected and execute the ``on_selected`` callback
         function as follows:
@@ -1351,16 +1352,21 @@ class Widget(object):
             Use :py:meth:`pygame_menu.widgets.core.Widget.render` method to force
             widget rendering after calling this method.
 
-        :param selected: Set item as selected
-        :type selected: bool
+        .. warning::
+
+            This method should only be used by the menu.
+
+        :param status: Selection status
+        :type status: bool
+        :param update_menu: If ``True`` this status is also applied on the menu that contains this widget
         :return: None
         """
-        assert isinstance(selected, bool)
+        assert isinstance(status, bool)
         if not self.is_selectable:
             return
-        self.selected = selected
+        self.selected = status
         self.active = False
-        if selected:
+        if self.selected:
             self._focus()
             self._selection_time = time.time()
         else:
@@ -1368,7 +1374,10 @@ class Widget(object):
             self._events = []  # Remove events
         self._force_render()
         if self._on_select is not None:
-            self._on_select(selected, self, self.get_menu())
+            self._on_select(self.selected, self, self.get_menu())
+        if update_menu:
+            assert self._menu is not None
+            self._menu.select_widget(self)
 
     def get_selected_time(self):
         """
@@ -1709,7 +1718,7 @@ class Widget(object):
         """
         assert isinstance(float_status, bool)
         self.floating = float_status
-        self._menu_surface_needs_update = True
+        self._menu_surface_needs_update = True  # Force Menu update
 
     def show(self):
         """
diff --git a/pygame_menu/widgets/widget/colorinput.py b/pygame_menu/widgets/widget/colorinput.py
index 4db85c4b..68e58f71 100644
--- a/pygame_menu/widgets/widget/colorinput.py
+++ b/pygame_menu/widgets/widget/colorinput.py
@@ -327,6 +327,11 @@ class ColorInput(TextInput):  # lgtm [py/missing-call-to-init]
         # Maybe TextInput did not rendered, so this has to be changed
         self._rect.width, self._rect.height = self._surface.get_size()
         self._rect.width += self._prev_size * self._rect.height  # Adds the previsualization size to the box
+
+        # Force previsualization rendering
+        if not r:
+            self._previsualization_surface = None
+
         return r
 
     def _format_hex(self):
diff --git a/pygame_menu/widgets/widget/menubar.py b/pygame_menu/widgets/widget/menubar.py
index d3bb5a90..17728d65 100644
--- a/pygame_menu/widgets/widget/menubar.py
+++ b/pygame_menu/widgets/widget/menubar.py
@@ -130,6 +130,15 @@ class MenuBar(Widget):
     def scale(self, width, height, smooth=True):  # Widget don't support scalling (yet)
         pass
 
+    def resize(self, width, height, smooth=True):  # Widget don't support scalling
+        pass
+
+    def set_max_height(self, height, scale_width=False, smooth=True):
+        pass
+
+    def set_max_width(self, width, scale_height=False, smooth=True):
+        pass
+
     def set_selection_effect(self, selection):
         pass
 
diff --git a/pygame_menu/widgets/widget/none.py b/pygame_menu/widgets/widget/none.py
index 7801b423..34a9890b 100644
--- a/pygame_menu/widgets/widget/none.py
+++ b/pygame_menu/widgets/widget/none.py
@@ -129,7 +129,7 @@ class NoneWidget(Widget):
     def set_alignment(self, align):
         pass
 
-    def set_selected(self, selected=True):
+    def select(self, select=True, update_menu=False):
         pass
 
     def set_shadow(self, enabled=True, color=None, position=None, offset=None):
diff --git a/pygame_menu/widgets/widget/scrollbar.py b/pygame_menu/widgets/widget/scrollbar.py
index 978eba23..2e28b7a8 100644
--- a/pygame_menu/widgets/widget/scrollbar.py
+++ b/pygame_menu/widgets/widget/scrollbar.py
@@ -135,6 +135,24 @@ class ScrollBar(Widget):
     def set_padding(self, padding):  # Don't accept padding
         pass
 
+    def scale(self, width, height, smooth=True):
+        pass
+
+    def resize(self, width, height, smooth=True):
+        pass
+
+    def set_max_width(self, width, scale_height=False, smooth=True):
+        pass
+
+    def set_max_height(self, height, scale_width=False, smooth=True):
+        pass
+
+    def rotate(self, angle):
+        pass
+
+    def flip(self, x, y):
+        pass
+
     def _apply_size_changes(self):
         """
         Apply scrollbar changes.
diff --git a/pygame_menu/widgets/widget/textinput.py b/pygame_menu/widgets/widget/textinput.py
index 1909a948..c803bfe8 100644
--- a/pygame_menu/widgets/widget/textinput.py
+++ b/pygame_menu/widgets/widget/textinput.py
@@ -443,7 +443,7 @@ class TextInput(Widget):
         self._rect.width, self._rect.height = self._surface.get_size()
 
         # Force Menu update
-        self._menu_surface_needs_update = True
+        self._menu_surface_needs_update = True  # Force Menu update
 
     def _render_selection_box(self, force=False):
         """


Test patch:
diff --git a/test/test_baseimage.py b/test/test_baseimage.py
index 9ce3acc5..68540172 100644
--- a/test/test_baseimage.py
+++ b/test/test_baseimage.py
@@ -31,12 +31,25 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 """
 
 from test._utils import *
+import sys
 
 from pygame_menu.baseimage import *
 
 
 class BaseImageTest(unittest.TestCase):
 
+    def test_pathlib(self):
+        """
+        Test image load with pathlib.
+        """
+        if sys.version_info >= (3, 0):
+            # noinspection PyCompatibility
+            from pathlib import Path
+            pathimg = Path(pygame_menu.baseimage.IMAGE_EXAMPLE_GRAY_LINES)
+            image = pygame_menu.baseimage.BaseImage(pathimg)
+            image.draw(surface)
+            self.assertEqual(image.get_path(), str(pathimg))
+
     def test_modes(self):
         """
         Test drawing modes.
diff --git a/test/test_menu.py b/test/test_menu.py
index 2f365517..22dce6de 100644
--- a/test/test_menu.py
+++ b/test/test_menu.py
@@ -34,6 +34,7 @@ from test._utils import *
 
 from pygame_menu import events
 from pygame_menu.widgets import Label, Button
+
 import pygame
 import timeit
 
@@ -94,12 +95,18 @@ class MenuTest(unittest.TestCase):
         self.assertEqual(menu.get_height(), 400)
         self.assertEqual(menu.get_height(inner=True), 345)
         self.assertEqual(menu.get_menubar_widget().get_height(), 55)
-        self.assertEqual(btn.get_height(), 41)
-        self.assertEqual(menu.get_height(widget=True), 41)
 
-        vpos = (345 - 41) / 2  # available_height - widget_height
+        h = 41
+        if pygame.version.vernum.major < 2:
+            h = 42
+
+        self.assertEqual(btn.get_height(), h)
+        self.assertEqual(btn.get_size()[1], h)
+        self.assertEqual(menu.get_height(widget=True), h)
+
+        vpos = int((345 - h) / 2)  # available_height - widget_height
         self.assertEqual(menu._widget_offset[1], vpos)
-        self.assertEqual(btn.get_position()[1], vpos)
+        self.assertEqual(btn.get_position()[1], vpos + 1)
 
         # If there's too many widgets, the centering should be disabled
         for i in range(20):
@@ -107,12 +114,13 @@ class MenuTest(unittest.TestCase):
         self.assertEqual(menu._widget_offset[1], 0)
 
         theme = menu.get_theme()
-        self.assertEqual(41 * 21 + theme.widget_margin[1] * 20, menu.get_height(widget=True))
 
         # Anyway, as the widget is 0, the first button position should be the height of its selection effect
         btneff = btn.get_selection_effect().get_margin()[0]
         self.assertEqual(btn.get_position()[1], btneff + 1)
 
+        self.assertEqual(h * 21 + theme.widget_margin[1] * 20, menu.get_height(widget=True))
+
         # Test menu not centered
         menu = MenuUtils.generic_menu(center_content=False)
         btn = menu.add_button('button', None)
@@ -183,6 +191,26 @@ class MenuTest(unittest.TestCase):
         self.assertEqual(x, 586)
         self.assertEqual(y, 82)
 
+        # Test no selectable position
+        menu = MenuUtils.generic_menu(center_content=False)
+        btn = menu.add_button('button', None)
+        btn.is_selectable = False
+        menu.render()
+        self.assertEqual(btn.get_position()[1], 1)
+
+        # Test no selectable + widget
+        menu = MenuUtils.generic_menu()
+        img = menu.add_image(
+            pygame_menu.baseimage.IMAGE_EXAMPLE_PYGAME_MENU,
+            scale=(0.25, 0.25),
+            align=pygame_menu.locals.ALIGN_CENTER
+        )
+        btn = menu.add_button('Nice', None)
+        margin = menu.get_theme().widget_margin[1]
+        menu.render()
+        self.assertEqual(menu.get_height(widget=True), img.get_height() + btn.get_height() + margin)
+        self.assertEqual(int((menu.get_height(inner=True) - menu.get_height(widget=True)) / 2), menu._widget_offset[1])
+
     def test_attributes(self):
         """
         Test menu attributes.
@@ -1117,6 +1145,105 @@ class MenuTest(unittest.TestCase):
         self.assertEqual(menubar.get_title_offset()[0], theme.title_offset[0])
         self.assertEqual(menubar.get_title_offset()[1], theme.title_offset[1])
 
+    def test_empty(self):
+        """
+        Test empty menu.
+        """
+        menu = MenuUtils.generic_menu(title='menu')
+        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', None)
+        btn.hide()
+        self.assertEqual(menu.get_height(widget=True), 0)
+
+        # Get the size of the scrollarea
+        sa = menu.get_scrollarea()
+
+        sa_height = menu.get_height() - menu.get_menubar_widget().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())
+
+        posrect = sa.to_world_position((10, 10))
+        self.assertEqual(posrect[0], 10)
+        self.assertEqual(posrect[1], -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)
+        self.assertEqual(sa.get_world_size()[0], 0)
+        sa._world = world
+
+    def test_focus(self):
+        """
+        Test menu focus effect.
+        """
+        menu = MenuUtils.generic_menu(title='menu', mouse_motion_selection=True)
+        btn = menu.add_button('nice', None)
+
+        # Test focus
+        btn.active = True
+        focus = menu._draw_focus_widget(surface, btn)
+        self.assertEqual(len(focus), 4)
+        if pygame.version.vernum.major < 2:
+            self.assertEqual(focus[1], ((0, 0), (600, 0), (600, 301), (0, 301)))
+            self.assertEqual(focus[2], ((0, 302), (261, 302), (261, 353), (0, 353)))
+            self.assertEqual(focus[3], ((337, 302), (600, 302), (600, 353), (337, 353)))
+            self.assertEqual(focus[4], ((0, 354), (600, 354), (600, 600), (0, 600)))
+        else:
+            self.assertEqual(focus[1], ((0, 0), (600, 0), (600, 302), (0, 302)))
+            self.assertEqual(focus[2], ((0, 303), (261, 303), (261, 353), (0, 353)))
+            self.assertEqual(focus[3], ((337, 303), (600, 303), (600, 353), (337, 353)))
+            self.assertEqual(focus[4], ((0, 354), (600, 354), (600, 600), (0, 600)))
+
+        # Test cases where the focus must fail
+        btn.selected = False
+        self.assertEqual(None, menu._draw_focus_widget(surface, btn))
+        btn.selected = True
+
+        # Set active false
+        btn.active = False
+        self.assertEqual(None, menu._draw_focus_widget(surface, btn))
+        btn.active = True
+
+        btn.hide()
+        self.assertEqual(None, menu._draw_focus_widget(surface, btn))
+        btn.show()
+
+        btn.is_selectable = False
+        self.assertEqual(None, menu._draw_focus_widget(surface, btn))
+        btn.is_selectable = True
+
+        menu._mouse_motion_selection = False
+        self.assertEqual(None, menu._draw_focus_widget(surface, btn))
+        menu._mouse_motion_selection = True
+
+        btn.active = True
+        btn.selected = True
+        self.assertNotEqual(None, menu._draw_focus_widget(surface, btn))
+
     def test_visible(self):
         """
         Test visible.
@@ -1168,3 +1295,36 @@ class MenuTest(unittest.TestCase):
         self.assertEqual(c2, -1)
         self.assertEqual(r2, -1)
         self.assertEqual(i2, -1)
+
+        menu = MenuUtils.generic_menu(title='menu')
+        btn = menu.add_button('button', None)
+        self.assertTrue(btn.selected)
+        btn.hide()
+
+        # As theres no more visible widgets, index must be -1
+        self.assertEqual(menu._index, -1)
+        self.assertFalse(btn.selected)
+        btn.show()
+
+        # Widget should be selected, and index must be 0
+        self.assertTrue(btn.selected)
+        self.assertEqual(menu._index, 0)
+
+        # Hide button, and set is as unselectable
+        btn.hide()
+        btn.is_selectable = False
+        self.assertEqual(menu._index, -1)
+        btn.show()
+
+        # Now, as widget is not selectable, button should not
+        # be selected and index still -1
+        self.assertFalse(btn.selected)
+        self.assertEqual(menu._index, -1)
+
+        # Set selectable again
+        btn.is_selectable = True
+        btn.select()
+        self.assertEqual(menu._index, -1)  # Menu still don't considers widget as selected
+        self.assertEqual(menu.get_selected_widget(), None)
+        btn.select(update_menu=True)
+        self.assertEqual(menu.get_selected_widget(), btn)
diff --git a/test/test_selection.py b/test/test_selection.py
index e00541b4..cea70f76 100644
--- a/test/test_selection.py
+++ b/test/test_selection.py
@@ -60,10 +60,27 @@ class WidgetSelectionTest(unittest.TestCase):
         Test highlight selection.
         """
         w = Button('epic')
-        w.set_selection_effect(HighlightSelection())
+        border_width = 1
+        margin_x = 18
+        margin_y = 10
+        w.set_selection_effect(HighlightSelection(
+            border_width=border_width,
+            margin_x=margin_x,
+            margin_y=margin_y
+        ))
         self.menu.add_generic_widget(w)
         self.menu.draw(surface)
 
+        sel = w.get_selection_effect()
+        self.assertEqual(sel.get_height(), margin_y + 2 * border_width)
+        self.assertEqual(sel.get_width(), margin_x + 2 * border_width)
+
+        # Test inflate
+        rect = w.get_rect()
+        inflate_rect = sel.inflate(rect)
+        self.assertEqual(-inflate_rect.x + rect.x, sel.get_width() / 2)
+        self.assertEqual(-inflate_rect.y + rect.y, sel.get_height() / 2)
+
     def test_none(self):
         """
         Test highlight selection.
@@ -72,3 +89,7 @@ class WidgetSelectionTest(unittest.TestCase):
         w.set_selection_effect(NoneSelection())
         self.menu.add_generic_widget(w)
         self.menu.draw(surface)
+
+        rect = w.get_rect()
+        new_rect = w.get_selection_effect().inflate(rect)
+        self.assertTrue(rect == new_rect)
diff --git a/test/test_widgets.py b/test/test_widgets.py
index bfcef009..c4c94c65 100644
--- a/test/test_widgets.py
+++ b/test/test_widgets.py
@@ -73,49 +73,49 @@ class WidgetsTest(unittest.TestCase):
         btn2 = menu.add_button('nice', None, onselect=on_select)
         self.assertEqual(test[0], btn)
         btn2.is_selectable = False
-        btn2.set_selected()
+        btn2.select()
         self.assertEqual(test[0], btn)
         btn2.is_selectable = True
-        btn2.set_selected()
+        btn2.select()
         self.assertEqual(test[0], btn2)
 
         # Color
         color = menu.add_color_input('nice', 'rgb', onselect=on_select)
-        color.set_selected()
+        color.select()
         self.assertEqual(test[0], color)
 
         # Image
         image = menu.add_image(pygame_menu.baseimage.IMAGE_EXAMPLE_GRAY_LINES, onselect=on_select)
-        image.set_selected()
+        image.select()
         self.assertEqual(test[0], color)
         image.is_selectable = True
-        image.set_selected()
+        image.select()
         self.assertEqual(test[0], image)
 
         # Label
         label = menu.add_label('label', onselect=on_select)
         label.is_selectable = True
-        label.set_selected()
+        label.select()
         self.assertEqual(test[0], label)
 
         # None, it cannot be selected
         none = menu.add_none_widget()
-        none.set_selected()
+        none.select()
         self.assertEqual(test[0], label)
 
         # Selector
         selector = menu.add_selector('nice', ['nice', 'epic'], onselect=on_select)
-        selector.set_selected()
+        selector.select()
         self.assertEqual(test[0], selector)
 
         # Textinput
         text = menu.add_text_input('nice', onselect=on_select)
-        text.set_selected()
+        text.select()
         self.assertEqual(test[0], text)
 
         # Vmargin
         vmargin = menu.add_vertical_margin(10)
-        vmargin.set_selected()
+        vmargin.select()
         self.assertEqual(test[0], text)
 
     def test_nonascii(self):
@@ -1081,7 +1081,7 @@ class WidgetsTest(unittest.TestCase):
         self.assertEqual(wid._max_height[0], None)
 
         # Selection
-        wid.set_selected()
+        wid.select()
         self.assertFalse(wid.selected)
         self.assertFalse(wid.is_selectable)
 
@@ -1101,7 +1101,6 @@ class WidgetsTest(unittest.TestCase):
         wid.set_sound(None)
         self.assertNotEqual(wid.sound, None)
 
-    # noinspection PyArgumentEqualDefault
     def test_scrollbar(self):
         """
         Test ScrollBar widget.
@@ -1126,9 +1125,7 @@ class WidgetsTest(unittest.TestCase):
                        page_ctrl_color=(235, 235, 230))
         self.assertEqual(sb.get_thickness(), 80)
 
-        sb.set_shadow(color=(245, 245, 245),
-                      position=_locals.POSITION_SOUTHEAST,
-                      offset=2)
+        sb.set_shadow(color=(245, 245, 245), position=_locals.POSITION_SOUTHEAST)
 
         sb.set_position(x, y)
 
@@ -1148,6 +1145,7 @@ class WidgetsTest(unittest.TestCase):
         sb.draw(surface)
 
         # Test remove onreturn
+        # noinspection PyArgumentEqualDefault
         sb = ScrollBar(length,
                        world_range,
                        '',
@@ -1156,3 +1154,24 @@ class WidgetsTest(unittest.TestCase):
                        )
         self.assertEqual(sb._on_return, None)
         self.assertTrue(sb._kwargs.get('onreturn', 0))
+
+        # Scrollbar ignores scalling
+        sb.scale(2, 2)
+        self.assertFalse(sb._scale[0])
+        sb.resize(2, 2)
+        self.assertFalse(sb._scale[0])
+        sb.set_max_width(10)
+        self.assertEqual(sb._max_width[0], None)
+        sb.set_max_height(10)
+        self.assertEqual(sb._max_height[0], None)
+        sb._apply_font()
+        sb.set_padding(10)
+        self.assertEqual(sb._padding[0], 0)
+        sb.rotate(10)
+        self.assertEqual(sb._angle, 0)
+        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])