Cleanup: camera_turnaround remove unnecessary lookup
[blender-addons.git] / object_collection_manager / qcd_move_widget.py
blob95e2505872bce66d65681a199fed4b443a4e6898
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # Copyright 2011, Ryan Inch
21 import time
22 from math import cos, sin, pi, floor
23 import bpy
24 import bgl
25 import blf
26 import gpu
27 from gpu_extras.batch import batch_for_shader
29 from bpy.types import Operator
30 from .internals import (
31 layer_collections,
32 qcd_slots,
34 from . import qcd_operators
36 def spacer():
37 spacer = 10
38 return round(spacer * scale_factor())
40 def scale_factor():
41 return bpy.context.preferences.system.ui_scale
43 def get_coords(area):
44 x = area["vert"][0]
45 y = area["vert"][1]
46 w = area["width"]
47 h = area["height"]
49 vertices = (
50 (x, y-h), # bottom left
51 (x+w, y-h), # bottom right
52 (x, y), # top left
53 (x+w, y)) # top right
55 indices = (
56 (0, 1, 2), (2, 1, 3))
58 return vertices, indices
60 def get_x_coords(area):
61 x = area["vert"][0]
62 y = area["vert"][1]
63 w = area["width"]
64 h = area["height"]
66 vertices = (
67 (x, y), # top left A
68 (x+(w*0.1), y), # top left B
69 (x+w, y), # top right A
70 (x+w-(w*0.1), y), # top right B
71 (x, y-h), # bottom left A
72 (x+(w*0.1), y-h), # bottom left B
73 (x+w, y-h), # bottom right A
74 (x+w-(w*0.1), y-h), # bottom right B
75 (x+(w/2)-(w*0.05), y-(h/2)), # center left
76 (x+(w/2)+(w*0.05), y-(h/2)) # center right
79 indices = (
80 (0,1,8), (1,8,9), # top left bar
81 (2,3,9), (3,9,8), # top right bar
82 (4,5,8), (5,8,9), # bottom left bar
83 (6,7,8), (6,9,8) # bottom right bar
86 return vertices, indices
88 def get_circle_coords(area):
89 # set x, y to center
90 x = area["vert"][0] + area["width"] / 2
91 y = area["vert"][1] - area["width"] / 2
92 radius = area["width"] / 2
93 sides = 32
94 vertices = [(radius * cos(side * 2 * pi / sides) + x,
95 radius * sin(side * 2 * pi / sides) + y)
96 for side in range(sides + 1)]
98 return vertices
100 def draw_rounded_rect(area, shader, color, tl=5, tr=5, bl=5, br=5, outline=False):
101 sides = 32
103 tl = round(tl * scale_factor())
104 tr = round(tr * scale_factor())
105 bl = round(bl * scale_factor())
106 br = round(br * scale_factor())
108 bgl.glEnable(bgl.GL_BLEND)
110 if outline:
111 thickness = round(2 * scale_factor())
112 thickness = max(thickness, 2)
114 bgl.glLineWidth(thickness)
115 bgl.glEnable(bgl.GL_LINE_SMOOTH)
116 bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
118 draw_type = 'TRI_FAN' if not outline else 'LINE_STRIP'
120 # top left corner
121 vert_x = area["vert"][0] + tl
122 vert_y = area["vert"][1] - tl
123 tl_vert = (vert_x, vert_y)
124 vertices = [(vert_x, vert_y)] if not outline else []
126 for side in range(sides+1):
127 if (8<=side<=16):
128 cosine = tl * cos(side * 2 * pi / sides) + vert_x
129 sine = tl * sin(side * 2 * pi / sides) + vert_y
130 vertices.append((cosine,sine))
132 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
133 shader.bind()
134 shader.uniform_float("color", color)
135 batch.draw(shader)
137 # top right corner
138 vert_x = area["vert"][0] + area["width"] - tr
139 vert_y = area["vert"][1] - tr
140 tr_vert = (vert_x, vert_y)
141 vertices = [(vert_x, vert_y)] if not outline else []
143 for side in range(sides+1):
144 if (0<=side<=8):
145 cosine = tr * cos(side * 2 * pi / sides) + vert_x
146 sine = tr * sin(side * 2 * pi / sides) + vert_y
147 vertices.append((cosine,sine))
149 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
150 shader.bind()
151 shader.uniform_float("color", color)
152 batch.draw(shader)
154 # bottom left corner
155 vert_x = area["vert"][0] + bl
156 vert_y = area["vert"][1] - area["height"] + bl
157 bl_vert = (vert_x, vert_y)
158 vertices = [(vert_x, vert_y)] if not outline else []
160 for side in range(sides+1):
161 if (16<=side<=24):
162 cosine = bl * cos(side * 2 * pi / sides) + vert_x
163 sine = bl * sin(side * 2 * pi / sides) + vert_y
164 vertices.append((cosine,sine))
166 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
167 shader.bind()
168 shader.uniform_float("color", color)
169 batch.draw(shader)
171 # bottom right corner
172 vert_x = area["vert"][0] + area["width"] - br
173 vert_y = area["vert"][1] - area["height"] + br
174 br_vert = (vert_x, vert_y)
175 vertices = [(vert_x, vert_y)] if not outline else []
177 for side in range(sides+1):
178 if (24<=side<=32):
179 cosine = br * cos(side * 2 * pi / sides) + vert_x
180 sine = br * sin(side * 2 * pi / sides) + vert_y
181 vertices.append((cosine,sine))
183 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
184 shader.bind()
185 shader.uniform_float("color", color)
186 batch.draw(shader)
188 if not outline:
189 vertices = []
190 indices = []
191 base_ind = 0
193 # left edge
194 width = max(tl, bl)
195 le_x = tl_vert[0]-tl
196 vertices.extend([
197 (le_x, tl_vert[1]),
198 (le_x+width, tl_vert[1]),
199 (le_x, bl_vert[1]),
200 (le_x+width, bl_vert[1])
202 indices.extend([
203 (base_ind,base_ind+1,base_ind+2),
204 (base_ind+2,base_ind+3,base_ind+1)
206 base_ind += 4
208 # right edge
209 width = max(tr, br)
210 re_x = tr_vert[0]+tr
211 vertices.extend([
212 (re_x, tr_vert[1]),
213 (re_x-width, tr_vert[1]),
214 (re_x, br_vert[1]),
215 (re_x-width, br_vert[1])
217 indices.extend([
218 (base_ind,base_ind+1,base_ind+2),
219 (base_ind+2,base_ind+3,base_ind+1)
221 base_ind += 4
223 # top edge
224 width = max(tl, tr)
225 te_y = tl_vert[1]+tl
226 vertices.extend([
227 (tl_vert[0], te_y),
228 (tl_vert[0], te_y-width),
229 (tr_vert[0], te_y),
230 (tr_vert[0], te_y-width)
232 indices.extend([
233 (base_ind,base_ind+1,base_ind+2),
234 (base_ind+2,base_ind+3,base_ind+1)
236 base_ind += 4
238 # bottom edge
239 width = max(bl, br)
240 be_y = bl_vert[1]-bl
241 vertices.extend([
242 (bl_vert[0], be_y),
243 (bl_vert[0], be_y+width),
244 (br_vert[0], be_y),
245 (br_vert[0], be_y+width)
247 indices.extend([
248 (base_ind,base_ind+1,base_ind+2),
249 (base_ind+2,base_ind+3,base_ind+1)
251 base_ind += 4
253 # middle
254 vertices.extend([
255 tl_vert,
256 tr_vert,
257 bl_vert,
258 br_vert
260 indices.extend([
261 (base_ind,base_ind+1,base_ind+2),
262 (base_ind+2,base_ind+3,base_ind+1)
265 batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
267 shader.uniform_float("color", color)
268 batch.draw(shader)
270 else:
271 overlap = round(thickness / 2 - scale_factor() / 2)
273 # left edge
274 le_x = tl_vert[0]-tl
275 vertices = [
276 (le_x, tl_vert[1] + (overlap if tl == 0 else 0)),
277 (le_x, bl_vert[1] - (overlap if bl == 0 else 0))
280 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
281 batch.draw(shader)
283 # right edge
284 re_x = tr_vert[0]+tr
285 vertices = [
286 (re_x, tr_vert[1] + (overlap if tr == 0 else 0)),
287 (re_x, br_vert[1] - (overlap if br == 0 else 0))
290 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
291 batch.draw(shader)
293 # top edge
294 te_y = tl_vert[1]+tl
295 vertices = [
296 (tl_vert[0] - (overlap if tl == 0 else 0), te_y),
297 (tr_vert[0] + (overlap if tr == 0 else 0), te_y)
300 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
301 batch.draw(shader)
303 # bottom edge
304 be_y = bl_vert[1]-bl
305 vertices = [
306 (bl_vert[0] - (overlap if bl == 0 else 0), be_y),
307 (br_vert[0] + (overlap if br == 0 else 0), be_y)
310 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
311 batch.draw(shader)
313 bgl.glDisable(bgl.GL_LINE_SMOOTH)
315 bgl.glDisable(bgl.GL_BLEND)
317 def mouse_in_area(mouse_pos, area, buf = 0):
318 x = mouse_pos[0]
319 y = mouse_pos[1]
321 # check left
322 if x+buf < area["vert"][0]:
323 return False
325 # check right
326 if x-buf > area["vert"][0] + area["width"]:
327 return False
329 # check top
330 if y-buf > area["vert"][1]:
331 return False
333 # check bottom
334 if y+buf < area["vert"][1] - area["height"]:
335 return False
337 # if we reach here we're in the area
338 return True
340 def account_for_view_bounds(area):
341 # make sure it renders in the 3d view
342 # left
343 if area["vert"][0] < 0:
344 x = 0
345 y = area["vert"][1]
347 area["vert"] = (x, y)
349 # right
350 if area["vert"][0] + area["width"] > bpy.context.region.width:
351 x = bpy.context.region.width - area["width"]
352 y = area["vert"][1]
354 area["vert"] = (x, y)
356 # top
357 if area["vert"][1] > bpy.context.region.height:
358 x = area["vert"][0]
359 y = bpy.context.region.height
361 area["vert"] = (x, y)
363 # bottom
364 if area["vert"][1] - area["height"] < 0:
365 x = area["vert"][0]
366 y = area["height"]
368 area["vert"] = (x, y)
370 def update_area_dimensions(area, w=0, h=0):
371 area["width"] += w
372 area["height"] += h
374 class QCDMoveWidget(Operator):
375 """QCD Move Widget"""
376 bl_idname = "view3d.qcd_move_widget"
377 bl_label = "QCD Move Widget"
379 slots = {
380 "ONE":1,
381 "TWO":2,
382 "THREE":3,
383 "FOUR":4,
384 "FIVE":5,
385 "SIX":6,
386 "SEVEN":7,
387 "EIGHT":8,
388 "NINE":9,
389 "ZERO":10,
392 last_type = ''
393 moved = False
395 def modal(self, context, event):
396 if event.type == 'TIMER':
397 if self.hover_time and self.hover_time + 0.5 < time.time():
398 self.draw_tooltip = True
400 context.area.tag_redraw()
401 return {'RUNNING_MODAL'}
404 context.area.tag_redraw()
406 if len(self.areas) == 1:
407 return {'RUNNING_MODAL'}
409 if self.last_type == 'LEFTMOUSE' and event.value == 'PRESS' and event.type == 'MOUSEMOVE':
410 if mouse_in_area(self.mouse_pos, self.areas["Grab Bar"]):
411 x_offset = self.areas["Main Window"]["vert"][0] - self.mouse_pos[0]
412 x = event.mouse_region_x + x_offset
414 y_offset = self.areas["Main Window"]["vert"][1] - self.mouse_pos[1]
415 y = event.mouse_region_y + y_offset
417 self.areas["Main Window"]["vert"] = (x, y)
419 self.mouse_pos = (event.mouse_region_x, event.mouse_region_y)
421 elif event.type == 'MOUSEMOVE':
422 self.draw_tooltip = False
423 self.hover_time = None
424 self.mouse_pos = (event.mouse_region_x, event.mouse_region_y)
426 if not mouse_in_area(self.mouse_pos, self.areas["Main Window"], 50 * scale_factor()):
427 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
429 if self.moved:
430 bpy.ops.ed.undo_push()
432 return {'FINISHED'}
434 elif event.value == 'PRESS' and event.type == 'LEFTMOUSE':
435 if not mouse_in_area(self.mouse_pos, self.areas["Main Window"], 10 * scale_factor()):
436 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
438 if self.moved:
439 bpy.ops.ed.undo_push()
441 return {'FINISHED'}
443 for num in range(20):
444 if not self.areas.get(f"Button {num + 1}", None):
445 continue
447 if mouse_in_area(self.mouse_pos, self.areas[f"Button {num + 1}"]):
448 bpy.ops.view3d.move_to_qcd_slot(slot=str(num + 1), toggle=event.shift)
449 self.moved = True
451 elif event.type in {'RIGHTMOUSE', 'ESC'}:
452 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
454 return {'CANCELLED'}
456 if event.value == 'PRESS' and event.type in self.slots:
457 move_to = self.slots[event.type]
459 if event.alt:
460 move_to += 10
462 if event.shift:
463 bpy.ops.view3d.move_to_qcd_slot(slot=str(move_to), toggle=True)
464 else:
465 bpy.ops.view3d.move_to_qcd_slot(slot=str(move_to), toggle=False)
467 self.moved = True
469 if event.type != 'MOUSEMOVE' and event.type != 'INBETWEEN_MOUSEMOVE':
470 self.last_type = event.type
472 return {'RUNNING_MODAL'}
474 def invoke(self, context, event):
475 if context.area.type == 'VIEW_3D':
476 # the arguments we pass the the callback
477 args = (self, context)
478 # Add the region OpenGL drawing callback
479 # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
480 self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
481 self._timer = context.window_manager.event_timer_add(0.1, window=context.window)
483 self.mouse_pos = (event.mouse_region_x, event.mouse_region_y)
485 self.draw_tooltip = False
487 self.hover_time = None
489 self.areas = {}
491 # MAIN WINDOW BACKGROUND
492 x = self.mouse_pos[0] - spacer()*2
493 y = self.mouse_pos[1] + spacer()*2
494 main_window = {
495 # Top Left Vertex
496 "vert": (x,y),
497 "width": 0,
498 "height": 0,
499 "value": None
501 account_for_view_bounds(main_window)
503 # add main window background to areas
504 self.areas["Main Window"] = main_window
506 context.window_manager.modal_handler_add(self)
507 return {'RUNNING_MODAL'}
508 else:
509 self.report({'WARNING'}, "View3D not found, cannot run operator")
510 return {'CANCELLED'}
513 def allocate_main_ui(self, context):
514 main_window = self.areas["Main Window"]
515 self.areas.clear()
516 main_window["width"] = 0
517 main_window["height"] = 0
518 self.areas["Main Window"] = main_window
520 cur_width_pos = main_window["vert"][0]
521 cur_height_pos = main_window["vert"][1]
523 # GRAB BAR
524 grab_bar = {
525 "vert": main_window["vert"],
526 "width": 0,
527 "height": round(23 * scale_factor()),
528 "value": None
531 # add grab bar to areas
532 self.areas["Grab Bar"] = grab_bar
535 # WINDOW TITLE
536 wt_indent_x = spacer()*2
537 wt_y_offset = round(spacer()/2)
538 window_title = {
539 "vert": main_window["vert"],
540 "width": 0,
541 "height": round(13 * scale_factor()),
542 "value": "Move Objects to QCD Slots"
545 x = main_window["vert"][0] + wt_indent_x
546 y = main_window["vert"][1] - window_title["height"] - wt_y_offset
547 window_title["vert"] = (x, y)
549 # add window title to areas
550 self.areas["Window Title"] = window_title
552 cur_height_pos = window_title["vert"][1]
555 # MAIN BUTTON AREA
556 button_size = round(20 * scale_factor())
557 button_gap = round(1 * scale_factor())
558 button_group = 5
559 button_group_gap = round(20 * scale_factor())
560 button_group_width = button_size * button_group + button_gap * (button_group - 1)
562 mba_indent_x = spacer()*2
563 mba_outdent_x = spacer()*2
564 mba_indent_y = spacer()
565 x = cur_width_pos + mba_indent_x
566 y = cur_height_pos - mba_indent_y
567 main_button_area = {
568 "vert": (x, y),
569 "width": 0,
570 "height": 0,
571 "value": None
574 # add main button area to areas
575 self.areas["Main Button Area"] = main_button_area
577 # update current position
578 cur_width_pos = main_button_area["vert"][0]
579 cur_height_pos = main_button_area["vert"][1]
582 # BUTTON ROW 1 A
583 button_row_1_a = {
584 "vert": main_button_area["vert"],
585 "width": button_group_width,
586 "height": button_size,
587 "value": None
590 # add button row 1 A to areas
591 self.areas["Button Row 1 A"] = button_row_1_a
593 # advance width pos to start of next row
594 cur_width_pos += button_row_1_a["width"]
595 cur_width_pos += button_group_gap
597 # BUTTON ROW 1 B
598 x = cur_width_pos
599 y = cur_height_pos
600 button_row_1_b = {
601 "vert": (x, y),
602 "width": button_group_width,
603 "height": button_size,
604 "value": None
607 # add button row 1 B to areas
608 self.areas["Button Row 1 B"] = button_row_1_b
610 # reset width pos to start of main button area
611 cur_width_pos = main_button_area["vert"][0]
612 # update height pos
613 cur_height_pos -= button_row_1_a["height"]
614 # add gap between button rows
615 cur_height_pos -= button_gap
618 # BUTTON ROW 2 A
619 x = cur_width_pos
620 y = cur_height_pos
621 button_row_2_a = {
622 "vert": (x, y),
623 "width": button_group_width,
624 "height": button_size,
625 "value": None
628 # add button row 2 A to areas
629 self.areas["Button Row 2 A"] = button_row_2_a
631 # advance width pos to start of next row
632 cur_width_pos += button_row_2_a["width"]
633 cur_width_pos += button_group_gap
635 # BUTTON ROW 2 B
636 x = cur_width_pos
637 y = cur_height_pos
638 button_row_2_b = {
639 "vert": (x, y),
640 "width": button_group_width,
641 "height": button_size,
642 "value": None
645 # add button row 2 B to areas
646 self.areas["Button Row 2 B"] = button_row_2_b
649 # BUTTONS
650 def get_buttons(button_row, row_num):
651 cur_width_pos = button_row["vert"][0]
652 cur_height_pos = button_row["vert"][1]
653 for num in range(button_group):
654 slot_num = row_num + num
656 qcd_slot_name = qcd_slots.get_name(f"{slot_num}")
658 if qcd_slot_name:
659 qcd_laycol = layer_collections[qcd_slot_name]["ptr"]
660 collection_objects = qcd_laycol.collection.objects
661 selected_objects = qcd_operators.get_move_selection()
662 active_object = qcd_operators.get_move_active()
664 # BUTTON
665 x = cur_width_pos
666 y = cur_height_pos
667 button = {
668 "vert": (x, y),
669 "width": button_size,
670 "height": button_size,
671 "value": slot_num
674 self.areas[f"Button {slot_num}"] = button
676 # ACTIVE OBJECT ICON
677 if active_object and active_object in selected_objects and active_object.name in collection_objects:
678 x = cur_width_pos + round(button_size / 4)
679 y = cur_height_pos - round(button_size / 4)
680 active_object_indicator = {
681 "vert": (x, y),
682 "width": floor(button_size / 2),
683 "height": floor(button_size / 2),
684 "value": None
687 self.areas[f"Button {slot_num} Active Object Indicator"] = active_object_indicator
689 elif not set(selected_objects).isdisjoint(collection_objects):
690 x = cur_width_pos + round(button_size / 4) + floor(1 * scale_factor())
691 y = cur_height_pos - round(button_size / 4) - floor(1 * scale_factor())
692 selected_object_indicator = {
693 "vert": (x, y),
694 "width": floor(button_size / 2) - floor(1 * scale_factor()),
695 "height": floor(button_size / 2) - floor(1 * scale_factor()),
696 "value": None
699 self.areas[f"Button {slot_num} Selected Object Indicator"] = selected_object_indicator
701 elif collection_objects:
702 x = cur_width_pos + floor(button_size / 4)
703 y = cur_height_pos - button_size / 2 + 1 * scale_factor()
704 object_indicator = {
705 "vert": (x, y),
706 "width": round(button_size / 2),
707 "height": round(2 * scale_factor()),
708 "value": None
710 self.areas[f"Button {slot_num} Object Indicator"] = object_indicator
712 else:
713 x = cur_width_pos + 2 * scale_factor()
714 y = cur_height_pos - 2 * scale_factor()
715 X_icon = {
716 "vert": (x, y),
717 "width": button_size - 4 * scale_factor(),
718 "height": button_size - 4 * scale_factor(),
719 "value": None
722 self.areas[f"X_icon {slot_num}"] = X_icon
724 cur_width_pos += button_size
725 cur_width_pos += button_gap
727 get_buttons(button_row_1_a, 1)
728 get_buttons(button_row_1_b, 6)
729 get_buttons(button_row_2_a, 11)
730 get_buttons(button_row_2_b, 16)
733 # UPDATE DYNAMIC DIMENSIONS
734 width = button_row_1_a["width"] + button_group_gap + button_row_1_b["width"]
735 height = button_row_1_a["height"] + button_gap + button_row_2_a["height"]
736 update_area_dimensions(main_button_area, width, height)
738 width = main_button_area["width"] + mba_indent_x + mba_outdent_x
739 height = main_button_area["height"] + mba_indent_y * 2 + window_title["height"] + wt_y_offset
740 update_area_dimensions(main_window, width, height)
742 update_area_dimensions(grab_bar, main_window["width"])
745 def draw_callback_px(self, context):
746 allocate_main_ui(self, context)
748 shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
749 shader.bind()
751 addon_prefs = context.preferences.addons[__package__].preferences
753 # main window background
754 main_window = self.areas["Main Window"]
755 outline_color = addon_prefs.qcd_ogl_widget_menu_back_outline
756 background_color = addon_prefs.qcd_ogl_widget_menu_back_inner
757 draw_rounded_rect(main_window, shader, outline_color[:] + (1,), outline=True)
758 draw_rounded_rect(main_window, shader, background_color)
760 # draw window title
761 window_title = self.areas["Window Title"]
762 x = window_title["vert"][0]
763 y = window_title["vert"][1]
764 h = window_title["height"]
765 text = window_title["value"]
766 text_color = addon_prefs.qcd_ogl_widget_menu_back_text
767 font_id = 0
768 blf.position(font_id, x, y, 0)
769 blf.size(font_id, int(h), 72)
770 blf.color(font_id, text_color[0], text_color[1], text_color[2], 1)
771 blf.draw(font_id, text)
773 # refresh shader - not sure why this is needed
774 shader.bind()
776 in_tooltip_area = False
777 tooltip_slot_idx = None
779 for num in range(20):
780 slot_num = num + 1
781 qcd_slot_name = qcd_slots.get_name(f"{slot_num}")
782 if qcd_slot_name:
783 qcd_laycol = layer_collections[qcd_slot_name]["ptr"]
784 collection_objects = qcd_laycol.collection.objects
785 selected_objects = qcd_operators.get_move_selection()
786 active_object = qcd_operators.get_move_active()
787 button_area = self.areas[f"Button {slot_num}"]
789 # colors
790 button_color = addon_prefs.qcd_ogl_widget_tool_inner
791 icon_color = addon_prefs.qcd_ogl_widget_tool_text
792 if not qcd_laycol.exclude:
793 button_color = addon_prefs.qcd_ogl_widget_tool_inner_sel
794 icon_color = addon_prefs.qcd_ogl_widget_tool_text_sel
796 if mouse_in_area(self.mouse_pos, button_area):
797 in_tooltip_area = True
798 tooltip_slot_idx = slot_num
800 mod = 0.1
802 if button_color[0] + mod > 1 or button_color[1] + mod > 1 or button_color[2] + mod > 1:
803 mod = -mod
805 button_color = (
806 button_color[0] + mod,
807 button_color[1] + mod,
808 button_color[2] + mod,
809 button_color[3]
813 # button roundness
814 tl = tr = bl = br = 0
815 rounding = 5
817 if num < 10:
818 if not qcd_slots.contains(idx=f"{num+2}"):
819 tr = rounding
821 if not qcd_slots.contains(idx=f"{num}"):
822 tl = rounding
823 else:
824 if not qcd_slots.contains(idx=f"{num+2}"):
825 br = rounding
827 if not qcd_slots.contains(idx=f"{num}"):
828 bl = rounding
830 if num in [0,5]:
831 tl = rounding
832 elif num in [4,9]:
833 tr = rounding
834 elif num in [10,15]:
835 bl = rounding
836 elif num in [14,19]:
837 br = rounding
839 # draw button
840 outline_color = addon_prefs.qcd_ogl_widget_tool_outline
841 draw_rounded_rect(button_area, shader, outline_color[:] + (1,), tl, tr, bl, br, outline=True)
842 draw_rounded_rect(button_area, shader, button_color, tl, tr, bl, br)
844 # ACTIVE OBJECT
845 if active_object and active_object in selected_objects and active_object.name in collection_objects:
846 active_object_indicator = self.areas[f"Button {slot_num} Active Object Indicator"]
848 vertices = get_circle_coords(active_object_indicator)
849 shader.uniform_float("color", icon_color[:] + (1,))
850 batch = batch_for_shader(shader, 'TRI_FAN', {"pos": vertices})
852 bgl.glEnable(bgl.GL_BLEND)
854 batch.draw(shader)
856 bgl.glDisable(bgl.GL_BLEND)
858 # SELECTED OBJECTS
859 elif not set(selected_objects).isdisjoint(collection_objects):
860 selected_object_indicator = self.areas[f"Button {slot_num} Selected Object Indicator"]
862 alpha = addon_prefs.qcd_ogl_selected_icon_alpha
863 vertices = get_circle_coords(selected_object_indicator)
864 shader.uniform_float("color", icon_color[:] + (alpha,))
865 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
867 bgl.glLineWidth(2 * scale_factor())
868 bgl.glEnable(bgl.GL_BLEND)
869 bgl.glEnable(bgl.GL_LINE_SMOOTH)
870 bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
872 batch.draw(shader)
874 bgl.glDisable(bgl.GL_LINE_SMOOTH)
875 bgl.glDisable(bgl.GL_BLEND)
877 # OBJECTS
878 elif collection_objects:
879 object_indicator = self.areas[f"Button {slot_num} Object Indicator"]
881 alpha = addon_prefs.qcd_ogl_objects_icon_alpha
882 vertices, indices = get_coords(object_indicator)
883 shader.uniform_float("color", icon_color[:] + (alpha,))
884 batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
886 bgl.glEnable(bgl.GL_BLEND)
888 batch.draw(shader)
890 bgl.glDisable(bgl.GL_BLEND)
893 # X ICON
894 else:
895 X_icon = self.areas[f"X_icon {slot_num}"]
896 X_icon_color = addon_prefs.qcd_ogl_widget_menu_back_text
898 vertices, indices = get_x_coords(X_icon)
899 shader.uniform_float("color", X_icon_color[:] + (1,))
900 batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
902 bgl.glEnable(bgl.GL_BLEND)
903 bgl.glEnable(bgl.GL_POLYGON_SMOOTH)
904 bgl.glHint(bgl.GL_POLYGON_SMOOTH_HINT, bgl.GL_NICEST)
906 batch.draw(shader)
908 bgl.glDisable(bgl.GL_POLYGON_SMOOTH)
909 bgl.glDisable(bgl.GL_BLEND)
911 if in_tooltip_area:
912 if self.draw_tooltip:
913 slot_name = qcd_slots.get_name(f"{tooltip_slot_idx}")
914 slot_string = f"QCD Slot {tooltip_slot_idx}: \"{slot_name}\"\n"
915 hotkey_string = " * Shift+LMB - Toggle objects\' slot."
917 draw_tooltip(self, context, shader, f"{slot_string}{hotkey_string}")
919 self.hover_time = None
921 else:
922 if not self.hover_time:
923 self.hover_time = time.time()
926 def draw_tooltip(self, context, shader, message):
927 addon_prefs = context.preferences.addons[__package__].preferences
929 font_id = 0
930 line_height = 11 * scale_factor()
931 text_color = addon_prefs.qcd_ogl_widget_tooltip_text
932 blf.size(font_id, int(line_height), 72)
933 blf.color(font_id, text_color[0], text_color[1], text_color[2], 1)
935 lines = message.split("\n")
936 longest = [0,""]
937 num_lines = len(lines)
939 for line in lines:
940 w, _ = blf.dimensions(font_id, line)
942 if w > longest[0]:
943 longest[0] = w
944 longest[1] = line
946 w, h = blf.dimensions(font_id, longest[1])
948 line_spacer = 1 * scale_factor()
949 padding = 4 * scale_factor()
951 # draw background
952 tooltip = {
953 "vert": self.mouse_pos,
954 "width": w + spacer()*2,
955 "height": (line_height * num_lines + line_spacer * num_lines) + padding*3,
956 "value": None
959 x = tooltip["vert"][0] - spacer()*2
960 y = tooltip["vert"][1] + tooltip["height"] + round(5 * scale_factor())
961 tooltip["vert"] = (x, y)
963 account_for_view_bounds(tooltip)
965 outline_color = addon_prefs.qcd_ogl_widget_tooltip_outline
966 background_color = addon_prefs.qcd_ogl_widget_tooltip_inner
967 draw_rounded_rect(tooltip, shader, outline_color[:] + (1,), outline=True)
968 draw_rounded_rect(tooltip, shader, background_color)
970 line_pos = padding + line_height
971 # draw text
972 for num, line in enumerate(lines):
973 x = tooltip["vert"][0] + spacer()
974 y = tooltip["vert"][1] - line_pos
975 blf.position(font_id, x, y, 0)
976 blf.draw(font_id, line)
978 line_pos += line_height + line_spacer