OverviewFilesCommitsIssuesDocsReleasesPeers

hardops / operators / modals / template.py

237 lines · python ref:
import bpy, bmesh, gpu
from gpu_extras.batch import batch_for_shader
from ... graphics.drawing2d import draw_text, set_drawing_dpi
from ... utils.blender_ui import get_dpi, get_dpi_factor
from ... utility import addon
from ... utility import modifier
from ... utility.addon import method_handler


class HOPS_OT_MOD_Template(bpy.types.Operator):
    bl_idname = "hops.mod_template"
    bl_label = "Modal Operator Template"
    bl_options = {'REGISTER', 'UNDO', 'BLOCKING', 'GRAB_CURSOR'}
    bl_description = """
LMB - Adjust some modifier
LMB + Ctrl - Create a new modifier
LMB + Shift - Some special behavior

Press H for help
"""

    @classmethod
    def poll(cls, context):
        obj = context.active_object
        return obj and obj.type == 'MESH' and obj.mode == 'OBJECT'


    def invoke(self, context, event):
        self.modal_scale = addon.preference().ui.Hops_modal_scale

        self.create_new = event.ctrl
        self.special_behavior = event.shift

        self.obj = context.active_object
        self.mods = [m for m in self.obj.modifiers if m.type == 'TYPE_HERE']

        if not self.mods:
            self.create_new = True

        if self.create_new:
            self.mod = self.obj.modifiers.new("Name Here", 'TYPE_HERE')
            self.mods.append(self.mod)

        else:
            self.mod = self.mods[-1]

        self.values = {m:{} for m in self.mods}
        for mod in self.mods:
            self.store(mod)

        self.buffer = self.mod.some_value
        self.mouse_prev_x = event.mouse_region_x

        self.mouse_start_x = event.mouse_region_x
        self.mouse_start_y = event.mouse_region_y

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


    def modal(self, context, event):
        if event.type == 'MIDDLEMOUSE':
            return {'PASS_THROUGH'}

        elif event.type == 'Z' and (event.shift or event.alt):
            return {'PASS_THROUGH'}

        elif event.type in {'LEFTMOUSE', 'SPACE'}:
            self.remove_draw_handler(context)
            self.report({'INFO'}, "Finished")
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            self.remove_draw_handler(context)
            self.cancel(context)
            self.report({'INFO'}, "Cancelled")
            return {'CANCELLED'}

        elif event.type in {'WHEELDOWNMOUSE', 'WHEELUPMOUSE'} and event.shift:
            if event.type == 'WHEELDOWNMOUSE':
                self.scroll(1)
            elif event.type == 'WHEELUPMOUSE':
                self.scroll(-1)
            self.buffer = self.mod.some_value
            self.report({'INFO'}, f"Scrolled to {self.mod.name}")

        elif event.type == 'MOUSEMOVE':
            divisor = self.modal_scale * (10000 if event.shift else 1000)
            offset = event.mouse_region_x - self.mouse_prev_x
            self.buffer -= offset / divisor / get_dpi_factor()
            self.buffer = max(self.buffer, 0)
            digits = 2 if event.ctrl and event.shift else 1 if event.ctrl else 3
            self.mod.some_value = round(self.buffer, digits)

        elif event.type == "H" and event.value == "PRESS":
            addon.preference().property.hops_modal_help = not addon.preference().property.hops_modal_help
            context.area.tag_redraw()
            self.report({'INFO'}, f"{'Show' if addon.preference().property.hops_modal_help else 'Hide'} Help")

        self.mouse_prev_x = event.mouse_region_x
        context.area.tag_redraw()
        return {'RUNNING_MODAL'}


    def cancel(self, context):
        for mod in self.mods:
            self.reset(mod)

        if self.create_new:
            self.obj.modifiers.remove(self.mods[-1])


    def scroll(self, direction):
        index = self.mods.index(self.mod)
        index = (index + direction) % len(self.mods)
        self.mod = self.mods[index]


    def store(self, mod):
        self.values[mod]["some_value"] = mod.some_value


    def reset(self, mod):
        mod.some_value = self.values[mod]["some_value"]


    def add_draw_handler(self, context):
        self.draw_handler = bpy.types.SpaceView3D.draw_handler_add(self.draw_ui, (context, ), "WINDOW", "POST_PIXEL")
        context.area.header_text_set(text=None)
        context.area.tag_redraw()


    def remove_draw_handler(self, context):
        self.remove_ui()
        context.area.header_text_set(text=None)
        context.area.tag_redraw()


    def draw_ui(self, context):
        method_handler(self._draw_ui,
            arguments = (context, ),
            identifier = f'{self.bl_label} UI Shader',
            exit_method = self.remove_ui)


    def _draw_ui(self, context):
        x = self.mouse_start_x
        y = self.mouse_start_y

        c1 = addon.preference().color.Hops_hud_color
        c2 = addon.preference().color.Hops_hud_help_color
        c3 = addon.preference().color.Hops_hud_text_color

        set_drawing_dpi(get_dpi())
        f = get_dpi_factor()
        o = 5

        l1 = (3, 23, 4, 44)
        l2 = (46, 23, 4, 146)
        l3 = (149, 23, 4, 280)

        vertices = (
            (x + (l1[0] - o) * f, y + l1[1] * f),
            (x + l1[0] * f, y + l1[2] * f),
            (x + (l1[3] - o) * f, y + l1[1] * f),
            (x + l1[3] * f, y + l1[2] * f),

            (x + (l2[0] - o) * f, y + l2[1] * f),
            (x + l2[0] * f, y + l2[2] * f),
            (x + (l2[3] - o) * f, y + l2[1] * f),
            (x + l2[3] * f, y + l2[2] * f),

            (x + (l3[0] - o) * f, y + l3[1] * f),
            (x + l3[0] * f, y + l3[2] * f),
            (x + (l3[3] - o) * f, y + l3[1] * f),
            (x + l3[3] * f, y + l3[2] * f))

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

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

        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", c1)

        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", c2)

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

        draw_text(f"{self.mod.some_value}", x + 10 * f, y + 9 * f, size=12, color=c3)

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


    def draw_help(self, context, x, y, f):
        c2 = addon.preference().color.Hops_hud_help_color

        if addon.preference().property.hops_modal_help:
            draw_text(" Key - Adjust Some Value", x + 45 * f, y - 14 * f, size=11, color=c2)
            draw_text(" H - Show/Hide Help", x + 45 * f, y - 26 * f, size=11, color=c2)

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


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