mirror of
https://github.com/evennia/evennia.git
synced 2026-04-02 05:57:16 +02:00
Simplify and expand building menus with proper callables and at-a-glance descriptions
This commit is contained in:
parent
09284df66b
commit
67d25e0468
1 changed files with 54 additions and 47 deletions
|
|
@ -11,7 +11,7 @@ Building menus are similar to `EvMenu`, except that they have been specifically-
|
|||
[T]itle: the limbo room
|
||||
[D]escription
|
||||
This is the limbo room. You can easily change this default description,
|
||||
either by using the |y@desc/edit|n command, or simply by selecting this
|
||||
either by using the |y@desc/edit|n command, or simply by entering this
|
||||
menu (enter |yd|n).
|
||||
[E]xits:
|
||||
north to A parking(#4)
|
||||
|
|
@ -124,7 +124,7 @@ class Choice(object):
|
|||
|
||||
"""A choice object, created by `add_choice`."""
|
||||
|
||||
def __init__(self, title, key=None, aliases=None, attr=None, on_select=None, on_nomatch=None, text=None, brief=None,
|
||||
def __init__(self, title, key=None, aliases=None, attr=None, text=None, glance=None, on_enter=None, on_nomatch=None, on_leave=None,
|
||||
menu=None, caller=None, obj=None):
|
||||
"""Constructor.
|
||||
|
||||
|
|
@ -134,15 +134,16 @@ class Choice(object):
|
|||
the sub-neu. If not set, try to guess it based on the title.
|
||||
aliases (list of str, optional): the allowed aliases for this choice.
|
||||
attr (str, optional): the name of the attribute of 'obj' to set.
|
||||
on_select (callable, optional): a callable to call when the choice is selected.
|
||||
on_nomatch (callable, optional): a callable to call when no match is entered in the choice.
|
||||
text (str or callable, optional): a text to be displayed when
|
||||
the menu is opened It can be a callable.
|
||||
brief (str or callable, optional): a brief summary of the
|
||||
glance (str or callable, optional): an at-a-glance summary of the
|
||||
sub-menu shown in the main menu. It can be set to
|
||||
display the current value of the attribute in the
|
||||
main menu itself.
|
||||
menu (BuildingMenu, optional): the parent building menu.
|
||||
on_enter (callable, optional): a callable to call when the choice is entered.
|
||||
on_nomatch (callable, optional): a callable to call when no match is entered in the choice.
|
||||
on_leave (callable, optional): a callable to call when the caller leaves the choice.
|
||||
caller (Account or Object, optional): the caller.
|
||||
obj (Object, optional): the object to edit.
|
||||
|
||||
|
|
@ -151,10 +152,11 @@ class Choice(object):
|
|||
self.key = key
|
||||
self.aliases = aliases
|
||||
self.attr = attr
|
||||
self.on_select = on_select
|
||||
self.on_nomatch = on_nomatch
|
||||
self.text = text
|
||||
self.brief = brief
|
||||
self.glance = glance
|
||||
self.on_enter = on_enter
|
||||
self.on_nomatch = on_nomatch
|
||||
self.on_leave = on_leave
|
||||
self.menu = menu
|
||||
self.caller = caller
|
||||
self.obj = obj
|
||||
|
|
@ -162,15 +164,13 @@ class Choice(object):
|
|||
def __repr__(self):
|
||||
return "<Choice (title={}, key={})>".format(self.title, self.key)
|
||||
|
||||
def select(self, string):
|
||||
def enter(self, string):
|
||||
"""Called when the user opens the choice."""
|
||||
if self.on_select:
|
||||
_call_or_get(self.on_select, menu=self.menu, choice=self, string=string, caller=self.caller, obj=self.obj)
|
||||
if self.on_enter:
|
||||
_call_or_get(self.on_enter, menu=self.menu, choice=self, string=string, caller=self.caller, obj=self.obj)
|
||||
|
||||
# Display the text if there is some
|
||||
if self.text:
|
||||
self.caller.msg(_call_or_get(self.text, menu=self.menu, choice=self, string=string, caller=self.caller, obj=self.obj))
|
||||
|
||||
self.display_text()
|
||||
|
||||
def nomatch(self, string):
|
||||
"""Called when the user entered something that wasn't a command in a given choice.
|
||||
|
|
@ -184,8 +184,9 @@ class Choice(object):
|
|||
|
||||
def display_text(self):
|
||||
"""Display the choice text to the caller."""
|
||||
text = _call_or_get(self.text, menu=self.menu, choice=self, string="", caller=self.caller, obj=self.obj)
|
||||
return text.format(obj=self.obj, caller=self.caller)
|
||||
if self.text:
|
||||
text = _call_or_get(self.text, menu=self.menu, choice=self, string="", caller=self.caller, obj=self.obj)
|
||||
self.caller.msg(text.format(obj=self.obj, caller=self.caller))
|
||||
|
||||
|
||||
class BuildingMenu(object):
|
||||
|
|
@ -206,6 +207,9 @@ class BuildingMenu(object):
|
|||
|
||||
"""
|
||||
|
||||
keys_go_back = ["@"]
|
||||
min_shortcut = 1
|
||||
|
||||
def __init__(self, caller=None, obj=None, title="Building menu: {obj}", key=None):
|
||||
"""Constructor, you shouldn't override. See `init` instead.
|
||||
|
||||
|
|
@ -220,9 +224,6 @@ class BuildingMenu(object):
|
|||
self.key = key
|
||||
self.cmds = {}
|
||||
|
||||
# Options (can be overridden in init)
|
||||
self.min_shortcut = 1
|
||||
|
||||
if obj:
|
||||
self.init(obj)
|
||||
|
||||
|
|
@ -263,7 +264,8 @@ class BuildingMenu(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def add_choice(self, title, key=None, aliases=None, attr=None, on_select=None, on_nomatch=None, text=None, brief=None):
|
||||
def add_choice(self, title, key=None, aliases=None, attr=None, text=None, glance=None,
|
||||
on_enter=None, on_nomatch=None, on_leave=None):
|
||||
"""Add a choice, a valid sub-menu, in the current builder menu.
|
||||
|
||||
Args:
|
||||
|
|
@ -272,16 +274,17 @@ class BuildingMenu(object):
|
|||
the sub-neu. If not set, try to guess it based on the title.
|
||||
aliases (list of str, optional): the allowed aliases for this choice.
|
||||
attr (str, optional): the name of the attribute of 'obj' to set.
|
||||
on_select (callable, optional): a callable to call when the choice is selected.
|
||||
on_nomatch (callable, optional): a callable to call when no match is entered in the choice.
|
||||
is set in `attr`. If `attr` is not set, you should
|
||||
specify a function that both callback and set the value in `obj`.
|
||||
text (str or callable, optional): a text to be displayed when
|
||||
the menu is opened It can be a callable.
|
||||
brief (str or callable, optional): a brief summary of the
|
||||
glance (str or callable, optional): an at-a-glance summary of the
|
||||
sub-menu shown in the main menu. It can be set to
|
||||
display the current value of the attribute in the
|
||||
main menu itself.
|
||||
on_enter (callable, optional): a callable to call when the choice is entered.
|
||||
on_nomatch (callable, optional): a callable to call when no match is entered in the choice.
|
||||
is set in `attr`. If `attr` is not set, you should
|
||||
specify a function that both callback and set the value in `obj`.
|
||||
on_leave (callable, optional): a callable to call when the caller leaves the choice.
|
||||
|
||||
Note:
|
||||
All arguments can be a callable, like a function. This has the
|
||||
|
|
@ -298,7 +301,7 @@ class BuildingMenu(object):
|
|||
key = key.lower()
|
||||
aliases = aliases or []
|
||||
aliases = [a.lower() for a in aliases]
|
||||
if on_select is None and on_nomatch is None:
|
||||
if on_enter is None and on_nomatch is None:
|
||||
if attr is None:
|
||||
raise ValueError("The choice {} has neither attr nor callback, specify one of these as arguments".format(title))
|
||||
|
||||
|
|
@ -311,8 +314,8 @@ class BuildingMenu(object):
|
|||
if key and key in self.cmds:
|
||||
raise ValueError("A conflict exists between {} and {}, both use key or alias {}".format(self.cmds[key], title, repr(key)))
|
||||
|
||||
choice = Choice(title, key=key, aliases=aliases, attr=attr, on_select=on_select, on_nomatch=on_nomatch, text=text,
|
||||
brief=brief, menu=self, caller=self.caller, obj=self.obj)
|
||||
choice = Choice(title, key=key, aliases=aliases, attr=attr, text=text, glance=glance, on_enter=on_enter, on_nomatch=on_nomatch, on_leave=on_leave,
|
||||
menu=self, caller=self.caller, obj=self.obj)
|
||||
self.choices.append(choice)
|
||||
if key:
|
||||
self.cmds[key] = choice
|
||||
|
|
@ -320,7 +323,7 @@ class BuildingMenu(object):
|
|||
for alias in aliases:
|
||||
self.cmds[alias] = choice
|
||||
|
||||
def add_choice_quit(self, title="quit the menu", key="q", aliases=None):
|
||||
def add_choice_quit(self, title="quit the menu", key="q", aliases=None, on_enter=None):
|
||||
"""
|
||||
Add a simple choice just to quit the building menu.
|
||||
|
||||
|
|
@ -328,12 +331,18 @@ class BuildingMenu(object):
|
|||
title (str, optional): the choice title.
|
||||
key (str, optional): the choice key.
|
||||
aliases (list of str, optional): the choice aliases.
|
||||
on_enter (callable, optional): a different callable to quit the building menu.
|
||||
|
||||
Note:
|
||||
This is just a shortcut method, calling `add_choice`.
|
||||
If `on_enter` is not set, use `menu_quit` which simply
|
||||
closes the menu and displays a message. It also
|
||||
removes the CmdSet from the caller. If you supply
|
||||
another callable instead, make sure to do the same.
|
||||
|
||||
"""
|
||||
return self.add_choice(title, key=key, aliases=aliases, on_select=menu_quit)
|
||||
on_enter = on_enter or menu_quit
|
||||
return self.add_choice(title, key=key, aliases=aliases, on_enter=on_enter)
|
||||
|
||||
def _generate_commands(self, cmdset):
|
||||
"""
|
||||
|
|
@ -363,13 +372,7 @@ class BuildingMenu(object):
|
|||
caller = self.caller
|
||||
self._save()
|
||||
self.caller.cmdset.add(BuildingMenuCmdSet, permanent=True)
|
||||
|
||||
# Try to find the newly added cmdset (a shortcut would be nice)
|
||||
for cmdset in self.caller.cmdset.get():
|
||||
if isinstance(cmdset, BuildingMenuCmdSet):
|
||||
self._generate_commands(cmdset)
|
||||
self.display()
|
||||
return
|
||||
self.display()
|
||||
|
||||
# Display methods. Override for customization
|
||||
def display_title(self):
|
||||
|
|
@ -391,6 +394,10 @@ class BuildingMenu(object):
|
|||
ret += title[:pos] + "[|y" + choice.key.title() + "|n]" + title[pos + len(choice.key):]
|
||||
else:
|
||||
ret += "[|y" + choice.key.title() + "|n] " + title
|
||||
if choice.glance:
|
||||
glance = _call_or_get(choice.glance, menu=self, choice=choice, caller=self.caller, string="", obj=self.obj)
|
||||
glance = glance.format(obj=self.obj, caller=self.caller)
|
||||
ret += ": " + glance
|
||||
|
||||
return ret
|
||||
|
||||
|
|
@ -415,7 +422,7 @@ class BuildingMenu(object):
|
|||
saved in the caller, but the object itself cannot be found.
|
||||
|
||||
"""
|
||||
menu = caller.db._buildingmenu
|
||||
menu = caller.db._building_menu
|
||||
if menu:
|
||||
class_name = menu.get("class")
|
||||
if not class_name:
|
||||
|
|
@ -426,10 +433,11 @@ class BuildingMenu(object):
|
|||
menu_class = class_from_module(class_name)
|
||||
except Exception:
|
||||
log_trace("BuildingMenu: attempting to load class {} failed".format(repr(class_name)))
|
||||
return False
|
||||
return
|
||||
|
||||
# Create the menu
|
||||
obj = menu.get("obj")
|
||||
key = menu.get("key")
|
||||
try:
|
||||
building_menu = menu_class(caller, obj)
|
||||
except Exception:
|
||||
|
|
@ -437,6 +445,7 @@ class BuildingMenu(object):
|
|||
return False
|
||||
|
||||
# If there's no saved key, add the menu commands
|
||||
building_menu.key = key
|
||||
building_menu._generate_commands(cmdset)
|
||||
|
||||
return building_menu
|
||||
|
|
@ -463,12 +472,9 @@ class MenuCommand(Command):
|
|||
|
||||
self.menu.key = self.choice.key
|
||||
self.menu._save()
|
||||
for cmdset in self.caller.cmdset.get():
|
||||
if isinstance(cmdset, BuildingMenuCmdSet):
|
||||
for command in cmdset:
|
||||
cmdset.remove(command)
|
||||
break
|
||||
self.choice.select(self.raw_string)
|
||||
self.caller.cmdset.delete(BuildingMenuCmdSet)
|
||||
self.caller.cmdset.add(BuildingMenuCmdSet, permanent=True)
|
||||
self.choice.enter(self.raw_string)
|
||||
|
||||
|
||||
class CmdNoInput(MenuCommand):
|
||||
|
|
@ -512,10 +518,11 @@ class CmdNoMatch(MenuCommand):
|
|||
log_err("When CMDNOMATCH was called, the building menu couldn't be found")
|
||||
self.caller.msg("|rThe building menu couldn't be found, remove the CmdSet.|n")
|
||||
self.caller.cmdset.delete(BuildingMenuCmdSet)
|
||||
elif self.args == "/" and self.menu.key:
|
||||
elif raw_string in self.menu.keys_go_back and self.menu.key:
|
||||
self.menu.key = None
|
||||
self.menu._save()
|
||||
self.menu._generate_commands(cmdset)
|
||||
self.caller.cmdset.delete(BuildingMenuCmdSet)
|
||||
self.caller.cmdset.add(BuildingMenuCmdSet, permanent=True)
|
||||
self.menu.display()
|
||||
elif self.menu.key:
|
||||
choice.nomatch(raw_string)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue