Add compact generated icon button surfaces

This commit is contained in:
2026-06-20 09:25:32 +09:00
parent 1ec8a78063
commit bd0b7a238e
13 changed files with 271 additions and 13 deletions

View File

@@ -2994,6 +2994,8 @@ func _check_generated_button_textures(failures: Array[String]) -> void:
var button_keys := ["normal", "hover", "pressed", "disabled"]
for button_key in button_keys:
_check_button_texture_path(failures, "res://art/ui/buttons/button_%s.png" % button_key, "button texture %s" % button_key)
for button_key in button_keys:
_check_icon_button_texture_path(failures, "res://art/ui/buttons/button_icon_%s.png" % button_key, "icon button texture %s" % button_key)
func _check_generated_tile_marker_textures(failures: Array[String]) -> void:
@@ -4134,12 +4136,26 @@ func _check_button_uses_generated_surface(failures: Array[String], button: Butto
if style.texture == null:
failures.append("%s generated button style is missing its texture" % context)
return
if style.texture.get_width() < 512 or style.texture.get_height() < 192:
failures.append("%s generated button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()])
if style.texture_margin_left < 48 or style.texture_margin_top < 24:
failures.append("%s generated button texture should define visible frame slice margins" % context)
if style.content_margin_left < 8 or style.content_margin_top < 5:
failures.append("%s generated button texture should reserve readable content margins" % context)
if bool(button.get_meta("icon_only", false)):
if not bool(button.get_meta("generated_icon_button_texture", false)):
failures.append("%s icon-only button should use the generated compact icon surface" % context)
if style.texture.get_width() < 256 or style.texture.get_height() < 256:
failures.append("%s generated icon button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()])
if abs(style.texture.get_width() - style.texture.get_height()) > 4:
failures.append("%s generated icon button texture should be square or near-square: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()])
if style.texture_margin_left < 48 or style.texture_margin_top < 48:
failures.append("%s generated icon button texture should define visible frame slice margins" % context)
if style.content_margin_left < 5 or style.content_margin_top < 5:
failures.append("%s generated icon button texture should reserve icon content margins" % context)
else:
if bool(button.get_meta("generated_icon_button_texture", false)):
failures.append("%s text button should keep the wide generated button surface" % context)
if style.texture.get_width() < 512 or style.texture.get_height() < 192:
failures.append("%s generated button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()])
if style.texture_margin_left < 48 or style.texture_margin_top < 24:
failures.append("%s generated button texture should define visible frame slice margins" % context)
if style.content_margin_left < 8 or style.content_margin_top < 5:
failures.append("%s generated button texture should reserve readable content margins" % context)
func _check_local_command_icon_button(failures: Array[String], button: Button, expected_label: String, expected_icon: String) -> void:
@@ -5489,6 +5505,64 @@ func _check_button_texture_path(failures: Array[String], path: String, context:
failures.append("%s should avoid a dominant yellow UI cast: %s (warm bias %.3f)" % [context, path, warm_bias])
func _check_icon_button_texture_path(failures: Array[String], path: String, context: String) -> void:
if path.is_empty():
failures.append("%s has an empty icon button path" % context)
return
if not path.begins_with("res://"):
failures.append("%s must use a res:// path: %s" % [context, path])
return
if not FileAccess.file_exists(path):
failures.append("%s references missing icon button texture: %s" % [context, path])
return
var image := Image.new()
var err := image.load(path)
if err != OK:
failures.append("%s references unreadable icon button texture: %s" % [context, path])
return
if image.get_width() < 256 or image.get_height() < 256:
failures.append("%s should keep high-resolution compact source pixels: %s (%dx%d)" % [context, path, image.get_width(), image.get_height()])
return
if abs(image.get_width() - image.get_height()) > 4:
failures.append("%s should be a square compact icon surface: %s (%dx%d)" % [context, path, image.get_width(), image.get_height()])
return
for corner in [Vector2i(0, 0), Vector2i(image.get_width() - 1, 0), Vector2i(0, image.get_height() - 1), Vector2i(image.get_width() - 1, image.get_height() - 1)]:
if image.get_pixelv(corner).a > 0.01:
failures.append("%s should have transparent corners: %s" % [context, path])
return
var opaque_samples := 0
var transparent_samples := 0
var green_fringe_samples := 0
var luma_min := 999.0
var luma_max := -999.0
var total_samples := 0
for y in range(0, image.get_height(), 8):
for x in range(0, image.get_width(), 8):
total_samples += 1
var pixel := image.get_pixel(x, y)
if pixel.a <= 0.01:
transparent_samples += 1
continue
if pixel.a >= 0.80:
opaque_samples += 1
if pixel.g > 0.80 and pixel.r < 0.18 and pixel.b < 0.18:
green_fringe_samples += 1
var luma := pixel.r * 0.299 + pixel.g * 0.587 + pixel.b * 0.114
luma_min = minf(luma_min, luma)
luma_max = maxf(luma_max, luma)
if transparent_samples < int(total_samples * 0.08):
failures.append("%s should retain transparent compact button padding: %s (%d/%d sampled pixels)" % [context, path, transparent_samples, total_samples])
if opaque_samples < int(total_samples * 0.42):
failures.append("%s should contain substantial generated compact button artwork: %s (%d/%d sampled pixels)" % [context, path, opaque_samples, total_samples])
if green_fringe_samples > 0:
failures.append("%s should not keep chroma-key green pixels: %s (%d sampled)" % [context, path, green_fringe_samples])
if luma_max - luma_min < 0.08:
failures.append("%s should contain visible material contrast: %s" % [context, path])
var warm_bias := _image_visible_warm_bias(image, 8)
if warm_bias > 0.12:
failures.append("%s should avoid a dominant yellow UI cast: %s (warm bias %.3f)" % [context, path, warm_bias])
func _image_visible_warm_bias(image: Image, sample_step: int) -> float:
var total_bias := 0.0
var visible_samples := 0