work
save startup blend for animation tab & whatnot
This commit is contained in:
@@ -1,14 +1,12 @@
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import blf
|
||||
import bpy
|
||||
import gpu
|
||||
|
||||
from .. import image_utils, ui_bgl
|
||||
from .bl_ui_widget import BL_UI_Widget
|
||||
from .bl_ui_image import BL_UI_Image
|
||||
from .bl_ui_widget import BL_UI_Widget, region_redraw
|
||||
|
||||
bk_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -18,6 +16,8 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
|
||||
def __init__(self, x, y, width, height):
|
||||
super().__init__(x, y, width, height)
|
||||
self.background = True
|
||||
self.background_padding = (0, 0)
|
||||
self._text_color = (1.0, 1.0, 1.0, 1.0)
|
||||
self._hover_bg_color = (0.5, 0.5, 0.5, 1.0)
|
||||
self._select_bg_color = (0.7, 0.7, 0.7, 1.0)
|
||||
@@ -30,6 +30,8 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
self.__image = None
|
||||
self.__image_size = (24, 24)
|
||||
self.__image_position = (4, 2)
|
||||
self.__image_padding = 0.0
|
||||
self.image_corner_radius = None
|
||||
|
||||
@property
|
||||
def text_color(self):
|
||||
@@ -38,7 +40,7 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
@text_color.setter
|
||||
def text_color(self, value):
|
||||
if value != self._text_color:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._text_color = value
|
||||
|
||||
@property
|
||||
@@ -48,7 +50,7 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
@text.setter
|
||||
def text(self, value):
|
||||
if value != self._text:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._text = value
|
||||
|
||||
@property
|
||||
@@ -58,7 +60,7 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
@text_size.setter
|
||||
def text_size(self, value):
|
||||
if value != self._text_size:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._text_size = value
|
||||
|
||||
@property
|
||||
@@ -68,7 +70,7 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
@hover_bg_color.setter
|
||||
def hover_bg_color(self, value):
|
||||
if value != self._hover_bg_color:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._hover_bg_color = value
|
||||
|
||||
@property
|
||||
@@ -78,7 +80,7 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
@select_bg_color.setter
|
||||
def select_bg_color(self, value):
|
||||
if value != self._select_bg_color:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._select_bg_color = value
|
||||
|
||||
def set_image_size(self, image_size):
|
||||
@@ -93,13 +95,16 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
self.__image
|
||||
self.__image.filepath
|
||||
# self.__image.pixels
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
self.__image = None
|
||||
|
||||
def set_image_colorspace(self, colorspace: str = ""):
|
||||
image_utils.set_colorspace(self.__image, colorspace)
|
||||
|
||||
def set_image(self, rel_filepath):
|
||||
if rel_filepath is None:
|
||||
self.__image = None
|
||||
return
|
||||
# first try to access the image, for cases where it can get removed
|
||||
self.check_image_exists()
|
||||
try:
|
||||
@@ -117,6 +122,17 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
return None
|
||||
return self.__image.filepath
|
||||
|
||||
@property
|
||||
def image_padding(self):
|
||||
return self.__image_padding
|
||||
|
||||
@image_padding.setter
|
||||
def image_padding(self, padding: float):
|
||||
self.__image_padding = padding
|
||||
|
||||
def get_image_padding(self):
|
||||
return self.__image_padding
|
||||
|
||||
def update(self, x, y):
|
||||
super().update(x, y)
|
||||
self._textpos = [x, y]
|
||||
@@ -127,19 +143,30 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
area_height = self.get_area_height()
|
||||
|
||||
gpu.state.blend_set("ALPHA")
|
||||
fill_color = self._resolve_panel_color()
|
||||
|
||||
self.shader.bind()
|
||||
|
||||
self.set_colors()
|
||||
|
||||
self.batch_panel.draw(self.shader)
|
||||
if self.use_rounded_background:
|
||||
rect_y = area_height - self.y_screen - self.height
|
||||
self.draw_background_rect(
|
||||
self.x_screen,
|
||||
rect_y,
|
||||
self.width,
|
||||
self.height,
|
||||
fill_color,
|
||||
force=True,
|
||||
fill_color_override=fill_color,
|
||||
)
|
||||
else:
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", fill_color)
|
||||
self.batch_panel.draw(self.shader)
|
||||
|
||||
self.draw_image()
|
||||
|
||||
# Draw text
|
||||
self.draw_text(area_height)
|
||||
|
||||
def set_colors(self):
|
||||
def _resolve_panel_color(self):
|
||||
color = self._bg_color
|
||||
|
||||
# pressed
|
||||
@@ -150,7 +177,7 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
elif self.__state == 2:
|
||||
color = self._hover_bg_color
|
||||
|
||||
self.shader.uniform_float("color", color)
|
||||
return color
|
||||
|
||||
def draw_text(self, area_height):
|
||||
font_id = 1
|
||||
@@ -165,9 +192,19 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
|
||||
size = blf.dimensions(font_id, self._text)
|
||||
|
||||
# When an image is present, center text in the remaining space after the image
|
||||
image_offset = 0
|
||||
if self.__image is not None:
|
||||
image_offset = self.__image_position[0] + self.__image_size[0]
|
||||
|
||||
textpos_y = area_height - self._textpos[1] - (self.height + size[1]) / 2.0
|
||||
blf.position(
|
||||
font_id, self._textpos[0] + (self.width - size[0]) / 2.0, textpos_y + 1, 0
|
||||
font_id,
|
||||
self._textpos[0]
|
||||
+ image_offset
|
||||
+ (self.width - image_offset - size[0]) / 2.0,
|
||||
textpos_y + 1,
|
||||
0,
|
||||
)
|
||||
|
||||
r, g, b, a = self._text_color
|
||||
@@ -180,15 +217,17 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
y_screen_flip = self.get_area_height() - self.y_screen
|
||||
off_x, off_y = self.__image_position
|
||||
sx, sy = self.__image_size
|
||||
pad = self.__image_padding
|
||||
ui_bgl.draw_image_runtime(
|
||||
self.x_screen + off_x,
|
||||
y_screen_flip - off_y - sy,
|
||||
sx,
|
||||
sy,
|
||||
self.x_screen + off_x + pad,
|
||||
y_screen_flip - off_y - sy + pad,
|
||||
sx - 2 * pad,
|
||||
sy - 2 * pad,
|
||||
self.__image,
|
||||
1.0,
|
||||
crop=(0, 0, 1, 1),
|
||||
batch=None,
|
||||
corner_radius=self.image_corner_radius,
|
||||
)
|
||||
return True
|
||||
return False
|
||||
@@ -203,9 +242,7 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
self.mouse_down_func(self)
|
||||
except Exception:
|
||||
bk_logger.exception("BL_UI_BUTTON mouse_down() error:")
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def set_mouse_down_right(self, mouse_down_right_func):
|
||||
@@ -213,10 +250,11 @@ class BL_UI_Button(BL_UI_Widget):
|
||||
|
||||
def mouse_down_right(self, x, y):
|
||||
if self.is_in_rect(x, y):
|
||||
try:
|
||||
self.mouse_down_right_func(self)
|
||||
except Exception:
|
||||
bk_logger.exception("BL_UI_BUTTON mouse_down_right() error:")
|
||||
if hasattr(self, "mouse_down_right_func"):
|
||||
try:
|
||||
self.mouse_down_right_func(self)
|
||||
except Exception:
|
||||
bk_logger.exception("BL_UI_BUTTON mouse_down_right() error:")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -20,6 +20,15 @@ class BL_UI_Drag_Panel(BL_UI_Widget):
|
||||
self.widgets = widgets
|
||||
self.layout_widgets()
|
||||
|
||||
def remove_widgets(self):
|
||||
self.widgets = []
|
||||
self.update(self.x_screen, self.y_screen)
|
||||
|
||||
def remove_widget(self, widget):
|
||||
if widget in self.widgets:
|
||||
self.widgets.remove(widget)
|
||||
self.layout_widgets()
|
||||
|
||||
def layout_widgets(self):
|
||||
for widget in self.widgets:
|
||||
widget.update(self.x_screen + widget.x, self.y_screen + widget.y)
|
||||
|
||||
@@ -4,9 +4,34 @@ from typing import Optional
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
from .. import ui_bgl
|
||||
from .bl_ui_widget import region_redraw
|
||||
|
||||
bk_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_safely(obj, attr_name, default=None):
|
||||
"""Get attribute from object while tolerating freed data."""
|
||||
try:
|
||||
return getattr(obj, attr_name, default)
|
||||
except ReferenceError:
|
||||
return default
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
|
||||
def restart_asset_bar():
|
||||
# ignore failures if already gone
|
||||
from asset_bar_op import BlenderKitAssetBarOperator
|
||||
|
||||
try:
|
||||
bpy.utils.unregister_class(BlenderKitAssetBarOperator)
|
||||
except Exception:
|
||||
pass
|
||||
bpy.utils.register_class(BlenderKitAssetBarOperator)
|
||||
bpy.ops.view3d.blenderkit_asset_bar_widget("INVOKE_DEFAULT")
|
||||
|
||||
|
||||
class BL_UI_OT_draw_operator(Operator):
|
||||
bl_idname = "object.bl_ui_ot_draw_operator"
|
||||
bl_label = "bl ui widgets operator"
|
||||
@@ -36,10 +61,6 @@ class BL_UI_OT_draw_operator(Operator):
|
||||
def invoke(self, context, event):
|
||||
self.on_invoke(context, event)
|
||||
|
||||
args = (self, context)
|
||||
|
||||
self.register_handlers(args, context, timer_interval=self._timer_interval)
|
||||
|
||||
context.window_manager.modal_handler_add(self)
|
||||
|
||||
# first set pointers to keep track if the area is still available
|
||||
@@ -47,7 +68,10 @@ class BL_UI_OT_draw_operator(Operator):
|
||||
self.active_area_pointer = context.area.as_pointer()
|
||||
self.active_region_pointer = context.region.as_pointer()
|
||||
|
||||
context.region.tag_redraw()
|
||||
args = (self, context)
|
||||
self.register_handlers(args, context, timer_interval=self._timer_interval)
|
||||
|
||||
region_redraw(context)
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def register_handlers(self, args, context, timer_interval=0.1):
|
||||
@@ -80,7 +104,7 @@ class BL_UI_OT_draw_operator(Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
if context.area:
|
||||
context.region.tag_redraw()
|
||||
region_redraw(context)
|
||||
|
||||
if self.handle_widget_events(event):
|
||||
return {"RUNNING_MODAL"}
|
||||
@@ -93,8 +117,7 @@ class BL_UI_OT_draw_operator(Operator):
|
||||
def finish(self):
|
||||
self.unregister_handlers(bpy.context)
|
||||
# it is possible that the area has been closed, so we check if it is still available
|
||||
if bpy.context.region is not None:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self.on_finish(bpy.context)
|
||||
|
||||
# Draw handler to paint onto the screen
|
||||
@@ -114,7 +137,27 @@ def draw_callback_px_separated(self, op, context):
|
||||
# hide during animation playback, to improve performance
|
||||
if context.screen.is_animation_playing:
|
||||
return
|
||||
if context.area.as_pointer() == self.active_area_pointer:
|
||||
area_pointer = (
|
||||
context.area.as_pointer() if getattr(context, "area", None) else None
|
||||
)
|
||||
|
||||
# get area, check if RNA failed
|
||||
active_pointer = get_safely(self, "active_area_pointer", None)
|
||||
if area_pointer is None or area_pointer != active_pointer:
|
||||
return
|
||||
|
||||
active_region_pointer = get_safely(self, "active_region_pointer", None)
|
||||
if active_region_pointer is not None:
|
||||
region_pointer = (
|
||||
context.region.as_pointer()
|
||||
if getattr(context, "region", None)
|
||||
else None
|
||||
)
|
||||
if region_pointer is None or region_pointer != active_region_pointer:
|
||||
return
|
||||
|
||||
region = getattr(context, "region", None)
|
||||
with ui_bgl.overlay_matrix_guard(region):
|
||||
for widget in self.widgets:
|
||||
widget.draw()
|
||||
except Exception:
|
||||
|
||||
@@ -18,11 +18,21 @@ class BL_UI_Image(BL_UI_Widget):
|
||||
|
||||
def __init__(self, x, y, width, height):
|
||||
super().__init__(x, y, width, height)
|
||||
self.bg_color = (1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
self.__state = 0
|
||||
self.__image = None
|
||||
self.__image_size = (24, 24)
|
||||
self.__image_position = (4, 2)
|
||||
self.__image_padding: float = 0.0
|
||||
|
||||
@property
|
||||
def image_padding(self):
|
||||
return self.__image_padding
|
||||
|
||||
@image_padding.setter
|
||||
def image_padding(self, value: float):
|
||||
self.__image_padding = value
|
||||
|
||||
def set_image_size(self, image_size):
|
||||
self.__image_size = image_size
|
||||
@@ -67,25 +77,68 @@ class BL_UI_Image(BL_UI_Widget):
|
||||
return
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
self.shader.bind()
|
||||
self.batch_panel.draw(self.shader)
|
||||
if self.draw_image():
|
||||
return
|
||||
|
||||
self.draw_image()
|
||||
if self.use_rounded_background:
|
||||
area_height = self.get_area_height()
|
||||
rect_y = area_height - self.y_screen - self.height
|
||||
self.draw_background_rect(
|
||||
self.x_screen,
|
||||
rect_y,
|
||||
self.width,
|
||||
self.height,
|
||||
self._bg_color,
|
||||
force=True,
|
||||
fill_color_override=self._bg_color,
|
||||
)
|
||||
return
|
||||
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", self._bg_color)
|
||||
|
||||
self.batch_panel.draw(self.shader)
|
||||
|
||||
def draw_image(self):
|
||||
if self.__image is not None:
|
||||
y_screen_flip = self.get_area_height() - self.y_screen
|
||||
off_x, off_y = self.__image_position
|
||||
sx, sy = self.__image_size
|
||||
pad = self.image_padding
|
||||
img_x = self.x_screen + off_x + pad
|
||||
img_y = y_screen_flip - off_y - sy + pad
|
||||
img_w = sx - 2 * pad
|
||||
img_h = sy - 2 * pad
|
||||
|
||||
if self.use_rounded_background:
|
||||
fill_color = self.bg_color or (1.0, 1.0, 1.0, 1.0)
|
||||
self.draw_background_rect(
|
||||
img_x,
|
||||
img_y,
|
||||
img_w,
|
||||
img_h,
|
||||
fill_color,
|
||||
force=True,
|
||||
fill_color_override=fill_color,
|
||||
)
|
||||
|
||||
corner_radius = (
|
||||
self.background_corner_radius
|
||||
if self.has_background_corner_radius_override()
|
||||
else None
|
||||
)
|
||||
|
||||
ui_bgl.draw_image_runtime(
|
||||
self.x_screen + off_x,
|
||||
y_screen_flip - off_y - sy,
|
||||
sx,
|
||||
sy,
|
||||
img_x,
|
||||
img_y,
|
||||
img_w,
|
||||
img_h,
|
||||
self.__image,
|
||||
1.0,
|
||||
crop=(0, 0, 1, 1),
|
||||
batch=None,
|
||||
corner_radius=corner_radius,
|
||||
corner_segments=12,
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import blf
|
||||
import bpy
|
||||
import gpu
|
||||
|
||||
from typing import Tuple, Union
|
||||
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
from .bl_ui_widget import BL_UI_Widget
|
||||
from .bl_ui_widget import BL_UI_Widget, region_redraw, set_font_size
|
||||
|
||||
|
||||
class BL_UI_Label(BL_UI_Widget):
|
||||
"""A simple text label widget."""
|
||||
|
||||
def __init__(self, x, y, width, height):
|
||||
super().__init__(x, y, width, height)
|
||||
self._set_background_corner_radius_default((4.0,))
|
||||
|
||||
self._text_color = (1.0, 1.0, 1.0, 1.0)
|
||||
self._text = "Label"
|
||||
@@ -32,7 +34,7 @@ class BL_UI_Label(BL_UI_Widget):
|
||||
@text_color.setter
|
||||
def text_color(self, value):
|
||||
if value != self._text_color:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._text_color = value
|
||||
|
||||
@property
|
||||
@@ -42,13 +44,173 @@ class BL_UI_Label(BL_UI_Widget):
|
||||
@text.setter
|
||||
def text(self, value):
|
||||
if value != self._text:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._text = value
|
||||
|
||||
@property
|
||||
def text_size(self):
|
||||
return self._text_size
|
||||
|
||||
@text_size.setter
|
||||
def text_size(self, value):
|
||||
if value != self._text_size:
|
||||
region_redraw()
|
||||
self._text_size = value
|
||||
|
||||
def is_in_rect(self, x, y):
|
||||
return False
|
||||
|
||||
def draw(self):
|
||||
if not self._is_visible or not self._text:
|
||||
return
|
||||
|
||||
area_height = self.get_area_height()
|
||||
font_id = 1
|
||||
set_font_size(font_id, self._text_size)
|
||||
|
||||
textpos_y = area_height - self.y_screen - self.height
|
||||
|
||||
x = self.x_screen
|
||||
y = textpos_y
|
||||
block_width, block_height = blf.dimensions(font_id, self._text)
|
||||
if self._halign == "RIGHT":
|
||||
x -= block_width
|
||||
elif self._halign == "CENTER":
|
||||
x -= block_width // 2
|
||||
if self._halign != "LEFT" and self._valign == "CENTER":
|
||||
y -= block_height // 2
|
||||
|
||||
lines = self._text.split("\n") if self.multiline else [self._text]
|
||||
entries = []
|
||||
cursor_y = y
|
||||
for index, line in enumerate(lines):
|
||||
if index > 0:
|
||||
cursor_y -= self.row_height
|
||||
width, height = blf.dimensions(font_id, line)
|
||||
if self.multiline and height == 0:
|
||||
height = self.row_height
|
||||
elif height == 0:
|
||||
height = self._text_size
|
||||
entries.append(
|
||||
{
|
||||
"text": line,
|
||||
"x": x,
|
||||
"y": cursor_y,
|
||||
"width": width,
|
||||
"height": height,
|
||||
}
|
||||
)
|
||||
|
||||
if not entries:
|
||||
return
|
||||
|
||||
min_x = min(item["x"] for item in entries)
|
||||
max_x = max(item["x"] + item["width"] for item in entries)
|
||||
min_y = min(item["y"] for item in entries)
|
||||
max_y = max(item["y"] + item["height"] for item in entries)
|
||||
content_width = max(0.0, max_x - min_x)
|
||||
content_height = max(0.0, max_y - min_y)
|
||||
|
||||
self.draw_background_rect(
|
||||
min_x,
|
||||
min_y,
|
||||
content_width,
|
||||
content_height,
|
||||
self._bg_color,
|
||||
)
|
||||
|
||||
r, g, b, a = self._text_color
|
||||
for item in entries:
|
||||
if not item["text"]:
|
||||
continue
|
||||
blf.position(font_id, item["x"], item["y"], 0)
|
||||
blf.color(font_id, r, g, b, a)
|
||||
blf.draw(font_id, item["text"])
|
||||
|
||||
if content_width > 0:
|
||||
strike_y = min_y + content_height * 0.5
|
||||
self.draw_strikethrough(
|
||||
min_x,
|
||||
max_x,
|
||||
strike_y,
|
||||
self._text_color,
|
||||
)
|
||||
|
||||
|
||||
class BL_UI_DuoLabel(BL_UI_Widget):
|
||||
"""A label with two text fields, A and B."""
|
||||
|
||||
def __init__(self, x, y, width, height):
|
||||
super().__init__(x, y, width, height)
|
||||
self._set_background_corner_radius_default((4.0,))
|
||||
|
||||
self._text_a_color = (1.0, 1.0, 1.0, 1.0)
|
||||
self._text_a = "Label"
|
||||
|
||||
self._text_b_color = (1.0, 1.0, 1.0, 1.0)
|
||||
self._text_b = ""
|
||||
|
||||
self._text_size = 16
|
||||
self._halign = "LEFT"
|
||||
self._valign = "TOP"
|
||||
# multiline
|
||||
self.multiline = False
|
||||
self.row_height = 20
|
||||
self.strikethrough_a = False
|
||||
self.strikethrough_b = False
|
||||
self.segment_backgrounds = False
|
||||
self.segment_background_padding = None
|
||||
self.segment_background_color_a = None
|
||||
self.segment_background_color_b = None
|
||||
self.segment_background_gap = 0.0
|
||||
self.segment_spacing = 4.0
|
||||
self.segment_background_extra_top = 0.0
|
||||
self.segment_background_extra_bottom = 1.5
|
||||
|
||||
@property
|
||||
def text_a_color(self):
|
||||
return self._text_a_color
|
||||
|
||||
@text_a_color.setter
|
||||
def text_a_color(self, value):
|
||||
if value != self._text_a_color:
|
||||
bpy.context.region.tag_redraw()
|
||||
self._text_a_color = value
|
||||
|
||||
@property
|
||||
def text_a(self):
|
||||
return self._text_a
|
||||
|
||||
@text_a.setter
|
||||
def text_a(self, value):
|
||||
if value != self._text_a:
|
||||
bpy.context.region.tag_redraw()
|
||||
self._text_a = value
|
||||
|
||||
@property
|
||||
def text_b_color(self):
|
||||
return self._text_b_color
|
||||
|
||||
@text_b_color.setter
|
||||
def text_b_color(self, value):
|
||||
if value != self._text_b_color:
|
||||
bpy.context.region.tag_redraw()
|
||||
self._text_b_color = value
|
||||
|
||||
@property
|
||||
def text_b(self):
|
||||
return self._text_b
|
||||
|
||||
@text_b.setter
|
||||
def text_b(self, value):
|
||||
if value != self._text_b:
|
||||
bpy.context.region.tag_redraw()
|
||||
self._text_b = value
|
||||
|
||||
@property
|
||||
def text_size(self):
|
||||
return self._text_size
|
||||
|
||||
@text_size.setter
|
||||
def text_size(self, value):
|
||||
if value != self._text_size:
|
||||
@@ -65,98 +227,266 @@ class BL_UI_Label(BL_UI_Widget):
|
||||
area_height = self.get_area_height()
|
||||
|
||||
font_id = 1
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
blf.size(font_id, self._text_size, 72)
|
||||
else:
|
||||
blf.size(font_id, self._text_size)
|
||||
lines = self._text.split("\n") if self.multiline else [self._text]
|
||||
if not lines:
|
||||
return
|
||||
|
||||
default_line_height = self.row_height if self.multiline else self._text_size
|
||||
line_metrics = []
|
||||
max_line_width = 0.0
|
||||
total_height = 0.0
|
||||
|
||||
for line in lines:
|
||||
width, height = blf.dimensions(font_id, line)
|
||||
if height == 0:
|
||||
height = default_line_height
|
||||
line_height = (
|
||||
self.row_height if self.multiline else max(height, self._text_size)
|
||||
)
|
||||
if line_height == 0:
|
||||
line_height = default_line_height
|
||||
line_metrics.append((line, width, line_height))
|
||||
max_line_width = max(max_line_width, width)
|
||||
total_height += line_height
|
||||
|
||||
if not line_metrics:
|
||||
return
|
||||
set_font_size(font_id, self._text_size)
|
||||
|
||||
textpos_y = area_height - self.y_screen - self.height
|
||||
|
||||
r, g, b, a = self._text_color
|
||||
x = self.x_screen
|
||||
y = textpos_y
|
||||
if self._halign != "LEFT":
|
||||
width, height = blf.dimensions(font_id, self._text)
|
||||
if self._halign == "RIGHT":
|
||||
x -= width
|
||||
elif self._halign == "CENTER":
|
||||
x -= width // 2
|
||||
cursor_x = self.x_screen
|
||||
spacing = max(0.0, float(self.segment_spacing))
|
||||
blocks = [
|
||||
(
|
||||
self._text_a,
|
||||
self._text_a_color,
|
||||
self.strikethrough_a,
|
||||
self.segment_background_color_a,
|
||||
),
|
||||
(
|
||||
self._text_b,
|
||||
self._text_b_color,
|
||||
self.strikethrough_b,
|
||||
self.segment_background_color_b,
|
||||
),
|
||||
]
|
||||
segments = []
|
||||
for text, color, strike_flag, background_color in blocks:
|
||||
if not text:
|
||||
continue
|
||||
|
||||
set_font_size(font_id, self._text_size)
|
||||
width, height = blf.dimensions(font_id, text)
|
||||
scaled_size = self._text_size
|
||||
scaled = False
|
||||
if self.width > 0:
|
||||
if self._halign == "LEFT":
|
||||
available_width = max(1, self.x_screen + self.width - cursor_x)
|
||||
else:
|
||||
available_width = self.width
|
||||
if width > available_width and width > 0:
|
||||
scale = available_width / width
|
||||
scaled_size = max(8, int(self._text_size * scale))
|
||||
if scaled_size < self._text_size:
|
||||
set_font_size(font_id, scaled_size)
|
||||
width, height = blf.dimensions(font_id, text)
|
||||
scaled = True
|
||||
|
||||
x = cursor_x if self._halign == "LEFT" else self.x_screen
|
||||
y = textpos_y
|
||||
if self._halign != "LEFT":
|
||||
if self._halign == "RIGHT":
|
||||
x -= width
|
||||
elif self._halign == "CENTER":
|
||||
x -= width // 2
|
||||
if self._valign == "CENTER":
|
||||
y -= height // 2
|
||||
# bottom could be here but there's no reason for it
|
||||
|
||||
first_line_height = line_metrics[0][2]
|
||||
if not self.multiline:
|
||||
lines = [
|
||||
{
|
||||
"text": text,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"width": width,
|
||||
"height": height or self._text_size,
|
||||
"font_size": scaled_size,
|
||||
}
|
||||
]
|
||||
else:
|
||||
lines = []
|
||||
current_y = y
|
||||
split_lines = text.split("\n")
|
||||
for index, line in enumerate(split_lines):
|
||||
if index > 0:
|
||||
current_y -= self.row_height
|
||||
line_width, line_height = blf.dimensions(font_id, line)
|
||||
if line_height == 0:
|
||||
line_height = self.row_height
|
||||
lines.append(
|
||||
{
|
||||
"text": line,
|
||||
"x": x,
|
||||
"y": current_y,
|
||||
"width": line_width,
|
||||
"height": line_height,
|
||||
"font_size": scaled_size,
|
||||
}
|
||||
)
|
||||
width = max((line["width"] for line in lines), default=width)
|
||||
height = max(len(lines) * self.row_height, height)
|
||||
|
||||
if self.background and (max_line_width > 0 or total_height > 0):
|
||||
pad_x, pad_y = self._padding_tuple()
|
||||
text_top = y + first_line_height
|
||||
text_bottom = text_top - total_height
|
||||
left = x - pad_x
|
||||
right = x + max_line_width + pad_x
|
||||
top = text_top + pad_y
|
||||
bottom = text_bottom - pad_y
|
||||
self._draw_background_rect(left, right, bottom, top)
|
||||
if lines:
|
||||
seg_min_x = min(line["x"] for line in lines)
|
||||
seg_max_x = max(line["x"] + line["width"] for line in lines)
|
||||
seg_min_y = min(line["y"] for line in lines)
|
||||
seg_max_y = max(line["y"] + line["height"] for line in lines)
|
||||
bounds = {
|
||||
"min_x": seg_min_x,
|
||||
"max_x": seg_max_x,
|
||||
"min_y": seg_min_y,
|
||||
"max_y": seg_max_y,
|
||||
}
|
||||
else:
|
||||
bounds = None
|
||||
|
||||
current_y = y
|
||||
if not self.multiline:
|
||||
blf.position(font_id, x, current_y, 0)
|
||||
blf.color(font_id, r, g, b, a)
|
||||
blf.draw(font_id, self._text)
|
||||
else:
|
||||
for line, _, line_height in line_metrics:
|
||||
blf.position(font_id, x, current_y, 0)
|
||||
segments.append(
|
||||
{
|
||||
"lines": lines,
|
||||
"color": color,
|
||||
"strikethrough": strike_flag,
|
||||
"bounds": bounds,
|
||||
"background_color": background_color,
|
||||
}
|
||||
)
|
||||
|
||||
if self._halign == "LEFT" and bounds:
|
||||
cursor_x = bounds["max_x"] + spacing
|
||||
|
||||
if scaled:
|
||||
set_font_size(font_id, self._text_size)
|
||||
|
||||
if not segments:
|
||||
return
|
||||
|
||||
all_lines = [line for segment in segments for line in segment["lines"]]
|
||||
if not all_lines:
|
||||
return
|
||||
|
||||
min_x = min(line["x"] for line in all_lines)
|
||||
max_x = max(line["x"] + line["width"] for line in all_lines)
|
||||
min_y = min(line["y"] for line in all_lines)
|
||||
max_y = max(line["y"] + line["height"] for line in all_lines)
|
||||
content_width = max(0.0, max_x - min_x)
|
||||
content_height = max(0.0, max_y - min_y)
|
||||
|
||||
if self.segment_backgrounds:
|
||||
pad_source = (
|
||||
self.segment_background_padding
|
||||
if self.segment_background_padding is not None
|
||||
else self.background_padding
|
||||
)
|
||||
if isinstance(pad_source, (list, tuple)):
|
||||
base_pad_x = float(pad_source[0])
|
||||
base_pad_y = (
|
||||
float(pad_source[1])
|
||||
if len(pad_source) > 1
|
||||
else float(pad_source[0])
|
||||
)
|
||||
else:
|
||||
base_pad_x = base_pad_y = float(pad_source)
|
||||
|
||||
bounded_segments = [seg for seg in segments if seg.get("bounds")]
|
||||
total_bounded = len(bounded_segments)
|
||||
desired_gap = max(0.0, float(self.segment_background_gap))
|
||||
spacing = max(0.0, float(self.segment_spacing))
|
||||
interior_pad = max(0.0, (spacing - desired_gap) * 0.5)
|
||||
extra_top = max(0.0, float(self.segment_background_extra_top))
|
||||
extra_bottom = max(0.0, float(self.segment_background_extra_bottom))
|
||||
|
||||
def coerce_corner_radii(value):
|
||||
if isinstance(value, (tuple, list)):
|
||||
values = list(value)
|
||||
else:
|
||||
values = [value]
|
||||
if not values:
|
||||
values = [0.0]
|
||||
if len(values) == 1:
|
||||
values = values * 4
|
||||
elif len(values) == 2:
|
||||
values = [values[0], values[1], values[1], values[0]]
|
||||
elif len(values) < 4:
|
||||
values = values + [values[-1]] * (4 - len(values))
|
||||
return tuple(values[:4])
|
||||
|
||||
base_corner_radii = coerce_corner_radii(self.background_corner_radius)
|
||||
|
||||
for idx, segment in enumerate(bounded_segments):
|
||||
bounds = segment.get("bounds")
|
||||
if not bounds:
|
||||
continue
|
||||
seg_width = max(0.0, bounds["max_x"] - bounds["min_x"])
|
||||
seg_height = max(0.0, bounds["max_y"] - bounds["min_y"])
|
||||
if seg_width <= 0 or seg_height <= 0:
|
||||
continue
|
||||
|
||||
pad_left = base_pad_x if idx == 0 else interior_pad
|
||||
pad_right = base_pad_x if idx == total_bounded - 1 else interior_pad
|
||||
|
||||
if total_bounded > 1:
|
||||
left_edge = idx == 0
|
||||
right_edge = idx == total_bounded - 1
|
||||
corner_override = (
|
||||
base_corner_radii[0] if left_edge else 0.0,
|
||||
base_corner_radii[1] if right_edge else 0.0,
|
||||
base_corner_radii[2] if right_edge else 0.0,
|
||||
base_corner_radii[3] if left_edge else 0.0,
|
||||
)
|
||||
else:
|
||||
corner_override = None
|
||||
|
||||
padding_override = (
|
||||
pad_left,
|
||||
pad_right,
|
||||
base_pad_y + extra_bottom,
|
||||
base_pad_y + extra_top,
|
||||
)
|
||||
self.draw_background_rect(
|
||||
bounds["min_x"],
|
||||
bounds["min_y"],
|
||||
seg_width,
|
||||
seg_height,
|
||||
segment.get("background_color") or segment["color"],
|
||||
force=True,
|
||||
padding_override=padding_override,
|
||||
corner_radius_override=corner_override,
|
||||
)
|
||||
|
||||
background_drawn = False
|
||||
if not self.segment_backgrounds:
|
||||
base_color = segments[0]["color"] if segments else self._text_a_color
|
||||
self.draw_background_rect(
|
||||
min_x,
|
||||
min_y,
|
||||
content_width,
|
||||
content_height,
|
||||
base_color,
|
||||
)
|
||||
background_drawn = True
|
||||
|
||||
for segment in segments:
|
||||
r, g, b, a = segment["color"]
|
||||
for line in segment["lines"]:
|
||||
if not line["text"]:
|
||||
continue
|
||||
set_font_size(font_id, line.get("font_size", self._text_size))
|
||||
blf.position(font_id, line["x"], line["y"], 0)
|
||||
blf.color(font_id, r, g, b, a)
|
||||
blf.draw(font_id, line)
|
||||
current_y -= line_height
|
||||
blf.draw(font_id, line["text"])
|
||||
set_font_size(font_id, self._text_size)
|
||||
|
||||
def _padding_tuple(self) -> Tuple[float, float]:
|
||||
pad = self.padding
|
||||
if isinstance(pad, (list, tuple)):
|
||||
if len(pad) == 0:
|
||||
return (0.0, 0.0)
|
||||
if len(pad) == 1:
|
||||
value = float(pad[0])
|
||||
return (value, value)
|
||||
return (float(pad[0]), float(pad[1]))
|
||||
value = float(pad)
|
||||
return (value, value)
|
||||
for segment in segments:
|
||||
if not segment.get("strikethrough"):
|
||||
continue
|
||||
bounds = segment.get("bounds")
|
||||
if not bounds:
|
||||
continue
|
||||
segment_min_x = bounds["min_x"]
|
||||
segment_max_x = bounds["max_x"]
|
||||
if segment_max_x <= segment_min_x:
|
||||
continue
|
||||
strike_y = bounds["min_y"] + (bounds["max_y"] - bounds["min_y"]) * 0.5
|
||||
self.draw_strikethrough(
|
||||
segment_min_x,
|
||||
segment_max_x,
|
||||
strike_y,
|
||||
segment["color"],
|
||||
force=True,
|
||||
)
|
||||
|
||||
def _draw_background_rect(self, left, right, bottom, top):
|
||||
vertices = (
|
||||
(left, top),
|
||||
(left, bottom),
|
||||
(right, bottom),
|
||||
(right, top),
|
||||
)
|
||||
indices = ((0, 1, 2), (0, 2, 3))
|
||||
gpu.state.blend_set("ALPHA")
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", self._bg_color)
|
||||
batch = batch_for_shader(
|
||||
self.shader, "TRIS", {"pos": vertices}, indices=indices
|
||||
)
|
||||
batch.draw(self.shader)
|
||||
if content_width > 0 and background_drawn:
|
||||
strike_color = segments[0]["color"]
|
||||
strike_y = min_y + content_height * 0.5
|
||||
self.draw_strikethrough(
|
||||
min_x,
|
||||
max_x,
|
||||
strike_y,
|
||||
strike_color,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,53 @@
|
||||
import blf
|
||||
import bpy
|
||||
import gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
|
||||
from .. import ui_bgl
|
||||
|
||||
from typing import Union
|
||||
|
||||
|
||||
def clamp(value, min_value=0.0, max_value=1.0):
|
||||
return max(min_value, min(max_value, value))
|
||||
|
||||
|
||||
def set_font_size(font_id, size):
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
blf.size(font_id, size, 72)
|
||||
else:
|
||||
blf.size(font_id, size)
|
||||
|
||||
|
||||
def tint_color(color, tint_amount):
|
||||
if tint_amount == 0.0:
|
||||
return color
|
||||
r, g, b, a = color
|
||||
if tint_amount > 0.0:
|
||||
r += (1.0 - r) * tint_amount
|
||||
g += (1.0 - g) * tint_amount
|
||||
b += (1.0 - b) * tint_amount
|
||||
else:
|
||||
r *= 1.0 + tint_amount
|
||||
g *= 1.0 + tint_amount
|
||||
b *= 1.0 + tint_amount
|
||||
return (clamp(r), clamp(g), clamp(b), a)
|
||||
|
||||
|
||||
def resolve_fill_color(preferred_color, fallback_color):
|
||||
color = preferred_color or fallback_color or (1.0, 1.0, 1.0, 1.0)
|
||||
r, g, b, a = color
|
||||
return (clamp(r), clamp(g), clamp(b), clamp(a))
|
||||
|
||||
|
||||
def region_redraw(ctx: bpy.types.Context = None):
|
||||
if ctx is not None:
|
||||
context = ctx
|
||||
else:
|
||||
context = bpy.context
|
||||
if context.region is not None:
|
||||
context.region.tag_redraw()
|
||||
|
||||
|
||||
class BL_UI_Widget:
|
||||
def __init__(self, x, y, width, height):
|
||||
@@ -19,15 +65,141 @@ class BL_UI_Widget:
|
||||
self._mouse_down_right = False
|
||||
self._is_visible = True
|
||||
self._is_active = True # if the widget needs to be disabled
|
||||
# decorative helpers (opt-in per widget)
|
||||
self._background_enabled = False
|
||||
self.use_rounded_background = False
|
||||
self.background_padding: tuple[int, int] = (0, 0)
|
||||
# Radius can be '50%' for pill shape, each corner individually
|
||||
self._background_corner_radius: Union[
|
||||
tuple[Union[str, float], ...],
|
||||
str,
|
||||
float,
|
||||
] = (0.0,)
|
||||
self._background_corner_radius_custom = False
|
||||
self.background_border = False
|
||||
self.background_border_color = None
|
||||
self.background_border_tint = 0.2
|
||||
self.background_border_thickness = 1.0
|
||||
self.strikethrough = False
|
||||
self.strikethrough_thickness = 1.25
|
||||
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
self.shader = gpu.shader.from_builtin("2D_UNIFORM_COLOR")
|
||||
else:
|
||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||
|
||||
@property
|
||||
def background_corner_radius(self):
|
||||
return self._background_corner_radius
|
||||
|
||||
@background_corner_radius.setter
|
||||
def background_corner_radius(self, value):
|
||||
self._background_corner_radius = value
|
||||
self._background_corner_radius_custom = True
|
||||
|
||||
@property
|
||||
def background(self):
|
||||
return self._background_enabled
|
||||
|
||||
@background.setter
|
||||
def background(self, value):
|
||||
enabled = bool(value)
|
||||
self._background_enabled = enabled
|
||||
if not enabled:
|
||||
self.use_rounded_background = False
|
||||
elif enabled and not self.use_rounded_background:
|
||||
self.use_rounded_background = True
|
||||
|
||||
def _set_background_corner_radius_default(self, value):
|
||||
self._background_corner_radius = value
|
||||
self._background_corner_radius_custom = False
|
||||
|
||||
def has_background_corner_radius_override(self):
|
||||
return self._background_corner_radius_custom
|
||||
|
||||
def resolve_background_fill(self, fallback_color, preferred_color=None):
|
||||
base_color = fallback_color if preferred_color is None else preferred_color
|
||||
return resolve_fill_color(
|
||||
base_color,
|
||||
fallback_color,
|
||||
)
|
||||
|
||||
def resolve_background_border(self, fill_color):
|
||||
if not self.background_border:
|
||||
return None
|
||||
if self.background_border_color:
|
||||
return self.background_border_color
|
||||
return tint_color(fill_color, self.background_border_tint)
|
||||
|
||||
def draw_background_rect(
|
||||
self,
|
||||
min_x,
|
||||
min_y,
|
||||
width,
|
||||
height,
|
||||
fallback_color,
|
||||
*,
|
||||
force=False,
|
||||
padding_override=None,
|
||||
corner_radius_override=None,
|
||||
fill_color_override=None,
|
||||
):
|
||||
if (not self.use_rounded_background and not force) or width <= 0 or height <= 0:
|
||||
return
|
||||
if padding_override is None:
|
||||
pad_x = self.background_padding[0]
|
||||
pad_y = self.background_padding[1]
|
||||
pad_left = pad_right = pad_x
|
||||
pad_bottom = pad_top = pad_y
|
||||
else:
|
||||
if len(padding_override) == 4:
|
||||
pad_left, pad_right, pad_bottom, pad_top = padding_override
|
||||
elif len(padding_override) == 2:
|
||||
pad_left = pad_right = padding_override[0]
|
||||
pad_bottom = pad_top = padding_override[1]
|
||||
else:
|
||||
pad_left = pad_right = padding_override[0]
|
||||
pad_bottom = pad_top = padding_override[1]
|
||||
rect_x = min_x - pad_left
|
||||
rect_y = min_y - pad_bottom
|
||||
rect_width = width + pad_left + pad_right
|
||||
rect_height = height + pad_top + pad_bottom
|
||||
fill_color = self.resolve_background_fill(
|
||||
fallback_color,
|
||||
preferred_color=fill_color_override,
|
||||
)
|
||||
border_color = self.resolve_background_border(fill_color)
|
||||
corner_radius = (
|
||||
corner_radius_override
|
||||
if corner_radius_override is not None
|
||||
else self.background_corner_radius
|
||||
)
|
||||
ui_bgl.draw_rounded_rect_with_border(
|
||||
rect_x,
|
||||
rect_y,
|
||||
rect_width,
|
||||
rect_height,
|
||||
radius=corner_radius,
|
||||
fill_color=fill_color,
|
||||
border_color=border_color,
|
||||
border_thickness=self.background_border_thickness,
|
||||
)
|
||||
|
||||
def draw_strikethrough(self, min_x, max_x, y, color, *, force=False):
|
||||
if (not self.strikethrough and not force) or max_x <= min_x:
|
||||
return
|
||||
ui_bgl.draw_line2d(
|
||||
min_x,
|
||||
y,
|
||||
max_x,
|
||||
y,
|
||||
self.strikethrough_thickness,
|
||||
color,
|
||||
)
|
||||
|
||||
def set_location(self, x, y):
|
||||
# if self.x != x or self.y != y or self.x_screen != x or self.y_screen != y:
|
||||
# bpy.context.region.tag_redraw()
|
||||
# region_redraw()
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.x_screen = x
|
||||
@@ -41,8 +213,15 @@ class BL_UI_Widget:
|
||||
@bg_color.setter
|
||||
def bg_color(self, value):
|
||||
self._bg_color = value
|
||||
if bpy.context.region is not None:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
|
||||
@property
|
||||
def background_color(self):
|
||||
return self._bg_color
|
||||
|
||||
@background_color.setter
|
||||
def background_color(self, value):
|
||||
self.bg_color = value
|
||||
|
||||
@property
|
||||
def visible(self):
|
||||
@@ -51,17 +230,17 @@ class BL_UI_Widget:
|
||||
@visible.setter
|
||||
def visible(self, value):
|
||||
if value != self._is_visible:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._is_visible = value
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
return self._is_active
|
||||
|
||||
@visible.setter
|
||||
@active.setter
|
||||
def active(self, value):
|
||||
if value != self._is_active:
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self._is_active = value
|
||||
|
||||
@property
|
||||
@@ -78,6 +257,20 @@ class BL_UI_Widget:
|
||||
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
if self.use_rounded_background:
|
||||
area_height = self.get_area_height()
|
||||
rect_y = area_height - self.y_screen - self.height
|
||||
self.draw_background_rect(
|
||||
self.x_screen,
|
||||
rect_y,
|
||||
self.width,
|
||||
self.height,
|
||||
self._bg_color,
|
||||
force=True,
|
||||
fill_color_override=self._bg_color,
|
||||
)
|
||||
return
|
||||
|
||||
self.shader.bind()
|
||||
self.shader.uniform_float("color", self._bg_color)
|
||||
|
||||
@@ -107,7 +300,7 @@ class BL_UI_Widget:
|
||||
self.batch_panel = batch_for_shader(
|
||||
self.shader, "TRIS", {"pos": vertices}, indices=indices
|
||||
)
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
|
||||
def handle_event(self, event):
|
||||
"""
|
||||
@@ -121,28 +314,27 @@ class BL_UI_Widget:
|
||||
if not self._is_active:
|
||||
return False
|
||||
|
||||
x = event.mouse_region_x
|
||||
y = event.mouse_region_y
|
||||
x, y = self._to_widget_region_coords(event)
|
||||
|
||||
if event.type == "LEFTMOUSE":
|
||||
if event.value == "PRESS":
|
||||
self._mouse_down = True
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
return self.mouse_down(x, y)
|
||||
else:
|
||||
self._mouse_down = False
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self.mouse_up(x, y)
|
||||
return False
|
||||
|
||||
elif event.type == "RIGHTMOUSE":
|
||||
if event.value == "PRESS":
|
||||
self._mouse_down_right = True
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
return self.mouse_down_right(x, y)
|
||||
else:
|
||||
self._mouse_down_right = False
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
self.mouse_up(x, y)
|
||||
|
||||
elif event.type == "MOUSEMOVE":
|
||||
@@ -154,13 +346,13 @@ class BL_UI_Widget:
|
||||
self.__inrect = True
|
||||
self.mouse_enter(event, x, y)
|
||||
# we tag redraw since the hover colors are picked in the draw function
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
|
||||
# we are leaving the rect
|
||||
elif self.__inrect and not inrect:
|
||||
self.__inrect = False
|
||||
self.mouse_exit(event, x, y)
|
||||
bpy.context.region.tag_redraw()
|
||||
region_redraw()
|
||||
|
||||
# return always false to enable mouse exit events on other buttons.(would sometimes not hide the tooltip)
|
||||
return False # self.__inrect
|
||||
@@ -174,6 +366,26 @@ class BL_UI_Widget:
|
||||
|
||||
return False
|
||||
|
||||
def _to_widget_region_coords(self, event):
|
||||
region = None
|
||||
ctx = self.context
|
||||
if isinstance(ctx, dict):
|
||||
region = ctx.get("region")
|
||||
elif hasattr(ctx, "region"):
|
||||
region = getattr(ctx, "region")
|
||||
|
||||
if (
|
||||
region is not None
|
||||
and hasattr(event, "mouse_x")
|
||||
and hasattr(event, "mouse_y")
|
||||
):
|
||||
try:
|
||||
return event.mouse_x - region.x, event.mouse_y - region.y
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return getattr(event, "mouse_region_x", 0), getattr(event, "mouse_region_y", 0)
|
||||
|
||||
def get_input_keys(self):
|
||||
return []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user