Skip to content

Commit a47c044

Browse files
authored
Styles Choices: Add override for custom choice buttons (#2554)
The choice buttons for the `Centered Choices` and `Textbubble Layer` layers can be replaced with a `PackedScene` descending from `DialogicNode_ChoiceButton`. This is intended for users who want custom behavior for their buttons (the choice must be pressed 3 times, on-hover animations, multi-`Control` UI, etc.). **New Settings:** _Centered Choices:_ `Choices/Behavior/Custom Button`, `Choices/Behavior/Maximum Choices` _Textbubble Layer:_ `Choices/Behavior/Custom Button`, `Choices/Behavior/Maximum Choices` **Changes to `DialogicNode_ChoiceButton`:** The on-pressed behavior of the button can be customized by overriding `_pressed()`. By default, this preserves the existing behavior by emitting the new `choice_selected` signal when the button is pressed. The Choices Subsystem has now listens for the button's `choice_selected` signal instead of `pressed`. **Changes to VN/Centered Choice Layer:** The centered choice layer now instantiates its buttons when the style is changed (functionally similar to Textbubble Layer) so that custom buttons are applied, including while the timeline is running.
1 parent 628e6ba commit a47c044

File tree

6 files changed

+80
-76
lines changed

6 files changed

+80
-76
lines changed

addons/dialogic/Modules/Choice/node_choice_button.gd

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ extends Button
1212
## not supported on buttons at this point.
1313

1414

15+
## Emitted when the choice is selected. Unless overridden, this is when the button or its shortcut is pressed.
16+
signal choice_selected
17+
18+
1519
## Used to identify what choices to put on. If you leave it at -1, choices will be distributed automatically.
1620
@export var choice_index: int = -1
1721

@@ -31,6 +35,24 @@ func _ready() -> void:
3135
hide()
3236

3337

38+
## Custom choice buttons can override this for specialized behavior when the choice button is pressed.
39+
func _pressed():
40+
choice_selected.emit()
41+
42+
43+
## Custom choice buttons can override this if their behavior should change
44+
## based on event data. If the custom choice button does not override
45+
## visibility, disabled-ness, nor the choice text, consider
46+
## calling super(choice_info) at the start of the override.
47+
##
48+
## The choice_info Dictionary has the following keys:
49+
## - event_index: The index of the choice event in the timeline.
50+
## - button_index: The relative index of the choice (starts from 1).
51+
## - visible: If the choice should be visible.
52+
## - disabled: If the choice should be selectable.
53+
## - text: The text of the choice.
54+
## - visited_before: If the choice has been selected before. Only available is the History submodule is enabled.
55+
## - *: Information from the event's additional info.
3456
func _load_info(choice_info: Dictionary) -> void:
3557
set_choice_text(choice_info.text)
3658
visible = choice_info.visible

addons/dialogic/Modules/Choice/subsystem_choices.gd

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ func post_install() -> void:
7070
func hide_all_choices() -> void:
7171
for node in get_tree().get_nodes_in_group('dialogic_choice_button'):
7272
node.hide()
73-
if node.pressed.is_connected(_on_choice_selected):
74-
node.pressed.disconnect(_on_choice_selected)
73+
if node.choice_selected.is_connected(_on_choice_selected):
74+
node.choice_selected.disconnect(_on_choice_selected)
7575

7676

7777
## Collects information on all the choices of the current question.
@@ -182,9 +182,9 @@ func show_current_question(instant:=true) -> void:
182182
shortcut.events.append(input_key)
183183
node.shortcut = shortcut
184184

185-
if node.pressed.is_connected(_on_choice_selected):
186-
node.pressed.disconnect(_on_choice_selected)
187-
node.pressed.connect(_on_choice_selected.bind(choice))
185+
if node.choice_selected.is_connected(_on_choice_selected):
186+
node.choice_selected.disconnect(_on_choice_selected)
187+
node.choice_selected.connect(_on_choice_selected.bind(choice))
188188

189189
_choice_blocker.start(block_delay)
190190
question_shown.emit(question_info)
@@ -202,12 +202,12 @@ func focus_choice(button_index:int) -> void:
202202
func select_choice(button_index:int) -> void:
203203
var node: DialogicNode_ChoiceButton = get_choice_button(button_index)
204204
if node:
205-
node.pressed.emit()
205+
node.choice_selected.emit()
206206

207207

208208
func select_focused_choice() -> void:
209209
if get_viewport().gui_get_focus_owner() is DialogicNode_ChoiceButton:
210-
(get_viewport().gui_get_focus_owner() as DialogicNode_ChoiceButton).pressed.emit()
210+
(get_viewport().gui_get_focus_owner() as DialogicNode_ChoiceButton).choice_selected.emit()
211211

212212

213213
func get_choice_button(button_index:int) -> DialogicNode_ChoiceButton:

addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ func get_base_content_size() -> Vector2:
155155
)
156156

157157

158-
func add_choice_container(node:Container, alignment:=FlowContainer.ALIGNMENT_BEGIN) -> void:
158+
func add_choice_container(node:Container, alignment:=FlowContainer.ALIGNMENT_BEGIN, choices_button_path:="", maximum_choices:=5) -> void:
159159
if choice_container:
160160
choice_container.get_parent().remove_child(choice_container)
161161
choice_container.queue_free()
@@ -168,9 +168,21 @@ func add_choice_container(node:Container, alignment:=FlowContainer.ALIGNMENT_BEG
168168

169169
if node is HFlowContainer:
170170
(node as HFlowContainer).alignment = alignment
171-
172-
for i:int in range(5):
173-
choice_container.add_child(DialogicNode_ChoiceButton.new())
171+
172+
var choices_button: PackedScene = null
173+
if not choices_button_path.is_empty():
174+
if ResourceLoader.exists(choices_button_path):
175+
choices_button = (load(choices_button_path) as PackedScene)
176+
else:
177+
printerr("[Dialogic] Unable to load custom choice button from ", choices_button_path)
178+
179+
for i:int in range(maximum_choices):
180+
var new_button : DialogicNode_ChoiceButton
181+
if choices_button == null:
182+
new_button = DialogicNode_ChoiceButton.new()
183+
else:
184+
new_button = (choices_button.instantiate() as DialogicNode_ChoiceButton)
185+
choice_container.add_child(new_button)
174186
if node is HFlowContainer:
175187
continue
176188
match alignment:

addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble_layer.gd

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ extends DialogicLayoutLayer
5656
@export var choices_layout_force_lines: bool = false
5757
@export_file('*.tres', "*.res") var choices_base_theme: String = ""
5858

59+
@export_subgroup('Behavior')
60+
@export var maximum_choices: int = 5
61+
@export_file('*.tscn') var choices_custom_button: String = ""
62+
5963
const TextBubble := preload("res://addons/dialogic/Modules/DefaultLayoutParts/Layer_Textbubble/text_bubble.gd")
6064

6165
var bubbles: Array[TextBubble] = []
@@ -150,9 +154,9 @@ func bubble_apply_overrides(bubble:TextBubble) -> void:
150154

151155
## CHOICE SETTINGS
152156
if choices_layout_force_lines:
153-
bubble.add_choice_container(VBoxContainer.new(), choices_layout_alignment)
157+
bubble.add_choice_container(VBoxContainer.new(), choices_layout_alignment, choices_custom_button, maximum_choices)
154158
else:
155-
bubble.add_choice_container(HFlowContainer.new(), choices_layout_alignment)
159+
bubble.add_choice_container(HFlowContainer.new(), choices_layout_alignment, choices_custom_button, maximum_choices)
156160

157161
var choice_theme: Theme = null
158162
if choices_base_theme.is_empty() or not ResourceLoader.exists(choices_base_theme):

addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.gd

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ extends DialogicLayoutLayer
3939
@export_file("*.wav", "*.ogg", "*.mp3") var sounds_hover: String = "res://addons/dialogic/Example Assets/sound-effects/typing2.wav"
4040
@export_file("*.wav", "*.ogg", "*.mp3") var sounds_focus: String = "res://addons/dialogic/Example Assets/sound-effects/typing4.wav"
4141

42+
@export_group('Choices')
43+
@export_subgroup('Behavior')
44+
@export var maximum_choices: int = 10
45+
@export_file('*.tscn') var choices_custom_button: String = ""
46+
4247
func get_choices() -> VBoxContainer:
4348
return $Choices
4449

@@ -96,20 +101,36 @@ func _apply_export_overrides() -> void:
96101
if ResourceLoader.exists(boxes_stylebox_focused):
97102
layer_theme.set_stylebox(&'focus', &'Button', load(boxes_stylebox_focused) as StyleBox)
98103

99-
get_choices().add_theme_constant_override(&"separation", boxes_v_separation)
104+
var choices : Control = get_choices()
105+
choices.add_theme_constant_override(&"separation", boxes_v_separation)
100106
self.position = boxes_offset
101107

102-
for child: Node in get_choices().get_children():
103-
if not child is DialogicNode_ChoiceButton:
104-
continue
105-
var choice: DialogicNode_ChoiceButton = child as DialogicNode_ChoiceButton
108+
# replace choice buttons and apply settings
109+
for child: Node in choices.get_children():
110+
if child is DialogicNode_ChoiceButton:
111+
child.queue_free()
112+
113+
var choices_button: PackedScene = null
114+
if not choices_custom_button.is_empty():
115+
if ResourceLoader.exists(choices_custom_button):
116+
choices_button = (load(choices_custom_button) as PackedScene)
117+
else:
118+
printerr("[Dialogic] Unable to load custom choice button from ", choices_custom_button)
119+
120+
for i in range(0, maximum_choices):
121+
var new_choice : DialogicNode_ChoiceButton
122+
if choices_button != null:
123+
new_choice = (choices_button.instantiate() as DialogicNode_ChoiceButton)
124+
else:
125+
new_choice = DialogicNode_ChoiceButton.new()
126+
choices.add_child(new_choice)
106127

107128
if boxes_fill_width:
108-
choice.size_flags_horizontal = Control.SIZE_FILL
129+
new_choice.size_flags_horizontal = Control.SIZE_FILL
109130
else:
110-
choice.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
131+
new_choice.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
111132

112-
choice.custom_minimum_size = boxes_min_size
133+
new_choice.custom_minimum_size = boxes_min_size
113134

114135

115136
set(&'theme', layer_theme)

addons/dialogic/Modules/DefaultLayoutParts/Layer_VN_Choices/vn_choice_layer.tscn

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -35,61 +35,6 @@ mouse_filter = 2
3535
alignment = 1
3636
metadata/_edit_layout_mode = 1
3737

38-
[node name="DialogicNode_ChoiceButton1" type="Button" parent="Choices"]
39-
layout_mode = 2
40-
text = "Some text"
41-
script = ExtResource("1_w632k")
42-
43-
[node name="DialogicNode_ChoiceButton2" type="Button" parent="Choices"]
44-
layout_mode = 2
45-
text = "Some text"
46-
script = ExtResource("1_w632k")
47-
48-
[node name="DialogicNode_ChoiceButton3" type="Button" parent="Choices"]
49-
layout_mode = 2
50-
text = "Some text"
51-
script = ExtResource("1_w632k")
52-
53-
[node name="DialogicNode_ChoiceButton4" type="Button" parent="Choices"]
54-
layout_mode = 2
55-
text = "Some text"
56-
script = ExtResource("1_w632k")
57-
58-
[node name="DialogicNode_ChoiceButton5" type="Button" parent="Choices"]
59-
layout_mode = 2
60-
text = "Some text"
61-
script = ExtResource("1_w632k")
62-
63-
[node name="DialogicNode_ChoiceButton6" type="Button" parent="Choices"]
64-
layout_mode = 2
65-
text = "Some text"
66-
script = ExtResource("1_w632k")
67-
68-
[node name="DialogicNode_ChoiceButton7" type="Button" parent="Choices"]
69-
layout_mode = 2
70-
text = "Some text"
71-
script = ExtResource("1_w632k")
72-
73-
[node name="DialogicNode_ChoiceButton8" type="Button" parent="Choices"]
74-
layout_mode = 2
75-
text = "Some text"
76-
script = ExtResource("1_w632k")
77-
78-
[node name="DialogicNode_ChoiceButton9" type="Button" parent="Choices"]
79-
layout_mode = 2
80-
text = "Some text"
81-
script = ExtResource("1_w632k")
82-
83-
[node name="DialogicNode_ChoiceButton10" type="Button" parent="Choices"]
84-
layout_mode = 2
85-
text = "Some text"
86-
script = ExtResource("1_w632k")
87-
88-
[node name="DialogicNode_ChoiceButton11" type="Button" parent="Choices"]
89-
layout_mode = 2
90-
text = "Some text"
91-
script = ExtResource("1_w632k")
92-
9338
[node name="DialogicNode_ButtonSound" type="AudioStreamPlayer" parent="Choices"]
9439
unique_name_in_owner = true
9540
script = ExtResource("2_mgko6")

0 commit comments

Comments
 (0)