Source code for pygame_menu.themes

"""
pygame-menu
https://github.com/ppizarror/pygame-menu

THEMES
Theme class and predefined themes.
"""

__all__ = [

    # Main class
    'Theme',

    # Custom themes
    'THEME_BLUE',
    'THEME_DARK',
    'THEME_DEFAULT',
    'THEME_GREEN',
    'THEME_ORANGE',
    'THEME_SOLARIZED',

    # Colors
    'TRANSPARENT_COLOR'

]

import copy

from pygame_menu.baseimage import BaseImage
from pygame_menu.font import FontType, FONT_OPEN_SANS, assert_font
from pygame_menu.locals import POSITION_NORTHWEST, POSITION_SOUTHEAST, ALIGN_CENTER, \
    CURSOR_ARROW
from pygame_menu._scrollarea import get_scrollbars_from_position
from pygame_menu.utils import assert_alignment, assert_cursor, assert_vector, \
    assert_position, assert_color, format_color, assert_position_vector
from pygame_menu.widgets import HighlightSelection, NoneSelection, MENUBAR_STYLE_ADAPTIVE, \
    MENUBAR_STYLE_SIMPLE, MENUBAR_STYLE_TITLE_ONLY, MENUBAR_STYLE_TITLE_ONLY_DIAGONAL, \
    MENUBAR_STYLE_NONE, MENUBAR_STYLE_UNDERLINE, MENUBAR_STYLE_UNDERLINE_TITLE
from pygame_menu.widgets.core import Selection
from pygame_menu.widgets.core.widget import WidgetBorderPositionType, WIDGET_FULL_BORDER, \
    WIDGET_SHADOW_TYPE_ELLIPSE, WIDGET_SHADOW_TYPE_RECTANGULAR

from pygame_menu._types import ColorType, ColorInputType, Tuple, List, Union, Dict, \
    Any, Tuple2IntType, VectorInstance, Tuple2NumberType, NumberType, PaddingType, \
    Optional, Type, NumberInstance, PaddingInstance, Tuple3IntType, CursorType

TRANSPARENT_COLOR: ColorType = (0, 0, 0, 0)


def _check_menubar_style(style: int) -> bool:
    """
    Check menubar style.

    :param style: Style
    :return: ``True`` if correct
    """
    return style in (MENUBAR_STYLE_ADAPTIVE, MENUBAR_STYLE_SIMPLE, MENUBAR_STYLE_TITLE_ONLY,
                     MENUBAR_STYLE_TITLE_ONLY_DIAGONAL, MENUBAR_STYLE_NONE,
                     MENUBAR_STYLE_UNDERLINE, MENUBAR_STYLE_UNDERLINE_TITLE)


[docs]class Theme(object): """ Class defining the visual rendering of menus and widgets. .. note:: All colors must be defined with a tuple of 3 or 4 numbers in the formats: - (R, G, B) - (R, G, B, A) Red (R), Green (G), and Blue (B) must be numbers between ``0`` and ``255``. A means the alpha channel (opacity), if ``0`` the color is transparent, ``100`` means opaque. .. note:: Themes only modify visual behaviour of the Menu. For other options like rows/columns, enabling or disabling overflow, position, or Menu width/height see Menu parameters. :param background_color: Menu background color :type background_color: tuple, list, str, int, :py:class:`pygame.Color`, :py:class:`pygame_menu.baseimage.BaseImage` :param border_color: Menu border color. If border is an image, it will be split in 9 tiles to use top, left, bottom, right, and the corners :type border_color: tuple, list, str, int, :py:class:`pygame.Color`, :py:class:`pygame_menu.baseimage.BaseImage`, None :param border_width: Border width in px. Used only if ``border_color`` is not an image :type border_width: int :param cursor_color: Cursor color (used in some text-gathering widgets like ``TextInput``) :type cursor_color: tuple, list, str, int, :py:class:`pygame.Color` :param cursor_selection_color: Color of the text selection if the cursor is enabled on certain widgets :type cursor_selection_color: tuple, list, str, int, :py:class:`pygame.Color` :param cursor_switch_ms: Interval of cursor switch between off and on status :type cursor_switch_ms: int, float :param focus_background_color: Color of the widget focus, this must be a tuple of 4 elements (R, G, B, A) :type focus_background_color: tuple, list, str, int, :py:class:`pygame.Color` :param fps: Menu max fps (frames per second). If ``0`` there's no limit :type fps: int, float :param readonly_color: Color of the widget in readonly mode :type readonly_color: tuple, list, str, int, :py:class:`pygame.Color` :param readonly_selected_color: Color of the selected widget in readonly mode :type readonly_selected_color: tuple, list, str, int, :py:class:`pygame.Color` :param scrollarea_outer_margin: Outer ScrollArea margin in px; the tuple is added to computed ScrollArea width/height, it can add a margin to bottom/right scrolls after widgets. If value less than ``1`` use percentage of width/height. It cannot be a negative value :type scrollarea_outer_margin: tuple, list :param scrollarea_position: Position of ScrollArea scrollbars. See :py:mod:`pygame_menu.locals` :type scrollarea_position: str :param scrollbar_color: Scrollbars color :type scrollbar_color: tuple, list, str, int, :py:class:`pygame.Color` :param scrollbar_cursor: Scrollbar cursor if mouse is placed over. If ``None`` the scrollbar don't change the cursor :type scrollbar_cursor: int, :py:class:`pygame.cursors.Cursor`, None :param scrollbar_shadow: Indicate if a shadow is drawn on each scrollbar :type scrollbar_shadow: bool :param scrollbar_shadow_color: Color of the scrollbar shadow :type scrollbar_shadow_color: tuple, list, str, int, :py:class:`pygame.Color` :param scrollbar_shadow_offset: Offset of the scrollbar shadow :type scrollbar_shadow_offset: int :param scrollbar_shadow_position: Position of the scrollbar shadow. See :py:mod:`pygame_menu.locals` :type scrollbar_shadow_position: str :param scrollbar_slider_color: Color of the sliders :type scrollbar_slider_color: tuple, list, str, int, :py:class:`pygame.Color` :param scrollbar_slider_hover_color: Color of the slider if hovered or clicked :type scrollbar_slider_hover_color: tuple, list, str, int, :py:class:`pygame.Color` :param scrollbar_slider_pad: Space between slider and scrollbars borders :type scrollbar_slider_pad: int, float :param scrollbar_thick: Scrollbar thickness in px :type scrollbar_thick: int :param selection_color: Color of the selected widget. It updates both selected font and ``widget_selection_effect`` color :type selection_color: tuple, list, str, int, :py:class:`pygame.Color` :param surface_clear_color: Surface clear color before applying background function :type surface_clear_color: tuple, list, str, int, :py:class:`pygame.Color` :param title: Title is enabled/disabled. If disabled the object is ``hidden`` :type title: bool :param title_background_color: Title background color :type title_background_color: tuple, list, str, int, :py:class:`pygame.Color` :param title_bar_modify_scrollarea: If ``True`` title bar modifies the scrollbars of the scrollarea depending on the style :type title_bar_modify_scrollarea: bool :param title_bar_style: Style of the title, use :py:class:`pygame_menu.widgets.MenuBar` widget styles :type title_bar_style: int :param title_close_button: Draw a back-box button on header to close the Menu. If user moves through nested submenus this buttons turns to a back-arrow :type title_close_button: bool :param title_close_button_background_color: Title back-box background color :type title_close_button_background_color: tuple, list, str, int, :py:class:`pygame.Color` :param title_close_button_cursor: Cursor applied over title close button :type title_close_button_cursor: int, :py:class:`pygame.cursors.Cursor`, None :param title_fixed: If ``True`` title is drawn over the scrollarea, forcing widget surface area to be drawn behind the title :type title_fixed: bool :param title_floating: If ``True`` title don't contribute height to the Menu. Thus, scroll uses full menu width/height :type title_floating: bool :param title_font: Title font :type title_font: str, :py:class:`pygame.font.Font`, :py:class:`pathlib.Path` :param title_font_antialias: Title font renders with antialiasing :type title_font_antialias: bool :param title_font_color: Title font color :type title_font_color: tuple, list, str, int, :py:class:`pygame.Color` :param title_font_shadow: Enable title font shadow :type title_font_shadow: bool :param title_font_shadow_color: Title font shadow color :type title_font_shadow_color: tuple, list, str, int, :py:class:`pygame.Color` :param title_font_shadow_offset: Offset of title font shadow in px :type title_font_shadow_offset: int :param title_font_shadow_position: Position of the title font shadow. See :py:mod:`pygame_menu.locals` :type title_font_shadow_position: str :param title_font_size: Font size of the title :type title_font_size: int :param title_offset: Offset (x-position, y-position) of title in px :type title_offset: tuple, list :param title_updates_pygame_display: If ``True`` the menu title updates See :py:mod:`pygame.display.caption` automatically on draw :type title_updates_pygame_display: bool :param widget_alignment: Widget default `alignment <https://pygame-menu.readthedocs.io/en/latest/_source/themes.html#alignment>`_. See :py:mod:`pygame_menu.locals` :type widget_alignment: str :param widget_background_color: Background color of a widget, it can be a color, ``None`` (transparent), or a BaseImage object. Background fills the entire widget + the padding :type widget_background_color: tuple, list, str, int, :py:class:`pygame.Color`, :py:class:`pygame_menu.baseimage.BaseImage`, None :param widget_background_inflate: Inflate background on x-axis and y-axis (x, y) in px. By default, it uses the highlight margin. This parameter is visual only. For modifying widget size use padding instead :type widget_background_inflate: tuple, list :param widget_background_inflate_to_selection: If ``True`` widget will inflate to match selection effect margin and overrides ``widget_background_inflate`` :type widget_background_inflate_to_selection: bool :param widget_border_color: Widget border color :type widget_border_color: tuple, list, str, int, :py:class:`pygame.Color` :param widget_border_inflate: Widget inflate size on x-axis and y-axis (x, y) in px. These values cannot be negative :type widget_border_inflate: tuple, list :param widget_border_position: Widget border positioning. It can be a single position, or a tuple/list of positions. Only are accepted: north, south, east, and west. See :py:mod:`pygame_menu.locals` :type widget_border_position: str, tuple, list :param widget_border_width: Widget border width in px. If ``0`` the border is disabled. Border width don't contribute to the widget width/height, it's visual-only :type widget_border_width: int :param widget_box_arrow_color: Widget box arrow color, used by some widgets such as DropSelect, Fancy Selector, etc. :type widget_box_arrow_color: tuple, list, str, int, :py:class:`pygame.Color` :param widget_box_arrow_margin: Widget box arrow margin (left, right, vertical) in px, used by some widgets such as DropSelect, Fancy Selector, etc. :type widget_box_arrow_margin: tuple :param widget_box_background_color: Widget box background color, used by some widgets such as DropSelect, Fancy Selector, etc. :type widget_box_background_color: tuple, list, str, int, :py:class:`pygame.Color` :param widget_box_border_color: Widget box border color, used by some widgets such as DropSelect, Fancy Selector, etc. :type widget_box_border_color: tuple, list, str, int, :py:class:`pygame.Color` :param widget_box_border_width: Widget box border width in px, used by some widgets such as DropSelect, Fancy Selector, etc. :type widget_box_border_width: int :param widget_box_inflate: Widget box inflate on x-axis and y-axis (x, y) in px, used by some widgets such as DropSelect, Fancy Selector, etc. :type widget_box_inflate: tuple, list :param widget_box_margin: Box margin on x-axis and y-axis (x, y) in px :type widget_box_margin: tuple, list :param widget_cursor: Widget cursor if mouse is placed over. If ``None`` the widget don't change the cursor :type widget_cursor: int, :py:class:`pygame.cursors.Cursor`, None :param widget_font: Widget font path or name :type widget_font: str, :py:class:`pygame.font.Font`, :py:class:`pathlib.Path` :param widget_font_antialias: Widget font renders with antialiasing :type widget_font_antialias: bool :param widget_font_background_color: Widget font background color. If ``None`` the value will be the same as ``background_color`` if it is a color object and if ``widget_font_background_color_from_menu`` is ``True`` and ``widget_background_color`` is ``None`` :type widget_font_background_color: tuple, list, str, int, :py:class:`pygame.Color`, None :param widget_font_background_color_from_menu: Use Menu background color as font background color. Disabled by default :type widget_font_background_color_from_menu: bool :param widget_font_color: Color of the font :type widget_font_color: tuple, list, str, int, :py:class:`pygame.Color` :param widget_font_shadow: Indicate if the widget font shadow is enabled :type widget_font_shadow: bool :param widget_font_shadow_color: Color of the widget font shadow :type widget_font_shadow_color: tuple, list, str, int, :py:class:`pygame.Color` :param widget_font_shadow_offset: Offset of the widget font shadow in px :type widget_font_shadow_offset: int :param widget_font_shadow_position: Position of the widget font shadow. See :py:mod:`pygame_menu.locals` :type widget_font_shadow_position: str :param widget_font_size: Font size :type widget_font_size: int :param widget_margin: Horizontal and vertical margin of each element in Menu in px :type widget_margin: tuple, list :param widget_padding: Padding of the widget according to CSS rules. It can be a single digit, or a tuple of 2, 3, or 4 elements. Padding modifies widget width/height :type widget_padding: int, float, tuple, list :param widget_offset: x-axis and y-axis (x, y) offset of widgets within Menu in px respect to top-left corner. If value less than ``1`` use percentage of width/height. It cannot be a negative value :type widget_offset: tuple, list :param widget_selection_effect: Widget selection effect object. This is visual-only, the selection properties does not affect widget height/width :type widget_selection_effect: :py:class:`pygame_menu.widgets.core.Selection`, None :param widget_shadow_aa: Widget shadow antialiasing factor, default is x4 :type widget_shadow_aa: int :param widget_shadow_color: The color of the widget shadow :type widget_shadow_color: tuple, list, str, int, :py:class:`pygame.Color` :param widget_shadow_radius: The border radius of the widget shadow :type widget_shadow_radius: int :param widget_shadow_type: The shadow type, it can be 'rectangular' or 'ellipse' :type widget_shadow_type: str :param widget_shadow_width: The width of the shadow in px :type widget_shadow_width: int :param widget_tab_size: Widget tab size :type widget_tab_size: int :param widget_url_color: Color of url text links :type widget_url_color: tuple, list, str, int, :py:class:`pygame.Color` """ _disable_validation: bool background_color: Union[ColorType, 'BaseImage'] border_color: Union[ColorType, 'BaseImage'] cursor_color: ColorType cursor_selection_color: ColorType cursor_switch_ms: NumberType focus_background_color: ColorType fps: NumberType readonly_color: ColorType readonly_selected_color: ColorType scrollarea_outer_margin: Tuple2NumberType scrollarea_position: str scrollbar_color: ColorType scrollbar_cursor: CursorType scrollbar_shadow: bool scrollbar_shadow_color: ColorType scrollbar_shadow_offset: int scrollbar_shadow_position: str scrollbar_slider_color: ColorType scrollbar_slider_hover_color: ColorType scrollbar_slider_pad: NumberType scrollbar_thick: int selection_color: ColorType surface_clear_color: ColorType title: bool title_background_color: ColorType title_bar_modify_scrollarea: bool title_bar_style: int title_close_button: bool title_close_button_background_color: ColorType title_close_button_cursor: CursorType title_fixed: bool title_floating: bool title_font: FontType title_font_antialias: bool title_font_color: ColorType title_font_shadow: bool title_font_shadow_color: ColorType title_font_shadow_offset: int title_font_shadow_position: str title_font_size: int title_offset: Tuple2NumberType title_updates_pygame_display: bool widget_alignment: str widget_background_color: Optional[Union[ColorType, 'BaseImage']] widget_background_inflate: Tuple2IntType widget_background_inflate_to_selection: bool widget_border_color: ColorType widget_border_inflate: Tuple2IntType widget_border_position: WidgetBorderPositionType widget_border_width: int widget_box_arrow_color: ColorType widget_box_arrow_margin: Tuple3IntType widget_box_background_color: ColorType widget_box_border_color: ColorType widget_box_border_width: int widget_box_inflate: Tuple2IntType widget_box_margin: Tuple2NumberType widget_cursor: CursorType widget_font: FontType widget_font_antialias: str widget_font_background_color: Optional[ColorType] widget_font_background_color_from_menu: bool widget_font_color: ColorType widget_font_shadow: bool widget_font_shadow_color: ColorType widget_font_shadow_offset: NumberType widget_font_shadow_position: str widget_font_size: int widget_margin: Tuple2NumberType widget_offset: Tuple2NumberType widget_padding: PaddingType widget_selection_effect: 'pygame_menu.widgets.core.Selection' widget_shadow_aa: int widget_shadow_color: ColorType widget_shadow_radius: int widget_shadow_type: str widget_shadow_width: int widget_tab_size: int widget_url_color: ColorType def __init__(self, **kwargs) -> None: # Menu general self.background_color = self._get(kwargs, 'background_color', 'color_image', (220, 220, 220)) self.border_color = self._get(kwargs, 'border_color', 'color_image_none') self.border_width = self._get(kwargs, 'border_width', int, 0) self.focus_background_color = self._get(kwargs, 'focus_background_color', 'color', (0, 0, 0, 180)) self.fps = self._get(kwargs, 'fps', NumberInstance, 30) self.readonly_color = self._get(kwargs, 'readonly_color', 'color', (120, 120, 120)) self.readonly_selected_color = self._get(kwargs, 'readonly_selected_color', 'color', (190, 190, 190)) self.selection_color = self._get(kwargs, 'selection_color', 'color', (255, 255, 255)) self.surface_clear_color = self._get(kwargs, 'surface_clear_color', 'color', (0, 0, 0)) # Cursor/Text gathering self.cursor_color = self._get(kwargs, 'cursor_color', 'color', (0, 0, 0)) self.cursor_selection_color = self._get(kwargs, 'cursor_selection_color', 'color', (30, 30, 30, 120)) self.cursor_switch_ms = self._get(kwargs, 'cursor_switch_ms', NumberInstance, 750) # Menubar/Title self.title = self._get(kwargs, 'title', bool, True) self.title_background_color = self._get(kwargs, 'title_background_color', 'color', (70, 70, 70)) self.title_bar_modify_scrollarea = self._get(kwargs, 'title_bar_modify_scrollarea', bool, True) self.title_bar_style = self._get(kwargs, 'title_bar_style', int, MENUBAR_STYLE_ADAPTIVE) self.title_close_button = self._get(kwargs, 'title_close_button', bool, True) self.title_close_button_background_color = self._get(kwargs, 'title_close_button_background_color', 'color', (255, 255, 255)) self.title_close_button_cursor = self._get(kwargs, 'title_close_button_cursor', 'cursor') self.title_fixed = self._get(kwargs, 'title_fixed', bool, True) self.title_floating = self._get(kwargs, 'title_floating', bool, False) self.title_font = self._get(kwargs, 'title_font', 'font', FONT_OPEN_SANS) self.title_font_antialias = self._get(kwargs, 'title_font_antialias', bool, True) self.title_font_color = self._get(kwargs, 'title_font_color', 'color', (220, 220, 220)) self.title_font_shadow = self._get(kwargs, 'title_font_shadow', bool, False) self.title_font_shadow_color = self._get(kwargs, 'title_font_shadow_color', 'color', (0, 0, 0)) self.title_font_shadow_offset = self._get(kwargs, 'title_font_shadow_offset', int, 2) self.title_font_shadow_position = self._get(kwargs, 'title_font_shadow_position', 'position', POSITION_NORTHWEST) self.title_font_size = self._get(kwargs, 'title_font_size', int, 40) self.title_offset = self._get(kwargs, 'title_offset', 'tuple2', (5, -1)) self.title_updates_pygame_display = self._get(kwargs, 'title_updates_pygame_display', bool, False) # ScrollArea self.scrollarea_outer_margin = self._get(kwargs, 'scrollarea_outer_margin', 'tuple2', (0, 0)) self.scrollarea_position = self._get(kwargs, 'scrollarea_position', str, POSITION_SOUTHEAST) # ScrollBar self.scrollbar_color = self._get(kwargs, 'scrollbar_color', 'color', (235, 235, 235)) self.scrollbar_cursor = self._get(kwargs, 'scrollbar_cursor', 'cursor') self.scrollbar_shadow = self._get(kwargs, 'scrollbar_shadow', bool, False) self.scrollbar_shadow_color = self._get(kwargs, 'scrollbar_shadow_color', 'color', (0, 0, 0)) self.scrollbar_shadow_offset = self._get(kwargs, 'scrollbar_shadow_offset', int, 2) self.scrollbar_shadow_position = self._get(kwargs, 'scrollbar_shadow_position', 'position', POSITION_NORTHWEST) self.scrollbar_slider_color = self._get(kwargs, 'scrollbar_slider_color', 'color', (200, 200, 200)) self.scrollbar_slider_hover_color = self._get(kwargs, 'scrollbar_slider_hover_color', 'color', (170, 170, 170)) self.scrollbar_slider_pad = self._get(kwargs, 'scrollbar_slider_pad', NumberInstance, 0) self.scrollbar_thick = self._get(kwargs, 'scrollbar_thick', int, 20) # Generic widget themes self.widget_alignment = self._get(kwargs, 'widget_alignment', 'alignment', ALIGN_CENTER) self.widget_background_color = self._get(kwargs, 'widget_background_color', 'color_image_none') self.widget_background_inflate = self._get(kwargs, 'background_inflate', 'tuple2int', (0, 0)) self.widget_background_inflate_to_selection = self._get(kwargs, 'widget_background_inflate_to_selection', bool, False) self.widget_border_color = self._get(kwargs, 'widget_border_color', 'color', (0, 0, 0)) self.widget_border_inflate = self._get(kwargs, 'widget_border_inflate', 'tuple2int', (0, 0)) self.widget_border_position = self._get(kwargs, 'widget_border_position', 'position_vector', WIDGET_FULL_BORDER) self.widget_border_width = self._get(kwargs, 'widget_border_width', int, 0) self.widget_box_arrow_color = self._get(kwargs, 'widget_box_arrow_color', 'color', (150, 150, 150)) self.widget_box_arrow_margin = self._get(kwargs, 'widget_box_arrow_margin', 'tuple3int', (5, 5, 0)) self.widget_box_background_color = self._get(kwargs, 'widget_box_background_color', 'color', (255, 255, 255)) self.widget_box_border_color = self._get(kwargs, 'widget_box_border_color', 'color', (0, 0, 0)) self.widget_box_border_width = self._get(kwargs, 'widget_box_border_width', int, 1) self.widget_box_inflate = self._get(kwargs, 'widget_box_inflate', 'tuple2int', (0, 0)) self.widget_box_margin = self._get(kwargs, 'widget_box_margin', 'tuple2', (25, 0)) self.widget_cursor = self._get(kwargs, 'widget_cursor', 'cursor', CURSOR_ARROW) self.widget_font = self._get(kwargs, 'widget_font', 'font', FONT_OPEN_SANS) self.widget_font_antialias = self._get(kwargs, 'widget_font_antialias', bool, True) self.widget_font_background_color = self._get(kwargs, 'widget_font_background_color', 'color_none', ) self.widget_font_background_color_from_menu = self._get(kwargs, 'widget_font_background_color_from_menu', bool, False) self.widget_font_color = self._get(kwargs, 'widget_font_color', 'color', (70, 70, 70)) self.widget_font_shadow = self._get(kwargs, 'widget_font_shadow', bool, False) self.widget_font_shadow_color = self._get(kwargs, 'widget_font_shadow_color', 'color', (0, 0, 0)) self.widget_font_shadow_offset = self._get(kwargs, 'widget_font_shadow_offset', int, 2) self.widget_font_shadow_position = self._get(kwargs, 'widget_font_shadow_position', 'position', POSITION_NORTHWEST) self.widget_font_size = self._get(kwargs, 'widget_font_size', int, 30) self.widget_margin = self._get(kwargs, 'widget_margin', 'tuple2', (0, 0)) self.widget_offset = self._get(kwargs, 'widget_offset', 'tuple2', (0, 0)) self.widget_padding = self._get(kwargs, 'widget_padding', PaddingInstance, (4, 8)) self.widget_selection_effect = self._get(kwargs, 'widget_selection_effect', Selection, HighlightSelection(margin_x=0, margin_y=0)) self.widget_shadow_aa = self._get(kwargs, 'widget_shadow_aa', int, 4) self.widget_shadow_color = self._get(kwargs, 'widget_shadow_color', 'color', (0, 0, 0)) self.widget_shadow_radius = self._get(kwargs, 'widget_shadow_radius', int, 0) self.widget_shadow_type = self._get(kwargs, 'widget_shadow_type', str, WIDGET_SHADOW_TYPE_RECTANGULAR) self.widget_shadow_width = self._get(kwargs, 'widget_shadow_width', int, 0) self.widget_tab_size = self._get(kwargs, 'widget_tab_size', int, 4) self.widget_url_color = self._get(kwargs, 'widget_url_color', 'color', (6, 69, 173)) # Upon this, no more kwargs should exist, raise exception if there's more for invalid_keyword in kwargs.keys(): raise ValueError(f'parameter Theme.{invalid_keyword} does not exist') # Test purpose only, if True disables any validation self._disable_validation = False
[docs] def validate(self) -> 'Theme': """ Validate the values of the theme. If there's an invalid parameter throws an ``AssertionError``. This function also converts all lists to tuples. This is done because lists are mutable. :return: Self reference """ if self._disable_validation: return self # Boolean asserts assert isinstance(self.scrollbar_shadow, bool) assert isinstance(self.title_bar_modify_scrollarea, bool) assert isinstance(self.title_close_button, bool) assert isinstance(self.title_font_antialias, bool) assert isinstance(self.title_font_shadow, bool) assert isinstance(self.widget_font_antialias, bool) assert isinstance(self.widget_font_background_color_from_menu, bool) assert isinstance(self.widget_font_shadow, bool) # Value type checks assert_alignment(self.widget_alignment) assert_cursor(self.scrollbar_cursor) assert_cursor(self.title_close_button_cursor) assert_cursor(self.widget_cursor) assert_font(self.title_font) assert_font(self.widget_font) assert_position(self.scrollbar_shadow_position) assert_position(self.title_font_shadow_position) assert_position(self.widget_font_shadow_position) assert_position_vector(self.widget_border_position) assert _check_menubar_style(self.title_bar_style) assert get_scrollbars_from_position(self.scrollarea_position) is not None # Check selection effect if None if self.widget_selection_effect is None: self.widget_selection_effect = NoneSelection() assert isinstance(self.border_width, int) assert isinstance(self.cursor_switch_ms, NumberInstance) assert isinstance(self.fps, NumberInstance) assert isinstance(self.scrollbar_shadow_offset, int) assert isinstance(self.scrollbar_slider_pad, NumberInstance) assert isinstance(self.scrollbar_thick, int) assert isinstance(self.title, bool) assert isinstance(self.title_fixed, bool) assert isinstance(self.title_floating, bool) assert isinstance(self.title_font_shadow_offset, int) assert isinstance(self.title_font_size, int) assert isinstance(self.title_updates_pygame_display, bool) assert isinstance(self.widget_background_inflate_to_selection, bool) assert isinstance(self.widget_border_width, int) assert isinstance(self.widget_box_border_width, int) assert isinstance(self.widget_font_shadow_offset, int) assert isinstance(self.widget_font_size, int) assert isinstance(self.widget_padding, PaddingInstance) assert isinstance(self.widget_selection_effect, Selection) assert isinstance(self.widget_shadow_aa, int) assert isinstance(self.widget_shadow_radius, int) assert isinstance(self.widget_shadow_width, int) assert isinstance(self.widget_tab_size, int) # Format colors, this converts all color lists to tuples automatically, # if it is an image, return the same object self.background_color = self._format_color_opacity(self.background_color) self.border_color = self._format_color_opacity(self.border_color, none=True) self.cursor_color = self._format_color_opacity(self.cursor_color) self.cursor_selection_color = self._format_color_opacity(self.cursor_selection_color) self.focus_background_color = self._format_color_opacity(self.focus_background_color) self.readonly_color = self._format_color_opacity(self.readonly_color) self.readonly_selected_color = self._format_color_opacity(self.readonly_selected_color) self.scrollbar_color = self._format_color_opacity(self.scrollbar_color) self.scrollbar_shadow_color = self._format_color_opacity(self.scrollbar_shadow_color) self.scrollbar_slider_color = self._format_color_opacity(self.scrollbar_slider_color) self.scrollbar_slider_hover_color = self._format_color_opacity(self.scrollbar_slider_hover_color) self.selection_color = self._format_color_opacity(self.selection_color) self.surface_clear_color = self._format_color_opacity(self.surface_clear_color) self.title_background_color = self._format_color_opacity(self.title_background_color) self.title_close_button_background_color = self._format_color_opacity(self.title_close_button_background_color) self.title_font_color = self._format_color_opacity(self.title_font_color) self.title_font_shadow_color = self._format_color_opacity(self.title_font_shadow_color) self.widget_background_color = self._format_color_opacity(self.widget_background_color, none=True) self.widget_border_color = self._format_color_opacity(self.widget_border_color) self.widget_box_arrow_color = self._format_color_opacity(self.widget_box_arrow_color) self.widget_box_background_color = self._format_color_opacity(self.widget_box_background_color) self.widget_box_border_color = self._format_color_opacity(self.widget_box_border_color) self.widget_font_background_color = self._format_color_opacity(self.widget_font_background_color, none=True) self.widget_font_color = self._format_color_opacity(self.widget_font_color) self.widget_font_shadow_color = self._format_color_opacity(self.widget_font_shadow_color) self.widget_shadow_color = self._format_color_opacity(self.widget_shadow_color) self.widget_url_color = self._format_color_opacity(self.widget_url_color) # List to tuple self.scrollarea_outer_margin = self._vec_to_tuple(self.scrollarea_outer_margin, 2, NumberInstance) self.title_offset = self._vec_to_tuple(self.title_offset, 2, NumberInstance) self.widget_background_inflate = self._vec_to_tuple(self.widget_background_inflate, 2, int) self.widget_border_inflate = self._vec_to_tuple(self.widget_border_inflate, 2, int) self.widget_box_arrow_margin = self._vec_to_tuple(self.widget_box_arrow_margin, 3, int) self.widget_box_inflate = self._vec_to_tuple(self.widget_box_inflate, 2, int) self.widget_box_margin = self._vec_to_tuple(self.widget_box_margin, 2, NumberInstance) self.widget_margin = self._vec_to_tuple(self.widget_margin, 2, NumberInstance) if isinstance(self.widget_padding, VectorInstance): self.widget_padding = self._vec_to_tuple(self.widget_padding) assert 2 <= len(self.widget_padding) <= 4, \ 'widget padding tuple length must be 2, 3 or 4' for p in self.widget_padding: assert isinstance(p, NumberInstance), \ 'each padding element must be numeric (integer or float)' assert p >= 0, \ 'all padding elements must be equal or greater than zero' else: assert self.widget_padding >= 0, 'padding cannot be a negative number' self.widget_offset = self._vec_to_tuple(self.widget_offset, 2, NumberInstance) # Check sizes assert self.border_width >= 0, 'border width must be equal or greater than zero' assert self.scrollarea_outer_margin[0] >= 0 and self.scrollarea_outer_margin[1] >= 0, \ 'scroll area outer margin must be equal or greater than zero on both axis' assert self.widget_offset[0] >= 0 and self.widget_offset[1] >= 0, \ 'widget offset must be equal or greater than zero' assert self.widget_background_inflate[0] >= 0 and self.widget_background_inflate[1] >= 0, \ 'widget background inflate must be equal or greater than zero on both axis' assert self.widget_border_inflate[0] >= 0 and self.widget_border_inflate[1] >= 0, \ 'widget border inflate must be equal or greater than zero on both axis' assert self.widget_box_inflate[0] >= 0 and self.widget_box_inflate[1] >= 0, \ 'widget box inflate inflate must be equal or greater than zero on both axis' assert self.widget_shadow_width >= 0 and self.widget_shadow_radius >= 0, \ 'widget shadow width and radius must be equal or greater than zero' # Check shadow type assert self.widget_shadow_type in (WIDGET_SHADOW_TYPE_ELLIPSE, WIDGET_SHADOW_TYPE_RECTANGULAR), \ 'widget shadow type must be ellipse or rectangular' assert self.cursor_switch_ms > 0, 'cursor switch ms must be greater than zero' assert self.fps >= 0, 'fps must be equal or greater than zero' assert self.scrollbar_shadow_offset > 0, 'scrollbar shadow offset must be greater than zero' assert self.scrollbar_slider_pad >= 0, 'slider pad must be equal or greater than zero' assert self.scrollbar_thick > 0, 'scrollbar thickness must be greater than zero' assert self.title_font_size > 0, 'title font size must be greater than zero' assert self.widget_border_width >= 0, 'widget border width must be equal or greater than zero' assert self.widget_box_border_width >= 0, 'widget border box width must be equal or greater than zero' assert self.widget_font_shadow_offset > 0, 'widget font shadow offset must be greater than zero' assert self.widget_font_size > 0, 'widget font size must be greater than zero' assert self.widget_shadow_aa >= 1, 'widget shadow antialiasing must be equal or greater than one' assert self.widget_tab_size >= 0, 'widget tab size must be equal or greater than zero' # Color asserts assert self.focus_background_color[3] != 0, \ 'focus background color cannot be fully transparent, suggested opacity between 1 and 255' return self
[docs] def set_background_color_opacity(self, opacity: float) -> 'Theme': """ Modify the Menu background color with given opacity. :param opacity: Opacity value, from ``0`` (transparent) to ``1`` (opaque) :return: Self reference """ self.background_color = assert_color(self.background_color) assert isinstance(opacity, NumberInstance) assert 0 <= opacity <= 1, 'opacity must be a number between 0 (transparent) and 1 (opaque)' self.background_color = (self.background_color[0], self.background_color[1], self.background_color[2], int(float(opacity) * 255)) return self
@staticmethod def _vec_to_tuple(obj: Union[Tuple, List], check_length: int = 0, check_instance: Type = Any) -> Tuple: """ Return a tuple from a list or tuple object. :param obj: Object :param check_length: Check length if not zero :return: Tuple """ if isinstance(obj, tuple): v = obj elif isinstance(obj, list): v = tuple(obj) else: if check_length == 0: raise ValueError('object is not a vector') raise ValueError(f'object is not a vector of length {check_length}') if check_length > 0: if len(v) != check_length: raise ValueError(f'object is not a vector of length {check_length}') if check_instance is not Any: for i in v: assert isinstance(i, check_instance), \ f'{i} element of tuple {v} is not {check_instance} instance' return v
[docs] def copy(self) -> 'Theme': """ Creates a deep copy of the object. :return: Copied theme """ self.validate() return copy.deepcopy(self)
def __copy__(self) -> 'Theme': """ Copy method. :return: Copied theme """ return self.copy() @staticmethod def _format_color_opacity( color: Optional[Union[ColorInputType, 'BaseImage']], none: bool = False ) -> Optional[Union[ColorType, 'BaseImage']]: """ Adds opacity to a 3 channel color. (R,G,B) -> (R,G,B,A) if the color has not an alpha channel. Also updates the opacity to a number between ``0`` (transparent) and ``255`` (opaque). Color may be an Image, so if this is the case return the same object. - If the color is a list, return a tuple. - If the color is ``None``, return ``None`` if ``None`` is True. :param color: Color object :param none: If ``True`` Color can be ``None`` :return: Color in the same format """ if isinstance(color, BaseImage): return color if color is None and none: return color color = format_color(color) if isinstance(color, VectorInstance): assert_color(color) if len(color) == 4: if isinstance(color, tuple): return color return tuple(color) elif len(color) == 3: color = color[0], color[1], color[2], 255 else: raise ValueError(f'invalid color type {color}, only tuple or list are valid') return color # noinspection PyTypeChecker @staticmethod def _get(params: Dict[str, Any], key: str, allowed_types: Optional[Union[Type, str, List[Type], Tuple[Type, ...]]] = None, default: Any = None) -> Any: """ Return a value from a dictionary. Custom types (str) - alignment – pygame-menu alignment (locals) - callable – Is callable type, same as ``"function"`` - color – Check color - color_image – Color or :py:class:`pygame_menu.baseimage.BaseImage` - color_image_none – Color, :py:class:`pygame_menu.baseimage.BaseImage`, or None - color_none – Color or None - cursor – Cursor object (pygame) - font – Font type - image – Value must be ``BaseImage`` - none – None only - position – pygame-menu position (locals) - position_vector – pygame-menu position (str or vector) - tuple2 – Only valid numeric tuples ``(x, y)`` or ``[x, y]`` - tuple2int – Only valid integer tuples ``(x, y)`` or ``[x, y]`` - tuple3 – Only valid numeric tuples ``(x, y, z)`` or ``[x, y, z]`` - tuple3int – Only valid integer tuples ``(x, y, z)`` or ``[x, y, z]`` - type – Type-class (bool, str, etc...) :param params: Parameters dictionary :param key: Key to look for :param allowed_types: List of allowed types :param default: Default value to return :return: The value associated to the key """ value = params.pop(key, default) if allowed_types is not None: other_types = [] # Contain other types to check from if not isinstance(allowed_types, VectorInstance): allowed_types = (allowed_types,) for val_type in allowed_types: if val_type == 'alignment': assert_alignment(value) elif val_type == callable or val_type == 'function' or val_type == 'callable': assert callable(value), \ 'value must be callable type' elif val_type == 'color': value = assert_color(value) elif val_type == 'color_image': if not isinstance(value, BaseImage): value = assert_color(value) elif val_type == 'color_image_none': if not (value is None or isinstance(value, BaseImage)): value = assert_color(value) elif val_type == 'color_none': if value is not None: value = assert_color(value) elif val_type == 'cursor': assert_cursor(value) elif val_type == 'font': assert_font(value) elif val_type == 'image': assert isinstance(value, BaseImage), \ 'value must be BaseImage type' elif val_type == 'none': assert value is None elif val_type == 'position': assert_position(value) elif val_type == 'position_vector': assert_position_vector(value) elif val_type == 'type': assert isinstance(value, type), \ 'value is not type-class' elif val_type == 'tuple2': assert_vector(value, 2) elif val_type == 'tuple2int': assert_vector(value, 2, int) elif val_type == 'tuple3': assert_vector(value, 3) elif val_type == 'tuple3int': assert_vector(value, 3, int) else: # Unknown type assert isinstance(val_type, type), \ f'allowed type "{val_type}" is not a type-class' other_types.append(val_type) # Check other types if len(other_types) > 0: others = tuple(other_types) assert isinstance(value, others), \ f'Theme.{key} type shall be in {others} types (got {type(value)})' return value
THEME_DEFAULT = Theme() THEME_DARK = Theme( background_color=(40, 41, 35), cursor_color=(255, 255, 255), cursor_selection_color=(80, 80, 80, 120), scrollbar_color=(39, 41, 42), scrollbar_slider_color=(65, 66, 67), scrollbar_slider_hover_color=(90, 89, 88), selection_color=(255, 255, 255), title_background_color=(47, 48, 51), title_font_color=(215, 215, 215), widget_font_color=(200, 200, 200) ) THEME_BLUE = Theme( background_color=(228, 230, 246), scrollbar_shadow=True, scrollbar_slider_color=(150, 200, 230), scrollbar_slider_hover_color=(123, 173, 202), scrollbar_slider_pad=2, selection_color=(100, 62, 132), title_background_color=(62, 149, 195), title_font_color=(228, 230, 246), title_font_shadow=True, widget_font_color=(61, 170, 220) ) THEME_GREEN = Theme( background_color=(186, 214, 177), scrollbar_slider_color=(125, 121, 114), scrollbar_slider_hover_color=(100, 96, 90), scrollbar_slider_pad=2, selection_color=(125, 121, 114), title_background_color=(125, 121, 114), title_font_color=(228, 230, 246), widget_font_color=(255, 255, 255) ) THEME_ORANGE = Theme( background_color=(228, 100, 36), selection_color=(255, 255, 255), title_background_color=(170, 65, 50), widget_font_color=(0, 0, 0), widget_font_size=30 ) THEME_SOLARIZED = Theme( background_color=(239, 231, 211), cursor_color=(0, 0, 0), cursor_selection_color=(146, 160, 160, 120), selection_color=(207, 62, 132), title_background_color=(4, 47, 58), title_font_color=(38, 158, 151), widget_font_color=(102, 122, 130) )