OverviewFilesCommitsIssuesDocsReleasesPeers

hardops / operators / modals / bool_object_scroll.py

279 lines · python ref:
import bpy
from . import infobar
from ... utility import addon
from ... ui_framework.master import Master
from ... ui_framework.utils.mods_list import get_mods_list
from ... utility.base_modal_controls import Base_Modal_Controls
from ... utils.toggle_view3d_panels import collapse_3D_view_panels
from ... utility.collections import turn_on_parent_collections


class HOPS_OT_BoolObjectScroll(bpy.types.Operator):
    bl_idname = "hops.bool_scroll_objects"
    bl_label = "Scroll Booleans"
    bl_description = "Use the scroll wheel to scroll through boolean modifiers on the selected object"

    running: bool = False


    def invoke(self, context, event):

        self.display_mesh_wire = True
        self.running = True
        self.original_visible = context.visible_objects[:]
        self.active = context.active_object
        self.modifiers = [mod for mod in context.active_object.modifiers if mod.type == "BOOLEAN"]
        if addon.preference().property.bool_scroll == 'ADDITIVE':
            def vis_check(obj):
                if not obj:
                    return
                return obj.hide_get() or obj.hide_viewport
            self.obj_list = [{"object": mod.object, "hide": mod.object.hide_viewport, "override": True} for mod in self.modifiers if vis_check(mod.object)]
        else:
            self.obj_list = [{"object": mod.object, "hide": mod.object.hide_viewport, "override": True} for mod in self.modifiers if mod.object is not None]
        self.obj_index = 0
        self.obj_name = ""
        self.ctrl_a = True
        self.show_current = True

        if not self.obj_list:
            return{'CANCELLED'}

        in_cutters = False
        if 'Cutters' in bpy.data.collections:
            for obj in self.obj_list:
                if obj['object'].name in bpy.data.collections['Cutters'].objects:
                    in_cutters = True
                    break

        if in_cutters:
            objects = bpy.data.collections['Cutters'].objects[:]
            bpy.data.collections.remove(bpy.data.collections['Cutters'])

            context.scene.collection.children.link(bpy.data.collections.new(name='Cutters'))

            for obj in objects:
                bpy.data.collections['Cutters'].objects.link(obj)

        bpy.ops.object.hide_view_clear()

        for obj in [i['object'] for i in self.obj_list]:
            obj.hide_viewport = True

        for obj in context.visible_objects:
            obj.select_set(obj not in self.original_visible)

        bpy.ops.object.hide_view_set()

        self.active.select_set(True)
        context.view_layer.objects.active= self.active

        # Base Systems
        self.master = Master(context=context)
        self.master.only_use_fast_ui = True
        self.base_controls = Base_Modal_Controls(context, event)
        self.original_tool_shelf, self.original_n_panel = collapse_3D_view_panels()

        context.window_manager.modal_handler_add(self)
        infobar.initiate(self)
        return {'RUNNING_MODAL'}


    def modal(self, context, event):

        # Base Systems
        self.master.receive_event(event=event)
        self.base_controls.update(context, event)

        if self.base_controls.pass_through:
            return {'PASS_THROUGH'}

        check_collections = False
        if self.base_controls.scroll:
            if event.shift:
                if self.base_controls.scroll == 1:
                    bpy.ops.object.modifier_move_up(modifier=self.modifiers[self.obj_index].name)
                elif  self.base_controls.scroll == -1:
                    bpy.ops.object.modifier_move_down(modifier=self.modifiers[self.obj_index].name)
            else:
                self.obj_index += self.base_controls.scroll
                self.display_mesh_wire = True
                check_collections = True

        if self.obj_index >= len(self.obj_list):
            self.obj_index = 0

        if self.obj_index < 0:
            self.obj_index = len(self.obj_list) - 1

        if not len(self.obj_list) > 0:
            collapse_3D_view_panels(self.original_tool_shelf, self.original_n_panel)
            self.master.run_fade()
            infobar.remove(self)
            return {'FINISHED'}

        obj = self.obj_list[self.obj_index]["object"]

        if obj and check_collections:
            turn_on_parent_collections(obj, context.scene.collection)
            obj.hide_viewport = False
            obj.select_set(True)


        for data in self.obj_list:
            data["object"].hide_viewport = data["override"]

        override_value = self.obj_list[self.obj_index]["override"]

        if self.show_current:
            obj.hide_viewport = False

        self.obj_name = obj.name

        # Display wire mesh
        if self.display_mesh_wire:
            self.display_mesh_wire = False
            bpy.ops.hops.draw_wire_mesh_launcher(object_name=self.obj_name)

        if event.type == 'F' and event.value == 'PRESS':
            for mod in self.modifiers:
                if mod.type == "BOOLEAN":
                    if mod.object == obj:
                        bpy.ops.object.modifier_apply(modifier=mod.name) # apply_as='DATA',
                        self.obj_list.remove(self.obj_list[self.obj_index])
                        if not event.shift:
                            bpy.data.objects.remove(obj)

        if event.type == 'A' and event.value == 'PRESS':
            self.obj_list[self.obj_index]["override"] = not override_value
            obj.hide_viewport = override_value
            bpy.ops.hops.draw_wire_mesh_launcher(object_name=obj.name)

        if event.type == 'T' and event.value == 'PRESS':
            self.modifiers[self.obj_index].show_viewport = not self.modifiers[self.obj_index].show_viewport

        if event.ctrl: # ctrl+a to toggle object visibility
            if event.type == 'A' and event.value == 'PRESS':
                self.ctrl_a = not self.ctrl_a
                for data in self.obj_list:
                    data["override"] = self.ctrl_a

        if event.type == "W" and event.value == "PRESS" and event.shift  and not event.ctrl:
            #would be cooler with a for that also set the ones leading up to this to be toggled off or on as well.
            # for proxe later.
            if self.modifiers[self.obj_index] != None:
                active_mod = self.modifiers[self.obj_index]
                active_mod.show_render = not active_mod.show_render
                self.report({'INFO'}, F'Modifiers Renderability : {active_mod.show_render}')

        if event.type == "W" and event.value == "PRESS" and event.ctrl and not event.shift:
            for mod in self.modifiers:
                mod.show_render = not mod.show_render
            self.report({'INFO'}, F'Modifier Visibility Re-enabled : {len(self.modifiers)}')

        if event.type == "H" and event.value == "PRESS":
            addon.preference().property.hops_modal_help = not addon.preference().property.hops_modal_help

        if self.base_controls.confirm or event.type in { 'RET', 'NUMPAD_ENTER'}:
            self.finish(context)
            collapse_3D_view_panels(self.original_tool_shelf, self.original_n_panel)
            self.master.run_fade()
            infobar.remove(self)
            return {'FINISHED'}

        if self.base_controls.cancel or event.type in {'BACK_SPACE'}:
            self.finish(context, cancel=True)
            collapse_3D_view_panels(self.original_tool_shelf, self.original_n_panel)
            self.master.run_fade()
            infobar.remove(self)
            return {'CANCELLED'}

        self.draw_master(context=context)
        context.area.tag_redraw()
        return {'RUNNING_MODAL'}


    def finish(self, context, cancel=False):

        self.running = False

        for obj in self.obj_list:
            if obj['object'].hide_viewport:
                obj['object'].hide_viewport = False
                obj['object'].select_set(True)
            else:
                obj['object'].select_set(False)

        self.active.select_set(False)

        bpy.ops.object.hide_view_set()

        obj = self.obj_list[self.obj_index]["object"]
        context.view_layer.objects.active = obj
        obj.select_set(True)

        if cancel:
            bpy.ops.object.hide_view_clear()

            for obj in context.visible_objects:
                obj.select_set(obj not in self.original_visible)

            bpy.ops.object.hide_view_set()

            self.active.select_set(True)
            context.view_layer.objects.active = self.active


    def draw_master(self, context):

        # Start
        self.master.setup()

        ########################
        #   Fast UI
        ########################

        if self.master.should_build_fast_ui():

            # Main
            win_list = []
            if addon.preference().ui.Hops_modal_fast_ui_loc_options != 1: #Fast Floating
                win_list.append("{}".format(self.obj_index + 1))
            else:
                win_list.append("Bool Scroll (Classic)")
                win_list.append("{}".format(self.obj_index + 1))
                win_list.append("{}".format(self.obj_name))
                try:
                    win_list.append("{}".format(self.modifiers[self.obj_index].name))
                except:
                    win_list.append("{}".format('APPLIED'))

            # Help
            help_items = {"GLOBAL" : [], "STANDARD" : []}

            help_items["GLOBAL"] = [
                ("M", "Toggle mods list"),
                ("H", "Toggle help"),
                ("~", "Toggle UI Display Type"),
                ("O", "Toggle viewport rendering")]

            help_items["STANDARD"] = [
                ("scroll",    "Change boolean visibility."),
                ("A",         "Toggle current visibility"),
                ("Ctrl + A",  "Toggle all visibility"),
                ("F",         "Apply current object"),
                ("shift + F", "Apply and keep current object"),
                ("Shift + Scroll", "Move mod up/down"),
                ("T",         "Toggle modifier visibility")]

            # Mods
            active_mod = ""
            if self.modifiers[self.obj_index] != None:
                active_mod = self.modifiers[self.obj_index].name
            mods_list = get_mods_list(mods=bpy.context.active_object.modifiers)

            self.master.receive_fast_ui(win_list=win_list, help_list=help_items, image="Booleans", mods_list=mods_list, active_mod_name=active_mod)

        # Finished
        self.master.finished()