diff options
Diffstat (limited to 'object_collection_manager/qcd_move_widget.py')
-rw-r--r-- | object_collection_manager/qcd_move_widget.py | 969 |
1 files changed, 969 insertions, 0 deletions
diff --git a/object_collection_manager/qcd_move_widget.py b/object_collection_manager/qcd_move_widget.py new file mode 100644 index 00000000..442d8cfb --- /dev/null +++ b/object_collection_manager/qcd_move_widget.py @@ -0,0 +1,969 @@ +# ##### 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 + +import time +from math import cos, sin, pi, floor +import bpy +import bgl +import blf +import gpu +from gpu_extras.batch import batch_for_shader + +from bpy.types import Operator +from .internals import ( + layer_collections, + qcd_slots, + ) +from . import qcd_operators + +def spacer(): + spacer = 10 + return round(spacer * scale_factor()) + +def scale_factor(): + return bpy.context.preferences.system.ui_scale + +def get_coords(area): + x = area["vert"][0] + y = area["vert"][1] + w = area["width"] + h = area["height"] + + vertices = ( + (x, y-h), # bottom left + (x+w, y-h), # bottom right + (x, y), # top left + (x+w, y)) # top right + + indices = ( + (0, 1, 2), (2, 1, 3)) + + return vertices, indices + +def get_x_coords(area): + x = area["vert"][0] + y = area["vert"][1] + w = area["width"] + h = area["height"] + + vertices = ( + (x, y), # top left A + (x+(w*0.1), y), # top left B + (x+w, y), # top right A + (x+w-(w*0.1), y), # top right B + (x, y-h), # bottom left A + (x+(w*0.1), y-h), # bottom left B + (x+w, y-h), # bottom right A + (x+w-(w*0.1), y-h), # bottom right B + (x+(w/2)-(w*0.05), y-(h/2)), # center left + (x+(w/2)+(w*0.05), y-(h/2)) # center right + ) + + indices = ( + (0,1,8), (1,8,9), # top left bar + (2,3,9), (3,9,8), # top right bar + (4,5,8), (5,8,9), # bottom left bar + (6,7,8), (6,9,8) # bottom right bar + ) + + return vertices, indices + +def get_circle_coords(area): + # set x, y to center + x = area["vert"][0] + area["width"] / 2 + y = area["vert"][1] - area["width"] / 2 + radius = area["width"] / 2 + sides = 32 + vertices = [(radius * cos(side * 2 * pi / sides) + x, + radius * sin(side * 2 * pi / sides) + y) + for side in range(sides + 1)] + + return vertices + +def draw_rounded_rect(area, shader, color, tl=5, tr=5, bl=5, br=5, outline=False): + sides = 32 + + tl = round(tl * scale_factor()) + tr = round(tr * scale_factor()) + bl = round(bl * scale_factor()) + br = round(br * scale_factor()) + + bgl.glEnable(bgl.GL_BLEND) + + if outline: + thickness = round(2 * scale_factor()) + thickness = max(thickness, 2) + + bgl.glLineWidth(thickness) + bgl.glEnable(bgl.GL_LINE_SMOOTH) + bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST) + + draw_type = 'TRI_FAN' if not outline else 'LINE_STRIP' + + # top left corner + vert_x = area["vert"][0] + tl + vert_y = area["vert"][1] - tl + tl_vert = (vert_x, vert_y) + vertices = [(vert_x, vert_y)] if not outline else [] + + for side in range(sides+1): + if (8<=side<=16): + cosine = tl * cos(side * 2 * pi / sides) + vert_x + sine = tl * sin(side * 2 * pi / sides) + vert_y + vertices.append((cosine,sine)) + + batch = batch_for_shader(shader, draw_type, {"pos": vertices}) + shader.bind() + shader.uniform_float("color", color) + batch.draw(shader) + + # top right corner + vert_x = area["vert"][0] + area["width"] - tr + vert_y = area["vert"][1] - tr + tr_vert = (vert_x, vert_y) + vertices = [(vert_x, vert_y)] if not outline else [] + + for side in range(sides+1): + if (0<=side<=8): + cosine = tr * cos(side * 2 * pi / sides) + vert_x + sine = tr * sin(side * 2 * pi / sides) + vert_y + vertices.append((cosine,sine)) + + batch = batch_for_shader(shader, draw_type, {"pos": vertices}) + shader.bind() + shader.uniform_float("color", color) + batch.draw(shader) + + # bottom left corner + vert_x = area["vert"][0] + bl + vert_y = area["vert"][1] - area["height"] + bl + bl_vert = (vert_x, vert_y) + vertices = [(vert_x, vert_y)] if not outline else [] + + for side in range(sides+1): + if (16<=side<=24): + cosine = bl * cos(side * 2 * pi / sides) + vert_x + sine = bl * sin(side * 2 * pi / sides) + vert_y + vertices.append((cosine,sine)) + + batch = batch_for_shader(shader, draw_type, {"pos": vertices}) + shader.bind() + shader.uniform_float("color", color) + batch.draw(shader) + + # bottom right corner + vert_x = area["vert"][0] + area["width"] - br + vert_y = area["vert"][1] - area["height"] + br + br_vert = (vert_x, vert_y) + vertices = [(vert_x, vert_y)] if not outline else [] + + for side in range(sides+1): + if (24<=side<=32): + cosine = br * cos(side * 2 * pi / sides) + vert_x + sine = br * sin(side * 2 * pi / sides) + vert_y + vertices.append((cosine,sine)) + + batch = batch_for_shader(shader, draw_type, {"pos": vertices}) + shader.bind() + shader.uniform_float("color", color) + batch.draw(shader) + + if not outline: + vertices = [] + indices = [] + base_ind = 0 + + # left edge + width = max(tl, bl) + le_x = tl_vert[0]-tl + vertices.extend([ + (le_x, tl_vert[1]), + (le_x+width, tl_vert[1]), + (le_x, bl_vert[1]), + (le_x+width, bl_vert[1]) + ]) + indices.extend([ + (base_ind,base_ind+1,base_ind+2), + (base_ind+2,base_ind+3,base_ind+1) + ]) + base_ind += 4 + + # right edge + width = max(tr, br) + re_x = tr_vert[0]+tr + vertices.extend([ + (re_x, tr_vert[1]), + (re_x-width, tr_vert[1]), + (re_x, br_vert[1]), + (re_x-width, br_vert[1]) + ]) + indices.extend([ + (base_ind,base_ind+1,base_ind+2), + (base_ind+2,base_ind+3,base_ind+1) + ]) + base_ind += 4 + + # top edge + width = max(tl, tr) + te_y = tl_vert[1]+tl + vertices.extend([ + (tl_vert[0], te_y), + (tl_vert[0], te_y-width), + (tr_vert[0], te_y), + (tr_vert[0], te_y-width) + ]) + indices.extend([ + (base_ind,base_ind+1,base_ind+2), + (base_ind+2,base_ind+3,base_ind+1) + ]) + base_ind += 4 + + # bottom edge + width = max(bl, br) + be_y = bl_vert[1]-bl + vertices.extend([ + (bl_vert[0], be_y), + (bl_vert[0], be_y+width), + (br_vert[0], be_y), + (br_vert[0], be_y+width) + ]) + indices.extend([ + (base_ind,base_ind+1,base_ind+2), + (base_ind+2,base_ind+3,base_ind+1) + ]) + base_ind += 4 + + # middle + vertices.extend([ + tl_vert, + tr_vert, + bl_vert, + br_vert + ]) + indices.extend([ + (base_ind,base_ind+1,base_ind+2), + (base_ind+2,base_ind+3,base_ind+1) + ]) + + batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices) + + shader.uniform_float("color", color) + batch.draw(shader) + + else: + overlap = round(thickness / 2 - scale_factor() / 2) + + # left edge + le_x = tl_vert[0]-tl + vertices = [ + (le_x, tl_vert[1] + (overlap if tl == 0 else 0)), + (le_x, bl_vert[1] - (overlap if bl == 0 else 0)) + ] + + batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices}) + batch.draw(shader) + + # right edge + re_x = tr_vert[0]+tr + vertices = [ + (re_x, tr_vert[1] + (overlap if tr == 0 else 0)), + (re_x, br_vert[1] - (overlap if br == 0 else 0)) + ] + + batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices}) + batch.draw(shader) + + # top edge + te_y = tl_vert[1]+tl + vertices = [ + (tl_vert[0] - (overlap if tl == 0 else 0), te_y), + (tr_vert[0] + (overlap if tr == 0 else 0), te_y) + ] + + batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices}) + batch.draw(shader) + + # bottom edge + be_y = bl_vert[1]-bl + vertices = [ + (bl_vert[0] - (overlap if bl == 0 else 0), be_y), + (br_vert[0] + (overlap if br == 0 else 0), be_y) + ] + + batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices}) + batch.draw(shader) + + bgl.glDisable(bgl.GL_LINE_SMOOTH) + + bgl.glDisable(bgl.GL_BLEND) + +def mouse_in_area(mouse_pos, area, buf = 0): + x = mouse_pos[0] + y = mouse_pos[1] + + # check left + if x+buf < area["vert"][0]: + return False + + # check right + if x-buf > area["vert"][0] + area["width"]: + return False + + # check top + if y-buf > area["vert"][1]: + return False + + # check bottom + if y+buf < area["vert"][1] - area["height"]: + return False + + # if we reach here we're in the area + return True + +def account_for_view_bounds(area): + # make sure it renders in the 3d view + # left + if area["vert"][0] < 0: + x = 0 + y = area["vert"][1] + + area["vert"] = (x, y) + + # right + if area["vert"][0] + area["width"] > bpy.context.region.width: + x = bpy.context.region.width - area["width"] + y = area["vert"][1] + + area["vert"] = (x, y) + + # top + if area["vert"][1] > bpy.context.region.height: + x = area["vert"][0] + y = bpy.context.region.height + + area["vert"] = (x, y) + + # bottom + if area["vert"][1] - area["height"] < 0: + x = area["vert"][0] + y = area["height"] + + area["vert"] = (x, y) + +def update_area_dimensions(area, w=0, h=0): + area["width"] += w + area["height"] += h + +class QCDMoveWidget(Operator): + """QCD Move Widget""" + bl_idname = "view3d.qcd_move_widget" + bl_label = "QCD Move Widget" + + slots = { + "ONE":1, + "TWO":2, + "THREE":3, + "FOUR":4, + "FIVE":5, + "SIX":6, + "SEVEN":7, + "EIGHT":8, + "NINE":9, + "ZERO":10, + } + + last_type = '' + moved = False + + def modal(self, context, event): + if event.type == 'TIMER': + if self.hover_time and self.hover_time + 0.5 < time.time(): + self.draw_tooltip = True + + context.area.tag_redraw() + return {'RUNNING_MODAL'} + + + context.area.tag_redraw() + + if len(self.areas) == 1: + return {'RUNNING_MODAL'} + + if self.last_type == 'LEFTMOUSE' and event.value == 'PRESS' and event.type == 'MOUSEMOVE': + if mouse_in_area(self.mouse_pos, self.areas["Grab Bar"]): + x_offset = self.areas["Main Window"]["vert"][0] - self.mouse_pos[0] + x = event.mouse_region_x + x_offset + + y_offset = self.areas["Main Window"]["vert"][1] - self.mouse_pos[1] + y = event.mouse_region_y + y_offset + + self.areas["Main Window"]["vert"] = (x, y) + + self.mouse_pos = (event.mouse_region_x, event.mouse_region_y) + + elif event.type == 'MOUSEMOVE': + self.draw_tooltip = False + self.hover_time = None + self.mouse_pos = (event.mouse_region_x, event.mouse_region_y) + + if not mouse_in_area(self.mouse_pos, self.areas["Main Window"], 50 * scale_factor()): + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + + if self.moved: + bpy.ops.ed.undo_push() + + return {'FINISHED'} + + elif event.value == 'PRESS' and event.type == 'LEFTMOUSE': + if not mouse_in_area(self.mouse_pos, self.areas["Main Window"], 10 * scale_factor()): + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + + if self.moved: + bpy.ops.ed.undo_push() + + return {'FINISHED'} + + for num in range(20): + if not self.areas.get(f"Button {num + 1}", None): + break + + if mouse_in_area(self.mouse_pos, self.areas[f"Button {num + 1}"]): + bpy.ops.view3d.move_to_qcd_slot(slot=str(num + 1), toggle=event.shift) + self.moved = True + + elif event.type in {'RIGHTMOUSE', 'ESC'}: + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + + return {'CANCELLED'} + + if event.value == 'PRESS' and event.type in self.slots: + move_to = self.slots[event.type] + + if event.alt: + move_to += 10 + + if event.shift: + bpy.ops.view3d.move_to_qcd_slot(slot=str(move_to), toggle=True) + else: + bpy.ops.view3d.move_to_qcd_slot(slot=str(move_to), toggle=False) + + self.moved = True + + if event.type != 'MOUSEMOVE' and event.type != 'INBETWEEN_MOUSEMOVE': + self.last_type = event.type + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + if context.area.type == 'VIEW_3D': + # the arguments we pass the the callback + args = (self, context) + # Add the region OpenGL drawing callback + # draw in view space with 'POST_VIEW' and 'PRE_VIEW' + self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') + self._timer = context.window_manager.event_timer_add(0.1, window=context.window) + + self.mouse_pos = (event.mouse_region_x, event.mouse_region_y) + + self.draw_tooltip = False + + self.hover_time = None + + self.areas = {} + + # MAIN WINDOW BACKGROUND + x = self.mouse_pos[0] - spacer()*2 + y = self.mouse_pos[1] + spacer()*2 + main_window = { + # Top Left Vertex + "vert": (x,y), + "width": 0, + "height": 0, + "value": None + } + account_for_view_bounds(main_window) + + # add main window background to areas + self.areas["Main Window"] = main_window + + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} + else: + self.report({'WARNING'}, "View3D not found, cannot run operator") + return {'CANCELLED'} + + +def allocate_main_ui(self, context): + main_window = self.areas["Main Window"] + self.areas.clear() + main_window["width"] = 0 + main_window["height"] = 0 + self.areas["Main Window"] = main_window + + cur_width_pos = main_window["vert"][0] + cur_height_pos = main_window["vert"][1] + + # GRAB BAR + grab_bar = { + "vert": main_window["vert"], + "width": 0, + "height": round(23 * scale_factor()), + "value": None + } + + # add grab bar to areas + self.areas["Grab Bar"] = grab_bar + + + # WINDOW TITLE + wt_indent_x = spacer()*2 + wt_y_offset = round(spacer()/2) + window_title = { + "vert": main_window["vert"], + "width": 0, + "height": round(13 * scale_factor()), + "value": "Move Objects to QCD Slots" + } + + x = main_window["vert"][0] + wt_indent_x + y = main_window["vert"][1] - window_title["height"] - wt_y_offset + window_title["vert"] = (x, y) + + # add window title to areas + self.areas["Window Title"] = window_title + + cur_height_pos = window_title["vert"][1] + + + # MAIN BUTTON AREA + button_size = round(20 * scale_factor()) + button_gap = round(1 * scale_factor()) + button_group = 5 + button_group_gap = round(20 * scale_factor()) + button_group_width = button_size * button_group + button_gap * (button_group - 1) + + mba_indent_x = spacer()*2 + mba_outdent_x = spacer()*2 + mba_indent_y = spacer() + x = cur_width_pos + mba_indent_x + y = cur_height_pos - mba_indent_y + main_button_area = { + "vert": (x, y), + "width": 0, + "height": 0, + "value": None + } + + # add main button area to areas + self.areas["Main Button Area"] = main_button_area + + # update current position + cur_width_pos = main_button_area["vert"][0] + cur_height_pos = main_button_area["vert"][1] + + + # BUTTON ROW 1 A + button_row_1_a = { + "vert": main_button_area["vert"], + "width": button_group_width, + "height": button_size, + "value": None + } + + # add button row 1 A to areas + self.areas["Button Row 1 A"] = button_row_1_a + + # advance width pos to start of next row + cur_width_pos += button_row_1_a["width"] + cur_width_pos += button_group_gap + + # BUTTON ROW 1 B + x = cur_width_pos + y = cur_height_pos + button_row_1_b = { + "vert": (x, y), + "width": button_group_width, + "height": button_size, + "value": None + } + + # add button row 1 B to areas + self.areas["Button Row 1 B"] = button_row_1_b + + # reset width pos to start of main button area + cur_width_pos = main_button_area["vert"][0] + # update height pos + cur_height_pos -= button_row_1_a["height"] + # add gap between button rows + cur_height_pos -= button_gap + + + # BUTTON ROW 2 A + x = cur_width_pos + y = cur_height_pos + button_row_2_a = { + "vert": (x, y), + "width": button_group_width, + "height": button_size, + "value": None + } + + # add button row 2 A to areas + self.areas["Button Row 2 A"] = button_row_2_a + + # advance width pos to start of next row + cur_width_pos += button_row_2_a["width"] + cur_width_pos += button_group_gap + + # BUTTON ROW 2 B + x = cur_width_pos + y = cur_height_pos + button_row_2_b = { + "vert": (x, y), + "width": button_group_width, + "height": button_size, + "value": None + } + + # add button row 2 B to areas + self.areas["Button Row 2 B"] = button_row_2_b + + + # BUTTONS + def get_buttons(button_row, row_num): + cur_width_pos = button_row["vert"][0] + cur_height_pos = button_row["vert"][1] + for num in range(button_group): + slot_num = row_num + num + + qcd_slot = qcd_slots.get_name(f"{slot_num}") + + if qcd_slot: + qcd_laycol = layer_collections[qcd_slot]["ptr"] + collection_objects = qcd_laycol.collection.objects + selected_objects = qcd_operators.get_move_selection() + active_object = qcd_operators.get_move_active() + + # BUTTON + x = cur_width_pos + y = cur_height_pos + button = { + "vert": (x, y), + "width": button_size, + "height": button_size, + "value": slot_num + } + + self.areas[f"Button {slot_num}"] = button + + # ACTIVE OBJECT ICON + if active_object and active_object in selected_objects and active_object.name in collection_objects: + x = cur_width_pos + round(button_size / 4) + y = cur_height_pos - round(button_size / 4) + active_object_indicator = { + "vert": (x, y), + "width": floor(button_size / 2), + "height": floor(button_size / 2), + "value": None + } + + self.areas[f"Button {slot_num} Active Object Indicator"] = active_object_indicator + + elif not set(selected_objects).isdisjoint(collection_objects): + x = cur_width_pos + round(button_size / 4) + floor(1 * scale_factor()) + y = cur_height_pos - round(button_size / 4) - floor(1 * scale_factor()) + selected_object_indicator = { + "vert": (x, y), + "width": floor(button_size / 2) - floor(1 * scale_factor()), + "height": floor(button_size / 2) - floor(1 * scale_factor()), + "value": None + } + + self.areas[f"Button {slot_num} Selected Object Indicator"] = selected_object_indicator + + elif collection_objects: + x = cur_width_pos + floor(button_size / 4) + y = cur_height_pos - button_size / 2 + 1 * scale_factor() + object_indicator = { + "vert": (x, y), + "width": round(button_size / 2), + "height": round(2 * scale_factor()), + "value": None + } + self.areas[f"Button {slot_num} Object Indicator"] = object_indicator + + else: + x = cur_width_pos + 2 * scale_factor() + y = cur_height_pos - 2 * scale_factor() + X_icon = { + "vert": (x, y), + "width": button_size - 4 * scale_factor(), + "height": button_size - 4 * scale_factor(), + "value": None + } + + self.areas[f"X_icon {slot_num}"] = X_icon + + cur_width_pos += button_size + cur_width_pos += button_gap + + get_buttons(button_row_1_a, 1) + get_buttons(button_row_1_b, 6) + get_buttons(button_row_2_a, 11) + get_buttons(button_row_2_b, 16) + + + # UPDATE DYNAMIC DIMENSIONS + width = button_row_1_a["width"] + button_group_gap + button_row_1_b["width"] + height = button_row_1_a["height"] + button_gap + button_row_2_a["height"] + update_area_dimensions(main_button_area, width, height) + + width = main_button_area["width"] + mba_indent_x + mba_outdent_x + height = main_button_area["height"] + mba_indent_y * 2 + window_title["height"] + wt_y_offset + update_area_dimensions(main_window, width, height) + + update_area_dimensions(grab_bar, main_window["width"]) + + +def draw_callback_px(self, context): + allocate_main_ui(self, context) + + shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') + shader.bind() + + addon_prefs = context.preferences.addons[__package__].preferences + + # main window background + main_window = self.areas["Main Window"] + outline_color = addon_prefs.qcd_ogl_widget_menu_back_outline + background_color = addon_prefs.qcd_ogl_widget_menu_back_inner + draw_rounded_rect(main_window, shader, outline_color[:] + (1,), outline=True) + draw_rounded_rect(main_window, shader, background_color) + + # draw window title + window_title = self.areas["Window Title"] + x = window_title["vert"][0] + y = window_title["vert"][1] + h = window_title["height"] + text = window_title["value"] + text_color = addon_prefs.qcd_ogl_widget_menu_back_text + font_id = 0 + blf.position(font_id, x, y, 0) + blf.size(font_id, int(h), 72) + blf.color(font_id, text_color[0], text_color[1], text_color[2], 1) + blf.draw(font_id, text) + + # refresh shader - not sure why this is needed + shader.bind() + + in_tooltip_area = False + + for num in range(20): + slot_num = num + 1 + qcd_slot = qcd_slots.get_name(f"{slot_num}") + if qcd_slot: + qcd_laycol = layer_collections[qcd_slot]["ptr"] + collection_objects = qcd_laycol.collection.objects + selected_objects = qcd_operators.get_move_selection() + active_object = qcd_operators.get_move_active() + button_area = self.areas[f"Button {slot_num}"] + + # colors + button_color = addon_prefs.qcd_ogl_widget_tool_inner + icon_color = addon_prefs.qcd_ogl_widget_tool_text + if not qcd_laycol.exclude: + button_color = addon_prefs.qcd_ogl_widget_tool_inner_sel + icon_color = addon_prefs.qcd_ogl_widget_tool_text_sel + + if mouse_in_area(self.mouse_pos, button_area): + in_tooltip_area = True + + mod = 0.1 + + if button_color[0] + mod > 1 or button_color[1] + mod > 1 or button_color[2] + mod > 1: + mod = -mod + + button_color = ( + button_color[0] + mod, + button_color[1] + mod, + button_color[2] + mod, + button_color[3] + ) + + + # button roundness + tl = tr = bl = br = 0 + rounding = 5 + + if num < 10: + if not f"{num+2}" in qcd_slots: + tr = rounding + + if not f"{num}" in qcd_slots: + tl = rounding + else: + if not f"{num+2}" in qcd_slots: + br = rounding + + if not f"{num}" in qcd_slots: + bl = rounding + + if num in [0,5]: + tl = rounding + elif num in [4,9]: + tr = rounding + elif num in [10,15]: + bl = rounding + elif num in [14,19]: + br = rounding + + # draw button + outline_color = addon_prefs.qcd_ogl_widget_tool_outline + draw_rounded_rect(button_area, shader, outline_color[:] + (1,), tl, tr, bl, br, outline=True) + draw_rounded_rect(button_area, shader, button_color, tl, tr, bl, br) + + # ACTIVE OBJECT + if active_object and active_object in selected_objects and active_object.name in collection_objects: + active_object_indicator = self.areas[f"Button {slot_num} Active Object Indicator"] + + vertices = get_circle_coords(active_object_indicator) + shader.uniform_float("color", icon_color[:] + (1,)) + batch = batch_for_shader(shader, 'TRI_FAN', {"pos": vertices}) + + bgl.glEnable(bgl.GL_BLEND) + + batch.draw(shader) + + bgl.glDisable(bgl.GL_BLEND) + + # SELECTED OBJECTS + elif not set(selected_objects).isdisjoint(collection_objects): + selected_object_indicator = self.areas[f"Button {slot_num} Selected Object Indicator"] + + alpha = addon_prefs.qcd_ogl_selected_icon_alpha + vertices = get_circle_coords(selected_object_indicator) + shader.uniform_float("color", icon_color[:] + (alpha,)) + batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices}) + + bgl.glLineWidth(2 * scale_factor()) + bgl.glEnable(bgl.GL_BLEND) + bgl.glEnable(bgl.GL_LINE_SMOOTH) + bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST) + + batch.draw(shader) + + bgl.glDisable(bgl.GL_LINE_SMOOTH) + bgl.glDisable(bgl.GL_BLEND) + + # OBJECTS + elif collection_objects: + object_indicator = self.areas[f"Button {slot_num} Object Indicator"] + + alpha = addon_prefs.qcd_ogl_objects_icon_alpha + vertices, indices = get_coords(object_indicator) + shader.uniform_float("color", icon_color[:] + (alpha,)) + batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices) + + bgl.glEnable(bgl.GL_BLEND) + + batch.draw(shader) + + bgl.glDisable(bgl.GL_BLEND) + + + # X ICON + else: + X_icon = self.areas[f"X_icon {slot_num}"] + X_icon_color = addon_prefs.qcd_ogl_widget_menu_back_text + + vertices, indices = get_x_coords(X_icon) + shader.uniform_float("color", X_icon_color[:] + (1,)) + batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices) + + bgl.glEnable(bgl.GL_BLEND) + bgl.glEnable(bgl.GL_POLYGON_SMOOTH) + bgl.glHint(bgl.GL_POLYGON_SMOOTH_HINT, bgl.GL_NICEST) + + batch.draw(shader) + + bgl.glDisable(bgl.GL_POLYGON_SMOOTH) + bgl.glDisable(bgl.GL_BLEND) + + if in_tooltip_area: + if self.draw_tooltip: + draw_tooltip(self, context, shader,"Move Object To QCD Slot\n * Shift-Click to toggle objects\' slot") + self.hover_time = None + + else: + if not self.hover_time: + self.hover_time = time.time() + + +def draw_tooltip(self, context, shader, message): + addon_prefs = context.preferences.addons[__package__].preferences + + font_id = 0 + line_height = 11 * scale_factor() + text_color = addon_prefs.qcd_ogl_widget_tooltip_text + blf.size(font_id, int(line_height), 72) + blf.color(font_id, text_color[0], text_color[1], text_color[2], 1) + + lines = message.split("\n") + longest = [0,""] + num_lines = len(lines) + + for line in lines: + if len(line) > longest[0]: + longest[0] = len(line) + longest[1] = line + + w, h = blf.dimensions(font_id, longest[1]) + + line_spacer = 1 * scale_factor() + padding = 4 * scale_factor() + + # draw background + tooltip = { + "vert": self.mouse_pos, + "width": w + spacer()*2, + "height": (line_height * num_lines + line_spacer * num_lines) + padding*3, + "value": None + } + + x = tooltip["vert"][0] - spacer()*2 + y = tooltip["vert"][1] + tooltip["height"] + round(5 * scale_factor()) + tooltip["vert"] = (x, y) + + account_for_view_bounds(tooltip) + + outline_color = addon_prefs.qcd_ogl_widget_tooltip_outline + background_color = addon_prefs.qcd_ogl_widget_tooltip_inner + draw_rounded_rect(tooltip, shader, outline_color[:] + (1,), outline=True) + draw_rounded_rect(tooltip, shader, background_color) + + line_pos = padding + line_height + # draw text + for num, line in enumerate(lines): + x = tooltip["vert"][0] + spacer() + y = tooltip["vert"][1] - line_pos + blf.position(font_id, x, y, 0) + blf.draw(font_id, line) + + line_pos += line_height + line_spacer |