OverviewFilesCommitsIssuesDocsReleasesPeers

hardops / operators / modals / bool_regular_scroll.py

268 lines · python ref:
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
from bpy.types import Operator
from bpy.props import BoolProperty
from ... utils.blender_ui import get_dpi, get_dpi_factor
from ... graphics.drawing2d import draw_text, set_drawing_dpi
from ... utility import addon
from ... utility import method_handler
from . import infobar
from ... utility.base_modal_controls import Base_Modal_Controls

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

    additive: BoolProperty()


    def invoke(self, context, event):

        self.base_controls = Base_Modal_Controls(context, event)

        self.start_mouse_position = [event.mouse_region_x, event.mouse_region_y]
        self.modifiers = [mod for mod in context.object.modifiers if mod.type == "BOOLEAN"]
        self.bools = {None: None}
        self.bool_index = 0
        self.all_mods = False
        self.loop = False
        self.original_obj = context.object

        for mod in self.modifiers:
            if mod.object is not None:
                self.bools.update({mod: {"original_show_viewport": mod.show_viewport, "override": False, "hide": mod.object.hide_viewport}})

        if len(self.bools) == 1:
            return {'CANCELLED'}

        args = (context, )
        self.draw_handler = bpy.types.SpaceView3D.draw_handler_add(self.draw_ui, args, "WINDOW", "POST_PIXEL")
        context.window_manager.modal_handler_add(self)
        infobar.initiate(self)
        return {'RUNNING_MODAL'}


    def modal(self, context, event):
        
        self.base_controls.update(context, event)

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

        if self.base_controls.scroll:
            self.bool_index += self.base_controls.scroll

        if self.loop:
            if self.bool_index >= len(self.bools):
                self.bool_index = 0
            if self.bool_index < 0:
                self.bool_index = len(self.bools) - 1

        else:
            self.bool_index = max(min(self.bool_index, len(self.bools) - 1), 0)

        if event.type == 'L' and event.value == 'PRESS':
            self.loop = not self.loop

        if event.type == 'A' and event.value == 'RELEASE':
            if self.bools[list(self.bools.keys())[self.bool_index]] is not None:
                # if list(self.bools.keys())[self.bool_index] is not None:
                override_value = self.bools[list(self.bools.keys())[self.bool_index]]["override"]
                self.bools[list(self.bools.keys())[self.bool_index]]["override"] = not override_value

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

        if event.type == 'M' and event.value == 'PRESS':
            self.all_mods = not self.all_mods
            self.bools = {None: None}
            self.bool_index = 0

            for mod in self.bools:
                if mod is not None:
                    mod.show_viewport = self.bools[mod]["original_show_viewport"]

            for mod in context.object.modifiers:
                if self.all_mods:
                    self.bools.update({mod: {"original_show_viewport": mod.show_viewport, "override": False}})

        if self.additive:
            for count, mod in enumerate(self.bools):
                if mod is not None:
                    if count <= self.bool_index:
                        mod.show_viewport = True
                    else:
                        mod.show_viewport = False
                    if self.bools[mod]["override"] or self.bools[list(self.bools.keys())[self.bool_index]] is None:
                        mod.show_viewport = False

        else:
            for mod in self.bools:
                if mod is not None and hasattr(mod, "object"):
                    mod.show_viewport = self.bools[mod]["override"]  # hide all mods except for those pressed A on.
                    mod.object.select_set(False)

                    mod.object.hide_viewport = True
                    if mod.show_viewport:  # show and select objects that have modifier being shown in viewport.
                        mod.object.hide_viewport = False

                        mod.object.select_set(True)

            if self.bools[list(self.bools.keys())[self.bool_index]] is not None:
                current_bool = list(self.bools.keys())[self.bool_index]
                current_bool.show_viewport = True  # show viewport on the current mod.
                if hasattr(current_bool, "object"):

                    current_bool.object.hide_viewport = False

                    current_bool.object.select_set(True)

        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()
            for mod in self.bools:
                if mod is not None and hasattr(mod, "object"):
                    if not mod.show_viewport:
                        mod.object.hide_viewport = True
                    else:

                        mod.object.hide_viewport = False
                        mod.object.select_set(True)

            if not self.additive:
                if hasattr(current_bool, "object") and current_bool.object is not None:
                    context.active_object.select_set(False)
                    current_bool.object.select_set(True)
                    context.view_layer.objects.active = current_bool.object
            infobar.remove(self)
            return {"FINISHED"}

        if self.base_controls.tilde:
            bpy.context.space_data.overlay.show_overlays = not bpy.context.space_data.overlay.show_overlays

        if self.base_controls.cancel or event.type in { 'BACK_SPACE'}:
            self.finish()
            for mod in self.bools:
                if mod is not None:
                    mod.show_viewport = self.bools[mod]["original_show_viewport"]
            infobar.remove(self)
            return {'CANCELLED'}

        context.area.tag_redraw()
        return {'RUNNING_MODAL'}


    def finish(self):
        # bpy.types.SpaceView3D.draw_handler_remove(self.draw_handler, "WINDOW")
        self.remove_ui()
        infobar.remove(self)
        return {"FINISHED"}


    def draw_ui(self, context):
        method_handler(self._draw_ui,
            arguments = (context, ),
            identifier = 'Bool Regular Scroll UI Shader',
            exit_method = self.remove_ui)


    def _draw_ui(self, context):
        x, y = self.start_mouse_position
        object = context.active_object

        set_drawing_dpi(get_dpi())
        factor = get_dpi_factor()


        offset = 5

        l1 = (-1, 23, 4, 44)
        l2 = (46, 23, 4, 220)
        l4 = (46, 42, 26, 220)
        vertices = (
            (x + (l1[0] - offset) * factor, y + l1[1] * factor), (x + l1[0] * factor, y + l1[2] * factor), (x + (l1[3] - offset) * factor, y + l1[1] * factor), (x + l1[3] * factor, y + l1[2] * factor),
            (x + (l2[0] - offset) * factor, y + l2[1] * factor), (x + l2[0] * factor, y + l2[2] * factor), (x + (l2[3] - offset) * factor, y + l2[1] * factor), (x + l2[3] * factor, y + l2[2] * factor),
            (x + (l4[0] - offset) * factor, y + l4[1] * factor), (x + l4[0] * factor, y + l4[2] * factor), (x + (l4[3] - offset) * factor, y + l4[1] * factor), (x + l4[3] * factor, y + l4[2] * factor))

        l1 = (l1[0] - 15, l1[1], l1[2], l1[0] - 6)

        vertices2 = (
            (x + (l1[0] - offset) * factor, y + l1[1] * factor), (x + l1[0] * factor, y + l1[2] * factor), (x + (l1[3] - offset) * factor, y + l1[1] * factor), (x + l1[3] * factor, y + l1[2] * factor))

        indices = (
            (0, 1, 2), (1, 2, 3), (4, 5, 6), (5, 6, 7), (8, 9, 10), (9, 10, 11))

        indices2 = (
            (0, 1, 2), (1, 2, 3))

        built_in_shader = 'UNIFORM_COLOR' if bpy.app.version[0] >=4 else '2D_UNIFORM_COLOR'
        shader = gpu.shader.from_builtin(built_in_shader)
        batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)

        shader.bind()
        shader.uniform_float("color", addon.preference().color.Hops_hud_color)
        gpu.state.blend_set('ALPHA')
        batch.draw(shader)
        gpu.state.blend_set('NONE')

        shader2 = gpu.shader.from_builtin(built_in_shader)
        batch2 = batch_for_shader(shader2, 'TRIS', {"pos": vertices2}, indices=indices2)
        shader2.bind()
        shader2.uniform_float("color", addon.preference().color.Hops_hud_help_color)

        gpu.state.blend_set('ALPHA')
        batch2.draw(shader2)
        gpu.state.blend_set('NONE')

        red = [1, 0.15, 0.15, 0.9]

        draw_text("{}".format(self.bool_index),
                  x + 25 * factor, y + 9 * factor, size=12, color=addon.preference().color.Hops_hud_text_color)

        if list(self.bools.keys())[self.bool_index] is not None:
            mod = list(self.bools.keys())[self.bool_index]
            name_color = addon.preference().color.Hops_hud_help_color
            if not mod.show_viewport and self.additive:
                name_color = red

            if hasattr(mod, "object"):
                draw_text("{}".format(mod.object.name),
                          x + 50 * factor, y + 9 * factor, size=12, color=addon.preference().color.Hops_hud_text_color)

            draw_text("{}".format(mod.name),
                      x + 50 * factor, y + 28 * factor, size=12, color=(name_color))

        self.draw_help(context, x, y, factor)


    def draw_help(self, context, x, y, factor):

        color_text2 = addon.preference().color.Hops_hud_help_color

        if addon.preference().property.hops_modal_help:

            draw_text(" scroll - change boolean visibility",
                      x + 45 * factor, y - 14 * factor, size=11, color=color_text2)

            draw_text(" a - toggle current visibility",
                      x + 45 * factor, y - 26 * factor, size=11, color=color_text2)

            draw_text(" m - use only booleans / all modifiers",
                      x + 45 * factor, y - 38 * factor, size=11, color=color_text2)

            draw_text(" l - toggle looping",
                      x + 45 * factor, y - 50 * factor, size=11, color=color_text2)

        else:
            draw_text(" H - Show/Hide Help",
                      x + 45 * factor, y - 14 * factor, size=11, color=color_text2)


    def remove_ui(self):
        if self.draw_handler:
            self.draw_handler = bpy.types.SpaceView3D.draw_handler_remove(self.draw_handler, "WINDOW")