forked from Orama-Interactive/Pixelorama
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExportDialog.gd
483 lines (369 loc) · 15 KB
/
ExportDialog.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
extends ConfirmationDialog
## Called when user resumes export after filename collision
signal resume_export_function
signal about_to_preview(dict: Dictionary)
var preview_current_frame := 0
var preview_frames: Array[Texture2D] = []
# Allow custom exporters to be added
var image_exports: Array[Export.FileFormat] = [
Export.FileFormat.PNG,
Export.FileFormat.WEBP,
Export.FileFormat.JPEG,
Export.FileFormat.GIF,
Export.FileFormat.APNG,
Export.FileFormat.MP4,
Export.FileFormat.AVI,
Export.FileFormat.OGV,
Export.FileFormat.MKV,
Export.FileFormat.WEBM,
]
var spritesheet_exports: Array[Export.FileFormat] = [
Export.FileFormat.PNG, Export.FileFormat.WEBP, Export.FileFormat.JPEG
]
var _preview_images: Array[Export.ProcessedImage]
@onready var tabs: TabBar = $VBoxContainer/TabBar
@onready var checker: ColorRect = $"%TransparentChecker"
@onready var previews: GridContainer = $"%Previews"
@onready var spritesheet_orientation: OptionButton = $"%Orientation"
@onready var spritesheet_lines_count: SpinBox = $"%LinesCount"
@onready var spritesheet_lines_count_label: Label = $"%LinesCountLabel"
@onready var frames_option_button: OptionButton = $"%Frames"
@onready var layers_option_button: OptionButton = $"%Layers"
@onready var options_resize: ValueSlider = $"%Resize"
@onready var dimension_label: Label = $"%DimensionLabel"
@onready var path_line_edit: LineEdit = $"%PathLineEdit"
@onready var file_line_edit: LineEdit = $"%FileLineEdit"
@onready var file_format_options: OptionButton = $"%FileFormat"
@onready var options_interpolation: OptionButton = $"%Interpolation"
@onready var file_exists_alert_popup: AcceptDialog = $FileExistsAlert
@onready var path_validation_alert_popup: AcceptDialog = $PathValidationAlert
@onready var path_dialog_popup: FileDialog = $PathDialog
@onready var export_progress_popup: Window = $ExportProgressBar
@onready var export_progress_bar := %ProgressBar as ProgressBar
@onready var frame_timer: Timer = $FrameTimer
func _ready() -> void:
get_ok_button().size_flags_horizontal = Control.SIZE_EXPAND_FILL
get_cancel_button().size_flags_horizontal = Control.SIZE_EXPAND_FILL
tabs.add_tab("Image")
tabs.add_tab("Spritesheet")
if OS.get_name() == "Windows":
file_exists_alert_popup.add_button("Cancel Export", true, "cancel")
else:
file_exists_alert_popup.add_button("Cancel Export", false, "cancel")
func show_tab() -> void:
get_tree().call_group("ExportImageOptions", "hide")
get_tree().call_group("ExportSpritesheetOptions", "hide")
set_file_format_selector()
create_frame_tag_list()
frames_option_button.select(Export.frame_current_tag)
create_layer_list()
layers_option_button.select(Export.export_layers)
match Export.current_tab:
Export.ExportTab.IMAGE:
Export.process_animation()
get_tree().call_group("ExportImageOptions", "show")
get_tree().set_group(
"ExportMultipleFilesOptions", "disabled", Export.is_single_file_format()
)
get_tree().set_group(
"ExportMultipleFilesEditableOptions", "editable", !Export.is_single_file_format()
)
Export.ExportTab.SPRITESHEET:
frame_timer.stop()
Export.process_spritesheet()
spritesheet_orientation.selected = Export.orientation
spritesheet_lines_count.max_value = Export.number_of_frames
spritesheet_lines_count.value = Export.lines_count
get_tree().call_group("ExportSpritesheetOptions", "show")
_handle_orientation_ui()
set_preview()
update_dimensions_label()
tabs.current_tab = Export.current_tab
func set_preview() -> void:
_preview_images = Export.processed_images
var preview_data := {
"exporter_id": Global.current_project.file_format,
"export_tab": Export.current_tab,
"preview_images": _preview_images,
}
about_to_preview.emit(preview_data)
remove_previews()
if _preview_images.size() == 1:
previews.columns = 1
add_image_preview(_preview_images[0].image)
else:
if Export.is_single_file_format():
previews.columns = 1
add_animated_preview()
else:
previews.columns = ceili(sqrt(_preview_images.size()))
for i in range(_preview_images.size()):
add_image_preview(_preview_images[i].image, i + 1)
func add_image_preview(image: Image, canvas_number: int = -1) -> void:
var container := create_preview_container()
var preview := create_preview_rect()
preview.texture = ImageTexture.create_from_image(image)
container.add_child(preview)
if canvas_number != -1:
var label := Label.new()
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.text = str(canvas_number)
container.add_child(label)
previews.add_child(container)
func add_animated_preview() -> void:
preview_current_frame = 0
preview_frames = []
for processed_image in _preview_images:
var texture := ImageTexture.create_from_image(processed_image.image)
preview_frames.push_back(texture)
var container := create_preview_container()
container.name = "PreviewContainer"
var preview := create_preview_rect()
preview.name = "Preview"
preview.texture = preview_frames[preview_current_frame]
container.add_child(preview)
previews.add_child(container)
frame_timer.set_one_shot(true) # wait_time can't change correctly if the timer is playing
frame_timer.wait_time = _preview_images[preview_current_frame].duration
frame_timer.start()
func create_preview_container() -> VBoxContainer:
var container := VBoxContainer.new()
container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
container.size_flags_vertical = Control.SIZE_EXPAND_FILL
container.custom_minimum_size = Vector2(0, 128)
return container
func create_preview_rect() -> TextureRect:
var preview := TextureRect.new()
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
preview.size_flags_horizontal = Control.SIZE_EXPAND_FILL
preview.size_flags_vertical = Control.SIZE_EXPAND_FILL
preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
return preview
func remove_previews() -> void:
for child in previews.get_children():
child.free()
func set_file_format_selector() -> void:
match Export.current_tab:
Export.ExportTab.IMAGE:
_set_file_format_selector_suitable_file_formats(image_exports)
if Export.is_ffmpeg_installed():
for format in Export.ffmpeg_formats:
file_format_options.set_item_disabled(format, false)
else:
for format in Export.ffmpeg_formats:
file_format_options.set_item_disabled(format, true)
Export.ExportTab.SPRITESHEET:
_set_file_format_selector_suitable_file_formats(spritesheet_exports)
## Updates the suitable list of file formats. First is preferred.
## Note that if the current format is in the list, it stays for consistency.
func _set_file_format_selector_suitable_file_formats(formats: Array[Export.FileFormat]) -> void:
var project := Global.current_project
file_format_options.clear()
var needs_update := true
for i in formats:
if project.file_format == i:
needs_update = false
var label := Export.file_format_string(i) + "; " + Export.file_format_description(i)
file_format_options.add_item(label, i)
if needs_update:
project.file_format = formats[0]
file_format_options.selected = file_format_options.get_item_index(project.file_format)
func create_frame_tag_list() -> void:
# Clear existing tag list from entry if it exists
frames_option_button.clear()
# Re-add removed items
frames_option_button.add_item("All frames", 0)
frames_option_button.add_item("Selected frames", 1)
# Repopulate list with current tag list
for item in Global.current_project.animation_tags:
frames_option_button.add_item(item.name)
func create_layer_list() -> void:
# Clear existing tag list from entry if it exists
layers_option_button.clear()
# Re-add removed items
layers_option_button.add_item("Visible layers", 0)
layers_option_button.add_item("Selected layers", 1)
# Repopulate list with current tag list
for layer in Global.current_project.layers:
var layer_name := tr("Pixel layer:")
if layer is GroupLayer:
layer_name = tr("Group layer:")
elif layer is Layer3D:
layer_name = tr("3D layer:")
layer_name += " %s" % layer.get_layer_path()
layers_option_button.add_item(layer_name)
func update_dimensions_label() -> void:
if _preview_images.size() > 0:
var new_size: Vector2i = _preview_images[0].image.get_size() * (Export.resize / 100.0)
dimension_label.text = str(new_size.x, "×", new_size.y)
func open_path_validation_alert_popup(path_or_name: int = -1) -> void:
# 0 is invalid path, 1 is invalid name
var error_text := "Directory path and file name are not valid!"
if path_or_name == 0:
error_text = "Directory path is not valid!"
elif path_or_name == 1:
error_text = "File name is not valid!"
path_validation_alert_popup.dialog_text = error_text
print(error_text)
path_validation_alert_popup.popup_centered()
func open_file_exists_alert_popup(text: String) -> void:
file_exists_alert_popup.dialog_text = text
file_exists_alert_popup.popup_centered()
func toggle_export_progress_popup(open: bool) -> void:
if open:
export_progress_popup.popup_centered()
else:
export_progress_popup.hide()
func set_export_progress_bar(value: float) -> void:
export_progress_bar.value = value
func _on_about_to_popup() -> void:
get_ok_button().text = "Export"
Global.canvas.selection.transform_content_confirm()
var project := Global.current_project
# If we're on Web, don't let the user change the directory path
if OS.get_name() == "Web":
get_tree().call_group("NotHTML5", "hide")
project.export_directory_path = "user://"
if project.export_directory_path.is_empty():
project.export_directory_path = Global.config_cache.get_value(
"data", "current_dir", OS.get_system_dir(OS.SYSTEM_DIR_DESKTOP)
)
# If export already occurred - sets GUI to show previous settings
options_resize.value = Export.resize
options_interpolation.selected = Export.interpolation
path_line_edit.text = project.export_directory_path
path_dialog_popup.current_dir = project.export_directory_path
file_line_edit.text = project.file_name
file_format_options.selected = project.file_format
Export.cache_blended_frames()
show_tab()
# Set the size of the preview checker
checker.size = checker.get_parent().size
func _on_tab_bar_tab_clicked(tab: Export.ExportTab) -> void:
Export.current_tab = tab
show_tab()
func _on_orientation_item_selected(id: Export.Orientation) -> void:
Export.orientation = id
_handle_orientation_ui()
spritesheet_lines_count.value = Export.frames_divided_by_spritesheet_lines()
Export.process_spritesheet()
update_dimensions_label()
set_preview()
func _handle_orientation_ui() -> void:
if Export.orientation == Export.Orientation.ROWS:
spritesheet_lines_count_label.visible = true
spritesheet_lines_count.visible = true
spritesheet_lines_count_label.text = "Columns:"
elif Export.orientation == Export.Orientation.COLUMNS:
spritesheet_lines_count_label.visible = true
spritesheet_lines_count.visible = true
spritesheet_lines_count_label.text = "Rows:"
else:
spritesheet_lines_count_label.visible = false
spritesheet_lines_count.visible = false
func _on_lines_count_value_changed(value: float) -> void:
Export.lines_count = value
Export.process_spritesheet()
update_dimensions_label()
set_preview()
func _on_direction_item_selected(id: Export.AnimationDirection) -> void:
Export.direction = id
preview_current_frame = 0
Export.process_data()
set_preview()
update_dimensions_label()
func _on_resize_value_changed(value: float) -> void:
Export.resize = value
update_dimensions_label()
func _on_quality_value_changed(value: float) -> void:
Export.save_quality = value / 100.0
func _on_interpolation_item_selected(id: Image.Interpolation) -> void:
Export.interpolation = id
func _on_confirmed() -> void:
Global.current_project.export_overwrite = false
if await Export.export_processed_images(false, self, Global.current_project):
hide()
func _on_path_button_pressed() -> void:
path_dialog_popup.popup_centered()
func _on_path_line_edit_text_changed(new_text: String) -> void:
Global.current_project.export_directory_path = new_text
func _on_file_line_edit_text_changed(new_text: String) -> void:
Global.current_project.file_name = new_text
func _on_path_dialog_dir_selected(dir: String) -> void:
path_line_edit.text = dir
Global.current_project.export_directory_path = dir
# Needed because if native file dialogs are enabled
# the export dialog closes when the path dialog closes
if not visible:
show()
func _on_path_dialog_canceled() -> void:
# Needed because if native file dialogs are enabled
# the export dialog closes when the path dialog closes
if not visible:
show()
func _on_file_format_item_selected(idx: int) -> void:
var id := file_format_options.get_item_id(idx) as Export.FileFormat
Global.current_project.file_format = id
if not Export.is_single_file_format():
get_tree().set_group("ExportMultipleFilesOptions", "disabled", false)
get_tree().set_group("ExportMultipleFilesEditableOptions", "editable", true)
frame_timer.stop()
else:
get_tree().set_group("ExportMultipleFilesOptions", "disabled", true)
get_tree().set_group("ExportMultipleFilesEditableOptions", "editable", false)
var show_quality := id == Export.FileFormat.JPEG
%QualityLabel.visible = show_quality
%Quality.visible = show_quality
set_preview()
## Overwrite existing file
func _on_file_exists_alert_confirmed() -> void:
file_exists_alert_popup.dialog_text = Export.file_exists_alert
Export.stop_export = false
resume_export_function.emit()
func _on_file_exists_alert_custom_action(action: StringName) -> void:
if action == &"cancel":
# Cancel export
file_exists_alert_popup.dialog_text = Export.file_exists_alert
Export.stop_export = true
resume_export_function.emit()
file_exists_alert_popup.hide()
func _on_frame_timer_timeout() -> void:
var preview_texture_rect: TextureRect = previews.get_node("PreviewContainer/Preview")
if not preview_texture_rect:
return
preview_texture_rect.texture = preview_frames[preview_current_frame]
if preview_current_frame == preview_frames.size() - 1:
preview_current_frame = 0
else:
preview_current_frame += 1
frame_timer.wait_time = _preview_images[preview_current_frame - 1].duration
frame_timer.start()
func _on_ExportDialog_popup_hide() -> void:
frame_timer.stop()
func _on_export_json_toggled(toggled_on: bool) -> void:
Export.export_json = toggled_on
func _on_split_layers_toggled(toggled_on: bool) -> void:
Export.split_layers = toggled_on
Export.process_data()
set_preview()
func _on_include_tags_in_filename_toggled(button_pressed: bool) -> void:
Export.include_tag_in_filename = button_pressed
func _on_multiple_animations_directories_toggled(button_pressed: bool) -> void:
Export.new_dir_for_each_frame_tag = button_pressed
func _on_trim_images_toggled(toggled_on: bool) -> void:
Export.trim_images = toggled_on
Export.process_data()
set_preview()
func _on_frames_item_selected(id: int) -> void:
Export.frame_current_tag = id
Export.process_data()
set_preview()
spritesheet_lines_count.max_value = Export.number_of_frames
spritesheet_lines_count.value = Export.lines_count
func _on_layers_item_selected(id: int) -> void:
Export.export_layers = id
Export.cache_blended_frames()
Export.process_data()
set_preview()
func _on_separator_character_text_changed(new_text: String) -> void:
Export.separator_character = new_text