Skip to content
Snippets Groups Projects
ui.py 37 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### 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, Ryan Inch
    
    
    from bpy.types import (
    
        Operator,
        Panel,
        UIList,
    
    from bpy.props import (
        BoolProperty,
        StringProperty,
    )
    
    from .internals import (
        collection_tree,
    
        qcd_collection_state,
    
        expanded,
        get_max_lvl,
        layer_collections,
    
        rto_history,
    
        qcd_history,
    
        expand_history,
        phantom_history,
        copy_buffer,
        swap_buffer,
    
        update_collection_tree,
        update_property_group,
    
        get_move_active,
        update_qcd_header,
    
    from .qcd_operators import (
        QCDAllBase,
    )
    
    
    
    preview_collections = {}
    last_icon_theme_text = None
    last_icon_theme_text_sel = None
    
    
    
    class CollectionManager(Operator):
    
        '''Manage and control collections, with advanced features, in a popup UI'''
    
        bl_label = "Collection Manager"
        bl_idname = "view3d.collection_manager"
    
        last_view_layer = ""
    
        window_open = False
    
    
        master_collection: StringProperty(
            default='Scene Collection',
            name="",
            description="Scene Collection"
            )
    
    
        def __init__(self):
            self.window_open = True
    
    
        def draw(self, context):
    
            cls = CollectionManager
    
            layout = self.layout
    
            cm = context.scene.collection_manager
    
            prefs = context.preferences.addons[__package__].preferences
    
            view_layer = context.view_layer
    
            if view_layer.name != cls.last_view_layer:
    
                if prefs.enable_qcd:
    
                    bpy.app.timers.register(update_qcd_header)
    
    
                update_collection_tree(context)
    
                cls.last_view_layer = view_layer.name
    
            # title and 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(view_layer, "use", text="")
    
            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()
    
            button_row_1 = layout.row()
    
            op_sec = button_row_1.row()
    
            op_sec.alignment = 'LEFT'
    
            collapse_sec = op_sec.row()
            collapse_sec.alignment = 'LEFT'
            collapse_sec.enabled = False
    
            if len(expanded) > 0:
                text = "Collapse All Items"
            else:
                text = "Expand All Items"
    
            collapse_sec.operator("view3d.expand_all_items", text=text)
    
            for laycol in collection_tree:
                if laycol["has_children"]:
    
                    collapse_sec.enabled = True
    
            if prefs.enable_qcd:
    
                renum_sec = op_sec.row()
                renum_sec.alignment = 'LEFT'
                renum_sec.operator("view3d.renumerate_qcd_slots")
    
            # menu & filter
            right_sec = button_row_1.row()
            right_sec.alignment = 'RIGHT'
    
            right_sec.menu("VIEW3D_MT_CM_specials_menu")
            right_sec.popover(panel="COLLECTIONMANAGER_PT_display_options",
    
                               text="", icon='FILTER')
    
            mc_box = layout.box()
            master_collection_row = mc_box.row(align=True)
    
    
            # collection icon
            c_icon = master_collection_row.row()
    
            highlight = False
            if (context.view_layer.active_layer_collection ==
                context.view_layer.layer_collection):
                    highlight = True
    
    
            prop = c_icon.operator("view3d.set_active_collection",
    
                                                  text='', icon='GROUP', depress=highlight)
    
            prop.is_master_collection = True
    
            prop.collection_name = 'Master Collection'
    
            master_collection_row.separator()
    
    
            name_row = master_collection_row.row()
            name_row.prop(self, "master_collection", text='')
            name_row.enabled = False
    
            master_collection_row.separator()
    
    
            global_rto_row = master_collection_row.row()
            global_rto_row.alignment = 'RIGHT'
    
            row_setcol = global_rto_row.row()
            row_setcol.alignment = 'LEFT'
            row_setcol.operator_context = 'INVOKE_DEFAULT'
    
            selected_objects = get_move_selection()
            active_object = get_move_active()
    
            CM_UL_items.selected_objects = selected_objects
            CM_UL_items.active_object = active_object
    
    
            collection = context.view_layer.layer_collection.collection
    
            icon = 'MESH_CUBE'
    
            if selected_objects:
                if active_object and active_object.name in collection.objects:
                    icon = 'SNAP_VOLUME'
    
    
                elif not selected_objects.isdisjoint(collection.objects):
    
                    icon = 'STICKY_UVS_LOC'
    
            else:
                row_setcol.enabled = False
    
            prop = row_setcol.operator("view3d.set_collection", text="",
                                       icon=icon, emboss=False)
    
            prop.is_master_collection = True
    
            prop.collection_name = 'Master Collection'
    
            copy_icon = 'COPYDOWN'
            swap_icon = 'ARROW_LEFTRIGHT'
            copy_swap_icon = 'SELECT_INTERSECT'
    
    
            if cm.show_exclude:
                exclude_all_history = rto_history["exclude_all"].get(view_layer.name, [])
    
                depress = True if len(exclude_all_history) else False
    
                buffers = [False, False]
    
                if copy_buffer["RTO"] == "exclude":
    
                    icon = copy_icon
                    buffers[0] = True
    
                if swap_buffer["A"]["RTO"] == "exclude":
    
                    icon = swap_icon
                    buffers[1] = True
    
                if buffers[0] and buffers[1]:
                    icon = copy_swap_icon
    
                global_rto_row.operator("view3d.un_exclude_all_collections", text="", icon=icon, depress=depress)
    
            if cm.show_selectable:
                select_all_history = rto_history["select_all"].get(view_layer.name, [])
    
                depress = True if len(select_all_history) else False
    
                icon = 'RESTRICT_SELECT_OFF'
    
                buffers = [False, False]
    
                if copy_buffer["RTO"] == "select":
    
                    icon = copy_icon
                    buffers[0] = True
    
                if swap_buffer["A"]["RTO"] == "select":
    
                    icon = swap_icon
                    buffers[1] = True
    
                if buffers[0] and buffers[1]:
                    icon = copy_swap_icon
    
                global_rto_row.operator("view3d.un_restrict_select_all_collections", text="", icon=icon, depress=depress)
    
            if cm.show_hide_viewport:
                hide_all_history = rto_history["hide_all"].get(view_layer.name, [])
    
                depress = True if len(hide_all_history) else False
    
                buffers = [False, False]
    
                if copy_buffer["RTO"] == "hide":
    
                    icon = copy_icon
                    buffers[0] = True
    
                if swap_buffer["A"]["RTO"] == "hide":
    
                    icon = swap_icon
                    buffers[1] = True
    
                if buffers[0] and buffers[1]:
                    icon = copy_swap_icon
    
                global_rto_row.operator("view3d.un_hide_all_collections", text="", icon=icon, depress=depress)
    
            if cm.show_disable_viewport:
                disable_all_history = rto_history["disable_all"].get(view_layer.name, [])
    
                depress = True if len(disable_all_history) else False
    
                icon = 'RESTRICT_VIEW_OFF'
    
                buffers = [False, False]
    
                if copy_buffer["RTO"] == "disable":
    
                    icon = copy_icon
                    buffers[0] = True
    
                if swap_buffer["A"]["RTO"] == "disable":
    
                    icon = swap_icon
                    buffers[1] = True
    
                if buffers[0] and buffers[1]:
                    icon = copy_swap_icon
    
                global_rto_row.operator("view3d.un_disable_viewport_all_collections", text="", icon=icon, depress=depress)
    
            if cm.show_render:
                render_all_history = rto_history["render_all"].get(view_layer.name, [])
    
                depress = True if len(render_all_history) else False
    
                icon = 'RESTRICT_RENDER_OFF'
    
                buffers = [False, False]
    
                if copy_buffer["RTO"] == "render":
    
                    icon = copy_icon
                    buffers[0] = True
    
                if swap_buffer["A"]["RTO"] == "render":
    
                    icon = swap_icon
                    buffers[1] = True
    
                if buffers[0] and buffers[1]:
                    icon = copy_swap_icon
    
                global_rto_row.operator("view3d.un_disable_render_all_collections", text="", icon=icon, depress=depress)
    
            if cm.show_holdout:
                holdout_all_history = rto_history["holdout_all"].get(view_layer.name, [])
                depress = True if len(holdout_all_history) else False
                icon = 'HOLDOUT_ON'
                buffers = [False, False]
    
                if copy_buffer["RTO"] == "holdout":
                    icon = copy_icon
                    buffers[0] = True
    
                if swap_buffer["A"]["RTO"] == "holdout":
                    icon = swap_icon
                    buffers[1] = True
    
                if buffers[0] and buffers[1]:
                    icon = copy_swap_icon
    
                global_rto_row.operator("view3d.un_holdout_all_collections", text="", icon=icon, depress=depress)
    
            if cm.show_indirect_only:
                indirect_all_history = rto_history["indirect_all"].get(view_layer.name, [])
                depress = True if len(indirect_all_history) else False
                icon = 'INDIRECT_ONLY_ON'
                buffers = [False, False]
    
                if copy_buffer["RTO"] == "indirect":
                    icon = copy_icon
                    buffers[0] = True
    
                if swap_buffer["A"]["RTO"] == "indirect":
                    icon = swap_icon
                    buffers[1] = True
    
                if buffers[0] and buffers[1]:
                    icon = copy_swap_icon
    
                global_rto_row.operator("view3d.un_indirect_only_all_collections", text="", icon=icon, depress=depress)
    
    
            layout.row().template_list("CM_UL_items", "",
                                       cm, "cm_list_collection",
                                       cm, "cm_list_index",
                                       rows=15,
                                       sort_lock=True)
    
            button_row_2 = layout.row()
            prop = button_row_2.operator("view3d.add_collection", text="Add Collection",
    
                                   icon='COLLECTION_NEW')
            prop.child = False
    
            prop = button_row_2.operator("view3d.add_collection", text="Add SubCollection",
    
                                   icon='COLLECTION_NEW')
            prop.child = True
    
            button_row_3 = layout.row()
    
    
            # phantom mode
            phantom_mode = button_row_3.row(align=True)
    
            toggle_text = "Disable " if cm.in_phantom_mode else "Enable "
    
            phantom_mode.operator("view3d.toggle_phantom_mode", text=toggle_text+"Phantom Mode")
            phantom_mode.operator("view3d.apply_phantom_mode", text="", icon='CHECKMARK')
    
            if cm.in_phantom_mode:
    
                view.enabled = False
    
                if prefs.enable_qcd:
                    renum_sec.enabled = False
    
                c_icon.enabled = False
                row_setcol.enabled = False
                addcollec_row.enabled = False
    
        def execute(self, context):
            wm = context.window_manager
    
            update_property_group(context)
    
    
            cm = context.scene.collection_manager
            view_layer = context.view_layer
    
            self.view_layer = view_layer.name
    
            # make sure list index is valid
            if cm.cm_list_index >= len(cm.cm_list_collection):
                cm.cm_list_index = -1
    
    
            # check if expanded & history/buffer state still correct
    
            if 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]
    
    
            # 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"] = ""
    
    
            # handle window sizing
            max_width = 960
            min_width = 456
    
            row_indent_width = 15
    
            scrollbar_width = 21
            lvl = get_max_lvl()
    
    
            width = min_width + row_indent_width + (width_step * lvl)
    
            if bpy.context.preferences.addons[__package__].preferences.enable_qcd:
                width += qcd_width
    
    
            if len(layer_collections) > 14:
                width += scrollbar_width
    
            if width > max_width:
                width = max_width
    
            return wm.invoke_popup(self, width=width)
    
        def __del__(self):
            global collection_state
    
    
            if not self.window_open:
                # prevent destructor execution when changing templates
                return
    
    
            collection_state.clear()
            collection_state.update(generate_state())
    
    
    
    class CM_UL_items(UIList):
    
        last_filter_value = ""
    
        selected_objects = set()
        active_object = None
    
    
        visible_items = []
        new_collections = []
    
        filter_name: StringProperty(
                            name="Filter By Name",
                            default="",
                            description="Filter collections by name",
                            update=lambda self, context:
                                CM_UL_items.new_collections.clear(),
                            )
    
        use_filter_invert: BoolProperty(
                            name="Invert",
                            default=False,
                            description="Invert filtering (show hidden items, and vice-versa)",
                            )
    
    
        filter_by_selected: BoolProperty(
                            name="Filter By Selected",
                            default=False,
    
                            description="Filter collections by selected items",
                            update=lambda self, context:
                                CM_UL_items.new_collections.clear(),
    
        filter_by_qcd: BoolProperty(
                            name="Filter By QCD",
                            default=False,
    
                            description="Filter collections to only show QCD slots",
                            update=lambda self, context:
                                CM_UL_items.new_collections.clear(),
    
        def draw_item(self, context, layout, data, item, icon, active_data,active_propname, index):
            self.use_filter_show = True
    
            cm = context.scene.collection_manager
    
            prefs = context.preferences.addons[__package__].preferences
    
            view_layer = context.view_layer
    
            laycol = layer_collections[item.name]
            collection = laycol["ptr"].collection
    
            selected_objects = CM_UL_items.selected_objects
            active_object = CM_UL_items.active_object
    
            column = layout.column(align=True)
    
            main_row = column.row()
    
            s1 = main_row.row(align=True)
            s1.alignment = 'LEFT'
    
            s2 = main_row.row(align=True)
            s2.alignment = 'RIGHT'
    
            row = s1
    
            # allow room to select the row from the beginning
            row.separator()
    
    
            # indent child items
            if laycol["lvl"] > 0:
    
                for _ 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"]:
    
                    highlight = True if expand_history["target"] == item.name else False
    
    
                    prop = row.operator("view3d.expand_sublevel", text="",
                                        icon='DISCLOSURE_TRI_DOWN',
    
                                        emboss=highlight, depress=highlight)
    
                    prop.expand = False
                    prop.name = item.name
                    prop.index = index
    
                    highlight = True if expand_history["target"] == item.name else False
    
    
                    prop = row.operator("view3d.expand_sublevel", text="",
    
                                        icon='DISCLOSURE_TRI_RIGHT',
                                        emboss=highlight, depress=highlight)
    
                    prop.expand = True
                    prop.name = item.name
                    prop.index = index
    
            else:
                row.label(icon='BLANK1')
    
            # collection icon
            c_icon = row.row()
            highlight = False
            if (context.view_layer.active_layer_collection == laycol["ptr"]):
                    highlight = True
    
            prop = c_icon.operator("view3d.set_active_collection", text='', icon='GROUP',
                                                  emboss=highlight, depress=highlight)
    
    
            prop.is_master_collection = False
    
            prop.collection_name = item.name
    
            if prefs.enable_qcd:
    
                QCD = row.row()
                QCD.scale_x = 0.4
    
                QCD.prop(item, "qcd_slot_idx", text="")
    
            #if rename[0] and index == cm.cm_list_index:
    
                #c_name.activate_init = True
    
                #rename[0] = False
    
            c_name.prop(item, "name", text="", expand=True)
    
            # used as a separator (actual separator not wide enough)
            row.label()
    
            row = s2 if cm.align_local_ops else s1
    
    
            # add set_collection op
    
            set_obj_col = row.row()
            set_obj_col.operator_context = 'INVOKE_DEFAULT'
    
            icon = 'MESH_CUBE'
    
            if selected_objects:
                if active_object and active_object.name in collection.objects:
    
                    icon = 'SNAP_VOLUME'
    
                elif not selected_objects.isdisjoint(collection.objects):
    
                set_obj_col.enabled = False
    
            prop = set_obj_col.operator("view3d.set_collection", text="",
    
                                       icon=icon, emboss=False)
    
            prop.is_master_collection = False
    
            prop.collection_name = item.name
    
            if cm.show_exclude:
                exclude_history_base = rto_history["exclude"].get(view_layer.name, {})
    
                exclude_target = exclude_history_base.get("target", "")
                exclude_history = exclude_history_base.get("history", [])
    
                highlight = bool(exclude_history and exclude_target == item.name)
    
                icon = 'CHECKBOX_DEHLT' if laycol["ptr"].exclude else 'CHECKBOX_HLT'
    
                prop = row.operator("view3d.exclude_collection", text="", icon=icon,
                             emboss=highlight, depress=highlight)
                prop.name = item.name
    
            if cm.show_selectable:
                select_history_base = rto_history["select"].get(view_layer.name, {})
    
                select_target = select_history_base.get("target", "")
                select_history = select_history_base.get("history", [])
    
                highlight = bool(select_history and select_target == item.name)
                icon = ('RESTRICT_SELECT_ON' if laycol["ptr"].collection.hide_select else
                        'RESTRICT_SELECT_OFF')
    
                prop = row.operator("view3d.restrict_select_collection", text="", icon=icon,
                             emboss=highlight, depress=highlight)
                prop.name = item.name
    
            if cm.show_hide_viewport:
                hide_history_base = rto_history["hide"].get(view_layer.name, {})
    
                hide_target = hide_history_base.get("target", "")
                hide_history = hide_history_base.get("history", [])
    
                highlight = bool(hide_history and hide_target == item.name)
    
                icon = 'HIDE_ON' if laycol["ptr"].hide_viewport else 'HIDE_OFF'
    
                prop = row.operator("view3d.hide_collection", text="", icon=icon,
                             emboss=highlight, depress=highlight)
                prop.name = item.name
    
            if cm.show_disable_viewport:
                disable_history_base = rto_history["disable"].get(view_layer.name, {})
    
                disable_target = disable_history_base.get("target", "")
                disable_history = disable_history_base.get("history", [])
    
                highlight = bool(disable_history and disable_target == item.name)
                icon = ('RESTRICT_VIEW_ON' if laycol["ptr"].collection.hide_viewport else
                        'RESTRICT_VIEW_OFF')
    
                prop = row.operator("view3d.disable_viewport_collection", text="", icon=icon,
                             emboss=highlight, depress=highlight)
                prop.name = item.name
    
            if cm.show_render:
                render_history_base = rto_history["render"].get(view_layer.name, {})
    
                render_target = render_history_base.get("target", "")
                render_history = render_history_base.get("history", [])
    
                highlight = bool(render_history and render_target == item.name)
                icon = ('RESTRICT_RENDER_ON' if laycol["ptr"].collection.hide_render else
                        'RESTRICT_RENDER_OFF')
    
                prop = row.operator("view3d.disable_render_collection", text="", icon=icon,
                             emboss=highlight, depress=highlight)
                prop.name = item.name
    
            if cm.show_holdout:
                holdout_history_base = rto_history["holdout"].get(view_layer.name, {})
                holdout_target = holdout_history_base.get("target", "")
                holdout_history = holdout_history_base.get("history", [])
    
                highlight = bool(holdout_history and holdout_target == item.name)
                icon = ('HOLDOUT_ON' if laycol["ptr"].holdout else
                        'HOLDOUT_OFF')
    
                prop = row.operator("view3d.holdout_collection", text="", icon=icon,
                             emboss=highlight, depress=highlight)
                prop.name = item.name
    
            if cm.show_indirect_only:
                indirect_history_base = rto_history["indirect"].get(view_layer.name, {})
                indirect_target = indirect_history_base.get("target", "")
                indirect_history = indirect_history_base.get("history", [])
    
                highlight = bool(indirect_history and indirect_target == item.name)
                icon = ('INDIRECT_ONLY_ON' if laycol["ptr"].indirect_only else
                        'INDIRECT_ONLY_OFF')
    
                prop = row.operator("view3d.indirect_only_collection", text="", icon=icon,
                             emboss=highlight, depress=highlight)
                prop.name = item.name
    
    
    
            row = s2
    
            row.separator()
            row.separator()
    
            rm_op = row.row()
    
            prop = rm_op.operator("view3d.remove_collection", text="", icon='X', emboss=False)
            prop.collection_name = item.name
    
    
            if len(data.cm_list_collection) > index + 1:
                line_separator = column.row(align=True)
                line_separator.ui_units_y = 0.01
                line_separator.scale_y = 0.1
                line_separator.enabled = False
    
                line_separator.separator()
                line_separator.label(icon='BLANK1')
    
                for _ in range(laycol["lvl"] + 1):
                    line_separator.label(icon='BLANK1')
    
                line_separator.prop(cm, "ui_separator")
    
    
            if cm.in_phantom_mode:
    
                c_icon.enabled = False
    
                c_name.enabled = False
                set_obj_col.enabled = False
    
                rm_op.enabled = False
    
                if prefs.enable_qcd:
    
        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')
    
    
            if context.preferences.addons[__package__].preferences.enable_qcd:
                subrow.prop(self, "filter_by_qcd", text="", icon='EVENT_Q')
    
        def filter_items(self, context, data, propname):
    
            CM_UL_items.filtering = False
    
    
            flt_flags = []
            flt_neworder = []
            list_items = getattr(data, propname)
    
            if self.filter_name:
    
                CM_UL_items.filtering = True
    
                new_flt_flags = filter_items_by_name_custom(self.filter_name, self.bitflag_filter_item, list_items)
    
                flt_flags = merge_flt_flags(flt_flags, new_flt_flags)
    
    
            if self.filter_by_selected:
                CM_UL_items.filtering = True
                new_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):
    
                        new_flt_flags[idx] = self.bitflag_filter_item
    
                    # add in any recently created collections
                    if item.name in CM_UL_items.new_collections:
    
                        new_flt_flags[idx] = self.bitflag_filter_item
    
                flt_flags = merge_flt_flags(flt_flags, new_flt_flags)
    
    
            if self.filter_by_qcd:
                CM_UL_items.filtering = True
                new_flt_flags = [0] * len(list_items)
    
    
                for idx, item in enumerate(list_items):
    
                    if item.qcd_slot_idx:
    
                        new_flt_flags[idx] = self.bitflag_filter_item
    
                    # add in any recently created collections
                    if item.name in CM_UL_items.new_collections:
    
                        new_flt_flags[idx] = self.bitflag_filter_item
    
                flt_flags = merge_flt_flags(flt_flags, new_flt_flags)
    
    
            if not CM_UL_items.filtering: # display as treeview
                CM_UL_items.new_collections.clear()
    
                flt_flags = [0] * len(list_items)
    
                for idx, item in enumerate(list_items):
    
                    if layer_collections[item.name]["visible"]:
                        flt_flags[idx] = self.bitflag_filter_item
    
    
            if self.use_filter_invert:
    
                CM_UL_items.filtering = True # invert can act as pseudo filtering
    
                for idx, flag in enumerate(flt_flags):
                    flt_flags[idx] = 0 if flag else self.bitflag_filter_item
    
    
            # update visible items list
            CM_UL_items.visible_items.clear()
            CM_UL_items.visible_items.extend(flt_flags)
    
    
            return flt_flags, flt_neworder
    
    
        def invoke(self, context, event):
            pass
    
    
    
    class CMDisplayOptionsPanel(Panel):
        bl_label = "Display Options"
        bl_idname = "COLLECTIONMANAGER_PT_display_options"
    
    
        # set space type to VIEW_3D and region type to HEADER
        # because we only need it in a popover in the 3D View
        # and don't want it always present in the UI/N-Panel
    
        bl_space_type = 'VIEW_3D'
    
        bl_region_type = 'HEADER'
    
        def draw(self, context):
    
            cm = context.scene.collection_manager
    
            layout = self.layout
    
            panel_header = layout.row()
            panel_header.alignment = 'CENTER'
            panel_header.label(text="Display Options")
    
            section_header = layout.row()
            section_header.alignment = 'LEFT'
            section_header.label(text="Restriction Toggles")
    
            row = layout.row()
    
            row.prop(cm, "show_exclude", icon='CHECKBOX_HLT', icon_only=True)
            row.prop(cm, "show_selectable", icon='RESTRICT_SELECT_OFF', icon_only=True)
            row.prop(cm, "show_hide_viewport", icon='HIDE_OFF', icon_only=True)
            row.prop(cm, "show_disable_viewport", icon='RESTRICT_VIEW_OFF', icon_only=True)
            row.prop(cm, "show_render", icon='RESTRICT_RENDER_OFF', icon_only=True)
    
            row.prop(cm, "show_holdout", icon='HOLDOUT_ON', icon_only=True)
            row.prop(cm, "show_indirect_only", icon='INDIRECT_ONLY_ON', icon_only=True)
    
            layout.separator()
    
            section_header = layout.row()
            section_header.label(text="Layout")
    
            row = layout.row()
            row.prop(cm, "align_local_ops")
    
    
    class SpecialsMenu(Menu):
        bl_label = "Specials"
        bl_idname = "VIEW3D_MT_CM_specials_menu"
    
        def draw(self, context):
            layout = self.layout
    
            prop = layout.operator("view3d.remove_empty_collections")
            prop.without_objects = False
    
            prop = layout.operator("view3d.remove_empty_collections",
                                   text="Purge All Collections Without Objects")
            prop.without_objects = True
    
    
    
    class EnableAllQCDSlotsMenu(Menu):
        bl_label = "Global QCD Slot Actions"
        bl_idname = "VIEW3D_MT_CM_qcd_enable_all_menu"
    
        def draw(self, context):
            layout = self.layout
    
            layout.operator("view3d.enable_all_qcd_slots")
            layout.operator("view3d.enable_all_qcd_slots_isolated")
    
            layout.separator()
    
            layout.operator("view3d.disable_all_non_qcd_slots")
            layout.operator("view3d.disable_all_collections")
    
            if context.mode == 'OBJECT':
                layout.separator()
                layout.operator("view3d.select_all_qcd_objects")
    
            layout.separator()
    
            layout.operator("view3d.discard_qcd_history")
    
    
    
    def view3d_header_qcd_slots(self, context):
    
        global qcd_collection_state
    
        update_collection_tree(context)
    
        view_layer = context.view_layer
    
    
        if qcd_collection_state:
            view_layer = context.view_layer
            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["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()
    
    
        main_row = layout.row(align=True)
        current_qcd_history = qcd_history.get(context.view_layer.name, [])
    
        main_row.operator_menu_hold("view3d.enable_all_qcd_slots_meta",
                                    text="",
                                    icon='HIDE_OFF',
                                    depress=bool(current_qcd_history),
                                    menu="VIEW3D_MT_CM_qcd_enable_all_menu")
    
    
        split = main_row.split()
    
        col = split.column(align=True)
        row = col.row(align=True)
        row.scale_y = 0.5
    
    
        selected_objects = get_move_selection()
        active_object = get_move_active()
    
    
        for x in range(20):
    
            qcd_slot_name = qcd_slots.get_name(str(x+1))
    
            if qcd_slot_name:
                qcd_laycol = layer_collections[qcd_slot_name]["ptr"]
    
                collection_objects = qcd_laycol.collection.objects
    
                icon_value = 0
    
                # if the active object is in the current collection use a custom icon
                if (active_object and active_object in selected_objects and
                    active_object.name in collection_objects):
                    icon = 'LAYER_ACTIVE'
    
                # if there are selected objects use LAYER_ACTIVE
    
                elif not selected_objects.isdisjoint(collection_objects):
    
                    icon = 'LAYER_USED'
    
                # If there are objects use LAYER_USED
                elif collection_objects:
                    icon = 'NONE'
                    active_icon = get_active_icon(context, qcd_laycol)
                    icon_value = active_icon.icon_id
    
                else:
                    icon = 'BLANK1'