update bme prop display
This commit is contained in:
parent
77b15a8797
commit
03441c642b
@ -7,23 +7,21 @@ from . import UTIL_functions, UTIL_icons_manager, UTIL_bme
|
||||
|
||||
_g_EnumHelper_BmeStructType: UTIL_bme.EnumPropHelper = UTIL_bme.EnumPropHelper()
|
||||
|
||||
class BBP_PG_bme_adder_params(bpy.types.PropertyGroup):
|
||||
class BBP_PG_bme_adder_cfgs(bpy.types.PropertyGroup):
|
||||
prop_int: bpy.props.IntProperty(
|
||||
name = 'Single Int', description = 'Single Int',
|
||||
min = 0, max = 64,
|
||||
soft_min = 0, soft_max = 32,
|
||||
step = 1,
|
||||
default = 1,
|
||||
)
|
||||
prop_float: bpy.props.FloatProperty(
|
||||
name = 'Single Float', description = 'Single Float',
|
||||
min = 0.0, max = 1024.0,
|
||||
soft_min = 0.0, soft_max = 64.0,
|
||||
step = 2.5,
|
||||
default = 5.0,
|
||||
)
|
||||
prop_str: bpy.props.StringProperty(
|
||||
name = 'Single Str', description = 'Single Str',
|
||||
default = ''
|
||||
)
|
||||
prop_bool: bpy.props.BoolProperty(
|
||||
name = 'Single Bool', description = 'Single Bool',
|
||||
default = True
|
||||
@ -31,30 +29,102 @@ class BBP_PG_bme_adder_params(bpy.types.PropertyGroup):
|
||||
|
||||
class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
"""Add BME Struct"""
|
||||
bl_idname = "bbp.dd_bme_struct"
|
||||
bl_idname = "bbp.add_bme_struct"
|
||||
bl_label = "Add BME Struct"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
## There is a compromise due to the shitty Blender design.
|
||||
#
|
||||
# The passed `self` of Blender Property update function is not the instance of operator,
|
||||
# but a simple OperatorProperties.
|
||||
# It mean that I can not visit the full operator, only what I can do is visit existing
|
||||
# Blender properties.
|
||||
#
|
||||
# So these is the solution about generating cache list according to the change of bme struct type.
|
||||
# First, update function will only set a "outdated" flag for operator which is a pre-registered Blender property.
|
||||
# The "outdated" flags is not showen and not saved.
|
||||
# Then call a internal cache list update function at the begin of `invoke` and `draw`.
|
||||
# In this internal cache list updator, check "outdated" flag first, if cache is outdated, update and reset flag.
|
||||
# Otherwise do nothing.
|
||||
#
|
||||
# Reference: https://docs.blender.org/api/current/bpy.props.html#update-example
|
||||
|
||||
## Compromise used "outdated" flag.
|
||||
outdated_flag: bpy.props.BoolProperty(
|
||||
name = "Outdated Type",
|
||||
description = "Internal flag.",
|
||||
options = {'HIDDEN', 'SKIP_SAVE'},
|
||||
default = False
|
||||
)
|
||||
|
||||
## A BME struct cfgs descriptor cache list
|
||||
# Not only the descriptor self, also the cfg associated index in bme_struct_cfgs
|
||||
bme_struct_cfg_index_cache: list[tuple[UTIL_bme.PrototypeShowcaseCfgDescriptor, int]]
|
||||
|
||||
def __internal_update_bme_struct_type(self) -> None:
|
||||
# if not outdated, skip
|
||||
if not self.outdated_flag: return
|
||||
|
||||
# get available cfg entires
|
||||
cfgs: typing.Iterator[UTIL_bme.PrototypeShowcaseCfgDescriptor]
|
||||
cfgs = _g_EnumHelper_BmeStructType.get_bme_showcase_cfgs(
|
||||
_g_EnumHelper_BmeStructType.get_selection(self.bme_struct_type)
|
||||
)
|
||||
|
||||
# analyse cfgs.
|
||||
# create counter first
|
||||
counter_int: int = 0
|
||||
counter_float: int = 0
|
||||
counter_bool: int = 0
|
||||
# create cache list
|
||||
self.bme_struct_cfg_index_cache.clear()
|
||||
# iterate cfgs and register them
|
||||
for cfg in cfgs:
|
||||
match(cfg.get_type()):
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
||||
self.bme_struct_cfg_index_cache.append((cfg, counter_int))
|
||||
counter_int += 1
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
||||
self.bme_struct_cfg_index_cache.append((cfg, counter_float))
|
||||
counter_float += 1
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
||||
self.bme_struct_cfg_index_cache.append((cfg, counter_bool))
|
||||
counter_bool += 1
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
||||
self.bme_struct_cfg_index_cache.append((cfg, counter_bool))
|
||||
counter_bool += 6 # face will occupy 6 bool.
|
||||
|
||||
|
||||
# init data collection
|
||||
# clear first
|
||||
self.bme_struct_cfgs.clear()
|
||||
# create enough entries specified by gotten cfgs
|
||||
for _ in range(max(counter_int, counter_float, counter_bool)):
|
||||
self.bme_struct_cfgs.add()
|
||||
|
||||
# assign default value
|
||||
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
||||
# show prop differently by cfg type
|
||||
match(cfg.get_type()):
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
||||
self.bme_struct_cfgs[cfg_index].prop_int = cfg.get_default()
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
||||
self.bme_struct_cfgs[cfg_index].prop_float = cfg.get_default()
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
||||
self.bme_struct_cfgs[cfg_index].prop_bool = cfg.get_default()
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
||||
# face is just 6 bool
|
||||
default_values: tuple[bool, ...] = cfg.get_default()
|
||||
for i in range(6):
|
||||
self.bme_struct_cfgs[cfg_index + i].prop_bool = default_values[i]
|
||||
|
||||
# reset outdated flag
|
||||
self.outdated_flag = False
|
||||
|
||||
# the updator for default side value
|
||||
def bme_struct_type_updated(self, context):
|
||||
# get floor prototype
|
||||
#floor_prototype = UTILS_constants.floor_blockDict[self.floor_type]
|
||||
|
||||
# try sync default value
|
||||
#default_sides = floor_prototype['DefaultSideConfig']
|
||||
#self.use_2d_top = default_sides['UseTwoDTop']
|
||||
#self.use_2d_right = default_sides['UseTwoDRight']
|
||||
#self.use_2d_bottom = default_sides['UseTwoDBottom']
|
||||
#self.use_2d_left = default_sides['UseTwoDLeft']
|
||||
#self.use_3d_top = default_sides['UseThreeDTop']
|
||||
#self.use_3d_bottom = default_sides['UseThreeDBottom']
|
||||
|
||||
# init data collection
|
||||
# todo: this state will add 32 items in each call.
|
||||
# please clear it or resize it.
|
||||
for i in range(32):
|
||||
item = self.data_floats.add()
|
||||
|
||||
# update outdated flag
|
||||
self.outdated_flag = True
|
||||
# blender required
|
||||
return None
|
||||
|
||||
@ -64,41 +134,73 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
items = _g_EnumHelper_BmeStructType.generate_items(),
|
||||
update = bme_struct_type_updated
|
||||
)
|
||||
|
||||
data_floats : bpy.props.CollectionProperty(
|
||||
name = "Floats",
|
||||
description = "Float collection.",
|
||||
type = BBP_PG_bme_adder_params,
|
||||
|
||||
bme_struct_cfgs : bpy.props.CollectionProperty(
|
||||
name = "Cfgs",
|
||||
description = "Cfg collection.",
|
||||
type = BBP_PG_bme_adder_cfgs,
|
||||
)
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
return PROP_preferences.get_raw_preferences().has_valid_blc_tex_folder()
|
||||
|
||||
|
||||
def invoke(self, context, event):
|
||||
# create internal list
|
||||
self.bme_struct_cfg_index_cache = []
|
||||
# trigger default bme struct type updator
|
||||
self.bme_struct_type_updated(context)
|
||||
# call internal updator
|
||||
self.__internal_update_bme_struct_type()
|
||||
# run execute() function
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
# todo: call general creator
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
# call internal updator
|
||||
self.__internal_update_bme_struct_type()
|
||||
|
||||
# start drawing
|
||||
layout = self.layout
|
||||
# show type
|
||||
layout.prop(self, 'bme_struct_type')
|
||||
# show type
|
||||
for i in self.data_floats:
|
||||
layout.prop(i, 'prop_bool')
|
||||
|
||||
# visit cfgs cache list to show cfg
|
||||
for (cfg, cfg_index) in self.bme_struct_cfg_index_cache:
|
||||
# draw title first
|
||||
layout.label(text = cfg.get_title())
|
||||
# show prop differently by cfg type
|
||||
match(cfg.get_type()):
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Integer:
|
||||
layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_int', text = '')
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Float:
|
||||
layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_float', text = '')
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Boolean:
|
||||
layout.prop(self.bme_struct_cfgs[cfg_index], 'prop_bool', text = '')
|
||||
case UTIL_bme.PrototypeShowcaseCfgsTypes.Face:
|
||||
# face will show a special layout (grid view)
|
||||
grids = layout.grid_flow(
|
||||
row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
|
||||
grids.alignment = 'CENTER'
|
||||
grids.separator()
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 0], 'prop_bool', text = 'Top') # top
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 2], 'prop_bool', text = 'Front') # front
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 4], 'prop_bool', text = 'Left') # left
|
||||
grids.label(text = '', icon = 'CUBE') # show a 3d cube as icon
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 5], 'prop_bool', text = 'Right') # right
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 3], 'prop_bool', text = 'Back') # back
|
||||
grids.prop(self.bme_struct_cfgs[cfg_index + 1], 'prop_bool', text = 'Bottom') # bottom
|
||||
grids.separator()
|
||||
|
||||
@classmethod
|
||||
def draw_blc_menu(self, layout: bpy.types.UILayout):
|
||||
for ident in _g_EnumHelper_BmeStructType.get_bme_identifiers():
|
||||
# draw operator
|
||||
cop = layout.operator(
|
||||
self.bl_idname,
|
||||
self.bl_idname,
|
||||
text = _g_EnumHelper_BmeStructType.get_bme_showcase_title(ident),
|
||||
icon_value = _g_EnumHelper_BmeStructType.get_bme_showcase_icon(ident)
|
||||
)
|
||||
@ -118,9 +220,9 @@ class BBP_OT_add_bme_struct(bpy.types.Operator):
|
||||
#endregion
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(BBP_PG_bme_adder_params)
|
||||
bpy.utils.register_class(BBP_PG_bme_adder_cfgs)
|
||||
bpy.utils.register_class(BBP_OT_add_bme_struct)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(BBP_OT_add_bme_struct)
|
||||
bpy.utils.unregister_class(BBP_PG_bme_adder_params)
|
||||
bpy.utils.unregister_class(BBP_PG_bme_adder_cfgs)
|
@ -11,7 +11,7 @@ from . import UTIL_functions, UTIL_icons_manager, UTIL_blender_mesh
|
||||
class PrototypeShowcaseCfgsTypes(enum.Enum):
|
||||
Integer = 'int'
|
||||
Float = 'float'
|
||||
String = 'str'
|
||||
Boolean = 'bool'
|
||||
Face = 'face'
|
||||
|
||||
class PrototypeShowcaseTypes(enum.Enum):
|
||||
@ -63,7 +63,7 @@ TOKEN_INSTANCES_TRANSFORM: str = 'transform'
|
||||
#region Prototype Loader
|
||||
|
||||
## The list storing BME prototype.
|
||||
_g_BMEPrototypes: list = []
|
||||
_g_BMEPrototypes: list[dict[str, typing.Any]] = []
|
||||
## The dict. Key is prototype identifier. value is the index of prototype in prototype list.
|
||||
_g_BMEPrototypeIndexMap: dict[str, int] = {}
|
||||
|
||||
@ -79,54 +79,8 @@ for walk_root, walk_dirs, walk_files in os.walk(os.path.join(os.path.dirname(__f
|
||||
# add into list
|
||||
_g_BMEPrototypes.append(proto)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Prototype EnumProp Visitor
|
||||
|
||||
class EnumPropHelper(UTIL_functions.EnumPropHelper):
|
||||
|
||||
def __init__(self):
|
||||
# init parent class
|
||||
UTIL_functions.EnumPropHelper.__init__(
|
||||
self,
|
||||
self.get_bme_identifiers(),
|
||||
lambda x: x,
|
||||
lambda x: x,
|
||||
lambda x: self.get_bme_showcase_title(x),
|
||||
lambda _: '',
|
||||
lambda x: self.get_bme_showcase_icon(x)
|
||||
)
|
||||
|
||||
def get_bme_identifiers(self) -> tuple[str, ...]:
|
||||
"""
|
||||
Get the identifier of prototype which need to be exposed to user.
|
||||
Template prototype is not included.
|
||||
"""
|
||||
return tuple(
|
||||
x[TOKEN_IDENTIFIER] # get identifier
|
||||
for x in filter(lambda x: x[TOKEN_SHOWCASE] is not None, _g_BMEPrototypes) # filter() to filter no showcase template.
|
||||
)
|
||||
|
||||
def get_bme_showcase_title(self, ident: str) -> str:
|
||||
"""
|
||||
Get BME display title by prototype identifier.
|
||||
"""
|
||||
# get prototype first
|
||||
proto: dict[str, typing.Any] = _g_BMEPrototypes[_g_BMEPrototypeIndexMap[ident]]
|
||||
# visit title field
|
||||
return proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_TITLE]
|
||||
|
||||
def get_bme_showcase_icon(self, ident: str) -> int:
|
||||
"""
|
||||
Get BME icon by prototype's identifier
|
||||
"""
|
||||
# get prototype specified icon name
|
||||
proto: dict[str, typing.Any] = _g_BMEPrototypes[_g_BMEPrototypeIndexMap[ident]]
|
||||
icon_name: str = proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_ICON]
|
||||
# get icon from icon manager
|
||||
cache: int | None = UTIL_icons_manager.get_bme_icon(icon_name)
|
||||
if cache is None: return UTIL_icons_manager.get_empty_icon()
|
||||
else: return cache
|
||||
def _get_prototype_by_identifier(ident: str) -> dict[str, typing.Any]:
|
||||
return _g_BMEPrototypes[_g_BMEPrototypeIndexMap[ident]]
|
||||
|
||||
#endregion
|
||||
|
||||
@ -181,6 +135,85 @@ def _eval_others(strl, str, params_vars_data: dict[str, typing.Any]) -> typing.A
|
||||
|
||||
#endregion
|
||||
|
||||
#region Prototype Helper
|
||||
|
||||
class PrototypeShowcaseCfgDescriptor():
|
||||
__mRawCfg: dict[str, str]
|
||||
|
||||
def __init__(self, raw_cfg: dict[str, str]):
|
||||
self.__mRawCfg = raw_cfg
|
||||
|
||||
def get_field(self) -> str:
|
||||
return self.__mRawCfg[TOKEN_SHOWCASE_CFGS_FIELD]
|
||||
|
||||
def get_type(self) -> PrototypeShowcaseCfgsTypes:
|
||||
return PrototypeShowcaseCfgsTypes(self.__mRawCfg[TOKEN_SHOWCASE_CFGS_TYPE])
|
||||
|
||||
def get_title(self) -> str:
|
||||
return self.__mRawCfg[TOKEN_SHOWCASE_CFGS_TITLE]
|
||||
|
||||
def get_desc(self) -> str:
|
||||
return self.__mRawCfg[TOKEN_SHOWCASE_CFGS_DESC]
|
||||
|
||||
def get_default(self) -> typing.Any:
|
||||
return _eval_showcase_cfgs_default(self.__mRawCfg[TOKEN_SHOWCASE_CFGS_DEFAULT])
|
||||
|
||||
class EnumPropHelper(UTIL_functions.EnumPropHelper):
|
||||
"""
|
||||
The BME specialized Blender EnumProperty helper.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# init parent class
|
||||
UTIL_functions.EnumPropHelper.__init__(
|
||||
self,
|
||||
self.get_bme_identifiers(),
|
||||
lambda x: x,
|
||||
lambda x: x,
|
||||
lambda x: self.get_bme_showcase_title(x),
|
||||
lambda _: '',
|
||||
lambda x: self.get_bme_showcase_icon(x)
|
||||
)
|
||||
|
||||
def get_bme_identifiers(self) -> tuple[str, ...]:
|
||||
"""
|
||||
Get the identifier of prototype which need to be exposed to user.
|
||||
Template prototype is not included.
|
||||
"""
|
||||
return tuple(
|
||||
x[TOKEN_IDENTIFIER] # get identifier
|
||||
for x in filter(lambda x: x[TOKEN_SHOWCASE] is not None, _g_BMEPrototypes) # filter() to filter no showcase template.
|
||||
)
|
||||
|
||||
def get_bme_showcase_title(self, ident: str) -> str:
|
||||
"""
|
||||
Get BME display title by prototype identifier.
|
||||
"""
|
||||
# get prototype first
|
||||
proto: dict[str, typing.Any] = _get_prototype_by_identifier(ident)
|
||||
# visit title field
|
||||
return proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_TITLE]
|
||||
|
||||
def get_bme_showcase_icon(self, ident: str) -> int:
|
||||
"""
|
||||
Get BME icon by prototype's identifier
|
||||
"""
|
||||
# get prototype specified icon name
|
||||
proto: dict[str, typing.Any] = _get_prototype_by_identifier(ident)
|
||||
icon_name: str = proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_ICON]
|
||||
# get icon from icon manager
|
||||
cache: int | None = UTIL_icons_manager.get_bme_icon(icon_name)
|
||||
if cache is None: return UTIL_icons_manager.get_empty_icon()
|
||||
else: return cache
|
||||
|
||||
def get_bme_showcase_cfgs(self, ident: str) -> typing.Iterator[PrototypeShowcaseCfgDescriptor]:
|
||||
# get prototype first
|
||||
proto: dict[str, typing.Any] = _get_prototype_by_identifier(ident)
|
||||
# use map to batch create descriptor
|
||||
return map(lambda x: PrototypeShowcaseCfgDescriptor(x), proto[TOKEN_SHOWCASE][TOKEN_SHOWCASE_CFGS])
|
||||
|
||||
#endregion
|
||||
|
||||
#region Core Creator
|
||||
|
||||
def get_bme_struct_cfgs():
|
||||
|
Loading…
Reference in New Issue
Block a user