Godot 4.7 project for authoring, previewing, validating, and building Exchanger PCK themes. Includes the example and space-pixel themes, binding contracts, editor tooling, validation/build scripts, and project documentation.
77 lines
2.7 KiB
GDScript
77 lines
2.7 KiB
GDScript
extends Control
|
|
|
|
const ELEMENT_META_KEY := &"exchanger_binding_id"
|
|
const SCREEN_META_KEY := &"exchanger_screen_binding_id"
|
|
const LABEL_FONT_SIZE := 10
|
|
const LABEL_HEIGHT := 22.0
|
|
const LABEL_PADDING := 5.0
|
|
|
|
var target: Node
|
|
|
|
|
|
func _ready() -> void:
|
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
set_process(true)
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
queue_redraw()
|
|
|
|
|
|
func _draw() -> void:
|
|
if target == null or not is_instance_valid(target):
|
|
return
|
|
for node in _all_nodes(target):
|
|
if (not node.has_meta(ELEMENT_META_KEY) and not node.has_meta(SCREEN_META_KEY)) or not node is CanvasItem:
|
|
continue
|
|
var canvas_item := node as CanvasItem
|
|
if not canvas_item.is_visible_in_tree():
|
|
continue
|
|
var binding_id := str(
|
|
node.get_meta(ELEMENT_META_KEY)
|
|
if node.has_meta(ELEMENT_META_KEY)
|
|
else node.get_meta(SCREEN_META_KEY)
|
|
)
|
|
var bounds := _bounds_for(canvas_item)
|
|
draw_rect(bounds, Color(1.0, 0.72, 0.08, 0.92), false, 2.0)
|
|
var label_width := minf(size.x - 8.0, maxf(80.0, binding_id.length() * 6.0 + LABEL_PADDING * 2.0))
|
|
var label_rect := Rect2(bounds.position, Vector2(label_width, LABEL_HEIGHT))
|
|
label_rect.position.x = clampf(label_rect.position.x, 4.0, size.x - label_width - 4.0)
|
|
if label_rect.position.y < 28.0:
|
|
label_rect.position.y = bounds.end.y
|
|
draw_rect(label_rect, Color(0.02, 0.06, 0.14, 0.94), true)
|
|
draw_rect(label_rect, Color(1.0, 0.72, 0.08, 1.0), false, 1.0)
|
|
draw_string(
|
|
ThemeDB.fallback_font,
|
|
label_rect.position + Vector2(LABEL_PADDING, 16.0),
|
|
binding_id,
|
|
HORIZONTAL_ALIGNMENT_LEFT,
|
|
label_width - LABEL_PADDING * 2.0,
|
|
LABEL_FONT_SIZE,
|
|
Color.WHITE
|
|
)
|
|
|
|
|
|
func _bounds_for(item: CanvasItem) -> Rect2:
|
|
var inverse := get_global_transform().affine_inverse()
|
|
if item is Control:
|
|
var control := item as Control
|
|
var top_left := inverse * control.get_global_transform_with_canvas() * Vector2.ZERO
|
|
var bottom_right := inverse * control.get_global_transform_with_canvas() * control.size
|
|
return Rect2(top_left, bottom_right - top_left).abs()
|
|
if item is Node2D:
|
|
var point := inverse * (item as Node2D).get_global_transform_with_canvas().origin
|
|
return Rect2(point - Vector2(8.0, 8.0), Vector2(16.0, 16.0))
|
|
return Rect2(Vector2.ZERO, Vector2(16.0, 16.0))
|
|
|
|
|
|
func _all_nodes(root: Node) -> Array[Node]:
|
|
var result: Array[Node] = [root]
|
|
var cursor := 0
|
|
while cursor < result.size():
|
|
var current := result[cursor]
|
|
for child in current.get_children():
|
|
result.append(child)
|
|
cursor += 1
|
|
return result
|