Newer
Older
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2011, Ryan Inch
from copy import deepcopy
from bpy.types import (
Operator,
)
from bpy.props import (
BoolProperty,
StringProperty,
IntProperty
from . import internals
from .internals import (
update_property_group,
generate_state,
check_state,
get_move_selection,
get_move_active,
from .operator_utils import (
apply_to_children,
isolate_rto,
toggle_children,
activate_all_rtos,
invert_rtos,
copy_rtos,
swap_rtos,
clear_copy,
clear_swap,
link_child_collections_to_parent,
remove_collection,
select_collection_objects,
isolate_sel_objs_collections,
disable_sel_objs_collections,
class SetActiveCollection(Operator):
'''Set the active collection'''
bl_label = "Set Active Collection"
bl_idname = "view3d.set_active_collection"
bl_options = {'UNDO'}
is_master_collection: BoolProperty()
collection_name: StringProperty()
def execute(self, context):
layer_collection = context.view_layer.layer_collection
else:
laycol = internals.layer_collections[self.collection_name]
layer_collection = laycol["ptr"]
# set selection to this row
cm = context.scene.collection_manager
cm.cm_list_index = laycol["row_index"]
context.view_layer.active_layer_collection = layer_collection
if context.view_layer.active_layer_collection != layer_collection:
self.report({'WARNING'}, "Can't set excluded collection as active")
return {'FINISHED'}
class ExpandAllOperator(Operator):
'''Expand/Collapse all collections'''
bl_label = "Expand All Items"
bl_idname = "view3d.expand_all_items"
if len(internals.expanded) > 0:
internals.expanded.clear()
context.scene.collection_manager.cm_list_index = 0
for laycol in internals.layer_collections.values():
internals.expanded.add(laycol["name"])
internals.expand_history["target"] = ""
internals.expand_history["history"].clear()
# update tree view
update_property_group(context)
return {'FINISHED'}
class ExpandSublevelOperator(Operator):
bl_label = "Expand Sublevel Items"
bl_description = (
" * Ctrl+LMB - Expand/Collapse all sublevels\n"
" * Shift+LMB - Isolate tree/Restore\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.expand_sublevel"
expand: BoolProperty()
name: StringProperty()
index: IntProperty()
def invoke(self, context, event):
cls = ExpandSublevelOperator
modifiers = get_modifiers(event)
if modifiers == {"alt"}:
internals.expand_history["target"] = ""
internals.expand_history["history"].clear()
elif modifiers == {"ctrl"}:
# expand/collapse all subcollections
expand = None
# check whether to expand or collapse
if self.name in internals.expanded:
internals.expanded.remove(self.name)
internals.expanded.add(self.name)
def set_expanded(layer_collection):
if expand:
internals.expanded.add(layer_collection.name)
internals.expanded.discard(layer_collection.name)
apply_to_children(internals.layer_collections[self.name]["ptr"], set_expanded)
internals.expand_history["target"] = ""
internals.expand_history["history"].clear()
elif modifiers == {"shift"}:
def isolate_tree(current_laycol):
parent = current_laycol["parent"]
for laycol in parent["children"]:
if (laycol["name"] != current_laycol["name"]
and laycol["name"] in internals.expanded):
internals.expanded.remove(laycol["name"])
internals.expand_history["history"].append(laycol["name"])
if parent["parent"]:
isolate_tree(parent)
if self.name == internals.expand_history["target"]:
for item in internals.expand_history["history"]:
internals.expanded.add(item)
internals.expand_history["target"] = ""
internals.expand_history["history"].clear()
internals.expand_history["target"] = ""
internals.expand_history["history"].clear()
isolate_tree(internals.layer_collections[self.name])
internals.expand_history["target"] = self.name
else:
# expand/collapse collection
if self.expand:
internals.expanded.add(self.name)
internals.expanded.remove(self.name)
internals.expand_history["target"] = ""
internals.expand_history["history"].clear()
# set the selected row to the collection you're expanding/collapsing to
# preserve the tree view's scrolling
context.scene.collection_manager.cm_list_index = self.index
update_property_group(context)
return {'FINISHED'}
class CMSelectCollectionObjectsOperator(Operator):
bl_label = "Select All Objects in the Collection"
bl_description = (
" * LMB - Select all objects in collection.\n"
" * Shift+LMB - Add/Remove collection objects from selection.\n"
" * Ctrl+LMB - Isolate nested selection.\n"
" * Ctrl+Shift+LMB - Add/Remove nested from selection"
)
bl_idname = "view3d.select_collection_objects"
bl_options = {'REGISTER', 'UNDO'}
is_master_collection: BoolProperty()
collection_name: StringProperty()
def invoke(self, context, event):
modifiers = get_modifiers(event)
if modifiers == {"shift"}:
select_collection_objects(
is_master_collection=self.is_master_collection,
collection_name=self.collection_name,
replace=False,
nested=False
)
elif modifiers == {"ctrl"}:
select_collection_objects(
is_master_collection=self.is_master_collection,
collection_name=self.collection_name,
replace=True,
nested=True
)
elif modifiers == {"ctrl", "shift"}:
select_collection_objects(
is_master_collection=self.is_master_collection,
collection_name=self.collection_name,
replace=False,
nested=True
)
else:
select_collection_objects(
is_master_collection=self.is_master_collection,
collection_name=self.collection_name,
replace=True,
nested=False
)
return {'FINISHED'}
class SelectAllCumulativeObjectsOperator(Operator):
'''Select all objects that are present in more than one collection'''
bl_label = "Select All Cumulative Objects"
bl_idname = "view3d.select_all_cumulative_objects"
def execute(self, context):
selected_cumulative_objects = 0
total_cumulative_objects = 0
bpy.ops.object.select_all(action='DESELECT')
for obj in bpy.data.objects:
if len(obj.users_collection) > 1:
if obj.visible_get():
obj.select_set(True)
if obj.select_get() == True: # needed because obj.select_set can fail silently
selected_cumulative_objects +=1
total_cumulative_objects += 1
self.report({'INFO'}, f"{selected_cumulative_objects}/{total_cumulative_objects} Cumulative Objects Selected")
return {'FINISHED'}
class CMSendObjectsToCollectionOperator(Operator):
bl_label = "Send Objects to Collection"
" * LMB - Move objects to collection.\n"
" * Shift+LMB - Add/Remove objects from collection"
bl_idname = "view3d.send_objects_to_collection"
bl_options = {'REGISTER', 'UNDO'}
is_master_collection: BoolProperty()
collection_name: StringProperty()
def invoke(self, context, event):
target_collection = context.view_layer.layer_collection.collection
else:
laycol = internals.layer_collections[self.collection_name]
target_collection = laycol["ptr"].collection
selected_objects = get_move_selection()
active_object = get_move_active()
internals.move_triggered = True
if not selected_objects:
return {'CANCELLED'}
# add objects to collection
# make sure there is an active object
if not active_object:
active_object = tuple(selected_objects)[0]
if not active_object.name in target_collection.objects:
for obj in selected_objects:
if obj.name not in target_collection.objects:
target_collection.objects.link(obj)
warnings = False
master_warning = False
# remove from collections
for obj in selected_objects:
if obj.name in target_collection.objects:
# disallow removing if only one
if len(obj.users_collection) == 1:
warnings = True
master_laycol = context.view_layer.layer_collection
master_collection = master_laycol.collection
if obj.name not in master_collection.objects:
master_collection.objects.link(obj)
else:
master_warning = True
continue
# remove from collection
target_collection.objects.unlink(obj)
if warnings:
if master_warning:
send_report(
"Error removing 1 or more objects from the Scene Collection.\n"
"Objects would be left without a collection."
)
self.report({"WARNING"},
"Error removing 1 or more objects from the Scene Collection."
" Objects would be left without a collection."
)
else:
self.report({"INFO"}, "1 or more objects moved to Scene Collection.")
# move objects to collection
for obj in selected_objects:
if obj.name not in target_collection.objects:
target_collection.objects.link(obj)
# remove from all other collections
for collection in obj.users_collection:
if collection != target_collection:
collection.objects.unlink(obj)
# update the active object if needed
if not context.active_object:
try:
context.view_layer.objects.active = active_object
except RuntimeError: # object not in visible collection
pass
# update qcd header UI
return {'FINISHED'}
class CMExcludeOperator(Operator):
bl_label = "[EC] Exclude from View Layer"
bl_description = (
" * Shift+LMB - Isolate/Restore.\n"
" * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
" * Ctrl+LMB - Toggle nested.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.exclude_collection"
bl_options = {'REGISTER', 'UNDO'}
# static class var
isolated = False
def invoke(self, context, event):
cls = CMExcludeOperator
modifiers = get_modifiers(event)
view_layer = context.view_layer.name
orig_active_collection = context.view_layer.active_layer_collection
orig_active_object = context.view_layer.objects.active
laycol_ptr = internals.layer_collections[self.name]["ptr"]
if not view_layer in internals.rto_history["exclude"]:
internals.rto_history["exclude"][view_layer] = {"target": "", "history": []}
if modifiers == {"alt"}:
del internals.rto_history["exclude"][view_layer]
cls.isolated = False
elif modifiers == {"shift"}:
isolate_rto(cls, self, view_layer, "exclude")
elif modifiers == {"ctrl"}:
toggle_children(self, view_layer, "exclude")
cls.isolated = False
elif modifiers == {"ctrl", "shift"}:
isolate_rto(cls, self, view_layer, "exclude", children=True)
else:
# toggle exclusion
del internals.rto_history["exclude"][view_layer]
set_exclude_state(laycol_ptr, not laycol_ptr.exclude)
# restore active collection
context.view_layer.active_layer_collection = orig_active_collection
# restore active object if possible
if orig_active_object:
if orig_active_object.name in context.view_layer.objects:
context.view_layer.objects.active = orig_active_object
# reset exclude all history
if view_layer in internals.rto_history["exclude_all"]:
del internals.rto_history["exclude_all"][view_layer]
return {'FINISHED'}
class CMUnExcludeAllOperator(Operator):
bl_label = "[EC Global] Exclude from View Layer"
bl_description = (
" * LMB - Enable all/Restore.\n"
" * Shift+LMB - Invert.\n"
" * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
" * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
" * Ctrl+LMB - Copy/Paste RTOs.\n"
" * Ctrl+Alt+LMB - Swap RTOs.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.un_exclude_all_collections"
bl_options = {'REGISTER', 'UNDO'}
def invoke(self, context, event):
orig_active_collection = context.view_layer.active_layer_collection
orig_active_object = context.view_layer.objects.active
view_layer = context.view_layer.name
modifiers = get_modifiers(event)
if not view_layer in internals.rto_history["exclude_all"]:
internals.rto_history["exclude_all"][view_layer] = []
if modifiers == {"alt"}:
del internals.rto_history["exclude_all"][view_layer]
clear_copy("exclude")
clear_swap("exclude")
copy_rtos(view_layer, "exclude")
elif modifiers == {"ctrl", "alt"}:
swap_rtos(view_layer, "exclude")
invert_rtos(view_layer, "exclude")
elif modifiers == {"shift", "ctrl"}:
error = isolate_sel_objs_collections(view_layer, "exclude", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
elif modifiers == {"shift", "alt"}:
error = disable_sel_objs_collections(view_layer, "exclude", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
activate_all_rtos(view_layer, "exclude")
# restore active collection
context.view_layer.active_layer_collection = orig_active_collection
# restore active object if possible
if orig_active_object:
if orig_active_object.name in context.view_layer.objects:
context.view_layer.objects.active = orig_active_object
return {'FINISHED'}
class CMRestrictSelectOperator(Operator):
bl_label = "[SS] Disable Selection"
bl_description = (
" * Shift+LMB - Isolate/Restore.\n"
" * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
" * Ctrl+LMB - Toggle nested.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.restrict_select_collection"
bl_options = {'REGISTER', 'UNDO'}
# static class var
isolated = False
def invoke(self, context, event):
cls = CMRestrictSelectOperator
modifiers = get_modifiers(event)
view_layer = context.view_layer.name
laycol_ptr = internals.layer_collections[self.name]["ptr"]
if not view_layer in internals.rto_history["select"]:
internals.rto_history["select"][view_layer] = {"target": "", "history": []}
if modifiers == {"alt"}:
del internals.rto_history["select"][view_layer]
cls.isolated = False
elif modifiers == {"shift"}:
isolate_rto(cls, self, view_layer, "select")
elif modifiers == {"ctrl"}:
toggle_children(self, view_layer, "select")
cls.isolated = False
elif modifiers == {"ctrl", "shift"}:
isolate_rto(cls, self, view_layer, "select", children=True)
# toggle selectable
del internals.rto_history["select"][view_layer]
# toggle selectability of collection
laycol_ptr.collection.hide_select = not laycol_ptr.collection.hide_select
if view_layer in internals.rto_history["select_all"]:
del internals.rto_history["select_all"][view_layer]
return {'FINISHED'}
class CMUnRestrictSelectAllOperator(Operator):
bl_label = "[SS Global] Disable Selection"
bl_description = (
" * LMB - Enable all/Restore.\n"
" * Shift+LMB - Invert.\n"
" * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
" * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
" * Ctrl+LMB - Copy/Paste RTOs.\n"
" * Ctrl+Alt+LMB - Swap RTOs.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.un_restrict_select_all_collections"
bl_options = {'REGISTER', 'UNDO'}
def invoke(self, context, event):
view_layer = context.view_layer.name
modifiers = get_modifiers(event)
if not view_layer in internals.rto_history["select_all"]:
internals.rto_history["select_all"][view_layer] = []
if modifiers == {"alt"}:
del internals.rto_history["select_all"][view_layer]
clear_copy("select")
clear_swap("select")
copy_rtos(view_layer, "select")
elif modifiers == {"ctrl", "alt"}:
swap_rtos(view_layer, "select")
invert_rtos(view_layer, "select")
elif modifiers == {"shift", "ctrl"}:
error = isolate_sel_objs_collections(view_layer, "select", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
elif modifiers == {"shift", "alt"}:
error = disable_sel_objs_collections(view_layer, "select", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
activate_all_rtos(view_layer, "select")
return {'FINISHED'}
class CMHideOperator(Operator):
bl_label = "[VV] Hide in Viewport"
bl_description = (
" * Shift+LMB - Isolate/Restore.\n"
" * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
" * Ctrl+LMB - Toggle nested.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.hide_collection"
bl_options = {'REGISTER', 'UNDO'}
# static class var
isolated = False
def invoke(self, context, event):
modifiers = get_modifiers(event)
view_layer = context.view_layer.name
laycol_ptr = internals.layer_collections[self.name]["ptr"]
if not view_layer in internals.rto_history["hide"]:
internals.rto_history["hide"][view_layer] = {"target": "", "history": []}
if modifiers == {"alt"}:
del internals.rto_history["hide"][view_layer]
cls.isolated = False
elif modifiers == {"shift"}:
isolate_rto(cls, self, view_layer, "hide")
elif modifiers == {"ctrl"}:
toggle_children(self, view_layer, "hide")
cls.isolated = False
elif modifiers == {"ctrl", "shift"}:
isolate_rto(cls, self, view_layer, "hide", children=True)
del internals.rto_history["hide"][view_layer]
# toggle view of collection
laycol_ptr.hide_viewport = not laycol_ptr.hide_viewport
if view_layer in internals.rto_history["hide_all"]:
del internals.rto_history["hide_all"][view_layer]
return {'FINISHED'}
class CMUnHideAllOperator(Operator):
bl_label = "[VV Global] Hide in Viewport"
bl_description = (
" * LMB - Enable all/Restore.\n"
" * Shift+LMB - Invert.\n"
" * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
" * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
" * Ctrl+LMB - Copy/Paste RTOs.\n"
" * Ctrl+Alt+LMB - Swap RTOs.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.un_hide_all_collections"
bl_options = {'REGISTER', 'UNDO'}
def invoke(self, context, event):
view_layer = context.view_layer.name
modifiers = get_modifiers(event)
if not view_layer in internals.rto_history["hide_all"]:
internals.rto_history["hide_all"][view_layer] = []
if modifiers == {"alt"}:
del internals.rto_history["hide_all"][view_layer]
clear_copy("hide")
clear_swap("hide")
copy_rtos(view_layer, "hide")
elif modifiers == {"ctrl", "alt"}:
swap_rtos(view_layer, "hide")
invert_rtos(view_layer, "hide")
elif modifiers == {"shift", "ctrl"}:
error = isolate_sel_objs_collections(view_layer, "hide", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
elif modifiers == {"shift", "alt"}:
error = disable_sel_objs_collections(view_layer, "hide", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
activate_all_rtos(view_layer, "hide")
return {'FINISHED'}
class CMDisableViewportOperator(Operator):
bl_label = "[DV] Disable in Viewports"
bl_description = (
" * Shift+LMB - Isolate/Restore.\n"
" * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
" * Ctrl+LMB - Toggle nested.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.disable_viewport_collection"
bl_options = {'REGISTER', 'UNDO'}
# static class var
isolated = False
def invoke(self, context, event):
cls = CMDisableViewportOperator
modifiers = get_modifiers(event)
view_layer = context.view_layer.name
laycol_ptr = internals.layer_collections[self.name]["ptr"]
if not view_layer in internals.rto_history["disable"]:
internals.rto_history["disable"][view_layer] = {"target": "", "history": []}
if modifiers == {"alt"}:
del internals.rto_history["disable"][view_layer]
cls.isolated = False
elif modifiers == {"shift"}:
isolate_rto(cls, self, view_layer, "disable")
elif modifiers == {"ctrl"}:
toggle_children(self, view_layer, "disable")
cls.isolated = False
elif modifiers == {"ctrl", "shift"}:
isolate_rto(cls, self, view_layer, "disable", children=True)
del internals.rto_history["disable"][view_layer]
# toggle disable of collection in viewport
laycol_ptr.collection.hide_viewport = not laycol_ptr.collection.hide_viewport
# reset disable all history
if view_layer in internals.rto_history["disable_all"]:
del internals.rto_history["disable_all"][view_layer]
return {'FINISHED'}
class CMUnDisableViewportAllOperator(Operator):
bl_label = "[DV Global] Disable in Viewports"
bl_description = (
" * LMB - Enable all/Restore.\n"
" * Shift+LMB - Invert.\n"
" * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
" * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
" * Ctrl+LMB - Copy/Paste RTOs.\n"
" * Ctrl+Alt+LMB - Swap RTOs.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.un_disable_viewport_all_collections"
bl_options = {'REGISTER', 'UNDO'}
def invoke(self, context, event):
view_layer = context.view_layer.name
modifiers = get_modifiers(event)
if not view_layer in internals.rto_history["disable_all"]:
internals.rto_history["disable_all"][view_layer] = []
if modifiers == {"alt"}:
del internals.rto_history["disable_all"][view_layer]
clear_copy("disable")
clear_swap("disable")
copy_rtos(view_layer, "disable")
elif modifiers == {"ctrl", "alt"}:
swap_rtos(view_layer, "disable")
invert_rtos(view_layer, "disable")
elif modifiers == {"shift", "ctrl"}:
error = isolate_sel_objs_collections(view_layer, "disable", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
elif modifiers == {"shift", "alt"}:
error = disable_sel_objs_collections(view_layer, "disable", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
activate_all_rtos(view_layer, "disable")
return {'FINISHED'}
class CMDisableRenderOperator(Operator):
bl_label = "[RR] Disable in Renders"
bl_description = (
" * Shift+LMB - Isolate/Restore.\n"
" * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
" * Ctrl+LMB - Toggle nested.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.disable_render_collection"
bl_options = {'REGISTER', 'UNDO'}
# static class var
isolated = False
def invoke(self, context, event):
cls = CMDisableRenderOperator
modifiers = get_modifiers(event)
view_layer = context.view_layer.name
laycol_ptr = internals.layer_collections[self.name]["ptr"]
if not view_layer in internals.rto_history["render"]:
internals.rto_history["render"][view_layer] = {"target": "", "history": []}
if modifiers == {"alt"}:
del internals.rto_history["render"][view_layer]
cls.isolated = False
elif modifiers == {"shift"}:
isolate_rto(cls, self, view_layer, "render")
elif modifiers == {"ctrl"}:
toggle_children(self, view_layer, "render")
cls.isolated = False
elif modifiers == {"ctrl", "shift"}:
isolate_rto(cls, self, view_layer, "render", children=True)
# toggle renderable
del internals.rto_history["render"][view_layer]
# toggle renderability of collection
laycol_ptr.collection.hide_render = not laycol_ptr.collection.hide_render
if view_layer in internals.rto_history["render_all"]:
del internals.rto_history["render_all"][view_layer]
return {'FINISHED'}
class CMUnDisableRenderAllOperator(Operator):
bl_label = "[RR Global] Disable in Renders"
bl_description = (
" * LMB - Enable all/Restore.\n"
" * Shift+LMB - Invert.\n"
" * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
" * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
" * Ctrl+LMB - Copy/Paste RTOs.\n"
" * Ctrl+Alt+LMB - Swap RTOs.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.un_disable_render_all_collections"
bl_options = {'REGISTER', 'UNDO'}
def invoke(self, context, event):
view_layer = context.view_layer.name
modifiers = get_modifiers(event)
if not view_layer in internals.rto_history["render_all"]:
internals.rto_history["render_all"][view_layer] = []
if modifiers == {"alt"}:
del internals.rto_history["render_all"][view_layer]
clear_copy("render")
clear_swap("render")
copy_rtos(view_layer, "render")
elif modifiers == {"ctrl", "alt"}:
swap_rtos(view_layer, "render")
invert_rtos(view_layer, "render")
elif modifiers == {"shift", "ctrl"}:
error = isolate_sel_objs_collections(view_layer, "render", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
elif modifiers == {"shift", "alt"}:
error = disable_sel_objs_collections(view_layer, "render", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
activate_all_rtos(view_layer, "render")
return {'FINISHED'}
class CMHoldoutOperator(Operator):
bl_label = "[HH] Holdout"
bl_description = (
" * Shift+LMB - Isolate/Restore.\n"
" * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
" * Ctrl+LMB - Toggle nested.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.holdout_collection"
bl_options = {'REGISTER', 'UNDO'}
name: StringProperty()
# static class var
isolated = False
def invoke(self, context, event):
cls = CMHoldoutOperator
modifiers = get_modifiers(event)
view_layer = context.view_layer.name
laycol_ptr = internals.layer_collections[self.name]["ptr"]