Compare commits
3 Commits
8bf19e6153
...
5014161b02
Author | SHA1 | Date |
---|---|---|
Nicholas Hope | 5014161b02 | |
Nicholas Hope | 3f9f35cced | |
Nicholas Hope | 1ac808ce1d |
|
@ -1,287 +1,31 @@
|
|||
""" Makes the configs as a massive string """
|
||||
""" This has only one function. It mostly exists to allow imports of things like tftypes """
|
||||
|
||||
# Used for the conditions in the <double> type
|
||||
condDict = {}
|
||||
defaultDict = {}
|
||||
bindOrAlias = "bind"
|
||||
from copy import deepcopy
|
||||
from tfscript.tftypes import double
|
||||
|
||||
def makeCFG(cfg, default=False):
|
||||
global bindOrAlias
|
||||
global condDict
|
||||
global defaultDict
|
||||
for bind in cfg:
|
||||
print(bind.key)
|
||||
return
|
||||
def makeCFG(bindList, default=False):
|
||||
|
||||
bindOrAlias = "bind"
|
||||
if default:
|
||||
# Write to defaultDict instead of condDict
|
||||
condDict = defaultDict
|
||||
double.condDict = double.defaultDict
|
||||
else:
|
||||
condDict = deepcopy(defaultDict)
|
||||
double.condDict = deepcopy(double.defaultDict)
|
||||
|
||||
ret = ''
|
||||
for key, data in cfg.items():
|
||||
isAlias = False
|
||||
if "alias" in data:
|
||||
isAlias = data.pop("alias")
|
||||
if isAlias:
|
||||
bindOrAlias = "alias"
|
||||
else:
|
||||
bindOrAlias = "bind"
|
||||
ret += branch(key, data)
|
||||
|
||||
for bind in bindList:
|
||||
ret += bind.toTF2()
|
||||
|
||||
# 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 condDict != 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 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'{bindOrAlias} {key} "+{key}_toggles"\n'
|
||||
|
||||
del condDict # free deep copy
|
||||
f'bind {key} "+{key}_toggles"\n'
|
||||
|
||||
return ret
|
||||
|
||||
def typeOf(dictIn):
|
||||
""" Find the first element common to both lists """
|
||||
types = [
|
||||
"impulse",
|
||||
"hold",
|
||||
"toggle",
|
||||
"double",
|
||||
"repeat"
|
||||
]
|
||||
for t in types:
|
||||
if t in dictIn.keys():
|
||||
return t
|
||||
|
||||
def branch(keyName, bindContent):
|
||||
""" Using the provided keyName and content, call the correct function """
|
||||
|
||||
"""
|
||||
Terser syntax, ex.
|
||||
impulse e:
|
||||
<content>
|
||||
instead of
|
||||
e:
|
||||
impulse:
|
||||
<content>
|
||||
"""
|
||||
splitKey = keyName.split(' ', 1)
|
||||
if len(splitKey) > 1:
|
||||
keyName = splitKey[1]
|
||||
bindContent = {splitKey[0]: bindContent}
|
||||
|
||||
bindType = typeOf(bindContent)
|
||||
bindContent = bindContent.pop(bindType)
|
||||
|
||||
if bindType == "impulse":
|
||||
return impulse(keyName, bindContent)
|
||||
|
||||
elif bindType == "hold":
|
||||
if isinstance(bindContent, str):
|
||||
return simpleHold(keyName, bindContent)
|
||||
else:
|
||||
return listHold(keyName, bindContent)
|
||||
|
||||
elif bindType == "toggle":
|
||||
return toggle(keyName, bindContent)
|
||||
|
||||
elif bindType == "double":
|
||||
return double(keyName, bindContent)
|
||||
|
||||
elif bindType == "repeat":
|
||||
return repeat(keyName, bindContent)
|
||||
|
||||
def impulse(key, instruction):
|
||||
global bindOrAlias
|
||||
if isinstance(instruction, dict):
|
||||
instruction = instruction["command"]
|
||||
|
||||
if not isinstance(instruction, list):
|
||||
instruction = instruction.split(';')
|
||||
|
||||
instuction = impulseShortcuts(instruction)
|
||||
instruction = ';'.join(instruction)
|
||||
|
||||
return f'{bindOrAlias} {key} "{instruction}"\n'
|
||||
|
||||
def impulseShortcuts(instList):
|
||||
for i, instruction in enumerate(instList):
|
||||
splitCmd = instruction.split(' ')
|
||||
cmd = splitCmd[0]
|
||||
restOfCmd = ' '.join(splitCmd[1:])
|
||||
shortcuts = {
|
||||
"primary": "slot1",
|
||||
"secondary": "slot2",
|
||||
"melee": "slot3"
|
||||
}
|
||||
if cmd in shortcuts:
|
||||
cmd = shortcuts[cmd]
|
||||
|
||||
if cmd == "voice":
|
||||
cmd = "voicemenu"
|
||||
restOfCmd = voice(restOfCmd)
|
||||
|
||||
elif cmd == "build" or cmd == "destroy":
|
||||
restOfCmd = expandBuildings(restOfCmd)
|
||||
|
||||
elif cmd == "load_itempreset" and restOfCmd.isalpha():
|
||||
restOfCmd = restOfCmd.lower()
|
||||
restOfCmd = ['a','b','c','d'].index(restOfCmd)
|
||||
|
||||
if restOfCmd != "":
|
||||
cmd += ' ' + restOfCmd
|
||||
instList[i] = cmd
|
||||
|
||||
return instList
|
||||
|
||||
def voice(keyword):
|
||||
keyword = keyword.lower()
|
||||
|
||||
allLists = [
|
||||
["medic", "thanks", "go", "move up", "go left", "go right", "yes", "no", "pass to me"],
|
||||
["incoming", "spy", "sentry ahead", "teleporter here", "dispenser here", "sentry here", "activate uber", "uber ready"],
|
||||
["help", "battle cry", "cheers", "jeers", "positive", "negative", "nice shot", "good job"]
|
||||
]
|
||||
|
||||
for menu, voiceList in enumerate(allLists):
|
||||
for selection, shortcut in enumerate(voiceList):
|
||||
if keyword == shortcut:
|
||||
return f'{menu} {selection}'
|
||||
|
||||
def expandBuildings(building):
|
||||
buildingNums = {
|
||||
"dispenser": "0 0",
|
||||
"entrance": "1 0",
|
||||
"exit": "1 1",
|
||||
"sentry": "2 0"
|
||||
}
|
||||
for shortBuild, num in buildingNums.items():
|
||||
if building == shortBuild:
|
||||
return num
|
||||
|
||||
def simpleHold(key, instruction):
|
||||
global bindOrAlias
|
||||
# This isn't quite right, fix later!
|
||||
if instruction[0] == '+' or instruction[0] == '-':
|
||||
return f'{bindOrAlias} {key} "{instruction}"\n'
|
||||
else:
|
||||
return f'{bindOrAlias} {key} "+{instruction}"\n'
|
||||
|
||||
def listHold(key, options):
|
||||
global bindOrAlias
|
||||
|
||||
oldBindOrAlias = bindOrAlias
|
||||
bindOrAlias = 'alias'
|
||||
plus = impulse(f'+{key}_press', options["press"])
|
||||
minus = impulse(f'-{key}_press', options["release"])
|
||||
|
||||
bindOrAlias = oldBindOrAlias
|
||||
|
||||
ret = plus + minus + f'{bindOrAlias} {key} "+{key}_press"\n'
|
||||
|
||||
return ret
|
||||
|
||||
def toggle(key, instruction):
|
||||
global bindOrAlias
|
||||
onStr = f'turn_{key}_on'
|
||||
offStr = f'turn_{key}_off'
|
||||
togStr = f'toggle_{key}'
|
||||
if instruction[0] == '+':
|
||||
instruction = instruction[1:]
|
||||
|
||||
ret = f'alias {onStr} "+{instruction}; alias {togStr} {offStr}"\n' +\
|
||||
f'alias {offStr} "-{instruction}; alias {togStr} {onStr}"\n' +\
|
||||
f'alias {togStr} "{onStr}"\n' +\
|
||||
f'{bindOrAlias} {key} "{togStr}"\n'
|
||||
return ret
|
||||
|
||||
def double(key, options):
|
||||
typeName = options["type"]
|
||||
primaryAction = {typeName: options.pop("primary")}
|
||||
|
||||
secAction = {typeName: options.pop("secondary")}
|
||||
|
||||
mainStr = f'{key}_main'
|
||||
altStr = f'{key}_alt'
|
||||
pShiftStr = f'+shift_{key}'
|
||||
mShiftStr = f'-shift_{key}'
|
||||
|
||||
global bindOrAlias
|
||||
oldBindOrAlias = bindOrAlias
|
||||
bindOrAlias = "alias"
|
||||
|
||||
mainCode = branch(mainStr, primaryAction)
|
||||
altCode = branch(altStr, secAction)
|
||||
cancelBoth = ("cancel" in options and options["cancel"] == "both")
|
||||
if cancelBoth:
|
||||
if isinstance(primaryAction["hold"], dict):
|
||||
lines = mainCode.splitlines()
|
||||
minusCmd = lines[1]
|
||||
_, minusStr, previousMinus = minus.split(' ', 2)
|
||||
newMinus = previousMinus[0:-2] + ';' + + '"\n'
|
||||
lines[1] = f'alias {minusStr} "{newMinus}"\n'
|
||||
else:
|
||||
# simple
|
||||
pass
|
||||
|
||||
bindOrAlias = oldBindOrAlias
|
||||
|
||||
ret = mainCode + altCode +\
|
||||
f'alias {pShiftStr} "{bindOrAlias} {key} {altStr}"\n' +\
|
||||
f'alias {mShiftStr} "{bindOrAlias} {key} {mainStr}"\n'+\
|
||||
f'{bindOrAlias} {key} "{mainStr}"\n'
|
||||
|
||||
isToggle = ("toggle" in options and options.pop("toggle") == True)
|
||||
if isToggle:
|
||||
toggleStr = toggle(key, pShiftStr)
|
||||
|
||||
condName = options["condition"]
|
||||
global condDict
|
||||
if condName in condDict:
|
||||
# If the condition key (like "mouse4") already has toggles,
|
||||
# just append another toggle string
|
||||
changes = condDict[condName]["change_keys"]
|
||||
restores = condDict[condName]["restore_keys"]
|
||||
|
||||
if isToggle:
|
||||
# "toggle: true" specified, add the toggle string
|
||||
if toggleStr not in changes:
|
||||
changes.append(toggleStr)
|
||||
if pShiftStr in changes:
|
||||
# If key already has normal shift, remove it
|
||||
changes.remove(pShiftStr)
|
||||
changes.remove(mShiftStr)
|
||||
|
||||
elif pShiftStr not in changes:
|
||||
# not toggle, not already in changes
|
||||
changes.append(pShiftStr)
|
||||
restores.append(mShiftStr)
|
||||
else:
|
||||
# If the condition key doesn't already exist, make it
|
||||
if isToggle:
|
||||
condDict.update( {
|
||||
condName: {
|
||||
"change_keys": [ toggleStr ],
|
||||
"restore_keys": [ ]
|
||||
}
|
||||
} )
|
||||
else:
|
||||
condDict.update( {
|
||||
condName: {
|
||||
"change_keys": [ pShiftStr ],
|
||||
"restore_keys": [ mShiftStr ]
|
||||
}
|
||||
} )
|
||||
|
||||
return ret
|
||||
|
||||
def repeat(key, options):
|
||||
return f'placeholder for {key} (repeat)\n'
|
||||
|
|
|
@ -27,8 +27,7 @@ except ModuleNotFoundError:
|
|||
|
||||
# Local libraries
|
||||
import tfscript
|
||||
from tfscript import verify
|
||||
from tfscript import writing
|
||||
from tfscript import verify, writing, makeCFG
|
||||
|
||||
args = {}
|
||||
targetDir = ''
|
||||
|
@ -63,12 +62,12 @@ def parseConfig(config, defaults):
|
|||
if defaults is not None:
|
||||
config.update({'default': defaults})
|
||||
|
||||
for currentClass in config:
|
||||
bindList = config[currentClass]
|
||||
stringToWrite = ''
|
||||
for bind in bindList:
|
||||
stringToWrite += bind.toTF2()
|
||||
replaceDict = writing.writeOutput(stringToWrite, currentClass, args)
|
||||
for class_ in config:
|
||||
stringToWrite = makeCFG(
|
||||
config[class_],
|
||||
default=(class_ == 'default')
|
||||
)
|
||||
replaceDict = writing.writeOutput(stringToWrite, class_, args)
|
||||
tempsAndReals.update(replaceDict)
|
||||
|
||||
return tempsAndReals
|
||||
|
|
|
@ -18,19 +18,13 @@ validKeyList = [
|
|||
'leftarrow', 'rightarrow'
|
||||
]
|
||||
|
||||
bindTypes = [
|
||||
'impulse',
|
||||
'hold',
|
||||
'toggle',
|
||||
'double',
|
||||
'repeat'
|
||||
]
|
||||
|
||||
class bind:
|
||||
class bind(object):
|
||||
'''
|
||||
Parent class for all bind types.
|
||||
Verifies key, creates local variables
|
||||
'''
|
||||
bindTypes = []
|
||||
instances = {}
|
||||
|
||||
def __init__(self, key, fields):
|
||||
self.alias = False
|
||||
|
@ -43,19 +37,29 @@ class bind:
|
|||
# and some other universal fields like alias and finds targetType
|
||||
self.verify()
|
||||
|
||||
if type(self) == bind or len(self.fields) == 0:
|
||||
if type(self) is bind:
|
||||
# not using isinstance(), because all subclasses are also instances
|
||||
# of bind.
|
||||
return
|
||||
# verify function should remove all fields relavent to the bind.
|
||||
# Any extras are errors
|
||||
|
||||
self.errors.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}"')
|
||||
else:
|
||||
for field in self.fields:
|
||||
self.errors.append(f' "{field}"')
|
||||
if len(self.fields) > 0:
|
||||
# verify function should remove all fields relavent to the bind.
|
||||
# Any extras are errors
|
||||
|
||||
self.errors.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}"')
|
||||
else:
|
||||
for field in self.fields:
|
||||
self.errors.append(f' "{field}"')
|
||||
elif len(self.errors) == 0:
|
||||
# no errors, add new instance to the list of instances
|
||||
try:
|
||||
self.instances[type(self)].append(self)
|
||||
except KeyError:
|
||||
self.instances[type(self)] = [self]
|
||||
|
||||
def verify(self):
|
||||
|
||||
|
@ -63,14 +67,14 @@ class bind:
|
|||
typeName, self.key = self.key.split(' ', 1)
|
||||
self.key = self.key.lower()
|
||||
except ValueError:
|
||||
# catastrophic error: no type
|
||||
# catastrophic error: either no type or no key, assume no type
|
||||
self.errors.append(f'could not find type in "{self.key}"')
|
||||
return
|
||||
|
||||
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):
|
||||
self.alias = False
|
||||
|
@ -78,16 +82,9 @@ class bind:
|
|||
if (not self.alias) and (self.key not in validKeyList):
|
||||
self.errors.append(f'invalid key name: "{self.key}"')
|
||||
|
||||
types = {
|
||||
'impulse': impulse,
|
||||
'hold': hold,
|
||||
'toggle': toggle,
|
||||
'double': double,
|
||||
'repeat': repeat
|
||||
}
|
||||
for loopName, typeClass in types.items():
|
||||
if loopName == typeName:
|
||||
self.targetType = typeClass
|
||||
for type_ in self.bindTypes:
|
||||
if typeName == type_.__name__:
|
||||
self.targetType = type_
|
||||
break
|
||||
|
||||
if self.targetType is None:
|
||||
|
@ -101,6 +98,7 @@ class bind:
|
|||
# cast to targetType, extend errors
|
||||
bind = self.targetType(self.key, self.fields)
|
||||
bind.errors.extend(self.errors)
|
||||
bind.alias = self.alias
|
||||
return bind
|
||||
|
||||
def err(self, message):
|
||||
|
@ -110,18 +108,19 @@ class bind:
|
|||
class impulse(bind):
|
||||
def verify(self):
|
||||
self.command = None
|
||||
if not isinstance(self.fields, dict):
|
||||
self.fields = {'command': self.fields}
|
||||
|
||||
try:
|
||||
self.command = self.fields.pop('command')
|
||||
except (KeyError, AttributeError, TypeError):
|
||||
self.fields = {'command': self.fields}
|
||||
self.command = self.fields.pop('command')
|
||||
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('must be command or argument of string or list')
|
||||
self.err('`command` field must be argument of string or list')
|
||||
self.command = None
|
||||
|
||||
def toTF2(self) -> str:
|
||||
|
@ -144,9 +143,9 @@ class impulse(bind):
|
|||
cmd, restOfCmd = instruction, ''
|
||||
|
||||
simpleSCs = {
|
||||
"primary": "slot1",
|
||||
"secondary": "slot2",
|
||||
"melee": "slot3"
|
||||
'primary': 'slot1',
|
||||
'secondary': 'slot2',
|
||||
'melee': 'slot3'
|
||||
}
|
||||
try:
|
||||
cmd = simpleSCs[cmd]
|
||||
|
@ -154,14 +153,14 @@ class impulse(bind):
|
|||
# not a shortcut
|
||||
pass
|
||||
|
||||
if cmd == "voice":
|
||||
cmd = "voicemenu"
|
||||
if cmd == 'voice':
|
||||
cmd = 'voicemenu'
|
||||
restOfCmd = self.expandVoice(restOfCmd)
|
||||
|
||||
elif cmd == "build" or cmd == "destroy":
|
||||
elif cmd == 'build' or cmd == 'destroy':
|
||||
restOfCmd = self.expandBuildings(restOfCmd)
|
||||
|
||||
elif cmd == "load_itempreset" and restOfCmd.isalpha():
|
||||
elif cmd == 'load_itempreset' and restOfCmd.isalpha():
|
||||
try:
|
||||
restOfCmd = restOfCmd.lower()
|
||||
restOfCmd = ['a','b','c','d'].index(restOfCmd)
|
||||
|
@ -169,7 +168,7 @@ class impulse(bind):
|
|||
# not a load_itempreset shortcut
|
||||
pass
|
||||
|
||||
if restOfCmd != "":
|
||||
if restOfCmd != '':
|
||||
cmd += ' ' + restOfCmd
|
||||
instList[i] = cmd
|
||||
|
||||
|
@ -179,9 +178,9 @@ class impulse(bind):
|
|||
keyword = keyword.lower()
|
||||
|
||||
allLists = (
|
||||
("medic", "thanks", "go", "move up", "go left", "go right", "yes", "no", "pass to me"),
|
||||
("incoming", "spy", "sentry ahead", "teleporter here", "dispenser here", "sentry here", "activate uber", "uber ready"),
|
||||
("help", "battle cry", "cheers", "jeers", "positive", "negative", "nice shot", "good job"),
|
||||
('medic', 'thanks', 'go', 'move up', 'go left', 'go right', 'yes', 'no', 'pass to me'),
|
||||
('incoming', 'spy', 'sentry ahead', 'teleporter here', 'dispenser here', 'sentry here', 'activate uber', 'uber ready'),
|
||||
('help', 'battle cry', 'cheers', 'jeers', 'positive', 'negative', 'nice shot', 'good job'),
|
||||
)
|
||||
|
||||
for menu, voiceList in enumerate(allLists):
|
||||
|
@ -191,10 +190,10 @@ class impulse(bind):
|
|||
|
||||
def expandBuildings(self, building):
|
||||
buildingNums = {
|
||||
"dispenser": "0 0",
|
||||
"entrance": "1 0",
|
||||
"exit": "1 1",
|
||||
"sentry": "2 0"
|
||||
'dispenser': '0 0',
|
||||
'entrance': '1 0',
|
||||
'exit': '1 1',
|
||||
'sentry': '2 0'
|
||||
}
|
||||
for shortBuild, num in buildingNums.items():
|
||||
if building == shortBuild:
|
||||
|
@ -205,60 +204,122 @@ class hold(bind):
|
|||
def verify(self):
|
||||
self.press = None
|
||||
self.release = None
|
||||
if isinstance(self.fields, dict):
|
||||
# verify press
|
||||
try:
|
||||
self.press = self.fields.pop('press')
|
||||
if not isinstance(self.press, (str, list)):
|
||||
self.err('press must be string or list')
|
||||
self.press = None
|
||||
except KeyError:
|
||||
self.err('requires press field')
|
||||
|
||||
# verify release
|
||||
try:
|
||||
self.release = self.fields.pop('release')
|
||||
if not isinstance(self.release, (str, list)):
|
||||
self.err('release must be string or list')
|
||||
self.release = None
|
||||
except KeyError:
|
||||
self.err('requires release field')
|
||||
|
||||
elif isinstance(self.fields, str):
|
||||
if not isinstance(self.fields, dict):
|
||||
self.fields = {'press': self.fields}
|
||||
|
||||
# verify press
|
||||
try:
|
||||
self.press = self.fields.pop('press')
|
||||
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
|
||||
|
||||
# verify release
|
||||
try:
|
||||
self.release = self.fields.pop('release')
|
||||
except KeyError:
|
||||
# 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'
|
||||
else:
|
||||
self.err('must be press and release, or argument of string')
|
||||
bindOrAlias = 'bind'
|
||||
holdStr = f'hold_{self.key}'
|
||||
|
||||
# Making impulse instances from self.press and .release
|
||||
# allows them to share the shortcuts
|
||||
pressObj = impulse(f'+{holdStr}', self.press)
|
||||
pressObj.alias = True
|
||||
pressStr = pressObj.toTF2()
|
||||
|
||||
releaseObj = impulse(f'-{holdStr}', self.release)
|
||||
releaseObj.alias = True
|
||||
releaseStr = releaseObj.toTF2()
|
||||
|
||||
if self.alias:
|
||||
# if alias, do this to avoid activating
|
||||
# and never deactivating
|
||||
self.key = '+' + self.key
|
||||
|
||||
return pressStr + releaseStr + f'{bindOrAlias} {self.key} "+{holdStr}"\n'
|
||||
|
||||
|
||||
class toggle(bind):
|
||||
def verify(self):
|
||||
self.on = None
|
||||
self.off = None
|
||||
if isinstance(self.fields, dict):
|
||||
# verify on
|
||||
try:
|
||||
self.on = self.fields.pop('on')
|
||||
if not isinstance(self.on, (str, list)):
|
||||
self.err('on must be string or list')
|
||||
self.on = None
|
||||
except KeyError:
|
||||
self.err('requires on field')
|
||||
|
||||
# verify off
|
||||
try:
|
||||
self.off = self.fields.pop('off')
|
||||
if not isinstance(self.off, (str, list)):
|
||||
self.err('off must be string or list')
|
||||
self.off = None
|
||||
except KeyError:
|
||||
self.err('requires end field')
|
||||
|
||||
elif isinstance(self.fields, str):
|
||||
if not isinstance(self.fields, dict):
|
||||
self.fields = {'on': self.fields}
|
||||
|
||||
# verify on
|
||||
try:
|
||||
self.on = self.fields.pop('on')
|
||||
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
|
||||
|
||||
# verify off
|
||||
try:
|
||||
self.off = self.fields.pop('off')
|
||||
except KeyError:
|
||||
# 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'
|
||||
else:
|
||||
self.err('must be on and end, or argument of string')
|
||||
bindOrAlias = 'bind'
|
||||
toggleStr = f'toggle_{self.key}'
|
||||
onStr = f'{toggleStr}_on'
|
||||
offStr = f'{toggleStr}_off'
|
||||
|
||||
onObj = impulse(onStr, self.on)
|
||||
onObj.alias = True
|
||||
toggleOn = onObj.toTF2()[
|
||||
# remove trailing " and \n
|
||||
:-2
|
||||
]
|
||||
|
||||
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'
|
||||
|
||||
|
||||
class double(bind):
|
||||
|
@ -267,69 +328,142 @@ class double(bind):
|
|||
|
||||
def verify(self):
|
||||
self.primary = None
|
||||
self.primStr = f'{self.key}_primary'
|
||||
|
||||
self.secondary = None
|
||||
self.secondStr = f'{self.key}_secondary'
|
||||
|
||||
self.condition = None
|
||||
self.isToggle = False
|
||||
|
||||
# name of a bind type
|
||||
self.type = None
|
||||
|
||||
# either "released" (default) or "both"
|
||||
# either 'released' (default) or 'both'
|
||||
self.cancel = 'released'
|
||||
|
||||
# toggler
|
||||
try:
|
||||
self.condition = self.fields.pop('condition')
|
||||
if self.condition not in validKeyList:
|
||||
self.err(f'has invalid condition to toggle: "{self.condition}"')
|
||||
self.err(f'has invalid `condition` field: "{self.condition}"')
|
||||
except KeyError:
|
||||
self.err('requires condition to toggle')
|
||||
self.err('requires `condition` field')
|
||||
|
||||
# cancel mode
|
||||
try:
|
||||
self.cancel = self.fields.pop('cancel')
|
||||
if self.cancel not in ['released', 'both']:
|
||||
self.err(f'cancel must be either "released" or "both", not "{self.cancel}"')
|
||||
except KeyError:
|
||||
# maintain default
|
||||
pass
|
||||
if 'toggle' in self.fields:
|
||||
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}"')
|
||||
|
||||
# type
|
||||
try:
|
||||
self.type = self.fields.pop('type')
|
||||
if self.type not in bindTypes:
|
||||
if self.type not in [ type_.__name__ for type_ in self.bindTypes ]:
|
||||
# catastrophic: invalid type
|
||||
self.err(f'has invalid type: "{self.type}"')
|
||||
return
|
||||
except KeyError:
|
||||
# catastrophic: no type given
|
||||
self.err('requires type')
|
||||
self.err('requires `type` field')
|
||||
return
|
||||
|
||||
# cancel mode
|
||||
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}"')
|
||||
else:
|
||||
self.err(f'`cancel` field must be argument of "released" or "both"')
|
||||
|
||||
# primary action
|
||||
try:
|
||||
mainSection = self.fields.pop('primary')
|
||||
# mainSection.update({'alias': True})
|
||||
|
||||
mainAction = bind(f'{self.type} primary', mainSection)
|
||||
if mainAction.targetType is not None:
|
||||
mainAction = mainAction.targetType(mainAction.key, mainAction.fields)
|
||||
|
||||
self.errors.extend(mainAction.errors)
|
||||
mainAction = bind(f'{self.type} {self.primStr}', mainSection)
|
||||
self.primary = mainAction.toTargetType()
|
||||
except KeyError:
|
||||
self.err('requires primary action')
|
||||
self.err('requires `primary` field')
|
||||
|
||||
# secondary action
|
||||
try:
|
||||
altSection = self.fields.pop('secondary')
|
||||
# altSection.update({'alias': True})
|
||||
|
||||
altBind = bind(f'{self.type} secondary', altSection)
|
||||
if altBind.targetType is not None:
|
||||
altBind = altBind.targetType(altBind.key, altBind.fields)
|
||||
|
||||
self.errors.extend(altBind.errors)
|
||||
altBind = bind(f'{self.type} {self.secondStr}', altSection)
|
||||
self.secondary = altBind.toTargetType()
|
||||
except KeyError:
|
||||
self.err('requires secondary action')
|
||||
self.err('requires `secondary` field')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
if self.alias:
|
||||
bindOrAlias = 'alias'
|
||||
else:
|
||||
bindOrAlias = 'bind'
|
||||
|
||||
# Get code for primary and secondary actions
|
||||
self.primary.alias = True
|
||||
mainCode = self.primary.toTF2()
|
||||
self.secondary.alias = True
|
||||
altCode = self.secondary.toTF2()
|
||||
|
||||
# Make code to toggle between the two actions
|
||||
pShiftStr = f'+shift_{self.key}'
|
||||
mShiftStr = f'-shift_{self.key}'
|
||||
|
||||
if self.cancel == 'both':
|
||||
# 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,
|
||||
# you'd end up with each recursively calling the other
|
||||
|
||||
mainLines = mainCode.splitlines()
|
||||
mainMinusLine = mainLines[1]
|
||||
# second arg, without first quote
|
||||
mainMinusStr = mainMinusLine.split(' ', 2)[2][1:]
|
||||
|
||||
altLines = altCode.splitlines()
|
||||
altMinusLine = altLines[1]
|
||||
# same as above
|
||||
altMinusStr = altMinusLine.split(' ', 2)[2][1:]
|
||||
|
||||
mainLines[1] = mainLines[1][:-1] + f';{altMinusStr}'
|
||||
altLines[1] = altLines[1][:-1] + f';{mainMinusStr}'
|
||||
mainCode = '\n'.join(mainLines) + '\n'
|
||||
altCode = '\n'.join(altLines) + '\n'
|
||||
|
||||
if self.type == 'hold':
|
||||
self.primStr = '+' + self.primStr
|
||||
self.secondStr = '+' + self.secondStr
|
||||
|
||||
result = mainCode + altCode +\
|
||||
f'alias {pShiftStr} "{bindOrAlias} {self.key} {self.primStr}"\n' +\
|
||||
f'alias {mShiftStr} "{bindOrAlias} {self.key} {self.secondStr}"\n'+\
|
||||
f'{bindOrAlias} {self.key} "{self.primStr}"\n'
|
||||
|
||||
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']
|
||||
|
||||
if pShiftStr not in changes:
|
||||
# not already in changes
|
||||
changes.append(pShiftStr)
|
||||
restores.append(mShiftStr)
|
||||
|
||||
except KeyError:
|
||||
# If the condition key doesn't already exist, make it
|
||||
self.condDict.update( {
|
||||
self.condition: {
|
||||
'change_keys': [ pShiftStr ],
|
||||
'restore_keys': [ mShiftStr ],
|
||||
'alias': self.alias
|
||||
}
|
||||
} )
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class repeat(bind):
|
||||
|
@ -354,3 +488,12 @@ class repeat(bind):
|
|||
self.command = None
|
||||
except KeyError:
|
||||
self.err('requires command')
|
||||
|
||||
def toTF2(self) -> str:
|
||||
# commented-out placeholder
|
||||
return f'// repeat {self.key}\n'
|
||||
|
||||
# This is at the bottom because it has to happen after
|
||||
# all inheritances have been completed
|
||||
|
||||
bind.bindTypes = bind.__subclasses__()
|
|
@ -1,5 +1,5 @@
|
|||
"""Verify all the things that could go wrong."""
|
||||
from copy import deepcopy
|
||||
|
||||
from tfscript import tftypes
|
||||
|
||||
def verifyConfig(cfg: dict) -> (dict, dict):
|
||||
|
|
Loading…
Reference in New Issue