project_export.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290
  1. /*************************************************************************/
  2. /* project_export.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  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. #include "project_export.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/io/dir_access.h"
  33. #include "core/io/file_access.h"
  34. #include "core/io/image_loader.h"
  35. #include "core/io/resource_loader.h"
  36. #include "core/io/resource_saver.h"
  37. #include "core/os/os.h"
  38. #include "core/string/optimized_translation.h"
  39. #include "editor_data.h"
  40. #include "editor_node.h"
  41. #include "editor_scale.h"
  42. #include "editor_settings.h"
  43. #include "scene/gui/box_container.h"
  44. #include "scene/gui/margin_container.h"
  45. #include "scene/gui/scroll_container.h"
  46. #include "scene/gui/tab_container.h"
  47. #include "servers/display_server.h"
  48. void ProjectExportDialog::_theme_changed() {
  49. duplicate_preset->set_icon(presets->get_theme_icon("Duplicate", "EditorIcons"));
  50. delete_preset->set_icon(presets->get_theme_icon("Remove", "EditorIcons"));
  51. }
  52. void ProjectExportDialog::_notification(int p_what) {
  53. switch (p_what) {
  54. case NOTIFICATION_VISIBILITY_CHANGED: {
  55. if (!is_visible()) {
  56. EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "export", Rect2(get_position(), get_size()));
  57. }
  58. } break;
  59. case NOTIFICATION_READY: {
  60. duplicate_preset->set_icon(presets->get_theme_icon("Duplicate", "EditorIcons"));
  61. delete_preset->set_icon(presets->get_theme_icon("Remove", "EditorIcons"));
  62. connect("confirmed", callable_mp(this, &ProjectExportDialog::_export_pck_zip));
  63. } break;
  64. }
  65. }
  66. void ProjectExportDialog::popup_export() {
  67. add_preset->get_popup()->clear();
  68. for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) {
  69. Ref<EditorExportPlatform> plat = EditorExport::get_singleton()->get_export_platform(i);
  70. add_preset->get_popup()->add_icon_item(plat->get_logo(), plat->get_name());
  71. }
  72. _update_presets();
  73. if (presets->get_current() >= 0) {
  74. _update_current_preset(); // triggers rescan for templates if newly installed
  75. }
  76. // Restore valid window bounds or pop up at default size.
  77. Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "export", Rect2());
  78. if (saved_size != Rect2()) {
  79. popup(saved_size);
  80. } else {
  81. popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8);
  82. }
  83. }
  84. void ProjectExportDialog::_add_preset(int p_platform) {
  85. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_platform(p_platform)->create_preset();
  86. ERR_FAIL_COND(!preset.is_valid());
  87. String name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name();
  88. bool make_runnable = true;
  89. int attempt = 1;
  90. while (true) {
  91. bool valid = true;
  92. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  93. Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
  94. if (p->get_platform() == preset->get_platform() && p->is_runnable()) {
  95. make_runnable = false;
  96. }
  97. if (p->get_name() == name) {
  98. valid = false;
  99. break;
  100. }
  101. }
  102. if (valid) {
  103. break;
  104. }
  105. attempt++;
  106. name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name() + " " + itos(attempt);
  107. }
  108. preset->set_name(name);
  109. if (make_runnable) {
  110. preset->set_runnable(make_runnable);
  111. }
  112. EditorExport::get_singleton()->add_export_preset(preset);
  113. _update_presets();
  114. _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
  115. }
  116. void ProjectExportDialog::_force_update_current_preset_parameters() {
  117. // Force the parameters section to refresh its UI.
  118. parameters->edit(nullptr);
  119. _update_current_preset();
  120. }
  121. void ProjectExportDialog::_update_current_preset() {
  122. _edit_preset(presets->get_current());
  123. }
  124. void ProjectExportDialog::_update_presets() {
  125. updating = true;
  126. Ref<EditorExportPreset> current;
  127. if (presets->get_current() >= 0 && presets->get_current() < presets->get_item_count()) {
  128. current = get_current_preset();
  129. }
  130. int current_idx = -1;
  131. presets->clear();
  132. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  133. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
  134. if (preset == current) {
  135. current_idx = i;
  136. }
  137. String name = preset->get_name();
  138. if (preset->is_runnable()) {
  139. name += " (" + TTR("Runnable") + ")";
  140. }
  141. preset->update_files_to_export();
  142. presets->add_item(name, preset->get_platform()->get_logo());
  143. }
  144. if (current_idx != -1) {
  145. presets->select(current_idx);
  146. }
  147. updating = false;
  148. }
  149. void ProjectExportDialog::_update_export_all() {
  150. bool can_export = EditorExport::get_singleton()->get_export_preset_count() > 0;
  151. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  152. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
  153. bool needs_templates;
  154. String error;
  155. if (preset->get_export_path() == "" || !preset->get_platform()->can_export(preset, error, needs_templates)) {
  156. can_export = false;
  157. break;
  158. }
  159. }
  160. if (can_export) {
  161. export_all_button->set_disabled(false);
  162. } else {
  163. export_all_button->set_disabled(true);
  164. }
  165. }
  166. void ProjectExportDialog::_edit_preset(int p_index) {
  167. if (p_index < 0 || p_index >= presets->get_item_count()) {
  168. name->set_text("");
  169. name->set_editable(false);
  170. export_path->hide();
  171. runnable->set_disabled(true);
  172. parameters->edit(nullptr);
  173. presets->deselect_all();
  174. duplicate_preset->set_disabled(true);
  175. delete_preset->set_disabled(true);
  176. sections->hide();
  177. export_error->hide();
  178. export_templates_error->hide();
  179. return;
  180. }
  181. Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(p_index);
  182. ERR_FAIL_COND(current.is_null());
  183. updating = true;
  184. presets->select(p_index);
  185. sections->show();
  186. name->set_editable(true);
  187. export_path->show();
  188. duplicate_preset->set_disabled(false);
  189. delete_preset->set_disabled(false);
  190. name->set_text(current->get_name());
  191. List<String> extension_list = current->get_platform()->get_binary_extensions(current);
  192. Vector<String> extension_vector;
  193. for (int i = 0; i < extension_list.size(); i++) {
  194. extension_vector.push_back("*." + extension_list[i]);
  195. }
  196. export_path->setup(extension_vector, false, true);
  197. export_path->update_property();
  198. runnable->set_disabled(false);
  199. runnable->set_pressed(current->is_runnable());
  200. parameters->edit(current.ptr());
  201. export_filter->select(current->get_export_filter());
  202. include_filters->set_text(current->get_include_filter());
  203. exclude_filters->set_text(current->get_exclude_filter());
  204. _fill_resource_tree();
  205. bool needs_templates;
  206. String error;
  207. if (!current->get_platform()->can_export(current, error, needs_templates)) {
  208. if (error != String()) {
  209. Vector<String> items = error.split("\n", false);
  210. error = "";
  211. for (int i = 0; i < items.size(); i++) {
  212. if (i > 0) {
  213. error += "\n";
  214. }
  215. error += " - " + items[i];
  216. }
  217. export_error->set_text(error);
  218. export_error->show();
  219. } else {
  220. export_error->hide();
  221. }
  222. if (needs_templates) {
  223. export_templates_error->show();
  224. } else {
  225. export_templates_error->hide();
  226. }
  227. export_button->set_disabled(true);
  228. get_ok_button()->set_disabled(true);
  229. } else {
  230. export_error->hide();
  231. export_templates_error->hide();
  232. export_button->set_disabled(false);
  233. get_ok_button()->set_disabled(false);
  234. }
  235. custom_features->set_text(current->get_custom_features());
  236. _update_feature_list();
  237. _update_export_all();
  238. child_controls_changed();
  239. String enc_in_filters_str = current->get_enc_in_filter();
  240. String enc_ex_filters_str = current->get_enc_ex_filter();
  241. if (!updating_enc_filters) {
  242. enc_in_filters->set_text(enc_in_filters_str);
  243. enc_ex_filters->set_text(enc_ex_filters_str);
  244. }
  245. bool enc_pck_mode = current->get_enc_pck();
  246. enc_pck->set_pressed(enc_pck_mode);
  247. enc_directory->set_disabled(!enc_pck_mode);
  248. enc_in_filters->set_editable(enc_pck_mode);
  249. enc_ex_filters->set_editable(enc_pck_mode);
  250. script_key->set_editable(enc_pck_mode);
  251. bool enc_directory_mode = current->get_enc_directory();
  252. enc_directory->set_pressed(enc_directory_mode);
  253. int script_export_mode = current->get_script_export_mode();
  254. script_mode->select(script_export_mode);
  255. String key = current->get_script_encryption_key();
  256. if (!updating_script_key) {
  257. script_key->set_text(key);
  258. }
  259. if (enc_pck_mode) {
  260. script_key->set_editable(true);
  261. bool key_valid = _validate_script_encryption_key(key);
  262. if (key_valid) {
  263. script_key_error->hide();
  264. } else {
  265. script_key_error->show();
  266. }
  267. } else {
  268. script_key->set_editable(false);
  269. script_key_error->hide();
  270. }
  271. updating = false;
  272. }
  273. void ProjectExportDialog::_update_feature_list() {
  274. Ref<EditorExportPreset> current = get_current_preset();
  275. ERR_FAIL_COND(current.is_null());
  276. Set<String> fset;
  277. List<String> features;
  278. current->get_platform()->get_platform_features(&features);
  279. current->get_platform()->get_preset_features(current, &features);
  280. String custom = current->get_custom_features();
  281. Vector<String> custom_list = custom.split(",");
  282. for (int i = 0; i < custom_list.size(); i++) {
  283. String f = custom_list[i].strip_edges();
  284. if (f != String()) {
  285. features.push_back(f);
  286. }
  287. }
  288. for (List<String>::Element *E = features.front(); E; E = E->next()) {
  289. fset.insert(E->get());
  290. }
  291. custom_feature_display->clear();
  292. for (Set<String>::Element *E = fset.front(); E; E = E->next()) {
  293. String f = E->get();
  294. if (E->next()) {
  295. f += ", ";
  296. }
  297. custom_feature_display->add_text(f);
  298. }
  299. }
  300. void ProjectExportDialog::_custom_features_changed(const String &p_text) {
  301. if (updating) {
  302. return;
  303. }
  304. Ref<EditorExportPreset> current = get_current_preset();
  305. ERR_FAIL_COND(current.is_null());
  306. current->set_custom_features(p_text);
  307. _update_feature_list();
  308. }
  309. void ProjectExportDialog::_tab_changed(int) {
  310. _update_feature_list();
  311. }
  312. void ProjectExportDialog::_update_parameters(const String &p_edited_property) {
  313. _update_current_preset();
  314. }
  315. void ProjectExportDialog::_runnable_pressed() {
  316. if (updating) {
  317. return;
  318. }
  319. Ref<EditorExportPreset> current = get_current_preset();
  320. ERR_FAIL_COND(current.is_null());
  321. if (runnable->is_pressed()) {
  322. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  323. Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
  324. if (p->get_platform() == current->get_platform()) {
  325. p->set_runnable(current == p);
  326. }
  327. }
  328. } else {
  329. current->set_runnable(false);
  330. }
  331. _update_presets();
  332. }
  333. void ProjectExportDialog::_name_changed(const String &p_string) {
  334. if (updating) {
  335. return;
  336. }
  337. Ref<EditorExportPreset> current = get_current_preset();
  338. ERR_FAIL_COND(current.is_null());
  339. current->set_name(p_string);
  340. _update_presets();
  341. }
  342. void ProjectExportDialog::set_export_path(const String &p_value) {
  343. Ref<EditorExportPreset> current = get_current_preset();
  344. ERR_FAIL_COND(current.is_null());
  345. current->set_export_path(p_value);
  346. }
  347. String ProjectExportDialog::get_export_path() {
  348. Ref<EditorExportPreset> current = get_current_preset();
  349. ERR_FAIL_COND_V(current.is_null(), String(""));
  350. return current->get_export_path();
  351. }
  352. Ref<EditorExportPreset> ProjectExportDialog::get_current_preset() const {
  353. return EditorExport::get_singleton()->get_export_preset(presets->get_current());
  354. }
  355. void ProjectExportDialog::_export_path_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
  356. if (updating) {
  357. return;
  358. }
  359. Ref<EditorExportPreset> current = get_current_preset();
  360. ERR_FAIL_COND(current.is_null());
  361. current->set_export_path(p_value);
  362. _update_presets();
  363. }
  364. void ProjectExportDialog::_enc_filters_changed(const String &p_filters) {
  365. if (updating) {
  366. return;
  367. }
  368. Ref<EditorExportPreset> current = get_current_preset();
  369. ERR_FAIL_COND(current.is_null());
  370. current->set_enc_in_filter(enc_in_filters->get_text());
  371. current->set_enc_ex_filter(enc_ex_filters->get_text());
  372. updating_enc_filters = true;
  373. _update_current_preset();
  374. updating_enc_filters = false;
  375. }
  376. void ProjectExportDialog::_open_key_help_link() {
  377. OS::get_singleton()->shell_open("https://docs.godotengine.org/en/latest/development/compiling/compiling_with_script_encryption_key.html");
  378. }
  379. void ProjectExportDialog::_enc_pck_changed(bool p_pressed) {
  380. if (updating) {
  381. return;
  382. }
  383. Ref<EditorExportPreset> current = get_current_preset();
  384. ERR_FAIL_COND(current.is_null());
  385. current->set_enc_pck(p_pressed);
  386. enc_directory->set_disabled(!p_pressed);
  387. enc_in_filters->set_editable(p_pressed);
  388. enc_ex_filters->set_editable(p_pressed);
  389. script_key->set_editable(p_pressed);
  390. _update_current_preset();
  391. }
  392. void ProjectExportDialog::_enc_directory_changed(bool p_pressed) {
  393. if (updating) {
  394. return;
  395. }
  396. Ref<EditorExportPreset> current = get_current_preset();
  397. ERR_FAIL_COND(current.is_null());
  398. current->set_enc_directory(p_pressed);
  399. _update_current_preset();
  400. }
  401. void ProjectExportDialog::_script_export_mode_changed(int p_mode) {
  402. if (updating) {
  403. return;
  404. }
  405. Ref<EditorExportPreset> current = get_current_preset();
  406. ERR_FAIL_COND(current.is_null());
  407. current->set_script_export_mode(p_mode);
  408. _update_current_preset();
  409. }
  410. void ProjectExportDialog::_script_encryption_key_changed(const String &p_key) {
  411. if (updating) {
  412. return;
  413. }
  414. Ref<EditorExportPreset> current = get_current_preset();
  415. ERR_FAIL_COND(current.is_null());
  416. current->set_script_encryption_key(p_key);
  417. updating_script_key = true;
  418. _update_current_preset();
  419. updating_script_key = false;
  420. }
  421. bool ProjectExportDialog::_validate_script_encryption_key(const String &p_key) {
  422. bool is_valid = false;
  423. if (!p_key.is_empty() && p_key.is_valid_hex_number(false) && p_key.length() == 64) {
  424. is_valid = true;
  425. }
  426. return is_valid;
  427. }
  428. void ProjectExportDialog::_duplicate_preset() {
  429. Ref<EditorExportPreset> current = get_current_preset();
  430. if (current.is_null()) {
  431. return;
  432. }
  433. Ref<EditorExportPreset> preset = current->get_platform()->create_preset();
  434. ERR_FAIL_COND(!preset.is_valid());
  435. String name = current->get_name() + " (copy)";
  436. bool make_runnable = true;
  437. while (true) {
  438. bool valid = true;
  439. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  440. Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
  441. if (p->get_platform() == preset->get_platform() && p->is_runnable()) {
  442. make_runnable = false;
  443. }
  444. if (p->get_name() == name) {
  445. valid = false;
  446. break;
  447. }
  448. }
  449. if (valid) {
  450. break;
  451. }
  452. name += " (copy)";
  453. }
  454. preset->set_name(name);
  455. if (make_runnable) {
  456. preset->set_runnable(make_runnable);
  457. }
  458. preset->set_export_filter(current->get_export_filter());
  459. preset->set_include_filter(current->get_include_filter());
  460. preset->set_exclude_filter(current->get_exclude_filter());
  461. preset->set_custom_features(current->get_custom_features());
  462. for (const List<PropertyInfo>::Element *E = current->get_properties().front(); E; E = E->next()) {
  463. preset->set(E->get().name, current->get(E->get().name));
  464. }
  465. EditorExport::get_singleton()->add_export_preset(preset);
  466. _update_presets();
  467. _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
  468. }
  469. void ProjectExportDialog::_delete_preset() {
  470. Ref<EditorExportPreset> current = get_current_preset();
  471. if (current.is_null()) {
  472. return;
  473. }
  474. delete_confirm->set_text(vformat(TTR("Delete preset '%s'?"), current->get_name()));
  475. delete_confirm->popup_centered();
  476. }
  477. void ProjectExportDialog::_delete_preset_confirm() {
  478. int idx = presets->get_current();
  479. _edit_preset(-1);
  480. export_button->set_disabled(true);
  481. get_ok_button()->set_disabled(true);
  482. EditorExport::get_singleton()->remove_export_preset(idx);
  483. _update_presets();
  484. }
  485. Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
  486. if (p_from == presets) {
  487. int pos = presets->get_item_at_position(p_point, true);
  488. if (pos >= 0) {
  489. Dictionary d;
  490. d["type"] = "export_preset";
  491. d["preset"] = pos;
  492. HBoxContainer *drag = memnew(HBoxContainer);
  493. TextureRect *tr = memnew(TextureRect);
  494. tr->set_texture(presets->get_item_icon(pos));
  495. drag->add_child(tr);
  496. Label *label = memnew(Label);
  497. label->set_text(presets->get_item_text(pos));
  498. drag->add_child(label);
  499. presets->set_drag_preview(drag);
  500. return d;
  501. }
  502. }
  503. return Variant();
  504. }
  505. bool ProjectExportDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
  506. if (p_from == presets) {
  507. Dictionary d = p_data;
  508. if (!d.has("type") || String(d["type"]) != "export_preset") {
  509. return false;
  510. }
  511. if (presets->get_item_at_position(p_point, true) < 0 && !presets->is_pos_at_end_of_items(p_point)) {
  512. return false;
  513. }
  514. }
  515. return true;
  516. }
  517. void ProjectExportDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
  518. if (p_from == presets) {
  519. Dictionary d = p_data;
  520. int from_pos = d["preset"];
  521. int to_pos = -1;
  522. if (presets->get_item_at_position(p_point, true) >= 0) {
  523. to_pos = presets->get_item_at_position(p_point, true);
  524. }
  525. if (to_pos == -1 && !presets->is_pos_at_end_of_items(p_point)) {
  526. return;
  527. }
  528. if (to_pos == from_pos) {
  529. return;
  530. } else if (to_pos > from_pos) {
  531. to_pos--;
  532. }
  533. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(from_pos);
  534. EditorExport::get_singleton()->remove_export_preset(from_pos);
  535. EditorExport::get_singleton()->add_export_preset(preset, to_pos);
  536. _update_presets();
  537. if (to_pos >= 0) {
  538. _edit_preset(to_pos);
  539. } else {
  540. _edit_preset(presets->get_item_count() - 1);
  541. }
  542. }
  543. }
  544. void ProjectExportDialog::_export_type_changed(int p_which) {
  545. if (updating) {
  546. return;
  547. }
  548. Ref<EditorExportPreset> current = get_current_preset();
  549. if (current.is_null()) {
  550. return;
  551. }
  552. current->set_export_filter(EditorExportPreset::ExportFilter(p_which));
  553. updating = true;
  554. _fill_resource_tree();
  555. updating = false;
  556. }
  557. void ProjectExportDialog::_filter_changed(const String &p_filter) {
  558. if (updating) {
  559. return;
  560. }
  561. Ref<EditorExportPreset> current = get_current_preset();
  562. if (current.is_null()) {
  563. return;
  564. }
  565. current->set_include_filter(include_filters->get_text());
  566. current->set_exclude_filter(exclude_filters->get_text());
  567. }
  568. void ProjectExportDialog::_fill_resource_tree() {
  569. include_files->clear();
  570. include_label->hide();
  571. include_margin->hide();
  572. Ref<EditorExportPreset> current = get_current_preset();
  573. if (current.is_null()) {
  574. return;
  575. }
  576. EditorExportPreset::ExportFilter f = current->get_export_filter();
  577. if (f == EditorExportPreset::EXPORT_ALL_RESOURCES) {
  578. return;
  579. }
  580. include_label->show();
  581. include_margin->show();
  582. TreeItem *root = include_files->create_item();
  583. _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f == EditorExportPreset::EXPORT_SELECTED_SCENES);
  584. }
  585. bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, bool p_only_scenes) {
  586. p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
  587. p_item->set_icon(0, presets->get_theme_icon("folder", "FileDialog"));
  588. p_item->set_text(0, p_dir->get_name() + "/");
  589. p_item->set_editable(0, true);
  590. p_item->set_metadata(0, p_dir->get_path());
  591. bool used = false;
  592. bool checked = true;
  593. for (int i = 0; i < p_dir->get_subdir_count(); i++) {
  594. TreeItem *subdir = include_files->create_item(p_item);
  595. if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes)) {
  596. used = true;
  597. checked = checked && subdir->is_checked(0);
  598. } else {
  599. memdelete(subdir);
  600. }
  601. }
  602. for (int i = 0; i < p_dir->get_file_count(); i++) {
  603. String type = p_dir->get_file_type(i);
  604. if (p_only_scenes && type != "PackedScene") {
  605. continue;
  606. }
  607. TreeItem *file = include_files->create_item(p_item);
  608. file->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
  609. file->set_text(0, p_dir->get_file(i));
  610. String path = p_dir->get_file_path(i);
  611. file->set_icon(0, EditorNode::get_singleton()->get_class_icon(type));
  612. file->set_editable(0, true);
  613. file->set_checked(0, current->has_export_file(path));
  614. file->set_metadata(0, path);
  615. checked = checked && file->is_checked(0);
  616. used = true;
  617. }
  618. p_item->set_checked(0, checked);
  619. return used;
  620. }
  621. void ProjectExportDialog::_tree_changed() {
  622. if (updating) {
  623. return;
  624. }
  625. Ref<EditorExportPreset> current = get_current_preset();
  626. if (current.is_null()) {
  627. return;
  628. }
  629. TreeItem *item = include_files->get_edited();
  630. if (!item) {
  631. return;
  632. }
  633. String path = item->get_metadata(0);
  634. bool added = item->is_checked(0);
  635. if (path.ends_with("/")) {
  636. _check_dir_recursive(item, added);
  637. } else {
  638. if (added) {
  639. current->add_export_file(path);
  640. } else {
  641. current->remove_export_file(path);
  642. }
  643. }
  644. _refresh_parent_checks(item); // Makes parent folder checked if all files/folders are checked.
  645. }
  646. void ProjectExportDialog::_check_dir_recursive(TreeItem *p_dir, bool p_checked) {
  647. for (TreeItem *child = p_dir->get_first_child(); child; child = child->get_next()) {
  648. String path = child->get_metadata(0);
  649. child->set_checked(0, p_checked);
  650. if (path.ends_with("/")) {
  651. _check_dir_recursive(child, p_checked);
  652. } else {
  653. if (p_checked) {
  654. get_current_preset()->add_export_file(path);
  655. } else {
  656. get_current_preset()->remove_export_file(path);
  657. }
  658. }
  659. }
  660. }
  661. void ProjectExportDialog::_refresh_parent_checks(TreeItem *p_item) {
  662. TreeItem *parent = p_item->get_parent();
  663. if (!parent) {
  664. return;
  665. }
  666. bool checked = true;
  667. for (TreeItem *child = parent->get_first_child(); child; child = child->get_next()) {
  668. checked = checked && child->is_checked(0);
  669. if (!checked) {
  670. break;
  671. }
  672. }
  673. parent->set_checked(0, checked);
  674. _refresh_parent_checks(parent);
  675. }
  676. void ProjectExportDialog::_export_pck_zip() {
  677. Ref<EditorExportPreset> current = get_current_preset();
  678. ERR_FAIL_COND(current.is_null());
  679. String dir = current->get_export_path().get_base_dir();
  680. export_pck_zip->set_current_dir(dir);
  681. export_pck_zip->popup_file_dialog();
  682. }
  683. void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
  684. Ref<EditorExportPreset> current = get_current_preset();
  685. ERR_FAIL_COND(current.is_null());
  686. Ref<EditorExportPlatform> platform = current->get_platform();
  687. ERR_FAIL_COND(platform.is_null());
  688. if (p_path.ends_with(".zip")) {
  689. platform->export_zip(current, export_pck_zip_debug->is_pressed(), p_path);
  690. } else if (p_path.ends_with(".pck")) {
  691. platform->export_pack(current, export_pck_zip_debug->is_pressed(), p_path);
  692. }
  693. }
  694. void ProjectExportDialog::_open_export_template_manager() {
  695. EditorNode::get_singleton()->open_export_template_manager();
  696. hide();
  697. }
  698. void ProjectExportDialog::_validate_export_path(const String &p_path) {
  699. // Disable export via OK button or Enter key if LineEdit has an empty filename
  700. bool invalid_path = (p_path.get_file().get_basename() == "");
  701. // Check if state change before needlessly messing with signals
  702. if (invalid_path && export_project->get_ok_button()->is_disabled()) {
  703. return;
  704. }
  705. if (!invalid_path && !export_project->get_ok_button()->is_disabled()) {
  706. return;
  707. }
  708. if (invalid_path) {
  709. export_project->get_ok_button()->set_disabled(true);
  710. export_project->get_line_edit()->disconnect("text_entered", Callable(export_project, "_file_entered"));
  711. } else {
  712. export_project->get_ok_button()->set_disabled(false);
  713. export_project->get_line_edit()->connect("text_entered", Callable(export_project, "_file_entered"));
  714. }
  715. }
  716. void ProjectExportDialog::_export_project() {
  717. Ref<EditorExportPreset> current = get_current_preset();
  718. ERR_FAIL_COND(current.is_null());
  719. Ref<EditorExportPlatform> platform = current->get_platform();
  720. ERR_FAIL_COND(platform.is_null());
  721. export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  722. export_project->clear_filters();
  723. List<String> extension_list = platform->get_binary_extensions(current);
  724. for (int i = 0; i < extension_list.size(); i++) {
  725. export_project->add_filter("*." + extension_list[i] + " ; " + platform->get_name() + " Export");
  726. }
  727. if (current->get_export_path() != "") {
  728. export_project->set_current_path(current->get_export_path());
  729. } else {
  730. if (extension_list.size() >= 1) {
  731. export_project->set_current_file(default_filename + "." + extension_list[0]);
  732. } else {
  733. export_project->set_current_file(default_filename);
  734. }
  735. }
  736. // Ensure that signal is connected if previous attempt left it disconnected
  737. // with _validate_export_path.
  738. // FIXME: This is a hack, we should instead change EditorFileDialog to allow
  739. // disabling validation by the "text_entered" signal.
  740. if (!export_project->get_line_edit()->is_connected("text_entered", Callable(export_project, "_file_entered"))) {
  741. export_project->get_ok_button()->set_disabled(false);
  742. export_project->get_line_edit()->connect("text_entered", Callable(export_project, "_file_entered"));
  743. }
  744. export_project->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
  745. export_project->popup_file_dialog();
  746. }
  747. void ProjectExportDialog::_export_project_to_path(const String &p_path) {
  748. // Save this name for use in future exports (but drop the file extension)
  749. default_filename = p_path.get_file().get_basename();
  750. EditorSettings::get_singleton()->set_project_metadata("export_options", "default_filename", default_filename);
  751. Ref<EditorExportPreset> current = get_current_preset();
  752. ERR_FAIL_COND(current.is_null());
  753. Ref<EditorExportPlatform> platform = current->get_platform();
  754. ERR_FAIL_COND(platform.is_null());
  755. current->set_export_path(p_path);
  756. Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0);
  757. if (err != OK && err != ERR_SKIP) {
  758. if (err == ERR_FILE_NOT_FOUND) {
  759. error_dialog->set_text(vformat(TTR("Failed to export the project for platform '%s'.\nExport templates seem to be missing or invalid."), platform->get_name()));
  760. } else { // Assume misconfiguration. FIXME: Improve error handling and preset config validation.
  761. error_dialog->set_text(vformat(TTR("Failed to export the project for platform '%s'.\nThis might be due to a configuration issue in the export preset or your export settings."), platform->get_name()));
  762. }
  763. ERR_PRINT(vformat("Failed to export the project for platform '%s'.", platform->get_name()));
  764. error_dialog->show();
  765. error_dialog->popup_centered(Size2(300, 80));
  766. }
  767. }
  768. void ProjectExportDialog::_export_all_dialog() {
  769. export_all_dialog->show();
  770. export_all_dialog->popup_centered(Size2(300, 80));
  771. }
  772. void ProjectExportDialog::_export_all_dialog_action(const String &p_str) {
  773. export_all_dialog->hide();
  774. _export_all(p_str != "release");
  775. }
  776. void ProjectExportDialog::_export_all(bool p_debug) {
  777. String mode = p_debug ? TTR("Debug") : TTR("Release");
  778. EditorProgress ep("exportall", TTR("Exporting All") + " " + mode, EditorExport::get_singleton()->get_export_preset_count(), true);
  779. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  780. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
  781. ERR_FAIL_COND(preset.is_null());
  782. Ref<EditorExportPlatform> platform = preset->get_platform();
  783. ERR_FAIL_COND(platform.is_null());
  784. ep.step(preset->get_name(), i);
  785. Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
  786. if (err != OK && err != ERR_SKIP) {
  787. if (err == ERR_FILE_BAD_PATH) {
  788. error_dialog->set_text(TTR("The given export path doesn't exist:") + "\n" + preset->get_export_path().get_base_dir());
  789. } else {
  790. error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " " + platform->get_name());
  791. }
  792. error_dialog->show();
  793. error_dialog->popup_centered(Size2(300, 80));
  794. ERR_PRINT("Failed to export project");
  795. }
  796. }
  797. }
  798. void ProjectExportDialog::_bind_methods() {
  799. ClassDB::bind_method("get_drag_data_fw", &ProjectExportDialog::get_drag_data_fw);
  800. ClassDB::bind_method("can_drop_data_fw", &ProjectExportDialog::can_drop_data_fw);
  801. ClassDB::bind_method("drop_data_fw", &ProjectExportDialog::drop_data_fw);
  802. ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all);
  803. ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path);
  804. ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
  805. ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);
  806. ADD_PROPERTY(PropertyInfo(Variant::STRING, "export_path"), "set_export_path", "get_export_path");
  807. }
  808. ProjectExportDialog::ProjectExportDialog() {
  809. set_title(TTR("Export"));
  810. VBoxContainer *main_vb = memnew(VBoxContainer);
  811. main_vb->connect("theme_changed", callable_mp(this, &ProjectExportDialog::_theme_changed));
  812. add_child(main_vb);
  813. HSplitContainer *hbox = memnew(HSplitContainer);
  814. main_vb->add_child(hbox);
  815. hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  816. // Presets list.
  817. VBoxContainer *preset_vb = memnew(VBoxContainer);
  818. preset_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  819. hbox->add_child(preset_vb);
  820. HBoxContainer *preset_hb = memnew(HBoxContainer);
  821. preset_hb->add_child(memnew(Label(TTR("Presets"))));
  822. preset_hb->add_spacer();
  823. preset_vb->add_child(preset_hb);
  824. add_preset = memnew(MenuButton);
  825. add_preset->set_text(TTR("Add..."));
  826. add_preset->get_popup()->connect("index_pressed", callable_mp(this, &ProjectExportDialog::_add_preset));
  827. preset_hb->add_child(add_preset);
  828. MarginContainer *mc = memnew(MarginContainer);
  829. preset_vb->add_child(mc);
  830. mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  831. presets = memnew(ItemList);
  832. #ifndef _MSC_VER
  833. #warning must reimplement drag forward
  834. #endif
  835. //presets->set_drag_forwarding(this);
  836. mc->add_child(presets);
  837. presets->connect("item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset));
  838. duplicate_preset = memnew(Button);
  839. duplicate_preset->set_flat(true);
  840. preset_hb->add_child(duplicate_preset);
  841. duplicate_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_duplicate_preset));
  842. delete_preset = memnew(Button);
  843. delete_preset->set_flat(true);
  844. preset_hb->add_child(delete_preset);
  845. delete_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_delete_preset));
  846. // Preset settings.
  847. VBoxContainer *settings_vb = memnew(VBoxContainer);
  848. settings_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  849. hbox->add_child(settings_vb);
  850. name = memnew(LineEdit);
  851. settings_vb->add_margin_child(TTR("Name:"), name);
  852. name->connect("text_changed", callable_mp(this, &ProjectExportDialog::_name_changed));
  853. runnable = memnew(CheckButton);
  854. runnable->set_text(TTR("Runnable"));
  855. runnable->set_tooltip(TTR("If checked, the preset will be available for use in one-click deploy.\nOnly one preset per platform may be marked as runnable."));
  856. runnable->connect("pressed", callable_mp(this, &ProjectExportDialog::_runnable_pressed));
  857. settings_vb->add_child(runnable);
  858. export_path = memnew(EditorPropertyPath);
  859. settings_vb->add_child(export_path);
  860. export_path->set_label(TTR("Export Path"));
  861. export_path->set_object_and_property(this, "export_path");
  862. export_path->set_save_mode();
  863. export_path->connect("property_changed", callable_mp(this, &ProjectExportDialog::_export_path_changed));
  864. // Subsections.
  865. sections = memnew(TabContainer);
  866. sections->set_tab_align(TabContainer::ALIGN_LEFT);
  867. sections->set_use_hidden_tabs_for_min_size(true);
  868. settings_vb->add_child(sections);
  869. sections->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  870. // Main preset parameters.
  871. parameters = memnew(EditorInspector);
  872. sections->add_child(parameters);
  873. parameters->set_name(TTR("Options"));
  874. parameters->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  875. parameters->connect("property_edited", callable_mp(this, &ProjectExportDialog::_update_parameters));
  876. EditorExport::get_singleton()->connect("export_presets_updated", callable_mp(this, &ProjectExportDialog::_force_update_current_preset_parameters));
  877. // Resources export parameters.
  878. VBoxContainer *resources_vb = memnew(VBoxContainer);
  879. sections->add_child(resources_vb);
  880. resources_vb->set_name(TTR("Resources"));
  881. export_filter = memnew(OptionButton);
  882. export_filter->add_item(TTR("Export all resources in the project"));
  883. export_filter->add_item(TTR("Export selected scenes (and dependencies)"));
  884. export_filter->add_item(TTR("Export selected resources (and dependencies)"));
  885. export_filter->add_item(TTR("Export all resources in the project except resources checked below"));
  886. resources_vb->add_margin_child(TTR("Export Mode:"), export_filter);
  887. export_filter->connect("item_selected", callable_mp(this, &ProjectExportDialog::_export_type_changed));
  888. include_label = memnew(Label);
  889. include_label->set_text(TTR("Resources to export:"));
  890. resources_vb->add_child(include_label);
  891. include_margin = memnew(MarginContainer);
  892. include_margin->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  893. resources_vb->add_child(include_margin);
  894. include_files = memnew(Tree);
  895. include_margin->add_child(include_files);
  896. include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed));
  897. include_filters = memnew(LineEdit);
  898. resources_vb->add_margin_child(
  899. TTR("Filters to export non-resource files/folders\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
  900. include_filters);
  901. include_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_filter_changed));
  902. exclude_filters = memnew(LineEdit);
  903. resources_vb->add_margin_child(
  904. TTR("Filters to exclude files/folders from project\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
  905. exclude_filters);
  906. exclude_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_filter_changed));
  907. script_mode = memnew(OptionButton);
  908. resources_vb->add_margin_child(TTR("Script Export Mode:"), script_mode);
  909. script_mode->add_item(TTR("Text"), (int)EditorExportPreset::MODE_SCRIPT_TEXT);
  910. script_mode->add_item(TTR("Compiled"), (int)EditorExportPreset::MODE_SCRIPT_COMPILED);
  911. script_mode->connect("item_selected", callable_mp(this, &ProjectExportDialog::_script_export_mode_changed));
  912. // Feature tags.
  913. VBoxContainer *feature_vb = memnew(VBoxContainer);
  914. feature_vb->set_name(TTR("Features"));
  915. custom_features = memnew(LineEdit);
  916. custom_features->connect("text_changed", callable_mp(this, &ProjectExportDialog::_custom_features_changed));
  917. feature_vb->add_margin_child(TTR("Custom (comma-separated):"), custom_features);
  918. custom_feature_display = memnew(RichTextLabel);
  919. custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  920. feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true);
  921. sections->add_child(feature_vb);
  922. // Script export parameters.
  923. updating_script_key = false;
  924. updating_enc_filters = false;
  925. VBoxContainer *sec_vb = memnew(VBoxContainer);
  926. sec_vb->set_name(TTR("Encryption"));
  927. enc_pck = memnew(CheckButton);
  928. enc_pck->connect("toggled", callable_mp(this, &ProjectExportDialog::_enc_pck_changed));
  929. enc_pck->set_text(TTR("Encrypt exported PCK"));
  930. sec_vb->add_child(enc_pck);
  931. enc_directory = memnew(CheckButton);
  932. enc_directory->connect("toggled", callable_mp(this, &ProjectExportDialog::_enc_directory_changed));
  933. enc_directory->set_text("Encrypt index (file names and info).");
  934. sec_vb->add_child(enc_directory);
  935. enc_in_filters = memnew(LineEdit);
  936. enc_in_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_enc_filters_changed));
  937. sec_vb->add_margin_child(
  938. TTR("Filters to include files/folders\n(comma-separated, e.g: *.tscn, *.tres, scenes/*)"),
  939. enc_in_filters);
  940. enc_ex_filters = memnew(LineEdit);
  941. enc_ex_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_enc_filters_changed));
  942. sec_vb->add_margin_child(
  943. TTR("Filters to exclude files/folders\n(comma-separated, e.g: *.stex, *.import, music/*)"),
  944. enc_ex_filters);
  945. script_key = memnew(LineEdit);
  946. script_key->connect("text_changed", callable_mp(this, &ProjectExportDialog::_script_encryption_key_changed));
  947. script_key_error = memnew(Label);
  948. script_key_error->set_text("- " + TTR("Invalid Encryption Key (must be 64 characters long)"));
  949. script_key_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color("error_color", "Editor"));
  950. sec_vb->add_margin_child(TTR("Encryption Key (256-bits as hex):"), script_key);
  951. sec_vb->add_child(script_key_error);
  952. sections->add_child(sec_vb);
  953. Label *sec_info = memnew(Label);
  954. sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source."));
  955. sec_vb->add_child(sec_info);
  956. LinkButton *sec_more_info = memnew(LinkButton);
  957. sec_more_info->set_text(TTR("More Info..."));
  958. sec_more_info->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_key_help_link));
  959. sec_vb->add_child(sec_more_info);
  960. sections->connect("tab_changed", callable_mp(this, &ProjectExportDialog::_tab_changed));
  961. // Disable by default.
  962. name->set_editable(false);
  963. export_path->hide();
  964. runnable->set_disabled(true);
  965. duplicate_preset->set_disabled(true);
  966. delete_preset->set_disabled(true);
  967. script_key_error->hide();
  968. sections->hide();
  969. parameters->edit(nullptr);
  970. // Deletion dialog.
  971. delete_confirm = memnew(ConfirmationDialog);
  972. add_child(delete_confirm);
  973. delete_confirm->get_ok_button()->set_text(TTR("Delete"));
  974. delete_confirm->connect("confirmed", callable_mp(this, &ProjectExportDialog::_delete_preset_confirm));
  975. // Export buttons, dialogs and errors.
  976. updating = false;
  977. get_cancel_button()->set_text(TTR("Close"));
  978. get_ok_button()->set_text(TTR("Export PCK/Zip"));
  979. export_button = add_button(TTR("Export Project"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export");
  980. export_button->connect("pressed", callable_mp(this, &ProjectExportDialog::_export_project));
  981. // Disable initially before we select a valid preset
  982. export_button->set_disabled(true);
  983. get_ok_button()->set_disabled(true);
  984. export_all_dialog = memnew(ConfirmationDialog);
  985. add_child(export_all_dialog);
  986. export_all_dialog->set_title("Export All");
  987. export_all_dialog->set_text(TTR("Export mode?"));
  988. export_all_dialog->get_ok_button()->hide();
  989. export_all_dialog->add_button(TTR("Debug"), true, "debug");
  990. export_all_dialog->add_button(TTR("Release"), true, "release");
  991. export_all_dialog->connect("custom_action", callable_mp(this, &ProjectExportDialog::_export_all_dialog_action));
  992. export_all_button = add_button(TTR("Export All"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export");
  993. export_all_button->connect("pressed", callable_mp(this, &ProjectExportDialog::_export_all_dialog));
  994. export_all_button->set_disabled(true);
  995. export_pck_zip = memnew(EditorFileDialog);
  996. export_pck_zip->add_filter("*.zip ; " + TTR("ZIP File"));
  997. export_pck_zip->add_filter("*.pck ; " + TTR("Godot Game Pack"));
  998. export_pck_zip->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  999. export_pck_zip->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
  1000. add_child(export_pck_zip);
  1001. export_pck_zip->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_pck_zip_selected));
  1002. export_error = memnew(Label);
  1003. main_vb->add_child(export_error);
  1004. export_error->hide();
  1005. export_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color("error_color", "Editor"));
  1006. export_templates_error = memnew(HBoxContainer);
  1007. main_vb->add_child(export_templates_error);
  1008. export_templates_error->hide();
  1009. Label *export_error2 = memnew(Label);
  1010. export_templates_error->add_child(export_error2);
  1011. export_error2->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color("error_color", "Editor"));
  1012. export_error2->set_text(" - " + TTR("Export templates for this platform are missing:") + " ");
  1013. error_dialog = memnew(AcceptDialog);
  1014. error_dialog->set_title("Error");
  1015. error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " ");
  1016. main_vb->add_child(error_dialog);
  1017. error_dialog->hide();
  1018. LinkButton *download_templates = memnew(LinkButton);
  1019. download_templates->set_text(TTR("Manage Export Templates"));
  1020. download_templates->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
  1021. export_templates_error->add_child(download_templates);
  1022. download_templates->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_export_template_manager));
  1023. export_project = memnew(EditorFileDialog);
  1024. export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  1025. add_child(export_project);
  1026. export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path));
  1027. export_project->get_line_edit()->connect("text_changed", callable_mp(this, &ProjectExportDialog::_validate_export_path));
  1028. export_debug = memnew(CheckBox);
  1029. export_debug->set_text(TTR("Export With Debug"));
  1030. export_debug->set_pressed(true);
  1031. export_project->get_vbox()->add_child(export_debug);
  1032. export_pck_zip_debug = memnew(CheckBox);
  1033. export_pck_zip_debug->set_text(TTR("Export With Debug"));
  1034. export_pck_zip_debug->set_pressed(true);
  1035. export_pck_zip->get_vbox()->add_child(export_pck_zip_debug);
  1036. set_hide_on_ok(false);
  1037. editor_icons = "EditorIcons";
  1038. default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", "");
  1039. // If no default set, use project name
  1040. if (default_filename == "") {
  1041. // If no project name defined, use a sane default
  1042. default_filename = ProjectSettings::get_singleton()->get("application/config/name");
  1043. if (default_filename == "") {
  1044. default_filename = "UnnamedProject";
  1045. }
  1046. }
  1047. _update_export_all();
  1048. }
  1049. ProjectExportDialog::~ProjectExportDialog() {
  1050. }