Just, a ton of work. Aded literals
parent
0bbca21fea
commit
f0dac0d1a1
|
@ -1,3 +1,5 @@
|
||||||
|
import re
|
||||||
|
|
||||||
validKeyList = [
|
validKeyList = [
|
||||||
# top row
|
# top row
|
||||||
'escape', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12',
|
'escape', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12',
|
||||||
|
@ -18,7 +20,9 @@ validKeyList = [
|
||||||
'leftarrow', 'rightarrow'
|
'leftarrow', 'rightarrow'
|
||||||
]
|
]
|
||||||
|
|
||||||
class bind(object):
|
popErrors = (AttributeError, KeyError, TypeError)
|
||||||
|
|
||||||
|
class Bind(object):
|
||||||
'''
|
'''
|
||||||
Parent class for all bind types.
|
Parent class for all bind types.
|
||||||
Verifies key, creates local variables
|
Verifies key, creates local variables
|
||||||
|
@ -26,18 +30,26 @@ class bind(object):
|
||||||
bindTypes = []
|
bindTypes = []
|
||||||
instances = {}
|
instances = {}
|
||||||
|
|
||||||
def __init__(self, key, fields):
|
def __init__(self, key='', fields={}, *, parent=None):
|
||||||
self.alias = False
|
if parent is None:
|
||||||
self.key = key
|
self.alias = False
|
||||||
self.fields = fields
|
self.key = str(key)
|
||||||
self.errors = []
|
self.fields = fields
|
||||||
self.targetType = None
|
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
|
# redefined for each unique type, default just verifies key
|
||||||
# and some other universal fields like alias and finds targetType
|
# and some other universal fields like alias and finds targetType
|
||||||
self.verify()
|
self.verify()
|
||||||
|
|
||||||
if type(self) is bind:
|
if type(self) is Bind:
|
||||||
# not using isinstance(), because all subclasses are also instances
|
# not using isinstance(), because all subclasses are also instances
|
||||||
# of bind.
|
# of bind.
|
||||||
return
|
return
|
||||||
|
@ -46,15 +58,15 @@ class bind(object):
|
||||||
# verify function should remove all fields relavent to the bind.
|
# verify function should remove all fields relavent to the bind.
|
||||||
# Any extras are errors
|
# 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):
|
if isinstance(self.fields, str):
|
||||||
# iterating over a str returns each character,
|
# iterating over a str returns each character,
|
||||||
# making meaningless error messages
|
# making meaningless error messages
|
||||||
self.errors.append(f' "{self.fields}"')
|
self.warnings.append(f' "{self.fields}"')
|
||||||
else:
|
else:
|
||||||
for field in self.fields:
|
for field in self.fields:
|
||||||
self.errors.append(f' "{field}"')
|
self.warnings.append(f' "{field}"')
|
||||||
elif len(self.errors) == 0:
|
if len(self.errors) == 0:
|
||||||
# no errors, add new instance to the list of instances
|
# no errors, add new instance to the list of instances
|
||||||
try:
|
try:
|
||||||
self.instances[type(self)].append(self)
|
self.instances[type(self)].append(self)
|
||||||
|
@ -62,20 +74,20 @@ class bind(object):
|
||||||
self.instances[type(self)] = [self]
|
self.instances[type(self)] = [self]
|
||||||
|
|
||||||
def verify(self):
|
def verify(self):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.alias = self.fields.pop('alias')
|
self.alias = self.fields.pop('alias')
|
||||||
if not isinstance(self.alias, bool):
|
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
|
self.alias = False
|
||||||
except (KeyError, AttributeError, TypeError):
|
except popErrors:
|
||||||
# KeyError, if dict but no alias field;
|
|
||||||
# AttributeError, if no pop method;
|
|
||||||
# TypeError, if pop method won't take str() as arg
|
|
||||||
self.alias = False
|
self.alias = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
typeName, self.key = self.key.split(' ', 1)
|
typeName, self.key = self.key.split(' ', 1)
|
||||||
|
# all types start with a capital
|
||||||
|
typeName = typeName.lower().capitalize()
|
||||||
if not self.alias:
|
if not self.alias:
|
||||||
# don't mess with alias names
|
# don't mess with alias names
|
||||||
self.key = self.key.lower()
|
self.key = self.key.lower()
|
||||||
|
@ -84,33 +96,39 @@ class bind(object):
|
||||||
self.errors.append(f'could not find type in "{self.key}"')
|
self.errors.append(f'could not find type in "{self.key}"')
|
||||||
return
|
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):
|
if (not self.alias) and (self.key not in validKeyList):
|
||||||
self.errors.append(f'invalid key name: "{self.key}"')
|
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):
|
def toTargetType(self):
|
||||||
if self.targetType is None:
|
if self.TargetType is None:
|
||||||
# do nothing
|
# do nothing
|
||||||
bind = self
|
return self
|
||||||
else:
|
# cast to targetType, "inheriting" stuff from self
|
||||||
# cast to targetType, extend errors
|
bind = self.TargetType(parent=self)
|
||||||
bind = self.targetType(self.key, self.fields)
|
|
||||||
bind.errors.extend(self.errors)
|
|
||||||
bind.alias = self.alias
|
|
||||||
return bind
|
return bind
|
||||||
|
|
||||||
def err(self, message):
|
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):
|
def verify(self):
|
||||||
self.command = None
|
self.command = None
|
||||||
if not isinstance(self.fields, dict):
|
if not isinstance(self.fields, dict):
|
||||||
|
@ -118,15 +136,14 @@ class impulse(bind):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.command = self.fields.pop('command')
|
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:
|
except KeyError:
|
||||||
self.err('requires `command` field')
|
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:
|
def toTF2(self) -> str:
|
||||||
if self.alias:
|
if self.alias:
|
||||||
|
@ -165,7 +182,8 @@ class impulse(bind):
|
||||||
elif cmd == 'build' or cmd == 'destroy':
|
elif cmd == 'build' or cmd == 'destroy':
|
||||||
restOfCmd = self.expandBuildings(restOfCmd)
|
restOfCmd = self.expandBuildings(restOfCmd)
|
||||||
|
|
||||||
elif cmd == 'load_itempreset' and restOfCmd.isalpha():
|
elif cmd == 'loadout' and restOfCmd.isalpha():
|
||||||
|
cmd = 'load_itempreset'
|
||||||
try:
|
try:
|
||||||
restOfCmd = restOfCmd.lower()
|
restOfCmd = restOfCmd.lower()
|
||||||
restOfCmd = str(['a','b','c','d'].index(restOfCmd))
|
restOfCmd = str(['a','b','c','d'].index(restOfCmd))
|
||||||
|
@ -173,6 +191,11 @@ class impulse(bind):
|
||||||
# not a load_itempreset shortcut
|
# not a load_itempreset shortcut
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
elif cmd == 'unalias':
|
||||||
|
cmd = 'alias'
|
||||||
|
# adding empty arg to indicate unaliasing
|
||||||
|
restOfCmd += ' '
|
||||||
|
|
||||||
if restOfCmd != '':
|
if restOfCmd != '':
|
||||||
cmd += ' ' + restOfCmd
|
cmd += ' ' + restOfCmd
|
||||||
instList[i] = cmd
|
instList[i] = cmd
|
||||||
|
@ -205,7 +228,7 @@ class impulse(bind):
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
class hold(bind):
|
class Hold(Bind):
|
||||||
def verify(self):
|
def verify(self):
|
||||||
self.press = None
|
self.press = None
|
||||||
self.release = None
|
self.release = None
|
||||||
|
@ -215,31 +238,32 @@ class hold(bind):
|
||||||
# verify press
|
# verify press
|
||||||
try:
|
try:
|
||||||
self.press = self.fields.pop('press')
|
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:
|
except KeyError:
|
||||||
self.err('requires `press` field')
|
self.err('requires `press` field')
|
||||||
|
if self.press is None:
|
||||||
if isinstance(self.press, str):
|
return
|
||||||
self.press = self.press.split(';')
|
|
||||||
elif not isinstance(self.press, list):
|
|
||||||
self.err('`press` field must be string or list')
|
|
||||||
self.press = None
|
|
||||||
|
|
||||||
# verify release
|
# verify release
|
||||||
try:
|
try:
|
||||||
self.release = self.fields.pop('release')
|
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
|
# no release specified, do -action for each item in press
|
||||||
self.release = []
|
self.release = []
|
||||||
for cmd in self.press:
|
for cmd in self.press:
|
||||||
if cmd[0] == '+':
|
if cmd[0] == '+':
|
||||||
self.release.append('-' + cmd[1:])
|
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:
|
def toTF2(self) -> str:
|
||||||
if self.alias:
|
if self.alias:
|
||||||
bindOrAlias = 'alias'
|
bindOrAlias = 'alias'
|
||||||
|
@ -249,11 +273,11 @@ class hold(bind):
|
||||||
|
|
||||||
# Making impulse instances from self.press and .release
|
# Making impulse instances from self.press and .release
|
||||||
# allows them to share the shortcuts
|
# allows them to share the shortcuts
|
||||||
pressObj = impulse(f'+{holdStr}', self.press)
|
pressObj = Impulse(f'+{holdStr}', self.press)
|
||||||
pressObj.alias = True
|
pressObj.alias = True
|
||||||
pressStr = pressObj.toTF2()
|
pressStr = pressObj.toTF2()
|
||||||
|
|
||||||
releaseObj = impulse(f'-{holdStr}', self.release)
|
releaseObj = Impulse(f'-{holdStr}', self.release)
|
||||||
releaseObj.alias = True
|
releaseObj.alias = True
|
||||||
releaseStr = releaseObj.toTF2()
|
releaseStr = releaseObj.toTF2()
|
||||||
|
|
||||||
|
@ -262,10 +286,13 @@ class hold(bind):
|
||||||
# and never deactivating
|
# and never deactivating
|
||||||
self.key = '+' + self.key
|
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):
|
def verify(self):
|
||||||
self.on = None
|
self.on = None
|
||||||
self.off = None
|
self.off = None
|
||||||
|
@ -275,31 +302,30 @@ class toggle(bind):
|
||||||
# verify on
|
# verify on
|
||||||
try:
|
try:
|
||||||
self.on = self.fields.pop('on')
|
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:
|
except KeyError:
|
||||||
self.err('requires `on` field')
|
self.err('requires `on` field')
|
||||||
|
if self.on is None:
|
||||||
if isinstance(self.on, str):
|
return
|
||||||
self.on = self.on.split(';')
|
|
||||||
elif not isinstance(self.on, list):
|
|
||||||
self.err('`on` field must be string or list')
|
|
||||||
self.on = None
|
|
||||||
|
|
||||||
# verify off
|
# verify off
|
||||||
try:
|
try:
|
||||||
self.off = self.fields.pop('off')
|
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
|
# no off specified, do -action for each item in on
|
||||||
self.off = []
|
self.off = []
|
||||||
for cmd in self.on:
|
for cmd in self.on:
|
||||||
if cmd[0] == '+':
|
if cmd[0] == '+':
|
||||||
self.off.append('-' + cmd[1:])
|
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:
|
def toTF2(self) -> str:
|
||||||
if self.alias:
|
if self.alias:
|
||||||
bindOrAlias = 'alias'
|
bindOrAlias = 'alias'
|
||||||
|
@ -309,27 +335,28 @@ class toggle(bind):
|
||||||
onStr = f'{toggleStr}_on'
|
onStr = f'{toggleStr}_on'
|
||||||
offStr = f'{toggleStr}_off'
|
offStr = f'{toggleStr}_off'
|
||||||
|
|
||||||
onObj = impulse(onStr, self.on)
|
onObj = Impulse(onStr, self.on)
|
||||||
onObj.alias = True
|
onObj.alias = True
|
||||||
toggleOn = onObj.toTF2()[
|
toggleOn = onObj.toTF2()
|
||||||
# remove trailing " and \n
|
# remove starting/trailing " and \n
|
||||||
:-2
|
toggleOn = toggleOn[:-2]
|
||||||
]
|
|
||||||
|
|
||||||
offObj = impulse(offStr, self.off)
|
offObj = Impulse(offStr, self.off)
|
||||||
offObj.alias = True
|
offObj.alias = True
|
||||||
toggleOff = offObj.toTF2()[:-2]
|
toggleOff = offObj.toTF2()[:-2]
|
||||||
|
|
||||||
return \
|
return (
|
||||||
f'{toggleOn}; alias {toggleStr} {offStr}"\n' +\
|
f'{toggleOn}; alias {toggleStr} {offStr}"\n'
|
||||||
f'{toggleOff}; alias {toggleStr} {onStr}"\n' +\
|
+ f'{toggleOff}; alias {toggleStr} {onStr}"\n'
|
||||||
f'alias {toggleStr} "{onStr}"\n' +\
|
+ f'alias {toggleStr} "{onStr}"\n'
|
||||||
f'{bindOrAlias} {self.key} "{toggleStr}"\n'
|
+ f'{bindOrAlias} {self.key} "{toggleStr}"\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class double(bind):
|
class Double(Bind):
|
||||||
defaultDict = {}
|
defaultDict = {}
|
||||||
condDict = {}
|
condDict = {}
|
||||||
|
bindNames = []
|
||||||
|
|
||||||
def verify(self):
|
def verify(self):
|
||||||
self.primary = None
|
self.primary = None
|
||||||
|
@ -345,114 +372,152 @@ class double(bind):
|
||||||
self.type = None
|
self.type = None
|
||||||
|
|
||||||
# either 'released' (default) or 'both'
|
# either 'released' (default) or 'both'
|
||||||
self.cancel = 'released'
|
self.cancelBoth = False
|
||||||
|
|
||||||
# toggler
|
# toggler
|
||||||
try:
|
try:
|
||||||
self.condition = self.fields.pop('condition')
|
self.condition = self.fields.pop('condition')
|
||||||
if self.condition not in validKeyList:
|
if self.condition not in validKeyList:
|
||||||
self.err(f'has invalid `condition` field: "{self.condition}"')
|
self.err(f'has invalid `condition` field: "{self.condition}"')
|
||||||
except KeyError:
|
except popErrors:
|
||||||
self.err('requires `condition` field')
|
self.err('requires `condition` field')
|
||||||
|
|
||||||
if 'toggle' in self.fields:
|
try:
|
||||||
self.isToggle = self.fields.pop('toggle')
|
self.isToggle = self.fields.pop('toggle')
|
||||||
if not isinstance(self.isToggle, bool):
|
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
|
# type
|
||||||
try:
|
try:
|
||||||
self.type = self.fields.pop('type')
|
self.type = self.fields.pop('type').lower()
|
||||||
if self.type not in [ type_.__name__ for type_ in self.bindTypes ]:
|
if self.type not in self.bindNames:
|
||||||
# catastrophic: invalid type
|
# catastrophic: invalid type
|
||||||
self.err(f'has invalid type: "{self.type}"')
|
self.err(f'has invalid type: "{self.type}"')
|
||||||
return
|
return
|
||||||
except KeyError:
|
except popErrors:
|
||||||
# catastrophic: no type given
|
# catastrophic: no type given
|
||||||
self.err('requires `type` field')
|
self.err('requires `type` field')
|
||||||
return
|
return
|
||||||
|
|
||||||
# cancel mode, must happend after type has been inferred
|
# cancel mode, must happend after type has been inferred
|
||||||
if 'cancel' in self.fields:
|
try:
|
||||||
self.cancel = self.fields.pop('cancel')
|
cancel = self.fields.pop('cancel')
|
||||||
if self.cancel in ('released', 'both'):
|
|
||||||
if self.cancel == 'both' and self.type != 'hold':
|
if not isinstance(cancel, str):
|
||||||
self.err(f'`cancel` field only affects "hold", not "{self.type}"')
|
self.err(f'`cancel` field must be "released" or "both"')
|
||||||
elif isinstance(self.cancel, str):
|
|
||||||
self.err(f'`cancel` field must be "released" or "both", not "{self.cancel}"')
|
|
||||||
else:
|
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
|
# primary action
|
||||||
try:
|
try:
|
||||||
mainSection = self.fields.pop('primary')
|
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.errors.extend(mainBind.errors)
|
||||||
self.primary = mainAction.toTargetType()
|
self.warnings.extend(mainBind.warnings)
|
||||||
except KeyError:
|
self.errors.remove(f'invalid key name: "{self.primStr}"')
|
||||||
|
self.primary = mainBind
|
||||||
|
except popErrors:
|
||||||
self.err('requires `primary` field')
|
self.err('requires `primary` field')
|
||||||
|
|
||||||
# secondary action
|
# secondary action
|
||||||
try:
|
try:
|
||||||
altSection = self.fields.pop('secondary')
|
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.errors.extend(altBind.errors)
|
||||||
self.secondary = altBind.toTargetType()
|
self.warnings.extend(altBind.warnings)
|
||||||
except KeyError:
|
self.errors.remove(f'invalid key name: "{self.secondStr}"')
|
||||||
|
self.secondary = altBind
|
||||||
|
except popErrors:
|
||||||
self.err('requires `secondary` field')
|
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
|
self.primary.alias = True
|
||||||
mainCode = self.primary.toTF2()
|
mainCode = self.primary.toTF2()
|
||||||
self.secondary.alias = True
|
self.secondary.alias = True
|
||||||
altCode = self.secondary.toTF2()
|
altCode = self.secondary.toTF2()
|
||||||
|
|
||||||
# Make code to switch between the two actions
|
# Make code to switch between the two actions
|
||||||
pShiftStr = f'+shift_{self.key}'
|
if self.cancelBoth:
|
||||||
mShiftStr = f'-shift_{self.key}'
|
mainCode, altCode = self.getCancelCode(mainCode, altCode)
|
||||||
|
|
||||||
if self.cancel == 'both':
|
|
||||||
mainCode, altCode = self.cancelBoth(mainCode, altCode)
|
|
||||||
|
|
||||||
if self.type == 'hold':
|
if self.type == 'hold':
|
||||||
self.primStr = '+' + self.primStr
|
self.primStr = '+' + self.primStr
|
||||||
self.secondStr = '+' + self.secondStr
|
self.secondStr = '+' + self.secondStr
|
||||||
|
|
||||||
result = mainCode + altCode +\
|
shiftStr = f'shift_{self.key}'
|
||||||
f'alias {pShiftStr} "{bindOrAlias} {self.key} {self.secondStr}"\n' +\
|
shiftCode = self.getChangeCode(shiftStr)
|
||||||
f'alias {mShiftStr} "{bindOrAlias} {self.key} {self.primStr}"\n'+\
|
self.addToCondDict(shiftStr)
|
||||||
f'{bindOrAlias} {self.key} "{self.primStr}"\n'
|
|
||||||
|
|
||||||
try:
|
return mainCode + altCode + shiftCode
|
||||||
# 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']
|
|
||||||
|
|
||||||
if pShiftStr not in changes:
|
def getChangeCode(self, shift):
|
||||||
# not already in changes
|
if self.alias:
|
||||||
changes.append(pShiftStr)
|
bindOrAlias = 'alias'
|
||||||
restores.append(mShiftStr)
|
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 self.isToggle:
|
||||||
# If the condition key doesn't already exist, make it
|
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.condDict.update( {
|
||||||
self.condition: {
|
self.condition: {
|
||||||
'change_keys': [ pShiftStr ],
|
'change_keys': [],
|
||||||
'restore_keys': [ mShiftStr ],
|
'restore_keys': []
|
||||||
'alias': self.alias
|
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
|
|
||||||
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
|
# code to cancel both if either is released
|
||||||
# it copies the - statement from each to both.
|
# it copies the - statement from each to both.
|
||||||
# if it just extracted the name of the - statement,
|
# if it just extracted the name of the - statement,
|
||||||
|
@ -469,18 +534,13 @@ class double(bind):
|
||||||
altMinusStr = altMinusLine.split(' ', 2)[2][1:-1]
|
altMinusStr = altMinusLine.split(' ', 2)[2][1:-1]
|
||||||
|
|
||||||
# remove duplicate - actions
|
# remove duplicate - actions
|
||||||
mainMinusList = set(mainMinusStr.split(';'))
|
mainMinusSet = set(mainMinusStr.split(';'))
|
||||||
altMinusList = set(altMinusStr.split(';'))
|
altMinusSet = set(altMinusStr.split(';'))
|
||||||
uniqMain = mainMinusList.difference(altMinusList)
|
allCancels = mainMinusSet | altMinusSet
|
||||||
uniqAlt = altMinusList.difference(mainMinusList)
|
allCancelStr = ';'.join(allCancels)
|
||||||
|
|
||||||
mainMinusStr = ';'.join(uniqMain)
|
altLines[1] = altLines[1][:-1] + f';{allCancelStr}"'
|
||||||
altMinusStr = ';'.join(uniqAlt)
|
mainLines[1] = mainLines[1][:-1] + f';{allCancelStr}"'
|
||||||
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}"'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
'\n'.join(mainLines) + '\n',
|
'\n'.join(mainLines) + '\n',
|
||||||
|
@ -488,7 +548,7 @@ class double(bind):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class repeat(bind):
|
class Repeat(Bind):
|
||||||
def verify(self):
|
def verify(self):
|
||||||
self.interval = None
|
self.interval = None
|
||||||
self.command = None
|
self.command = None
|
||||||
|
@ -497,25 +557,73 @@ class repeat(bind):
|
||||||
intervalStr = str(self.fields.pop('interval'))
|
intervalStr = str(self.fields.pop('interval'))
|
||||||
self.interval = int(intervalStr)
|
self.interval = int(intervalStr)
|
||||||
if self.interval <= 0:
|
if self.interval <= 0:
|
||||||
self.err('interval must be greater than 0')
|
self.err('`interval` must be greater than 0')
|
||||||
except KeyError:
|
except (KeyError, TypeError):
|
||||||
self.err('requires interval')
|
self.err('requires `interval` field')
|
||||||
except ValueError:
|
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:
|
try:
|
||||||
self.command = self.fields.pop('command')
|
self.command = self.fields.pop('command')
|
||||||
if not isinstance(self.command, (str, list)):
|
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
|
self.command = None
|
||||||
except KeyError:
|
except popErrors:
|
||||||
self.err('requires command')
|
self.err('requires `command` field')
|
||||||
|
|
||||||
def toTF2(self) -> str:
|
def toTF2(self) -> str:
|
||||||
# commented-out placeholder
|
# commented-out placeholder
|
||||||
return f'// repeat {self.key}\n'
|
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
|
# This is at the bottom because it has to happen after
|
||||||
# all inheritances have been completed
|
# all inheritances have been completed
|
||||||
|
|
||||||
bind.bindTypes = bind.__subclasses__()
|
Bind.bindTypes = Bind.__subclasses__()
|
||||||
|
Double.bindNames = [ bind.__name__.lower() for bind in Bind.bindTypes ]
|
Loading…
Reference in New Issue