1 # SPDX-FileCopyrightText: 2011 Ryan Inch
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 from math
import cos
, sin
, pi
, floor
10 from gpu_extras
.batch
import batch_for_shader
12 from bpy
.types
import Operator
14 from . import internals
16 from .qcd_operators
import (
23 return round(spacer
* scale_factor())
26 return bpy
.context
.preferences
.system
.ui_scale
35 (x
, y
-h
), # bottom left
36 (x
+w
, y
-h
), # bottom right
43 return vertices
, indices
45 def get_x_coords(area
):
53 (x
+(w
*0.1), y
), # top left B
54 (x
+w
, y
), # top right A
55 (x
+w
-(w
*0.1), y
), # top right B
56 (x
, y
-h
), # bottom left A
57 (x
+(w
*0.1), y
-h
), # bottom left B
58 (x
+w
, y
-h
), # bottom right A
59 (x
+w
-(w
*0.1), y
-h
), # bottom right B
60 (x
+(w
/2)-(w
*0.05), y
-(h
/2)), # center left
61 (x
+(w
/2)+(w
*0.05), y
-(h
/2)) # center right
65 (0,1,8), (1,8,9), # top left bar
66 (2,3,9), (3,9,8), # top right bar
67 (4,5,8), (5,8,9), # bottom left bar
68 (6,7,8), (6,9,8) # bottom right bar
71 return vertices
, indices
73 def get_circle_coords(area
):
75 x
= area
["vert"][0] + area
["width"] / 2
76 y
= area
["vert"][1] - area
["width"] / 2
77 radius
= area
["width"] / 2
79 vertices
= [(radius
* cos(side
* 2 * pi
/ sides
) + x
,
80 radius
* sin(side
* 2 * pi
/ sides
) + y
)
81 for side
in range(sides
+ 1)]
85 def draw_rounded_rect(area
, shader
, color
, tl
=5, tr
=5, bl
=5, br
=5, outline
=False):
88 tl
= round(tl
* scale_factor())
89 tr
= round(tr
* scale_factor())
90 bl
= round(bl
* scale_factor())
91 br
= round(br
* scale_factor())
93 gpu
.state
.blend_set('ALPHA')
96 thickness
= round(2 * scale_factor())
97 thickness
= max(thickness
, 2)
98 shader
.uniform_float("lineWidth", thickness
)
100 draw_type
= 'TRI_FAN' if not outline
else 'LINE_STRIP'
103 vert_x
= area
["vert"][0] + tl
104 vert_y
= area
["vert"][1] - tl
105 tl_vert
= (vert_x
, vert_y
)
106 vertices
= [(vert_x
, vert_y
)] if not outline
else []
108 for side
in range(sides
+1):
110 cosine
= tl
* cos(side
* 2 * pi
/ sides
) + vert_x
111 sine
= tl
* sin(side
* 2 * pi
/ sides
) + vert_y
112 vertices
.append((cosine
,sine
))
115 batch
= batch_for_shader(shader
, draw_type
, {"pos": vertices
})
117 shader
.uniform_float("color", color
)
120 batch
= batch_for_shader(shader
, draw_type
, {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
125 vert_x
= area
["vert"][0] + area
["width"] - tr
126 vert_y
= area
["vert"][1] - tr
127 tr_vert
= (vert_x
, vert_y
)
128 vertices
= [(vert_x
, vert_y
)] if not outline
else []
130 for side
in range(sides
+1):
132 cosine
= tr
* cos(side
* 2 * pi
/ sides
) + vert_x
133 sine
= tr
* sin(side
* 2 * pi
/ sides
) + vert_y
134 vertices
.append((cosine
,sine
))
137 batch
= batch_for_shader(shader
, draw_type
, {"pos": vertices
})
139 shader
.uniform_float("color", color
)
142 batch
= batch_for_shader(shader
, draw_type
, {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
147 vert_x
= area
["vert"][0] + bl
148 vert_y
= area
["vert"][1] - area
["height"] + bl
149 bl_vert
= (vert_x
, vert_y
)
150 vertices
= [(vert_x
, vert_y
)] if not outline
else []
152 for side
in range(sides
+1):
154 cosine
= bl
* cos(side
* 2 * pi
/ sides
) + vert_x
155 sine
= bl
* sin(side
* 2 * pi
/ sides
) + vert_y
156 vertices
.append((cosine
,sine
))
159 batch
= batch_for_shader(shader
, draw_type
, {"pos": vertices
})
161 shader
.uniform_float("color", color
)
164 batch
= batch_for_shader(shader
, draw_type
, {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
168 # bottom right corner
169 vert_x
= area
["vert"][0] + area
["width"] - br
170 vert_y
= area
["vert"][1] - area
["height"] + br
171 br_vert
= (vert_x
, vert_y
)
172 vertices
= [(vert_x
, vert_y
)] if not outline
else []
174 for side
in range(sides
+1):
176 cosine
= br
* cos(side
* 2 * pi
/ sides
) + vert_x
177 sine
= br
* sin(side
* 2 * pi
/ sides
) + vert_y
178 vertices
.append((cosine
,sine
))
181 batch
= batch_for_shader(shader
, draw_type
, {"pos": vertices
})
183 shader
.uniform_float("color", color
)
186 batch
= batch_for_shader(shader
, draw_type
, {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
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
)
270 shader
.uniform_float("color", color
)
274 overlap
= round(thickness
/ 2 - scale_factor() / 2)
279 (le_x
, tl_vert
[1] + (overlap
if tl
== 0 else 0)),
280 (le_x
, bl_vert
[1] - (overlap
if bl
== 0 else 0))
283 batch
= batch_for_shader(shader
, 'LINE_STRIP', {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
290 (re_x
, tr_vert
[1] + (overlap
if tr
== 0 else 0)),
291 (re_x
, br_vert
[1] - (overlap
if br
== 0 else 0))
294 batch
= batch_for_shader(shader
, 'LINE_STRIP', {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
301 (tl_vert
[0] - (overlap
if tl
== 0 else 0), te_y
),
302 (tr_vert
[0] + (overlap
if tr
== 0 else 0), te_y
)
305 batch
= batch_for_shader(shader
, 'LINE_STRIP', {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
312 (bl_vert
[0] - (overlap
if bl
== 0 else 0), be_y
),
313 (br_vert
[0] + (overlap
if br
== 0 else 0), be_y
)
316 batch
= batch_for_shader(shader
, 'LINE_STRIP', {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
321 gpu
.state
.blend_set('NONE')
323 def mouse_in_area(mouse_pos
, area
, buf
= 0):
328 if x
+buf
< area
["vert"][0]:
332 if x
-buf
> area
["vert"][0] + area
["width"]:
336 if y
-buf
> area
["vert"][1]:
340 if y
+buf
< area
["vert"][1] - area
["height"]:
343 # if we reach here we're in the area
346 def account_for_view_bounds(area
):
347 # make sure it renders in the 3d view - prioritize top left
350 if area
["vert"][0] + area
["width"] > bpy
.context
.region
.width
:
351 x
= bpy
.context
.region
.width
- area
["width"]
354 area
["vert"] = (x
, y
)
357 if area
["vert"][0] < 0:
361 area
["vert"] = (x
, y
)
364 if area
["vert"][1] - area
["height"] < 0:
368 area
["vert"] = (x
, y
)
371 if area
["vert"][1] > bpy
.context
.region
.height
:
373 y
= bpy
.context
.region
.height
375 area
["vert"] = (x
, y
)
377 def update_area_dimensions(area
, w
=0, h
=0):
381 class QCDMoveWidget(Operator
):
382 """Move objects to QCD Slots"""
383 bl_idname
= "view3d.qcd_move_widget"
384 bl_label
= "QCD Move Widget"
404 def modal(self
, context
, event
):
405 if event
.type == 'TIMER':
406 if self
.hover_time
and self
.hover_time
+ 0.5 < time
.time():
407 self
.draw_tooltip
= True
409 context
.area
.tag_redraw()
410 return {'RUNNING_MODAL'}
413 context
.area
.tag_redraw()
415 if len(self
.areas
) == 1:
416 return {'RUNNING_MODAL'}
418 if self
.last_type
== 'LEFTMOUSE' and self
.last_type_value
== 'PRESS' and event
.type == 'MOUSEMOVE':
419 if mouse_in_area(self
.mouse_pos
, self
.areas
["Grab Bar"]):
420 x_offset
= self
.areas
["Main Window"]["vert"][0] - self
.mouse_pos
[0]
421 x
= event
.mouse_region_x
+ x_offset
423 y_offset
= self
.areas
["Main Window"]["vert"][1] - self
.mouse_pos
[1]
424 y
= event
.mouse_region_y
+ y_offset
426 self
.areas
["Main Window"]["vert"] = (x
, y
)
428 self
.mouse_pos
= (event
.mouse_region_x
, event
.mouse_region_y
)
430 elif event
.type == 'MOUSEMOVE':
431 self
.draw_tooltip
= False
432 self
.hover_time
= None
433 self
.mouse_pos
= (event
.mouse_region_x
, event
.mouse_region_y
)
435 if not mouse_in_area(self
.mouse_pos
, self
.areas
["Main Window"], 50 * scale_factor()):
437 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
440 bpy
.ops
.ed
.undo_push()
445 self
.initialized
= True
447 elif event
.value
== 'PRESS' and event
.type == 'LEFTMOUSE':
448 if not mouse_in_area(self
.mouse_pos
, self
.areas
["Main Window"], 10 * scale_factor()):
449 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
452 bpy
.ops
.ed
.undo_push()
456 for num
in range(20):
457 if not self
.areas
.get(f
"Button {num + 1}", None):
460 if mouse_in_area(self
.mouse_pos
, self
.areas
[f
"Button {num + 1}"]):
461 bpy
.ops
.view3d
.move_to_qcd_slot(slot
=str(num
+ 1), toggle
=event
.shift
)
464 elif event
.type in {'RIGHTMOUSE', 'ESC'}:
465 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
469 if event
.value
== 'PRESS' and event
.type in self
.slots
:
470 move_to
= self
.slots
[event
.type]
476 bpy
.ops
.view3d
.move_to_qcd_slot(slot
=str(move_to
), toggle
=True)
478 bpy
.ops
.view3d
.move_to_qcd_slot(slot
=str(move_to
), toggle
=False)
482 if event
.type != 'MOUSEMOVE' and event
.type != 'INBETWEEN_MOUSEMOVE':
483 self
.last_type
= event
.type
484 self
.last_type_value
= event
.value
486 return {'RUNNING_MODAL'}
488 def invoke(self
, context
, event
):
489 if context
.area
.type == 'VIEW_3D':
490 # the arguments we pass the the callback
491 args
= (self
, context
)
492 # Add the region OpenGL drawing callback
493 # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
494 self
._handle
= bpy
.types
.SpaceView3D
.draw_handler_add(draw_callback_px
, args
, 'WINDOW', 'POST_PIXEL')
495 self
._timer
= context
.window_manager
.event_timer_add(0.1, window
=context
.window
)
497 self
.mouse_pos
= (event
.mouse_region_x
, event
.mouse_region_y
)
499 self
.draw_tooltip
= False
501 self
.hover_time
= None
505 # MAIN WINDOW BACKGROUND
506 x
= self
.mouse_pos
[0] - spacer()*2
507 y
= self
.mouse_pos
[1] + spacer()*2
516 self
.areas
["Main Window"] = main_window
517 allocate_main_ui(self
, context
)
518 account_for_view_bounds(main_window
)
520 context
.window_manager
.modal_handler_add(self
)
521 return {'RUNNING_MODAL'}
524 self
.report({'WARNING'}, "View3D not found, cannot run operator")
528 def allocate_main_ui(self
, context
):
529 main_window
= self
.areas
["Main Window"]
531 main_window
["width"] = 0
532 main_window
["height"] = 0
533 self
.areas
["Main Window"] = main_window
535 cur_width_pos
= main_window
["vert"][0]
536 cur_height_pos
= main_window
["vert"][1]
540 "vert": main_window
["vert"],
542 "height": round(23 * scale_factor()),
546 # add grab bar to areas
547 self
.areas
["Grab Bar"] = grab_bar
551 wt_indent_x
= spacer()*2
552 wt_y_offset
= round(spacer()/2)
554 "vert": main_window
["vert"],
556 "height": round(13 * scale_factor()),
557 "value": "Move Objects to QCD Slots"
560 x
= main_window
["vert"][0] + wt_indent_x
561 y
= main_window
["vert"][1] - window_title
["height"] - wt_y_offset
562 window_title
["vert"] = (x
, y
)
564 # add window title to areas
565 self
.areas
["Window Title"] = window_title
567 cur_height_pos
= window_title
["vert"][1]
571 button_size
= round(20 * scale_factor())
572 button_gap
= round(1 * scale_factor())
574 button_group_gap
= round(20 * scale_factor())
575 button_group_width
= button_size
* button_group
+ button_gap
* (button_group
- 1)
577 mba_indent_x
= spacer()*2
578 mba_outdent_x
= spacer()*2
579 mba_indent_y
= spacer()
580 x
= cur_width_pos
+ mba_indent_x
581 y
= cur_height_pos
- mba_indent_y
589 # add main button area to areas
590 self
.areas
["Main Button Area"] = main_button_area
592 # update current position
593 cur_width_pos
= main_button_area
["vert"][0]
594 cur_height_pos
= main_button_area
["vert"][1]
599 "vert": main_button_area
["vert"],
600 "width": button_group_width
,
601 "height": button_size
,
605 # add button row 1 A to areas
606 self
.areas
["Button Row 1 A"] = button_row_1_a
608 # advance width pos to start of next row
609 cur_width_pos
+= button_row_1_a
["width"]
610 cur_width_pos
+= button_group_gap
617 "width": button_group_width
,
618 "height": button_size
,
622 # add button row 1 B to areas
623 self
.areas
["Button Row 1 B"] = button_row_1_b
625 # reset width pos to start of main button area
626 cur_width_pos
= main_button_area
["vert"][0]
628 cur_height_pos
-= button_row_1_a
["height"]
629 # add gap between button rows
630 cur_height_pos
-= button_gap
638 "width": button_group_width
,
639 "height": button_size
,
643 # add button row 2 A to areas
644 self
.areas
["Button Row 2 A"] = button_row_2_a
646 # advance width pos to start of next row
647 cur_width_pos
+= button_row_2_a
["width"]
648 cur_width_pos
+= button_group_gap
655 "width": button_group_width
,
656 "height": button_size
,
660 # add button row 2 B to areas
661 self
.areas
["Button Row 2 B"] = button_row_2_b
664 selected_objects
= get_move_selection()
665 active_object
= get_move_active()
669 def get_buttons(button_row
, row_num
):
670 cur_width_pos
= button_row
["vert"][0]
671 cur_height_pos
= button_row
["vert"][1]
672 for num
in range(button_group
):
673 slot_num
= row_num
+ num
675 qcd_slot_name
= internals
.qcd_slots
.get_name(f
"{slot_num}")
678 qcd_laycol
= internals
.layer_collections
[qcd_slot_name
]["ptr"]
679 collection_objects
= qcd_laycol
.collection
.objects
686 "width": button_size
,
687 "height": button_size
,
691 self
.areas
[f
"Button {slot_num}"] = button
694 if active_object
and active_object
in selected_objects
and active_object
.name
in collection_objects
:
695 x
= cur_width_pos
+ round(button_size
/ 4)
696 y
= cur_height_pos
- round(button_size
/ 4)
697 active_object_indicator
= {
699 "width": floor(button_size
/ 2),
700 "height": floor(button_size
/ 2),
704 self
.areas
[f
"Button {slot_num} Active Object Indicator"] = active_object_indicator
706 elif not set(selected_objects
).isdisjoint(collection_objects
):
707 x
= cur_width_pos
+ round(button_size
/ 4) + floor(1 * scale_factor())
708 y
= cur_height_pos
- round(button_size
/ 4) - floor(1 * scale_factor())
709 selected_object_indicator
= {
711 "width": floor(button_size
/ 2) - floor(1 * scale_factor()),
712 "height": floor(button_size
/ 2) - floor(1 * scale_factor()),
716 self
.areas
[f
"Button {slot_num} Selected Object Indicator"] = selected_object_indicator
718 elif collection_objects
:
719 x
= cur_width_pos
+ floor(button_size
/ 4)
720 y
= cur_height_pos
- button_size
/ 2 + 1 * scale_factor()
723 "width": round(button_size
/ 2),
724 "height": round(2 * scale_factor()),
727 self
.areas
[f
"Button {slot_num} Object Indicator"] = object_indicator
730 x
= cur_width_pos
+ 2 * scale_factor()
731 y
= cur_height_pos
- 2 * scale_factor()
734 "width": button_size
- 4 * scale_factor(),
735 "height": button_size
- 4 * scale_factor(),
739 self
.areas
[f
"X_icon {slot_num}"] = X_icon
741 cur_width_pos
+= button_size
742 cur_width_pos
+= button_gap
744 get_buttons(button_row_1_a
, 1)
745 get_buttons(button_row_1_b
, 6)
746 get_buttons(button_row_2_a
, 11)
747 get_buttons(button_row_2_b
, 16)
750 # UPDATE DYNAMIC DIMENSIONS
751 width
= button_row_1_a
["width"] + button_group_gap
+ button_row_1_b
["width"]
752 height
= button_row_1_a
["height"] + button_gap
+ button_row_2_a
["height"]
753 update_area_dimensions(main_button_area
, width
, height
)
755 width
= main_button_area
["width"] + mba_indent_x
+ mba_outdent_x
756 height
= main_button_area
["height"] + mba_indent_y
* 2 + window_title
["height"] + wt_y_offset
757 update_area_dimensions(main_window
, width
, height
)
759 update_area_dimensions(grab_bar
, main_window
["width"])
762 def draw_callback_px(self
, context
):
763 allocate_main_ui(self
, context
)
765 shader
= gpu
.shader
.from_builtin('UNIFORM_COLOR')
766 line_shader
= gpu
.shader
.from_builtin('POLYLINE_SMOOTH_COLOR')
768 addon_prefs
= context
.preferences
.addons
[__package__
].preferences
770 # main window background
771 main_window
= self
.areas
["Main Window"]
772 outline_color
= addon_prefs
.qcd_ogl_widget_menu_back_outline
773 background_color
= addon_prefs
.qcd_ogl_widget_menu_back_inner
774 draw_rounded_rect(main_window
, line_shader
, outline_color
[:] + (1,), outline
=True)
775 draw_rounded_rect(main_window
, shader
, background_color
)
778 window_title
= self
.areas
["Window Title"]
779 x
= window_title
["vert"][0]
780 y
= window_title
["vert"][1]
781 h
= window_title
["height"]
782 text
= window_title
["value"]
783 text_color
= addon_prefs
.qcd_ogl_widget_menu_back_text
785 blf
.position(font_id
, x
, y
, 0)
786 blf
.size(font_id
, int(h
))
787 blf
.color(font_id
, text_color
[0], text_color
[1], text_color
[2], 1)
788 blf
.draw(font_id
, text
)
790 in_tooltip_area
= False
791 tooltip_slot_idx
= None
793 for num
in range(20):
795 qcd_slot_name
= internals
.qcd_slots
.get_name(f
"{slot_num}")
797 qcd_laycol
= internals
.layer_collections
[qcd_slot_name
]["ptr"]
798 collection_objects
= qcd_laycol
.collection
.objects
799 selected_objects
= get_move_selection()
800 active_object
= get_move_active()
801 button_area
= self
.areas
[f
"Button {slot_num}"]
804 button_color
= addon_prefs
.qcd_ogl_widget_tool_inner
805 icon_color
= addon_prefs
.qcd_ogl_widget_tool_text
806 if not qcd_laycol
.exclude
:
807 button_color
= addon_prefs
.qcd_ogl_widget_tool_inner_sel
808 icon_color
= addon_prefs
.qcd_ogl_widget_tool_text_sel
810 if mouse_in_area(self
.mouse_pos
, button_area
):
811 in_tooltip_area
= True
812 tooltip_slot_idx
= slot_num
816 if button_color
[0] + mod
> 1 or button_color
[1] + mod
> 1 or button_color
[2] + mod
> 1:
820 button_color
[0] + mod
,
821 button_color
[1] + mod
,
822 button_color
[2] + mod
,
828 tl
= tr
= bl
= br
= 0
832 if not internals
.qcd_slots
.contains(idx
=f
"{num+2}"):
835 if not internals
.qcd_slots
.contains(idx
=f
"{num}"):
838 if not internals
.qcd_slots
.contains(idx
=f
"{num+2}"):
841 if not internals
.qcd_slots
.contains(idx
=f
"{num}"):
854 outline_color
= addon_prefs
.qcd_ogl_widget_tool_outline
855 draw_rounded_rect(button_area
, line_shader
, outline_color
[:] + (1,), tl
, tr
, bl
, br
, outline
=True)
856 draw_rounded_rect(button_area
, shader
, button_color
, tl
, tr
, bl
, br
)
859 if active_object
and active_object
in selected_objects
and active_object
.name
in collection_objects
:
860 active_object_indicator
= self
.areas
[f
"Button {slot_num} Active Object Indicator"]
862 vertices
= get_circle_coords(active_object_indicator
)
863 batch
= batch_for_shader(shader
, 'TRI_FAN', {"pos": vertices
})
865 shader
.uniform_float("color", icon_color
[:] + (1,))
867 gpu
.state
.blend_set('ALPHA')
871 gpu
.state
.blend_set('NONE')
874 elif not set(selected_objects
).isdisjoint(collection_objects
):
875 selected_object_indicator
= self
.areas
[f
"Button {slot_num} Selected Object Indicator"]
877 alpha
= addon_prefs
.qcd_ogl_selected_icon_alpha
878 vertices
= get_circle_coords(selected_object_indicator
)
879 line_shader
.uniform_float("lineWidth", 2 * scale_factor())
880 color
= icon_color
[:] + (alpha
,)
881 batch
= batch_for_shader(line_shader
, 'LINE_STRIP', {"pos": [(v
[0], v
[1], 0) for v
in vertices
], "color": [color
for v
in vertices
]})
884 gpu
.state
.blend_set('ALPHA')
886 batch
.draw(line_shader
)
888 gpu
.state
.blend_set('NONE')
891 elif collection_objects
:
892 object_indicator
= self
.areas
[f
"Button {slot_num} Object Indicator"]
894 alpha
= addon_prefs
.qcd_ogl_objects_icon_alpha
895 vertices
, indices
= get_coords(object_indicator
)
896 batch
= batch_for_shader(shader
, 'TRIS', {"pos": vertices
}, indices
=indices
)
898 shader
.uniform_float("color", icon_color
[:] + (alpha
,))
900 gpu
.state
.blend_set('ALPHA')
904 gpu
.state
.blend_set('NONE')
909 X_icon
= self
.areas
[f
"X_icon {slot_num}"]
910 X_icon_color
= addon_prefs
.qcd_ogl_widget_menu_back_text
912 vertices
, indices
= get_x_coords(X_icon
)
913 batch
= batch_for_shader(shader
, 'TRIS', {"pos": vertices
}, indices
=indices
)
915 shader
.uniform_float("color", X_icon_color
[:] + (1,))
917 gpu
.state
.blend_set('ALPHA')
921 gpu
.state
.blend_set('NONE')
924 if self
.draw_tooltip
:
925 slot_name
= internals
.qcd_slots
.get_name(f
"{tooltip_slot_idx}")
926 slot_string
= f
"QCD Slot {tooltip_slot_idx}: \"{slot_name}\"\n"
928 " * LMB - Move objects to slot.\n"
929 " * Shift+LMB - Toggle objects\' slot."
932 draw_tooltip(self
, context
, shader
, line_shader
, f
"{slot_string}{hotkey_string}")
934 self
.hover_time
= None
937 if not self
.hover_time
:
938 self
.hover_time
= time
.time()
941 def draw_tooltip(self
, context
, shader
, line_shader
, message
):
942 addon_prefs
= context
.preferences
.addons
[__package__
].preferences
945 line_height
= 11 * scale_factor()
946 text_color
= addon_prefs
.qcd_ogl_widget_tooltip_text
947 blf
.size(font_id
, int(line_height
))
948 blf
.color(font_id
, text_color
[0], text_color
[1], text_color
[2], 1)
950 lines
= message
.split("\n")
952 num_lines
= len(lines
)
955 w
, _
= blf
.dimensions(font_id
, line
)
961 w
, h
= blf
.dimensions(font_id
, longest
[1])
963 line_spacer
= 1 * scale_factor()
964 padding
= 4 * scale_factor()
968 "vert": self
.mouse_pos
,
969 "width": w
+ spacer()*2,
970 "height": (line_height
* num_lines
+ line_spacer
* num_lines
) + padding
*3,
974 x
= tooltip
["vert"][0] - spacer()*2
975 y
= tooltip
["vert"][1] + tooltip
["height"] + round(5 * scale_factor())
976 tooltip
["vert"] = (x
, y
)
978 account_for_view_bounds(tooltip
)
980 outline_color
= addon_prefs
.qcd_ogl_widget_tooltip_outline
981 background_color
= addon_prefs
.qcd_ogl_widget_tooltip_inner
982 draw_rounded_rect(tooltip
, line_shader
, outline_color
[:] + (1,), outline
=True)
983 draw_rounded_rect(tooltip
, shader
, background_color
)
985 line_pos
= padding
+ line_height
987 for num
, line
in enumerate(lines
):
988 x
= tooltip
["vert"][0] + spacer()
989 y
= tooltip
["vert"][1] - line_pos
990 blf
.position(font_id
, x
, y
, 0)
991 blf
.draw(font_id
, line
)
993 line_pos
+= line_height
+ line_spacer