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