TinyAdventure/addons/dialogue_manager/dialogue_reponses_menu.gd

142 lines
3.5 KiB
GDScript
Raw Permalink Normal View History

@icon("./assets/responses_menu.svg")
## A [Container] for dialogue responses provided by [b]Dialogue Manager[/b].
class_name DialogueResponsesMenu extends Container
## Emitted when a response is selected.
signal response_selected(response)
## Optionally specify a control to duplicate for each response
@export var response_template: Control
## The action for accepting a response (is possibly overridden by parent dialogue balloon).
@export var next_action: StringName = &""
## The list of dialogue responses.
var responses: Array = []:
get:
return responses
set(value):
responses = value
# Remove any current items
for item in get_children():
if item == response_template: continue
remove_child(item)
item.queue_free()
# Add new items
if responses.size() > 0:
for response in responses:
var item: Control
if is_instance_valid(response_template):
item = response_template.duplicate(DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_SIGNALS)
item.show()
else:
item = Button.new()
item.name = "Response%d" % get_child_count()
if not response.is_allowed:
item.name = String(item.name) + "Disallowed"
item.disabled = true
# If the item has a response property then use that
if "response" in item:
item.response = response
# Otherwise assume we can just set the text
else:
item.text = response.text
item.set_meta("response", response)
add_child(item)
_configure_focus()
func _ready() -> void:
visibility_changed.connect(func():
if visible and get_menu_items().size() > 0:
get_menu_items()[0].grab_focus()
)
if is_instance_valid(response_template):
response_template.hide()
## Get the selectable items in the menu.
func get_menu_items() -> Array:
var items: Array = []
for child in get_children():
if not child.visible: continue
if "Disallowed" in child.name: continue
items.append(child)
return items
## [b]DEPRECATED[/b]. Do not use.
func set_responses(next_responses: Array) -> void:
self.responses = next_responses
#region Internal
# Prepare the menu for keyboard and mouse navigation.
func _configure_focus() -> void:
var items = get_menu_items()
for i in items.size():
var item: Control = items[i]
item.focus_mode = Control.FOCUS_ALL
item.focus_neighbor_left = item.get_path()
item.focus_neighbor_right = item.get_path()
if i == 0:
item.focus_neighbor_top = item.get_path()
item.focus_previous = item.get_path()
else:
item.focus_neighbor_top = items[i - 1].get_path()
item.focus_previous = items[i - 1].get_path()
if i == items.size() - 1:
item.focus_neighbor_bottom = item.get_path()
item.focus_next = item.get_path()
else:
item.focus_neighbor_bottom = items[i + 1].get_path()
item.focus_next = items[i + 1].get_path()
item.mouse_entered.connect(_on_response_mouse_entered.bind(item))
item.gui_input.connect(_on_response_gui_input.bind(item, item.get_meta("response")))
items[0].grab_focus()
#endregion
#region Signals
func _on_response_mouse_entered(item: Control) -> void:
if "Disallowed" in item.name: return
item.grab_focus()
func _on_response_gui_input(event: InputEvent, item: Control, response) -> void:
if "Disallowed" in item.name: return
get_viewport().set_input_as_handled()
if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
response_selected.emit(response)
elif event.is_action_pressed(&"ui_accept" if next_action.is_empty() else next_action) and item in get_menu_items():
response_selected.emit(response)
#endregion