diff --git a/scene/entity/ux/hover_light_click_area.gd b/scene/entity/ux/hover_light_click_area.gd new file mode 100644 index 00000000..41e83d35 --- /dev/null +++ b/scene/entity/ux/hover_light_click_area.gd @@ -0,0 +1,102 @@ +class_name HoverLightClickArea extends Area2D + +signal interacted + +@export var freezing := false: + set(val): + freezing = val + if freezing and touching: + _on_mouse_exited() + +var touching = false +static var _focus_mutex = Mutex.new() +static var current_focusing_item = "": + set(val): + current_focusing_item = val + if GlobalConfig.DEBUG: + print("HoverLightClickArea current_focusing_item=", current_focusing_item) +static var pending_enter_callables := [] as Array[Callable] + +# to pointlight2d +var lights: Array[NodePath] = [] + + +func _ready() -> void: + for c in get_children(): + if c is PointLight2D: + lights.append(self.get_path_to(c)) + if lights.is_empty(): + printerr("HoverLightButton has no PointLight2D children. auto freezed name=", name) + freezing = true + mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) + # 默认先熄灭 + _toggle_light(false) + + +func is_focused() -> bool: + return current_focusing_item == name + + +func _on_mouse_entered() -> bool: + touching = true + if freezing or not is_visible_in_tree(): + return false + if is_focused(): + return true + _focus_mutex.lock() + # 尝试获得 current_focusing_item + if current_focusing_item != "": + if not pending_enter_callables.has(_on_mouse_entered): + pending_enter_callables.append(_on_mouse_entered) + _focus_mutex.unlock() + return false + current_focusing_item = name + _focus_mutex.unlock() + _toggle_light(true) + return true + + +func _on_mouse_exited() -> void: + touching = false + _focus_mutex.lock() + pending_enter_callables.erase(_on_mouse_entered) + # frezzing 不影响 mouse exited + if is_focused(): + current_focusing_item = "" + for c in pending_enter_callables: + if c.call(): + break + _focus_mutex.unlock() + _toggle_light(false) + + +func _toggle_light(on: bool) -> void: + for l in lights: + var light = get_node(l) as PointLight2D + if light: + light.enabled = on + + +func invalid_blink() -> void: + for l in lights: + var light = get_node(l) as PointLight2D + if light: + var tween = create_tween() + tween.tween_property(light, "color", Color.RED, 0.2) + tween.tween_property(light, "color", Color.WHITE, 0.3) + + +func _unhandled_input(event: InputEvent) -> void: + if freezing or Engine.is_editor_hint() or not is_visible_in_tree(): + return + if event is InputEventMouseButton: + if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: + if is_focused(): + get_viewport().set_input_as_handled() + interacted.emit() + + +func _exit_tree() -> void: + if touching: + _on_mouse_exited() \ No newline at end of file diff --git a/scene/entity/ux/hover_light_click_area.gd.uid b/scene/entity/ux/hover_light_click_area.gd.uid new file mode 100644 index 00000000..d63edbf9 --- /dev/null +++ b/scene/entity/ux/hover_light_click_area.gd.uid @@ -0,0 +1 @@ +uid://ubn3pgywffro diff --git a/scene/entity/ux/hover_light_click_area.tscn b/scene/entity/ux/hover_light_click_area.tscn new file mode 100644 index 00000000..fa4be6fc --- /dev/null +++ b/scene/entity/ux/hover_light_click_area.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://bkk1rxx36ghrl"] + +[ext_resource type="Script" uid="uid://ubn3pgywffro" path="res://scene/entity/ux/hover_light_click_area.gd" id="1_qoyst"] + +[node name="HoverLightClickArea" type="Area2D"] +collision_layer = 4 +collision_mask = 4 +monitorable = false +script = ExtResource("1_qoyst")