mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Refactored with more consistent naming and placement. Fixed the table style wrapper.
This commit is contained in:
parent
d96cf3b809
commit
652186d829
9 changed files with 164 additions and 118 deletions
|
|
@ -118,6 +118,9 @@ Web/Django standard initiative (@strikaco)
|
|||
- `evennia.MONITOR_HANDLER.all` now takes keyword argument `obj` to only retrieve monitors from that specific
|
||||
Object (rather than all monitors in the entire handler).
|
||||
- Support adding `\f` in command doc strings to force where EvMore puts page breaks.
|
||||
- Validation Functions now added with standard API to homogenize user input validation.
|
||||
- Option Classes added to make storing user-options easier and smoother.
|
||||
- `evennia.VALIDATOR_CONTAINER` and `evennia.OPTION_CONTAINER` added to load these.
|
||||
|
||||
### Contribs
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,10 @@ TICKER_HANDLER = None
|
|||
MONITOR_HANDLER = None
|
||||
CHANNEL_HANDLER = None
|
||||
|
||||
# Containers
|
||||
VALIDATOR_CONTAINER = None
|
||||
OPTION_CONTAINER = None
|
||||
|
||||
|
||||
|
||||
def _create_version():
|
||||
|
|
@ -154,6 +158,7 @@ def _init():
|
|||
global create_object, create_script, create_account, create_channel, create_message, create_help_entry
|
||||
global settings, lockfuncs, logger, utils, gametime, ansi, spawn, managers
|
||||
global contrib, TICKER_HANDLER, MONITOR_HANDLER, SESSION_HANDLER, CHANNEL_HANDLER, TASK_HANDLER
|
||||
global VALIDATOR_CONTAINER, OPTION_CONTAINER
|
||||
global EvMenu, EvTable, EvForm, EvMore, EvEditor
|
||||
global ANSIString
|
||||
|
||||
|
|
@ -215,6 +220,10 @@ def _init():
|
|||
from .comms.channelhandler import CHANNEL_HANDLER
|
||||
from .scripts.monitorhandler import MONITOR_HANDLER
|
||||
|
||||
# containers
|
||||
from .utils.containers import VALIDATOR_CONTAINER
|
||||
from .utils.containers import OPTION_CONTAINER
|
||||
|
||||
# initialize the doc string
|
||||
global __doc__
|
||||
__doc__ = ansi.parse_ansi(DOCSTRING)
|
||||
|
|
|
|||
|
|
@ -7,11 +7,15 @@ All commands in Evennia inherit from the 'Command' class in this module.
|
|||
from builtins import range
|
||||
|
||||
import re
|
||||
import math
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from evennia.locks.lockhandler import LockHandler
|
||||
from evennia.utils.utils import is_iter, fill, lazy_property, make_iter
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.ansi import ANSIString
|
||||
|
||||
from future.utils import with_metaclass
|
||||
|
||||
|
||||
|
|
@ -468,6 +472,98 @@ class Command(with_metaclass(CommandMeta, object)):
|
|||
"""
|
||||
return self.__doc__
|
||||
|
||||
def width(self):
|
||||
return self.session.protocol_flags['SCREENWIDTH'][0]
|
||||
|
||||
def style_table(self, *args, **kwargs):
|
||||
border_color = self.account.options.get('border_color')
|
||||
column_color = self.account.options.get('column_names_color')
|
||||
|
||||
colornames = ['|%s%s|n' % (column_color, col) for col in args]
|
||||
|
||||
h_line_char = kwargs.pop('header_line_char', '~')
|
||||
header_line_char = ANSIString(f'|{border_color}{h_line_char}|n')
|
||||
|
||||
c_char = kwargs.pop('corner_char', '+')
|
||||
corner_char = ANSIString(f'|{border_color}{c_char}|n')
|
||||
|
||||
b_left_char = kwargs.pop('border_left_char', '||')
|
||||
border_left_char = ANSIString(f'|{border_color}{b_left_char}|n')
|
||||
|
||||
b_right_char = kwargs.pop('border_right_char', '||')
|
||||
border_right_char = ANSIString(f'|{border_color}{b_right_char}|n')
|
||||
|
||||
b_bottom_char = kwargs.pop('border_bottom_char', '-')
|
||||
border_bottom_char = ANSIString(f'|{border_color}{b_bottom_char}|n')
|
||||
|
||||
b_top_char = kwargs.pop('border_top_char', '-')
|
||||
border_top_char = ANSIString(f'|{border_color}{b_top_char}|n')
|
||||
|
||||
table = EvTable(*colornames, header_line_char=header_line_char, corner_char=corner_char,
|
||||
border_left_char=border_left_char, border_right_char=border_right_char,
|
||||
border_top_char=border_top_char, **kwargs)
|
||||
return table
|
||||
|
||||
def render_header(self, header_text=None, fill_character=None, edge_character=None,
|
||||
mode='header', color_header=True):
|
||||
colors = dict()
|
||||
colors['border'] = self.account.options.get('border_color')
|
||||
colors['headertext'] = self.account.options.get('%s_text_color' % mode)
|
||||
colors['headerstar'] = self.account.options.get('%s_star_color' % mode)
|
||||
|
||||
width = self.width()
|
||||
if edge_character:
|
||||
width -= 2
|
||||
|
||||
if header_text:
|
||||
if color_header:
|
||||
header_text = ANSIString(header_text).clean()
|
||||
header_text = ANSIString('|n|%s%s|n' % (colors['headertext'], header_text))
|
||||
if mode == 'header':
|
||||
begin_center = ANSIString("|n|%s<|%s* |n" % (colors['border'], colors['headerstar']))
|
||||
end_center = ANSIString("|n |%s*|%s>|n" % (colors['headerstar'], colors['border']))
|
||||
center_string = ANSIString(begin_center + header_text + end_center)
|
||||
else:
|
||||
center_string = ANSIString('|n |%s%s |n' % (colors['headertext'], header_text))
|
||||
else:
|
||||
center_string = ''
|
||||
|
||||
fill_character = self.account.options.get('%s_fill' % mode)
|
||||
|
||||
remain_fill = width - len(center_string)
|
||||
if remain_fill % 2 == 0:
|
||||
right_width = remain_fill / 2
|
||||
left_width = remain_fill / 2
|
||||
else:
|
||||
right_width = math.floor(remain_fill / 2)
|
||||
left_width = math.ceil(remain_fill / 2)
|
||||
|
||||
right_fill = ANSIString('|n|%s%s|n' % (colors['border'], fill_character * int(right_width)))
|
||||
left_fill = ANSIString('|n|%s%s|n' % (colors['border'], fill_character * int(left_width)))
|
||||
|
||||
if edge_character:
|
||||
edge_fill = ANSIString('|n|%s%s|n' % (colors['border'], edge_character))
|
||||
main_string = ANSIString(center_string)
|
||||
final_send = ANSIString(edge_fill) + left_fill + main_string + right_fill + ANSIString(edge_fill)
|
||||
else:
|
||||
final_send = left_fill + ANSIString(center_string) + right_fill
|
||||
return final_send
|
||||
|
||||
def style_header(self, *args, **kwargs):
|
||||
if 'mode' not in kwargs:
|
||||
kwargs['mode'] = 'header'
|
||||
return self.render_header(*args, **kwargs)
|
||||
|
||||
def style_separator(self, *args, **kwargs):
|
||||
if 'mode' not in kwargs:
|
||||
kwargs['mode'] = 'separator'
|
||||
return self.render_header(*args, **kwargs)
|
||||
|
||||
def style_footer(self, *args, **kwargs):
|
||||
if 'mode' not in kwargs:
|
||||
kwargs['mode'] = 'footer'
|
||||
return self.render_header(*args, **kwargs)
|
||||
|
||||
|
||||
class InterruptCommand(Exception):
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,8 @@ The command template for the default MUX-style command set. There
|
|||
is also an Account/OOC version that makes sure caller is an Account object.
|
||||
"""
|
||||
|
||||
import math
|
||||
from evennia.utils import utils
|
||||
from evennia.commands.command import Command
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.utils.ansi import ANSIString
|
||||
|
||||
# limit symbol import for API
|
||||
__all__ = ("MuxCommand", "MuxAccountCommand")
|
||||
|
|
@ -232,98 +229,6 @@ class MuxCommand(Command):
|
|||
string += "-" * 50
|
||||
self.caller.msg(string)
|
||||
|
||||
def width(self):
|
||||
return self.session.protocol_flags['SCREENWIDTH'][0]
|
||||
|
||||
def style_table(self, *args, **kwargs):
|
||||
border_color = self.account.options.get('border_color')
|
||||
column_color = self.account.options.get('column_names_color')
|
||||
|
||||
colornames = ['|%s%s|n' % (column_color, col) for col in args]
|
||||
|
||||
h_line_char = kwargs.pop('header_line_char', '-')
|
||||
header_line_char = ANSIString(f'|{border_color}{h_line_char}|n')
|
||||
|
||||
c_char = kwargs.pop('corner_char', '+')
|
||||
corner_char = ANSIString(f'|{border_color}{c_char}|n')
|
||||
|
||||
b_left_char = kwargs.pop('border_left_char', '||')
|
||||
border_left_char = ANSIString(f'|{border_color}{b_left_char}|n')
|
||||
|
||||
b_right_char = kwargs.pop('border_right_char', '||')
|
||||
border_right_char = ANSIString(f'|{border_color}{b_right_char}|n')
|
||||
|
||||
b_bottom_char = kwargs.pop('border_bottom_char', '-')
|
||||
border_bottom_char = ANSIString(f'|{border_color}{b_bottom_char}|n')
|
||||
|
||||
b_top_char = kwargs.pop('border_top_char', '-')
|
||||
border_top_char = ANSIString(f'|{border_color}{b_top_char}|n')
|
||||
|
||||
table = EvTable(*colornames, header_line_char=header_line_char, corner_char=corner_char,
|
||||
border_left_char=border_left_char, border_right_char=border_right_char,
|
||||
border_bottom_char=border_bottom_char, border_top_char=border_top_char, **kwargs)
|
||||
return table
|
||||
|
||||
def render_header(self, header_text=None, fill_character=None, edge_character=None,
|
||||
mode='header', color_header=True):
|
||||
colors = dict()
|
||||
colors['border'] = self.account.options.get('border_color')
|
||||
colors['headertext'] = self.account.options.get('%s_text_color' % mode)
|
||||
colors['headerstar'] = self.account.options.get('%s_star_color' % mode)
|
||||
|
||||
width = self.width()
|
||||
if edge_character:
|
||||
width -= 2
|
||||
|
||||
if header_text:
|
||||
if color_header:
|
||||
header_text = ANSIString(header_text).clean()
|
||||
header_text = ANSIString('|n|%s%s|n' % (colors['headertext'], header_text))
|
||||
if mode == 'header':
|
||||
begin_center = ANSIString("|n|%s<|%s* |n" % (colors['border'], colors['headerstar']))
|
||||
end_center = ANSIString("|n |%s*|%s>|n" % (colors['headerstar'], colors['border']))
|
||||
center_string = ANSIString(begin_center + header_text + end_center)
|
||||
else:
|
||||
center_string = ANSIString('|n |%s%s |n' % (colors['headertext'], header_text))
|
||||
else:
|
||||
center_string = ''
|
||||
|
||||
fill_character = self.account.options.get('%s_fill' % mode)
|
||||
|
||||
remain_fill = width - len(center_string)
|
||||
if remain_fill % 2 == 0:
|
||||
right_width = remain_fill / 2
|
||||
left_width = remain_fill / 2
|
||||
else:
|
||||
right_width = math.floor(remain_fill / 2)
|
||||
left_width = math.ceil(remain_fill / 2)
|
||||
|
||||
right_fill = ANSIString('|n|%s%s|n' % (colors['border'], fill_character * int(right_width)))
|
||||
left_fill = ANSIString('|n|%s%s|n' % (colors['border'], fill_character * int(left_width)))
|
||||
|
||||
if edge_character:
|
||||
edge_fill = ANSIString('|n|%s%s|n' % (colors['border'], edge_character))
|
||||
main_string = ANSIString(center_string)
|
||||
final_send = ANSIString(edge_fill) + left_fill + main_string + right_fill + ANSIString(edge_fill)
|
||||
else:
|
||||
final_send = left_fill + ANSIString(center_string) + right_fill
|
||||
return final_send
|
||||
|
||||
def style_header(self, *args, **kwargs):
|
||||
if 'mode' not in kwargs:
|
||||
kwargs['mode'] = 'header'
|
||||
return self.render_header(*args, **kwargs)
|
||||
|
||||
def style_separator(self, *args, **kwargs):
|
||||
if 'mode' not in kwargs:
|
||||
kwargs['mode'] = 'separator'
|
||||
return self.render_header(*args, **kwargs)
|
||||
|
||||
def style_footer(self, *args, **kwargs):
|
||||
if 'mode' not in kwargs:
|
||||
kwargs['mode'] = 'footer'
|
||||
return self.render_header(*args, **kwargs)
|
||||
|
||||
|
||||
class MuxAccountCommand(MuxCommand):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -511,6 +511,7 @@ OPTIONS_ACCOUNT_DEFAULT = {
|
|||
'footer_fill': ('Fill for Footer Lines.', 'Text', '='),
|
||||
'help_category_color': ('Help category names.', 'Color', 'g'),
|
||||
'help_entry_color': ('Help entry names.', 'Color', 'c'),
|
||||
'timezone': ('Timezone for dates. @tz for a list.', 'Timezone', 'UTC')
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -565,11 +566,11 @@ PROTOTYPEFUNC_MODULES = ["evennia.utils.prototypefuncs",
|
|||
|
||||
# Module holding validator functions. functions in later modules will
|
||||
# override those in earlier ones.
|
||||
VALIDATOR_MODULES = ['evennia.utils.validfuncs', ]
|
||||
VALIDATOR_MODULES = ['evennia.utils.validatorfunctions', ]
|
||||
|
||||
# Modules holding Option classes. Those in later modules will
|
||||
# override ones in earlier modules.
|
||||
OPTION_MODULES = ['evennia.utils.opclasses', ]
|
||||
OPTION_MODULES = ['evennia.utils.optionclasses', ]
|
||||
|
||||
######################################################################
|
||||
# Default Account setup and access
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from django.conf import settings
|
|||
from evennia.utils.utils import callables_from_module
|
||||
|
||||
|
||||
class ValidContainer(object):
|
||||
class ValidatorContainer(object):
|
||||
"""
|
||||
Loads and stores the final list of VALIDATOR FUNCTIONS.
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ class ValidContainer(object):
|
|||
|
||||
|
||||
# Ensure that we have a Singleton of ValidHandler that is always loaded... and only needs to be loaded once.
|
||||
VALID_CONTAINER = ValidContainer()
|
||||
VALIDATOR_CONTAINER = ValidatorContainer()
|
||||
|
||||
|
||||
class OptionContainer(object):
|
||||
|
|
|
|||
|
|
@ -39,20 +39,49 @@ class OptionHandler(object):
|
|||
return_list=True, return_obj=True) if s}
|
||||
|
||||
def __getitem__(self, item):
|
||||
"""
|
||||
Shortcut to self.get(item) used as a different syntax. This entire object is
|
||||
essentially a dictionary of option_key -> value.
|
||||
|
||||
Args:
|
||||
item (str): The Key of the item to get.
|
||||
|
||||
Returns:
|
||||
The Option's value.
|
||||
"""
|
||||
return self.get(item).value
|
||||
|
||||
def get(self, item, return_obj=False):
|
||||
"""
|
||||
Retrieves an Option stored in the handler. Will load it if it doesn't exist.
|
||||
|
||||
Args:
|
||||
item (str): The key to retrieve.
|
||||
return_obj (bool): If True, returns the actual option object instead of its value.
|
||||
|
||||
Returns:
|
||||
An option value (varies) or the Option itself.
|
||||
"""
|
||||
if item not in self.options_dict:
|
||||
raise KeyError("Option not found!")
|
||||
if item in self.options:
|
||||
op_found = self.options[item]
|
||||
else:
|
||||
op_found = self.load_option(item)
|
||||
op_found = self._load_option(item)
|
||||
if return_obj:
|
||||
return op_found
|
||||
return op_found.value
|
||||
|
||||
def load_option(self, key):
|
||||
def _load_option(self, key):
|
||||
"""
|
||||
Loads option on-demand if it has not been loaded yet.
|
||||
|
||||
Args:
|
||||
key (str): The option being loaded.
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
option_def = self.options_dict[key]
|
||||
save_data = self.save_data.get(key, None)
|
||||
self.obj.msg(save_data)
|
||||
|
|
@ -60,7 +89,7 @@ class OptionHandler(object):
|
|||
self.options[key] = loaded_option
|
||||
return loaded_option
|
||||
|
||||
def set(self, option, value):
|
||||
def set(self, option, value, **kwargs):
|
||||
"""
|
||||
Change an individual option.
|
||||
|
||||
|
|
@ -80,7 +109,7 @@ class OptionHandler(object):
|
|||
raise ValueError(f"That matched: {', '.join(found)}. Please be more specific.")
|
||||
found = found[0]
|
||||
op = self.get(found, return_obj=True)
|
||||
op.value = value
|
||||
op.set(value, **kwargs)
|
||||
return op.display()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import datetime as _dt
|
||||
from evennia import logger as _log
|
||||
from evennia.utils.ansi import ANSIString as _ANSI
|
||||
from evennia.utils.validfuncs import _TZ_DICT
|
||||
from evennia.utils.containers import VALID_CONTAINER as _VAL
|
||||
from evennia.utils.validatorfunctions import _TZ_DICT
|
||||
from evennia.utils.containers import VALIDATOR_CONTAINER as _VAL
|
||||
|
||||
|
||||
class BaseOption(object):
|
||||
|
|
@ -14,9 +14,8 @@ class BaseOption(object):
|
|||
Designed to be extremely overloadable as some options can be cantankerous.
|
||||
|
||||
Properties:
|
||||
expect_type (str): What users will see this as asking for. Example: Color or email.
|
||||
valid: Shortcut to the loaded VALID_HANDLER.
|
||||
valid_type (str): The key of the Validator this uses.
|
||||
validator_key (str): The key of the Validator this uses.
|
||||
"""
|
||||
validator_key = ''
|
||||
|
||||
|
|
@ -55,7 +54,7 @@ class BaseOption(object):
|
|||
"""
|
||||
return self.value
|
||||
|
||||
def _load(self):
|
||||
def load(self):
|
||||
"""
|
||||
Takes the provided save data, validates it, and gets this Option ready to use.
|
||||
|
||||
|
|
@ -71,7 +70,7 @@ class BaseOption(object):
|
|||
_log.log_trace(e)
|
||||
return False
|
||||
|
||||
def _save(self):
|
||||
def save(self):
|
||||
"""
|
||||
Exports the current value to an Attribute.
|
||||
|
||||
|
|
@ -115,7 +114,7 @@ class BaseOption(object):
|
|||
@property
|
||||
def value(self):
|
||||
if not self.loaded and self.save_data is not None:
|
||||
self._load()
|
||||
self.load()
|
||||
if self.loaded:
|
||||
return self.value_storage
|
||||
else:
|
||||
|
|
@ -123,22 +122,24 @@ class BaseOption(object):
|
|||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self.set(value)
|
||||
|
||||
def set(self, value, **kwargs):
|
||||
"""
|
||||
Takes user input, presumed to be a string, and changes the value if it is a valid input.
|
||||
|
||||
Args:
|
||||
value:
|
||||
account:
|
||||
value: The new value of this Option.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
final_value = self.validate(value)
|
||||
final_value = self.validate(value, **kwargs)
|
||||
self.value_storage = final_value
|
||||
self.loaded = True
|
||||
self._save()
|
||||
self.save()
|
||||
|
||||
def validate(self, value):
|
||||
def validate(self, value, **kwargs):
|
||||
"""
|
||||
Validate user input, which is presumed to be a string.
|
||||
|
||||
|
|
@ -151,7 +152,7 @@ class BaseOption(object):
|
|||
Returns:
|
||||
The results of a Validator call. Might be any kind of python object.
|
||||
"""
|
||||
return _VAL[self.validator_key](value, thing_name=self.key)
|
||||
return _VAL[self.validator_key](value, thing_name=self.key, **kwargs)
|
||||
|
||||
|
||||
class Text(BaseOption):
|
||||
|
|
@ -170,9 +170,11 @@ def timezone(entry, thing_name="Timezone", **kwargs):
|
|||
"""
|
||||
if not entry:
|
||||
raise ValueError(f"No {thing_name} entered!")
|
||||
found = _partial(list(_TZ_DICT.keys()), entry)
|
||||
found = _partial(list(_TZ_DICT.keys()), entry, ret_index=False)
|
||||
if len(found) > 1:
|
||||
raise ValueError(f"That matched: {', '.join(str(t) for t in found)}. Please be more specific!")
|
||||
if found:
|
||||
return _TZ_DICT[found]
|
||||
return _TZ_DICT[found[0]]
|
||||
raise ValueError(f"Could not find timezone '{entry}' for {thing_name}!")
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue