diff options
author | Ryan Inch <mythologylover75@gmail.com> | 2021-03-16 23:57:56 +0300 |
---|---|---|
committer | Ryan Inch <mythologylover75@gmail.com> | 2021-03-17 00:03:51 +0300 |
commit | 88db9c67be6b851ca1a0edf9afab35531f5b1961 (patch) | |
tree | 271f2ec1d3ec0a4c60485396854fd24f33d4f5a8 | |
parent | e24ff87b48445467f673641504fde759da531148 (diff) |
Collection Manager: Add undo support. Task: T69577
Add undo and redo buttons to the CM popup.
These buttons use operators that wrap the internal undo/redo
operators with handling of CM internal data.
-rw-r--r-- | object_collection_manager/__init__.py | 2 | ||||
-rw-r--r-- | object_collection_manager/cm_init.py | 2 | ||||
-rw-r--r-- | object_collection_manager/internals.py | 74 | ||||
-rw-r--r-- | object_collection_manager/operators.py | 47 | ||||
-rw-r--r-- | object_collection_manager/ui.py | 77 |
5 files changed, 133 insertions, 69 deletions
diff --git a/object_collection_manager/__init__.py b/object_collection_manager/__init__.py index 392c8d73..246e6bd7 100644 --- a/object_collection_manager/__init__.py +++ b/object_collection_manager/__init__.py @@ -22,7 +22,7 @@ bl_info = { "name": "Collection Manager", "description": "Manage collections and their objects", "author": "Ryan Inch", - "version": (2, 19, 3), + "version": (2, 20, 0), "blender": (2, 80, 0), "location": "View3D - Object Mode (Shortcut - M)", "warning": '', # used for warning icon and text in addons panel diff --git a/object_collection_manager/cm_init.py b/object_collection_manager/cm_init.py index 9d0afea3..54d616ce 100644 --- a/object_collection_manager/cm_init.py +++ b/object_collection_manager/cm_init.py @@ -104,6 +104,8 @@ classes = ( operators.CMDisableObjectsOperator, operators.CMDisableUnSelectedObjectsOperator, operators.CMRestoreDisabledObjectsOperator, + operators.CMUndoWrapper, + operators.CMRedoWrapper, preferences.CMPreferences, ui.CM_UL_items, ui.CollectionManager, diff --git a/object_collection_manager/internals.py b/object_collection_manager/internals.py index 77e801f6..eb80ae40 100644 --- a/object_collection_manager/internals.py +++ b/object_collection_manager/internals.py @@ -621,6 +621,80 @@ def generate_state(*, qcd=False): return state +def check_state(context, *, cm_popup=False, phantom_mode=False, qcd=False): + view_layer = context.view_layer + + # check if expanded & history/buffer state still correct + if cm_popup and collection_state: + new_state = generate_state() + + if new_state["name"] != collection_state["name"]: + copy_buffer["RTO"] = "" + copy_buffer["values"].clear() + + swap_buffer["A"]["RTO"] = "" + swap_buffer["A"]["values"].clear() + swap_buffer["B"]["RTO"] = "" + swap_buffer["B"]["values"].clear() + + for name in list(expanded): + laycol = layer_collections.get(name) + if not laycol or not laycol["has_children"]: + expanded.remove(name) + + for name in list(expand_history["history"]): + laycol = layer_collections.get(name) + if not laycol or not laycol["has_children"]: + expand_history["history"].remove(name) + + for rto, history in rto_history.items(): + if view_layer.name in history: + del history[view_layer.name] + + + else: + for rto in ["exclude", "select", "hide", "disable", "render", "holdout", "indirect"]: + if new_state[rto] != collection_state[rto]: + if view_layer.name in rto_history[rto]: + del rto_history[rto][view_layer.name] + + if view_layer.name in rto_history[rto+"_all"]: + del rto_history[rto+"_all"][view_layer.name] + + + if phantom_mode: + cm = context.scene.collection_manager + + # check if in phantom mode and if it's still viable + if cm.in_phantom_mode: + if layer_collections.keys() != phantom_history["initial_state"].keys(): + cm.in_phantom_mode = False + + if view_layer.name != phantom_history["view_layer"]: + cm.in_phantom_mode = False + + if not cm.in_phantom_mode: + for key, value in phantom_history.items(): + try: + value.clear() + except AttributeError: + if key == "view_layer": + phantom_history["view_layer"] = "" + + + if qcd and qcd_collection_state: + from .qcd_operators import QCDAllBase + new_state = generate_state(qcd=True) + + if (new_state["name"] != qcd_collection_state["name"] + or new_state["exclude"] != qcd_collection_state["exclude"] + or new_state["qcd"] != qcd_collection_state["qcd"]): + if view_layer.name in qcd_history: + del qcd_history[view_layer.name] + qcd_collection_state.clear() + QCDAllBase.clear() + + def get_move_selection(*, names_only=False): global move_selection diff --git a/object_collection_manager/operators.py b/object_collection_manager/operators.py index 45dc4df5..b38e2fe6 100644 --- a/object_collection_manager/operators.py +++ b/object_collection_manager/operators.py @@ -38,6 +38,8 @@ from . import internals # For FUNCTIONS from .internals import ( update_property_group, + generate_state, + check_state, get_modifiers, get_move_selection, get_move_active, @@ -1499,3 +1501,48 @@ class CMRestoreDisabledObjectsOperator(Operator): obj.select_set(True) return {'FINISHED'} + + +class CMUndoWrapper(Operator): + bl_label = "Undo" + bl_description = "Undo previous action" + bl_idname = "view3d.undo_wrapper" + + @classmethod + def poll(self, context): + return bpy.ops.ed.undo.poll() + + def execute(self, context): + internals.collection_state.clear() + internals.collection_state.update(generate_state()) + bpy.ops.ed.undo() + update_property_group(context) + + check_state(context, cm_popup=True) + + # clear buffers + internals.copy_buffer["RTO"] = "" + internals.copy_buffer["values"].clear() + + internals.swap_buffer["A"]["RTO"] = "" + internals.swap_buffer["A"]["values"].clear() + internals.swap_buffer["B"]["RTO"] = "" + internals.swap_buffer["B"]["values"].clear() + + return {'FINISHED'} + + +class CMRedoWrapper(Operator): + bl_label = "Redo" + bl_description = "Redo previous action" + bl_idname = "view3d.redo_wrapper" + + @classmethod + def poll(self, context): + return bpy.ops.ed.redo.poll() + + def execute(self, context): + bpy.ops.ed.redo() + update_property_group(context) + + return {'FINISHED'} diff --git a/object_collection_manager/ui.py b/object_collection_manager/ui.py index b11a08bc..d5740e86 100644 --- a/object_collection_manager/ui.py +++ b/object_collection_manager/ui.py @@ -40,16 +40,12 @@ from .internals import ( update_collection_tree, update_property_group, generate_state, + check_state, get_move_selection, get_move_active, update_qcd_header, ) -from .qcd_operators import ( - QCDAllBase, -) - - preview_collections = {} last_icon_theme_text = None last_icon_theme_text_sel = None @@ -136,6 +132,11 @@ class CollectionManager(Operator): renum_sec.alignment = 'LEFT' renum_sec.operator("view3d.renumerate_qcd_slots") + undo_sec = op_sec.row(align=True) + undo_sec.alignment = 'LEFT' + undo_sec.operator("view3d.undo_wrapper", text="", icon='LOOP_BACK') + undo_sec.operator("view3d.redo_wrapper", text="", icon='LOOP_FORWARDS') + # menu & filter right_sec = button_row_1.row() right_sec.alignment = 'RIGHT' @@ -387,58 +388,8 @@ class CollectionManager(Operator): if cm.cm_list_index >= len(cm.cm_list_collection): cm.cm_list_index = -1 - # check if expanded & history/buffer state still correct - if internals.collection_state: - new_state = generate_state() - - if new_state["name"] != internals.collection_state["name"]: - internals.copy_buffer["RTO"] = "" - internals.copy_buffer["values"].clear() - - internals.swap_buffer["A"]["RTO"] = "" - internals.swap_buffer["A"]["values"].clear() - internals.swap_buffer["B"]["RTO"] = "" - internals.swap_buffer["B"]["values"].clear() - - for name in list(internals.expanded): - laycol = internals.layer_collections.get(name) - if not laycol or not laycol["has_children"]: - internals.expanded.remove(name) - - for name in list(internals.expand_history["history"]): - laycol = internals.layer_collections.get(name) - if not laycol or not laycol["has_children"]: - internals.expand_history["history"].remove(name) - - for rto, history in internals.rto_history.items(): - if view_layer.name in history: - del history[view_layer.name] - - - else: - for rto in ["exclude", "select", "hide", "disable", "render", "holdout", "indirect"]: - if new_state[rto] != internals.collection_state[rto]: - if view_layer.name in internals.rto_history[rto]: - del internals.rto_history[rto][view_layer.name] - - if view_layer.name in internals.rto_history[rto+"_all"]: - del internals.rto_history[rto+"_all"][view_layer.name] - - # check if in phantom mode and if it's still viable - if cm.in_phantom_mode: - if internals.layer_collections.keys() != internals.phantom_history["initial_state"].keys(): - cm.in_phantom_mode = False - - if view_layer.name != internals.phantom_history["view_layer"]: - cm.in_phantom_mode = False - - if not cm.in_phantom_mode: - for key, value in internals.phantom_history.items(): - try: - value.clear() - except AttributeError: - if key == "view_layer": - internals.phantom_history["view_layer"] = "" + # check if history/buffer/phantom state still correct + check_state(context, cm_popup=True, phantom_mode=True) # handle window sizing max_width = 960 @@ -930,17 +881,7 @@ def view3d_header_qcd_slots(self, context): layout = self.layout idx = 1 - if internals.qcd_collection_state: - view_layer = context.view_layer - new_state = generate_state(qcd=True) - - if (new_state["name"] != internals.qcd_collection_state["name"] - or new_state["exclude"] != internals.qcd_collection_state["exclude"] - or new_state["qcd"] != internals.qcd_collection_state["qcd"]): - if view_layer.name in internals.qcd_history: - del internals.qcd_history[view_layer.name] - internals.qcd_collection_state.clear() - QCDAllBase.clear() + check_state(context, qcd=True) main_row = layout.row(align=True) |