]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Editor/Events/Fields/field_options_dynamic.gd
Squashed commit of the following:
[wolf-seeking-sheep.git] / addons / dialogic / Editor / Events / Fields / field_options_dynamic.gd
1 @tool
2 extends DialogicVisualEditorField
3 ## Event block field for strings. Options are determined by a function.
4
5
6 ## SETTINGS
7 @export var placeholder_text := "Select Resource"
8 @export var empty_text := ""
9 enum Modes {PURE_STRING, PRETTY_PATH, IDENTIFIER}
10 @export var mode := Modes.PURE_STRING
11 @export var fit_text_length := true
12 var collapse_when_empty := false
13 var valid_file_drop_extension := ""
14 var get_suggestions_func: Callable
15
16 var resource_icon: Texture = null:
17         get:
18                 return resource_icon
19         set(new_icon):
20                 resource_icon = new_icon
21                 %Icon.texture = new_icon
22
23 ## STATE
24 var current_value: String
25 var current_selected := 0
26
27 ## SUGGESTIONS ITEM LIST
28 var _v_separation := 0
29 var _h_separation := 0
30 var _icon_margin := 0
31 var _line_height := 24
32 var _max_height := 200 * DialogicUtil.get_editor_scale()
33
34
35 #region FIELD METHODS
36 ################################################################################
37
38 func _set_value(value:Variant) -> void:
39         if value == null or value.is_empty():
40                 %Search.text = empty_text
41         else:
42                 match mode:
43                         Modes.PRETTY_PATH:
44                                 %Search.text = DialogicUtil.pretty_name(value)
45                         Modes.IDENTIFIER when value.begins_with("res://"):
46                                 %Search.text = DialogicResourceUtil.get_unique_identifier(value)
47                         _:
48                                 %Search.text = str(value)
49
50         %Search.visible = not collapse_when_empty or value
51         current_value = str(value)
52
53
54
55 func _load_display_info(info:Dictionary) -> void:
56         valid_file_drop_extension = info.get('file_extension', '')
57         collapse_when_empty = info.get('collapse_when_empty', false)
58         get_suggestions_func = info.get('suggestions_func', get_suggestions_func)
59         empty_text = info.get('empty_text', '')
60         placeholder_text = info.get('placeholder', 'Select Resource')
61         mode = info.get("mode", 0)
62         resource_icon = info.get('icon', null)
63         %Search.tooltip_text = info.get('tooltip_text', '')
64         await ready
65         if resource_icon == null and info.has('editor_icon'):
66                 resource_icon = callv('get_theme_icon', info.editor_icon)
67
68
69 func _autofocus() -> void:
70         %Search.grab_focus()
71
72 #endregion
73
74
75 #region BASIC
76 ################################################################################
77
78 func _ready() -> void:
79         var focus := get_theme_stylebox("focus", "LineEdit")
80         if has_theme_stylebox("focus", "DialogicEventEdit"):
81                 focus = get_theme_stylebox('focus', 'DialogicEventEdit')
82         %Focus.add_theme_stylebox_override('panel', focus)
83
84         %Search.text_changed.connect(_on_Search_text_changed)
85         %Search.text_submitted.connect(_on_Search_text_entered)
86         %Search.placeholder_text = placeholder_text
87         %Search.expand_to_text_length = fit_text_length
88
89         %SelectButton.icon = get_theme_icon("Collapse", "EditorIcons")
90
91         %Suggestions.add_theme_stylebox_override('bg', load("res://addons/dialogic/Editor/Events/styles/ResourceMenuPanelBackground.tres"))
92         %Suggestions.hide()
93         %Suggestions.item_selected.connect(suggestion_selected)
94         %Suggestions.item_clicked.connect(suggestion_selected)
95         %Suggestions.fixed_icon_size = Vector2i(16, 16) * DialogicUtil.get_editor_scale()
96
97         _v_separation = %Suggestions.get_theme_constant("v_separation")
98         _h_separation = %Suggestions.get_theme_constant("h_separation")
99         _icon_margin = %Suggestions.get_theme_constant("icon_margin")
100
101         if resource_icon == null:
102                 self.resource_icon = null
103
104
105 func change_to_empty() -> void:
106         value_changed.emit(property_name, "")
107
108 #endregion
109
110
111 #region SEARCH & SUGGESTION POPUP
112 ################################################################################
113 func _on_Search_text_entered(new_text:String) -> void:
114         if %Suggestions.get_item_count():
115                 if %Suggestions.is_anything_selected():
116                         suggestion_selected(%Suggestions.get_selected_items()[0])
117                 else:
118                         suggestion_selected(0)
119         else:
120                 change_to_empty()
121
122
123 func _on_Search_text_changed(new_text:String, just_update:bool = false) -> void:
124         %Suggestions.clear()
125
126         if new_text == "" and !just_update:
127                 change_to_empty()
128         else:
129                 %Search.show()
130
131         var suggestions: Dictionary = get_suggestions_func.call(new_text)
132
133         var line_length := 0
134         var idx := 0
135         for element in suggestions:
136                 if new_text.is_empty() or new_text.to_lower() in element.to_lower() or new_text.to_lower() in str(suggestions[element].value).to_lower() or new_text.to_lower() in suggestions[element].get('tooltip', '').to_lower():
137                         var curr_line_length: int = 0
138                         curr_line_length = get_theme_font('font', 'Label').get_string_size(
139                                 element, HORIZONTAL_ALIGNMENT_LEFT, -1, get_theme_font_size("font_size", 'Label')
140                         ).x
141
142                         %Suggestions.add_item(element)
143                         if suggestions[element].has('icon'):
144                                 %Suggestions.set_item_icon(idx, suggestions[element].icon)
145                                 curr_line_length += %Suggestions.fixed_icon_size.x * %Suggestions.get_icon_scale() + _icon_margin * 2 + _h_separation
146                         elif suggestions[element].has('editor_icon'):
147                                 %Suggestions.set_item_icon(idx, get_theme_icon(suggestions[element].editor_icon[0],suggestions[element].editor_icon[1]))
148                                 curr_line_length += %Suggestions.fixed_icon_size.x * %Suggestions.get_icon_scale() + _icon_margin * 2 + _h_separation
149
150                         line_length = max(line_length, curr_line_length)
151
152                         %Suggestions.set_item_tooltip(idx, suggestions[element].get('tooltip', ''))
153                         %Suggestions.set_item_metadata(idx, suggestions[element].value)
154                         idx += 1
155
156         if not %Suggestions.visible:
157                 %Suggestions.show()
158                 %Suggestions.global_position = $PanelContainer.global_position+Vector2(0,1)*$PanelContainer.size.y
159
160         if %Suggestions.item_count:
161                 %Suggestions.select(0)
162                 current_selected = 0
163         else:
164                 current_selected = -1
165         %Search.grab_focus()
166
167         var total_height: int = 0
168         for item in %Suggestions.item_count:
169                 total_height += _line_height * DialogicUtil.get_editor_scale() + _v_separation
170         total_height += _v_separation * 2
171         if total_height > _max_height:
172                 line_length += %Suggestions.get_v_scroll_bar().get_minimum_size().x
173
174         %Suggestions.size.x = max(%PanelContainer.size.x, line_length)
175         %Suggestions.size.y = min(total_height, _max_height)
176
177         # Defer setting width to give PanelContainer
178         # time to update it's size
179         await get_tree().process_frame
180         await get_tree().process_frame
181
182         %Suggestions.size.x = max(%PanelContainer.size.x, line_length)
183
184
185 func suggestion_selected(index: int, position := Vector2(), button_index := MOUSE_BUTTON_LEFT) -> void:
186         if button_index != MOUSE_BUTTON_LEFT:
187                 return
188         if %Suggestions.is_item_disabled(index):
189                 return
190
191         %Search.text = %Suggestions.get_item_text(index)
192
193         if %Suggestions.get_item_metadata(index) == null:
194                 current_value = ""
195
196         else:
197                 current_value = %Suggestions.get_item_metadata(index)
198
199         hide_suggestions()
200
201         grab_focus()
202         value_changed.emit(property_name, current_value)
203
204
205 func _input(event:InputEvent) -> void:
206         if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
207                 if %Suggestions.visible:
208                         if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()) and \
209                                 !%SelectButton.get_global_rect().has_point(get_global_mouse_position()):
210                                 hide_suggestions()
211
212
213 func hide_suggestions() -> void:
214         %SelectButton.set_pressed_no_signal(false)
215         %Suggestions.hide()
216         if !current_value and collapse_when_empty:
217                 %Search.hide()
218
219
220 func _on_SelectButton_toggled(button_pressed:bool) -> void:
221         if button_pressed:
222                 _on_Search_text_changed('', true)
223         else:
224                 hide_suggestions()
225
226
227 func _on_focus_entered() -> void:
228         %Search.grab_focus()
229
230
231 func _on_search_gui_input(event: InputEvent) -> void:
232         if event is InputEventKey and (event.keycode == KEY_DOWN or event.keycode == KEY_UP) and event.pressed:
233                 if !%Suggestions.visible:
234                         _on_Search_text_changed('', true)
235                         current_selected = -1
236                 if event.keycode == KEY_DOWN:
237                         current_selected = wrapi(current_selected+1, 0, %Suggestions.item_count)
238                 if event.keycode == KEY_UP:
239                         current_selected = wrapi(current_selected-1, 0, %Suggestions.item_count)
240                 %Suggestions.select(current_selected)
241                 %Suggestions.ensure_current_is_visible()
242
243         if Input.is_key_pressed(KEY_CTRL):
244                 if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
245                         if valid_file_drop_extension in [".dch", ".dtl"] and not current_value.is_empty():
246                                 EditorInterface.edit_resource(DialogicResourceUtil.get_resource_from_identifier(current_value, valid_file_drop_extension))
247
248                 if valid_file_drop_extension in [".dch", ".dtl"] and not current_value.is_empty():
249                         %Search.mouse_default_cursor_shape = CURSOR_POINTING_HAND
250         else:
251                 %Search.mouse_default_cursor_shape = CURSOR_IBEAM
252
253
254 func _on_search_focus_entered() -> void:
255         if %Search.text == "":
256                 _on_Search_text_changed("")
257         %Search.call_deferred('select_all')
258         %Focus.show()
259
260
261 func _on_search_focus_exited() -> void:
262         %Focus.hide()
263         if !%Suggestions.get_global_rect().has_point(get_global_mouse_position()):
264                 hide_suggestions()
265
266 #endregion
267
268
269 #region DRAG AND DROP
270 ################################################################################
271
272 func _can_drop_data(position:Vector2, data:Variant) -> bool:
273         if typeof(data) == TYPE_DICTIONARY and data.has('files') and len(data.files) == 1:
274                 if valid_file_drop_extension:
275                         if data.files[0].ends_with(valid_file_drop_extension):
276                                 return true
277                 else:
278                         return false
279         return false
280
281
282 func _drop_data(position:Vector2, data:Variant) -> void:
283         var path := str(data.files[0])
284         if mode == Modes.IDENTIFIER:
285                 path = DialogicResourceUtil.get_unique_identifier(path)
286         _set_value(path)
287         value_changed.emit(property_name, path)
288
289 #endregion