From c14aeb7af21c2c31c0b25a1a7efc82aeb3b0871b Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Fri, 3 May 2024 14:57:03 +0200 Subject: [PATCH] feat(bevy_components): fleshed out & refined hashmap UI & internals --- tools/bevy_components/components/maps.py | 67 +++++++++++++++---- tools/bevy_components/components/ui.py | 56 ++++++++++------ .../propGroups/conversions_from_prop_group.py | 2 - .../bevy_components/propGroups/process_map.py | 22 ++++-- tools/bevy_components/propGroups/utils.py | 3 - 5 files changed, 106 insertions(+), 44 deletions(-) diff --git a/tools/bevy_components/components/maps.py b/tools/bevy_components/components/maps.py index 4d665be..7d180ab 100644 --- a/tools/bevy_components/components/maps.py +++ b/tools/bevy_components/components/maps.py @@ -5,7 +5,7 @@ from bpy.props import (StringProperty, EnumProperty, PointerProperty, FloatVecto class GENERIC_MAP_OT_actions(Operator): """Move items up and down, add and remove""" bl_idname = "generic_map.map_action" - bl_label = "List Actions" + bl_label = "Map Actions" bl_description = "Move items up and down, add and remove" bl_options = {'REGISTER', 'UNDO'} @@ -26,6 +26,8 @@ class GENERIC_MAP_OT_actions(Operator): description="", ) # type: ignore + target_index: IntProperty(name="target index", description="index of item to manipulate")# type: ignore + def invoke(self, context, event): object = context.object # information is stored in component meta @@ -36,33 +38,74 @@ class GENERIC_MAP_OT_actions(Operator): for path_item in json.loads(self.property_group_path): propertyGroup = getattr(propertyGroup, path_item) - target_list = getattr(propertyGroup, "list") + keys_list = getattr(propertyGroup, "list") index = getattr(propertyGroup, "list_index") values_list = getattr(propertyGroup, "values_list") values_index = getattr(propertyGroup, "values_list_index") + key_setter = getattr(propertyGroup, "keys_setter") + value_setter = getattr(propertyGroup, "values_setter") + - if self.action == 'DOWN' and index < len(target_list) - 1: + if self.action == 'DOWN' and index < len(keys_list) - 1: #item_next = scn.rule_list[index + 1].name - target_list.move(index, index + 1) + keys_list.move(index, index + 1) propertyGroup.list_index += 1 elif self.action == 'UP' and index >= 1: #item_prev = scn.rule_list[index - 1].name - target_list.move(index, index - 1) + keys_list.move(index, index - 1) propertyGroup.list_index -= 1 elif self.action == 'REMOVE': - target_list.remove(index) - propertyGroup.list_index = min(max(0, index - 1), len(target_list) - 1) + index = self.target_index + keys_list.remove(index) + values_list.remove(index) + propertyGroup.list_index = min(max(0, index - 1), len(keys_list) - 1) + propertyGroup.values_index = min(max(0, index - 1), len(keys_list) - 1) if self.action == 'ADD': - key = target_list.add() - value = values_list.add() - - propertyGroup.list_index = index + 1 # we use this to force the change detection - propertyGroup.values_index = index + 1 # we use this to force the change detection + print("keys_list", keys_list) + hashmap = {} + for index, key in enumerate(keys_list): + key_entry = {} + for field_name in key.field_names: + print("field name", field_name, key) + key_entry[field_name] = key[field_name] + value_entry = {} + for field_name in values_list[index].field_names: + value_entry[field_name] = values_list[index][field_name] + + hashmap[json.dumps(key_entry)] = index #{"value": json.dumps(value_entry), "index": index} + print("hashmap", hashmap ) + + # we need to find the index of a specific value + key_entry = {} + for field_name in key_setter.field_names: + key_entry[field_name] = key_setter[field_name] + key_to_add = json.dumps(key_entry) + existing_index = hashmap.get(key_to_add, None) + print("existing_index", existing_index) + + if existing_index is None: + print("adding new value") + key = keys_list.add() + # copy the values over + for field_name in key_setter.field_names: + key[field_name] = key_setter[field_name] + + value = values_list.add() + # copy the values over + for field_name in value_setter.field_names: + value[field_name] = value_setter[field_name] + + propertyGroup.list_index = index + 1 # we use this to force the change detection + propertyGroup.values_index = index + 1 # we use this to force the change detection + else: + print("overriding value") + for field_name in value_setter.field_names: + values_list[existing_index][field_name] = value_setter[field_name] #info = '"%s" added to list' % (item.name) diff --git a/tools/bevy_components/components/ui.py b/tools/bevy_components/components/ui.py index ff6f0bf..8e943d6 100644 --- a/tools/bevy_components/components/ui.py +++ b/tools/bevy_components/components/ui.py @@ -85,55 +85,71 @@ def draw_propertyGroup( propertyGroup, layout, nesting =[], rootName=None): op.property_group_path = json.dumps(nesting) elif is_map: + root = layout.row().column() + """box = root.box() + box.label(text="test") + root.separator() + + box = root.box() + box.label(text="test2")""" + keys_list = getattr(propertyGroup, "list") values_list = getattr(propertyGroup, "values_list") - box = layout.box() + box = root.box() row = box.row() - row.label(text="key") - row.label(text="value") - #values_setter = getattr(propertyGroup, "values_setter") - # draw_propertyGroup(values_setter, row, nesting, rootName) + row.label(text="Add entry:") + keys_setter = getattr(propertyGroup, "keys_setter") + draw_propertyGroup(keys_setter, row, nesting, rootName) + values_setter = getattr(propertyGroup, "values_setter") + draw_propertyGroup(values_setter, row, nesting, rootName) + + op = row.operator('generic_map.map_action', icon='ADD', text="") + op.action = 'ADD' + op.component_name = rootName + op.property_group_path = json.dumps(nesting) + + box = root.box() split = box.split(factor=0.9) list_column, buttons_column = (split.column(),split.column()) list_column = list_column.box() for index, item in enumerate(keys_list): row = list_column.row() - #row.label(text=str(index)) draw_propertyGroup(item, row, nesting, rootName) value = values_list[index] draw_propertyGroup(value, row, nesting, rootName) + op = row.operator('generic_map.map_action', icon='REMOVE', text="") + op.action = 'REMOVE' + op.component_name = rootName + op.property_group_path = json.dumps(nesting) + op.target_index = index + #various control buttons buttons_column.separator() row = buttons_column.row() - op = row.operator('generic_map.map_action', icon='ADD', text="") - op.action = 'ADD' - op.component_name = rootName - op.property_group_path = json.dumps(nesting) - - row = buttons_column.row() - op = row.operator('generic_map.map_action', icon='REMOVE', text="") - op.action = 'REMOVE' - op.component_name = rootName - op.property_group_path = json.dumps(nesting) + else: for fname in field_names: - subrow = layout.row() + #subrow = layout.row() nestedPropertyGroup = getattr(propertyGroup, fname) nested = getattr(nestedPropertyGroup, "nested", False) display_name = fname if propertyGroup.tupple_or_struct == "struct" else "" if nested: layout.separator() + layout.separator() + layout.label(text=display_name) # this is the name of the field/sub field layout.separator() - draw_propertyGroup(nestedPropertyGroup, subrow.column(), nesting + [fname], rootName ) + subrow = layout.row() + draw_propertyGroup(nestedPropertyGroup, subrow, nesting + [fname], rootName ) else: + subrow = layout.row() subrow.prop(propertyGroup, fname, text=display_name) subrow.separator() @@ -226,8 +242,8 @@ class BEVY_COMPONENTS_PT_ComponentsPanel(bpy.types.Panel): # if the component has only 0 or 1 field names, display inline, otherwise change layout single_field = len(propertyGroup.field_names) < 2 prop_group_location = box.row(align=True).column() - if single_field: - prop_group_location = row.column(align=True)#.split(factor=0.9)#layout.row(align=False) + """if single_field: + prop_group_location = row.column(align=True)#.split(factor=0.9)#layout.row(align=False)""" if component_visible: if component_invalid: diff --git a/tools/bevy_components/propGroups/conversions_from_prop_group.py b/tools/bevy_components/propGroups/conversions_from_prop_group.py index 12402c9..74e0553 100644 --- a/tools/bevy_components/propGroups/conversions_from_prop_group.py +++ b/tools/bevy_components/propGroups/conversions_from_prop_group.py @@ -38,12 +38,10 @@ def property_group_value_to_custom_property_value(property_group, definition, re if is_value_type: value = conversion_tables[type_name](value) elif type_info == "Struct": - print("generating string for struct") values = {} if len(property_group.field_names) ==0: value = '()' else: - print("toto", type_def, definition, property_group) for index, field_name in enumerate(property_group.field_names): item_type_name = definition["properties"][field_name]["type"]["$ref"].replace("#/$defs/", "") item_definition = registry.type_infos[item_type_name] if item_type_name in registry.type_infos else None diff --git a/tools/bevy_components/propGroups/process_map.py b/tools/bevy_components/propGroups/process_map.py index dfb306a..16c2471 100644 --- a/tools/bevy_components/propGroups/process_map.py +++ b/tools/bevy_components/propGroups/process_map.py @@ -1,4 +1,5 @@ from bpy.props import (StringProperty, IntProperty, CollectionProperty) +from bpy.props import (PointerProperty) from .utils import generate_wrapper_propertyGroup from . import process_component @@ -13,11 +14,9 @@ def process_map(registry, definition, update, nesting=[], nesting_long_names=[]) nesting = nesting + [short_name] nesting_long_names = nesting_long_names + [long_name] - #ref_name = definition["items"]["type"]["$ref"].replace("#/$defs/", "") value_ref_name = definition["additionalProperties"]["type"]["$ref"].replace("#/$defs/", "") key_ref_name = long_name.split(',')[0].split('<')[1]# FIXME: hack !!! - key_type = '' - value_type = '' + print("infos", short_name, "long name", long_name) print("value ref", value_ref_name, "key ref", key_ref_name) #print("definition", definition) @@ -33,12 +32,14 @@ def process_map(registry, definition, update, nesting=[], nesting_long_names=[]) #if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName ! if is_value_value_type: - values_property_group_class = generate_wrapper_propertyGroup(long_name, original_type_name, definition_link, registry, update) + values_property_group_class = generate_wrapper_propertyGroup(f"{long_name}_keys", original_type_name, definition_link, registry, update) else: - (_, list_content_group_class) = process_component.process_component(registry, value_definition, update, {"nested": True, "type_name": original_type_name}, nesting) + (_, list_content_group_class) = process_component.process_component(registry, value_definition, update, {"nested": True, "type_name": original_type_name}, nesting, nesting_long_names) values_property_group_class = list_content_group_class values_collection = CollectionProperty(type=values_property_group_class) + values_property_group_pointer = PointerProperty(type=values_property_group_class) + if key_ref_name in type_infos: key_definition = type_infos[key_ref_name] @@ -49,18 +50,25 @@ def process_map(registry, definition, update, nesting=[], nesting_long_names=[]) #if the content of the list is a unit type, we need to generate a fake wrapper, otherwise we cannot use layout.prop(group, "propertyName") as there is no propertyName ! if is_key_value_type: - keys_property_group_class = generate_wrapper_propertyGroup(long_name+'_values', original_type_name, definition_link, registry, update) + keys_property_group_class = generate_wrapper_propertyGroup(f"{long_name}_values", original_type_name, definition_link, registry, update) else: - (_, list_content_group_class) = process_component.process_component(registry, key_definition, update, {"nested": True, "type_name": original_type_name}, nesting) + (_, list_content_group_class) = process_component.process_component(registry, key_definition, update, {"nested": True, "type_name": original_type_name}, nesting, nesting_long_names) keys_property_group_class = list_content_group_class keys_collection = CollectionProperty(type=keys_property_group_class) + keys_property_group_pointer = PointerProperty(type=keys_property_group_class) + __annotations__ = { "list": keys_collection, "list_index": IntProperty(name = "Index for keys", default = 0, update=update), + "keys_setter":keys_property_group_pointer, + "values_list": values_collection, "values_list_index": IntProperty(name = "Index for values", default = 0, update=update), + "values_setter":values_property_group_pointer, + "toto": {} + } ''' "values_setter": values_setter, diff --git a/tools/bevy_components/propGroups/utils.py b/tools/bevy_components/propGroups/utils.py index c4b770f..c96554e 100644 --- a/tools/bevy_components/propGroups/utils.py +++ b/tools/bevy_components/propGroups/utils.py @@ -14,10 +14,7 @@ def generate_wrapper_propertyGroup(wrapped_type_long_name_name, item_long_name, is_item_value_type = item_long_name in value_types_defaults wrapper_name = "wrapper_" + wrapped_type_long_name_name - print("WRAPPER NAME", wrapper_name) - - # FIXME: this is not a correct generic value for hashmaps !!? wrapper_definition = { "isComponent": False, "isResource": False,