Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/export/project_export.cpp
20836 views
1
/**************************************************************************/
2
/* project_export.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "project_export.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/version.h"
35
#include "editor/editor_node.h"
36
#include "editor/editor_string_names.h"
37
#include "editor/export/editor_export.h"
38
#include "editor/file_system/editor_file_system.h"
39
#include "editor/gui/editor_file_dialog.h"
40
#include "editor/import/resource_importer_texture_settings.h"
41
#include "editor/inspector/editor_properties.h"
42
#include "editor/settings/editor_settings.h"
43
#include "editor/settings/project_settings_editor.h"
44
#include "editor/themes/editor_scale.h"
45
#include "scene/gui/check_button.h"
46
#include "scene/gui/item_list.h"
47
#include "scene/gui/line_edit.h"
48
#include "scene/gui/link_button.h"
49
#include "scene/gui/margin_container.h"
50
#include "scene/gui/menu_button.h"
51
#include "scene/gui/option_button.h"
52
#include "scene/gui/popup_menu.h"
53
#include "scene/gui/rich_text_label.h"
54
#include "scene/gui/spin_box.h"
55
#include "scene/gui/split_container.h"
56
#include "scene/gui/tab_container.h"
57
#include "scene/gui/texture_rect.h"
58
#include "scene/gui/tree.h"
59
60
#include <zstd.h>
61
62
void ProjectExportTextureFormatError::_on_fix_texture_format_pressed() {
63
export_dialog->hide();
64
ProjectSettingsEditor *project_settings = EditorNode::get_singleton()->get_project_settings();
65
project_settings->set_general_page("rendering/textures");
66
project_settings->set_filter(setting_identifier);
67
project_settings->popup_project_settings(false);
68
}
69
70
void ProjectExportTextureFormatError::_bind_methods() {
71
}
72
73
void ProjectExportTextureFormatError::_notification(int p_what) {
74
switch (p_what) {
75
case NOTIFICATION_THEME_CHANGED: {
76
texture_format_error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
77
} break;
78
}
79
}
80
81
void ProjectExportTextureFormatError::show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier) {
82
texture_format_error_label->set_text(vformat(TTR("Target platform requires '%s' texture compression. Enable 'Import %s' to fix."), p_friendly_name, p_friendly_name.replace_char('/', ' ')));
83
setting_identifier = p_setting_identifier;
84
show();
85
}
86
87
ProjectExportTextureFormatError::ProjectExportTextureFormatError(ProjectExportDialog *p_export_dialog) {
88
export_dialog = p_export_dialog;
89
// Set up the label.
90
texture_format_error_label = memnew(Label);
91
texture_format_error_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
92
add_child(texture_format_error_label);
93
// Set up the fix button.
94
fix_texture_format_button = memnew(LinkButton);
95
fix_texture_format_button->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
96
fix_texture_format_button->set_text(TTR("Show Project Setting"));
97
add_child(fix_texture_format_button);
98
fix_texture_format_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportTextureFormatError::_on_fix_texture_format_pressed));
99
}
100
101
void ProjectExportDialog::_notification(int p_what) {
102
switch (p_what) {
103
case NOTIFICATION_VISIBILITY_CHANGED: {
104
if (!is_visible()) {
105
EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "export", Rect2(get_position(), get_size()));
106
show_script_key->set_pressed(false);
107
}
108
} break;
109
110
case NOTIFICATION_THEME_CHANGED: {
111
_script_encryption_key_visibility_changed(show_script_key->is_pressed());
112
duplicate_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Duplicate")));
113
delete_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Remove")));
114
patch_add_btn->set_button_icon(get_editor_theme_icon(SNAME("Add")));
115
} break;
116
117
case NOTIFICATION_READY: {
118
duplicate_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Duplicate")));
119
delete_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Remove")));
120
patch_add_btn->set_button_icon(get_editor_theme_icon(SNAME("Add")));
121
connect(SceneStringName(confirmed), callable_mp(this, &ProjectExportDialog::_export_pck_zip));
122
_update_export_all();
123
} break;
124
}
125
}
126
127
void ProjectExportDialog::popup_export() {
128
add_preset->get_popup()->clear();
129
for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) {
130
Ref<EditorExportPlatform> plat = EditorExport::get_singleton()->get_export_platform(i);
131
132
add_preset->get_popup()->add_icon_item(plat->get_logo(), plat->get_name());
133
}
134
135
_update_presets();
136
if (presets->get_current() >= 0) {
137
_update_current_preset(); // triggers rescan for templates if newly installed
138
}
139
140
// Restore valid window bounds or pop up at default size.
141
Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "export", Rect2());
142
if (saved_size != Rect2()) {
143
popup(saved_size);
144
} else {
145
popup_centered_clamped(Size2(900, 500) * EDSCALE, 0.7);
146
}
147
}
148
149
void ProjectExportDialog::_add_preset(int p_platform) {
150
Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_platform(p_platform)->create_preset();
151
ERR_FAIL_COND(preset.is_null());
152
153
String preset_name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name();
154
int attempt = 1;
155
while (true) {
156
bool valid = true;
157
158
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
159
Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
160
if (p->get_name() == preset_name) {
161
valid = false;
162
break;
163
}
164
}
165
166
if (valid) {
167
break;
168
}
169
170
attempt++;
171
preset_name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name() + " " + itos(attempt);
172
}
173
174
preset->set_name(preset_name);
175
if (EditorExport::get_singleton()->get_runnable_preset_for_platform(preset->get_platform()).is_null()) {
176
EditorExport::get_singleton()->set_runnable_preset(preset);
177
}
178
EditorExport::get_singleton()->add_export_preset(preset);
179
_update_presets();
180
_edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
181
}
182
183
void ProjectExportDialog::_force_update_current_preset_parameters() {
184
// Force the parameters section to refresh its UI.
185
parameters->edit(nullptr);
186
_update_current_preset();
187
}
188
189
void ProjectExportDialog::_update_current_preset() {
190
_edit_preset(presets->get_current());
191
}
192
193
void ProjectExportDialog::_update_presets() {
194
updating = true;
195
196
Ref<EditorExportPreset> current;
197
if (presets->get_current() >= 0 && presets->get_current() < presets->get_item_count()) {
198
current = get_current_preset();
199
}
200
201
int current_idx = -1;
202
int preset_count = EditorExport::get_singleton()->get_export_preset_count();
203
presets->clear();
204
for (int i = 0; i < preset_count; i++) {
205
Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
206
if (preset == current) {
207
current_idx = i;
208
} else if (current.is_null()) {
209
current_idx = i;
210
_edit_preset(i);
211
}
212
213
String preset_name = preset->get_name();
214
if (preset->is_runnable()) {
215
preset_name += " (" + TTR("Runnable") + ")";
216
}
217
preset->update_files();
218
presets->add_item(preset_name, preset->get_platform()->get_logo());
219
}
220
221
settings_vb->set_visible(current_idx != -1);
222
empty_label->set_visible(current_idx == -1);
223
224
if (current_idx != -1) {
225
presets->select(current_idx);
226
}
227
228
updating = false;
229
}
230
231
void ProjectExportDialog::_update_export_all() {
232
bool can_export = EditorExport::get_singleton()->get_export_preset_count() > 0;
233
234
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
235
Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
236
bool needs_templates;
237
String error;
238
if (preset->get_export_path().is_empty() || !preset->get_platform()->can_export(preset, error, needs_templates)) {
239
can_export = false;
240
break;
241
}
242
}
243
244
export_all_button->set_disabled(!can_export);
245
246
if (can_export) {
247
export_all_button->set_tooltip_text(TTR("Export the project for all the presets defined."));
248
} else {
249
export_all_button->set_tooltip_text(TTR("All presets must have an export path defined for Export All to work."));
250
}
251
}
252
253
void ProjectExportDialog::_edit_preset(int p_index) {
254
if (p_index < 0 || p_index >= presets->get_item_count()) {
255
parameters->edit(nullptr);
256
presets->deselect_all();
257
duplicate_preset->set_disabled(true);
258
delete_preset->set_disabled(true);
259
patches->clear();
260
export_error->hide();
261
export_templates_error->hide();
262
export_texture_format_error->hide();
263
return;
264
}
265
266
Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(p_index);
267
ERR_FAIL_COND(current.is_null());
268
269
updating = true;
270
271
presets->select(p_index);
272
duplicate_preset->set_disabled(false);
273
delete_preset->set_disabled(false);
274
get_ok_button()->set_disabled(false);
275
name->set_text(current->get_name());
276
277
List<String> extension_list = current->get_platform()->get_binary_extensions(current);
278
Vector<String> extension_vector;
279
for (const String &extension : extension_list) {
280
extension_vector.push_back("*." + extension);
281
}
282
283
export_path->get_path_edit()->clear();
284
export_path->setup(extension_vector, false, true, false);
285
export_path->update_property();
286
runnable->set_pressed(current->is_runnable());
287
if (parameters->get_edited_object() != current.ptr()) {
288
current->update_value_overrides();
289
}
290
parameters->set_object_class(current->get_platform()->get_class_name());
291
parameters->edit(current.ptr());
292
293
export_filter->select(current->get_export_filter());
294
include_filters->set_text(current->get_include_filter());
295
include_label->set_text(_get_resource_export_header(current->get_export_filter()));
296
exclude_filters->set_text(current->get_exclude_filter());
297
server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
298
299
bool patch_delta_encoding_enabled = current->is_patch_delta_encoding_enabled();
300
patch_delta_encoding->set_pressed(patch_delta_encoding_enabled);
301
patch_delta_zstd_level->set_editable(patch_delta_encoding_enabled);
302
patch_delta_zstd_level->set_value(current->get_patch_delta_zstd_level());
303
patch_delta_min_reduction->set_editable(patch_delta_encoding_enabled);
304
patch_delta_min_reduction->set_value(current->get_patch_delta_min_reduction() * 100);
305
patch_delta_include_filter->set_editable(patch_delta_encoding_enabled);
306
patch_delta_exclude_filter->set_editable(patch_delta_encoding_enabled);
307
if (!updating_patch_delta_filters) {
308
patch_delta_include_filter->set_text(current->get_patch_delta_include_filter());
309
patch_delta_exclude_filter->set_text(current->get_patch_delta_exclude_filter());
310
}
311
312
patches->clear();
313
TreeItem *patch_root = patches->create_item();
314
Vector<String> patch_list = current->get_patches();
315
for (int i = 0; i < patch_list.size(); i++) {
316
TreeItem *patch = patches->create_item(patch_root);
317
const String &patch_path = patch_list[i];
318
patch->set_cell_mode(0, TreeItem::CELL_MODE_STRING);
319
patch->set_editable(0, true);
320
patch->set_text(0, patch_path.get_file());
321
patch->set_tooltip_text(0, patch_path);
322
patch->set_metadata(0, i);
323
patch->add_button(0, get_editor_theme_icon(SNAME("Remove")), 0);
324
patch->add_button(0, get_editor_theme_icon(SNAME("FileBrowse")), 1);
325
}
326
327
_fill_resource_tree();
328
329
bool needs_templates;
330
String error;
331
if (!current->get_platform()->can_export(current, error, needs_templates)) {
332
if (!error.is_empty()) {
333
Vector<String> items = error.split("\n", false);
334
error = "";
335
for (int i = 0; i < items.size(); i++) {
336
if (i > 0) {
337
error += "\n";
338
}
339
error += " - " + items[i];
340
}
341
342
export_error->set_text(error);
343
export_error->show();
344
} else {
345
export_error->hide();
346
}
347
if (needs_templates) {
348
export_templates_error->show();
349
} else {
350
export_templates_error->hide();
351
}
352
353
export_warning->hide();
354
export_button->set_disabled(true);
355
} else {
356
if (error != String()) {
357
Vector<String> items = error.split("\n", false);
358
error = "";
359
for (int i = 0; i < items.size(); i++) {
360
if (i > 0) {
361
error += "\n";
362
}
363
error += " - " + items[i];
364
}
365
export_warning->set_text(error);
366
export_warning->show();
367
} else {
368
export_warning->hide();
369
}
370
371
export_error->hide();
372
export_templates_error->hide();
373
export_button->set_disabled(false);
374
}
375
376
custom_features->set_text(current->get_custom_features());
377
_update_feature_list();
378
_update_export_all();
379
child_controls_changed();
380
381
if ((feature_set.has("s3tc") || feature_set.has("bptc")) && !ResourceImporterTextureSettings::should_import_s3tc_bptc()) {
382
export_texture_format_error->show_for_texture_format("S3TC/BPTC", "rendering/textures/vram_compression/import_s3tc_bptc");
383
} else if ((feature_set.has("etc2") || feature_set.has("astc")) && !ResourceImporterTextureSettings::should_import_etc2_astc()) {
384
export_texture_format_error->show_for_texture_format("ETC2/ASTC", "rendering/textures/vram_compression/import_etc2_astc");
385
} else {
386
export_texture_format_error->hide();
387
}
388
389
String enc_in_filters_str = current->get_enc_in_filter();
390
String enc_ex_filters_str = current->get_enc_ex_filter();
391
if (!updating_enc_filters) {
392
enc_in_filters->set_text(enc_in_filters_str);
393
enc_ex_filters->set_text(enc_ex_filters_str);
394
}
395
396
bool enc_pck_mode = current->get_enc_pck();
397
enc_pck->set_pressed(enc_pck_mode);
398
399
uint64_t seed = current->get_seed();
400
if (!updating_seed) {
401
seed_input->set_text(itos(seed));
402
}
403
404
enc_directory->set_disabled(!enc_pck_mode);
405
enc_in_filters->set_editable(enc_pck_mode);
406
enc_ex_filters->set_editable(enc_pck_mode);
407
script_key->set_editable(enc_pck_mode);
408
show_script_key->set_disabled(!enc_pck_mode);
409
seed_input->set_editable(enc_pck_mode);
410
411
bool enc_directory_mode = current->get_enc_directory();
412
enc_directory->set_pressed(enc_directory_mode);
413
414
String key = current->get_script_encryption_key();
415
if (!updating_script_key) {
416
script_key->set_text(key);
417
}
418
if (enc_pck_mode) {
419
script_key->set_editable(true);
420
421
bool key_valid = _validate_script_encryption_key(key);
422
if (key_valid) {
423
script_key_error->hide();
424
} else {
425
script_key_error->show();
426
}
427
} else {
428
script_key->set_editable(false);
429
script_key_error->hide();
430
}
431
432
int script_export_mode = int(current->get_script_export_mode());
433
script_mode->select(script_export_mode);
434
435
updating = false;
436
}
437
438
void ProjectExportDialog::_update_feature_list() {
439
Ref<EditorExportPreset> current = get_current_preset();
440
ERR_FAIL_COND(current.is_null());
441
442
List<String> features_list;
443
444
current->get_platform()->get_platform_features(&features_list);
445
current->get_platform()->get_preset_features(current, &features_list);
446
447
String custom = current->get_custom_features();
448
Vector<String> custom_list = custom.split(",");
449
for (int i = 0; i < custom_list.size(); i++) {
450
String f = custom_list[i].strip_edges();
451
if (!f.is_empty()) {
452
features_list.push_back(f);
453
}
454
}
455
456
feature_set.clear();
457
for (const String &E : features_list) {
458
feature_set.insert(E);
459
}
460
461
#ifdef REAL_T_IS_DOUBLE
462
feature_set.insert("double");
463
#else
464
feature_set.insert("single");
465
#endif // REAL_T_IS_DOUBLE
466
467
custom_feature_display->clear();
468
String text;
469
bool first = true;
470
for (const String &E : feature_set) {
471
if (!first) {
472
text += ", ";
473
} else {
474
first = false;
475
}
476
text += E;
477
}
478
custom_feature_display->add_text(text);
479
}
480
481
void ProjectExportDialog::_custom_features_changed(const String &p_text) {
482
if (updating) {
483
return;
484
}
485
486
Ref<EditorExportPreset> current = get_current_preset();
487
ERR_FAIL_COND(current.is_null());
488
489
current->set_custom_features(p_text);
490
_update_feature_list();
491
}
492
493
void ProjectExportDialog::_tab_changed(int) {
494
_update_feature_list();
495
}
496
497
void ProjectExportDialog::_update_parameters(const String &p_edited_property) {
498
_update_current_preset();
499
}
500
501
void ProjectExportDialog::_advanced_options_pressed() {
502
if (updating) {
503
return;
504
}
505
EditorSettings::get_singleton()->set_setting("_export_preset_advanced_mode", advanced_options->is_pressed());
506
EditorSettings::get_singleton()->save();
507
508
Ref<EditorExportPreset> current = get_current_preset();
509
if (current.is_valid()) {
510
current->notify_property_list_changed();
511
}
512
_update_presets();
513
}
514
515
void ProjectExportDialog::_runnable_pressed() {
516
if (updating) {
517
return;
518
}
519
520
Ref<EditorExportPreset> current = get_current_preset();
521
ERR_FAIL_COND(current.is_null());
522
523
if (runnable->is_pressed()) {
524
EditorExport::get_singleton()->set_runnable_preset(current);
525
} else {
526
EditorExport::get_singleton()->unset_runnable_preset(current);
527
}
528
529
_update_presets();
530
}
531
532
void ProjectExportDialog::_name_changed(const String &p_string) {
533
if (updating) {
534
return;
535
}
536
537
Ref<EditorExportPreset> current = get_current_preset();
538
ERR_FAIL_COND(current.is_null());
539
540
int current_index = presets->get_current();
541
542
String trimmed_name = p_string.strip_edges();
543
if (trimmed_name.is_empty()) {
544
ERR_PRINT_ED("Invalid preset name: preset name cannot be empty!");
545
name->set_text(current->get_name());
546
return;
547
}
548
549
if (EditorExport::get_singleton()->has_preset_with_name(trimmed_name, current_index)) {
550
ERR_PRINT_ED(vformat("Invalid preset name: a preset with the name '%s' already exists!", trimmed_name));
551
name->set_text(current->get_name());
552
return;
553
}
554
555
current->set_name(trimmed_name);
556
_update_presets();
557
}
558
559
void ProjectExportDialog::_name_editing_finished() {
560
if (updating) {
561
return;
562
}
563
564
_name_changed(name->get_text());
565
}
566
567
void ProjectExportDialog::set_export_path(const String &p_value) {
568
Ref<EditorExportPreset> current = get_current_preset();
569
ERR_FAIL_COND(current.is_null());
570
571
current->set_export_path(p_value);
572
}
573
574
String ProjectExportDialog::get_export_path() {
575
Ref<EditorExportPreset> current = get_current_preset();
576
ERR_FAIL_COND_V(current.is_null(), String(""));
577
578
return current->get_export_path();
579
}
580
581
Ref<EditorExportPreset> ProjectExportDialog::get_current_preset() const {
582
return EditorExport::get_singleton()->get_export_preset(presets->get_current());
583
}
584
585
void ProjectExportDialog::_export_path_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
586
if (updating) {
587
return;
588
}
589
590
Ref<EditorExportPreset> current = get_current_preset();
591
ERR_FAIL_COND(current.is_null());
592
593
current->set_export_path(p_value);
594
_update_presets();
595
_update_export_all();
596
}
597
598
void ProjectExportDialog::_enc_filters_changed(const String &p_filters) {
599
if (updating) {
600
return;
601
}
602
603
Ref<EditorExportPreset> current = get_current_preset();
604
ERR_FAIL_COND(current.is_null());
605
606
current->set_enc_in_filter(enc_in_filters->get_text());
607
current->set_enc_ex_filter(enc_ex_filters->get_text());
608
609
updating_enc_filters = true;
610
_update_current_preset();
611
updating_enc_filters = false;
612
}
613
614
void ProjectExportDialog::_open_key_help_link() {
615
OS::get_singleton()->shell_open(vformat("%s/engine_details/development/compiling/compiling_with_script_encryption_key.html", GODOT_VERSION_DOCS_URL));
616
}
617
618
void ProjectExportDialog::_enc_pck_changed(bool p_pressed) {
619
if (updating) {
620
return;
621
}
622
623
Ref<EditorExportPreset> current = get_current_preset();
624
ERR_FAIL_COND(current.is_null());
625
626
current->set_enc_pck(p_pressed);
627
enc_directory->set_disabled(!p_pressed);
628
enc_in_filters->set_editable(p_pressed);
629
enc_ex_filters->set_editable(p_pressed);
630
script_key->set_editable(p_pressed);
631
show_script_key->set_disabled(!p_pressed);
632
if (!p_pressed) {
633
show_script_key->set_pressed(false);
634
}
635
636
_update_current_preset();
637
}
638
639
void ProjectExportDialog::_seed_input_changed(const String &p_text) {
640
if (updating) {
641
return;
642
}
643
644
Ref<EditorExportPreset> current = get_current_preset();
645
ERR_FAIL_COND(current.is_null());
646
647
current->set_seed(seed_input->get_text().to_int());
648
649
updating_seed = true;
650
_update_current_preset();
651
updating_seed = false;
652
}
653
654
void ProjectExportDialog::_enc_directory_changed(bool p_pressed) {
655
if (updating) {
656
return;
657
}
658
659
Ref<EditorExportPreset> current = get_current_preset();
660
ERR_FAIL_COND(current.is_null());
661
662
current->set_enc_directory(p_pressed);
663
664
_update_current_preset();
665
}
666
667
void ProjectExportDialog::_script_encryption_key_changed(const String &p_key) {
668
if (updating) {
669
return;
670
}
671
672
Ref<EditorExportPreset> current = get_current_preset();
673
ERR_FAIL_COND(current.is_null());
674
675
current->set_script_encryption_key(p_key);
676
677
updating_script_key = true;
678
_update_current_preset();
679
updating_script_key = false;
680
}
681
682
void ProjectExportDialog::_script_encryption_key_visibility_changed(bool p_visible) {
683
show_script_key->set_button_icon(get_editor_theme_icon(p_visible ? SNAME("GuiVisibilityVisible") : SNAME("GuiVisibilityHidden")));
684
show_script_key->set_tooltip_text(p_visible ? TTRC("Hide encryption key") : TTRC("Show encryption key"));
685
script_key->set_secret(!p_visible);
686
}
687
688
bool ProjectExportDialog::_validate_script_encryption_key(const String &p_key) {
689
bool is_valid = false;
690
691
if (!p_key.is_empty() && p_key.is_valid_hex_number(false) && p_key.length() == 64) {
692
is_valid = true;
693
}
694
return is_valid;
695
}
696
697
void ProjectExportDialog::_script_export_mode_changed(EditorExportPreset::ScriptExportMode p_mode) {
698
if (updating) {
699
return;
700
}
701
702
Ref<EditorExportPreset> current = get_current_preset();
703
ERR_FAIL_COND(current.is_null());
704
705
current->set_script_export_mode(p_mode);
706
707
_update_current_preset();
708
}
709
710
void ProjectExportDialog::_duplicate_preset() {
711
Ref<EditorExportPreset> current = get_current_preset();
712
if (current.is_null()) {
713
return;
714
}
715
716
Ref<EditorExportPreset> preset = current->get_platform()->create_preset();
717
ERR_FAIL_COND(preset.is_null());
718
719
String preset_name = current->get_name() + " (copy)";
720
while (true) {
721
bool valid = true;
722
723
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
724
Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
725
if (p->get_name() == preset_name) {
726
valid = false;
727
break;
728
}
729
}
730
731
if (valid) {
732
break;
733
}
734
735
preset_name += " (copy)";
736
}
737
738
preset->set_name(preset_name);
739
if (EditorExport::get_singleton()->get_runnable_preset_for_platform(preset->get_platform()).is_null()) {
740
EditorExport::get_singleton()->set_runnable_preset(preset);
741
}
742
preset->set_dedicated_server(current->is_dedicated_server());
743
preset->set_export_filter(current->get_export_filter());
744
preset->set_include_filter(current->get_include_filter());
745
preset->set_exclude_filter(current->get_exclude_filter());
746
preset->set_customized_files(current->get_customized_files());
747
preset->set_selected_files(current->get_selected_files());
748
preset->set_patches(current->get_patches());
749
preset->set_patch_delta_encoding_enabled(current->is_patch_delta_encoding_enabled());
750
preset->set_patch_delta_zstd_level(current->get_patch_delta_zstd_level());
751
preset->set_patch_delta_min_reduction(current->get_patch_delta_min_reduction());
752
preset->set_patch_delta_include_filter(current->get_patch_delta_include_filter());
753
preset->set_patch_delta_exclude_filter(current->get_patch_delta_exclude_filter());
754
preset->set_custom_features(current->get_custom_features());
755
preset->set_enc_in_filter(current->get_enc_in_filter());
756
preset->set_enc_ex_filter(current->get_enc_ex_filter());
757
preset->set_enc_pck(current->get_enc_pck());
758
preset->set_enc_directory(current->get_enc_directory());
759
preset->set_script_encryption_key(current->get_script_encryption_key());
760
preset->set_script_export_mode(current->get_script_export_mode());
761
762
for (const KeyValue<StringName, Variant> &E : current->get_values()) {
763
preset->set(E.key, E.value);
764
}
765
766
EditorExport::get_singleton()->add_export_preset(preset);
767
_update_presets();
768
_edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
769
}
770
771
void ProjectExportDialog::_delete_preset() {
772
Ref<EditorExportPreset> current = get_current_preset();
773
if (current.is_null()) {
774
return;
775
}
776
777
delete_confirm->set_text(vformat(TTR("Delete preset '%s'?"), current->get_name()));
778
delete_confirm->popup_centered();
779
}
780
781
void ProjectExportDialog::_delete_preset_confirm() {
782
int idx = presets->get_current();
783
_edit_preset(idx - 1);
784
export_button->set_disabled(true);
785
get_ok_button()->set_disabled(true);
786
EditorExport::get_singleton()->remove_export_preset(idx);
787
_update_presets();
788
789
// The Export All button might become enabled (if all other presets have an export path defined),
790
// or it could be disabled (if there are no presets anymore).
791
_update_export_all();
792
}
793
794
Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
795
if (p_from == presets) {
796
int pos = -1;
797
if (p_point == Vector2(Math::INF, Math::INF)) {
798
if (presets->is_anything_selected()) {
799
pos = presets->get_selected_items()[0];
800
}
801
} else {
802
pos = presets->get_item_at_position(p_point, true);
803
}
804
805
if (pos >= 0) {
806
Dictionary d;
807
d["type"] = "export_preset";
808
d["preset"] = pos;
809
810
HBoxContainer *drag = memnew(HBoxContainer);
811
TextureRect *tr = memnew(TextureRect);
812
tr->set_texture(presets->get_item_icon(pos));
813
drag->add_child(tr);
814
Label *label = memnew(Label);
815
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
816
label->set_text(presets->get_item_text(pos));
817
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Don't translate user input.
818
drag->add_child(label);
819
820
presets->set_drag_preview(drag);
821
822
return d;
823
}
824
} else if (p_from == patches) {
825
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? patches->get_selected() : patches->get_item_at_position(p_point);
826
827
if (item) {
828
int item_metadata = item->get_metadata(0);
829
Dictionary d;
830
d["type"] = "export_patch";
831
d["patch"] = item_metadata;
832
833
Label *label = memnew(Label);
834
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
835
label->set_text(item->get_text(0));
836
patches->set_drag_preview(label);
837
838
return d;
839
}
840
}
841
return Variant();
842
}
843
844
bool ProjectExportDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
845
if (p_from == presets) {
846
Dictionary d = p_data;
847
if (!d.has("type") || String(d["type"]) != "export_preset") {
848
return false;
849
}
850
851
int pos = -1;
852
bool end = true;
853
if (p_point == Vector2(Math::INF, Math::INF)) {
854
if (presets->is_anything_selected()) {
855
pos = presets->get_selected_items()[0];
856
}
857
} else {
858
pos = presets->get_item_at_position(p_point, true);
859
end = presets->is_pos_at_end_of_items(p_point);
860
}
861
862
if (pos < 0 && !end) {
863
return false;
864
}
865
} else if (p_from == patches) {
866
Dictionary d = p_data;
867
if (d.get("type", "") != "export_patch") {
868
return false;
869
}
870
871
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? patches->get_selected() : patches->get_item_at_position(p_point);
872
if (!item) {
873
return false;
874
}
875
876
patches->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN);
877
}
878
879
return true;
880
}
881
882
void ProjectExportDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
883
if (p_from == presets) {
884
Dictionary d = p_data;
885
int from_pos = d["preset"];
886
887
int to_pos = -1;
888
889
int pos = -1;
890
bool end = true;
891
if (p_point == Vector2(Math::INF, Math::INF)) {
892
if (presets->is_anything_selected()) {
893
pos = presets->get_selected_items()[0];
894
}
895
} else {
896
pos = presets->get_item_at_position(p_point, true);
897
end = presets->is_pos_at_end_of_items(p_point);
898
}
899
900
if (pos >= 0) {
901
to_pos = pos;
902
}
903
904
if (to_pos == -1 && !end) {
905
return;
906
}
907
908
if (to_pos == from_pos) {
909
return;
910
} else if (to_pos > from_pos) {
911
to_pos--;
912
}
913
914
Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(from_pos);
915
EditorExport::get_singleton()->remove_export_preset(from_pos);
916
EditorExport::get_singleton()->add_export_preset(preset, to_pos);
917
918
_update_presets();
919
if (to_pos >= 0) {
920
_edit_preset(to_pos);
921
} else {
922
_edit_preset(presets->get_item_count() - 1);
923
}
924
} else if (p_from == patches) {
925
Dictionary d = p_data;
926
int from_pos = d["patch"];
927
928
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? patches->get_selected() : patches->get_item_at_position(p_point);
929
if (!item) {
930
return;
931
}
932
933
int to_pos = item->get_metadata(0);
934
935
int pos = -1;
936
if (p_point == Vector2(Math::INF, Math::INF)) {
937
pos = patches->get_drop_section_at_position(patches->get_item_rect(item).position);
938
} else {
939
pos = patches->get_drop_section_at_position(p_point);
940
}
941
if (pos > 0) {
942
to_pos++;
943
}
944
945
if (to_pos > from_pos) {
946
to_pos--;
947
}
948
949
Ref<EditorExportPreset> preset = get_current_preset();
950
String patch = preset->get_patch(from_pos);
951
preset->remove_patch(from_pos);
952
preset->add_patch(patch, to_pos);
953
954
_update_current_preset();
955
}
956
}
957
958
void ProjectExportDialog::_export_type_changed(int p_which) {
959
if (updating) {
960
return;
961
}
962
963
Ref<EditorExportPreset> current = get_current_preset();
964
if (current.is_null()) {
965
return;
966
}
967
968
EditorExportPreset::ExportFilter filter_type = (EditorExportPreset::ExportFilter)p_which;
969
current->set_export_filter(filter_type);
970
current->set_dedicated_server(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED);
971
server_strip_message->set_visible(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED);
972
973
// Default to stripping everything when first switching to server build.
974
if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) {
975
current->set_file_export_mode("res://", EditorExportPreset::MODE_FILE_STRIP);
976
}
977
include_label->set_text(_get_resource_export_header(current->get_export_filter()));
978
979
updating = true;
980
_fill_resource_tree();
981
updating = false;
982
}
983
984
String ProjectExportDialog::_get_resource_export_header(EditorExportPreset::ExportFilter p_filter) const {
985
switch (p_filter) {
986
case EditorExportPreset::EXCLUDE_SELECTED_RESOURCES:
987
return TTR("Resources to exclude:");
988
case EditorExportPreset::EXPORT_CUSTOMIZED:
989
return TTR("Resources to override export behavior:");
990
default:
991
return TTR("Resources to export:");
992
}
993
}
994
995
void ProjectExportDialog::_filter_changed(const String &p_filter) {
996
if (updating) {
997
return;
998
}
999
1000
Ref<EditorExportPreset> current = get_current_preset();
1001
if (current.is_null()) {
1002
return;
1003
}
1004
1005
current->set_include_filter(include_filters->get_text());
1006
current->set_exclude_filter(exclude_filters->get_text());
1007
}
1008
1009
void ProjectExportDialog::_fill_resource_tree() {
1010
include_files->clear();
1011
include_label->hide();
1012
include_margin->hide();
1013
1014
Ref<EditorExportPreset> current = get_current_preset();
1015
if (current.is_null()) {
1016
return;
1017
}
1018
1019
EditorExportPreset::ExportFilter f = current->get_export_filter();
1020
1021
if (f == EditorExportPreset::EXPORT_ALL_RESOURCES) {
1022
return;
1023
}
1024
1025
TreeItem *root = include_files->create_item();
1026
1027
if (f == EditorExportPreset::EXPORT_CUSTOMIZED) {
1028
include_files->set_columns(2);
1029
include_files->set_column_expand(1, false);
1030
include_files->set_column_custom_minimum_width(1, 250 * EDSCALE);
1031
} else {
1032
include_files->set_columns(1);
1033
}
1034
1035
include_label->show();
1036
include_margin->show();
1037
1038
_fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f);
1039
1040
if (f == EditorExportPreset::EXPORT_CUSTOMIZED) {
1041
_propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
1042
}
1043
}
1044
1045
void ProjectExportDialog::_setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode) {
1046
if (p_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
1047
p_item->set_checked(0, false);
1048
p_item->set_cell_mode(1, TreeItem::CELL_MODE_STRING);
1049
p_item->set_editable(1, false);
1050
p_item->set_selectable(1, false);
1051
p_item->set_custom_color(1, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
1052
} else {
1053
p_item->set_checked(0, true);
1054
p_item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM);
1055
p_item->set_editable(1, true);
1056
p_item->set_selectable(1, true);
1057
p_item->clear_custom_color(1);
1058
}
1059
p_item->set_metadata(1, p_mode);
1060
}
1061
1062
bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, EditorExportPreset::ExportFilter p_export_filter) {
1063
p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
1064
p_item->set_icon(0, presets->get_theme_icon(SNAME("folder"), SNAME("FileDialog")));
1065
p_item->set_text(0, p_dir->get_name() + "/");
1066
p_item->set_editable(0, true);
1067
p_item->set_metadata(0, p_dir->get_path());
1068
1069
if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) {
1070
_setup_item_for_file_mode(p_item, current->get_file_export_mode(p_dir->get_path()));
1071
}
1072
1073
bool used = false;
1074
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
1075
TreeItem *subdir = include_files->create_item(p_item);
1076
if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_export_filter)) {
1077
used = true;
1078
} else {
1079
memdelete(subdir);
1080
}
1081
}
1082
1083
for (int i = 0; i < p_dir->get_file_count(); i++) {
1084
String type = p_dir->get_file_type(i);
1085
if (p_export_filter == EditorExportPreset::EXPORT_SELECTED_SCENES && type != "PackedScene") {
1086
continue;
1087
}
1088
if (type == "TextFile" || type == "OtherFile") {
1089
continue;
1090
}
1091
1092
TreeItem *file = include_files->create_item(p_item);
1093
file->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
1094
file->set_text(0, p_dir->get_file(i));
1095
1096
String path = p_dir->get_file_path(i);
1097
1098
Ref<Texture2D> icon;
1099
if (!type.is_empty()) {
1100
icon = EditorNode::get_singleton()->get_class_icon(type);
1101
}
1102
if (icon.is_null()) {
1103
icon = get_editor_theme_icon(SNAME("File"));
1104
}
1105
file->set_icon(0, icon);
1106
file->set_editable(0, true);
1107
file->set_metadata(0, path);
1108
1109
if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) {
1110
_setup_item_for_file_mode(file, current->get_file_export_mode(path));
1111
} else {
1112
file->set_checked(0, current->has_export_file(path));
1113
file->propagate_check(0);
1114
}
1115
1116
used = true;
1117
}
1118
return used;
1119
}
1120
1121
void ProjectExportDialog::_propagate_file_export_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_inherited_export_mode) {
1122
EditorExportPreset::FileExportMode file_export_mode = (EditorExportPreset::FileExportMode)(int)p_item->get_metadata(1);
1123
bool is_inherited = false;
1124
if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
1125
file_export_mode = p_inherited_export_mode;
1126
is_inherited = true;
1127
}
1128
1129
if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
1130
p_item->set_text(1, "");
1131
} else {
1132
String text = file_mode_popup->get_item_text(file_mode_popup->get_item_index(file_export_mode));
1133
if (is_inherited) {
1134
text += " " + TTR("(Inherited)");
1135
}
1136
p_item->set_text(1, text);
1137
}
1138
1139
for (int i = 0; i < p_item->get_child_count(); i++) {
1140
_propagate_file_export_mode(p_item->get_child(i), file_export_mode);
1141
}
1142
}
1143
1144
void ProjectExportDialog::_tree_changed() {
1145
if (updating) {
1146
return;
1147
}
1148
1149
Ref<EditorExportPreset> current = get_current_preset();
1150
if (current.is_null()) {
1151
return;
1152
}
1153
1154
TreeItem *item = include_files->get_edited();
1155
if (!item) {
1156
return;
1157
}
1158
1159
if (current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED) {
1160
EditorExportPreset::FileExportMode file_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
1161
String path = item->get_metadata(0);
1162
1163
if (item->is_checked(0)) {
1164
file_mode = current->get_file_export_mode(path, EditorExportPreset::MODE_FILE_STRIP);
1165
}
1166
1167
current->set_file_export_mode(path, file_mode);
1168
_setup_item_for_file_mode(item, file_mode);
1169
_propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
1170
} else {
1171
item->propagate_check(0);
1172
}
1173
}
1174
1175
void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) {
1176
Ref<EditorExportPreset> current = get_current_preset();
1177
if (current.is_null()) {
1178
return;
1179
}
1180
TreeItem *item = Object::cast_to<TreeItem>(p_obj);
1181
String path = item->get_metadata(0);
1182
if (item && !path.ends_with("/")) {
1183
bool added = item->is_checked(0);
1184
if (added) {
1185
current->add_export_file(path);
1186
} else {
1187
current->remove_export_file(path);
1188
}
1189
}
1190
}
1191
1192
void ProjectExportDialog::_tree_popup_edited(bool p_arrow_clicked) {
1193
Rect2 bounds = include_files->get_custom_popup_rect();
1194
bounds.position += get_global_canvas_transform().get_origin();
1195
bounds.size *= get_global_canvas_transform().get_scale();
1196
if (!is_embedding_subwindows()) {
1197
bounds.position += get_position();
1198
}
1199
file_mode_popup->popup(bounds);
1200
}
1201
1202
void ProjectExportDialog::_set_file_export_mode(int p_id) {
1203
Ref<EditorExportPreset> current = get_current_preset();
1204
if (current.is_null()) {
1205
return;
1206
}
1207
1208
TreeItem *item = include_files->get_edited();
1209
String path = item->get_metadata(0);
1210
1211
EditorExportPreset::FileExportMode file_export_mode = (EditorExportPreset::FileExportMode)p_id;
1212
current->set_file_export_mode(path, file_export_mode);
1213
item->set_metadata(1, file_export_mode);
1214
_propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
1215
}
1216
1217
void ProjectExportDialog::_patch_delta_encoding_changed(bool p_pressed) {
1218
if (updating) {
1219
return;
1220
}
1221
1222
Ref<EditorExportPreset> current = get_current_preset();
1223
ERR_FAIL_COND(current.is_null());
1224
1225
current->set_patch_delta_encoding_enabled(p_pressed);
1226
1227
_update_current_preset();
1228
}
1229
1230
void ProjectExportDialog::_patch_delta_include_filter_changed(const String &p_filter) {
1231
if (updating) {
1232
return;
1233
}
1234
1235
Ref<EditorExportPreset> current = get_current_preset();
1236
ERR_FAIL_COND(current.is_null());
1237
1238
current->set_patch_delta_include_filter(patch_delta_include_filter->get_text());
1239
1240
updating_patch_delta_filters = true;
1241
_update_current_preset();
1242
updating_patch_delta_filters = false;
1243
}
1244
1245
void ProjectExportDialog::_patch_delta_exclude_filter_changed(const String &p_filter) {
1246
if (updating) {
1247
return;
1248
}
1249
1250
Ref<EditorExportPreset> current = get_current_preset();
1251
ERR_FAIL_COND(current.is_null());
1252
1253
current->set_patch_delta_exclude_filter(patch_delta_exclude_filter->get_text());
1254
1255
updating_patch_delta_filters = true;
1256
_update_current_preset();
1257
updating_patch_delta_filters = false;
1258
}
1259
1260
void ProjectExportDialog::_patch_delta_zstd_level_changed(double p_value) {
1261
if (updating) {
1262
return;
1263
}
1264
1265
Ref<EditorExportPreset> current = get_current_preset();
1266
ERR_FAIL_COND(current.is_null());
1267
1268
current->set_patch_delta_zstd_level((int)p_value);
1269
1270
_update_current_preset();
1271
}
1272
1273
void ProjectExportDialog::_patch_delta_min_reduction_changed(double p_value) {
1274
if (updating) {
1275
return;
1276
}
1277
1278
Ref<EditorExportPreset> current = get_current_preset();
1279
ERR_FAIL_COND(current.is_null());
1280
1281
current->set_patch_delta_min_reduction(p_value / 100.0);
1282
1283
_update_current_preset();
1284
}
1285
1286
void ProjectExportDialog::_patch_tree_button_clicked(Object *p_item, int p_column, int p_id, int p_mouse_button_index) {
1287
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
1288
1289
patch_index = ti->get_metadata(0);
1290
1291
Ref<EditorExportPreset> current = get_current_preset();
1292
ERR_FAIL_COND(current.is_null());
1293
1294
if (p_id == 0) {
1295
Vector<String> preset_patches = current->get_patches();
1296
ERR_FAIL_INDEX(patch_index, preset_patches.size());
1297
patch_erase->set_text(vformat(TTR("Delete patch '%s' from list?"), preset_patches[patch_index].get_file()));
1298
patch_erase->popup_centered();
1299
} else {
1300
patch_dialog->popup_file_dialog();
1301
}
1302
}
1303
1304
void ProjectExportDialog::_patch_tree_item_edited() {
1305
TreeItem *item = patches->get_edited();
1306
if (!item) {
1307
return;
1308
}
1309
1310
Ref<EditorExportPreset> current = get_current_preset();
1311
ERR_FAIL_COND(current.is_null());
1312
1313
int index = item->get_metadata(0);
1314
String patch_path = item->get_text(0);
1315
1316
current->set_patch(index, patch_path);
1317
item->set_tooltip_text(0, patch_path);
1318
}
1319
1320
void ProjectExportDialog::_patch_file_selected(const String &p_path) {
1321
Ref<EditorExportPreset> current = get_current_preset();
1322
ERR_FAIL_COND(current.is_null());
1323
1324
String relative_path = ProjectSettings::get_singleton()->get_resource_path().path_to_file(p_path);
1325
1326
Vector<String> preset_patches = current->get_patches();
1327
if (patch_index >= preset_patches.size()) {
1328
current->add_patch(relative_path);
1329
} else {
1330
current->set_patch(patch_index, relative_path);
1331
}
1332
1333
_update_current_preset();
1334
}
1335
1336
void ProjectExportDialog::_patch_delete_confirmed() {
1337
Ref<EditorExportPreset> current = get_current_preset();
1338
ERR_FAIL_COND(current.is_null());
1339
1340
Vector<String> preset_patches = current->get_patches();
1341
if (patch_index < preset_patches.size()) {
1342
current->remove_patch(patch_index);
1343
_update_current_preset();
1344
}
1345
}
1346
1347
void ProjectExportDialog::_patch_add_pack_pressed() {
1348
Ref<EditorExportPreset> current = get_current_preset();
1349
ERR_FAIL_COND(current.is_null());
1350
1351
patch_index = current->get_patches().size();
1352
patch_dialog->popup_file_dialog();
1353
}
1354
1355
void ProjectExportDialog::_export_pck_zip() {
1356
Ref<EditorExportPreset> current = get_current_preset();
1357
ERR_FAIL_COND(current.is_null());
1358
1359
String dir = current->get_export_path().get_base_dir();
1360
export_pck_zip->set_current_dir(dir);
1361
1362
export_pck_zip->popup_file_dialog();
1363
}
1364
1365
void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
1366
Ref<EditorExportPreset> current = get_current_preset();
1367
ERR_FAIL_COND(current.is_null());
1368
Ref<EditorExportPlatform> platform = current->get_platform();
1369
ERR_FAIL_COND(platform.is_null());
1370
1371
const Dictionary &fd_option = export_pck_zip->get_selected_options();
1372
bool export_debug = fd_option.get(TTR("Export With Debug"), true);
1373
bool export_as_patch = fd_option.get(TTR("Export As Patch"), true);
1374
1375
EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug);
1376
EditorSettings::get_singleton()->set_project_metadata("export_options", "export_as_patch", export_as_patch);
1377
1378
if (p_path.ends_with(".zip")) {
1379
if (export_as_patch) {
1380
platform->export_zip_patch(current, export_debug, p_path);
1381
} else {
1382
platform->export_zip(current, export_debug, p_path);
1383
}
1384
} else if (p_path.ends_with(".pck")) {
1385
if (export_as_patch) {
1386
platform->export_pack_patch(current, export_debug, p_path);
1387
} else {
1388
platform->export_pack(current, export_debug, p_path);
1389
}
1390
} else {
1391
ERR_FAIL_MSG("Path must end with .pck or .zip");
1392
}
1393
}
1394
1395
void ProjectExportDialog::_open_export_template_manager() {
1396
hide();
1397
EditorNode::get_singleton()->open_export_template_manager();
1398
}
1399
1400
void ProjectExportDialog::_export_project() {
1401
Ref<EditorExportPreset> current = get_current_preset();
1402
ERR_FAIL_COND(current.is_null());
1403
Ref<EditorExportPlatform> platform = current->get_platform();
1404
ERR_FAIL_COND(platform.is_null());
1405
1406
export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1407
export_project->clear_filters();
1408
1409
List<String> extension_list = platform->get_binary_extensions(current);
1410
for (const String &extension : extension_list) {
1411
// TRANSLATORS: This is the name of a project export file format. %s will be replaced by the platform name.
1412
export_project->add_filter("*." + extension, vformat(TTR("%s Export"), platform->get_name()));
1413
}
1414
1415
String path = current->get_export_path();
1416
if (!path.is_empty()) {
1417
if (extension_list.find(path.get_extension()) == nullptr && extension_list.size() >= 1) {
1418
path = path.get_basename() + "." + extension_list.front()->get();
1419
}
1420
export_project->set_current_path(path);
1421
} else {
1422
if (extension_list.size() >= 1) {
1423
export_project->set_current_file(default_filename + "." + extension_list.front()->get());
1424
} else {
1425
export_project->set_current_file(default_filename);
1426
}
1427
}
1428
export_project->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
1429
export_project->popup_file_dialog();
1430
}
1431
1432
void ProjectExportDialog::_export_project_to_path(const String &p_path) {
1433
// Save this name for use in future exports (but drop the file extension)
1434
default_filename = p_path.get_file().get_basename();
1435
EditorSettings::get_singleton()->set_project_metadata("export_options", "default_filename", default_filename);
1436
1437
Ref<EditorExportPreset> current = get_current_preset();
1438
ERR_FAIL_COND_MSG(current.is_null(), "Failed to start the export: current preset is invalid.");
1439
Ref<EditorExportPlatform> platform = current->get_platform();
1440
ERR_FAIL_COND_MSG(platform.is_null(), "Failed to start the export: current preset has no valid platform.");
1441
current->set_export_path(p_path);
1442
1443
exporting = true;
1444
1445
platform->clear_messages();
1446
current->update_value_overrides();
1447
Dictionary fd_option = export_project->get_selected_options();
1448
bool export_debug = fd_option.get(TTR("Export With Debug"), true);
1449
1450
EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug);
1451
1452
Error err = platform->export_project(current, export_debug, current->get_export_path(), 0);
1453
result_dialog_log->clear();
1454
if (err != ERR_SKIP) {
1455
if (platform->fill_log_messages(result_dialog_log, err)) {
1456
result_dialog->popup_centered_ratio(0.5);
1457
}
1458
}
1459
1460
exporting = false;
1461
}
1462
1463
void ProjectExportDialog::_export_all_dialog() {
1464
export_all_dialog->show();
1465
export_all_dialog->popup_centered(Size2(300, 80));
1466
}
1467
1468
void ProjectExportDialog::_export_all_dialog_action(const String &p_str) {
1469
export_all_dialog->hide();
1470
1471
_export_all(p_str != "release");
1472
}
1473
1474
void ProjectExportDialog::_export_all(bool p_debug) {
1475
exporting = true;
1476
bool show_dialog = false;
1477
1478
{ // Scope for the editor progress, we must free it before showing the dialog at the end.
1479
String export_target = p_debug ? TTR("Debug") : TTR("Release");
1480
EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true);
1481
1482
result_dialog_log->clear();
1483
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
1484
Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
1485
if (preset.is_null()) {
1486
exporting = false;
1487
ERR_FAIL_MSG("Failed to start the export: one of the presets is invalid.");
1488
}
1489
1490
Ref<EditorExportPlatform> platform = preset->get_platform();
1491
if (platform.is_null()) {
1492
exporting = false;
1493
ERR_FAIL_MSG("Failed to start the export: one of the presets has no valid platform.");
1494
}
1495
1496
ep.step(preset->get_name(), i);
1497
1498
platform->clear_messages();
1499
preset->update_value_overrides();
1500
Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
1501
if (err == ERR_SKIP) {
1502
exporting = false;
1503
return;
1504
}
1505
bool has_messages = platform->fill_log_messages(result_dialog_log, err);
1506
show_dialog = show_dialog || has_messages;
1507
}
1508
}
1509
1510
if (show_dialog) {
1511
result_dialog->popup_centered_ratio(0.5);
1512
}
1513
1514
exporting = false;
1515
}
1516
1517
void ProjectExportDialog::_bind_methods() {
1518
ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path);
1519
ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
1520
ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);
1521
1522
ADD_PROPERTY(PropertyInfo(Variant::STRING, "export_path"), "set_export_path", "get_export_path");
1523
}
1524
1525
ProjectExportDialog::ProjectExportDialog() {
1526
set_title(TTR("Export"));
1527
set_flag(FLAG_MAXIMIZE_DISABLED, false);
1528
set_clamp_to_embedder(true);
1529
1530
VBoxContainer *main_vb = memnew(VBoxContainer);
1531
add_child(main_vb);
1532
1533
HSplitContainer *hbox = memnew(HSplitContainer);
1534
main_vb->add_child(hbox);
1535
hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1536
if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {
1537
hbox->set_touch_dragger_enabled(true);
1538
}
1539
1540
// Presets list.
1541
1542
VBoxContainer *preset_vb = memnew(VBoxContainer);
1543
preset_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1544
preset_vb->set_stretch_ratio(0.35);
1545
hbox->add_child(preset_vb);
1546
1547
Label *l = memnew(Label(TTR("Presets")));
1548
l->set_theme_type_variation("HeaderSmall");
1549
1550
HBoxContainer *preset_hb = memnew(HBoxContainer);
1551
preset_hb->add_child(l);
1552
preset_hb->add_spacer();
1553
preset_vb->add_child(preset_hb);
1554
1555
add_preset = memnew(MenuButton);
1556
add_preset->set_text(TTR("Add..."));
1557
add_preset->get_popup()->connect("index_pressed", callable_mp(this, &ProjectExportDialog::_add_preset));
1558
preset_hb->add_child(add_preset);
1559
MarginContainer *mc = memnew(MarginContainer);
1560
preset_vb->add_child(mc);
1561
mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1562
presets = memnew(ItemList);
1563
presets->set_theme_type_variation("ItemListSecondary");
1564
presets->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1565
presets->set_custom_minimum_size(Size2(0, 200 * EDSCALE));
1566
SET_DRAG_FORWARDING_GCD(presets, ProjectExportDialog);
1567
mc->add_child(presets);
1568
presets->connect(SceneStringName(item_selected), callable_mp(this, &ProjectExportDialog::_edit_preset));
1569
duplicate_preset = memnew(Button);
1570
duplicate_preset->set_tooltip_text(TTR("Duplicate"));
1571
duplicate_preset->set_flat(true);
1572
preset_hb->add_child(duplicate_preset);
1573
duplicate_preset->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_duplicate_preset));
1574
delete_preset = memnew(Button);
1575
delete_preset->set_tooltip_text(TTR("Delete"));
1576
delete_preset->set_flat(true);
1577
preset_hb->add_child(delete_preset);
1578
delete_preset->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_delete_preset));
1579
1580
// Preset settings.
1581
1582
settings_vb = memnew(VBoxContainer);
1583
settings_vb->hide();
1584
settings_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1585
hbox->add_child(settings_vb);
1586
1587
PanelContainer *panel = memnew(PanelContainer);
1588
panel->set_theme_type_variation(SNAME("PanelForeground"));
1589
settings_vb->add_child(panel);
1590
1591
VBoxContainer *top_settings = memnew(VBoxContainer);
1592
top_settings->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1593
panel->add_child(top_settings);
1594
1595
HBoxContainer *name_hbox = memnew(HBoxContainer);
1596
Label *name_label = memnew(Label);
1597
name_label->set_theme_type_variation("HeaderSmall");
1598
name_label->set_text(TTR("Name:"));
1599
name_hbox->add_child(name_label);
1600
name = memnew(LineEdit);
1601
name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1602
name->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectExportDialog::_name_changed));
1603
name->connect(SceneStringName(focus_exited), callable_mp(this, &ProjectExportDialog::_name_editing_finished));
1604
name_hbox->add_child(name);
1605
1606
top_settings->add_child(name_hbox);
1607
1608
runnable = memnew(CheckButton);
1609
runnable->set_text(TTR("Runnable"));
1610
runnable->set_tooltip_text(TTR("If checked, the preset will be available for use in one-click deploy.\nOnly one preset per platform may be marked as runnable."));
1611
runnable->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_runnable_pressed));
1612
1613
advanced_options = memnew(CheckButton);
1614
advanced_options->set_text(TTR("Advanced Options"));
1615
advanced_options->set_tooltip_text(TTR("If checked, the advanced options will be shown."));
1616
advanced_options->set_pressed(EDITOR_GET("_export_preset_advanced_mode"));
1617
advanced_options->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_advanced_options_pressed));
1618
1619
HBoxContainer *preset_configs_container = memnew(HBoxContainer);
1620
preset_configs_container->add_spacer(true);
1621
preset_configs_container->add_child(advanced_options);
1622
preset_configs_container->add_child(runnable);
1623
top_settings->add_child(preset_configs_container);
1624
1625
export_path = memnew(EditorPropertyPath);
1626
top_settings->add_child(export_path);
1627
export_path->set_label(TTR("Export Path"));
1628
export_path->set_object_and_property(this, "export_path");
1629
export_path->set_save_mode();
1630
export_path->connect("property_changed", callable_mp(this, &ProjectExportDialog::_export_path_changed));
1631
1632
// Subsections.
1633
1634
sections = memnew(TabContainer);
1635
sections->set_use_hidden_tabs_for_min_size(true);
1636
sections->set_theme_type_variation("TabContainerOdd");
1637
settings_vb->add_child(sections);
1638
sections->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1639
1640
// Main preset parameters.
1641
1642
parameters = memnew(EditorInspector);
1643
parameters->set_name(TTRC("Options"));
1644
parameters->set_mark_unsaved(false);
1645
parameters->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1646
parameters->set_use_doc_hints(true);
1647
sections->add_child(parameters);
1648
parameters->connect("property_edited", callable_mp(this, &ProjectExportDialog::_update_parameters));
1649
EditorExport::get_singleton()->connect("export_presets_updated", callable_mp(this, &ProjectExportDialog::_force_update_current_preset_parameters));
1650
1651
// Resources export parameters.
1652
1653
ScrollContainer *resources_scroll_container = memnew(ScrollContainer);
1654
resources_scroll_container->set_name(TTR("Resources"));
1655
resources_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
1656
sections->add_child(resources_scroll_container);
1657
1658
VBoxContainer *resources_vb = memnew(VBoxContainer);
1659
resources_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1660
resources_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1661
resources_scroll_container->add_child(resources_vb);
1662
1663
export_filter = memnew(OptionButton);
1664
export_filter->set_accessibility_name(TTRC("Export Mode"));
1665
export_filter->add_item(TTR("Export all resources in the project"));
1666
export_filter->add_item(TTR("Export selected scenes (and dependencies)"));
1667
export_filter->add_item(TTR("Export selected resources (and dependencies)"));
1668
export_filter->add_item(TTR("Export all resources in the project except resources checked below"));
1669
export_filter->add_item(TTR("Export as dedicated server"));
1670
resources_vb->add_margin_child(TTR("Export Mode:"), export_filter);
1671
export_filter->connect(SceneStringName(item_selected), callable_mp(this, &ProjectExportDialog::_export_type_changed));
1672
1673
include_label = memnew(Label);
1674
include_label->set_text(TTR("Resources to export:"));
1675
resources_vb->add_child(include_label);
1676
include_margin = memnew(MarginContainer);
1677
include_margin->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1678
resources_vb->add_child(include_margin);
1679
1680
include_files = memnew(Tree);
1681
include_files->set_custom_minimum_size(Size2(1, 75 * EDSCALE));
1682
include_margin->add_child(include_files);
1683
include_files->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1684
include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed));
1685
include_files->connect("check_propagated_to_item", callable_mp(this, &ProjectExportDialog::_check_propagated_to_item));
1686
include_files->connect("custom_popup_edited", callable_mp(this, &ProjectExportDialog::_tree_popup_edited));
1687
1688
server_strip_message = memnew(Label);
1689
server_strip_message->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1690
server_strip_message->set_visible(false);
1691
server_strip_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
1692
server_strip_message->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
1693
resources_vb->add_child(server_strip_message);
1694
1695
{
1696
LocalVector<StringName> resource_names;
1697
ClassDB::get_inheriters_from_class("Resource", resource_names);
1698
1699
PackedStringArray strippable;
1700
for (const StringName &resource_name : resource_names) {
1701
if (ClassDB::has_method(resource_name, "create_placeholder", true)) {
1702
strippable.push_back(resource_name);
1703
}
1704
}
1705
strippable.sort();
1706
1707
String message = TTR("\"Strip Visuals\" will replace the following resources with placeholders:") + " ";
1708
message += String(", ").join(strippable);
1709
server_strip_message->set_text(message);
1710
}
1711
1712
file_mode_popup = memnew(PopupMenu);
1713
add_child(file_mode_popup);
1714
file_mode_popup->add_item(TTR("Strip Visuals"), EditorExportPreset::MODE_FILE_STRIP);
1715
file_mode_popup->add_item(TTR("Keep"), EditorExportPreset::MODE_FILE_KEEP);
1716
file_mode_popup->add_item(TTR("Remove"), EditorExportPreset::MODE_FILE_REMOVE);
1717
file_mode_popup->connect(SceneStringName(id_pressed), callable_mp(this, &ProjectExportDialog::_set_file_export_mode));
1718
1719
include_filters = memnew(LineEdit);
1720
include_filters->set_accessibility_name(TTRC("Include Filters"));
1721
resources_vb->add_margin_child(
1722
TTR("Filters to export non-resource files/folders\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
1723
include_filters);
1724
include_filters->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_filter_changed));
1725
1726
exclude_filters = memnew(LineEdit);
1727
exclude_filters->set_accessibility_name(TTRC("Exclude Filters"));
1728
resources_vb->add_margin_child(
1729
TTR("Filters to exclude files/folders from project\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
1730
exclude_filters);
1731
exclude_filters->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_filter_changed));
1732
1733
// Patching.
1734
1735
ScrollContainer *patch_scroll_container = memnew(ScrollContainer);
1736
patch_scroll_container->set_name(TTRC("Patching"));
1737
patch_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
1738
sections->add_child(patch_scroll_container);
1739
1740
VBoxContainer *patch_vb = memnew(VBoxContainer);
1741
patch_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1742
patch_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1743
patch_scroll_container->add_child(patch_vb);
1744
1745
patch_delta_encoding = memnew(CheckButton);
1746
patch_delta_encoding->connect(SceneStringName(toggled), callable_mp(this, &ProjectExportDialog::_patch_delta_encoding_changed));
1747
patch_delta_encoding->set_text(TTRC("Enable Delta Encoding"));
1748
patch_delta_encoding->set_tooltip_text(TTRC("If checked, any change to a file already present in the base packs will be exported as the difference between the old file and the new file.\n"
1749
"Enabling this comes at the cost of longer export times as well as longer load times for patched resources."));
1750
patch_vb->add_child(patch_delta_encoding);
1751
1752
patch_delta_zstd_level = memnew(SpinBox);
1753
patch_delta_zstd_level->set_min(ZSTD_minCLevel());
1754
patch_delta_zstd_level->set_max(ZSTD_maxCLevel());
1755
patch_delta_zstd_level->set_step(1);
1756
patch_delta_zstd_level->set_tooltip_text(
1757
vformat(TTR("The Zstandard compression level to use when generating delta-encoded patches.\n"
1758
"Higher positive levels will reduce patch sizes, at the cost of longer export time, but do not affect the time it takes to apply patches.\n"
1759
"Negative levels will reduce the time it takes to apply patches, at the cost of worse compression.\n"
1760
"Levels above 19 require more memory both during export and when applying patches, usually for very little benefit.\n"
1761
"Level 0 will cause Zstandard to use its default compression level, which is currently level %d."),
1762
ZSTD_CLEVEL_DEFAULT));
1763
patch_delta_zstd_level->connect(SceneStringName(value_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_zstd_level_changed));
1764
patch_vb->add_margin_child(TTRC("Delta Encoding Compression Level"), patch_delta_zstd_level);
1765
1766
patch_delta_min_reduction = memnew(SpinBox);
1767
patch_delta_min_reduction->set_min(0.0);
1768
patch_delta_min_reduction->set_max(100.0);
1769
patch_delta_min_reduction->set_step(1.0);
1770
patch_delta_min_reduction->set_suffix("%");
1771
patch_delta_min_reduction->set_tooltip_text(TTRC("How much smaller, when compared to the new file, a delta-encoded patch needs to be for it to be exported.\n"
1772
"If the patch is not at least this much smaller, the new file will be exported as-is."));
1773
patch_delta_min_reduction->connect(SceneStringName(value_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_min_reduction_changed));
1774
patch_vb->add_margin_child(TTRC("Delta Encoding Minimum Size Reduction"), patch_delta_min_reduction);
1775
1776
patch_delta_include_filter = memnew(LineEdit);
1777
patch_delta_include_filter->set_accessibility_name(TTRC("Delta Encoding Include Filters"));
1778
patch_delta_include_filter->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_include_filter_changed));
1779
patch_vb->add_margin_child(TTRC("Filters to include files/folders from being delta-encoded\n(comma-separated, e.g: *.gdc, scripts/*)"), patch_delta_include_filter);
1780
1781
patch_delta_exclude_filter = memnew(LineEdit);
1782
patch_delta_exclude_filter->set_accessibility_name(TTRC("Delta Encoding Exclude Filters"));
1783
patch_delta_exclude_filter->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_patch_delta_exclude_filter_changed));
1784
patch_vb->add_margin_child(TTRC("Filters to exclude files/folders from being delta-encoded\n(comma-separated, e.g: *.ctex, textures/*)"), patch_delta_exclude_filter);
1785
1786
patches = memnew(Tree);
1787
patches->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1788
patches->set_hide_root(true);
1789
patches->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1790
patches->connect("button_clicked", callable_mp(this, &ProjectExportDialog::_patch_tree_button_clicked));
1791
patches->connect("item_edited", callable_mp(this, &ProjectExportDialog::_patch_tree_item_edited));
1792
SET_DRAG_FORWARDING_GCD(patches, ProjectExportDialog);
1793
patches->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
1794
patch_vb->add_margin_child(TTR("Base Packs:"), patches, true);
1795
1796
patch_dialog = memnew(EditorFileDialog);
1797
patch_dialog->add_filter("*.pck", TTR("Godot Project Pack"));
1798
patch_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1799
patch_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
1800
patch_dialog->connect("file_selected", callable_mp(this, &ProjectExportDialog::_patch_file_selected));
1801
add_child(patch_dialog);
1802
1803
patch_erase = memnew(ConfirmationDialog);
1804
patch_erase->set_ok_button_text(TTR("Delete"));
1805
patch_erase->connect(SceneStringName(confirmed), callable_mp(this, &ProjectExportDialog::_patch_delete_confirmed));
1806
add_child(patch_erase);
1807
1808
patch_add_btn = memnew(Button);
1809
patch_add_btn->set_text(TTR("Add Pack"));
1810
patch_add_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
1811
patch_add_btn->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_patch_add_pack_pressed));
1812
patch_vb->add_child(patch_add_btn);
1813
1814
// Feature tags.
1815
1816
VBoxContainer *feature_vb = memnew(VBoxContainer);
1817
feature_vb->set_name(TTR("Features"));
1818
feature_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1819
custom_features = memnew(LineEdit);
1820
custom_features->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_custom_features_changed));
1821
feature_vb->add_margin_child(TTR("Custom (comma-separated):"), custom_features);
1822
custom_feature_display = memnew(RichTextLabel);
1823
custom_feature_display->set_custom_minimum_size(Size2(1, 75 * EDSCALE));
1824
custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1825
feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true);
1826
sections->add_child(feature_vb);
1827
1828
// Encryption export parameters.
1829
1830
ScrollContainer *sec_scroll_container = memnew(ScrollContainer);
1831
sec_scroll_container->set_name(TTR("Encryption"));
1832
sec_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
1833
1834
VBoxContainer *sec_vb = memnew(VBoxContainer);
1835
sec_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1836
sec_scroll_container->add_child(sec_vb);
1837
1838
enc_pck = memnew(CheckButton);
1839
enc_pck->connect(SceneStringName(toggled), callable_mp(this, &ProjectExportDialog::_enc_pck_changed));
1840
enc_pck->set_text(TTR("Encrypt Exported PCK"));
1841
sec_vb->add_child(enc_pck);
1842
1843
enc_directory = memnew(CheckButton);
1844
enc_directory->connect(SceneStringName(toggled), callable_mp(this, &ProjectExportDialog::_enc_directory_changed));
1845
enc_directory->set_text(TTR("Encrypt Index (File Names and Info)"));
1846
sec_vb->add_child(enc_directory);
1847
1848
enc_in_filters = memnew(LineEdit);
1849
enc_in_filters->set_accessibility_name(TTRC("Include Filters"));
1850
enc_in_filters->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_enc_filters_changed));
1851
sec_vb->add_margin_child(
1852
TTR("Filters to include files/folders\n(comma-separated, e.g: *.tscn, *.tres, scenes/*)"),
1853
enc_in_filters);
1854
1855
enc_ex_filters = memnew(LineEdit);
1856
enc_ex_filters->set_accessibility_name(TTRC("Exclude Filters"));
1857
enc_ex_filters->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_enc_filters_changed));
1858
sec_vb->add_margin_child(
1859
TTR("Filters to exclude files/folders\n(comma-separated, e.g: *.ctex, *.import, music/*)"),
1860
enc_ex_filters);
1861
1862
script_key = memnew(LineEdit);
1863
script_key->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1864
script_key->set_accessibility_name(TTRC("Encryption Key (256-bits as hexadecimal):"));
1865
script_key->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_script_encryption_key_changed));
1866
script_key->set_secret(true);
1867
1868
show_script_key = memnew(Button);
1869
show_script_key->set_toggle_mode(true);
1870
show_script_key->connect(SceneStringName(toggled), callable_mp(this, &ProjectExportDialog::_script_encryption_key_visibility_changed));
1871
1872
HBoxContainer *encryption_hb = memnew(HBoxContainer);
1873
encryption_hb->add_child(script_key);
1874
encryption_hb->add_child(show_script_key);
1875
1876
script_key_error = memnew(Label);
1877
script_key_error->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1878
script_key_error->set_text(String::utf8("• ") + TTR("Invalid Encryption Key (must be 64 hexadecimal characters long)"));
1879
script_key_error->add_theme_color_override(SceneStringName(font_color), EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor)));
1880
sec_vb->add_margin_child(TTRC("Encryption Key (256-bits as hexadecimal):"), encryption_hb);
1881
sec_vb->add_child(script_key_error);
1882
sections->add_child(sec_scroll_container);
1883
1884
seed_input = memnew(LineEdit);
1885
seed_input->set_accessibility_name(TTRC("Initialization vector seed"));
1886
seed_input->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_seed_input_changed));
1887
sec_vb->add_margin_child(TTR("Initialization vector seed"), seed_input);
1888
1889
Label *sec_info = memnew(Label);
1890
sec_info->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1891
sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source."));
1892
sec_vb->add_child(sec_info);
1893
1894
LinkButton *sec_more_info = memnew(LinkButton);
1895
sec_more_info->set_text(TTR("More Info..."));
1896
sec_more_info->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_open_key_help_link));
1897
sec_vb->add_child(sec_more_info);
1898
1899
// Script export parameters.
1900
1901
VBoxContainer *script_vb = memnew(VBoxContainer);
1902
script_vb->set_name(TTR("Scripts"));
1903
1904
script_mode = memnew(OptionButton);
1905
script_mode->set_accessibility_name(TTRC("GDScript Export Mode:"));
1906
script_vb->add_margin_child(TTR("GDScript Export Mode:"), script_mode);
1907
script_mode->add_item(TTR("Text (easier debugging)"), (int)EditorExportPreset::MODE_SCRIPT_TEXT);
1908
script_mode->add_item(TTR("Binary tokens (faster loading)"), (int)EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS);
1909
script_mode->add_item(TTR("Compressed binary tokens (smaller files)"), (int)EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED);
1910
script_mode->connect(SceneStringName(item_selected), callable_mp(this, &ProjectExportDialog::_script_export_mode_changed));
1911
1912
sections->add_child(script_vb);
1913
1914
sections->connect("tab_changed", callable_mp(this, &ProjectExportDialog::_tab_changed));
1915
1916
// Disable by default.
1917
duplicate_preset->set_disabled(true);
1918
delete_preset->set_disabled(true);
1919
script_key_error->hide();
1920
parameters->edit(nullptr);
1921
1922
// Label shown when no presets are present.
1923
1924
empty_label = memnew(Label(TTRC("No presets found.\nCreate one so that its parameters can be edited here.")));
1925
empty_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
1926
empty_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
1927
empty_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
1928
empty_label->set_clip_text(true); // Necessary to avoid overexpanding the dialog vertically.
1929
empty_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1930
empty_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1931
empty_label->hide();
1932
hbox->add_child(empty_label);
1933
1934
// Deletion dialog.
1935
1936
delete_confirm = memnew(ConfirmationDialog);
1937
add_child(delete_confirm);
1938
delete_confirm->set_ok_button_text(TTR("Delete"));
1939
delete_confirm->connect(SceneStringName(confirmed), callable_mp(this, &ProjectExportDialog::_delete_preset_confirm));
1940
1941
// Export buttons, dialogs and errors.
1942
1943
set_cancel_button_text(TTR("Close"));
1944
set_ok_button_text(TTR("Export PCK/ZIP..."));
1945
get_ok_button()->set_tooltip_text(TTR("Export the project resources as a PCK or ZIP package. This is not a playable build, only the project data without a Godot executable."));
1946
get_ok_button()->set_disabled(true);
1947
1948
export_button = add_button(TTR("Export Project..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export");
1949
export_button->set_tooltip_text(TTR("Export the project as a playable build (Godot executable and project data) for the selected preset."));
1950
export_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_export_project));
1951
// Disable initially before we select a valid preset.
1952
export_button->set_disabled(true);
1953
1954
export_all_dialog = memnew(ConfirmationDialog);
1955
add_child(export_all_dialog);
1956
export_all_dialog->set_title(TTR("Export All"));
1957
export_all_dialog->set_text(TTR("Choose an export mode:"));
1958
export_all_dialog->get_ok_button()->hide();
1959
export_all_dialog->add_button(TTR("Debug"), true, "debug");
1960
export_all_dialog->add_button(TTR("Release"), true, "release");
1961
export_all_dialog->connect("custom_action", callable_mp(this, &ProjectExportDialog::_export_all_dialog_action));
1962
1963
export_all_button = add_button(TTR("Export All..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export");
1964
export_all_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_export_all_dialog));
1965
export_all_button->set_disabled(true);
1966
1967
export_pck_zip = memnew(EditorFileDialog);
1968
export_pck_zip->add_filter("*.zip", TTR("ZIP File"));
1969
export_pck_zip->add_filter("*.pck", TTR("Godot Project Pack"));
1970
export_pck_zip->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1971
export_pck_zip->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
1972
add_child(export_pck_zip);
1973
export_pck_zip->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_pck_zip_selected));
1974
1975
// Export warnings and errors bottom section.
1976
1977
export_texture_format_error = memnew(ProjectExportTextureFormatError(this));
1978
main_vb->add_child(export_texture_format_error);
1979
export_texture_format_error->hide();
1980
1981
export_error = memnew(Label);
1982
export_error->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1983
main_vb->add_child(export_error);
1984
export_error->hide();
1985
export_error->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
1986
export_error->add_theme_color_override(SceneStringName(font_color), EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor)));
1987
1988
export_warning = memnew(Label);
1989
export_warning->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
1990
main_vb->add_child(export_warning);
1991
export_warning->hide();
1992
export_warning->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
1993
export_warning->add_theme_color_override(SceneStringName(font_color), EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("warning_color"), EditorStringName(Editor)));
1994
1995
export_templates_error = memnew(HBoxContainer);
1996
main_vb->add_child(export_templates_error);
1997
export_templates_error->hide();
1998
1999
Label *export_error2 = memnew(Label);
2000
export_error2->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
2001
export_templates_error->add_child(export_error2);
2002
export_error2->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
2003
export_error2->add_theme_color_override(SceneStringName(font_color), EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor)));
2004
export_error2->set_text(String::utf8("• ") + TTR("Export templates for this platform are missing:") + " ");
2005
2006
result_dialog = memnew(AcceptDialog);
2007
result_dialog->set_title(TTR("Project Export"));
2008
result_dialog_log = memnew(RichTextLabel);
2009
result_dialog_log->set_custom_minimum_size(Size2(300, 80) * EDSCALE);
2010
result_dialog->add_child(result_dialog_log);
2011
2012
main_vb->add_child(result_dialog);
2013
result_dialog->hide();
2014
2015
LinkButton *download_templates = memnew(LinkButton);
2016
download_templates->set_text(TTR("Manage Export Templates"));
2017
download_templates->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
2018
export_templates_error->add_child(download_templates);
2019
download_templates->connect(SceneStringName(pressed), callable_mp(this, &ProjectExportDialog::_open_export_template_manager));
2020
2021
// Export project file dialog.
2022
2023
export_project = memnew(EditorFileDialog);
2024
export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
2025
add_child(export_project);
2026
export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path));
2027
2028
export_project->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true));
2029
export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true));
2030
export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_as_patch", true));
2031
2032
set_hide_on_ok(false);
2033
2034
default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", "");
2035
// If no default set, use project name
2036
if (default_filename.is_empty()) {
2037
// If no project name defined, use a sane default
2038
default_filename = GLOBAL_GET("application/config/name");
2039
if (default_filename.is_empty()) {
2040
default_filename = "UnnamedProject";
2041
}
2042
}
2043
}
2044
2045