Refactor trait prop system and support different prop types
This commit is contained in:
parent
71ba39f653
commit
049911504c
|
@ -2493,19 +2493,16 @@ class ArmoryExporter:
|
|||
print('Armory Error: Scene "' + self.scene.name + '" - Object "' + bobject.name + '" : Referenced trait file "' + hxfile + '" not found')
|
||||
|
||||
x['class_name'] = trait_prefix + t.class_name_prop
|
||||
if len(t.arm_traitpropslist) > 0:
|
||||
|
||||
# Export trait properties
|
||||
if t.arm_traitpropslist:
|
||||
x['props'] = []
|
||||
for pt in t.arm_traitpropslist: # Append props
|
||||
prop = pt.name.replace(')', '').split('(')
|
||||
x['props'].append(prop[0])
|
||||
if(len(prop) > 1):
|
||||
if prop[1] == 'String':
|
||||
value = "'" + pt.value + "'"
|
||||
else:
|
||||
value = pt.value
|
||||
else:
|
||||
value = pt.value
|
||||
for trait_prop in t.arm_traitpropslist:
|
||||
x['props'].append(trait_prop.name)
|
||||
|
||||
value = trait_prop.get_value()
|
||||
x['props'].append(value)
|
||||
|
||||
o['traits'].append(x)
|
||||
|
||||
def export_canvas_themes(self):
|
||||
|
|
|
@ -1,35 +1,96 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
|
||||
class ArmTraitPropListItem(bpy.types.PropertyGroup):
|
||||
# Group of properties representing an item in the list
|
||||
name: StringProperty(
|
||||
name="Name",
|
||||
description="A name for this item",
|
||||
default="Untitled")
|
||||
PROP_TYPE_ICONS = {
|
||||
"String": "SORTALPHA",
|
||||
"Int": "CHECKBOX_DEHLT",
|
||||
"Float": "RADIOBUT_OFF",
|
||||
"Bool": "CHECKMARK",
|
||||
}
|
||||
|
||||
|
||||
class ArmTraitPropListItem(bpy.types.PropertyGroup):
|
||||
"""Group of properties representing an item in the list."""
|
||||
name: StringProperty(
|
||||
name="Name",
|
||||
description="The name of this property",
|
||||
default="Untitled")
|
||||
|
||||
type: EnumProperty(
|
||||
items=(
|
||||
# (Haxe Type, Display Name, Description)
|
||||
("String", "String", "String Type"),
|
||||
("Int", "Integer", "Integer Type"),
|
||||
("Float", "Float", "Float Type"),
|
||||
("Bool", "Boolean", "Boolean Type")),
|
||||
name="Type",
|
||||
description="The type of this property",
|
||||
default="String")
|
||||
|
||||
value_string: StringProperty(
|
||||
name="Value",
|
||||
description="The value of this property",
|
||||
default="")
|
||||
|
||||
value_int: IntProperty(
|
||||
name="Value",
|
||||
description="The value of this property",
|
||||
default=0)
|
||||
|
||||
value_float: FloatProperty(
|
||||
name="Value",
|
||||
description="The value of this property",
|
||||
default=0.0)
|
||||
|
||||
value_bool: BoolProperty(
|
||||
name="Value",
|
||||
description="The value of this property",
|
||||
default=False)
|
||||
|
||||
def set_value(self, val):
|
||||
if self.type == "Int":
|
||||
self.value_int = int(val)
|
||||
elif self.type == "Float":
|
||||
self.value_float = float(val)
|
||||
elif self.type == "Bool":
|
||||
self.value_bool = bool(val)
|
||||
else:
|
||||
self.value_string = str(val)
|
||||
|
||||
def get_value(self):
|
||||
if self.type == "Int":
|
||||
return self.value_int
|
||||
if self.type == "Float":
|
||||
return self.value_float
|
||||
if self.type == "Bool":
|
||||
return self.value_bool
|
||||
return self.value_string
|
||||
|
||||
value: StringProperty(
|
||||
name="Value",
|
||||
description="A name for this item",
|
||||
default="")
|
||||
|
||||
class ARM_UL_PropList(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
# We could write some code to decide which icon to use here...
|
||||
custom_icon = 'OBJECT_DATAMODE'
|
||||
item_value_ref = "value_" + item.type.lower()
|
||||
|
||||
sp = layout.split(factor=0.2)
|
||||
# Some properties don't support icons
|
||||
sp.label(text=item.type, icon=PROP_TYPE_ICONS[item.type])
|
||||
sp = sp.split(factor=0.6)
|
||||
sp.label(text=item.name)
|
||||
|
||||
# Make sure your code supports all 3 layout types
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
layout.prop(item, "value", text=item.name, emboss=False, icon=custom_icon)
|
||||
use_emboss = item.type == "Bool"
|
||||
sp.prop(item, item_value_ref, text="", emboss=use_emboss)
|
||||
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon=custom_icon)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(ArmTraitPropListItem)
|
||||
bpy.utils.register_class(ARM_UL_PropList)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(ArmTraitPropListItem)
|
||||
bpy.utils.unregister_class(ARM_UL_PropList)
|
||||
|
|
|
@ -238,9 +238,13 @@ def fetch_script_props(file):
|
|||
name = name.replace('/', '.')
|
||||
if '\\' in file:
|
||||
name = name.replace('\\', '.')
|
||||
|
||||
script_props[name] = []
|
||||
script_props_defaults[name] = []
|
||||
|
||||
lines = f.read().splitlines()
|
||||
|
||||
# Read next line
|
||||
read_prop = False
|
||||
for line in lines:
|
||||
if not read_prop:
|
||||
|
@ -249,33 +253,60 @@ def fetch_script_props(file):
|
|||
|
||||
if read_prop:
|
||||
if 'var ' in line:
|
||||
p = line.split('var ')[1]
|
||||
elif 'final ' in line:
|
||||
p = line.split('final ')[1]
|
||||
# Line of code
|
||||
code_ref = line.split('var ')[1].split(';')[0]
|
||||
else:
|
||||
break
|
||||
|
||||
valid_prop = False
|
||||
# Has type
|
||||
if ':' in p:
|
||||
# Fetch default value
|
||||
if '=' in p:
|
||||
s = p.split('=')
|
||||
ps = s[0].split(':')
|
||||
prop = (ps[0].strip(), ps[1].split(';')[0].strip())
|
||||
prop_value = s[1].split(';')[0].replace('\'', '').replace('"', '').strip()
|
||||
valid_prop = True
|
||||
|
||||
# Declaration = Assignment;
|
||||
var_sides = code_ref.split('=')
|
||||
# DeclarationName: DeclarationType
|
||||
decl_sides = var_sides[0].split(':')
|
||||
|
||||
prop_name = decl_sides[0].strip()
|
||||
|
||||
# If the prop type is annotated in the code
|
||||
# (= declaration has two parts)
|
||||
if len(decl_sides) > 1:
|
||||
prop_type = decl_sides[1].strip()
|
||||
|
||||
# Default value exists
|
||||
if len(var_sides) > 1 and var_sides[1].strip() != "":
|
||||
# Type is not supported
|
||||
if get_type_default_value(prop_type) is None:
|
||||
read_prop = False
|
||||
continue
|
||||
|
||||
prop_value = var_sides[1].replace('\'', '').replace('"', '').strip()
|
||||
|
||||
else:
|
||||
ps = p.split(':')
|
||||
prop = (ps[0].strip(), ps[1].split(';')[0].strip())
|
||||
prop_value = ''
|
||||
valid_prop = True
|
||||
# Fetch default value
|
||||
elif '=' in p:
|
||||
s = p.split('=')
|
||||
prop = (s[0].strip(), None)
|
||||
prop_value = s[1].split(';')[0].replace('\'', '').replace('"', '').strip()
|
||||
prop_value = get_type_default_value(prop_type)
|
||||
|
||||
# Type is not supported
|
||||
if prop_value is None:
|
||||
read_prop = False
|
||||
continue
|
||||
|
||||
valid_prop = True
|
||||
|
||||
# Default value exists
|
||||
elif len(var_sides) > 1 and var_sides[1].strip() != "":
|
||||
prop_value = var_sides[1].strip()
|
||||
prop_type = get_prop_type_from_value(prop_value)
|
||||
|
||||
# Type is not supported
|
||||
if prop_type is None:
|
||||
read_prop = False
|
||||
continue
|
||||
if prop_type == "String":
|
||||
prop_value = prop_value.replace('\'', '').replace('"', '')
|
||||
|
||||
valid_prop = True
|
||||
|
||||
prop = (prop_name, prop_type)
|
||||
|
||||
# Register prop
|
||||
if valid_prop:
|
||||
script_props[name].append(prop)
|
||||
|
@ -283,6 +314,45 @@ def fetch_script_props(file):
|
|||
|
||||
read_prop = False
|
||||
|
||||
def get_prop_type_from_value(value: str):
|
||||
"""
|
||||
Returns the property type based on its representation in the code.
|
||||
|
||||
If the type is not supported, `None` is returned.
|
||||
"""
|
||||
# Maybe ast.literal_eval() is better here?
|
||||
try:
|
||||
int(value)
|
||||
return "Int"
|
||||
except ValueError:
|
||||
try:
|
||||
float(value)
|
||||
return "Float"
|
||||
except ValueError:
|
||||
if value.startswith("\"") and value.endswith("\""):
|
||||
return "String"
|
||||
if value in ("true", "false"):
|
||||
return "Bool"
|
||||
|
||||
return None
|
||||
|
||||
def get_type_default_value(prop_type: str):
|
||||
"""
|
||||
Returns the default value of the given Haxe type.
|
||||
|
||||
If the type is not supported, `None` is returned:
|
||||
"""
|
||||
if prop_type == "Int":
|
||||
return 0
|
||||
if prop_type == "Float":
|
||||
return 0.0
|
||||
if prop_type == "String":
|
||||
return ""
|
||||
if prop_type == "Bool":
|
||||
return False
|
||||
|
||||
return None
|
||||
|
||||
def fetch_script_names():
|
||||
if bpy.data.filepath == "":
|
||||
return
|
||||
|
@ -341,36 +411,38 @@ def fetch_prop(o):
|
|||
continue
|
||||
props = script_props[name]
|
||||
defaults = script_props_defaults[name]
|
||||
|
||||
# Remove old props
|
||||
for i in range(len(item.arm_traitpropslist) - 1, -1, -1):
|
||||
ip = item.arm_traitpropslist[i]
|
||||
# if ip.name not in props:
|
||||
if ip.name.split('(')[0] not in [p[0] for p in props]:
|
||||
if ip.name not in [p[0] for p in props]:
|
||||
item.arm_traitpropslist.remove(i)
|
||||
# Add new props
|
||||
for i in range(0, len(props)):
|
||||
p = props[i]
|
||||
found = False
|
||||
for ip in item.arm_traitpropslist:
|
||||
if ip.name.replace(')', '').split('(')[0] == p[0]:
|
||||
found = ip
|
||||
break
|
||||
# Not in list
|
||||
if not found:
|
||||
prop = item.arm_traitpropslist.add()
|
||||
prop.name = p[0] + ('(' + p[1] + ')' if p[1] else '')
|
||||
prop.value = defaults[i]
|
||||
|
||||
if found:
|
||||
prop = item.arm_traitpropslist[found.name]
|
||||
f = found.name.replace(')', '').split('(')
|
||||
# Add new props
|
||||
for index, p in enumerate(props):
|
||||
found_prop = False
|
||||
for i_prop in item.arm_traitpropslist:
|
||||
if i_prop.name == p[0]:
|
||||
found_prop = i_prop
|
||||
break
|
||||
|
||||
# Not in list
|
||||
if not found_prop:
|
||||
prop = item.arm_traitpropslist.add()
|
||||
prop.name = p[0]
|
||||
prop.type = p[1]
|
||||
prop.set_value(defaults[index])
|
||||
|
||||
if found_prop:
|
||||
prop = item.arm_traitpropslist[found_prop.name]
|
||||
|
||||
# Default value added and current value is blank (no override)
|
||||
if (not found.value and defaults[i]):
|
||||
prop.value = defaults[i]
|
||||
if (not found_prop.get_value() and defaults[index]):
|
||||
prop.set_value(defaults[index])
|
||||
# Type has changed, update displayed name
|
||||
if (len(f) == 1 or (len(f) > 1 and f[1] != p[1])):
|
||||
prop.name = p[0] + ('(' + p[1] + ')' if p[1] else '')
|
||||
if (len(found_prop.name) == 1 or (len(found_prop.name) > 1 and found_prop.name[1] != p[1])):
|
||||
prop.name = p[0]
|
||||
prop.type = p[1]
|
||||
|
||||
def fetch_bundled_trait_props():
|
||||
# Bundled script props
|
||||
|
|
Loading…
Reference in a new issue