diff --git a/collection_manager/__init__.py b/collection_manager/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..75142a3543766c6f617954ceb9cc93049b38d068 --- /dev/null +++ b/collection_manager/__init__.py @@ -0,0 +1,121 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + + +""" Copyright 2011 GPL licence applies""" + +bl_info = { + "name": "Collection Manager", + "description": "Manage collections and their objects", + "author": "Ryan Inch", + "version": (1,8,0), + "blender": (2, 80, 0), + "location": "View3D - Object Mode (Shortcut - M)", + "warning": '', # used for warning icon and text in addons panel + "wiki_url": "", + "category": "User Interface"} + + +if "bpy" in locals(): + import importlib + + importlib.reload(internals) + importlib.reload(operators) + importlib.reload(ui) + +else: + from . import internals + from . import operators + from . import ui + +import bpy +from bpy.props import ( + CollectionProperty, + IntProperty, + BoolProperty, + ) + +addon_keymaps = [] + +classes = ( + internals.CMListCollection, + operators.ExpandAllOperator, + operators.ExpandSublevelOperator, + operators.CMExcludeOperator, + operators.CMUnExcludeAllOperator, + operators.CMRestrictSelectOperator, + operators.CMUnRestrictSelectAllOperator, + operators.CMHideOperator, + operators.CMUnHideAllOperator, + operators.CMDisableViewportOperator, + operators.CMUnDisableViewportAllOperator, + operators.CMDisableRenderOperator, + operators.CMUnDisableRenderAllOperator, + operators.CMNewCollectionOperator, + operators.CMRemoveCollectionOperator, + operators.CMSetCollectionOperator, + operators.CMPhantomModeOperator, + ui.CM_UL_items, + ui.CollectionManager, + ui.CMRestrictionTogglesPanel, + ) + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.Scene.CMListCollection = CollectionProperty(type=internals.CMListCollection) + bpy.types.Scene.CMListIndex = IntProperty(update=ui.update_selection) + + bpy.types.Scene.show_exclude = BoolProperty(default=True, name="Exclude from View Layer") + bpy.types.Scene.show_selectable = BoolProperty(default=True, name="Selectable") + bpy.types.Scene.show_hideviewport = BoolProperty(default=True, name="Hide in Viewport") + bpy.types.Scene.show_disableviewport = BoolProperty(default=False, name="Disable in Viewports") + bpy.types.Scene.show_render = BoolProperty(default=False, name="Disable in Renders") + + bpy.types.Scene.CM_Phantom_Mode = BoolProperty(default=False) + + + # create the global menu hotkey + wm = bpy.context.window_manager + km = wm.keyconfigs.addon.keymaps.new(name='Object Mode') + kmi = km.keymap_items.new('view3d.collection_manager', 'M', 'PRESS') + addon_keymaps.append((km, kmi)) + +def unregister(): + for cls in classes: + bpy.utils.unregister_class(cls) + + del bpy.types.Scene.CMListCollection + del bpy.types.Scene.CMListIndex + + del bpy.types.Scene.show_exclude + del bpy.types.Scene.show_selectable + del bpy.types.Scene.show_hideviewport + del bpy.types.Scene.show_disableviewport + del bpy.types.Scene.show_render + + del bpy.types.Scene.CM_Phantom_Mode + + # remove keymaps when add-on is deactivated + for km, kmi in addon_keymaps: + km.keymap_items.remove(kmi) + addon_keymaps.clear() + +if __name__ == "__main__": + register() diff --git a/collection_manager/internals.py b/collection_manager/internals.py new file mode 100644 index 0000000000000000000000000000000000000000..e898de1cefa7d4d0fdff705c125221f610bf4300 --- /dev/null +++ b/collection_manager/internals.py @@ -0,0 +1,107 @@ +from bpy.types import PropertyGroup +from bpy.props import StringProperty + +layer_collections = {} + +collection_tree = [] + +expanded = [] + +max_lvl = 0 +row_index = 0 + +def get_max_lvl(): + return max_lvl + +def update_col_name(self, context): + if self.name != self.last_name: + if self.name == '': + self.name = self.last_name + return + + if self.last_name != '': + layer_collections[self.last_name]["ptr"].collection.name = self.name + + update_property_group(context) + + self.last_name = self.name + +class CMListCollection(PropertyGroup): + name: StringProperty(update=update_col_name) + last_name: StringProperty() + + +def update_collection_tree(context): + global max_lvl + global row_index + collection_tree.clear() + layer_collections.clear() + max_lvl = 0 + row_index = 0 + + init_laycol_list = context.view_layer.layer_collection.children + + master_laycol = {"id": 0, + "name": context.view_layer.layer_collection.name, + "lvl": -1, + "row_index": -1, + "visible": True, + "has_children": True, + "expanded": True, + "parent": None, + "children": [], + "ptr": context.view_layer.layer_collection + } + + get_all_collections(context, init_laycol_list, master_laycol, collection_tree, visible=True) + + +def get_all_collections(context, collections, parent, tree, level=0, visible=False): + global row_index + + for item in collections: + laycol = {"id": len(layer_collections) +1, + "name": item.name, + "lvl": level, + "row_index": row_index, + "visible": visible, + "has_children": False, + "expanded": False, + "parent": parent, + "children": [], + "ptr": item + } + + row_index += 1 + + layer_collections[item.name] = laycol + tree.append(laycol) + + if len(item.children) > 0: + global max_lvl + max_lvl += 1 + laycol["has_children"] = True + + if item.name in expanded and laycol["visible"]: + laycol["expanded"] = True + get_all_collections(context, item.children, laycol, laycol["children"], level+1, visible=True) + + else: + get_all_collections(context, item.children, laycol, laycol["children"], level+1) + + +def update_property_group(context): + update_collection_tree(context) + context.scene.CMListCollection.clear() + create_property_group(context, collection_tree) + + +def create_property_group(context, tree): + global in_filter + + for laycol in tree: + new_cm_listitem = context.scene.CMListCollection.add() + new_cm_listitem.name = laycol["name"] + + if laycol["has_children"]: + create_property_group(context, laycol["children"]) diff --git a/collection_manager/operators.py b/collection_manager/operators.py new file mode 100644 index 0000000000000000000000000000000000000000..84396e7e25a8f0effc5044d6377c06d6894b3e12 --- /dev/null +++ b/collection_manager/operators.py @@ -0,0 +1,1210 @@ +import bpy +from bpy.types import Operator +from bpy.props import ( + BoolProperty, + StringProperty, + IntProperty + ) + +from .internals import * + +rto_history = {"exclude": {}, + "exclude_all": {}, + "select": {}, + "select_all": {}, + "hide": {}, + "hide_all": {}, + "disable": {}, + "disable_all": {}, + "render": {}, + "render_all": {} + } + +class ExpandAllOperator(bpy.types.Operator): + '''Expand/Collapse all collections''' + bl_label = "Expand All Items" + bl_idname = "view3d.expand_all_items" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + if len(expanded) > 0: + expanded.clear() + else: + for laycol in layer_collections.values(): + if laycol["ptr"].children: + expanded.append(laycol["name"]) + + # update tree view + update_property_group(context) + + return {'FINISHED'} + + +class ExpandSublevelOperator(bpy.types.Operator): + ''' * Shift-Click to expand/collapse all sublevels''' + bl_label = "Expand Sublevel Items" + bl_idname = "view3d.expand_sublevel" + bl_options = {'REGISTER', 'UNDO'} + + expand: BoolProperty() + name: StringProperty() + index: IntProperty() + + def invoke(self, context, event): + if event.shift: + # expand/collapse all subcollections + expand = None + + # check whether to expand or collapse + if self.name in expanded: + expanded.remove(self.name) + expand = False + else: + expanded.append(self.name) + expand = True + + # do expanding/collapsing + def loop(laycol): + for item in laycol.children: + if expand: + if not item.name in expanded: + expanded.append(item.name) + else: + if item.name in expanded: + expanded.remove(item.name) + + if len(item.children) > 0: + loop(item) + + loop(layer_collections[self.name]["ptr"]) + + else: + # expand/collapse collection + if self.expand: + expanded.append(self.name) + else: + expanded.remove(self.name) + + + # set selected row to the collection you're expanding/collapsing and update tree view + context.scene.CMListIndex = self.index + update_property_group(context) + + return {'FINISHED'} + + +class CMSetCollectionOperator(bpy.types.Operator): + ''' * Click to move object to collection.\n * Shift-Click to add/remove object from collection''' + bl_label = "Set Object Collection" + bl_idname = "view3d.set_collection" + bl_options = {'REGISTER', 'UNDO'} + + collection_index: IntProperty() + collection_name: StringProperty() + + def invoke(self, context, event): + collection = layer_collections[self.collection_name]["ptr"].collection + + if event.shift: + # add object to collection + + # check if in collection + in_collection = True + + for obj in context.selected_objects: + if obj.name not in collection.objects: + in_collection = False + + if not in_collection: + # add to collection + bpy.ops.object.link_to_collection(collection_index=self.collection_index) + + else: + # check and disallow removing from all collections + for obj in context.selected_objects: + if len(obj.users_collection) == 1: + send_report("Error removing 1 or more objects from this collection.\nObjects would be left without a collection") + + return {'FINISHED'} + + # remove from collection + bpy.ops.collection.objects_remove(collection=collection.name) + + else: + # move object to collection + bpy.ops.object.move_to_collection(collection_index=self.collection_index) + + return {'FINISHED'} + + +class CMExcludeOperator(bpy.types.Operator): + ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children''' + bl_label = "Exclude Collection from View Layer" + bl_idname = "view3d.exclude_collection" + bl_options = {'REGISTER', 'UNDO'} + + name: StringProperty() + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + laycol_ptr = layer_collections[self.name]["ptr"] + + if not view_layer in rto_history["exclude"]: + rto_history["exclude"][view_layer] = {"target": "", "history": []} + + rto_history["exclude"][view_layer]["target"] = self.name + exclude_history = rto_history["exclude"][view_layer]["history"] + + if event.shift: + # isolate/de-isolate exclusion of collections + + # get active layer collections + active_layer_collections = [x for x in layer_collections.values() \ + if x["ptr"].exclude == False] + + # check if collection isolated + if len(active_layer_collections) == 1 and active_layer_collections[0]["name"] == self.name: + if len(exclude_history) > 1: + # restore previous state + for x, item in enumerate(layer_collections.values()): + item["ptr"].exclude = exclude_history[x] + + else: + # enable all collections + for item in layer_collections.values(): + item["ptr"].exclude = False + + # reset exclude history + del rto_history["exclude"][view_layer] + + else: + # isolate collection + + # reset exclude history + exclude_history.clear() + + # save state + keep_history = -1 + for item in layer_collections.values(): + exclude_history.append(item["ptr"].exclude) + + if item["ptr"].exclude == False: + keep_history += 1 + + if not keep_history: + del rto_history["exclude"][view_layer] + + + # isolate collection + for item in layer_collections.values(): + if item["name"] != laycol_ptr.name: + item["ptr"].exclude = True + + laycol_ptr.exclude = False + + # exclude all children + laycol_iter_list = [laycol_ptr.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + layer_collection.exclude = True + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + + elif event.ctrl: + # toggle children + + # reset exclude history + del rto_history["exclude"][view_layer] + + # toggle exclusion of collection (this propagates to children) + laycol_ptr.exclude = not laycol_ptr.exclude + + else: + # toggle exclusion + + # reset exclude history + del rto_history["exclude"][view_layer] + + + # get current child exclusion state + child_exclusion = [] + + laycol_iter_list = [laycol_ptr.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + child_exclusion.append([layer_collection, layer_collection.exclude]) + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + + # toggle exclusion of collection + laycol_ptr.exclude = not laycol_ptr.exclude + + + # set correct state for all children + for laycol in child_exclusion: + laycol[0].exclude = laycol[1] + + + # reset exclude all history + if view_layer in rto_history["exclude_all"]: + del rto_history["exclude_all"][view_layer] + + return {'FINISHED'} + + +class CMUnExcludeAllOperator(bpy.types.Operator): + ''' * Click to toggle between current excluded state and all included.\n * Shift-Click to invert excluded status of all collections''' + bl_label = "Toggle Excluded Status Of All Collections" + bl_idname = "view3d.un_exclude_all_collections" + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + + if not view_layer in rto_history["exclude_all"]: + rto_history["exclude_all"][view_layer] = [] + + exclude_all_history = rto_history["exclude_all"][view_layer] + + if len(exclude_all_history) == 0: + exclude_all_history.clear() + keep_history = False + + if event.shift: + for item in layer_collections.values(): + keep_history = True + exclude_all_history.append(item["ptr"].exclude) + + for x, item in enumerate(layer_collections.values()): + item["ptr"].exclude = not exclude_all_history[x] + + print([not i for i in exclude_all_history]) + + else: + for item in reversed(list(layer_collections.values())): + if item["ptr"].exclude: + keep_history = True + + exclude_all_history.append(item["ptr"].exclude) + + item["ptr"].exclude = False + + exclude_all_history.reverse() + + if not keep_history: + del rto_history["exclude_all"][view_layer] + + else: + for x, item in enumerate(layer_collections.values()): + item["ptr"].exclude = exclude_all_history[x] + + del rto_history["exclude_all"][view_layer] + + return {'FINISHED'} + + +class CMRestrictSelectOperator(bpy.types.Operator): + ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children''' + bl_label = "Disable Selection of Collection" + bl_idname = "view3d.restrict_select_collection" + bl_options = {'REGISTER', 'UNDO'} + + name: StringProperty() + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + laycol_ptr = layer_collections[self.name]["ptr"] + + if not view_layer in rto_history["select"]: + rto_history["select"][view_layer] = {"target": "", "history": []} + + rto_history["select"][view_layer]["target"] = self.name + select_history = rto_history["select"][view_layer]["history"] + + if event.shift: + # isolate/de-isolate selectability of collections + + # get active collections + active_layer_collections = [x for x in layer_collections.values() \ + if x["ptr"].collection.hide_select == False] + + layerchain = [] + laycol = layer_collections[self.name] + + # get chain of parents up to top level collection + while laycol["id"] != 0: + layerchain.append(laycol) + laycol = laycol["parent"] + + # check if reversed layerchain matches active collections + if layerchain[::-1] == active_layer_collections: + if len(select_history) > 1: + # restore previous state + for x, item in enumerate(layer_collections.values()): + item["ptr"].collection.hide_select = select_history[x] + + else: + # make all collections selectable + for item in layer_collections.values(): + item["ptr"].collection.hide_select = False + + # reset select history + del rto_history["select"][view_layer] + + else: + # reset select history + select_history.clear() + + # save state + keep_history = -1 + for item in layer_collections.values(): + select_history.append(item["ptr"].collection.hide_select) + + if item["ptr"].collection.hide_select == False: + keep_history += 1 + + if not keep_history: + del rto_history["select"][view_layer] + + # make all collections unselectable + for item in layer_collections.values(): + item["ptr"].collection.hide_select = True + + # allow selection of active collection plus parents + laycol_ptr.collection.hide_select = False + + laycol = layer_collections[self.name] + while laycol["id"] != 0: + laycol["ptr"].collection.hide_select = False + laycol = laycol["parent"] + + + elif event.ctrl: + # toggle children + + # reset select history + del rto_history["select"][view_layer] + + # toggle selectability of collection + state = not laycol_ptr.collection.hide_select + laycol_ptr.collection.hide_select = state + + # pass state to children + laycol_iter_list = [laycol_ptr.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + layer_collection.collection.hide_select = state + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + else: + # reset select history + del rto_history["select"][view_layer] + + # toggle selectability of collection + laycol_ptr.collection.hide_select = not laycol_ptr.collection.hide_select + + + # reset select all history + if view_layer in rto_history["select_all"]: + del rto_history["select_all"][view_layer] + + return {'FINISHED'} + + +class CMUnRestrictSelectAllOperator(bpy.types.Operator): + ''' * Click to toggle between current selectable state and all selectable.\n * Shift-Click to invert selectable status of all collections''' + bl_label = "Toggle Selectable Status Of All Collections" + bl_idname = "view3d.un_restrict_select_all_collections" + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + + if not view_layer in rto_history["select_all"]: + rto_history["select_all"][view_layer] = [] + + select_all_history = rto_history["select_all"][view_layer] + + if len(select_all_history) == 0: + select_all_history.clear() + keep_history = False + + for item in layer_collections.values(): + if event.shift: + keep_history = True + select_all_history.append(item["ptr"].collection.hide_select) + item["ptr"].collection.hide_select = not item["ptr"].collection.hide_select + + else: + if item["ptr"].collection.hide_select: + keep_history = True + + select_all_history.append(item["ptr"].collection.hide_select) + item["ptr"].collection.hide_select = False + + if not keep_history: + del rto_history["select_all"][view_layer] + + else: + for x, item in enumerate(layer_collections.values()): + item["ptr"].collection.hide_select = select_all_history[x] + + del rto_history["select_all"][view_layer] + + return {'FINISHED'} + + +class CMHideOperator(bpy.types.Operator): + ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children''' + bl_label = "Hide Collection" + bl_idname = "view3d.hide_collection" + bl_options = {'REGISTER', 'UNDO'} + + name: StringProperty() + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + laycol_ptr = layer_collections[self.name]["ptr"] + + if not view_layer in rto_history["hide"]: + rto_history["hide"][view_layer] = {"target": "", "history": []} + + rto_history["hide"][view_layer]["target"] = self.name + hide_history = rto_history["hide"][view_layer]["history"] + + if event.shift: + # isolate/de-isolate view of collections + + # get active collections + active_layer_collections = [x for x in layer_collections.values() \ + if x["ptr"].hide_viewport == False] + + layerchain = [] + laycol = layer_collections[self.name] + + # get chain of parents up to top level collection + while laycol["id"] != 0: + layerchain.append(laycol) + laycol = laycol["parent"] + + # check if reversed layerchain matches active collections + if layerchain[::-1] == active_layer_collections: + if len(hide_history) > 1: + # restore previous state + for x, item in enumerate(layer_collections.values()): + item["ptr"].hide_viewport = hide_history[x] + + else: + # show all collections + for laycol in layer_collections.values(): + laycol["ptr"].hide_viewport = False + + # reset hide history + del rto_history["hide"][view_layer] + + else: + # reset hide history + hide_history.clear() + + # save state + keep_history = -1 + for item in layer_collections.values(): + hide_history.append(item["ptr"].hide_viewport) + + if item["ptr"].hide_viewport == False: + keep_history += 1 + + if not keep_history: + del rto_history["hide"][view_layer] + + # hide all collections + for laycol in layer_collections.values(): + laycol["ptr"].hide_viewport = True + + # show active collection plus parents + laycol_ptr.hide_viewport = False + + laycol = layer_collections[self.name] + while laycol["id"] != 0: + laycol["ptr"].hide_viewport = False + laycol = laycol["parent"] + + elif event.ctrl: + # toggle children + + # reset hide history + del rto_history["hide"][view_layer] + + # toggle view of collection + state = not laycol_ptr.hide_viewport + laycol_ptr.hide_viewport = state + + # pass state to children + laycol_iter_list = [laycol_ptr.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + layer_collection.hide_viewport = state + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + else: + # reset hide history + del rto_history["hide"][view_layer] + + # toggle view of collection + laycol_ptr.hide_viewport = not laycol_ptr.hide_viewport + + + # reset hide all history + if view_layer in rto_history["hide_all"]: + del rto_history["hide_all"][view_layer] + + return {'FINISHED'} + + +class CMUnHideAllOperator(bpy.types.Operator): + ''' * Click to toggle between current visibility state and all visible.\n * Shift-Click to invert visibility status of all collections''' + bl_label = "Toggle Hidden Status Of All Collections" + bl_idname = "view3d.un_hide_all_collections" + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + + if not view_layer in rto_history["hide_all"]: + rto_history["hide_all"][view_layer] = [] + + hide_all_history = rto_history["hide_all"][view_layer] + + if len(hide_all_history) == 0: + hide_all_history.clear() + keep_history = False + + for item in layer_collections.values(): + if event.shift: + keep_history = True + hide_all_history.append(item["ptr"].hide_viewport) + item["ptr"].hide_viewport = not item["ptr"].hide_viewport + + else: + if item["ptr"].hide_viewport: + keep_history = True + + hide_all_history.append(item["ptr"].hide_viewport) + item["ptr"].hide_viewport = False + + if not keep_history: + del rto_history["hide_all"][view_layer] + + else: + for x, item in enumerate(layer_collections.values()): + item["ptr"].hide_viewport = hide_all_history[x] + + del rto_history["hide_all"][view_layer] + + return {'FINISHED'} + + +class CMDisableViewportOperator(bpy.types.Operator): + ''' * Shift-Click to isolate/restore previous state\n * Ctrl-Click to toggle children''' + bl_label = "Disable Collection in Viewport" + bl_idname = "view3d.disable_viewport_collection" + bl_options = {'REGISTER', 'UNDO'} + + name: StringProperty() + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + laycol_ptr = layer_collections[self.name]["ptr"] + + if not view_layer in rto_history["disable"]: + rto_history["disable"][view_layer] = {"target": "", "history": []} + + rto_history["disable"][view_layer]["target"] = self.name + disable_history = rto_history["disable"][view_layer]["history"] + + if event.shift: + # isolate/de-isolate disablement of collections in viewport + + # get active collections + active_layer_collections = [x for x in layer_collections.values() \ + if x["ptr"].collection.hide_viewport == False] + + layerchain = [] + laycol = layer_collections[self.name] + + # get chain of parents up to top level collection + while laycol["id"] != 0: + layerchain.append(laycol) + laycol = laycol["parent"] + + # check if reversed layerchain matches active collections + if layerchain[::-1] == active_layer_collections: + if len(disable_history) > 1: + # restore previous state + for x, item in enumerate(layer_collections.values()): + item["ptr"].collection.hide_viewport = disable_history[x] + + else: + # enable all collections in viewport + for laycol in layer_collections.values(): + laycol["ptr"].collection.hide_viewport = False + + # reset disable history + del rto_history["disable"][view_layer] + + else: + # reset disable history + disable_history.clear() + + # save state + keep_history = -1 + for item in layer_collections.values(): + disable_history.append(item["ptr"].collection.hide_viewport) + + if item["ptr"].collection.hide_viewport == False: + keep_history += 1 + + if not keep_history: + del rto_history["disable"][view_layer] + + # disable all collections in viewport + for laycol in layer_collections.values(): + laycol["ptr"].collection.hide_viewport = True + + # enable active collection plus parents in viewport + laycol_ptr.collection.hide_viewport = False + + laycol = layer_collections[self.name] + while laycol["id"] != 0: + laycol["ptr"].collection.hide_viewport = False + laycol = laycol["parent"] + + elif event.ctrl: + # toggle children + + # reset disable history + del rto_history["disable"][view_layer] + + # toggle view of collection + state = not laycol_ptr.collection.hide_viewport + laycol_ptr.collection.hide_viewport = state + + # pass state to children + laycol_iter_list = [laycol_ptr.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + layer_collection.collection.hide_viewport = state + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + else: + # reset disable history + del 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 rto_history["disable_all"]: + del rto_history["disable_all"][view_layer] + + return {'FINISHED'} + + +class CMUnDisableViewportAllOperator(bpy.types.Operator): + ''' * Click to toggle between current viewport display and all enabled.\n * Shift-Click to invert viewport display of all collections''' + bl_label = "Toggle Viewport Display of All Collections" + bl_idname = "view3d.un_disable_viewport_all_collections" + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + + if not view_layer in rto_history["disable_all"]: + rto_history["disable_all"][view_layer] = [] + + disable_all_history = rto_history["disable_all"][view_layer] + + if len(disable_all_history) == 0: + disable_all_history.clear() + keep_history = False + + for item in layer_collections.values(): + if event.shift: + keep_history = True + disable_all_history.append(item["ptr"].collection.hide_viewport) + item["ptr"].collection.hide_viewport = not \ + item["ptr"].collection.hide_viewport + + else: + if item["ptr"].collection.hide_viewport: + keep_history = True + + disable_all_history.append(item["ptr"].collection.hide_viewport) + item["ptr"].collection.hide_viewport = False + + if not keep_history: + del rto_history["disable_all"][view_layer] + + else: + for x, item in enumerate(layer_collections.values()): + item["ptr"].collection.hide_viewport = disable_all_history[x] + + del rto_history["disable_all"][view_layer] + + return {'FINISHED'} + + +class CMDisableRenderOperator(bpy.types.Operator): + ''' * Shift-Click to isolate/restore previous state\n * Shift-Click to invert viewport display of all collections''' + bl_label = "Disable Collection in Render" + bl_idname = "view3d.disable_render_collection" + bl_options = {'REGISTER', 'UNDO'} + + name: StringProperty() + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + laycol_ptr = layer_collections[self.name]["ptr"] + + if not view_layer in rto_history["render"]: + rto_history["render"][view_layer] = {"target": "", "history": []} + + rto_history["render"][view_layer]["target"] = self.name + render_history = rto_history["render"][view_layer]["history"] + + if event.shift: + # isolate/de-isolate render of collections + + # get active collections + active_layer_collections = [x for x in layer_collections.values() \ + if x["ptr"].collection.hide_render == False] + + layerchain = [] + laycol = layer_collections[self.name] + + # get chain of parents up to top level collection + while laycol["id"] != 0: + layerchain.append(laycol) + laycol = laycol["parent"] + + # check if reversed layerchain matches active collections + if layerchain[::-1] == active_layer_collections: + if len(render_history) > 1: + # restore previous state + for x, item in enumerate(layer_collections.values()): + item["ptr"].collection.hide_render = render_history[x] + + else: + # allow render of all collections + for laycol in layer_collections.values(): + laycol["ptr"].collection.hide_render = False + + # reset render history + del rto_history["render"][view_layer] + + else: + # reset render history + render_history.clear() + + # save state + keep_history = -1 + for item in layer_collections.values(): + render_history.append(item["ptr"].collection.hide_render) + + if item["ptr"].collection.hide_render == False: + keep_history += 1 + + if not keep_history: + del rto_history["render"][view_layer] + + # disallow render of all collections + for laycol in layer_collections.values(): + laycol["ptr"].collection.hide_render = True + + # allow render of active collection plus parents + laycol_ptr.collection.hide_render = False + + laycol = layer_collections[self.name] + while laycol["id"] != 0: + laycol["ptr"].collection.hide_render = False + laycol = laycol["parent"] + + elif event.ctrl: + # toggle children + + # reset render history + del rto_history["render"][view_layer] + + # toggle view of collection + state = not laycol_ptr.collection.hide_render + laycol_ptr.collection.hide_render = state + + # pass state to children + laycol_iter_list = [laycol_ptr.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + layer_collection.collection.hide_render = state + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + else: + # reset render history + del rto_history["render"][view_layer] + + # toggle renderability of collection + laycol_ptr.collection.hide_render = not laycol_ptr.collection.hide_render + + + # reset render all history + if view_layer in rto_history["render_all"]: + del rto_history["render_all"][view_layer] + + return {'FINISHED'} + + +class CMUnDisableRenderAllOperator(bpy.types.Operator): + ''' * Click to toggle between current render status and all rendered.\n * Shift-Click to invert render status of all collections''' + bl_label = "Toggle Render Status of All Collections" + bl_idname = "view3d.un_disable_render_all_collections" + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + global rto_history + + view_layer = context.view_layer.name + + if not view_layer in rto_history["render_all"]: + rto_history["render_all"][view_layer] = [] + + render_all_history = rto_history["render_all"][view_layer] + + if len(render_all_history) == 0: + render_all_history.clear() + keep_history = False + + for item in layer_collections.values(): + if event.shift: + keep_history = True + render_all_history.append(item["ptr"].collection.hide_render) + item["ptr"].collection.hide_render = not \ + item["ptr"].collection.hide_render + + else: + if item["ptr"].collection.hide_render: + keep_history = True + + render_all_history.append(item["ptr"].collection.hide_render) + item["ptr"].collection.hide_render = False + + if not keep_history: + del rto_history["render_all"][view_layer] + + else: + for x, item in enumerate(layer_collections.values()): + item["ptr"].collection.hide_render = render_all_history[x] + + del rto_history["render_all"][view_layer] + + return {'FINISHED'} + + +class CMRemoveCollectionOperator(bpy.types.Operator): + '''Remove Collection''' + bl_label = "Remove Collection" + bl_idname = "view3d.remove_collection" + bl_options = {'UNDO'} + + collection_name: StringProperty() + + def execute(self, context): + global rto_history + + laycol = layer_collections[self.collection_name] + collection = laycol["ptr"].collection + laycol_parent = laycol["parent"] + + # save state and remove all hiding properties of parent collection + orig_parent_hide_select = False + orig_parent_exclude = False + orig_parent_hide_viewport = False + + if laycol_parent["ptr"].collection.hide_select: + orig_parent_hide_select = True + + if laycol_parent["ptr"].exclude: + orig_parent_exclude = True + + if laycol_parent["ptr"].hide_viewport: + orig_parent_hide_viewport = True + + laycol_parent["ptr"].collection.hide_select = False + laycol_parent["ptr"].exclude = False + laycol_parent["ptr"].hide_viewport = False + + + # remove all hiding properties of this collection + collection.hide_select = False + laycol["ptr"].exclude = False + laycol["ptr"].hide_viewport = False + + + # shift all objects in this collection to the parent collection + if collection.objects: + orig_selected_objs = context.selected_objects + orig_active_obj = context.active_object + + # select all objects in collection + bpy.ops.object.select_same_collection(collection=collection.name) + context.view_layer.objects.active = context.selected_objects[0] + + # remove any objects already in parent collection from selection + for obj in context.selected_objects: + if obj in laycol["parent"]["ptr"].collection.objects.values(): + obj.select_set(False) + + # link selected objects to parent collection + bpy.ops.object.link_to_collection(collection_index=laycol_parent["id"]) + + # remove objects from collection + bpy.ops.collection.objects_remove(collection=collection.name) + + # reset selection original values + bpy.ops.object.select_all(action='DESELECT') + + for obj in orig_selected_objs: + obj.select_set(True) + context.view_layer.objects.active = orig_active_obj + + + # shift all child collections to the parent collection + if collection.children: + for subcollection in collection.children: + laycol_parent["ptr"].collection.children.link(subcollection) + + # reset hiding properties of parent collection + laycol_parent["ptr"].collection.hide_select = orig_parent_hide_select + laycol_parent["ptr"].exclude = orig_parent_exclude + laycol_parent["ptr"].hide_viewport = orig_parent_hide_viewport + + + # remove collection and update tree view + bpy.data.collections.remove(collection) + + + update_property_group(context) + + if len(context.scene.CMListCollection) == context.scene.CMListIndex: + context.scene.CMListIndex = len(context.scene.CMListCollection) - 1 + update_property_group(context) + + + # reset history + for rto in rto_history.values(): + rto.clear() + + return {'FINISHED'} + +rename = [False] +class CMNewCollectionOperator(bpy.types.Operator): + '''Add New Collection''' + bl_label = "Add New Collection" + bl_idname = "view3d.add_collection" + bl_options = {'UNDO'} + + child: BoolProperty() + + def execute(self, context): + global rto_history + + new_collection = bpy.data.collections.new('Collection') + scn = context.scene + + # if there are collections + if len(scn.CMListCollection) > 0: + # get selected collection + laycol = layer_collections[scn.CMListCollection[scn.CMListIndex].name] + + # add new collection + if self.child: + laycol["ptr"].collection.children.link(new_collection) + expanded.append(laycol["name"]) + + # update tree view property + update_property_group(context) + + scn.CMListIndex = layer_collections[new_collection.name]["row_index"] + + else: + laycol["parent"]["ptr"].collection.children.link(new_collection) + + # update tree view property + update_property_group(context) + + scn.CMListIndex = layer_collections[new_collection.name]["row_index"] + + # if no collections add top level collection and select it + else: + scn.collection.children.link(new_collection) + + # update tree view property + update_property_group(context) + + scn.CMListIndex = 0 + + global rename + rename[0] = True + + # reset history + for rto in rto_history.values(): + rto.clear() + + return {'FINISHED'} + + +phantom_history = {"view_layer": "", + "initial_state": {}, + + "exclude_history": [], + "select_history": [], + "hide_history": [], + "disable_history": [], + "render_history": [], + + "exclude_all_history": [], + "select_all_history": [], + "hide_all_history": [], + "disable_all_history": [], + "render_all_history": [] + } + +class CMPhantomModeOperator(bpy.types.Operator): + '''Toggle Phantom Mode''' + bl_label = "Toggle Phantom Mode" + bl_idname = "view3d.toggle_phantom_mode" + + def execute(self, context): + global phantom_history + global rto_history + + scn = context.scene + view_layer = context.view_layer.name + + # enter Phantom Mode + if not scn.CM_Phantom_Mode: + + scn.CM_Phantom_Mode = True + + # save current visibility state + phantom_history["view_layer"] = view_layer + + laycol_iter_list = [context.view_layer.layer_collection.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + phantom_history["initial_state"][layer_collection.name] = { + "exclude": layer_collection.exclude, + "select": layer_collection.collection.hide_select, + "hide": layer_collection.hide_viewport, + "disable": layer_collection.collection.hide_viewport, + "render": layer_collection.collection.hide_render, + } + + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + + # save current rto history + for rto, history, in rto_history.items(): + phantom_history[rto+"_history"] = history.get(view_layer, {}).copy() + + + # return to normal mode + else: + laycol_iter_list = [context.view_layer.layer_collection.children] + while len(laycol_iter_list) > 0: + new_laycol_iter_list = [] + for laycol_iter in laycol_iter_list: + for layer_collection in laycol_iter: + phantom_laycol = phantom_history["initial_state"][layer_collection.name] + + layer_collection.exclude = \ + phantom_laycol["exclude"] + + layer_collection.collection.hide_select = \ + phantom_laycol["select"] + + layer_collection.hide_viewport = \ + phantom_laycol["hide"] + + layer_collection.collection.hide_viewport = \ + phantom_laycol["disable"] + + layer_collection.collection.hide_render = \ + phantom_laycol["render"] + + + if len(layer_collection.children) > 0: + new_laycol_iter_list.append(layer_collection.children) + + laycol_iter_list = new_laycol_iter_list + + + # restore previous rto history + for rto, history, in rto_history.items(): + history[view_layer] = phantom_history[rto+"_history"].copy() + + scn.CM_Phantom_Mode = False + + + return {'FINISHED'} diff --git a/collection_manager/ui.py b/collection_manager/ui.py new file mode 100644 index 0000000000000000000000000000000000000000..4c0e4597b6badf62355d79f901fa4cf71a62c42e --- /dev/null +++ b/collection_manager/ui.py @@ -0,0 +1,412 @@ +from bpy.types import ( + Operator, + Panel, + UIList, + UI_UL_list, + ) + +from bpy.props import BoolProperty + +from .internals import * +from .operators import ( + rto_history, + rename, + phantom_history, + ) + + +class CollectionManager(Operator): + bl_label = "Collection Manager" + bl_idname = "view3d.collection_manager" + + last_view_layer = "" + + def draw(self, context): + layout = self.layout + scn = context.scene + view_layer = context.view_layer.name + + if view_layer != self.last_view_layer: + update_collection_tree(context) + self.last_view_layer = view_layer + + title_row = layout.split(factor=0.5) + main = title_row.row() + view = title_row.row(align=True) + view.alignment = 'RIGHT' + + main.label(text="Collection Manager") + + view.prop(context.view_layer, "use", text="") + view.separator() + + window = context.window + scene = window.scene + view.template_search( + window, "view_layer", + scene, "view_layers", + new="scene.view_layer_add", + unlink="scene.view_layer_remove") + + layout.row().separator() + layout.row().separator() + + filter_row = layout.row() + filter_row.alignment = 'RIGHT' + + filter_row.popover(panel="COLLECTIONMANAGER_PT_restriction_toggles", text="", icon='FILTER') + + toggle_row = layout.split(factor=0.3) + toggle_row.alignment = 'LEFT' + + sec1 = toggle_row.row() + sec1.alignment = 'LEFT' + sec1.enabled = False + + if len(expanded) > 0: + text = "Collapse All Items" + else: + text = "Expand All Items" + + sec1.operator("view3d.expand_all_items", text=text) + + for laycol in collection_tree: + if laycol["has_children"]: + sec1.enabled = True + break + + sec2 = toggle_row.row() + sec2.alignment = 'RIGHT' + + if scn.show_exclude: + exclude_all_history = rto_history["exclude_all"].get(view_layer, []) + depress = True if len(exclude_all_history) else False + + sec2.operator("view3d.un_exclude_all_collections", text="", icon='CHECKBOX_HLT', depress=depress) + + if scn.show_selectable: + select_all_history = rto_history["select_all"].get(view_layer, []) + depress = True if len(select_all_history) else False + + sec2.operator("view3d.un_restrict_select_all_collections", text="", icon='RESTRICT_SELECT_OFF', depress=depress) + + if scn.show_hideviewport: + hide_all_history = rto_history["hide_all"].get(view_layer, []) + depress = True if len(hide_all_history) else False + + sec2.operator("view3d.un_hide_all_collections", text="", icon='HIDE_OFF', depress=depress) + + if scn.show_disableviewport: + disable_all_history = rto_history["disable_all"].get(view_layer, []) + depress = True if len(disable_all_history) else False + + sec2.operator("view3d.un_disable_viewport_all_collections", text="", icon='RESTRICT_VIEW_OFF', depress=depress) + + if scn.show_render: + render_all_history = rto_history["render_all"].get(view_layer, []) + depress = True if len(render_all_history) else False + + sec2.operator("view3d.un_disable_render_all_collections", text="", icon='RESTRICT_RENDER_OFF', depress=depress) + + layout.row().template_list("CM_UL_items", "", context.scene, "CMListCollection", context.scene, "CMListIndex", rows=15, sort_lock=True) + + addcollec_row = layout.row() + addcollec_row.operator("view3d.add_collection", text="Add Collection", icon='COLLECTION_NEW').child = False + + addcollec_row.operator("view3d.add_collection", text="Add SubCollection", icon='COLLECTION_NEW').child = True + + phantom_row = layout.row() + toggle_text = "Disable " if scn.CM_Phantom_Mode else "Enable " + phantom_row.operator("view3d.toggle_phantom_mode", text=toggle_text+"Phantom Mode") + + if scn.CM_Phantom_Mode: + view.enabled = False + addcollec_row.enabled = False + + + def execute(self, context): + wm = context.window_manager + lvl = 0 + + #expanded.clear() + + #excludeall_history.clear() + #restrictselectall_history.clear() + #hideall_history.clear() + #disableviewall_history.clear() + #disablerenderall_history.clear() + + update_property_group(context) + + lvl = get_max_lvl() + + if lvl > 25: + lvl = 25 + + self.view_layer = context.view_layer.name + + # sync selection in ui list with active layer collection + try: + active_laycol_name = context.view_layer.active_layer_collection.name + active_laycol_row_index = layer_collections[active_laycol_name]["row_index"] + context.scene.CMListIndex = active_laycol_row_index + except: + context.scene.CMListIndex = 0 + + if context.scene.CM_Phantom_Mode: + if set(layer_collections.keys()) != set(phantom_history["initial_state"].keys()): + context.scene.CM_Phantom_Mode = False + + if context.view_layer.name != phantom_history["view_layer"]: + context.scene.CM_Phantom_Mode = False + + return wm.invoke_popup(self, width=(400+(lvl*20))) + + +def update_selection(self, context): + selected_item = context.scene.CMListCollection[context.scene.CMListIndex] + layer_collection = layer_collections[selected_item.name]["ptr"] + + context.view_layer.active_layer_collection = layer_collection + + +def filter_items_by_name_insensitive(pattern, bitflag, items, propname="name", flags=None, reverse=False): + """ + Set FILTER_ITEM for items which name matches filter_name one (case-insensitive). + pattern is the filtering pattern. + propname is the name of the string property to use for filtering. + flags must be a list of integers the same length as items, or None! + return a list of flags (based on given flags if not None), + or an empty list if no flags were given and no filtering has been done. + """ + import fnmatch + + if not pattern or not items: # Empty pattern or list = no filtering! + return flags or [] + + if flags is None: + flags = [0] * len(items) + + # Make pattern case-insensitive + pattern = pattern.lower() + + # Implicitly add heading/trailing wildcards. + pattern = "*" + pattern + "*" + + for i, item in enumerate(items): + name = getattr(item, propname, None) + + # Make name case-insensitive + name = name.lower() + + # This is similar to a logical xor + if bool(name and fnmatch.fnmatch(name, pattern)) is not bool(reverse): + flags[i] |= bitflag + + return flags + + +class CM_UL_items(UIList): + last_filter_value = "" + + filter_by_selected: BoolProperty( + name="Filter By Selected", + default=False, + description="Filter collections by selected items" + ) + + def draw_item(self, context, layout, data, item, icon, active_data,active_propname, index): + self.use_filter_show = True + + scn = context.scene + view_layer = context.view_layer.name + laycol = layer_collections[item.name] + collection = laycol["ptr"].collection + + split = layout.split(factor=0.96) + row = split.row(align=True) + row.alignment = 'LEFT' + + # indent child items + if laycol["lvl"] > 0: + for x in range(laycol["lvl"]): + row.label(icon='BLANK1') + + # add expander if collection has children to make UIList act like tree view + if laycol["has_children"]: + if laycol["expanded"]: + prop = row.operator("view3d.expand_sublevel", text="", icon='DISCLOSURE_TRI_DOWN', emboss=False) + prop.expand = False + prop.name = item.name + prop.index = index + + else: + prop = row.operator("view3d.expand_sublevel", text="", icon='DISCLOSURE_TRI_RIGHT', emboss=False) + prop.expand = True + prop.name = item.name + prop.index = index + + else: + row.label(icon='BLANK1') + + + row.label(icon='GROUP') + + name_row = row.row() + + #if rename[0] and index == scn.CMListIndex: + #name_row.activate_init = True + #rename[0] = False + + name_row.prop(item, "name", text="", expand=True) + + # used as a separator (actual separator not wide enough) + row.label() + + # add set_collection op + row_setcol = row.row() + row_setcol.operator_context = 'INVOKE_DEFAULT' + + icon = 'MESH_CUBE' + + if len(context.selected_objects) > 0 and context.active_object: + if context.active_object.name in collection.objects: + icon = 'SNAP_VOLUME' + else: + row_setcol.enabled = False + + + prop = row_setcol.operator("view3d.set_collection", text="", icon=icon, emboss=False) + prop.collection_index = laycol["id"] + prop.collection_name = item.name + + + if scn.show_exclude: + exclude_history_base = rto_history["exclude"].get(view_layer, {}) + exclude_target = exclude_history_base.get("target", "") + exclude_history = exclude_history_base.get("history", []) + + depress = True if len(exclude_history) and exclude_target == item.name else False + emboss = True if len(exclude_history) and exclude_target == item.name else False + icon = 'CHECKBOX_DEHLT' if laycol["ptr"].exclude else 'CHECKBOX_HLT' + + row.operator("view3d.exclude_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name + + if scn.show_selectable: + select_history_base = rto_history["select"].get(view_layer, {}) + select_target = select_history_base.get("target", "") + select_history = select_history_base.get("history", []) + + depress = True if len(select_history) and select_target == item.name else False + emboss = True if len(select_history) and select_target == item.name else False + icon = 'RESTRICT_SELECT_ON' if laycol["ptr"].collection.hide_select else 'RESTRICT_SELECT_OFF' + + row.operator("view3d.restrict_select_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name + + if scn.show_hideviewport: + hide_history_base = rto_history["hide"].get(view_layer, {}) + hide_target = hide_history_base.get("target", "") + hide_history = hide_history_base.get("history", []) + + depress = True if len(hide_history) and hide_target == item.name else False + emboss = True if len(hide_history) and hide_target == item.name else False + icon = 'HIDE_ON' if laycol["ptr"].hide_viewport else 'HIDE_OFF' + + row.operator("view3d.hide_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name + + if scn.show_disableviewport: + disable_history_base = rto_history["disable"].get(view_layer, {}) + disable_target = disable_history_base.get("target", "") + disable_history = disable_history_base.get("history", []) + + depress = True if len(disable_history) and disable_target == item.name else False + emboss = True if len(disable_history) and disable_target == item.name else False + icon = 'RESTRICT_VIEW_ON' if laycol["ptr"].collection.hide_viewport else 'RESTRICT_VIEW_OFF' + + row.operator("view3d.disable_viewport_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name + + if scn.show_render: + render_history_base = rto_history["render"].get(view_layer, {}) + render_target = render_history_base.get("target", "") + render_history = render_history_base.get("history", []) + + depress = True if len(render_history) and render_target == item.name else False + emboss = True if len(render_history) and render_target == item.name else False + icon = 'RESTRICT_RENDER_ON' if laycol["ptr"].collection.hide_render else 'RESTRICT_RENDER_OFF' + + row.operator("view3d.disable_render_collection", text="", icon=icon, emboss=emboss, depress=depress).name = item.name + + + rm_op = split.row() + rm_op.alignment = 'RIGHT' + rm_op.operator("view3d.remove_collection", text="", icon='X', emboss=False).collection_name = item.name + + if scn.CM_Phantom_Mode: + name_row.enabled = False + row_setcol.enabled = False + rm_op.enabled = False + + + def draw_filter(self, context, layout): + row = layout.row() + + subrow = row.row(align=True) + subrow.prop(self, "filter_name", text="") + + icon = 'ZOOM_OUT' if self.use_filter_invert else 'ZOOM_IN' + subrow.prop(self, "use_filter_invert", text="", icon=icon) + + subrow = row.row(align=True) + subrow.prop(self, "filter_by_selected", text="", icon='SNAP_VOLUME') + + def filter_items(self, context, data, propname): + flt_flags = [] + flt_neworder = [] + + list_items = getattr(data, propname) + + if self.filter_name: + flt_flags = filter_items_by_name_insensitive(self.filter_name, self.bitflag_filter_item, list_items) + + elif self.filter_by_selected: + flt_flags = [0] * len(list_items) + + for idx, item in enumerate(list_items): + collection = layer_collections[item.name]["ptr"].collection + + # check if any of the selected objects are in the collection + if not set(context.selected_objects).isdisjoint(collection.objects): + flt_flags[idx] |= self.bitflag_filter_item + + else: # display as treeview + flt_flags = [self.bitflag_filter_item] * len(list_items) + + for idx, item in enumerate(list_items): + if not layer_collections[item.name]["visible"]: + flt_flags[idx] = 0 + + return flt_flags, flt_neworder + + + + def invoke(self, context, event): + pass + + +class CMRestrictionTogglesPanel(Panel): + bl_label = "Restriction Toggles" + bl_idname = "COLLECTIONMANAGER_PT_restriction_toggles" + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + + def draw(self, context): + + layout = self.layout + + row = layout.row() + + row.prop(context.scene, "show_exclude", icon='CHECKBOX_HLT', icon_only=True) + row.prop(context.scene, "show_selectable", icon='RESTRICT_SELECT_OFF', icon_only=True) + row.prop(context.scene, "show_hideviewport", icon='HIDE_OFF', icon_only=True) + row.prop(context.scene, "show_disableviewport", icon='RESTRICT_VIEW_OFF', icon_only=True) + row.prop(context.scene, "show_render", icon='RESTRICT_RENDER_OFF', icon_only=True)