Vendor gdUnit4 for Godot 4 client testing (D-030). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
152 lines
5.5 KiB
GDScript
152 lines
5.5 KiB
GDScript
class_name GdUnitUiTools
|
|
extends RefCounted
|
|
|
|
|
|
static var _spinner: AnimatedTexture
|
|
|
|
|
|
enum ImageFlipMode {
|
|
HORIZONTAl,
|
|
VERITCAL
|
|
}
|
|
|
|
|
|
## Returns the icon by name, if it exists.
|
|
static func get_icon(icon_name: String, color: = Color.BLACK) -> Texture2D:
|
|
if not Engine.is_editor_hint():
|
|
return null
|
|
var icon := EditorInterface.get_base_control().get_theme_icon(icon_name, "EditorIcons")
|
|
if icon == null:
|
|
return null
|
|
if color != Color.BLACK:
|
|
icon = _modulate_texture(icon, color)
|
|
return icon
|
|
|
|
|
|
## Returns the icon flipped
|
|
static func get_flipped_icon(icon_name: String, mode: = ImageFlipMode.HORIZONTAl) -> Texture2D:
|
|
if not Engine.is_editor_hint():
|
|
return null
|
|
var icon := EditorInterface.get_base_control().get_theme_icon(icon_name, "EditorIcons")
|
|
if icon == null:
|
|
return null
|
|
return ImageTexture.create_from_image(_flip_image(icon, mode))
|
|
|
|
|
|
static func get_spinner() -> AnimatedTexture:
|
|
if _spinner != null:
|
|
return _spinner
|
|
_spinner = AnimatedTexture.new()
|
|
_spinner.frames = 8
|
|
_spinner.speed_scale = 2.5
|
|
for frame in _spinner.frames:
|
|
_spinner.set_frame_texture(frame, get_icon("Progress%d" % (frame+1)))
|
|
_spinner.set_frame_duration(frame, 0.2)
|
|
return _spinner
|
|
|
|
|
|
static func get_color_animated_icon(icon_name :String, from :Color, to :Color) -> AnimatedTexture:
|
|
if not Engine.is_editor_hint():
|
|
return null
|
|
var texture := AnimatedTexture.new()
|
|
texture.frames = 8
|
|
texture.speed_scale = 2.5
|
|
var color := from
|
|
for frame in texture.frames:
|
|
color = lerp(color, to, .2)
|
|
texture.set_frame_texture(frame, get_icon(icon_name, color))
|
|
texture.set_frame_duration(frame, 0.2)
|
|
return texture
|
|
|
|
|
|
static func get_run_overall_icon() -> Texture2D:
|
|
if not Engine.is_editor_hint():
|
|
return null
|
|
var icon := EditorInterface.get_base_control().get_theme_icon("Play", "EditorIcons")
|
|
var image := _merge_images(icon.get_image(), Vector2i(-2, 0), icon.get_image(), Vector2i(3, 0))
|
|
return ImageTexture.create_from_image(image)
|
|
|
|
|
|
static func get_GDScript_icon(status: String, color: Color) -> Texture2D:
|
|
if not Engine.is_editor_hint():
|
|
return null
|
|
var icon_a := EditorInterface.get_base_control().get_theme_icon("GDScript", "EditorIcons")
|
|
var icon_b := EditorInterface.get_base_control().get_theme_icon(status, "EditorIcons")
|
|
var overlay_image := _modulate_image(icon_b.get_image(), color)
|
|
var image := _merge_images_scaled(icon_a.get_image(), Vector2i(0, 0), overlay_image, Vector2i(5, 5))
|
|
return ImageTexture.create_from_image(image)
|
|
|
|
|
|
static func get_CSharpScript_icon(status: String, color: Color) -> Texture2D:
|
|
if not Engine.is_editor_hint():
|
|
return null
|
|
var icon_a := EditorInterface.get_base_control().get_theme_icon("CSharpScript", "EditorIcons")
|
|
var icon_b := EditorInterface.get_base_control().get_theme_icon(status, "EditorIcons")
|
|
var overlay_image := _modulate_image(icon_b.get_image(), color)
|
|
var image := _merge_images_scaled(icon_a.get_image(), Vector2i(0, 0), overlay_image, Vector2i(5, 5))
|
|
return ImageTexture.create_from_image(image)
|
|
|
|
|
|
static func _modulate_texture(texture: Texture2D, color: Color) -> Texture2D:
|
|
var image := _modulate_image(texture.get_image(), color)
|
|
return ImageTexture.create_from_image(image)
|
|
|
|
|
|
static func _modulate_image(image: Image, color: Color) -> Image:
|
|
var data: PackedByteArray = image.data["data"]
|
|
for pixel in range(0, data.size(), 4):
|
|
var pixel_a := _to_color(data, pixel)
|
|
if pixel_a.a8 != 0:
|
|
pixel_a = pixel_a.lerp(color, .9)
|
|
data[pixel + 0] = pixel_a.r8
|
|
data[pixel + 1] = pixel_a.g8
|
|
data[pixel + 2] = pixel_a.b8
|
|
data[pixel + 3] = pixel_a.a8
|
|
var output_image := Image.new()
|
|
output_image.set_data(image.get_width(), image.get_height(), image.has_mipmaps(), image.get_format(), data)
|
|
return output_image
|
|
|
|
|
|
static func _merge_images(image1: Image, offset1: Vector2i, image2: Image, offset2: Vector2i) -> Image:
|
|
## we need to fix the image to have the same size to avoid merge conflicts
|
|
if image1.get_height() < image2.get_height():
|
|
image1.resize(image2.get_width(), image2.get_height())
|
|
# Create a new Image for the merged result
|
|
var merged_image := Image.create(image1.get_width(), image1.get_height(), false, Image.FORMAT_RGBA8)
|
|
merged_image.blit_rect_mask(image1, image2, Rect2(Vector2.ZERO, image1.get_size()), offset1)
|
|
merged_image.blit_rect_mask(image1, image2, Rect2(Vector2.ZERO, image2.get_size()), offset2)
|
|
return merged_image
|
|
|
|
|
|
@warning_ignore("narrowing_conversion")
|
|
static func _merge_images_scaled(image1: Image, offset1: Vector2i, image2: Image, offset2: Vector2i) -> Image:
|
|
## we need to fix the image to have the same size to avoid merge conflicts
|
|
if image1.get_height() < image2.get_height():
|
|
image1.resize(image2.get_width(), image2.get_height())
|
|
# Create a new Image for the merged result
|
|
var merged_image := Image.create(image1.get_width(), image1.get_height(), false, image1.get_format())
|
|
merged_image.blend_rect(image1, Rect2(Vector2.ZERO, image1.get_size()), offset1)
|
|
@warning_ignore("narrowing_conversion")
|
|
image2.resize(image2.get_width()/1.3, image2.get_height()/1.3)
|
|
merged_image.blend_rect(image2, Rect2(Vector2.ZERO, image2.get_size()), offset2)
|
|
return merged_image
|
|
|
|
|
|
static func _flip_image(texture: Texture2D, mode: ImageFlipMode) -> Image:
|
|
var flipped_image := Image.new()
|
|
flipped_image.copy_from(texture.get_image())
|
|
if mode == ImageFlipMode.VERITCAL:
|
|
flipped_image.flip_x()
|
|
else:
|
|
flipped_image.flip_y()
|
|
return flipped_image
|
|
|
|
|
|
static func _to_color(data: PackedByteArray, position: int) -> Color:
|
|
var pixel_a := Color()
|
|
pixel_a.r8 = data[position + 0]
|
|
pixel_a.g8 = data[position + 1]
|
|
pixel_a.b8 = data[position + 2]
|
|
pixel_a.a8 = data[position + 3]
|
|
return pixel_a
|