Compare commits
4 Commits
ace0af7101
...
a7552fad96
Author | SHA1 | Date |
---|---|---|
Nicholas Hope | a7552fad96 | |
Nicholas Hope | f483beb0bd | |
Nicholas Hope | f0dac0d1a1 | |
Nicholas Hope | 0bbca21fea |
|
@ -26,11 +26,11 @@ default:
|
|||
|
||||
# hold doubles
|
||||
double r:
|
||||
condition: mouse4
|
||||
cancel: both
|
||||
type: hold
|
||||
primary: +class_action
|
||||
secondary: +reload
|
||||
condition: mouse4
|
||||
cancel: both
|
||||
|
||||
# other
|
||||
impulse =: kill
|
||||
|
@ -60,26 +60,26 @@ default:
|
|||
press:
|
||||
- "-moveright"
|
||||
- "+moveleft"
|
||||
- "alias maybeMoveLeft +moveleft"
|
||||
- "alias maybe_move_left +moveleft"
|
||||
release:
|
||||
- "-moveleft"
|
||||
- "maybeMoveRight"
|
||||
- "alias maybeMoveLeft "
|
||||
- "maybe_move_right"
|
||||
- "unalias maybe_move_left"
|
||||
hold d:
|
||||
press:
|
||||
- "-moveleft"
|
||||
- "+moveright"
|
||||
- "alias maybeMoveRight +moveright"
|
||||
- "alias maybe_move_right +moveright"
|
||||
release:
|
||||
- "-moveright"
|
||||
- "maybeMoveLeft"
|
||||
- "alias maybeMoveRight "
|
||||
- "maybe_move_left"
|
||||
- "unalias maybe_move_right"
|
||||
# This just stops an error message the first time you release
|
||||
# either of 'a' or 'd'
|
||||
impulse maybeMoveLeft:
|
||||
impulse maybe_move_left:
|
||||
alias: yes
|
||||
command: ""
|
||||
impulse maybeMoveRight:
|
||||
impulse maybe_move_right:
|
||||
alias: yes
|
||||
command: ""
|
||||
# class action is something useful for each class,
|
||||
|
@ -97,16 +97,16 @@ default:
|
|||
|
||||
impulse load0:
|
||||
alias: yes
|
||||
command: "load_itempreset a"
|
||||
command: "loadout a"
|
||||
impulse load1:
|
||||
alias: yes
|
||||
command: "load_itempreset b"
|
||||
command: "loadout b"
|
||||
impulse load2:
|
||||
alias: yes
|
||||
command: "load_itempreset c"
|
||||
command: "loadout c"
|
||||
impulse load3:
|
||||
alias: yes
|
||||
command: "load_itempreset d"
|
||||
command: "loadout d"
|
||||
|
||||
impulse INS:
|
||||
- "load0"
|
||||
|
@ -120,8 +120,10 @@ default:
|
|||
impulse END:
|
||||
- "load3"
|
||||
- "alias reload_presets load3"
|
||||
literal set_default_loadout:
|
||||
alias reload_presets load0
|
||||
impulse backspace:
|
||||
"reload_presets"
|
||||
reload_presets
|
||||
|
||||
engi:
|
||||
hold class_action:
|
||||
|
@ -129,7 +131,6 @@ engi:
|
|||
press:
|
||||
- destroy sentry
|
||||
- build sentry
|
||||
release: ""
|
||||
|
||||
medic:
|
||||
# "Radar" feature: causes all teammates to autocall for medic, allowing
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
""" This has only one function. It mostly exists to allow imports of things like tftypes """
|
||||
|
||||
from copy import deepcopy
|
||||
from tfscript.tftypes import double
|
||||
from tfscript.tftypes import Double
|
||||
|
||||
def makeCFG(bindList, default=False):
|
||||
|
||||
if default:
|
||||
# Write to defaultDict instead of condDict
|
||||
double.condDict = double.defaultDict
|
||||
Double.condDict = Double.defaultDict
|
||||
else:
|
||||
double.condDict = deepcopy(double.defaultDict)
|
||||
Double.condDict = deepcopy(Double.defaultDict)
|
||||
|
||||
ret = ''
|
||||
|
||||
|
@ -18,14 +18,16 @@ def makeCFG(bindList, default=False):
|
|||
|
||||
# Doubles are weird. All of the toggles got put into a dictionary.
|
||||
# This takes all of the nested dictionaries and turns them into the right string
|
||||
if default or double.condDict != double.defaultDict:
|
||||
if default or Double.condDict != Double.defaultDict:
|
||||
# ==, and by extension !=, does in fact check
|
||||
# for dictionary equality in keys and values
|
||||
for key, toggles in double.condDict.items():
|
||||
for key, toggles in Double.condDict.items():
|
||||
onCondPress = ';'.join(toggles["change_keys"])
|
||||
onCondRelease = ';'.join(toggles["restore_keys"])
|
||||
ret += f'alias +{key}_toggles "{onCondPress}"\n' +\
|
||||
f'alias -{key}_toggles "{onCondRelease}"\n' +\
|
||||
f'bind {key} "+{key}_toggles"\n'
|
||||
ret += (
|
||||
f'alias +{key}_toggles "{onCondPress}"\n'
|
||||
+ f'alias -{key}_toggles "{onCondRelease}"\n'
|
||||
+ f'bind {key} "+{key}_toggles"\n'
|
||||
)
|
||||
|
||||
return ret
|
||||
|
|
|
@ -38,6 +38,12 @@ def parseFile(inputFile) -> (dict, dict):
|
|||
|
||||
# See verify.py
|
||||
config, defaults = verify.verifyConfig(config)
|
||||
if 'warnings' in config:
|
||||
for cclass, messages in config.pop('warnings').items():
|
||||
print(f'Warning in {cclass}:', file=stderr)
|
||||
for msg in messages:
|
||||
print(f' {msg}', file=stderr)
|
||||
|
||||
if 'errors' in config:
|
||||
for cclass, messages in config['errors'].items():
|
||||
print(f'Error in {cclass}:', file=stderr)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import re
|
||||
|
||||
validKeyList = [
|
||||
# top row
|
||||
'escape', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12',
|
||||
|
@ -18,7 +20,9 @@ validKeyList = [
|
|||
'leftarrow', 'rightarrow'
|
||||
]
|
||||
|
||||
class bind(object):
|
||||
popErrors = (AttributeError, KeyError, TypeError)
|
||||
|
||||
class Bind(object):
|
||||
'''
|
||||
Parent class for all bind types.
|
||||
Verifies key, creates local variables
|
||||
|
@ -26,18 +30,26 @@ class bind(object):
|
|||
bindTypes = []
|
||||
instances = {}
|
||||
|
||||
def __init__(self, key, fields):
|
||||
self.alias = False
|
||||
self.key = key
|
||||
self.fields = fields
|
||||
self.errors = []
|
||||
self.targetType = None
|
||||
def __init__(self, key='', fields={}, *, parent=None):
|
||||
if parent is None:
|
||||
self.alias = False
|
||||
self.key = str(key)
|
||||
self.fields = fields
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
self.TargetType = None
|
||||
else:
|
||||
self.alias = parent.alias
|
||||
self.key = parent.key
|
||||
self.fields = parent.fields
|
||||
self.errors = parent.errors
|
||||
self.warnings = parent.warnings
|
||||
|
||||
# redefined for each unique type, default just verifies key
|
||||
# and some other universal fields like alias and finds targetType
|
||||
self.verify()
|
||||
|
||||
if type(self) is bind:
|
||||
if type(self) is Bind:
|
||||
# not using isinstance(), because all subclasses are also instances
|
||||
# of bind.
|
||||
return
|
||||
|
@ -46,15 +58,15 @@ class bind(object):
|
|||
# verify function should remove all fields relavent to the bind.
|
||||
# Any extras are errors
|
||||
|
||||
self.errors.append(f'extra fields in "{self.key}":')
|
||||
self.warnings.append(f'extra fields in "{self.key}":')
|
||||
if isinstance(self.fields, str):
|
||||
# iterating over a str returns each character,
|
||||
# making meaningless error messages
|
||||
self.errors.append(f' "{self.fields}"')
|
||||
self.warnings.append(f' "{self.fields}"')
|
||||
else:
|
||||
for field in self.fields:
|
||||
self.errors.append(f' "{field}"')
|
||||
elif len(self.errors) == 0:
|
||||
self.warnings.append(f' "{field}"')
|
||||
if len(self.errors) == 0:
|
||||
# no errors, add new instance to the list of instances
|
||||
try:
|
||||
self.instances[type(self)].append(self)
|
||||
|
@ -62,20 +74,20 @@ class bind(object):
|
|||
self.instances[type(self)] = [self]
|
||||
|
||||
def verify(self):
|
||||
|
||||
try:
|
||||
self.alias = self.fields.pop('alias')
|
||||
if not isinstance(self.alias, bool):
|
||||
self.errors.append(f'alias should be "yes" or "no", not "{self.alias}"')
|
||||
self.errors.append(
|
||||
f'alias should be "yes" or "no", not "{self.alias}"'
|
||||
)
|
||||
self.alias = False
|
||||
except (KeyError, AttributeError, TypeError):
|
||||
# KeyError, if dict but no alias field;
|
||||
# AttributeError, if no pop method;
|
||||
# TypeError, if pop method won't take str() as arg
|
||||
except popErrors:
|
||||
self.alias = False
|
||||
|
||||
try:
|
||||
typeName, self.key = self.key.split(' ', 1)
|
||||
# all types start with a capital
|
||||
typeName = typeName.lower().capitalize()
|
||||
if not self.alias:
|
||||
# don't mess with alias names
|
||||
self.key = self.key.lower()
|
||||
|
@ -84,33 +96,39 @@ class bind(object):
|
|||
self.errors.append(f'could not find type in "{self.key}"')
|
||||
return
|
||||
|
||||
for type_ in self.bindTypes:
|
||||
if typeName == type_.__name__:
|
||||
self.TargetType = type_
|
||||
break
|
||||
|
||||
if self.TargetType is None:
|
||||
self.errors.append(
|
||||
f'"{typeName}" is not a valid type for "{self.key}"'
|
||||
)
|
||||
|
||||
if (not self.alias) and (self.key not in validKeyList):
|
||||
self.errors.append(f'invalid key name: "{self.key}"')
|
||||
|
||||
for type_ in self.bindTypes:
|
||||
if typeName == type_.__name__:
|
||||
self.targetType = type_
|
||||
break
|
||||
|
||||
if self.targetType is None:
|
||||
self.errors.append(f'could not find type in "{self.key}"')
|
||||
|
||||
def toTargetType(self):
|
||||
if self.targetType is None:
|
||||
if self.TargetType is None:
|
||||
# do nothing
|
||||
bind = self
|
||||
else:
|
||||
# cast to targetType, extend errors
|
||||
bind = self.targetType(self.key, self.fields)
|
||||
bind.errors.extend(self.errors)
|
||||
bind.alias = self.alias
|
||||
return self
|
||||
# cast to targetType, "inheriting" stuff from self
|
||||
bind = self.TargetType(parent=self)
|
||||
return bind
|
||||
|
||||
def err(self, message):
|
||||
self.errors.append(f'{type(self).__name__} "{self.key}" {message}')
|
||||
self.errors.append(
|
||||
f'{type(self).__name__.lower()} "{self.key}" {message}'
|
||||
)
|
||||
|
||||
def warn(self, message):
|
||||
self.warnings.append(
|
||||
f'{type(self).__name__.lower()} "{self.key}" {message}'
|
||||
)
|
||||
|
||||
|
||||
class impulse(bind):
|
||||
class Impulse(Bind):
|
||||
def verify(self):
|
||||
self.command = None
|
||||
if not isinstance(self.fields, dict):
|
||||
|
@ -118,15 +136,14 @@ class impulse(bind):
|
|||
|
||||
try:
|
||||
self.command = self.fields.pop('command')
|
||||
if isinstance(self.command, str):
|
||||
self.command = self.command.split(';')
|
||||
elif not isinstance(self.command, list):
|
||||
self.err('`command` field must be argument of string or list')
|
||||
self.command = None
|
||||
except KeyError:
|
||||
self.err('requires `command` field')
|
||||
|
||||
if isinstance(self.command, str):
|
||||
self.command = self.command.split(';')
|
||||
|
||||
elif not isinstance(self.command, list):
|
||||
self.err('`command` field must be argument of string or list')
|
||||
self.command = None
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
|
@ -165,7 +182,8 @@ class impulse(bind):
|
|||
elif cmd == 'build' or cmd == 'destroy':
|
||||
restOfCmd = self.expandBuildings(restOfCmd)
|
||||
|
||||
elif cmd == 'load_itempreset' and restOfCmd.isalpha():
|
||||
elif cmd == 'loadout' and restOfCmd.isalpha():
|
||||
cmd = 'load_itempreset'
|
||||
try:
|
||||
restOfCmd = restOfCmd.lower()
|
||||
restOfCmd = str(['a','b','c','d'].index(restOfCmd))
|
||||
|
@ -173,6 +191,11 @@ class impulse(bind):
|
|||
# not a load_itempreset shortcut
|
||||
pass
|
||||
|
||||
elif cmd == 'unalias':
|
||||
cmd = 'alias'
|
||||
# adding empty arg to indicate unaliasing
|
||||
restOfCmd += ' '
|
||||
|
||||
if restOfCmd != '':
|
||||
cmd += ' ' + restOfCmd
|
||||
instList[i] = cmd
|
||||
|
@ -205,7 +228,7 @@ class impulse(bind):
|
|||
return num
|
||||
|
||||
|
||||
class hold(bind):
|
||||
class Hold(Bind):
|
||||
def verify(self):
|
||||
self.press = None
|
||||
self.release = None
|
||||
|
@ -215,31 +238,32 @@ class hold(bind):
|
|||
# verify press
|
||||
try:
|
||||
self.press = self.fields.pop('press')
|
||||
if isinstance(self.press, str):
|
||||
self.press = self.press.split(';')
|
||||
elif not isinstance(self.press, list):
|
||||
self.err('`press` field must be string or list')
|
||||
self.press = None
|
||||
except KeyError:
|
||||
self.err('requires `press` field')
|
||||
|
||||
if isinstance(self.press, str):
|
||||
self.press = self.press.split(';')
|
||||
elif not isinstance(self.press, list):
|
||||
self.err('`press` field must be string or list')
|
||||
self.press = None
|
||||
if self.press is None:
|
||||
return
|
||||
|
||||
# verify release
|
||||
try:
|
||||
self.release = self.fields.pop('release')
|
||||
except KeyError:
|
||||
if isinstance(self.release, str):
|
||||
self.release = self.release.split(';')
|
||||
elif not isinstance(self.release, list):
|
||||
self.err('`release` field must be string or list')
|
||||
self.release = None
|
||||
except popErrors:
|
||||
self.warn('has no `release`, creating one')
|
||||
# no release specified, do -action for each item in press
|
||||
self.release = []
|
||||
for cmd in self.press:
|
||||
if cmd[0] == '+':
|
||||
self.release.append('-' + cmd[1:])
|
||||
|
||||
if isinstance(self.release, str):
|
||||
self.release = self.release.split(';')
|
||||
elif not isinstance(self.release, list):
|
||||
self.err('"release" field must be string or list')
|
||||
self.release = None
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
|
@ -249,11 +273,11 @@ class hold(bind):
|
|||
|
||||
# Making impulse instances from self.press and .release
|
||||
# allows them to share the shortcuts
|
||||
pressObj = impulse(f'+{holdStr}', self.press)
|
||||
pressObj = Impulse(f'+{holdStr}', self.press)
|
||||
pressObj.alias = True
|
||||
pressStr = pressObj.toTF2()
|
||||
|
||||
releaseObj = impulse(f'-{holdStr}', self.release)
|
||||
releaseObj = Impulse(f'-{holdStr}', self.release)
|
||||
releaseObj.alias = True
|
||||
releaseStr = releaseObj.toTF2()
|
||||
|
||||
|
@ -262,10 +286,13 @@ class hold(bind):
|
|||
# and never deactivating
|
||||
self.key = '+' + self.key
|
||||
|
||||
return pressStr + releaseStr + f'{bindOrAlias} {self.key} "+{holdStr}"\n'
|
||||
return (
|
||||
pressStr + releaseStr
|
||||
+ f'{bindOrAlias} {self.key} "+{holdStr}"\n'
|
||||
)
|
||||
|
||||
|
||||
class toggle(bind):
|
||||
class Toggle(Bind):
|
||||
def verify(self):
|
||||
self.on = None
|
||||
self.off = None
|
||||
|
@ -275,31 +302,30 @@ class toggle(bind):
|
|||
# verify on
|
||||
try:
|
||||
self.on = self.fields.pop('on')
|
||||
if isinstance(self.on, str):
|
||||
self.on = self.on.split(';')
|
||||
elif not isinstance(self.on, list):
|
||||
self.err('`on` field must be string or list')
|
||||
self.on = None
|
||||
except KeyError:
|
||||
self.err('requires `on` field')
|
||||
|
||||
if isinstance(self.on, str):
|
||||
self.on = self.on.split(';')
|
||||
elif not isinstance(self.on, list):
|
||||
self.err('`on` field must be string or list')
|
||||
self.on = None
|
||||
if self.on is None:
|
||||
return
|
||||
|
||||
# verify off
|
||||
try:
|
||||
self.off = self.fields.pop('off')
|
||||
except KeyError:
|
||||
if isinstance(self.off, str):
|
||||
self.off = self.off.split(';')
|
||||
elif not isinstance(self.off, list):
|
||||
self.err('`off` field must be string or list')
|
||||
except popErrors:
|
||||
# no off specified, do -action for each item in on
|
||||
self.off = []
|
||||
for cmd in self.on:
|
||||
if cmd[0] == '+':
|
||||
self.off.append('-' + cmd[1:])
|
||||
|
||||
if isinstance(self.off, str):
|
||||
self.off = self.off.split(';')
|
||||
elif not isinstance(self.off, list):
|
||||
self.err('`off` field must be string or list')
|
||||
self.off = None
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
|
@ -309,27 +335,28 @@ class toggle(bind):
|
|||
onStr = f'{toggleStr}_on'
|
||||
offStr = f'{toggleStr}_off'
|
||||
|
||||
onObj = impulse(onStr, self.on)
|
||||
onObj = Impulse(onStr, self.on)
|
||||
onObj.alias = True
|
||||
toggleOn = onObj.toTF2()[
|
||||
# remove trailing " and \n
|
||||
:-2
|
||||
]
|
||||
toggleOn = onObj.toTF2()
|
||||
# remove starting/trailing " and \n
|
||||
toggleOn = toggleOn[:-2]
|
||||
|
||||
offObj = impulse(offStr, self.off)
|
||||
offObj = Impulse(offStr, self.off)
|
||||
offObj.alias = True
|
||||
toggleOff = offObj.toTF2()[:-2]
|
||||
|
||||
return \
|
||||
f'{toggleOn}; alias {toggleStr} {offStr}"\n' +\
|
||||
f'{toggleOff}; alias {toggleStr} {onStr}"\n' +\
|
||||
f'alias {toggleStr} "{onStr}"\n' +\
|
||||
f'{bindOrAlias} {self.key} "{toggleStr}"\n'
|
||||
return (
|
||||
f'{toggleOn}; alias {toggleStr} {offStr}"\n'
|
||||
+ f'{toggleOff}; alias {toggleStr} {onStr}"\n'
|
||||
+ f'alias {toggleStr} "{onStr}"\n'
|
||||
+ f'{bindOrAlias} {self.key} "{toggleStr}"\n'
|
||||
)
|
||||
|
||||
|
||||
class double(bind):
|
||||
class Double(Bind):
|
||||
defaultDict = {}
|
||||
condDict = {}
|
||||
bindNames = []
|
||||
|
||||
def verify(self):
|
||||
self.primary = None
|
||||
|
@ -345,114 +372,152 @@ class double(bind):
|
|||
self.type = None
|
||||
|
||||
# either 'released' (default) or 'both'
|
||||
self.cancel = 'released'
|
||||
self.cancelBoth = False
|
||||
|
||||
# toggler
|
||||
try:
|
||||
self.condition = self.fields.pop('condition')
|
||||
if self.condition not in validKeyList:
|
||||
self.err(f'has invalid `condition` field: "{self.condition}"')
|
||||
except KeyError:
|
||||
except popErrors:
|
||||
self.err('requires `condition` field')
|
||||
|
||||
if 'toggle' in self.fields:
|
||||
try:
|
||||
self.isToggle = self.fields.pop('toggle')
|
||||
if not isinstance(self.isToggle, bool):
|
||||
self.err(f'`toggle` field should be "yes" or "no", not "{self.isToggle}"')
|
||||
self.err(
|
||||
'`toggle` field should be "yes" or "no", '
|
||||
+ f'not "{self.isToggle}"'
|
||||
)
|
||||
except popErrors:
|
||||
self.isToggle = False
|
||||
|
||||
# type
|
||||
try:
|
||||
self.type = self.fields.pop('type')
|
||||
if self.type not in [ type_.__name__ for type_ in self.bindTypes ]:
|
||||
self.type = self.fields.pop('type').lower()
|
||||
if self.type not in self.bindNames:
|
||||
# catastrophic: invalid type
|
||||
self.err(f'has invalid type: "{self.type}"')
|
||||
return
|
||||
except KeyError:
|
||||
except popErrors:
|
||||
# catastrophic: no type given
|
||||
self.err('requires `type` field')
|
||||
return
|
||||
|
||||
# cancel mode, must happend after type has been inferred
|
||||
if 'cancel' in self.fields:
|
||||
self.cancel = self.fields.pop('cancel')
|
||||
if self.cancel in ('released', 'both'):
|
||||
if self.cancel == 'both' and self.type != 'hold':
|
||||
self.err(f'`cancel` field only affects "hold", not "{self.type}"')
|
||||
elif isinstance(self.cancel, str):
|
||||
self.err(f'`cancel` field must be "released" or "both", not "{self.cancel}"')
|
||||
try:
|
||||
cancel = self.fields.pop('cancel')
|
||||
|
||||
if not isinstance(cancel, str):
|
||||
self.err(f'`cancel` field must be "released" or "both"')
|
||||
|
||||
else:
|
||||
self.err(f'`cancel` field must be argument of "released" or "both"')
|
||||
if cancel == 'both':
|
||||
if self.type == 'hold':
|
||||
self.cancelBoth = True
|
||||
else:
|
||||
self.err(
|
||||
'`cancel` field only affects "hold", '
|
||||
+ f'not "{self.type}"'
|
||||
)
|
||||
|
||||
elif cancel != 'released':
|
||||
self.err(
|
||||
'`cancel` field must be "released" '
|
||||
+ f'or "both", not "{cancel}"'
|
||||
)
|
||||
except popErrors:
|
||||
cancel = 'released'
|
||||
|
||||
# primary action
|
||||
try:
|
||||
mainSection = self.fields.pop('primary')
|
||||
mainBind = Bind(f'{self.type} {self.primStr}', mainSection)
|
||||
mainBind = mainBind.toTargetType()
|
||||
|
||||
mainAction = bind(f'{self.type} {self.primStr}', mainSection)
|
||||
self.primary = mainAction.toTargetType()
|
||||
except KeyError:
|
||||
self.errors.extend(mainBind.errors)
|
||||
self.warnings.extend(mainBind.warnings)
|
||||
self.errors.remove(f'invalid key name: "{self.primStr}"')
|
||||
self.primary = mainBind
|
||||
except popErrors:
|
||||
self.err('requires `primary` field')
|
||||
|
||||
# secondary action
|
||||
try:
|
||||
altSection = self.fields.pop('secondary')
|
||||
altBind = Bind(f'{self.type} {self.secondStr}', altSection)
|
||||
altBind = altBind.toTargetType()
|
||||
|
||||
altBind = bind(f'{self.type} {self.secondStr}', altSection)
|
||||
self.secondary = altBind.toTargetType()
|
||||
except KeyError:
|
||||
self.errors.extend(altBind.errors)
|
||||
self.warnings.extend(altBind.warnings)
|
||||
self.errors.remove(f'invalid key name: "{self.secondStr}"')
|
||||
self.secondary = altBind
|
||||
except popErrors:
|
||||
self.err('requires `secondary` field')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
else:
|
||||
bindOrAlias = 'bind'
|
||||
|
||||
# Get code for primary and secondary actions
|
||||
def toTF2(self) -> str:
|
||||
# Get code for primary and secondary actions.
|
||||
# alias=true so the toTF2() method aliases
|
||||
# them instead of binding them
|
||||
self.primary.alias = True
|
||||
mainCode = self.primary.toTF2()
|
||||
self.secondary.alias = True
|
||||
altCode = self.secondary.toTF2()
|
||||
|
||||
# Make code to switch between the two actions
|
||||
pShiftStr = f'+shift_{self.key}'
|
||||
mShiftStr = f'-shift_{self.key}'
|
||||
|
||||
if self.cancel == 'both':
|
||||
mainCode, altCode = self.cancelBoth(mainCode, altCode)
|
||||
if self.cancelBoth:
|
||||
mainCode, altCode = self.getCancelCode(mainCode, altCode)
|
||||
|
||||
if self.type == 'hold':
|
||||
self.primStr = '+' + self.primStr
|
||||
self.secondStr = '+' + self.secondStr
|
||||
|
||||
result = mainCode + altCode +\
|
||||
f'alias {pShiftStr} "{bindOrAlias} {self.key} {self.secondStr}"\n' +\
|
||||
f'alias {mShiftStr} "{bindOrAlias} {self.key} {self.primStr}"\n'+\
|
||||
f'{bindOrAlias} {self.key} "{self.primStr}"\n'
|
||||
shiftStr = f'shift_{self.key}'
|
||||
shiftCode = self.getChangeCode(shiftStr)
|
||||
self.addToCondDict(shiftStr)
|
||||
|
||||
try:
|
||||
# If the condition key (like 'mouse4') already has toggles,
|
||||
# just append another toggle string
|
||||
changes = self.condDict[self.condition]['change_keys']
|
||||
restores = self.condDict[self.condition]['restore_keys']
|
||||
return mainCode + altCode + shiftCode
|
||||
|
||||
if pShiftStr not in changes:
|
||||
# not already in changes
|
||||
changes.append(pShiftStr)
|
||||
restores.append(mShiftStr)
|
||||
def getChangeCode(self, shift):
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
else:
|
||||
bindOrAlias = 'bind'
|
||||
code = (
|
||||
f'alias +{shift} "{bindOrAlias} {self.key} {self.secondStr}"\n'
|
||||
+ f'alias -{shift} "{bindOrAlias} {self.key} {self.primStr}"\n'
|
||||
)
|
||||
|
||||
except KeyError:
|
||||
# If the condition key doesn't already exist, make it
|
||||
if self.isToggle:
|
||||
toggleObj = Toggle(shift, f'+{shift}')
|
||||
toggleObj.alias = True # so it aliases instead of binding
|
||||
code += toggleObj.toTF2()
|
||||
else:
|
||||
code += f'{bindOrAlias} {self.key} "{self.primStr}"\n'
|
||||
return code
|
||||
|
||||
def addToCondDict(self, shiftStr):
|
||||
if self.isToggle:
|
||||
changeStr = shiftStr
|
||||
else:
|
||||
changeStr = '+' + shiftStr
|
||||
restoreStr = '-' + shiftStr
|
||||
|
||||
if self.condition not in self.condDict:
|
||||
# if not already present, make dict for key
|
||||
self.condDict.update( {
|
||||
self.condition: {
|
||||
'change_keys': [ pShiftStr ],
|
||||
'restore_keys': [ mShiftStr ],
|
||||
'alias': self.alias
|
||||
'change_keys': [],
|
||||
'restore_keys': []
|
||||
}
|
||||
} )
|
||||
|
||||
return result
|
||||
self.condDict[self.condition]['change_keys'].append(changeStr)
|
||||
if self.isToggle == False:
|
||||
self.condDict[self.condition]['restore_keys'].append(restoreStr)
|
||||
|
||||
def cancelBoth(self, mainCode, altCode) -> (str, str):
|
||||
def getCancelCode(self, mainCode, altCode) -> (str, str):
|
||||
# code to cancel both if either is released
|
||||
# it copies the - statement from each to both.
|
||||
# if it just extracted the name of the - statement,
|
||||
|
@ -469,18 +534,13 @@ class double(bind):
|
|||
altMinusStr = altMinusLine.split(' ', 2)[2][1:-1]
|
||||
|
||||
# remove duplicate - actions
|
||||
mainMinusList = set(mainMinusStr.split(';'))
|
||||
altMinusList = set(altMinusStr.split(';'))
|
||||
uniqMain = mainMinusList.difference(altMinusList)
|
||||
uniqAlt = altMinusList.difference(mainMinusList)
|
||||
mainMinusSet = set(mainMinusStr.split(';'))
|
||||
altMinusSet = set(altMinusStr.split(';'))
|
||||
allCancels = mainMinusSet | altMinusSet
|
||||
allCancelStr = ';'.join(allCancels)
|
||||
|
||||
mainMinusStr = ';'.join(uniqMain)
|
||||
altMinusStr = ';'.join(uniqAlt)
|
||||
if not uniqMain.issuperset(uniqAlt):
|
||||
# main has things alt doesn't
|
||||
mainLines[1] = mainLines[1][:-1] + f';{altMinusStr}"'
|
||||
if not uniqAlt.issuperset(uniqMain):
|
||||
altLines[1] = altLines[1][:-1] + f';{mainMinusStr}"'
|
||||
altLines[1] = altLines[1][:-1] + f';{allCancelStr}"'
|
||||
mainLines[1] = mainLines[1][:-1] + f';{allCancelStr}"'
|
||||
|
||||
return (
|
||||
'\n'.join(mainLines) + '\n',
|
||||
|
@ -488,7 +548,7 @@ class double(bind):
|
|||
)
|
||||
|
||||
|
||||
class repeat(bind):
|
||||
class Repeat(Bind):
|
||||
def verify(self):
|
||||
self.interval = None
|
||||
self.command = None
|
||||
|
@ -497,25 +557,73 @@ class repeat(bind):
|
|||
intervalStr = str(self.fields.pop('interval'))
|
||||
self.interval = int(intervalStr)
|
||||
if self.interval <= 0:
|
||||
self.err('interval must be greater than 0')
|
||||
except KeyError:
|
||||
self.err('requires interval')
|
||||
self.err('`interval` must be greater than 0')
|
||||
except (KeyError, TypeError):
|
||||
self.err('requires `interval` field')
|
||||
except ValueError:
|
||||
self.err(f'has invalid number of ticks: "{self.interval}"')
|
||||
self.err(f'has invalid `interval`: "{self.interval}"')
|
||||
except AttributeError:
|
||||
self.err(f'requires `interval` field')
|
||||
|
||||
try:
|
||||
self.command = self.fields.pop('command')
|
||||
if not isinstance(self.command, (str, list)):
|
||||
self.err('command must be string or list')
|
||||
self.err('`command` must be string or list')
|
||||
self.command = None
|
||||
except KeyError:
|
||||
self.err('requires command')
|
||||
except popErrors:
|
||||
self.err('requires `command` field')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
# commented-out placeholder
|
||||
return f'// repeat {self.key}\n'
|
||||
|
||||
|
||||
class Literal(Bind):
|
||||
def verify(self):
|
||||
self.text = ''
|
||||
self.run = False
|
||||
if not isinstance(self.fields, dict):
|
||||
self.fields = {'text': self.fields}
|
||||
|
||||
if not self.alias:
|
||||
try:
|
||||
# keyname should be invalid, remove the error
|
||||
self.errors.remove(
|
||||
f'invalid key name: "{self.key}"'
|
||||
)
|
||||
except ValueError:
|
||||
# if not invalid key, indicate as such
|
||||
self.warn('should not use a key as a label')
|
||||
|
||||
if 'run' in self.fields:
|
||||
self.run = self.fields.pop('run')
|
||||
if not isinstance(self.run, bool):
|
||||
self.errors.append(
|
||||
f'`run` should be "yes" or "no", not "{self.run}"'
|
||||
)
|
||||
if not self.alias:
|
||||
self.warn('`run` specified without alias')
|
||||
|
||||
try:
|
||||
self.text = self.fields.pop('text')
|
||||
except KeyError:
|
||||
self.err('requires `text` field')
|
||||
|
||||
if isinstance(self.text, str):
|
||||
self.text = self.text.split(';')
|
||||
elif not isinstance(self.text, list):
|
||||
self.err('argument must be of string or list')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
result = ';'.join(self.text)
|
||||
if self.alias:
|
||||
result = f'alias {self.key} "{result}"'
|
||||
if self.run:
|
||||
result += f'\n{self.key}'
|
||||
return result + '\n'
|
||||
|
||||
# This is at the bottom because it has to happen after
|
||||
# all inheritances have been completed
|
||||
|
||||
bind.bindTypes = bind.__subclasses__()
|
||||
Bind.bindTypes = Bind.__subclasses__()
|
||||
Double.bindNames = [ bind.__name__.lower() for bind in Bind.bindTypes ]
|
|
@ -6,6 +6,7 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
verifiedConfig = {}
|
||||
|
||||
errors = {}
|
||||
warnings = {}
|
||||
|
||||
# Do defaults first
|
||||
defaults = []
|
||||
|
@ -26,28 +27,27 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
for isclass, class_ in enumerate(classList):
|
||||
|
||||
classCFG = None
|
||||
classBinds = []
|
||||
className = class_
|
||||
|
||||
if isinstance(class_, str) and class_ in cfg:
|
||||
classCFG = cfg.pop(class_)
|
||||
|
||||
elif isinstance(class_, tuple):
|
||||
for tupClass in class_:
|
||||
if tupClass in cfg:
|
||||
classCFG = cfg.pop(tupClass)
|
||||
className = class_[0]
|
||||
break
|
||||
|
||||
if classCFG is None:
|
||||
# Invalid class, this gets caught later.
|
||||
# It may be less efficient this way, but
|
||||
# it makes for more descriptive error messages
|
||||
continue
|
||||
|
||||
classBinds = []
|
||||
errMessages = []
|
||||
warnMessages = []
|
||||
for key, data in classCFG.items():
|
||||
bind = tftypes.bind(key, data)
|
||||
bind = tftypes.Bind(key, data)
|
||||
|
||||
bind = bind.toTargetType()
|
||||
if isclass:
|
||||
|
@ -56,9 +56,12 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
defaults.append(bind)
|
||||
|
||||
errMessages.extend(bind.errors)
|
||||
warnMessages.extend(bind.warnings)
|
||||
|
||||
if len(errMessages) > 0:
|
||||
errors.update( {className: errMessages} )
|
||||
if len(warnMessages) > 0:
|
||||
warnings.update( {className: warnMessages} )
|
||||
|
||||
verifiedConfig.update({className: classBinds})
|
||||
|
||||
|
@ -82,6 +85,8 @@ def verifyConfig(cfg: dict) -> (dict, dict):
|
|||
|
||||
if len(errors) > 0:
|
||||
verifiedConfig.update({'errors': errors})
|
||||
if len(warnings) > 0:
|
||||
verifiedConfig.update({'warnings': warnings})
|
||||
|
||||
return verifiedConfig, defaults
|
||||
|
||||
|
|
Loading…
Reference in New Issue