2
0

project_export.cpp 43 KB

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