pygo/sgc/widgets/menu.py
2012-04-14 18:38:47 -04:00

218 lines
7.5 KiB
Python

#!/usr/bin/env python
# Copyright (C) 2010-2012 Sam Bull
"""
Menu widget. Creates a menu for a game.
"""
from _locals import *
from boxes import VBox
from scroll_box import ScrollBox
from . import *
class Menu(Simple):
"""
Menu
Can be indexed to access widgets by name.
Attributes:
func_dict: Assign a lambda to return a dictionary of functions for
config file to utilise.
"""
_modal = True
_layered = True
_settings_default = {"offset": (100, 50), "col": (0,0,1), "apply": False}
_menus = []
_dict = {} # Dict for indexing widgets
_old_menu = None
_curr_menu = 0
func_dict = lambda self: {}
def _config(self, **kwargs):
"""
menu: Either tuple containing menu data, to be documented.
Or file object to read config data from, in the same format.
apply: TODO ``bool`` True if an apply button should be added.
col: ``tuple`` (r,g,b), Colour used for the background.
offset: ``tuple`` (x,y) Contains position of menu widgets. y is
added to bottom of title.
"""
if "apply" in kwargs:
self._settings["apply"] = kwargs["apply"]
if "col" in kwargs:
self._settings["col"] = kwargs["col"]
if "offset" in kwargs:
self._settings["offset"] = kwargs["offset"]
if "menu" in kwargs:
self._menus = [] # Remove previous menus
self._dict = {}
# Grab function dictionary
self._funcs = self.func_dict()
# Set size to screen size
if not hasattr(self, "image"):
self._create_base_images(get_screen().rect.size)
menu = kwargs["menu"]
# If file, read config tuple
if isinstance(menu, file):
menu_data = "".join(menu.readlines())
menu = eval(menu_data)
assert isinstance(menu, tuple)
menu_data = [(menu, None)] # (data, parent)
# Create each submenu, by iterating through the menu data
while menu_data:
self._menus.append(_SubMenu(self.rect.size,
col=self._settings["col"]))
self._config_menu(menu_data, self._menus[-1])
def _config_menu(self, data_queue, menu):
"""
Configure the passed in menu, using the information from the first
item in data_queue.
New sub-menus discovered in the data will be appended to the data_queue
for later processing.
"""
data, parent = data_queue.pop(0)
widgets = []
# Parse menu data
for item in data:
# Title
if isinstance(item, str):
menu._title = Simple(Font["title"].render(item[2:], True,
Font.col))
menu._title.rect.midtop = (self.rect.centerx, 40)
# Sub-menu
elif item[0].startswith("m:"):
data_queue.append((item, len(self._menus)-1))
surf = Font["widget"].render(item[0][2:], True, Font.col)
widgets.append(Button(surf))
# Change menu on button activate
num = len(self._menus)-1 + len(data_queue)
widgets[-1].activate = lambda n=num: self.change_menu(n)
# Category/divider
elif item[0].startswith("c:"):
div = Simple(Font["widget"].render(item[0][2:], True,
Font.col))
self.get_draw().line(div.image, Font.col, (0, div.rect.h-1),
(div.rect.w, div.rect.h-1))
widgets.append(div)
# Widget
elif item[0].startswith("w:"):
args = self._get_args(item[1:])
name = args.pop("name")
f = args.pop("func") if ("func" in args) else None
if item[0].endswith("input_box"):
widget = input_box.InputBox(**args)
elif item[0].endswith("button"):
for key in args:
if key == "surf":
args[key] = eval(args[key])
widget = button.Button(**args)
elif item[0].endswith("label"):
widget = label.Label(**args)
self._dict[name] = widget
if f: widget.activate = self._funcs[f]
widgets.append(widget)
# Function
elif item[0].startswith("f:"):
surf = Font["widget"].render(item[1], True, Font.col)
widgets.append(button.Button(surf=surf))
widgets[-1].activate = self._funcs[item[0][2:]]
# Draw a back menu item
if parent is not None:
surf = Font["widget"].render("Back", True, Font.col)
widgets.append(button.Button(surf=surf))
widgets[-1].activate = lambda n=parent: self.change_menu(n)
menu._widgets = tuple(widgets)
def _draw(self, draw):
for menu in self._menus:
# Pack all widgets into a VBox
box = VBox(widgets=menu._widgets, spacing=15)
pos = (self._settings["offset"][0],
self._settings["offset"][1] + menu._title.rect.bottom)
box = ScrollBox((self.rect.w - pos[0], self.rect.h - pos[1]),
widget=box, pos=pos)
menu.config(col=self._settings["col"], menu=box)
def change_menu(self, menu_num):
"""
Change the currently displayed menu.
Args:
menu_num: ``int`` The number representing the menu.
"""
self._old_menu = self._curr_menu
self._curr_menu = menu_num
self._menus[self._curr_menu]._fade = 0
def update(self, time):
menu = self._menus[self._curr_menu]
menu.update(time)
if self._old_menu is not None:
self.image.blit(self._menus[self._old_menu].image, (0,0))
menu.image.set_alpha(menu._fade)
menu._fade += time / 3.
if menu._fade >= 255:
menu._fade = None
self._old_menu = None
menu.image.set_alpha(255)
self.image.blit(menu.image, (0,0))
def _event(self, event):
self._menus[self._curr_menu]._event(event)
def _get_args(self, args):
"""Get the arguments passed in, saving them into a dictionary."""
return {arg.split("=")[0]: arg.split("=")[1] for arg in args}
def __getitem__(self, key):
"""Return widgets by name."""
return self._dict[key]
class _SubMenu(Simple):
"""
A single menu object to be created and managed by the Menu class.
"""
_settings_default = {"col": (0,0,1), "menu": None}
_title = None
_widgets = ()
def _config(self, **kwargs):
if "col" in kwargs:
self._settings["col"] = kwargs["col"]
if "menu" in kwargs:
self._settings["menu"] = kwargs["menu"]
def _draw(self, draw):
self._images["image"].fill(self._settings["col"])
if self._title:
self._images["image"].blit(self._title.image, self._title.pos)
def update(self, time):
self.image = self._images["image"].copy()
self._settings["menu"].update(time)
self.image.blit(self._settings["menu"].image,self._settings["menu"].pos)
def _event(self, event):
"""Send events to container."""
self._settings["menu"]._event(event)