#!/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)