Merge branch 'blender-v2.92-release'
[blender-addons.git] / object_collection_manager / qcd_move_widget.py
blob57d281c402c0a726051419973dc57a98bc7f7764
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
31 from . import internals
33 from .qcd_operators import (
34 get_move_selection,
35 get_move_active,
38 def spacer():
39 spacer = 10
40 return round(spacer * scale_factor())
42 def scale_factor():
43 return bpy.context.preferences.system.ui_scale
45 def get_coords(area):
46 x = area["vert"][0]
47 y = area["vert"][1]
48 w = area["width"]
49 h = area["height"]
51 vertices = (
52 (x, y-h), # bottom left
53 (x+w, y-h), # bottom right
54 (x, y), # top left
55 (x+w, y)) # top right
57 indices = (
58 (0, 1, 2), (2, 1, 3))
60 return vertices, indices
62 def get_x_coords(area):
63 x = area["vert"][0]
64 y = area["vert"][1]
65 w = area["width"]
66 h = area["height"]
68 vertices = (
69 (x, y), # top left A
70 (x+(w*0.1), y), # top left B
71 (x+w, y), # top right A
72 (x+w-(w*0.1), y), # top right B
73 (x, y-h), # bottom left A
74 (x+(w*0.1), y-h), # bottom left B
75 (x+w, y-h), # bottom right A
76 (x+w-(w*0.1), y-h), # bottom right B
77 (x+(w/2)-(w*0.05), y-(h/2)), # center left
78 (x+(w/2)+(w*0.05), y-(h/2)) # center right
81 indices = (
82 (0,1,8), (1,8,9), # top left bar
83 (2,3,9), (3,9,8), # top right bar
84 (4,5,8), (5,8,9), # bottom left bar
85 (6,7,8), (6,9,8) # bottom right bar
88 return vertices, indices
90 def get_circle_coords(area):
91 # set x, y to center
92 x = area["vert"][0] + area["width"] / 2
93 y = area["vert"][1] - area["width"] / 2
94 radius = area["width"] / 2
95 sides = 32
96 vertices = [(radius * cos(side * 2 * pi / sides) + x,
97 radius * sin(side * 2 * pi / sides) + y)
98 for side in range(sides + 1)]
100 return vertices
102 def draw_rounded_rect(area, shader, color, tl=5, tr=5, bl=5, br=5, outline=False):
103 sides = 32
105 tl = round(tl * scale_factor())
106 tr = round(tr * scale_factor())
107 bl = round(bl * scale_factor())
108 br = round(br * scale_factor())
110 bgl.glEnable(bgl.GL_BLEND)
112 if outline:
113 thickness = round(2 * scale_factor())
114 thickness = max(thickness, 2)
116 bgl.glLineWidth(thickness)
117 bgl.glEnable(bgl.GL_LINE_SMOOTH)
118 bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
120 draw_type = 'TRI_FAN' if not outline else 'LINE_STRIP'
122 # top left corner
123 vert_x = area["vert"][0] + tl
124 vert_y = area["vert"][1] - tl
125 tl_vert = (vert_x, vert_y)
126 vertices = [(vert_x, vert_y)] if not outline else []
128 for side in range(sides+1):
129 if (8<=side<=16):
130 cosine = tl * cos(side * 2 * pi / sides) + vert_x
131 sine = tl * sin(side * 2 * pi / sides) + vert_y
132 vertices.append((cosine,sine))
134 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
135 shader.bind()
136 shader.uniform_float("color", color)
137 batch.draw(shader)
139 # top right corner
140 vert_x = area["vert"][0] + area["width"] - tr
141 vert_y = area["vert"][1] - tr
142 tr_vert = (vert_x, vert_y)
143 vertices = [(vert_x, vert_y)] if not outline else []
145 for side in range(sides+1):
146 if (0<=side<=8):
147 cosine = tr * cos(side * 2 * pi / sides) + vert_x
148 sine = tr * sin(side * 2 * pi / sides) + vert_y
149 vertices.append((cosine,sine))
151 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
152 shader.bind()
153 shader.uniform_float("color", color)
154 batch.draw(shader)
156 # bottom left corner
157 vert_x = area["vert"][0] + bl
158 vert_y = area["vert"][1] - area["height"] + bl
159 bl_vert = (vert_x, vert_y)
160 vertices = [(vert_x, vert_y)] if not outline else []
162 for side in range(sides+1):
163 if (16<=side<=24):
164 cosine = bl * cos(side * 2 * pi / sides) + vert_x
165 sine = bl * sin(side * 2 * pi / sides) + vert_y
166 vertices.append((cosine,sine))
168 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
169 shader.bind()
170 shader.uniform_float("color", color)
171 batch.draw(shader)
173 # bottom right corner
174 vert_x = area["vert"][0] + area["width"] - br
175 vert_y = area["vert"][1] - area["height"] + br
176 br_vert = (vert_x, vert_y)
177 vertices = [(vert_x, vert_y)] if not outline else []
179 for side in range(sides+1):
180 if (24<=side<=32):
181 cosine = br * cos(side * 2 * pi / sides) + vert_x
182 sine = br * sin(side * 2 * pi / sides) + vert_y
183 vertices.append((cosine,sine))
185 batch = batch_for_shader(shader, draw_type, {"pos": vertices})
186 shader.bind()
187 shader.uniform_float("color", color)
188 batch.draw(shader)
190 if not outline:
191 vertices = []
192 indices = []
193 base_ind = 0
195 # left edge
196 width = max(tl, bl)
197 le_x = tl_vert[0]-tl
198 vertices.extend([
199 (le_x, tl_vert[1]),
200 (le_x+width, tl_vert[1]),
201 (le_x, bl_vert[1]),
202 (le_x+width, bl_vert[1])
204 indices.extend([
205 (base_ind,base_ind+1,base_ind+2),
206 (base_ind+2,base_ind+3,base_ind+1)
208 base_ind += 4
210 # right edge
211 width = max(tr, br)
212 re_x = tr_vert[0]+tr
213 vertices.extend([
214 (re_x, tr_vert[1]),
215 (re_x-width, tr_vert[1]),
216 (re_x, br_vert[1]),
217 (re_x-width, br_vert[1])
219 indices.extend([
220 (base_ind,base_ind+1,base_ind+2),
221 (base_ind+2,base_ind+3,base_ind+1)
223 base_ind += 4
225 # top edge
226 width = max(tl, tr)
227 te_y = tl_vert[1]+tl
228 vertices.extend([
229 (tl_vert[0], te_y),
230 (tl_vert[0], te_y-width),
231 (tr_vert[0], te_y),
232 (tr_vert[0], te_y-width)
234 indices.extend([
235 (base_ind,base_ind+1,base_ind+2),
236 (base_ind+2,base_ind+3,base_ind+1)
238 base_ind += 4
240 # bottom edge
241 width = max(bl, br)
242 be_y = bl_vert[1]-bl
243 vertices.extend([
244 (bl_vert[0], be_y),
245 (bl_vert[0], be_y+width),
246 (br_vert[0], be_y),
247 (br_vert[0], be_y+width)
249 indices.extend([
250 (base_ind,base_ind+1,base_ind+2),
251 (base_ind+2,base_ind+3,base_ind+1)
253 base_ind += 4
255 # middle
256 vertices.extend([
257 tl_vert,
258 tr_vert,
259 bl_vert,
260 br_vert
262 indices.extend([
263 (base_ind,base_ind+1,base_ind+2),
264 (base_ind+2,base_ind+3,base_ind+1)
267 batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
269 shader.uniform_float("color", color)
270 batch.draw(shader)
272 else:
273 overlap = round(thickness / 2 - scale_factor() / 2)
275 # left edge
276 le_x = tl_vert[0]-tl
277 vertices = [
278 (le_x, tl_vert[1] + (overlap if tl == 0 else 0)),
279 (le_x, bl_vert[1] - (overlap if bl == 0 else 0))
282 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
283 batch.draw(shader)
285 # right edge
286 re_x = tr_vert[0]+tr
287 vertices = [
288 (re_x, tr_vert[1] + (overlap if tr == 0 else 0)),
289 (re_x, br_vert[1] - (overlap if br == 0 else 0))
292 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
293 batch.draw(shader)
295 # top edge
296 te_y = tl_vert[1]+tl
297 vertices = [
298 (tl_vert[0] - (overlap if tl == 0 else 0), te_y),
299 (tr_vert[0] + (overlap if tr == 0 else 0), te_y)
302 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
303 batch.draw(shader)
305 # bottom edge
306 be_y = bl_vert[1]-bl
307 vertices = [
308 (bl_vert[0] - (overlap if bl == 0 else 0), be_y),
309 (br_vert[0] + (overlap if br == 0 else 0), be_y)
312 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
313 batch.draw(shader)
315 bgl.glDisable(bgl.GL_LINE_SMOOTH)
317 bgl.glDisable(bgl.GL_BLEND)
319 def mouse_in_area(mouse_pos, area, buf = 0):
320 x = mouse_pos[0]
321 y = mouse_pos[1]
323 # check left
324 if x+buf < area["vert"][0]:
325 return False
327 # check right
328 if x-buf > area["vert"][0] + area["width"]:
329 return False
331 # check top
332 if y-buf > area["vert"][1]:
333 return False
335 # check bottom
336 if y+buf < area["vert"][1] - area["height"]:
337 return False
339 # if we reach here we're in the area
340 return True
342 def account_for_view_bounds(area):
343 # make sure it renders in the 3d view - prioritize top left
345 # right
346 if area["vert"][0] + area["width"] > bpy.context.region.width:
347 x = bpy.context.region.width - area["width"]
348 y = area["vert"][1]
350 area["vert"] = (x, y)
352 # left
353 if area["vert"][0] < 0:
354 x = 0
355 y = area["vert"][1]
357 area["vert"] = (x, y)
359 # bottom
360 if area["vert"][1] - area["height"] < 0:
361 x = area["vert"][0]
362 y = area["height"]
364 area["vert"] = (x, y)
366 # top
367 if area["vert"][1] > bpy.context.region.height:
368 x = area["vert"][0]
369 y = bpy.context.region.height
371 area["vert"] = (x, y)
373 def update_area_dimensions(area, w=0, h=0):
374 area["width"] += w
375 area["height"] += h
377 class QCDMoveWidget(Operator):
378 """Move objects to QCD Slots"""
379 bl_idname = "view3d.qcd_move_widget"
380 bl_label = "QCD Move Widget"
382 slots = {
383 "ONE":1,
384 "TWO":2,
385 "THREE":3,
386 "FOUR":4,
387 "FIVE":5,
388 "SIX":6,
389 "SEVEN":7,
390 "EIGHT":8,
391 "NINE":9,
392 "ZERO":10,
395 last_type = ''
396 initialized = False
397 moved = False
399 def modal(self, context, event):
400 if event.type == 'TIMER':
401 if self.hover_time and self.hover_time + 0.5 < time.time():
402 self.draw_tooltip = True
404 context.area.tag_redraw()
405 return {'RUNNING_MODAL'}
408 context.area.tag_redraw()
410 if len(self.areas) == 1:
411 return {'RUNNING_MODAL'}
413 if self.last_type == 'LEFTMOUSE' and event.value == 'PRESS' and event.type == 'MOUSEMOVE':
414 if mouse_in_area(self.mouse_pos, self.areas["Grab Bar"]):
415 x_offset = self.areas["Main Window"]["vert"][0] - self.mouse_pos[0]
416 x = event.mouse_region_x + x_offset
418 y_offset = self.areas["Main Window"]["vert"][1] - self.mouse_pos[1]
419 y = event.mouse_region_y + y_offset
421 self.areas["Main Window"]["vert"] = (x, y)
423 self.mouse_pos = (event.mouse_region_x, event.mouse_region_y)
425 elif event.type == 'MOUSEMOVE':
426 self.draw_tooltip = False
427 self.hover_time = None
428 self.mouse_pos = (event.mouse_region_x, event.mouse_region_y)
430 if not mouse_in_area(self.mouse_pos, self.areas["Main Window"], 50 * scale_factor()):
431 if self.initialized:
432 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
434 if self.moved:
435 bpy.ops.ed.undo_push()
437 return {'FINISHED'}
439 else:
440 self.initialized = True
442 elif event.value == 'PRESS' and event.type == 'LEFTMOUSE':
443 if not mouse_in_area(self.mouse_pos, self.areas["Main Window"], 10 * scale_factor()):
444 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
446 if self.moved:
447 bpy.ops.ed.undo_push()
449 return {'FINISHED'}
451 for num in range(20):
452 if not self.areas.get(f"Button {num + 1}", None):
453 continue
455 if mouse_in_area(self.mouse_pos, self.areas[f"Button {num + 1}"]):
456 bpy.ops.view3d.move_to_qcd_slot(slot=str(num + 1), toggle=event.shift)
457 self.moved = True
459 elif event.type in {'RIGHTMOUSE', 'ESC'}:
460 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
462 return {'CANCELLED'}
464 if event.value == 'PRESS' and event.type in self.slots:
465 move_to = self.slots[event.type]
467 if event.alt:
468 move_to += 10
470 if event.shift:
471 bpy.ops.view3d.move_to_qcd_slot(slot=str(move_to), toggle=True)
472 else:
473 bpy.ops.view3d.move_to_qcd_slot(slot=str(move_to), toggle=False)
475 self.moved = True
477 if event.type != 'MOUSEMOVE' and event.type != 'INBETWEEN_MOUSEMOVE':
478 self.last_type = event.type
480 return {'RUNNING_MODAL'}
482 def invoke(self, context, event):
483 if context.area.type == 'VIEW_3D':
484 # the arguments we pass the the callback
485 args = (self, context)
486 # Add the region OpenGL drawing callback
487 # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
488 self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
489 self._timer = context.window_manager.event_timer_add(0.1, window=context.window)
491 self.mouse_pos = (event.mouse_region_x, event.mouse_region_y)
493 self.draw_tooltip = False
495 self.hover_time = None
497 self.areas = {}
499 # MAIN WINDOW BACKGROUND
500 x = self.mouse_pos[0] - spacer()*2
501 y = self.mouse_pos[1] + spacer()*2
502 main_window = {
503 # Top Left Vertex
504 "vert": (x,y),
505 "width": 0,
506 "height": 0,
507 "value": None
510 self.areas["Main Window"] = main_window
511 allocate_main_ui(self, context)
512 account_for_view_bounds(main_window)
514 context.window_manager.modal_handler_add(self)
515 return {'RUNNING_MODAL'}
517 else:
518 self.report({'WARNING'}, "View3D not found, cannot run operator")
519 return {'CANCELLED'}
522 def allocate_main_ui(self, context):
523 main_window = self.areas["Main Window"]
524 self.areas.clear()
525 main_window["width"] = 0
526 main_window["height"] = 0
527 self.areas["Main Window"] = main_window
529 cur_width_pos = main_window["vert"][0]
530 cur_height_pos = main_window["vert"][1]
532 # GRAB BAR
533 grab_bar = {
534 "vert": main_window["vert"],
535 "width": 0,
536 "height": round(23 * scale_factor()),
537 "value": None
540 # add grab bar to areas
541 self.areas["Grab Bar"] = grab_bar
544 # WINDOW TITLE
545 wt_indent_x = spacer()*2
546 wt_y_offset = round(spacer()/2)
547 window_title = {
548 "vert": main_window["vert"],
549 "width": 0,
550 "height": round(13 * scale_factor()),
551 "value": "Move Objects to QCD Slots"
554 x = main_window["vert"][0] + wt_indent_x
555 y = main_window["vert"][1] - window_title["height"] - wt_y_offset
556 window_title["vert"] = (x, y)
558 # add window title to areas
559 self.areas["Window Title"] = window_title
561 cur_height_pos = window_title["vert"][1]
564 # MAIN BUTTON AREA
565 button_size = round(20 * scale_factor())
566 button_gap = round(1 * scale_factor())
567 button_group = 5
568 button_group_gap = round(20 * scale_factor())
569 button_group_width = button_size * button_group + button_gap * (button_group - 1)
571 mba_indent_x = spacer()*2
572 mba_outdent_x = spacer()*2
573 mba_indent_y = spacer()
574 x = cur_width_pos + mba_indent_x
575 y = cur_height_pos - mba_indent_y
576 main_button_area = {
577 "vert": (x, y),
578 "width": 0,
579 "height": 0,
580 "value": None
583 # add main button area to areas
584 self.areas["Main Button Area"] = main_button_area
586 # update current position
587 cur_width_pos = main_button_area["vert"][0]
588 cur_height_pos = main_button_area["vert"][1]
591 # BUTTON ROW 1 A
592 button_row_1_a = {
593 "vert": main_button_area["vert"],
594 "width": button_group_width,
595 "height": button_size,
596 "value": None
599 # add button row 1 A to areas
600 self.areas["Button Row 1 A"] = button_row_1_a
602 # advance width pos to start of next row
603 cur_width_pos += button_row_1_a["width"]
604 cur_width_pos += button_group_gap
606 # BUTTON ROW 1 B
607 x = cur_width_pos
608 y = cur_height_pos
609 button_row_1_b = {
610 "vert": (x, y),
611 "width": button_group_width,
612 "height": button_size,
613 "value": None
616 # add button row 1 B to areas
617 self.areas["Button Row 1 B"] = button_row_1_b
619 # reset width pos to start of main button area
620 cur_width_pos = main_button_area["vert"][0]
621 # update height pos
622 cur_height_pos -= button_row_1_a["height"]
623 # add gap between button rows
624 cur_height_pos -= button_gap
627 # BUTTON ROW 2 A
628 x = cur_width_pos
629 y = cur_height_pos
630 button_row_2_a = {
631 "vert": (x, y),
632 "width": button_group_width,
633 "height": button_size,
634 "value": None
637 # add button row 2 A to areas
638 self.areas["Button Row 2 A"] = button_row_2_a
640 # advance width pos to start of next row
641 cur_width_pos += button_row_2_a["width"]
642 cur_width_pos += button_group_gap
644 # BUTTON ROW 2 B
645 x = cur_width_pos
646 y = cur_height_pos
647 button_row_2_b = {
648 "vert": (x, y),
649 "width": button_group_width,
650 "height": button_size,
651 "value": None
654 # add button row 2 B to areas
655 self.areas["Button Row 2 B"] = button_row_2_b
658 selected_objects = get_move_selection()
659 active_object = get_move_active()
662 # BUTTONS
663 def get_buttons(button_row, row_num):
664 cur_width_pos = button_row["vert"][0]
665 cur_height_pos = button_row["vert"][1]
666 for num in range(button_group):
667 slot_num = row_num + num
669 qcd_slot_name = internals.qcd_slots.get_name(f"{slot_num}")
671 if qcd_slot_name:
672 qcd_laycol = internals.layer_collections[qcd_slot_name]["ptr"]
673 collection_objects = qcd_laycol.collection.objects
675 # BUTTON
676 x = cur_width_pos
677 y = cur_height_pos
678 button = {
679 "vert": (x, y),
680 "width": button_size,
681 "height": button_size,
682 "value": slot_num
685 self.areas[f"Button {slot_num}"] = button
687 # ACTIVE OBJECT ICON
688 if active_object and active_object in selected_objects and active_object.name in collection_objects:
689 x = cur_width_pos + round(button_size / 4)
690 y = cur_height_pos - round(button_size / 4)
691 active_object_indicator = {
692 "vert": (x, y),
693 "width": floor(button_size / 2),
694 "height": floor(button_size / 2),
695 "value": None
698 self.areas[f"Button {slot_num} Active Object Indicator"] = active_object_indicator
700 elif not set(selected_objects).isdisjoint(collection_objects):
701 x = cur_width_pos + round(button_size / 4) + floor(1 * scale_factor())
702 y = cur_height_pos - round(button_size / 4) - floor(1 * scale_factor())
703 selected_object_indicator = {
704 "vert": (x, y),
705 "width": floor(button_size / 2) - floor(1 * scale_factor()),
706 "height": floor(button_size / 2) - floor(1 * scale_factor()),
707 "value": None
710 self.areas[f"Button {slot_num} Selected Object Indicator"] = selected_object_indicator
712 elif collection_objects:
713 x = cur_width_pos + floor(button_size / 4)
714 y = cur_height_pos - button_size / 2 + 1 * scale_factor()
715 object_indicator = {
716 "vert": (x, y),
717 "width": round(button_size / 2),
718 "height": round(2 * scale_factor()),
719 "value": None
721 self.areas[f"Button {slot_num} Object Indicator"] = object_indicator
723 else:
724 x = cur_width_pos + 2 * scale_factor()
725 y = cur_height_pos - 2 * scale_factor()
726 X_icon = {
727 "vert": (x, y),
728 "width": button_size - 4 * scale_factor(),
729 "height": button_size - 4 * scale_factor(),
730 "value": None
733 self.areas[f"X_icon {slot_num}"] = X_icon
735 cur_width_pos += button_size
736 cur_width_pos += button_gap
738 get_buttons(button_row_1_a, 1)
739 get_buttons(button_row_1_b, 6)
740 get_buttons(button_row_2_a, 11)
741 get_buttons(button_row_2_b, 16)
744 # UPDATE DYNAMIC DIMENSIONS
745 width = button_row_1_a["width"] + button_group_gap + button_row_1_b["width"]
746 height = button_row_1_a["height"] + button_gap + button_row_2_a["height"]
747 update_area_dimensions(main_button_area, width, height)
749 width = main_button_area["width"] + mba_indent_x + mba_outdent_x
750 height = main_button_area["height"] + mba_indent_y * 2 + window_title["height"] + wt_y_offset
751 update_area_dimensions(main_window, width, height)
753 update_area_dimensions(grab_bar, main_window["width"])
756 def draw_callback_px(self, context):
757 allocate_main_ui(self, context)
759 shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
760 shader.bind()
762 addon_prefs = context.preferences.addons[__package__].preferences
764 # main window background
765 main_window = self.areas["Main Window"]
766 outline_color = addon_prefs.qcd_ogl_widget_menu_back_outline
767 background_color = addon_prefs.qcd_ogl_widget_menu_back_inner
768 draw_rounded_rect(main_window, shader, outline_color[:] + (1,), outline=True)
769 draw_rounded_rect(main_window, shader, background_color)
771 # draw window title
772 window_title = self.areas["Window Title"]
773 x = window_title["vert"][0]
774 y = window_title["vert"][1]
775 h = window_title["height"]
776 text = window_title["value"]
777 text_color = addon_prefs.qcd_ogl_widget_menu_back_text
778 font_id = 0
779 blf.position(font_id, x, y, 0)
780 blf.size(font_id, int(h), 72)
781 blf.color(font_id, text_color[0], text_color[1], text_color[2], 1)
782 blf.draw(font_id, text)
784 # refresh shader - not sure why this is needed
785 shader.bind()
787 in_tooltip_area = False
788 tooltip_slot_idx = None
790 for num in range(20):
791 slot_num = num + 1
792 qcd_slot_name = internals.qcd_slots.get_name(f"{slot_num}")
793 if qcd_slot_name:
794 qcd_laycol = internals.layer_collections[qcd_slot_name]["ptr"]
795 collection_objects = qcd_laycol.collection.objects
796 selected_objects = get_move_selection()
797 active_object = get_move_active()
798 button_area = self.areas[f"Button {slot_num}"]
800 # colors
801 button_color = addon_prefs.qcd_ogl_widget_tool_inner
802 icon_color = addon_prefs.qcd_ogl_widget_tool_text
803 if not qcd_laycol.exclude:
804 button_color = addon_prefs.qcd_ogl_widget_tool_inner_sel
805 icon_color = addon_prefs.qcd_ogl_widget_tool_text_sel
807 if mouse_in_area(self.mouse_pos, button_area):
808 in_tooltip_area = True
809 tooltip_slot_idx = slot_num
811 mod = 0.1
813 if button_color[0] + mod > 1 or button_color[1] + mod > 1 or button_color[2] + mod > 1:
814 mod = -mod
816 button_color = (
817 button_color[0] + mod,
818 button_color[1] + mod,
819 button_color[2] + mod,
820 button_color[3]
824 # button roundness
825 tl = tr = bl = br = 0
826 rounding = 5
828 if num < 10:
829 if not internals.qcd_slots.contains(idx=f"{num+2}"):
830 tr = rounding
832 if not internals.qcd_slots.contains(idx=f"{num}"):
833 tl = rounding
834 else:
835 if not internals.qcd_slots.contains(idx=f"{num+2}"):
836 br = rounding
838 if not internals.qcd_slots.contains(idx=f"{num}"):
839 bl = rounding
841 if num in [0,5]:
842 tl = rounding
843 elif num in [4,9]:
844 tr = rounding
845 elif num in [10,15]:
846 bl = rounding
847 elif num in [14,19]:
848 br = rounding
850 # draw button
851 outline_color = addon_prefs.qcd_ogl_widget_tool_outline
852 draw_rounded_rect(button_area, shader, outline_color[:] + (1,), tl, tr, bl, br, outline=True)
853 draw_rounded_rect(button_area, shader, button_color, tl, tr, bl, br)
855 # ACTIVE OBJECT
856 if active_object and active_object in selected_objects and active_object.name in collection_objects:
857 active_object_indicator = self.areas[f"Button {slot_num} Active Object Indicator"]
859 vertices = get_circle_coords(active_object_indicator)
860 shader.uniform_float("color", icon_color[:] + (1,))
861 batch = batch_for_shader(shader, 'TRI_FAN', {"pos": vertices})
863 bgl.glEnable(bgl.GL_BLEND)
865 batch.draw(shader)
867 bgl.glDisable(bgl.GL_BLEND)
869 # SELECTED OBJECTS
870 elif not set(selected_objects).isdisjoint(collection_objects):
871 selected_object_indicator = self.areas[f"Button {slot_num} Selected Object Indicator"]
873 alpha = addon_prefs.qcd_ogl_selected_icon_alpha
874 vertices = get_circle_coords(selected_object_indicator)
875 shader.uniform_float("color", icon_color[:] + (alpha,))
876 batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices})
878 bgl.glLineWidth(2 * scale_factor())
879 bgl.glEnable(bgl.GL_BLEND)
880 bgl.glEnable(bgl.GL_LINE_SMOOTH)
881 bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST)
883 batch.draw(shader)
885 bgl.glDisable(bgl.GL_LINE_SMOOTH)
886 bgl.glDisable(bgl.GL_BLEND)
888 # OBJECTS
889 elif collection_objects:
890 object_indicator = self.areas[f"Button {slot_num} Object Indicator"]
892 alpha = addon_prefs.qcd_ogl_objects_icon_alpha
893 vertices, indices = get_coords(object_indicator)
894 shader.uniform_float("color", icon_color[:] + (alpha,))
895 batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
897 bgl.glEnable(bgl.GL_BLEND)
899 batch.draw(shader)
901 bgl.glDisable(bgl.GL_BLEND)
904 # X ICON
905 else:
906 X_icon = self.areas[f"X_icon {slot_num}"]
907 X_icon_color = addon_prefs.qcd_ogl_widget_menu_back_text
909 vertices, indices = get_x_coords(X_icon)
910 shader.uniform_float("color", X_icon_color[:] + (1,))
911 batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
913 bgl.glEnable(bgl.GL_BLEND)
914 bgl.glEnable(bgl.GL_POLYGON_SMOOTH)
915 bgl.glHint(bgl.GL_POLYGON_SMOOTH_HINT, bgl.GL_NICEST)
917 batch.draw(shader)
919 bgl.glDisable(bgl.GL_POLYGON_SMOOTH)
920 bgl.glDisable(bgl.GL_BLEND)
922 if in_tooltip_area:
923 if self.draw_tooltip:
924 slot_name = internals.qcd_slots.get_name(f"{tooltip_slot_idx}")
925 slot_string = f"QCD Slot {tooltip_slot_idx}: \"{slot_name}\"\n"
926 hotkey_string = " * Shift+LMB - Toggle objects\' slot."
928 draw_tooltip(self, context, shader, f"{slot_string}{hotkey_string}")
930 self.hover_time = None
932 else:
933 if not self.hover_time:
934 self.hover_time = time.time()
937 def draw_tooltip(self, context, shader, message):
938 addon_prefs = context.preferences.addons[__package__].preferences
940 font_id = 0
941 line_height = 11 * scale_factor()
942 text_color = addon_prefs.qcd_ogl_widget_tooltip_text
943 blf.size(font_id, int(line_height), 72)
944 blf.color(font_id, text_color[0], text_color[1], text_color[2], 1)
946 lines = message.split("\n")
947 longest = [0,""]
948 num_lines = len(lines)
950 for line in lines:
951 w, _ = blf.dimensions(font_id, line)
953 if w > longest[0]:
954 longest[0] = w
955 longest[1] = line
957 w, h = blf.dimensions(font_id, longest[1])
959 line_spacer = 1 * scale_factor()
960 padding = 4 * scale_factor()
962 # draw background
963 tooltip = {
964 "vert": self.mouse_pos,
965 "width": w + spacer()*2,
966 "height": (line_height * num_lines + line_spacer * num_lines) + padding*3,
967 "value": None
970 x = tooltip["vert"][0] - spacer()*2
971 y = tooltip["vert"][1] + tooltip["height"] + round(5 * scale_factor())
972 tooltip["vert"] = (x, y)
974 account_for_view_bounds(tooltip)
976 outline_color = addon_prefs.qcd_ogl_widget_tooltip_outline
977 background_color = addon_prefs.qcd_ogl_widget_tooltip_inner
978 draw_rounded_rect(tooltip, shader, outline_color[:] + (1,), outline=True)
979 draw_rounded_rect(tooltip, shader, background_color)
981 line_pos = padding + line_height
982 # draw text
983 for num, line in enumerate(lines):
984 x = tooltip["vert"][0] + spacer()
985 y = tooltip["vert"][1] - line_pos
986 blf.position(font_id, x, y, 0)
987 blf.draw(font_id, line)
989 line_pos += line_height + line_spacer