class_name InspectorBalloon extends MarginContainer signal quitted(is_manually: bool) @export var prop_title_to_content_wait_time := 3.0 @export var auto_quit_time := 5.0 @onready var character_label = %CharacterLabel as RichTextLabel @onready var content_label = %ContentLabel as RichTextLabel enum { DISPLAYING_NONE, DISPLAYING_TITLE, DISPLAYING_CONTENT } var _display_mutex := Mutex.new() var _displaying_status := DISPLAYING_NONE var _pending_content := "" var _balloon_tween: Tween func _ready() -> void: character_label.text = "" content_label.text = "" func show_prop_balloon(obtain_str: String, prop_title: String, content: String) -> void: _display_mutex.lock() if _balloon_tween and _balloon_tween.is_running(): _balloon_tween.kill() _balloon_tween = create_tween() show() _pending_content = content obtain_str = "[color=orange]" + obtain_str + "[/color]" _displaying_status = DISPLAYING_TITLE character_label.show() character_label.text = obtain_str content_label.text = prop_title if not _pending_content.is_empty(): _balloon_tween.tween_interval(prop_title_to_content_wait_time) _balloon_tween.tween_callback(_display_content) else: _balloon_tween.tween_interval(auto_quit_time) _balloon_tween.tween_callback(_finished.bind(false)) _display_mutex.unlock() func _display_content() -> void: _display_mutex.lock() if _balloon_tween and _balloon_tween.is_running(): _balloon_tween.kill() show() _displaying_status = DISPLAYING_CONTENT character_label.hide() content_label.text = _pending_content _pending_content = "" _balloon_tween = create_tween() _balloon_tween.tween_interval(auto_quit_time) _balloon_tween.tween_callback(_finished.bind(false)) _display_mutex.unlock() func show_prop_content(content: String) -> void: _pending_content = content _display_content() func _finished(manually: bool) -> void: _display_mutex.lock() character_label.text = "" content_label.text = "" _displaying_status = DISPLAYING_NONE hide() quitted.emit(manually) _display_mutex.unlock() func _input(event: InputEvent) -> void: if _displaying_status == DISPLAYING_NONE: return if event.is_action_pressed("interact") or event.is_action_pressed("cancel"): get_viewport().set_input_as_handled() _display_mutex.lock() if _balloon_tween and _balloon_tween.is_running(): _balloon_tween.kill() if _displaying_status == DISPLAYING_TITLE: if not _pending_content.is_empty(): _display_content() else: # 如果没有文本,直接退出 _finished(true) elif _displaying_status == DISPLAYING_CONTENT: _finished(true) _display_mutex.unlock() # 不释放 quitted 结束 balloon func kill_without_signal() -> void: _display_mutex.lock() if _balloon_tween and _balloon_tween.is_running(): _balloon_tween.kill() character_label.text = "" content_label.text = "" _displaying_status = DISPLAYING_NONE hide() _display_mutex.unlock()