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
22 from math
import cos
, sin
, pi
, floor
27 from gpu_extras
.batch
import batch_for_shader
29 from bpy
.types
import Operator
31 from . import internals
33 from .qcd_operators
import (
40 return round(spacer
* scale_factor())
43 return bpy
.context
.preferences
.system
.ui_scale
52 (x
, y
-h
), # bottom left
53 (x
+w
, y
-h
), # bottom right
60 return vertices
, indices
62 def get_x_coords(area
):
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
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
):
92 x
= area
["vert"][0] + area
["width"] / 2
93 y
= area
["vert"][1] - area
["width"] / 2
94 radius
= area
["width"] / 2
96 vertices
= [(radius
* cos(side
* 2 * pi
/ sides
) + x
,
97 radius
* sin(side
* 2 * pi
/ sides
) + y
)
98 for side
in range(sides
+ 1)]
102 def draw_rounded_rect(area
, shader
, color
, tl
=5, tr
=5, bl
=5, br
=5, outline
=False):
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
)
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'
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):
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
})
136 shader
.uniform_float("color", color
)
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):
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
})
153 shader
.uniform_float("color", color
)
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):
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
})
170 shader
.uniform_float("color", color
)
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):
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
})
187 shader
.uniform_float("color", color
)
200 (le_x
+width
, tl_vert
[1]),
202 (le_x
+width
, bl_vert
[1])
205 (base_ind
,base_ind
+1,base_ind
+2),
206 (base_ind
+2,base_ind
+3,base_ind
+1)
215 (re_x
-width
, tr_vert
[1]),
217 (re_x
-width
, br_vert
[1])
220 (base_ind
,base_ind
+1,base_ind
+2),
221 (base_ind
+2,base_ind
+3,base_ind
+1)
230 (tl_vert
[0], te_y
-width
),
232 (tr_vert
[0], te_y
-width
)
235 (base_ind
,base_ind
+1,base_ind
+2),
236 (base_ind
+2,base_ind
+3,base_ind
+1)
245 (bl_vert
[0], be_y
+width
),
247 (br_vert
[0], be_y
+width
)
250 (base_ind
,base_ind
+1,base_ind
+2),
251 (base_ind
+2,base_ind
+3,base_ind
+1)
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
)
273 overlap
= round(thickness
/ 2 - scale_factor() / 2)
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
})
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
})
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
})
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
})
315 bgl
.glDisable(bgl
.GL_LINE_SMOOTH
)
317 bgl
.glDisable(bgl
.GL_BLEND
)
319 def mouse_in_area(mouse_pos
, area
, buf
= 0):
324 if x
+buf
< area
["vert"][0]:
328 if x
-buf
> area
["vert"][0] + area
["width"]:
332 if y
-buf
> area
["vert"][1]:
336 if y
+buf
< area
["vert"][1] - area
["height"]:
339 # if we reach here we're in the area
342 def account_for_view_bounds(area
):
343 # make sure it renders in the 3d view - prioritize top left
346 if area
["vert"][0] + area
["width"] > bpy
.context
.region
.width
:
347 x
= bpy
.context
.region
.width
- area
["width"]
350 area
["vert"] = (x
, y
)
353 if area
["vert"][0] < 0:
357 area
["vert"] = (x
, y
)
360 if area
["vert"][1] - area
["height"] < 0:
364 area
["vert"] = (x
, y
)
367 if area
["vert"][1] > bpy
.context
.region
.height
:
369 y
= bpy
.context
.region
.height
371 area
["vert"] = (x
, y
)
373 def update_area_dimensions(area
, w
=0, h
=0):
377 class QCDMoveWidget(Operator
):
378 """Move objects to QCD Slots"""
379 bl_idname
= "view3d.qcd_move_widget"
380 bl_label
= "QCD Move Widget"
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()):
432 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
435 bpy
.ops
.ed
.undo_push()
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')
447 bpy
.ops
.ed
.undo_push()
451 for num
in range(20):
452 if not self
.areas
.get(f
"Button {num + 1}", None):
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
)
459 elif event
.type in {'RIGHTMOUSE', 'ESC'}:
460 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
464 if event
.value
== 'PRESS' and event
.type in self
.slots
:
465 move_to
= self
.slots
[event
.type]
471 bpy
.ops
.view3d
.move_to_qcd_slot(slot
=str(move_to
), toggle
=True)
473 bpy
.ops
.view3d
.move_to_qcd_slot(slot
=str(move_to
), toggle
=False)
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
499 # MAIN WINDOW BACKGROUND
500 x
= self
.mouse_pos
[0] - spacer()*2
501 y
= self
.mouse_pos
[1] + spacer()*2
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'}
518 self
.report({'WARNING'}, "View3D not found, cannot run operator")
522 def allocate_main_ui(self
, context
):
523 main_window
= self
.areas
["Main Window"]
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]
534 "vert": main_window
["vert"],
536 "height": round(23 * scale_factor()),
540 # add grab bar to areas
541 self
.areas
["Grab Bar"] = grab_bar
545 wt_indent_x
= spacer()*2
546 wt_y_offset
= round(spacer()/2)
548 "vert": main_window
["vert"],
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]
565 button_size
= round(20 * scale_factor())
566 button_gap
= round(1 * scale_factor())
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
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]
593 "vert": main_button_area
["vert"],
594 "width": button_group_width
,
595 "height": button_size
,
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
611 "width": button_group_width
,
612 "height": button_size
,
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]
622 cur_height_pos
-= button_row_1_a
["height"]
623 # add gap between button rows
624 cur_height_pos
-= button_gap
632 "width": button_group_width
,
633 "height": button_size
,
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
649 "width": button_group_width
,
650 "height": button_size
,
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()
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}")
672 qcd_laycol
= internals
.layer_collections
[qcd_slot_name
]["ptr"]
673 collection_objects
= qcd_laycol
.collection
.objects
680 "width": button_size
,
681 "height": button_size
,
685 self
.areas
[f
"Button {slot_num}"] = button
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
= {
693 "width": floor(button_size
/ 2),
694 "height": floor(button_size
/ 2),
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
= {
705 "width": floor(button_size
/ 2) - floor(1 * scale_factor()),
706 "height": floor(button_size
/ 2) - floor(1 * scale_factor()),
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()
717 "width": round(button_size
/ 2),
718 "height": round(2 * scale_factor()),
721 self
.areas
[f
"Button {slot_num} Object Indicator"] = object_indicator
724 x
= cur_width_pos
+ 2 * scale_factor()
725 y
= cur_height_pos
- 2 * scale_factor()
728 "width": button_size
- 4 * scale_factor(),
729 "height": button_size
- 4 * scale_factor(),
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')
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
)
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
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
787 in_tooltip_area
= False
788 tooltip_slot_idx
= None
790 for num
in range(20):
792 qcd_slot_name
= internals
.qcd_slots
.get_name(f
"{slot_num}")
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}"]
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
813 if button_color
[0] + mod
> 1 or button_color
[1] + mod
> 1 or button_color
[2] + mod
> 1:
817 button_color
[0] + mod
,
818 button_color
[1] + mod
,
819 button_color
[2] + mod
,
825 tl
= tr
= bl
= br
= 0
829 if not internals
.qcd_slots
.contains(idx
=f
"{num+2}"):
832 if not internals
.qcd_slots
.contains(idx
=f
"{num}"):
835 if not internals
.qcd_slots
.contains(idx
=f
"{num+2}"):
838 if not internals
.qcd_slots
.contains(idx
=f
"{num}"):
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
)
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
)
867 bgl
.glDisable(bgl
.GL_BLEND
)
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
)
885 bgl
.glDisable(bgl
.GL_LINE_SMOOTH
)
886 bgl
.glDisable(bgl
.GL_BLEND
)
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
)
901 bgl
.glDisable(bgl
.GL_BLEND
)
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
)
919 bgl
.glDisable(bgl
.GL_POLYGON_SMOOTH
)
920 bgl
.glDisable(bgl
.GL_BLEND
)
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
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
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")
948 num_lines
= len(lines
)
951 w
, _
= blf
.dimensions(font_id
, line
)
957 w
, h
= blf
.dimensions(font_id
, longest
[1])
959 line_spacer
= 1 * scale_factor()
960 padding
= 4 * scale_factor()
964 "vert": self
.mouse_pos
,
965 "width": w
+ spacer()*2,
966 "height": (line_height
* num_lines
+ line_spacer
* num_lines
) + padding
*3,
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
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