| 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_file_system.h" | 
|---|
| 36 | #include "editor/editor_node.h" | 
|---|
| 37 | #include "editor/editor_properties.h" | 
|---|
| 38 | #include "editor/editor_scale.h" | 
|---|
| 39 | #include "editor/editor_settings.h" | 
|---|
| 40 | #include "editor/editor_string_names.h" | 
|---|
| 41 | #include "editor/export/editor_export.h" | 
|---|
| 42 | #include "editor/gui/editor_file_dialog.h" | 
|---|
| 43 | #include "editor/import/resource_importer_texture_settings.h" | 
|---|
| 44 | #include "scene/gui/check_box.h" | 
|---|
| 45 | #include "scene/gui/check_button.h" | 
|---|
| 46 | #include "scene/gui/item_list.h" | 
|---|
| 47 | #include "scene/gui/link_button.h" | 
|---|
| 48 | #include "scene/gui/menu_button.h" | 
|---|
| 49 | #include "scene/gui/option_button.h" | 
|---|
| 50 | #include "scene/gui/popup_menu.h" | 
|---|
| 51 | #include "scene/gui/split_container.h" | 
|---|
| 52 | #include "scene/gui/tab_container.h" | 
|---|
| 53 | #include "scene/gui/texture_rect.h" | 
|---|
| 54 | #include "scene/gui/tree.h" | 
|---|
| 55 |  | 
|---|
| 56 | void ProjectExportTextureFormatError::_on_fix_texture_format_pressed() { | 
|---|
| 57 | ProjectSettings::get_singleton()->set_setting(setting_identifier, true); | 
|---|
| 58 | ProjectSettings::get_singleton()->save(); | 
|---|
| 59 | EditorFileSystem::get_singleton()->scan_changes(); | 
|---|
| 60 | emit_signal( "texture_format_enabled"); | 
|---|
| 61 | } | 
|---|
| 62 |  | 
|---|
| 63 | void ProjectExportTextureFormatError::_bind_methods() { | 
|---|
| 64 | ADD_SIGNAL(MethodInfo( "texture_format_enabled")); | 
|---|
| 65 | } | 
|---|
| 66 |  | 
|---|
| 67 | void ProjectExportTextureFormatError::_notification(int p_what) { | 
|---|
| 68 | switch (p_what) { | 
|---|
| 69 | case NOTIFICATION_THEME_CHANGED: { | 
|---|
| 70 | texture_format_error_label->add_theme_color_override( "font_color", get_theme_color(SNAME( "error_color"), EditorStringName(Editor))); | 
|---|
| 71 | } break; | 
|---|
| 72 | } | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 | void ProjectExportTextureFormatError::show_for_texture_format(const String &p_friendly_name, const String &p_setting_identifier) { | 
|---|
| 76 | 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( "/", " "))); | 
|---|
| 77 | setting_identifier = p_setting_identifier; | 
|---|
| 78 | show(); | 
|---|
| 79 | } | 
|---|
| 80 |  | 
|---|
| 81 | ProjectExportTextureFormatError::ProjectExportTextureFormatError() { | 
|---|
| 82 | // Set up the label. | 
|---|
| 83 | texture_format_error_label = memnew(Label); | 
|---|
| 84 | add_child(texture_format_error_label); | 
|---|
| 85 | // Set up the fix button. | 
|---|
| 86 | fix_texture_format_button = memnew(LinkButton); | 
|---|
| 87 | fix_texture_format_button->set_v_size_flags(Control::SIZE_SHRINK_CENTER); | 
|---|
| 88 | fix_texture_format_button->set_text(TTR( "Fix Import")); | 
|---|
| 89 | add_child(fix_texture_format_button); | 
|---|
| 90 | fix_texture_format_button->connect( "pressed", callable_mp(this, &ProjectExportTextureFormatError::_on_fix_texture_format_pressed)); | 
|---|
| 91 | } | 
|---|
| 92 |  | 
|---|
| 93 | void ProjectExportDialog::_theme_changed() { | 
|---|
| 94 | duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME( "Duplicate"))); | 
|---|
| 95 | delete_preset->set_icon(presets->get_editor_theme_icon(SNAME( "Remove"))); | 
|---|
| 96 | } | 
|---|
| 97 |  | 
|---|
| 98 | void ProjectExportDialog::_notification(int p_what) { | 
|---|
| 99 | switch (p_what) { | 
|---|
| 100 | case NOTIFICATION_VISIBILITY_CHANGED: { | 
|---|
| 101 | if (!is_visible()) { | 
|---|
| 102 | EditorSettings::get_singleton()->set_project_metadata( "dialog_bounds", "export", Rect2(get_position(), get_size())); | 
|---|
| 103 | } | 
|---|
| 104 | } break; | 
|---|
| 105 |  | 
|---|
| 106 | case NOTIFICATION_READY: { | 
|---|
| 107 | duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME( "Duplicate"))); | 
|---|
| 108 | delete_preset->set_icon(presets->get_editor_theme_icon(SNAME( "Remove"))); | 
|---|
| 109 | connect( "confirmed", callable_mp(this, &ProjectExportDialog::_export_pck_zip)); | 
|---|
| 110 | _update_export_all(); | 
|---|
| 111 | } break; | 
|---|
| 112 | } | 
|---|
| 113 | } | 
|---|
| 114 |  | 
|---|
| 115 | void ProjectExportDialog::() { | 
|---|
| 116 | add_preset->get_popup()->clear(); | 
|---|
| 117 | for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { | 
|---|
| 118 | Ref<EditorExportPlatform> plat = EditorExport::get_singleton()->get_export_platform(i); | 
|---|
| 119 |  | 
|---|
| 120 | add_preset->get_popup()->add_icon_item(plat->get_logo(), plat->get_name()); | 
|---|
| 121 | } | 
|---|
| 122 |  | 
|---|
| 123 | _update_presets(); | 
|---|
| 124 | if (presets->get_current() >= 0) { | 
|---|
| 125 | _update_current_preset(); // triggers rescan for templates if newly installed | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | // Restore valid window bounds or pop up at default size. | 
|---|
| 129 | Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata( "dialog_bounds", "export", Rect2()); | 
|---|
| 130 | if (saved_size != Rect2()) { | 
|---|
| 131 | popup(saved_size); | 
|---|
| 132 | } else { | 
|---|
| 133 | popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); | 
|---|
| 134 | } | 
|---|
| 135 | } | 
|---|
| 136 |  | 
|---|
| 137 | void ProjectExportDialog::_add_preset(int p_platform) { | 
|---|
| 138 | Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_platform(p_platform)->create_preset(); | 
|---|
| 139 | ERR_FAIL_COND(!preset.is_valid()); | 
|---|
| 140 |  | 
|---|
| 141 | String preset_name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name(); | 
|---|
| 142 | bool make_runnable = true; | 
|---|
| 143 | int attempt = 1; | 
|---|
| 144 | while (true) { | 
|---|
| 145 | bool valid = true; | 
|---|
| 146 |  | 
|---|
| 147 | for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { | 
|---|
| 148 | Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i); | 
|---|
| 149 | if (p->get_platform() == preset->get_platform() && p->is_runnable()) { | 
|---|
| 150 | make_runnable = false; | 
|---|
| 151 | } | 
|---|
| 152 | if (p->get_name() == preset_name) { | 
|---|
| 153 | valid = false; | 
|---|
| 154 | break; | 
|---|
| 155 | } | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|
| 158 | if (valid) { | 
|---|
| 159 | break; | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|
| 162 | attempt++; | 
|---|
| 163 | preset_name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name() + " "+ itos(attempt); | 
|---|
| 164 | } | 
|---|
| 165 |  | 
|---|
| 166 | preset->set_name(preset_name); | 
|---|
| 167 | if (make_runnable) { | 
|---|
| 168 | preset->set_runnable(make_runnable); | 
|---|
| 169 | } | 
|---|
| 170 | EditorExport::get_singleton()->add_export_preset(preset); | 
|---|
| 171 | _update_presets(); | 
|---|
| 172 | _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1); | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | void ProjectExportDialog::_force_update_current_preset_parameters() { | 
|---|
| 176 | // Force the parameters section to refresh its UI. | 
|---|
| 177 | parameters->edit(nullptr); | 
|---|
| 178 | _update_current_preset(); | 
|---|
| 179 | } | 
|---|
| 180 |  | 
|---|
| 181 | void ProjectExportDialog::_update_current_preset() { | 
|---|
| 182 | _edit_preset(presets->get_current()); | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | void ProjectExportDialog::_update_presets() { | 
|---|
| 186 | updating = true; | 
|---|
| 187 |  | 
|---|
| 188 | Ref<EditorExportPreset> current; | 
|---|
| 189 | if (presets->get_current() >= 0 && presets->get_current() < presets->get_item_count()) { | 
|---|
| 190 | current = get_current_preset(); | 
|---|
| 191 | } | 
|---|
| 192 |  | 
|---|
| 193 | int current_idx = -1; | 
|---|
| 194 | presets->clear(); | 
|---|
| 195 | for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { | 
|---|
| 196 | Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); | 
|---|
| 197 | if (preset == current) { | 
|---|
| 198 | current_idx = i; | 
|---|
| 199 | } | 
|---|
| 200 |  | 
|---|
| 201 | String preset_name = preset->get_name(); | 
|---|
| 202 | if (preset->is_runnable()) { | 
|---|
| 203 | preset_name += " ("+ TTR( "Runnable") + ")"; | 
|---|
| 204 | } | 
|---|
| 205 | preset->update_files(); | 
|---|
| 206 | presets->add_item(preset_name, preset->get_platform()->get_logo()); | 
|---|
| 207 | } | 
|---|
| 208 |  | 
|---|
| 209 | if (current_idx != -1) { | 
|---|
| 210 | presets->select(current_idx); | 
|---|
| 211 | } | 
|---|
| 212 |  | 
|---|
| 213 | updating = false; | 
|---|
| 214 | } | 
|---|
| 215 |  | 
|---|
| 216 | void ProjectExportDialog::_update_export_all() { | 
|---|
| 217 | bool can_export = EditorExport::get_singleton()->get_export_preset_count() > 0; | 
|---|
| 218 |  | 
|---|
| 219 | for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { | 
|---|
| 220 | Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); | 
|---|
| 221 | bool needs_templates; | 
|---|
| 222 | String error; | 
|---|
| 223 | if (preset->get_export_path().is_empty() || !preset->get_platform()->can_export(preset, error, needs_templates)) { | 
|---|
| 224 | can_export = false; | 
|---|
| 225 | break; | 
|---|
| 226 | } | 
|---|
| 227 | } | 
|---|
| 228 |  | 
|---|
| 229 | export_all_button->set_disabled(!can_export); | 
|---|
| 230 |  | 
|---|
| 231 | if (can_export) { | 
|---|
| 232 | export_all_button->set_tooltip_text(TTR( "Export the project for all the presets defined.")); | 
|---|
| 233 | } else { | 
|---|
| 234 | export_all_button->set_tooltip_text(TTR( "All presets must have an export path defined for Export All to work.")); | 
|---|
| 235 | } | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | void ProjectExportDialog::_edit_preset(int p_index) { | 
|---|
| 239 | if (p_index < 0 || p_index >= presets->get_item_count()) { | 
|---|
| 240 | name->set_text( ""); | 
|---|
| 241 | name->set_editable(false); | 
|---|
| 242 | export_path->hide(); | 
|---|
| 243 | runnable->set_disabled(true); | 
|---|
| 244 | parameters->edit(nullptr); | 
|---|
| 245 | presets->deselect_all(); | 
|---|
| 246 | duplicate_preset->set_disabled(true); | 
|---|
| 247 | delete_preset->set_disabled(true); | 
|---|
| 248 | sections->hide(); | 
|---|
| 249 | export_error->hide(); | 
|---|
| 250 | export_templates_error->hide(); | 
|---|
| 251 | return; | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(p_index); | 
|---|
| 255 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 256 |  | 
|---|
| 257 | updating = true; | 
|---|
| 258 |  | 
|---|
| 259 | presets->select(p_index); | 
|---|
| 260 | sections->show(); | 
|---|
| 261 |  | 
|---|
| 262 | name->set_editable(true); | 
|---|
| 263 | export_path->show(); | 
|---|
| 264 | duplicate_preset->set_disabled(false); | 
|---|
| 265 | delete_preset->set_disabled(false); | 
|---|
| 266 | get_ok_button()->set_disabled(false); | 
|---|
| 267 | name->set_text(current->get_name()); | 
|---|
| 268 |  | 
|---|
| 269 | List<String> extension_list = current->get_platform()->get_binary_extensions(current); | 
|---|
| 270 | Vector<String> extension_vector; | 
|---|
| 271 | for (int i = 0; i < extension_list.size(); i++) { | 
|---|
| 272 | extension_vector.push_back( "*."+ extension_list[i]); | 
|---|
| 273 | } | 
|---|
| 274 |  | 
|---|
| 275 | export_path->setup(extension_vector, false, true); | 
|---|
| 276 | export_path->update_property(); | 
|---|
| 277 | runnable->set_disabled(false); | 
|---|
| 278 | runnable->set_pressed(current->is_runnable()); | 
|---|
| 279 | parameters->set_object_class(current->get_platform()->get_class_name()); | 
|---|
| 280 | parameters->edit(current.ptr()); | 
|---|
| 281 |  | 
|---|
| 282 | export_filter->select(current->get_export_filter()); | 
|---|
| 283 | include_filters->set_text(current->get_include_filter()); | 
|---|
| 284 | include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR( "Resources to exclude:") : TTR( "Resources to export:")); | 
|---|
| 285 | exclude_filters->set_text(current->get_exclude_filter()); | 
|---|
| 286 | server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED); | 
|---|
| 287 |  | 
|---|
| 288 | _fill_resource_tree(); | 
|---|
| 289 |  | 
|---|
| 290 | bool needs_templates; | 
|---|
| 291 | String error; | 
|---|
| 292 | if (!current->get_platform()->can_export(current, error, needs_templates)) { | 
|---|
| 293 | if (!error.is_empty()) { | 
|---|
| 294 | Vector<String> items = error.split( "\n", false); | 
|---|
| 295 | error = ""; | 
|---|
| 296 | for (int i = 0; i < items.size(); i++) { | 
|---|
| 297 | if (i > 0) { | 
|---|
| 298 | error += "\n"; | 
|---|
| 299 | } | 
|---|
| 300 | error += " - "+ items[i]; | 
|---|
| 301 | } | 
|---|
| 302 |  | 
|---|
| 303 | export_error->set_text(error); | 
|---|
| 304 | export_error->show(); | 
|---|
| 305 | } else { | 
|---|
| 306 | export_error->hide(); | 
|---|
| 307 | } | 
|---|
| 308 | if (needs_templates) { | 
|---|
| 309 | export_templates_error->show(); | 
|---|
| 310 | } else { | 
|---|
| 311 | export_templates_error->hide(); | 
|---|
| 312 | } | 
|---|
| 313 |  | 
|---|
| 314 | export_warning->hide(); | 
|---|
| 315 | export_button->set_disabled(true); | 
|---|
| 316 | } else { | 
|---|
| 317 | if (error != String()) { | 
|---|
| 318 | Vector<String> items = error.split( "\n", false); | 
|---|
| 319 | error = ""; | 
|---|
| 320 | for (int i = 0; i < items.size(); i++) { | 
|---|
| 321 | if (i > 0) { | 
|---|
| 322 | error += "\n"; | 
|---|
| 323 | } | 
|---|
| 324 | error += " - "+ items[i]; | 
|---|
| 325 | } | 
|---|
| 326 | export_warning->set_text(error); | 
|---|
| 327 | export_warning->show(); | 
|---|
| 328 | } else { | 
|---|
| 329 | export_warning->hide(); | 
|---|
| 330 | } | 
|---|
| 331 |  | 
|---|
| 332 | export_error->hide(); | 
|---|
| 333 | export_templates_error->hide(); | 
|---|
| 334 | export_button->set_disabled(false); | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|
| 337 | custom_features->set_text(current->get_custom_features()); | 
|---|
| 338 | _update_feature_list(); | 
|---|
| 339 | _update_export_all(); | 
|---|
| 340 | child_controls_changed(); | 
|---|
| 341 |  | 
|---|
| 342 | if ((feature_set.has( "s3tc") || feature_set.has( "bptc")) && !ResourceImporterTextureSettings::should_import_s3tc_bptc()) { | 
|---|
| 343 | export_texture_format_error->show_for_texture_format( "S3TC/BPTC", "rendering/textures/vram_compression/import_s3tc_bptc"); | 
|---|
| 344 | } else if ((feature_set.has( "etc2") || feature_set.has( "astc")) && !ResourceImporterTextureSettings::should_import_etc2_astc()) { | 
|---|
| 345 | export_texture_format_error->show_for_texture_format( "ETC2/ASTC", "rendering/textures/vram_compression/import_etc2_astc"); | 
|---|
| 346 | } else { | 
|---|
| 347 | export_texture_format_error->hide(); | 
|---|
| 348 | } | 
|---|
| 349 |  | 
|---|
| 350 | String enc_in_filters_str = current->get_enc_in_filter(); | 
|---|
| 351 | String enc_ex_filters_str = current->get_enc_ex_filter(); | 
|---|
| 352 | if (!updating_enc_filters) { | 
|---|
| 353 | enc_in_filters->set_text(enc_in_filters_str); | 
|---|
| 354 | enc_ex_filters->set_text(enc_ex_filters_str); | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | bool enc_pck_mode = current->get_enc_pck(); | 
|---|
| 358 | enc_pck->set_pressed(enc_pck_mode); | 
|---|
| 359 |  | 
|---|
| 360 | enc_directory->set_disabled(!enc_pck_mode); | 
|---|
| 361 | enc_in_filters->set_editable(enc_pck_mode); | 
|---|
| 362 | enc_ex_filters->set_editable(enc_pck_mode); | 
|---|
| 363 | script_key->set_editable(enc_pck_mode); | 
|---|
| 364 |  | 
|---|
| 365 | bool enc_directory_mode = current->get_enc_directory(); | 
|---|
| 366 | enc_directory->set_pressed(enc_directory_mode); | 
|---|
| 367 |  | 
|---|
| 368 | String key = current->get_script_encryption_key(); | 
|---|
| 369 | if (!updating_script_key) { | 
|---|
| 370 | script_key->set_text(key); | 
|---|
| 371 | } | 
|---|
| 372 | if (enc_pck_mode) { | 
|---|
| 373 | script_key->set_editable(true); | 
|---|
| 374 |  | 
|---|
| 375 | bool key_valid = _validate_script_encryption_key(key); | 
|---|
| 376 | if (key_valid) { | 
|---|
| 377 | script_key_error->hide(); | 
|---|
| 378 | } else { | 
|---|
| 379 | script_key_error->show(); | 
|---|
| 380 | } | 
|---|
| 381 | } else { | 
|---|
| 382 | script_key->set_editable(false); | 
|---|
| 383 | script_key_error->hide(); | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|
| 386 | updating = false; | 
|---|
| 387 | } | 
|---|
| 388 |  | 
|---|
| 389 | void ProjectExportDialog::_update_feature_list() { | 
|---|
| 390 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 391 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 392 |  | 
|---|
| 393 | List<String> features_list; | 
|---|
| 394 |  | 
|---|
| 395 | current->get_platform()->get_platform_features(&features_list); | 
|---|
| 396 | current->get_platform()->get_preset_features(current, &features_list); | 
|---|
| 397 |  | 
|---|
| 398 | String custom = current->get_custom_features(); | 
|---|
| 399 | Vector<String> custom_list = custom.split( ","); | 
|---|
| 400 | for (int i = 0; i < custom_list.size(); i++) { | 
|---|
| 401 | String f = custom_list[i].strip_edges(); | 
|---|
| 402 | if (!f.is_empty()) { | 
|---|
| 403 | features_list.push_back(f); | 
|---|
| 404 | } | 
|---|
| 405 | } | 
|---|
| 406 |  | 
|---|
| 407 | feature_set.clear(); | 
|---|
| 408 | for (const String &E : features_list) { | 
|---|
| 409 | feature_set.insert(E); | 
|---|
| 410 | } | 
|---|
| 411 |  | 
|---|
| 412 | custom_feature_display->clear(); | 
|---|
| 413 | String text; | 
|---|
| 414 | bool first = true; | 
|---|
| 415 | for (const String &E : feature_set) { | 
|---|
| 416 | if (!first) { | 
|---|
| 417 | text += ", "; | 
|---|
| 418 | } else { | 
|---|
| 419 | first = false; | 
|---|
| 420 | } | 
|---|
| 421 | text += E; | 
|---|
| 422 | } | 
|---|
| 423 | custom_feature_display->add_text(text); | 
|---|
| 424 | } | 
|---|
| 425 |  | 
|---|
| 426 | void ProjectExportDialog::_custom_features_changed(const String &p_text) { | 
|---|
| 427 | if (updating) { | 
|---|
| 428 | return; | 
|---|
| 429 | } | 
|---|
| 430 |  | 
|---|
| 431 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 432 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 433 |  | 
|---|
| 434 | current->set_custom_features(p_text); | 
|---|
| 435 | _update_feature_list(); | 
|---|
| 436 | } | 
|---|
| 437 |  | 
|---|
| 438 | void ProjectExportDialog::_tab_changed(int) { | 
|---|
| 439 | _update_feature_list(); | 
|---|
| 440 | } | 
|---|
| 441 |  | 
|---|
| 442 | void ProjectExportDialog::_update_parameters(const String &p_edited_property) { | 
|---|
| 443 | _update_current_preset(); | 
|---|
| 444 | } | 
|---|
| 445 |  | 
|---|
| 446 | void ProjectExportDialog::_runnable_pressed() { | 
|---|
| 447 | if (updating) { | 
|---|
| 448 | return; | 
|---|
| 449 | } | 
|---|
| 450 |  | 
|---|
| 451 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 452 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 453 |  | 
|---|
| 454 | if (runnable->is_pressed()) { | 
|---|
| 455 | for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { | 
|---|
| 456 | Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i); | 
|---|
| 457 | if (p->get_platform() == current->get_platform()) { | 
|---|
| 458 | p->set_runnable(current == p); | 
|---|
| 459 | } | 
|---|
| 460 | } | 
|---|
| 461 | } else { | 
|---|
| 462 | current->set_runnable(false); | 
|---|
| 463 | } | 
|---|
| 464 |  | 
|---|
| 465 | _update_presets(); | 
|---|
| 466 | } | 
|---|
| 467 |  | 
|---|
| 468 | void ProjectExportDialog::_name_changed(const String &p_string) { | 
|---|
| 469 | if (updating) { | 
|---|
| 470 | return; | 
|---|
| 471 | } | 
|---|
| 472 |  | 
|---|
| 473 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 474 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 475 |  | 
|---|
| 476 | current->set_name(p_string); | 
|---|
| 477 | _update_presets(); | 
|---|
| 478 | } | 
|---|
| 479 |  | 
|---|
| 480 | void ProjectExportDialog::set_export_path(const String &p_value) { | 
|---|
| 481 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 482 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 483 |  | 
|---|
| 484 | current->set_export_path(p_value); | 
|---|
| 485 | } | 
|---|
| 486 |  | 
|---|
| 487 | String ProjectExportDialog::get_export_path() { | 
|---|
| 488 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 489 | ERR_FAIL_COND_V(current.is_null(), String( "")); | 
|---|
| 490 |  | 
|---|
| 491 | return current->get_export_path(); | 
|---|
| 492 | } | 
|---|
| 493 |  | 
|---|
| 494 | Ref<EditorExportPreset> ProjectExportDialog::get_current_preset() const { | 
|---|
| 495 | return EditorExport::get_singleton()->get_export_preset(presets->get_current()); | 
|---|
| 496 | } | 
|---|
| 497 |  | 
|---|
| 498 | void ProjectExportDialog::_export_path_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) { | 
|---|
| 499 | if (updating) { | 
|---|
| 500 | return; | 
|---|
| 501 | } | 
|---|
| 502 |  | 
|---|
| 503 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 504 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 505 |  | 
|---|
| 506 | current->set_export_path(p_value); | 
|---|
| 507 | _update_presets(); | 
|---|
| 508 | _update_export_all(); | 
|---|
| 509 | } | 
|---|
| 510 |  | 
|---|
| 511 | void ProjectExportDialog::_enc_filters_changed(const String &p_filters) { | 
|---|
| 512 | if (updating) { | 
|---|
| 513 | return; | 
|---|
| 514 | } | 
|---|
| 515 |  | 
|---|
| 516 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 517 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 518 |  | 
|---|
| 519 | current->set_enc_in_filter(enc_in_filters->get_text()); | 
|---|
| 520 | current->set_enc_ex_filter(enc_ex_filters->get_text()); | 
|---|
| 521 |  | 
|---|
| 522 | updating_enc_filters = true; | 
|---|
| 523 | _update_current_preset(); | 
|---|
| 524 | updating_enc_filters = false; | 
|---|
| 525 | } | 
|---|
| 526 |  | 
|---|
| 527 | void ProjectExportDialog::_open_key_help_link() { | 
|---|
| 528 | OS::get_singleton()->shell_open(vformat( "%s/contributing/development/compiling/compiling_with_script_encryption_key.html", VERSION_DOCS_URL)); | 
|---|
| 529 | } | 
|---|
| 530 |  | 
|---|
| 531 | void ProjectExportDialog::_enc_pck_changed(bool p_pressed) { | 
|---|
| 532 | if (updating) { | 
|---|
| 533 | return; | 
|---|
| 534 | } | 
|---|
| 535 |  | 
|---|
| 536 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 537 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 538 |  | 
|---|
| 539 | current->set_enc_pck(p_pressed); | 
|---|
| 540 | enc_directory->set_disabled(!p_pressed); | 
|---|
| 541 | enc_in_filters->set_editable(p_pressed); | 
|---|
| 542 | enc_ex_filters->set_editable(p_pressed); | 
|---|
| 543 | script_key->set_editable(p_pressed); | 
|---|
| 544 |  | 
|---|
| 545 | _update_current_preset(); | 
|---|
| 546 | } | 
|---|
| 547 |  | 
|---|
| 548 | void ProjectExportDialog::_enc_directory_changed(bool p_pressed) { | 
|---|
| 549 | if (updating) { | 
|---|
| 550 | return; | 
|---|
| 551 | } | 
|---|
| 552 |  | 
|---|
| 553 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 554 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 555 |  | 
|---|
| 556 | current->set_enc_directory(p_pressed); | 
|---|
| 557 |  | 
|---|
| 558 | _update_current_preset(); | 
|---|
| 559 | } | 
|---|
| 560 |  | 
|---|
| 561 | void ProjectExportDialog::_script_encryption_key_changed(const String &p_key) { | 
|---|
| 562 | if (updating) { | 
|---|
| 563 | return; | 
|---|
| 564 | } | 
|---|
| 565 |  | 
|---|
| 566 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 567 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 568 |  | 
|---|
| 569 | current->set_script_encryption_key(p_key); | 
|---|
| 570 |  | 
|---|
| 571 | updating_script_key = true; | 
|---|
| 572 | _update_current_preset(); | 
|---|
| 573 | updating_script_key = false; | 
|---|
| 574 | } | 
|---|
| 575 |  | 
|---|
| 576 | bool ProjectExportDialog::_validate_script_encryption_key(const String &p_key) { | 
|---|
| 577 | bool is_valid = false; | 
|---|
| 578 |  | 
|---|
| 579 | if (!p_key.is_empty() && p_key.is_valid_hex_number(false) && p_key.length() == 64) { | 
|---|
| 580 | is_valid = true; | 
|---|
| 581 | } | 
|---|
| 582 | return is_valid; | 
|---|
| 583 | } | 
|---|
| 584 |  | 
|---|
| 585 | void ProjectExportDialog::_duplicate_preset() { | 
|---|
| 586 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 587 | if (current.is_null()) { | 
|---|
| 588 | return; | 
|---|
| 589 | } | 
|---|
| 590 |  | 
|---|
| 591 | Ref<EditorExportPreset> preset = current->get_platform()->create_preset(); | 
|---|
| 592 | ERR_FAIL_COND(!preset.is_valid()); | 
|---|
| 593 |  | 
|---|
| 594 | String preset_name = current->get_name() + " (copy)"; | 
|---|
| 595 | bool make_runnable = true; | 
|---|
| 596 | while (true) { | 
|---|
| 597 | bool valid = true; | 
|---|
| 598 |  | 
|---|
| 599 | for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { | 
|---|
| 600 | Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i); | 
|---|
| 601 | if (p->get_platform() == preset->get_platform() && p->is_runnable()) { | 
|---|
| 602 | make_runnable = false; | 
|---|
| 603 | } | 
|---|
| 604 | if (p->get_name() == preset_name) { | 
|---|
| 605 | valid = false; | 
|---|
| 606 | break; | 
|---|
| 607 | } | 
|---|
| 608 | } | 
|---|
| 609 |  | 
|---|
| 610 | if (valid) { | 
|---|
| 611 | break; | 
|---|
| 612 | } | 
|---|
| 613 |  | 
|---|
| 614 | preset_name += " (copy)"; | 
|---|
| 615 | } | 
|---|
| 616 |  | 
|---|
| 617 | preset->set_name(preset_name); | 
|---|
| 618 | if (make_runnable) { | 
|---|
| 619 | preset->set_runnable(make_runnable); | 
|---|
| 620 | } | 
|---|
| 621 | preset->set_dedicated_server(current->is_dedicated_server()); | 
|---|
| 622 | preset->set_export_filter(current->get_export_filter()); | 
|---|
| 623 | preset->set_include_filter(current->get_include_filter()); | 
|---|
| 624 | preset->set_exclude_filter(current->get_exclude_filter()); | 
|---|
| 625 | preset->set_custom_features(current->get_custom_features()); | 
|---|
| 626 |  | 
|---|
| 627 | for (const KeyValue<StringName, Variant> &E : current->get_values()) { | 
|---|
| 628 | preset->set(E.key, E.value); | 
|---|
| 629 | } | 
|---|
| 630 |  | 
|---|
| 631 | EditorExport::get_singleton()->add_export_preset(preset); | 
|---|
| 632 | _update_presets(); | 
|---|
| 633 | _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1); | 
|---|
| 634 | } | 
|---|
| 635 |  | 
|---|
| 636 | void ProjectExportDialog::_delete_preset() { | 
|---|
| 637 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 638 | if (current.is_null()) { | 
|---|
| 639 | return; | 
|---|
| 640 | } | 
|---|
| 641 |  | 
|---|
| 642 | delete_confirm->set_text(vformat(TTR( "Delete preset '%s'?"), current->get_name())); | 
|---|
| 643 | delete_confirm->popup_centered(); | 
|---|
| 644 | } | 
|---|
| 645 |  | 
|---|
| 646 | void ProjectExportDialog::_delete_preset_confirm() { | 
|---|
| 647 | int idx = presets->get_current(); | 
|---|
| 648 | _edit_preset(-1); | 
|---|
| 649 | export_button->set_disabled(true); | 
|---|
| 650 | get_ok_button()->set_disabled(true); | 
|---|
| 651 | EditorExport::get_singleton()->remove_export_preset(idx); | 
|---|
| 652 | _update_presets(); | 
|---|
| 653 |  | 
|---|
| 654 | // The Export All button might become enabled (if all other presets have an export path defined), | 
|---|
| 655 | // or it could be disabled (if there are no presets anymore). | 
|---|
| 656 | _update_export_all(); | 
|---|
| 657 | } | 
|---|
| 658 |  | 
|---|
| 659 | Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) { | 
|---|
| 660 | if (p_from == presets) { | 
|---|
| 661 | int pos = presets->get_item_at_position(p_point, true); | 
|---|
| 662 |  | 
|---|
| 663 | if (pos >= 0) { | 
|---|
| 664 | Dictionary d; | 
|---|
| 665 | d[ "type"] = "export_preset"; | 
|---|
| 666 | d[ "preset"] = pos; | 
|---|
| 667 |  | 
|---|
| 668 | HBoxContainer *drag = memnew(HBoxContainer); | 
|---|
| 669 | TextureRect *tr = memnew(TextureRect); | 
|---|
| 670 | tr->set_texture(presets->get_item_icon(pos)); | 
|---|
| 671 | drag->add_child(tr); | 
|---|
| 672 | Label *label = memnew(Label); | 
|---|
| 673 | label->set_text(presets->get_item_text(pos)); | 
|---|
| 674 | drag->add_child(label); | 
|---|
| 675 |  | 
|---|
| 676 | presets->set_drag_preview(drag); | 
|---|
| 677 |  | 
|---|
| 678 | return d; | 
|---|
| 679 | } | 
|---|
| 680 | } | 
|---|
| 681 |  | 
|---|
| 682 | return Variant(); | 
|---|
| 683 | } | 
|---|
| 684 |  | 
|---|
| 685 | bool ProjectExportDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { | 
|---|
| 686 | if (p_from == presets) { | 
|---|
| 687 | Dictionary d = p_data; | 
|---|
| 688 | if (!d.has( "type") || String(d[ "type"]) != "export_preset") { | 
|---|
| 689 | return false; | 
|---|
| 690 | } | 
|---|
| 691 |  | 
|---|
| 692 | if (presets->get_item_at_position(p_point, true) < 0 && !presets->is_pos_at_end_of_items(p_point)) { | 
|---|
| 693 | return false; | 
|---|
| 694 | } | 
|---|
| 695 | } | 
|---|
| 696 |  | 
|---|
| 697 | return true; | 
|---|
| 698 | } | 
|---|
| 699 |  | 
|---|
| 700 | void ProjectExportDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { | 
|---|
| 701 | if (p_from == presets) { | 
|---|
| 702 | Dictionary d = p_data; | 
|---|
| 703 | int from_pos = d[ "preset"]; | 
|---|
| 704 |  | 
|---|
| 705 | int to_pos = -1; | 
|---|
| 706 |  | 
|---|
| 707 | if (presets->get_item_at_position(p_point, true) >= 0) { | 
|---|
| 708 | to_pos = presets->get_item_at_position(p_point, true); | 
|---|
| 709 | } | 
|---|
| 710 |  | 
|---|
| 711 | if (to_pos == -1 && !presets->is_pos_at_end_of_items(p_point)) { | 
|---|
| 712 | return; | 
|---|
| 713 | } | 
|---|
| 714 |  | 
|---|
| 715 | if (to_pos == from_pos) { | 
|---|
| 716 | return; | 
|---|
| 717 | } else if (to_pos > from_pos) { | 
|---|
| 718 | to_pos--; | 
|---|
| 719 | } | 
|---|
| 720 |  | 
|---|
| 721 | Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(from_pos); | 
|---|
| 722 | EditorExport::get_singleton()->remove_export_preset(from_pos); | 
|---|
| 723 | EditorExport::get_singleton()->add_export_preset(preset, to_pos); | 
|---|
| 724 |  | 
|---|
| 725 | _update_presets(); | 
|---|
| 726 | if (to_pos >= 0) { | 
|---|
| 727 | _edit_preset(to_pos); | 
|---|
| 728 | } else { | 
|---|
| 729 | _edit_preset(presets->get_item_count() - 1); | 
|---|
| 730 | } | 
|---|
| 731 | } | 
|---|
| 732 | } | 
|---|
| 733 |  | 
|---|
| 734 | void ProjectExportDialog::_export_type_changed(int p_which) { | 
|---|
| 735 | if (updating) { | 
|---|
| 736 | return; | 
|---|
| 737 | } | 
|---|
| 738 |  | 
|---|
| 739 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 740 | if (current.is_null()) { | 
|---|
| 741 | return; | 
|---|
| 742 | } | 
|---|
| 743 |  | 
|---|
| 744 | EditorExportPreset::ExportFilter filter_type = (EditorExportPreset::ExportFilter)p_which; | 
|---|
| 745 | current->set_export_filter(filter_type); | 
|---|
| 746 | current->set_dedicated_server(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED); | 
|---|
| 747 | server_strip_message->set_visible(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED); | 
|---|
| 748 |  | 
|---|
| 749 | // Default to stripping everything when first switching to server build. | 
|---|
| 750 | if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) { | 
|---|
| 751 | current->set_file_export_mode( "res://", EditorExportPreset::MODE_FILE_STRIP); | 
|---|
| 752 | } | 
|---|
| 753 | include_label->set_text(current->get_export_filter() == EditorExportPreset::EXCLUDE_SELECTED_RESOURCES ? TTR( "Resources to exclude:") : TTR( "Resources to export:")); | 
|---|
| 754 |  | 
|---|
| 755 | updating = true; | 
|---|
| 756 | _fill_resource_tree(); | 
|---|
| 757 | updating = false; | 
|---|
| 758 | } | 
|---|
| 759 |  | 
|---|
| 760 | void ProjectExportDialog::_filter_changed(const String &p_filter) { | 
|---|
| 761 | if (updating) { | 
|---|
| 762 | return; | 
|---|
| 763 | } | 
|---|
| 764 |  | 
|---|
| 765 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 766 | if (current.is_null()) { | 
|---|
| 767 | return; | 
|---|
| 768 | } | 
|---|
| 769 |  | 
|---|
| 770 | current->set_include_filter(include_filters->get_text()); | 
|---|
| 771 | current->set_exclude_filter(exclude_filters->get_text()); | 
|---|
| 772 | } | 
|---|
| 773 |  | 
|---|
| 774 | void ProjectExportDialog::_fill_resource_tree() { | 
|---|
| 775 | include_files->clear(); | 
|---|
| 776 | include_label->hide(); | 
|---|
| 777 | include_margin->hide(); | 
|---|
| 778 |  | 
|---|
| 779 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 780 | if (current.is_null()) { | 
|---|
| 781 | return; | 
|---|
| 782 | } | 
|---|
| 783 |  | 
|---|
| 784 | EditorExportPreset::ExportFilter f = current->get_export_filter(); | 
|---|
| 785 |  | 
|---|
| 786 | if (f == EditorExportPreset::EXPORT_ALL_RESOURCES) { | 
|---|
| 787 | return; | 
|---|
| 788 | } | 
|---|
| 789 |  | 
|---|
| 790 | TreeItem *root = include_files->create_item(); | 
|---|
| 791 |  | 
|---|
| 792 | if (f == EditorExportPreset::EXPORT_CUSTOMIZED) { | 
|---|
| 793 | include_files->set_columns(2); | 
|---|
| 794 | include_files->set_column_expand(1, false); | 
|---|
| 795 | include_files->set_column_custom_minimum_width(1, 250 * EDSCALE); | 
|---|
| 796 | } else { | 
|---|
| 797 | include_files->set_columns(1); | 
|---|
| 798 | } | 
|---|
| 799 |  | 
|---|
| 800 | include_label->show(); | 
|---|
| 801 | include_margin->show(); | 
|---|
| 802 |  | 
|---|
| 803 | _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f); | 
|---|
| 804 |  | 
|---|
| 805 | if (f == EditorExportPreset::EXPORT_CUSTOMIZED) { | 
|---|
| 806 | _propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED); | 
|---|
| 807 | } | 
|---|
| 808 | } | 
|---|
| 809 |  | 
|---|
| 810 | void ProjectExportDialog::_setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode) { | 
|---|
| 811 | if (p_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) { | 
|---|
| 812 | p_item->set_checked(0, false); | 
|---|
| 813 | p_item->set_cell_mode(1, TreeItem::CELL_MODE_STRING); | 
|---|
| 814 | p_item->set_editable(1, false); | 
|---|
| 815 | p_item->set_selectable(1, false); | 
|---|
| 816 | p_item->set_custom_color(1, get_theme_color(SNAME( "disabled_font_color"), EditorStringName(Editor))); | 
|---|
| 817 | } else { | 
|---|
| 818 | p_item->set_checked(0, true); | 
|---|
| 819 | p_item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); | 
|---|
| 820 | p_item->set_editable(1, true); | 
|---|
| 821 | p_item->set_selectable(1, true); | 
|---|
| 822 | p_item->clear_custom_color(1); | 
|---|
| 823 | } | 
|---|
| 824 | p_item->set_metadata(1, p_mode); | 
|---|
| 825 | } | 
|---|
| 826 |  | 
|---|
| 827 | bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, EditorExportPreset::ExportFilter p_export_filter) { | 
|---|
| 828 | p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); | 
|---|
| 829 | p_item->set_icon(0, presets->get_theme_icon(SNAME( "folder"), SNAME( "FileDialog"))); | 
|---|
| 830 | p_item->set_text(0, p_dir->get_name() + "/"); | 
|---|
| 831 | p_item->set_editable(0, true); | 
|---|
| 832 | p_item->set_metadata(0, p_dir->get_path()); | 
|---|
| 833 |  | 
|---|
| 834 | if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) { | 
|---|
| 835 | _setup_item_for_file_mode(p_item, current->get_file_export_mode(p_dir->get_path())); | 
|---|
| 836 | } | 
|---|
| 837 |  | 
|---|
| 838 | bool used = false; | 
|---|
| 839 | for (int i = 0; i < p_dir->get_subdir_count(); i++) { | 
|---|
| 840 | TreeItem *subdir = include_files->create_item(p_item); | 
|---|
| 841 | if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_export_filter)) { | 
|---|
| 842 | used = true; | 
|---|
| 843 | } else { | 
|---|
| 844 | memdelete(subdir); | 
|---|
| 845 | } | 
|---|
| 846 | } | 
|---|
| 847 |  | 
|---|
| 848 | for (int i = 0; i < p_dir->get_file_count(); i++) { | 
|---|
| 849 | String type = p_dir->get_file_type(i); | 
|---|
| 850 | if (p_export_filter == EditorExportPreset::EXPORT_SELECTED_SCENES && type != "PackedScene") { | 
|---|
| 851 | continue; | 
|---|
| 852 | } | 
|---|
| 853 | if (type == "TextFile") { | 
|---|
| 854 | continue; | 
|---|
| 855 | } | 
|---|
| 856 |  | 
|---|
| 857 | TreeItem *file = include_files->create_item(p_item); | 
|---|
| 858 | file->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); | 
|---|
| 859 | file->set_text(0, p_dir->get_file(i)); | 
|---|
| 860 |  | 
|---|
| 861 | String path = p_dir->get_file_path(i); | 
|---|
| 862 |  | 
|---|
| 863 | file->set_icon(0, EditorNode::get_singleton()->get_class_icon(type)); | 
|---|
| 864 | file->set_editable(0, true); | 
|---|
| 865 | file->set_metadata(0, path); | 
|---|
| 866 |  | 
|---|
| 867 | if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) { | 
|---|
| 868 | _setup_item_for_file_mode(file, current->get_file_export_mode(path)); | 
|---|
| 869 | } else { | 
|---|
| 870 | file->set_checked(0, current->has_export_file(path)); | 
|---|
| 871 | file->propagate_check(0); | 
|---|
| 872 | } | 
|---|
| 873 |  | 
|---|
| 874 | used = true; | 
|---|
| 875 | } | 
|---|
| 876 | return used; | 
|---|
| 877 | } | 
|---|
| 878 |  | 
|---|
| 879 | void ProjectExportDialog::_propagate_file_export_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_inherited_export_mode) { | 
|---|
| 880 | EditorExportPreset::FileExportMode file_export_mode = (EditorExportPreset::FileExportMode)(int)p_item->get_metadata(1); | 
|---|
| 881 | bool is_inherited = false; | 
|---|
| 882 | if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) { | 
|---|
| 883 | file_export_mode = p_inherited_export_mode; | 
|---|
| 884 | is_inherited = true; | 
|---|
| 885 | } | 
|---|
| 886 |  | 
|---|
| 887 | if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) { | 
|---|
| 888 | p_item->set_text(1, ""); | 
|---|
| 889 | } else { | 
|---|
| 890 | String text = file_mode_popup->get_item_text(file_mode_popup->get_item_index(file_export_mode)); | 
|---|
| 891 | if (is_inherited) { | 
|---|
| 892 | text += " "+ TTR( "(Inherited)"); | 
|---|
| 893 | } | 
|---|
| 894 | p_item->set_text(1, text); | 
|---|
| 895 | } | 
|---|
| 896 |  | 
|---|
| 897 | for (int i = 0; i < p_item->get_child_count(); i++) { | 
|---|
| 898 | _propagate_file_export_mode(p_item->get_child(i), file_export_mode); | 
|---|
| 899 | } | 
|---|
| 900 | } | 
|---|
| 901 |  | 
|---|
| 902 | void ProjectExportDialog::_tree_changed() { | 
|---|
| 903 | if (updating) { | 
|---|
| 904 | return; | 
|---|
| 905 | } | 
|---|
| 906 |  | 
|---|
| 907 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 908 | if (current.is_null()) { | 
|---|
| 909 | return; | 
|---|
| 910 | } | 
|---|
| 911 |  | 
|---|
| 912 | TreeItem *item = include_files->get_edited(); | 
|---|
| 913 | if (!item) { | 
|---|
| 914 | return; | 
|---|
| 915 | } | 
|---|
| 916 |  | 
|---|
| 917 | if (current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED) { | 
|---|
| 918 | EditorExportPreset::FileExportMode file_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED; | 
|---|
| 919 | String path = item->get_metadata(0); | 
|---|
| 920 |  | 
|---|
| 921 | if (item->is_checked(0)) { | 
|---|
| 922 | file_mode = current->get_file_export_mode(path, EditorExportPreset::MODE_FILE_STRIP); | 
|---|
| 923 | } | 
|---|
| 924 |  | 
|---|
| 925 | current->set_file_export_mode(path, file_mode); | 
|---|
| 926 | _setup_item_for_file_mode(item, file_mode); | 
|---|
| 927 | _propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED); | 
|---|
| 928 | } else { | 
|---|
| 929 | item->propagate_check(0); | 
|---|
| 930 | } | 
|---|
| 931 | } | 
|---|
| 932 |  | 
|---|
| 933 | void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) { | 
|---|
| 934 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 935 | if (current.is_null()) { | 
|---|
| 936 | return; | 
|---|
| 937 | } | 
|---|
| 938 | TreeItem *item = Object::cast_to<TreeItem>(p_obj); | 
|---|
| 939 | String path = item->get_metadata(0); | 
|---|
| 940 | if (item && !path.ends_with( "/")) { | 
|---|
| 941 | bool added = item->is_checked(0); | 
|---|
| 942 | if (added) { | 
|---|
| 943 | current->add_export_file(path); | 
|---|
| 944 | } else { | 
|---|
| 945 | current->remove_export_file(path); | 
|---|
| 946 | } | 
|---|
| 947 | } | 
|---|
| 948 | } | 
|---|
| 949 |  | 
|---|
| 950 | void ProjectExportDialog::(bool p_arrow_clicked) { | 
|---|
| 951 | Rect2 bounds = include_files->get_custom_popup_rect(); | 
|---|
| 952 | bounds.position += get_global_canvas_transform().get_origin(); | 
|---|
| 953 | bounds.size *= get_global_canvas_transform().get_scale(); | 
|---|
| 954 | if (!is_embedding_subwindows()) { | 
|---|
| 955 | bounds.position += get_position(); | 
|---|
| 956 | } | 
|---|
| 957 | file_mode_popup->popup(bounds); | 
|---|
| 958 | } | 
|---|
| 959 |  | 
|---|
| 960 | void ProjectExportDialog::_set_file_export_mode(int p_id) { | 
|---|
| 961 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 962 | if (current.is_null()) { | 
|---|
| 963 | return; | 
|---|
| 964 | } | 
|---|
| 965 |  | 
|---|
| 966 | TreeItem *item = include_files->get_edited(); | 
|---|
| 967 | String path = item->get_metadata(0); | 
|---|
| 968 |  | 
|---|
| 969 | EditorExportPreset::FileExportMode file_export_mode = (EditorExportPreset::FileExportMode)p_id; | 
|---|
| 970 | current->set_file_export_mode(path, file_export_mode); | 
|---|
| 971 | item->set_metadata(1, file_export_mode); | 
|---|
| 972 | _propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED); | 
|---|
| 973 | } | 
|---|
| 974 |  | 
|---|
| 975 | void ProjectExportDialog::_export_pck_zip() { | 
|---|
| 976 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 977 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 978 |  | 
|---|
| 979 | String dir = current->get_export_path().get_base_dir(); | 
|---|
| 980 | export_pck_zip->set_current_dir(dir); | 
|---|
| 981 |  | 
|---|
| 982 | export_pck_zip->popup_file_dialog(); | 
|---|
| 983 | } | 
|---|
| 984 |  | 
|---|
| 985 | void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) { | 
|---|
| 986 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 987 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 988 | Ref<EditorExportPlatform> platform = current->get_platform(); | 
|---|
| 989 | ERR_FAIL_COND(platform.is_null()); | 
|---|
| 990 |  | 
|---|
| 991 | if (p_path.ends_with( ".zip")) { | 
|---|
| 992 | platform->export_zip(current, export_pck_zip_debug->is_pressed(), p_path); | 
|---|
| 993 | } else if (p_path.ends_with( ".pck")) { | 
|---|
| 994 | platform->export_pack(current, export_pck_zip_debug->is_pressed(), p_path); | 
|---|
| 995 | } | 
|---|
| 996 | } | 
|---|
| 997 |  | 
|---|
| 998 | void ProjectExportDialog::_open_export_template_manager() { | 
|---|
| 999 | hide(); | 
|---|
| 1000 | EditorNode::get_singleton()->open_export_template_manager(); | 
|---|
| 1001 | } | 
|---|
| 1002 |  | 
|---|
| 1003 | void ProjectExportDialog::_validate_export_path(const String &p_path) { | 
|---|
| 1004 | // Disable export via OK button or Enter key if LineEdit has an empty filename | 
|---|
| 1005 | bool invalid_path = (p_path.get_file().get_basename().is_empty()); | 
|---|
| 1006 |  | 
|---|
| 1007 | // Check if state change before needlessly messing with signals | 
|---|
| 1008 | if (invalid_path && export_project->get_ok_button()->is_disabled()) { | 
|---|
| 1009 | return; | 
|---|
| 1010 | } | 
|---|
| 1011 | if (!invalid_path && !export_project->get_ok_button()->is_disabled()) { | 
|---|
| 1012 | return; | 
|---|
| 1013 | } | 
|---|
| 1014 |  | 
|---|
| 1015 | if (invalid_path) { | 
|---|
| 1016 | export_project->get_ok_button()->set_disabled(true); | 
|---|
| 1017 | export_project->get_line_edit()->disconnect( "text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); | 
|---|
| 1018 | } else { | 
|---|
| 1019 | export_project->get_ok_button()->set_disabled(false); | 
|---|
| 1020 | export_project->get_line_edit()->connect( "text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); | 
|---|
| 1021 | } | 
|---|
| 1022 | } | 
|---|
| 1023 |  | 
|---|
| 1024 | void ProjectExportDialog::_export_project() { | 
|---|
| 1025 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 1026 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 1027 | Ref<EditorExportPlatform> platform = current->get_platform(); | 
|---|
| 1028 | ERR_FAIL_COND(platform.is_null()); | 
|---|
| 1029 |  | 
|---|
| 1030 | export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM); | 
|---|
| 1031 | export_project->clear_filters(); | 
|---|
| 1032 |  | 
|---|
| 1033 | List<String> extension_list = platform->get_binary_extensions(current); | 
|---|
| 1034 | for (int i = 0; i < extension_list.size(); i++) { | 
|---|
| 1035 | // TRANSLATORS: This is the name of a project export file format. %s will be replaced by the platform name. | 
|---|
| 1036 | export_project->add_filter( "*."+ extension_list[i], vformat(TTR( "%s Export"), platform->get_name())); | 
|---|
| 1037 | } | 
|---|
| 1038 |  | 
|---|
| 1039 | if (!current->get_export_path().is_empty()) { | 
|---|
| 1040 | export_project->set_current_path(current->get_export_path()); | 
|---|
| 1041 | } else { | 
|---|
| 1042 | if (extension_list.size() >= 1) { | 
|---|
| 1043 | export_project->set_current_file(default_filename + "."+ extension_list[0]); | 
|---|
| 1044 | } else { | 
|---|
| 1045 | export_project->set_current_file(default_filename); | 
|---|
| 1046 | } | 
|---|
| 1047 | } | 
|---|
| 1048 |  | 
|---|
| 1049 | // Ensure that signal is connected if previous attempt left it disconnected | 
|---|
| 1050 | // with _validate_export_path. | 
|---|
| 1051 | // FIXME: This is a hack, we should instead change EditorFileDialog to allow | 
|---|
| 1052 | // disabling validation by the "text_submitted" signal. | 
|---|
| 1053 | if (!export_project->get_line_edit()->is_connected( "text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted))) { | 
|---|
| 1054 | export_project->get_ok_button()->set_disabled(false); | 
|---|
| 1055 | export_project->get_line_edit()->connect( "text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); | 
|---|
| 1056 | } | 
|---|
| 1057 |  | 
|---|
| 1058 | export_project->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); | 
|---|
| 1059 | export_project->popup_file_dialog(); | 
|---|
| 1060 | } | 
|---|
| 1061 |  | 
|---|
| 1062 | void ProjectExportDialog::_export_project_to_path(const String &p_path) { | 
|---|
| 1063 | // Save this name for use in future exports (but drop the file extension) | 
|---|
| 1064 | default_filename = p_path.get_file().get_basename(); | 
|---|
| 1065 | EditorSettings::get_singleton()->set_project_metadata( "export_options", "default_filename", default_filename); | 
|---|
| 1066 |  | 
|---|
| 1067 | Ref<EditorExportPreset> current = get_current_preset(); | 
|---|
| 1068 | ERR_FAIL_COND(current.is_null()); | 
|---|
| 1069 | Ref<EditorExportPlatform> platform = current->get_platform(); | 
|---|
| 1070 | ERR_FAIL_COND(platform.is_null()); | 
|---|
| 1071 | current->set_export_path(p_path); | 
|---|
| 1072 |  | 
|---|
| 1073 | platform->clear_messages(); | 
|---|
| 1074 | Error err = platform->export_project(current, export_debug->is_pressed(), current->get_export_path(), 0); | 
|---|
| 1075 | result_dialog_log->clear(); | 
|---|
| 1076 | if (err != ERR_SKIP) { | 
|---|
| 1077 | if (platform->fill_log_messages(result_dialog_log, err)) { | 
|---|
| 1078 | result_dialog->popup_centered_ratio(0.5); | 
|---|
| 1079 | } | 
|---|
| 1080 | } | 
|---|
| 1081 | } | 
|---|
| 1082 |  | 
|---|
| 1083 | void ProjectExportDialog::_export_all_dialog() { | 
|---|
| 1084 | #ifndef ANDROID_ENABLED | 
|---|
| 1085 | export_all_dialog->show(); | 
|---|
| 1086 | export_all_dialog->popup_centered(Size2(300, 80)); | 
|---|
| 1087 | #endif | 
|---|
| 1088 | } | 
|---|
| 1089 |  | 
|---|
| 1090 | void ProjectExportDialog::_export_all_dialog_action(const String &p_str) { | 
|---|
| 1091 | export_all_dialog->hide(); | 
|---|
| 1092 |  | 
|---|
| 1093 | _export_all(p_str != "release"); | 
|---|
| 1094 | } | 
|---|
| 1095 |  | 
|---|
| 1096 | void ProjectExportDialog::_export_all(bool p_debug) { | 
|---|
| 1097 | String export_target = p_debug ? TTR( "Debug") : TTR( "Release"); | 
|---|
| 1098 | EditorProgress ep( "exportall", TTR( "Exporting All") + " "+ export_target, EditorExport::get_singleton()->get_export_preset_count(), true); | 
|---|
| 1099 |  | 
|---|
| 1100 | bool show_dialog = false; | 
|---|
| 1101 | result_dialog_log->clear(); | 
|---|
| 1102 | for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { | 
|---|
| 1103 | Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); | 
|---|
| 1104 | ERR_FAIL_COND(preset.is_null()); | 
|---|
| 1105 | Ref<EditorExportPlatform> platform = preset->get_platform(); | 
|---|
| 1106 | ERR_FAIL_COND(platform.is_null()); | 
|---|
| 1107 |  | 
|---|
| 1108 | ep.step(preset->get_name(), i); | 
|---|
| 1109 |  | 
|---|
| 1110 | platform->clear_messages(); | 
|---|
| 1111 | Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0); | 
|---|
| 1112 | if (err == ERR_SKIP) { | 
|---|
| 1113 | return; | 
|---|
| 1114 | } | 
|---|
| 1115 | bool has_messages = platform->fill_log_messages(result_dialog_log, err); | 
|---|
| 1116 | show_dialog = show_dialog || has_messages; | 
|---|
| 1117 | } | 
|---|
| 1118 | if (show_dialog) { | 
|---|
| 1119 | result_dialog->popup_centered_ratio(0.5); | 
|---|
| 1120 | } | 
|---|
| 1121 | } | 
|---|
| 1122 |  | 
|---|
| 1123 | void ProjectExportDialog::_bind_methods() { | 
|---|
| 1124 | ClassDB::bind_method( "_export_all", &ProjectExportDialog::_export_all); | 
|---|
| 1125 | ClassDB::bind_method( "set_export_path", &ProjectExportDialog::set_export_path); | 
|---|
| 1126 | ClassDB::bind_method( "get_export_path", &ProjectExportDialog::get_export_path); | 
|---|
| 1127 | ClassDB::bind_method( "get_current_preset", &ProjectExportDialog::get_current_preset); | 
|---|
| 1128 |  | 
|---|
| 1129 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "export_path"), "set_export_path", "get_export_path"); | 
|---|
| 1130 | } | 
|---|
| 1131 |  | 
|---|
| 1132 | ProjectExportDialog::ProjectExportDialog() { | 
|---|
| 1133 | set_title(TTR( "Export")); | 
|---|
| 1134 | set_clamp_to_embedder(true); | 
|---|
| 1135 |  | 
|---|
| 1136 | VBoxContainer *main_vb = memnew(VBoxContainer); | 
|---|
| 1137 | main_vb->connect( "theme_changed", callable_mp(this, &ProjectExportDialog::_theme_changed)); | 
|---|
| 1138 | add_child(main_vb); | 
|---|
| 1139 | HSplitContainer *hbox = memnew(HSplitContainer); | 
|---|
| 1140 | main_vb->add_child(hbox); | 
|---|
| 1141 | hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1142 |  | 
|---|
| 1143 | // Presets list. | 
|---|
| 1144 |  | 
|---|
| 1145 | VBoxContainer *preset_vb = memnew(VBoxContainer); | 
|---|
| 1146 | preset_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1147 | hbox->add_child(preset_vb); | 
|---|
| 1148 |  | 
|---|
| 1149 | Label *l = memnew(Label(TTR( "Presets"))); | 
|---|
| 1150 | l->set_theme_type_variation( "HeaderSmall"); | 
|---|
| 1151 |  | 
|---|
| 1152 | HBoxContainer *preset_hb = memnew(HBoxContainer); | 
|---|
| 1153 | preset_hb->add_child(l); | 
|---|
| 1154 | preset_hb->add_spacer(); | 
|---|
| 1155 | preset_vb->add_child(preset_hb); | 
|---|
| 1156 |  | 
|---|
| 1157 | add_preset = memnew(MenuButton); | 
|---|
| 1158 | add_preset->set_text(TTR( "Add...")); | 
|---|
| 1159 | add_preset->get_popup()->connect( "index_pressed", callable_mp(this, &ProjectExportDialog::_add_preset)); | 
|---|
| 1160 | preset_hb->add_child(add_preset); | 
|---|
| 1161 | MarginContainer *mc = memnew(MarginContainer); | 
|---|
| 1162 | preset_vb->add_child(mc); | 
|---|
| 1163 | mc->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1164 | presets = memnew(ItemList); | 
|---|
| 1165 | SET_DRAG_FORWARDING_GCD(presets, ProjectExportDialog); | 
|---|
| 1166 | mc->add_child(presets); | 
|---|
| 1167 | presets->connect( "item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset)); | 
|---|
| 1168 | duplicate_preset = memnew(Button); | 
|---|
| 1169 | duplicate_preset->set_tooltip_text(TTR( "Duplicate")); | 
|---|
| 1170 | duplicate_preset->set_flat(true); | 
|---|
| 1171 | preset_hb->add_child(duplicate_preset); | 
|---|
| 1172 | duplicate_preset->connect( "pressed", callable_mp(this, &ProjectExportDialog::_duplicate_preset)); | 
|---|
| 1173 | delete_preset = memnew(Button); | 
|---|
| 1174 | delete_preset->set_tooltip_text(TTR( "Delete")); | 
|---|
| 1175 | delete_preset->set_flat(true); | 
|---|
| 1176 | preset_hb->add_child(delete_preset); | 
|---|
| 1177 | delete_preset->connect( "pressed", callable_mp(this, &ProjectExportDialog::_delete_preset)); | 
|---|
| 1178 |  | 
|---|
| 1179 | // Preset settings. | 
|---|
| 1180 |  | 
|---|
| 1181 | VBoxContainer *settings_vb = memnew(VBoxContainer); | 
|---|
| 1182 | settings_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1183 | hbox->add_child(settings_vb); | 
|---|
| 1184 |  | 
|---|
| 1185 | name = memnew(LineEdit); | 
|---|
| 1186 | settings_vb->add_margin_child(TTR( "Name:"), name); | 
|---|
| 1187 | name->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_name_changed)); | 
|---|
| 1188 | runnable = memnew(CheckButton); | 
|---|
| 1189 | runnable->set_text(TTR( "Runnable")); | 
|---|
| 1190 | 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.")); | 
|---|
| 1191 | runnable->connect( "pressed", callable_mp(this, &ProjectExportDialog::_runnable_pressed)); | 
|---|
| 1192 | settings_vb->add_child(runnable); | 
|---|
| 1193 |  | 
|---|
| 1194 | export_path = memnew(EditorPropertyPath); | 
|---|
| 1195 | settings_vb->add_child(export_path); | 
|---|
| 1196 | export_path->set_label(TTR( "Export Path")); | 
|---|
| 1197 | export_path->set_object_and_property(this, "export_path"); | 
|---|
| 1198 | export_path->set_save_mode(); | 
|---|
| 1199 | export_path->connect( "property_changed", callable_mp(this, &ProjectExportDialog::_export_path_changed)); | 
|---|
| 1200 |  | 
|---|
| 1201 | // Subsections. | 
|---|
| 1202 |  | 
|---|
| 1203 | sections = memnew(TabContainer); | 
|---|
| 1204 | sections->set_use_hidden_tabs_for_min_size(true); | 
|---|
| 1205 | sections->set_theme_type_variation( "TabContainerOdd"); | 
|---|
| 1206 | settings_vb->add_child(sections); | 
|---|
| 1207 | sections->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1208 |  | 
|---|
| 1209 | // Main preset parameters. | 
|---|
| 1210 |  | 
|---|
| 1211 | parameters = memnew(EditorInspector); | 
|---|
| 1212 | sections->add_child(parameters); | 
|---|
| 1213 | parameters->set_name(TTR( "Options")); | 
|---|
| 1214 | parameters->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1215 | parameters->set_use_doc_hints(true); | 
|---|
| 1216 | parameters->connect( "property_edited", callable_mp(this, &ProjectExportDialog::_update_parameters)); | 
|---|
| 1217 | EditorExport::get_singleton()->connect( "export_presets_updated", callable_mp(this, &ProjectExportDialog::_force_update_current_preset_parameters)); | 
|---|
| 1218 |  | 
|---|
| 1219 | // Resources export parameters. | 
|---|
| 1220 |  | 
|---|
| 1221 | VBoxContainer *resources_vb = memnew(VBoxContainer); | 
|---|
| 1222 | sections->add_child(resources_vb); | 
|---|
| 1223 | resources_vb->set_name(TTR( "Resources")); | 
|---|
| 1224 |  | 
|---|
| 1225 | export_filter = memnew(OptionButton); | 
|---|
| 1226 | export_filter->add_item(TTR( "Export all resources in the project")); | 
|---|
| 1227 | export_filter->add_item(TTR( "Export selected scenes (and dependencies)")); | 
|---|
| 1228 | export_filter->add_item(TTR( "Export selected resources (and dependencies)")); | 
|---|
| 1229 | export_filter->add_item(TTR( "Export all resources in the project except resources checked below")); | 
|---|
| 1230 | export_filter->add_item(TTR( "Export as dedicated server")); | 
|---|
| 1231 | resources_vb->add_margin_child(TTR( "Export Mode:"), export_filter); | 
|---|
| 1232 | export_filter->connect( "item_selected", callable_mp(this, &ProjectExportDialog::_export_type_changed)); | 
|---|
| 1233 |  | 
|---|
| 1234 | include_label = memnew(Label); | 
|---|
| 1235 | include_label->set_text(TTR( "Resources to export:")); | 
|---|
| 1236 | resources_vb->add_child(include_label); | 
|---|
| 1237 | include_margin = memnew(MarginContainer); | 
|---|
| 1238 | include_margin->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1239 | resources_vb->add_child(include_margin); | 
|---|
| 1240 |  | 
|---|
| 1241 | include_files = memnew(Tree); | 
|---|
| 1242 | include_margin->add_child(include_files); | 
|---|
| 1243 | include_files->connect( "item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed)); | 
|---|
| 1244 | include_files->connect( "check_propagated_to_item", callable_mp(this, &ProjectExportDialog::_check_propagated_to_item)); | 
|---|
| 1245 | include_files->connect( "custom_popup_edited", callable_mp(this, &ProjectExportDialog::_tree_popup_edited)); | 
|---|
| 1246 |  | 
|---|
| 1247 | server_strip_message = memnew(Label); | 
|---|
| 1248 | server_strip_message->set_visible(false); | 
|---|
| 1249 | server_strip_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); | 
|---|
| 1250 | resources_vb->add_child(server_strip_message); | 
|---|
| 1251 |  | 
|---|
| 1252 | { | 
|---|
| 1253 | List<StringName> resource_names; | 
|---|
| 1254 | ClassDB::get_inheriters_from_class( "Resource", &resource_names); | 
|---|
| 1255 |  | 
|---|
| 1256 | PackedStringArray strippable; | 
|---|
| 1257 | for (StringName resource_name : resource_names) { | 
|---|
| 1258 | if (ClassDB::has_method(resource_name, "create_placeholder", true)) { | 
|---|
| 1259 | strippable.push_back(resource_name); | 
|---|
| 1260 | } | 
|---|
| 1261 | } | 
|---|
| 1262 | strippable.sort(); | 
|---|
| 1263 |  | 
|---|
| 1264 | String message = TTR( "\"Strip Visuals\" will replace the following resources with placeholders:") + " "; | 
|---|
| 1265 | message += String( ", ").join(strippable); | 
|---|
| 1266 | server_strip_message->set_text(message); | 
|---|
| 1267 | } | 
|---|
| 1268 |  | 
|---|
| 1269 | file_mode_popup = memnew(PopupMenu); | 
|---|
| 1270 | add_child(file_mode_popup); | 
|---|
| 1271 | file_mode_popup->add_item(TTR( "Strip Visuals"), EditorExportPreset::MODE_FILE_STRIP); | 
|---|
| 1272 | file_mode_popup->add_item(TTR( "Keep"), EditorExportPreset::MODE_FILE_KEEP); | 
|---|
| 1273 | file_mode_popup->add_item(TTR( "Remove"), EditorExportPreset::MODE_FILE_REMOVE); | 
|---|
| 1274 | file_mode_popup->connect( "id_pressed", callable_mp(this, &ProjectExportDialog::_set_file_export_mode)); | 
|---|
| 1275 |  | 
|---|
| 1276 | include_filters = memnew(LineEdit); | 
|---|
| 1277 | resources_vb->add_margin_child( | 
|---|
| 1278 | TTR( "Filters to export non-resource files/folders\n(comma-separated, e.g: *.json, *.txt, docs/*)"), | 
|---|
| 1279 | include_filters); | 
|---|
| 1280 | include_filters->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_filter_changed)); | 
|---|
| 1281 |  | 
|---|
| 1282 | exclude_filters = memnew(LineEdit); | 
|---|
| 1283 | resources_vb->add_margin_child( | 
|---|
| 1284 | TTR( "Filters to exclude files/folders from project\n(comma-separated, e.g: *.json, *.txt, docs/*)"), | 
|---|
| 1285 | exclude_filters); | 
|---|
| 1286 | exclude_filters->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_filter_changed)); | 
|---|
| 1287 |  | 
|---|
| 1288 | // Feature tags. | 
|---|
| 1289 |  | 
|---|
| 1290 | VBoxContainer *feature_vb = memnew(VBoxContainer); | 
|---|
| 1291 | feature_vb->set_name(TTR( "Features")); | 
|---|
| 1292 | custom_features = memnew(LineEdit); | 
|---|
| 1293 | custom_features->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_custom_features_changed)); | 
|---|
| 1294 | feature_vb->add_margin_child(TTR( "Custom (comma-separated):"), custom_features); | 
|---|
| 1295 | custom_feature_display = memnew(RichTextLabel); | 
|---|
| 1296 | custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL); | 
|---|
| 1297 | feature_vb->add_margin_child(TTR( "Feature List:"), custom_feature_display, true); | 
|---|
| 1298 | sections->add_child(feature_vb); | 
|---|
| 1299 |  | 
|---|
| 1300 | // Script export parameters. | 
|---|
| 1301 |  | 
|---|
| 1302 | VBoxContainer *sec_vb = memnew(VBoxContainer); | 
|---|
| 1303 | sec_vb->set_name(TTR( "Encryption")); | 
|---|
| 1304 |  | 
|---|
| 1305 | enc_pck = memnew(CheckButton); | 
|---|
| 1306 | enc_pck->connect( "toggled", callable_mp(this, &ProjectExportDialog::_enc_pck_changed)); | 
|---|
| 1307 | enc_pck->set_text(TTR( "Encrypt Exported PCK")); | 
|---|
| 1308 | sec_vb->add_child(enc_pck); | 
|---|
| 1309 |  | 
|---|
| 1310 | enc_directory = memnew(CheckButton); | 
|---|
| 1311 | enc_directory->connect( "toggled", callable_mp(this, &ProjectExportDialog::_enc_directory_changed)); | 
|---|
| 1312 | enc_directory->set_text(TTR( "Encrypt Index (File Names and Info)")); | 
|---|
| 1313 | sec_vb->add_child(enc_directory); | 
|---|
| 1314 |  | 
|---|
| 1315 | enc_in_filters = memnew(LineEdit); | 
|---|
| 1316 | enc_in_filters->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_enc_filters_changed)); | 
|---|
| 1317 | sec_vb->add_margin_child( | 
|---|
| 1318 | TTR( "Filters to include files/folders\n(comma-separated, e.g: *.tscn, *.tres, scenes/*)"), | 
|---|
| 1319 | enc_in_filters); | 
|---|
| 1320 |  | 
|---|
| 1321 | enc_ex_filters = memnew(LineEdit); | 
|---|
| 1322 | enc_ex_filters->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_enc_filters_changed)); | 
|---|
| 1323 | sec_vb->add_margin_child( | 
|---|
| 1324 | TTR( "Filters to exclude files/folders\n(comma-separated, e.g: *.ctex, *.import, music/*)"), | 
|---|
| 1325 | enc_ex_filters); | 
|---|
| 1326 |  | 
|---|
| 1327 | script_key = memnew(LineEdit); | 
|---|
| 1328 | script_key->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_script_encryption_key_changed)); | 
|---|
| 1329 | script_key_error = memnew(Label); | 
|---|
| 1330 | script_key_error->set_text(String::utf8( "•  ") + TTR( "Invalid Encryption Key (must be 64 hexadecimal characters long)")); | 
|---|
| 1331 | script_key_error->add_theme_color_override( "font_color", EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME( "error_color"), EditorStringName(Editor))); | 
|---|
| 1332 | sec_vb->add_margin_child(TTR( "Encryption Key (256-bits as hexadecimal):"), script_key); | 
|---|
| 1333 | sec_vb->add_child(script_key_error); | 
|---|
| 1334 | sections->add_child(sec_vb); | 
|---|
| 1335 |  | 
|---|
| 1336 | Label *sec_info = memnew(Label); | 
|---|
| 1337 | sec_info->set_text(TTR( "Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source.")); | 
|---|
| 1338 | sec_vb->add_child(sec_info); | 
|---|
| 1339 |  | 
|---|
| 1340 | LinkButton *sec_more_info = memnew(LinkButton); | 
|---|
| 1341 | sec_more_info->set_text(TTR( "More Info...")); | 
|---|
| 1342 | sec_more_info->connect( "pressed", callable_mp(this, &ProjectExportDialog::_open_key_help_link)); | 
|---|
| 1343 | sec_vb->add_child(sec_more_info); | 
|---|
| 1344 |  | 
|---|
| 1345 | sections->connect( "tab_changed", callable_mp(this, &ProjectExportDialog::_tab_changed)); | 
|---|
| 1346 |  | 
|---|
| 1347 | // Disable by default. | 
|---|
| 1348 | name->set_editable(false); | 
|---|
| 1349 | export_path->hide(); | 
|---|
| 1350 | runnable->set_disabled(true); | 
|---|
| 1351 | duplicate_preset->set_disabled(true); | 
|---|
| 1352 | delete_preset->set_disabled(true); | 
|---|
| 1353 | script_key_error->hide(); | 
|---|
| 1354 | sections->hide(); | 
|---|
| 1355 | parameters->edit(nullptr); | 
|---|
| 1356 |  | 
|---|
| 1357 | // Deletion dialog. | 
|---|
| 1358 |  | 
|---|
| 1359 | delete_confirm = memnew(ConfirmationDialog); | 
|---|
| 1360 | add_child(delete_confirm); | 
|---|
| 1361 | delete_confirm->set_ok_button_text(TTR( "Delete")); | 
|---|
| 1362 | delete_confirm->connect( "confirmed", callable_mp(this, &ProjectExportDialog::_delete_preset_confirm)); | 
|---|
| 1363 |  | 
|---|
| 1364 | // Export buttons, dialogs and errors. | 
|---|
| 1365 |  | 
|---|
| 1366 | set_cancel_button_text(TTR( "Close")); | 
|---|
| 1367 | set_ok_button_text(TTR( "Export PCK/ZIP...")); | 
|---|
| 1368 | get_ok_button()->set_disabled(true); | 
|---|
| 1369 | #ifdef ANDROID_ENABLED | 
|---|
| 1370 | export_button = memnew(Button); | 
|---|
| 1371 | export_button->hide(); | 
|---|
| 1372 | #else | 
|---|
| 1373 | export_button = add_button(TTR( "Export Project..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export"); | 
|---|
| 1374 | #endif | 
|---|
| 1375 | export_button->connect( "pressed", callable_mp(this, &ProjectExportDialog::_export_project)); | 
|---|
| 1376 | // Disable initially before we select a valid preset | 
|---|
| 1377 | export_button->set_disabled(true); | 
|---|
| 1378 |  | 
|---|
| 1379 | export_all_dialog = memnew(ConfirmationDialog); | 
|---|
| 1380 | add_child(export_all_dialog); | 
|---|
| 1381 | export_all_dialog->set_title(TTR( "Export All")); | 
|---|
| 1382 | export_all_dialog->set_text(TTR( "Choose an export mode:")); | 
|---|
| 1383 | export_all_dialog->get_ok_button()->hide(); | 
|---|
| 1384 | export_all_dialog->add_button(TTR( "Debug"), true, "debug"); | 
|---|
| 1385 | export_all_dialog->add_button(TTR( "Release"), true, "release"); | 
|---|
| 1386 | export_all_dialog->connect( "custom_action", callable_mp(this, &ProjectExportDialog::_export_all_dialog_action)); | 
|---|
| 1387 | #ifdef ANDROID_ENABLED | 
|---|
| 1388 | export_all_dialog->hide(); | 
|---|
| 1389 |  | 
|---|
| 1390 | export_all_button = memnew(Button); | 
|---|
| 1391 | export_all_button->hide(); | 
|---|
| 1392 | #else | 
|---|
| 1393 | export_all_button = add_button(TTR( "Export All..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export"); | 
|---|
| 1394 | #endif | 
|---|
| 1395 | export_all_button->connect( "pressed", callable_mp(this, &ProjectExportDialog::_export_all_dialog)); | 
|---|
| 1396 | export_all_button->set_disabled(true); | 
|---|
| 1397 |  | 
|---|
| 1398 | export_pck_zip = memnew(EditorFileDialog); | 
|---|
| 1399 | export_pck_zip->add_filter( "*.zip", TTR( "ZIP File")); | 
|---|
| 1400 | export_pck_zip->add_filter( "*.pck", TTR( "Godot Project Pack")); | 
|---|
| 1401 | export_pck_zip->set_access(EditorFileDialog::ACCESS_FILESYSTEM); | 
|---|
| 1402 | export_pck_zip->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); | 
|---|
| 1403 | add_child(export_pck_zip); | 
|---|
| 1404 | export_pck_zip->connect( "file_selected", callable_mp(this, &ProjectExportDialog::_export_pck_zip_selected)); | 
|---|
| 1405 |  | 
|---|
| 1406 | // Export warnings and errors bottom section. | 
|---|
| 1407 |  | 
|---|
| 1408 | export_texture_format_error = memnew(ProjectExportTextureFormatError); | 
|---|
| 1409 | main_vb->add_child(export_texture_format_error); | 
|---|
| 1410 | export_texture_format_error->hide(); | 
|---|
| 1411 | export_texture_format_error->connect( "texture_format_enabled", callable_mp(this, &ProjectExportDialog::_update_current_preset)); | 
|---|
| 1412 |  | 
|---|
| 1413 | export_error = memnew(Label); | 
|---|
| 1414 | main_vb->add_child(export_error); | 
|---|
| 1415 | export_error->hide(); | 
|---|
| 1416 | export_error->add_theme_color_override( "font_color", EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME( "error_color"), EditorStringName(Editor))); | 
|---|
| 1417 |  | 
|---|
| 1418 | export_warning = memnew(Label); | 
|---|
| 1419 | main_vb->add_child(export_warning); | 
|---|
| 1420 | export_warning->hide(); | 
|---|
| 1421 | export_warning->add_theme_color_override( "font_color", EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME( "warning_color"), EditorStringName(Editor))); | 
|---|
| 1422 |  | 
|---|
| 1423 | export_templates_error = memnew(HBoxContainer); | 
|---|
| 1424 | main_vb->add_child(export_templates_error); | 
|---|
| 1425 | export_templates_error->hide(); | 
|---|
| 1426 |  | 
|---|
| 1427 | Label *export_error2 = memnew(Label); | 
|---|
| 1428 | export_templates_error->add_child(export_error2); | 
|---|
| 1429 | export_error2->add_theme_color_override( "font_color", EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME( "error_color"), EditorStringName(Editor))); | 
|---|
| 1430 | export_error2->set_text(String::utf8( "•  ") + TTR( "Export templates for this platform are missing:") + " "); | 
|---|
| 1431 |  | 
|---|
| 1432 | result_dialog = memnew(AcceptDialog); | 
|---|
| 1433 | result_dialog->set_title(TTR( "Project Export")); | 
|---|
| 1434 | result_dialog_log = memnew(RichTextLabel); | 
|---|
| 1435 | result_dialog_log->set_custom_minimum_size(Size2(300, 80) * EDSCALE); | 
|---|
| 1436 | result_dialog->add_child(result_dialog_log); | 
|---|
| 1437 |  | 
|---|
| 1438 | main_vb->add_child(result_dialog); | 
|---|
| 1439 | result_dialog->hide(); | 
|---|
| 1440 |  | 
|---|
| 1441 | LinkButton *download_templates = memnew(LinkButton); | 
|---|
| 1442 | download_templates->set_text(TTR( "Manage Export Templates")); | 
|---|
| 1443 | download_templates->set_v_size_flags(Control::SIZE_SHRINK_CENTER); | 
|---|
| 1444 | export_templates_error->add_child(download_templates); | 
|---|
| 1445 | download_templates->connect( "pressed", callable_mp(this, &ProjectExportDialog::_open_export_template_manager)); | 
|---|
| 1446 |  | 
|---|
| 1447 | // Export project file dialog. | 
|---|
| 1448 |  | 
|---|
| 1449 | export_project = memnew(EditorFileDialog); | 
|---|
| 1450 | export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM); | 
|---|
| 1451 | add_child(export_project); | 
|---|
| 1452 | export_project->connect( "file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path)); | 
|---|
| 1453 | export_project->get_line_edit()->connect( "text_changed", callable_mp(this, &ProjectExportDialog::_validate_export_path)); | 
|---|
| 1454 |  | 
|---|
| 1455 | export_debug = memnew(CheckBox); | 
|---|
| 1456 | export_debug->set_text(TTR( "Export With Debug")); | 
|---|
| 1457 | export_debug->set_pressed(true); | 
|---|
| 1458 | export_debug->set_h_size_flags(Control::SIZE_SHRINK_CENTER); | 
|---|
| 1459 | export_project->get_vbox()->add_child(export_debug); | 
|---|
| 1460 |  | 
|---|
| 1461 | export_pck_zip_debug = memnew(CheckBox); | 
|---|
| 1462 | export_pck_zip_debug->set_text(TTR( "Export With Debug")); | 
|---|
| 1463 | export_pck_zip_debug->set_pressed(true); | 
|---|
| 1464 | export_pck_zip_debug->set_h_size_flags(Control::SIZE_SHRINK_CENTER); | 
|---|
| 1465 | export_pck_zip->get_vbox()->add_child(export_pck_zip_debug); | 
|---|
| 1466 |  | 
|---|
| 1467 | set_hide_on_ok(false); | 
|---|
| 1468 |  | 
|---|
| 1469 | default_filename = EditorSettings::get_singleton()->get_project_metadata( "export_options", "default_filename", ""); | 
|---|
| 1470 | // If no default set, use project name | 
|---|
| 1471 | if (default_filename.is_empty()) { | 
|---|
| 1472 | // If no project name defined, use a sane default | 
|---|
| 1473 | default_filename = GLOBAL_GET( "application/config/name"); | 
|---|
| 1474 | if (default_filename.is_empty()) { | 
|---|
| 1475 | default_filename = "UnnamedProject"; | 
|---|
| 1476 | } | 
|---|
| 1477 | } | 
|---|
| 1478 | } | 
|---|
| 1479 |  | 
|---|
| 1480 | ProjectExportDialog::~ProjectExportDialog() { | 
|---|
| 1481 | } | 
|---|
| 1482 |  | 
|---|