Source code for pygame_menu._widgetmanager

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

MENU WIDGET MANAGER
Easy widget add/remove to Menus.
"""

from __future__ import annotations

__all__ = ["WidgetManager"]

from typing import Any

import pygame_menu
from pygame_menu._base import Base
from pygame_menu._types import PaddingInstance
from pygame_menu.font import assert_font
from pygame_menu.utils import (
    assert_color,
    assert_cursor,
    assert_position_vector,
    assert_vector,
    warn,
)

# Import widgets
from pygame_menu.widgets.core.widget import Widget, check_widget_mouseleave
from pygame_menu.widgets.widget.button import ButtonManager
from pygame_menu.widgets.widget.colorinput import ColorInputManager
from pygame_menu.widgets.widget.dropselect import DropSelectManager
from pygame_menu.widgets.widget.dropselect_multiple import DropSelectMultipleManager
from pygame_menu.widgets.widget.frame import FrameManager
from pygame_menu.widgets.widget.hmargin import HMarginManager
from pygame_menu.widgets.widget.image import ImageManager
from pygame_menu.widgets.widget.label import LabelManager
from pygame_menu.widgets.widget.menulink import MenuLinkManager
from pygame_menu.widgets.widget.none import NoneWidgetManager
from pygame_menu.widgets.widget.progressbar import ProgressBarManager
from pygame_menu.widgets.widget.rangeslider import RangeSliderManager
from pygame_menu.widgets.widget.selector import SelectorManager
from pygame_menu.widgets.widget.surface import SurfaceWidgetManager
from pygame_menu.widgets.widget.table import TableManager
from pygame_menu.widgets.widget.textinput import TextInputManager
from pygame_menu.widgets.widget.toggleswitch import ToggleSwitchManager
from pygame_menu.widgets.widget.vfill import VFillManager
from pygame_menu.widgets.widget.vmargin import VMarginManager


# noinspection PyProtectedMember
[docs] class WidgetManager( Base, ButtonManager, ColorInputManager, DropSelectManager, DropSelectMultipleManager, FrameManager, HMarginManager, ImageManager, LabelManager, MenuLinkManager, NoneWidgetManager, ProgressBarManager, RangeSliderManager, SelectorManager, SurfaceWidgetManager, TableManager, TextInputManager, ToggleSwitchManager, VFillManager, VMarginManager, ): """ Add/Remove widgets to the Menu. :param menu: The Menu reference :param verbose: Enables/disables verbose mode (warnings/errors) """ def __init__(self, menu: pygame_menu.Menu, verbose: bool = True) -> None: super().__init__(object_id=menu.get_id() + "+widget-manager", verbose=verbose) self._menu = menu @property def _theme(self) -> pygame_menu.Theme: return self._menu.get_theme() def _add_submenu(self, menu: pygame_menu.Menu, hook: Widget) -> None: assert isinstance(menu, pygame_menu.Menu) assert menu != self._menu, "submenu cannot point to menu itself" assert isinstance(hook, Widget) if menu not in self._menu._submenus.keys(): self._menu._submenus[menu] = [] assert hook not in self._menu._submenus[menu], ( f"widget {hook.get_class_id()} already hooks submenu {menu.get_class_id()}" ) self._menu._submenus[menu].append(hook) hook._menu_hook = menu def _filter_widget_attributes(self, kwargs: dict[str, Any]) -> dict[str, Any]: attributes = {} # align align = kwargs.pop("align", self._theme.widget_alignment) assert isinstance(align, str) attributes["align"] = align # background_color background_is_color = False background_color = kwargs.pop( "background_color", self._theme.widget_background_color ) if background_color is not None: if isinstance(background_color, pygame_menu.BaseImage): pass else: background_color = assert_color(background_color) background_is_color = True attributes["background_color"] = background_color # background_inflate background_inflate = kwargs.pop( "background_inflate", self._theme.widget_background_inflate ) if background_inflate == 0: background_inflate = (0, 0) assert_vector(background_inflate, 2, int) assert background_inflate[0] >= 0 and background_inflate[1] >= 0, ( "both background inflate components must be equal or greater than zero" ) attributes["background_inflate"] = background_inflate # border_color border_color = kwargs.pop("border_color", self._theme.widget_border_color) if border_color is not None: border_color = assert_color(border_color) attributes["border_color"] = border_color # border_inflate border_inflate = kwargs.pop("border_inflate", self._theme.widget_border_inflate) if border_inflate == 0: border_inflate = (0, 0) assert_vector(border_inflate, 2, int) assert isinstance(border_inflate[0], int) and border_inflate[0] >= 0 assert isinstance(border_inflate[1], int) and border_inflate[1] >= 0 attributes["border_inflate"] = border_inflate # border_position border_position = kwargs.pop( "border_position", self._theme.widget_border_position ) assert_position_vector(border_position) attributes["border_position"] = border_position # border_width border_width = kwargs.pop("border_width", self._theme.widget_border_width) assert isinstance(border_width, int) and border_width >= 0 attributes["border_width"] = border_width # cursor cursor = kwargs.pop("cursor", self._theme.widget_cursor) assert_cursor(cursor) attributes["cursor"] = cursor # floating status float_ = kwargs.pop("float", False) assert isinstance(float_, bool) attributes["float"] = float_ float_origin_position = kwargs.pop("float_origin_position", False) assert isinstance(float_origin_position, bool) attributes["float_origin_position"] = float_origin_position # font_antialias attributes["font_antialias"] = self._theme.widget_font_antialias # font_background_color font_background_color = kwargs.pop( "font_background_color", self._theme.widget_font_background_color ) if ( font_background_color is None and self._theme.widget_font_background_color_from_menu and not background_is_color ): if not isinstance(self._theme.background_color, pygame_menu.BaseImage): font_background_color = assert_color(self._theme.background_color) attributes["font_background_color"] = font_background_color # font_color font_color = kwargs.pop("font_color", self._theme.widget_font_color) attributes["font_color"] = assert_color(font_color) # font_name font_name = kwargs.pop("font_name", self._theme.widget_font) assert_font(font_name) attributes["font_name"] = font_name # font_shadow font_shadow = kwargs.pop("font_shadow", self._theme.widget_font_shadow) assert isinstance(font_shadow, bool) attributes["font_shadow"] = font_shadow # font_shadow_color font_shadow_color = kwargs.pop( "font_shadow_color", self._theme.widget_font_shadow_color ) attributes["font_shadow_color"] = assert_color(font_shadow_color) # font_shadow_offset font_shadow_offset = kwargs.pop( "font_shadow_offset", self._theme.widget_font_shadow_offset ) assert isinstance(font_shadow_offset, int) attributes["font_shadow_offset"] = font_shadow_offset # font_shadow_position font_shadow_position = kwargs.pop( "font_shadow_position", self._theme.widget_font_shadow_position ) assert isinstance(font_shadow_position, str) attributes["font_shadow_position"] = font_shadow_position # font_size font_size = kwargs.pop("font_size", self._theme.widget_font_size) assert isinstance(font_size, int) assert font_size > 0, "font size must be greater than zero" attributes["font_size"] = font_size # margin margin = kwargs.pop("margin", self._theme.widget_margin) if margin == 0: margin = (0, 0) assert_vector(margin, 2) attributes["margin"] = margin # padding padding = kwargs.pop("padding", self._theme.widget_padding) assert isinstance(padding, PaddingInstance) attributes["padding"] = padding # readonly_color readonly_color = kwargs.pop("readonly_color", self._theme.readonly_color) attributes["readonly_color"] = assert_color(readonly_color) # readonly_selected_color readonly_selected_color = kwargs.pop( "readonly_selected_color", self._theme.readonly_selected_color ) attributes["readonly_selected_color"] = assert_color(readonly_selected_color) # selection_color selection_color = kwargs.pop("selection_color", self._theme.selection_color) attributes["selection_color"] = assert_color(selection_color) # selection_effect selection_effect = kwargs.pop( "selection_effect", self._theme.widget_selection_effect ) if selection_effect is None: selection_effect = pygame_menu.widgets.NoneSelection() else: selection_effect = selection_effect.copy() assert isinstance(selection_effect, pygame_menu.widgets.core.Selection) selection_effect.set_color(attributes["selection_color"]) attributes["selection_effect"] = selection_effect # shadow attributes["shadow_aa"] = kwargs.pop("shadow_aa", self._theme.widget_shadow_aa) attributes["shadow_color"] = kwargs.pop( "shadow_color", self._theme.widget_shadow_color ) attributes["shadow_radius"] = kwargs.pop( "shadow_radius", self._theme.widget_shadow_radius ) attributes["shadow_type"] = kwargs.pop( "shadow_type", self._theme.widget_shadow_type ) attributes["shadow_width"] = kwargs.pop( "shadow_width", self._theme.widget_shadow_width ) # tab_size attributes["tab_size"] = kwargs.pop("tab_size", self._theme.widget_tab_size) return attributes def _configure_widget(self, widget: Widget, **kwargs) -> None: assert isinstance(widget, Widget) widget._verbose = self._verbose widget.set_alignment(align=kwargs["align"]) widget.set_background_color( color=kwargs["background_color"], inflate=kwargs["background_inflate"] ) widget.set_border( color=kwargs["border_color"], inflate=kwargs["border_inflate"], position=kwargs["border_position"], width=kwargs["border_width"], ) widget.set_controls( joystick=self._menu._joystick, keyboard=self._menu._keyboard, mouse=self._menu._mouse, touchscreen=self._menu._touchscreen, ) widget.set_cursor(cursor=kwargs["cursor"]) widget.set_float( float_status=kwargs["float"], origin_position=kwargs["float_origin_position"], ) widget.set_font( antialias=kwargs["font_antialias"], background_color=kwargs["font_background_color"], color=kwargs["font_color"], font=kwargs["font_name"], font_size=kwargs["font_size"], readonly_color=kwargs["readonly_color"], readonly_selected_color=kwargs["readonly_selected_color"], selected_color=kwargs["selection_color"], ) widget.set_font_shadow( color=kwargs["font_shadow_color"], enabled=kwargs["font_shadow"], offset=kwargs["font_shadow_offset"], position=kwargs["font_shadow_position"], ) widget.set_margin(x=kwargs["margin"][0], y=kwargs["margin"][1]) widget.set_padding(padding=kwargs["padding"]) widget.set_selection_effect(selection=kwargs["selection_effect"]) widget.set_tab_size(tab_size=kwargs["tab_size"]) widget.shadow( aa_amount=kwargs["shadow_aa"], color=kwargs["shadow_color"], corner_radius=kwargs["shadow_radius"], shadow_type=kwargs["shadow_type"], shadow_width=kwargs["shadow_width"], ) if self._theme.widget_background_inflate_to_selection: widget.background_inflate_to_selection_effect() widget._update__repr___(self) widget._keyboard_ignore_nonphysical = self._menu._keyboard_ignore_nonphysical widget.configured = True widget._configure() @staticmethod def _check_kwargs(kwargs: dict[str, Any]) -> None: for invalid_keyword in kwargs.keys(): raise ValueError( f"widget addition optional parameter kwargs.{invalid_keyword} is not valid" ) def _append_widget(self, widget: Widget) -> None: assert isinstance(widget, Widget) if widget.get_menu() is None: widget.set_menu(self._menu) assert widget.get_menu() == self._menu, ( "widget cannot have a different instance of menu" ) self._menu._check_id_duplicated(widget.get_id()) if widget.get_scrollarea() is None: widget.set_scrollarea(self._menu.get_scrollarea()) # Unselect widget.select(False) # Append to lists self._menu._widgets.append(widget) # Update selection index if self._menu._index < 0 and widget.is_selectable: widget.select() self._menu._index = len(self._menu._widgets) - 1 # Force menu rendering, this checks if the menu overflows or has sizing # errors; if added on execution time forces the update of the surface self._menu._widgets_surface = None try: self._menu._render() except ( pygame_menu.menu._MenuSizingException, pygame_menu.menu._MenuWidgetOverflow, ): self._menu.remove_widget(widget) raise self._menu.render() # Sort frame widgets, as render position changes frame position/frame if len(self._menu._update_frames) > 0: self._menu._update_frames[0]._sort_menu_update_frames() # Update widgets check_widget_mouseleave() # Call event widget._append_to_menu()
[docs] def configure_defaults_widget(self, widget: Widget) -> None: self._configure_widget(widget, **self._filter_widget_attributes({}))
[docs] def generic_widget( self, widget: Widget, configure_defaults: bool = False ) -> Widget: """ Add generic widget to the Menu. .. note:: The widget should be fully configured by the user: font, padding, etc. .. note:: This is applied only to the base Menu (not the currently displayed, stored in ``_current`` pointer); for such behavior apply to :py:meth:`pygame_menu.menu.Menu.get_current` object. .. warning:: Unintended behaviors may happen while using this method, use only with caution; specially while creating nested submenus with buttons. :param widget: Widget to be added :param configure_defaults: Apply defaults widget configuration (for example, theme) :return: The added widget object :rtype: :py:class:`pygame_menu.widgets.Widget` """ assert isinstance(widget, Widget) if widget.get_menu() is not None: raise ValueError("widget to be added is already appended to another Menu") # Raise warning if adding button with Menu if isinstance(widget, pygame_menu.widgets.Button) and widget.to_menu: if self._verbose: warn( "prefer adding submenus using add_button method instead, " "unintended behaviours may occur" ) # Configure widget if configure_defaults: self.configure_defaults_widget(widget) self._append_widget(widget) return widget