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.
This commit is contained in:
parent
e24ff87b48
commit
88db9c67be
|
@ -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
|
||||
|
|
|
@ -104,6 +104,8 @@ classes = (
|
|||
operators.CMDisableObjectsOperator,
|
||||
operators.CMDisableUnSelectedObjectsOperator,
|
||||
operators.CMRestoreDisabledObjectsOperator,
|
||||
operators.CMUndoWrapper,
|
||||
operators.CMRedoWrapper,
|
||||
preferences.CMPreferences,
|
||||
ui.CM_UL_items,
|
||||
ui.CollectionManager,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue