project_export.cpp 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418
  1. /**************************************************************************/
  2. /* project_export.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "project_export.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/version.h"
  33. #include "editor/editor_file_dialog.h"
  34. #include "editor/editor_file_system.h"
  35. #include "editor/editor_node.h"
  36. #include "editor/editor_properties.h"
  37. #include "editor/editor_scale.h"
  38. #include "editor/editor_settings.h"
  39. #include "editor/export/editor_export.h"
  40. #include "scene/gui/check_box.h"
  41. #include "scene/gui/check_button.h"
  42. #include "scene/gui/item_list.h"
  43. #include "scene/gui/link_button.h"
  44. #include "scene/gui/menu_button.h"
  45. #include "scene/gui/option_button.h"
  46. #include "scene/gui/popup_menu.h"
  47. #include "scene/gui/split_container.h"
  48. #include "scene/gui/texture_rect.h"
  49. #include "scene/gui/tree.h"
  50. void ProjectExportDialog::_theme_changed() {
  51. duplicate_preset->set_icon(presets->get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")));
  52. delete_preset->set_icon(presets->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
  53. }
  54. void ProjectExportDialog::_notification(int p_what) {
  55. switch (p_what) {
  56. case NOTIFICATION_VISIBILITY_CHANGED: {
  57. if (!is_visible()) {
  58. EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "export", Rect2(get_position(), get_size()));
  59. }
  60. } break;
  61. case NOTIFICATION_READY: {
  62. duplicate_preset->set_icon(presets->get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")));
  63. delete_preset->set_icon(presets->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
  64. connect("confirmed", callable_mp(this, &ProjectExportDialog::_export_pck_zip));
  65. _update_export_all();
  66. } break;
  67. case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
  68. parameters->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
  69. } break;
  70. }
  71. }
  72. void ProjectExportDialog::popup_export() {
  73. add_preset->get_popup()->clear();
  74. for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) {
  75. Ref<EditorExportPlatform> plat = EditorExport::get_singleton()->get_export_platform(i);
  76. add_preset->get_popup()->add_icon_item(plat->get_logo(), plat->get_name());
  77. }
  78. _update_presets();
  79. if (presets->get_current() >= 0) {
  80. _update_current_preset(); // triggers rescan for templates if newly installed
  81. }
  82. // Restore valid window bounds or pop up at default size.
  83. Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "export", Rect2());
  84. if (saved_size != Rect2()) {
  85. popup(saved_size);
  86. } else {
  87. popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8);
  88. }
  89. }
  90. void ProjectExportDialog::_add_preset(int p_platform) {
  91. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_platform(p_platform)->create_preset();
  92. ERR_FAIL_COND(!preset.is_valid());
  93. String preset_name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name();
  94. bool make_runnable = true;
  95. int attempt = 1;
  96. while (true) {
  97. bool valid = true;
  98. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  99. Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
  100. if (p->get_platform() == preset->get_platform() && p->is_runnable()) {
  101. make_runnable = false;
  102. }
  103. if (p->get_name() == preset_name) {
  104. valid = false;
  105. break;
  106. }
  107. }
  108. if (valid) {
  109. break;
  110. }
  111. attempt++;
  112. preset_name = EditorExport::get_singleton()->get_export_platform(p_platform)->get_name() + " " + itos(attempt);
  113. }
  114. preset->set_name(preset_name);
  115. if (make_runnable) {
  116. preset->set_runnable(make_runnable);
  117. }
  118. EditorExport::get_singleton()->add_export_preset(preset);
  119. _update_presets();
  120. _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
  121. }
  122. void ProjectExportDialog::_force_update_current_preset_parameters() {
  123. // Force the parameters section to refresh its UI.
  124. parameters->edit(nullptr);
  125. _update_current_preset();
  126. }
  127. void ProjectExportDialog::_update_current_preset() {
  128. _edit_preset(presets->get_current());
  129. }
  130. void ProjectExportDialog::_update_presets() {
  131. updating = true;
  132. Ref<EditorExportPreset> current;
  133. if (presets->get_current() >= 0 && presets->get_current() < presets->get_item_count()) {
  134. current = get_current_preset();
  135. }
  136. int current_idx = -1;
  137. presets->clear();
  138. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  139. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
  140. if (preset == current) {
  141. current_idx = i;
  142. }
  143. String preset_name = preset->get_name();
  144. if (preset->is_runnable()) {
  145. preset_name += " (" + TTR("Runnable") + ")";
  146. }
  147. preset->update_files();
  148. presets->add_item(preset_name, preset->get_platform()->get_logo());
  149. }
  150. if (current_idx != -1) {
  151. presets->select(current_idx);
  152. }
  153. updating = false;
  154. }
  155. void ProjectExportDialog::_update_export_all() {
  156. bool can_export = EditorExport::get_singleton()->get_export_preset_count() > 0;
  157. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  158. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
  159. bool needs_templates;
  160. String error;
  161. if (preset->get_export_path().is_empty() || !preset->get_platform()->can_export(preset, error, needs_templates)) {
  162. can_export = false;
  163. break;
  164. }
  165. }
  166. export_all_button->set_disabled(!can_export);
  167. if (can_export) {
  168. export_all_button->set_tooltip_text(TTR("Export the project for all the presets defined."));
  169. } else {
  170. export_all_button->set_tooltip_text(TTR("All presets must have an export path defined for Export All to work."));
  171. }
  172. }
  173. void ProjectExportDialog::_edit_preset(int p_index) {
  174. if (p_index < 0 || p_index >= presets->get_item_count()) {
  175. name->set_text("");
  176. name->set_editable(false);
  177. export_path->hide();
  178. runnable->set_disabled(true);
  179. parameters->edit(nullptr);
  180. presets->deselect_all();
  181. duplicate_preset->set_disabled(true);
  182. delete_preset->set_disabled(true);
  183. sections->hide();
  184. export_error->hide();
  185. export_templates_error->hide();
  186. return;
  187. }
  188. Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(p_index);
  189. ERR_FAIL_COND(current.is_null());
  190. updating = true;
  191. presets->select(p_index);
  192. sections->show();
  193. name->set_editable(true);
  194. export_path->show();
  195. duplicate_preset->set_disabled(false);
  196. delete_preset->set_disabled(false);
  197. get_ok_button()->set_disabled(false);
  198. name->set_text(current->get_name());
  199. List<String> extension_list = current->get_platform()->get_binary_extensions(current);
  200. Vector<String> extension_vector;
  201. for (int i = 0; i < extension_list.size(); i++) {
  202. extension_vector.push_back("*." + extension_list[i]);
  203. }
  204. export_path->setup(extension_vector, false, true);
  205. export_path->update_property();
  206. runnable->set_disabled(false);
  207. runnable->set_pressed(current->is_runnable());
  208. parameters->edit(current.ptr());
  209. export_filter->select(current->get_export_filter());
  210. include_filters->set_text(current->get_include_filter());
  211. exclude_filters->set_text(current->get_exclude_filter());
  212. server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED);
  213. _fill_resource_tree();
  214. bool needs_templates;
  215. String error;
  216. if (!current->get_platform()->can_export(current, error, needs_templates)) {
  217. if (!error.is_empty()) {
  218. Vector<String> items = error.split("\n", false);
  219. error = "";
  220. for (int i = 0; i < items.size(); i++) {
  221. if (i > 0) {
  222. error += "\n";
  223. }
  224. error += " - " + items[i];
  225. }
  226. export_error->set_text(error);
  227. export_error->show();
  228. } else {
  229. export_error->hide();
  230. }
  231. if (needs_templates) {
  232. export_templates_error->show();
  233. } else {
  234. export_templates_error->hide();
  235. }
  236. export_warning->hide();
  237. export_button->set_disabled(true);
  238. } else {
  239. if (error != String()) {
  240. Vector<String> items = error.split("\n", false);
  241. error = "";
  242. for (int i = 0; i < items.size(); i++) {
  243. if (i > 0) {
  244. error += "\n";
  245. }
  246. error += " - " + items[i];
  247. }
  248. export_warning->set_text(error);
  249. export_warning->show();
  250. } else {
  251. export_warning->hide();
  252. }
  253. export_error->hide();
  254. export_templates_error->hide();
  255. export_button->set_disabled(false);
  256. }
  257. custom_features->set_text(current->get_custom_features());
  258. _update_feature_list();
  259. _update_export_all();
  260. child_controls_changed();
  261. String enc_in_filters_str = current->get_enc_in_filter();
  262. String enc_ex_filters_str = current->get_enc_ex_filter();
  263. if (!updating_enc_filters) {
  264. enc_in_filters->set_text(enc_in_filters_str);
  265. enc_ex_filters->set_text(enc_ex_filters_str);
  266. }
  267. bool enc_pck_mode = current->get_enc_pck();
  268. enc_pck->set_pressed(enc_pck_mode);
  269. enc_directory->set_disabled(!enc_pck_mode);
  270. enc_in_filters->set_editable(enc_pck_mode);
  271. enc_ex_filters->set_editable(enc_pck_mode);
  272. script_key->set_editable(enc_pck_mode);
  273. bool enc_directory_mode = current->get_enc_directory();
  274. enc_directory->set_pressed(enc_directory_mode);
  275. String key = current->get_script_encryption_key();
  276. if (!updating_script_key) {
  277. script_key->set_text(key);
  278. }
  279. if (enc_pck_mode) {
  280. script_key->set_editable(true);
  281. bool key_valid = _validate_script_encryption_key(key);
  282. if (key_valid) {
  283. script_key_error->hide();
  284. } else {
  285. script_key_error->show();
  286. }
  287. } else {
  288. script_key->set_editable(false);
  289. script_key_error->hide();
  290. }
  291. updating = false;
  292. }
  293. void ProjectExportDialog::_update_feature_list() {
  294. Ref<EditorExportPreset> current = get_current_preset();
  295. ERR_FAIL_COND(current.is_null());
  296. RBSet<String> fset;
  297. List<String> features;
  298. current->get_platform()->get_platform_features(&features);
  299. current->get_platform()->get_preset_features(current, &features);
  300. String custom = current->get_custom_features();
  301. Vector<String> custom_list = custom.split(",");
  302. for (int i = 0; i < custom_list.size(); i++) {
  303. String f = custom_list[i].strip_edges();
  304. if (!f.is_empty()) {
  305. features.push_back(f);
  306. }
  307. }
  308. for (const String &E : features) {
  309. fset.insert(E);
  310. }
  311. custom_feature_display->clear();
  312. String text;
  313. bool first = true;
  314. for (const String &E : fset) {
  315. if (!first) {
  316. text += ", ";
  317. } else {
  318. first = false;
  319. }
  320. text += E;
  321. }
  322. custom_feature_display->add_text(text);
  323. }
  324. void ProjectExportDialog::_custom_features_changed(const String &p_text) {
  325. if (updating) {
  326. return;
  327. }
  328. Ref<EditorExportPreset> current = get_current_preset();
  329. ERR_FAIL_COND(current.is_null());
  330. current->set_custom_features(p_text);
  331. _update_feature_list();
  332. }
  333. void ProjectExportDialog::_tab_changed(int) {
  334. _update_feature_list();
  335. }
  336. void ProjectExportDialog::_update_parameters(const String &p_edited_property) {
  337. _update_current_preset();
  338. }
  339. void ProjectExportDialog::_runnable_pressed() {
  340. if (updating) {
  341. return;
  342. }
  343. Ref<EditorExportPreset> current = get_current_preset();
  344. ERR_FAIL_COND(current.is_null());
  345. if (runnable->is_pressed()) {
  346. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  347. Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
  348. if (p->get_platform() == current->get_platform()) {
  349. p->set_runnable(current == p);
  350. }
  351. }
  352. } else {
  353. current->set_runnable(false);
  354. }
  355. _update_presets();
  356. }
  357. void ProjectExportDialog::_name_changed(const String &p_string) {
  358. if (updating) {
  359. return;
  360. }
  361. Ref<EditorExportPreset> current = get_current_preset();
  362. ERR_FAIL_COND(current.is_null());
  363. current->set_name(p_string);
  364. _update_presets();
  365. }
  366. void ProjectExportDialog::set_export_path(const String &p_value) {
  367. Ref<EditorExportPreset> current = get_current_preset();
  368. ERR_FAIL_COND(current.is_null());
  369. current->set_export_path(p_value);
  370. }
  371. String ProjectExportDialog::get_export_path() {
  372. Ref<EditorExportPreset> current = get_current_preset();
  373. ERR_FAIL_COND_V(current.is_null(), String(""));
  374. return current->get_export_path();
  375. }
  376. Ref<EditorExportPreset> ProjectExportDialog::get_current_preset() const {
  377. return EditorExport::get_singleton()->get_export_preset(presets->get_current());
  378. }
  379. void ProjectExportDialog::_export_path_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {
  380. if (updating) {
  381. return;
  382. }
  383. Ref<EditorExportPreset> current = get_current_preset();
  384. ERR_FAIL_COND(current.is_null());
  385. current->set_export_path(p_value);
  386. _update_presets();
  387. _update_export_all();
  388. }
  389. void ProjectExportDialog::_enc_filters_changed(const String &p_filters) {
  390. if (updating) {
  391. return;
  392. }
  393. Ref<EditorExportPreset> current = get_current_preset();
  394. ERR_FAIL_COND(current.is_null());
  395. current->set_enc_in_filter(enc_in_filters->get_text());
  396. current->set_enc_ex_filter(enc_ex_filters->get_text());
  397. updating_enc_filters = true;
  398. _update_current_preset();
  399. updating_enc_filters = false;
  400. }
  401. void ProjectExportDialog::_open_key_help_link() {
  402. OS::get_singleton()->shell_open(vformat("%s/contributing/development/compiling/compiling_with_script_encryption_key.html", VERSION_DOCS_URL));
  403. }
  404. void ProjectExportDialog::_enc_pck_changed(bool p_pressed) {
  405. if (updating) {
  406. return;
  407. }
  408. Ref<EditorExportPreset> current = get_current_preset();
  409. ERR_FAIL_COND(current.is_null());
  410. current->set_enc_pck(p_pressed);
  411. enc_directory->set_disabled(!p_pressed);
  412. enc_in_filters->set_editable(p_pressed);
  413. enc_ex_filters->set_editable(p_pressed);
  414. script_key->set_editable(p_pressed);
  415. _update_current_preset();
  416. }
  417. void ProjectExportDialog::_enc_directory_changed(bool p_pressed) {
  418. if (updating) {
  419. return;
  420. }
  421. Ref<EditorExportPreset> current = get_current_preset();
  422. ERR_FAIL_COND(current.is_null());
  423. current->set_enc_directory(p_pressed);
  424. _update_current_preset();
  425. }
  426. void ProjectExportDialog::_script_encryption_key_changed(const String &p_key) {
  427. if (updating) {
  428. return;
  429. }
  430. Ref<EditorExportPreset> current = get_current_preset();
  431. ERR_FAIL_COND(current.is_null());
  432. current->set_script_encryption_key(p_key);
  433. updating_script_key = true;
  434. _update_current_preset();
  435. updating_script_key = false;
  436. }
  437. bool ProjectExportDialog::_validate_script_encryption_key(const String &p_key) {
  438. bool is_valid = false;
  439. if (!p_key.is_empty() && p_key.is_valid_hex_number(false) && p_key.length() == 64) {
  440. is_valid = true;
  441. }
  442. return is_valid;
  443. }
  444. void ProjectExportDialog::_duplicate_preset() {
  445. Ref<EditorExportPreset> current = get_current_preset();
  446. if (current.is_null()) {
  447. return;
  448. }
  449. Ref<EditorExportPreset> preset = current->get_platform()->create_preset();
  450. ERR_FAIL_COND(!preset.is_valid());
  451. String preset_name = current->get_name() + " (copy)";
  452. bool make_runnable = true;
  453. while (true) {
  454. bool valid = true;
  455. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  456. Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i);
  457. if (p->get_platform() == preset->get_platform() && p->is_runnable()) {
  458. make_runnable = false;
  459. }
  460. if (p->get_name() == preset_name) {
  461. valid = false;
  462. break;
  463. }
  464. }
  465. if (valid) {
  466. break;
  467. }
  468. preset_name += " (copy)";
  469. }
  470. preset->set_name(preset_name);
  471. if (make_runnable) {
  472. preset->set_runnable(make_runnable);
  473. }
  474. preset->set_dedicated_server(current->is_dedicated_server());
  475. preset->set_export_filter(current->get_export_filter());
  476. preset->set_include_filter(current->get_include_filter());
  477. preset->set_exclude_filter(current->get_exclude_filter());
  478. preset->set_custom_features(current->get_custom_features());
  479. for (const PropertyInfo &E : current->get_properties()) {
  480. preset->set(E.name, current->get(E.name));
  481. }
  482. EditorExport::get_singleton()->add_export_preset(preset);
  483. _update_presets();
  484. _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1);
  485. }
  486. void ProjectExportDialog::_delete_preset() {
  487. Ref<EditorExportPreset> current = get_current_preset();
  488. if (current.is_null()) {
  489. return;
  490. }
  491. delete_confirm->set_text(vformat(TTR("Delete preset '%s'?"), current->get_name()));
  492. delete_confirm->popup_centered();
  493. }
  494. void ProjectExportDialog::_delete_preset_confirm() {
  495. int idx = presets->get_current();
  496. _edit_preset(-1);
  497. export_button->set_disabled(true);
  498. get_ok_button()->set_disabled(true);
  499. EditorExport::get_singleton()->remove_export_preset(idx);
  500. _update_presets();
  501. // The Export All button might become enabled (if all other presets have an export path defined),
  502. // or it could be disabled (if there are no presets anymore).
  503. _update_export_all();
  504. }
  505. Variant ProjectExportDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
  506. if (p_from == presets) {
  507. int pos = presets->get_item_at_position(p_point, true);
  508. if (pos >= 0) {
  509. Dictionary d;
  510. d["type"] = "export_preset";
  511. d["preset"] = pos;
  512. HBoxContainer *drag = memnew(HBoxContainer);
  513. TextureRect *tr = memnew(TextureRect);
  514. tr->set_texture(presets->get_item_icon(pos));
  515. drag->add_child(tr);
  516. Label *label = memnew(Label);
  517. label->set_text(presets->get_item_text(pos));
  518. drag->add_child(label);
  519. presets->set_drag_preview(drag);
  520. return d;
  521. }
  522. }
  523. return Variant();
  524. }
  525. bool ProjectExportDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
  526. if (p_from == presets) {
  527. Dictionary d = p_data;
  528. if (!d.has("type") || String(d["type"]) != "export_preset") {
  529. return false;
  530. }
  531. if (presets->get_item_at_position(p_point, true) < 0 && !presets->is_pos_at_end_of_items(p_point)) {
  532. return false;
  533. }
  534. }
  535. return true;
  536. }
  537. void ProjectExportDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
  538. if (p_from == presets) {
  539. Dictionary d = p_data;
  540. int from_pos = d["preset"];
  541. int to_pos = -1;
  542. if (presets->get_item_at_position(p_point, true) >= 0) {
  543. to_pos = presets->get_item_at_position(p_point, true);
  544. }
  545. if (to_pos == -1 && !presets->is_pos_at_end_of_items(p_point)) {
  546. return;
  547. }
  548. if (to_pos == from_pos) {
  549. return;
  550. } else if (to_pos > from_pos) {
  551. to_pos--;
  552. }
  553. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(from_pos);
  554. EditorExport::get_singleton()->remove_export_preset(from_pos);
  555. EditorExport::get_singleton()->add_export_preset(preset, to_pos);
  556. _update_presets();
  557. if (to_pos >= 0) {
  558. _edit_preset(to_pos);
  559. } else {
  560. _edit_preset(presets->get_item_count() - 1);
  561. }
  562. }
  563. }
  564. void ProjectExportDialog::_export_type_changed(int p_which) {
  565. if (updating) {
  566. return;
  567. }
  568. Ref<EditorExportPreset> current = get_current_preset();
  569. if (current.is_null()) {
  570. return;
  571. }
  572. EditorExportPreset::ExportFilter filter_type = (EditorExportPreset::ExportFilter)p_which;
  573. current->set_export_filter(filter_type);
  574. current->set_dedicated_server(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED);
  575. server_strip_message->set_visible(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED);
  576. // Default to stripping everything when first switching to server build.
  577. if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) {
  578. current->set_file_export_mode("res://", EditorExportPreset::MODE_FILE_STRIP);
  579. }
  580. updating = true;
  581. _fill_resource_tree();
  582. updating = false;
  583. }
  584. void ProjectExportDialog::_filter_changed(const String &p_filter) {
  585. if (updating) {
  586. return;
  587. }
  588. Ref<EditorExportPreset> current = get_current_preset();
  589. if (current.is_null()) {
  590. return;
  591. }
  592. current->set_include_filter(include_filters->get_text());
  593. current->set_exclude_filter(exclude_filters->get_text());
  594. }
  595. void ProjectExportDialog::_fill_resource_tree() {
  596. include_files->clear();
  597. include_label->hide();
  598. include_margin->hide();
  599. Ref<EditorExportPreset> current = get_current_preset();
  600. if (current.is_null()) {
  601. return;
  602. }
  603. EditorExportPreset::ExportFilter f = current->get_export_filter();
  604. if (f == EditorExportPreset::EXPORT_ALL_RESOURCES) {
  605. return;
  606. }
  607. TreeItem *root = include_files->create_item();
  608. if (f == EditorExportPreset::EXPORT_CUSTOMIZED) {
  609. include_files->set_columns(2);
  610. include_files->set_column_expand(1, false);
  611. include_files->set_column_custom_minimum_width(1, 250 * EDSCALE);
  612. } else {
  613. include_files->set_columns(1);
  614. }
  615. include_label->show();
  616. include_margin->show();
  617. _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f);
  618. if (f == EditorExportPreset::EXPORT_CUSTOMIZED) {
  619. _propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
  620. }
  621. }
  622. void ProjectExportDialog::_setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode) {
  623. if (p_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
  624. p_item->set_checked(0, false);
  625. p_item->set_cell_mode(1, TreeItem::CELL_MODE_STRING);
  626. p_item->set_editable(1, false);
  627. p_item->set_selectable(1, false);
  628. p_item->set_custom_color(1, get_theme_color(SNAME("disabled_font_color"), SNAME("Editor")));
  629. } else {
  630. p_item->set_checked(0, true);
  631. p_item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM);
  632. p_item->set_editable(1, true);
  633. p_item->set_selectable(1, true);
  634. p_item->clear_custom_color(1);
  635. }
  636. p_item->set_metadata(1, p_mode);
  637. }
  638. bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> &current, EditorExportPreset::ExportFilter p_export_filter) {
  639. p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
  640. p_item->set_icon(0, presets->get_theme_icon(SNAME("folder"), SNAME("FileDialog")));
  641. p_item->set_text(0, p_dir->get_name() + "/");
  642. p_item->set_editable(0, true);
  643. p_item->set_metadata(0, p_dir->get_path());
  644. if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) {
  645. _setup_item_for_file_mode(p_item, current->get_file_export_mode(p_dir->get_path()));
  646. }
  647. bool used = false;
  648. for (int i = 0; i < p_dir->get_subdir_count(); i++) {
  649. TreeItem *subdir = include_files->create_item(p_item);
  650. if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_export_filter)) {
  651. used = true;
  652. } else {
  653. memdelete(subdir);
  654. }
  655. }
  656. for (int i = 0; i < p_dir->get_file_count(); i++) {
  657. String type = p_dir->get_file_type(i);
  658. if (p_export_filter == EditorExportPreset::EXPORT_SELECTED_SCENES && type != "PackedScene") {
  659. continue;
  660. }
  661. if (type == "TextFile") {
  662. continue;
  663. }
  664. TreeItem *file = include_files->create_item(p_item);
  665. file->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
  666. file->set_text(0, p_dir->get_file(i));
  667. String path = p_dir->get_file_path(i);
  668. file->set_icon(0, EditorNode::get_singleton()->get_class_icon(type));
  669. file->set_editable(0, true);
  670. file->set_metadata(0, path);
  671. if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) {
  672. _setup_item_for_file_mode(file, current->get_file_export_mode(path));
  673. } else {
  674. file->set_checked(0, current->has_export_file(path));
  675. file->propagate_check(0);
  676. }
  677. used = true;
  678. }
  679. return used;
  680. }
  681. void ProjectExportDialog::_propagate_file_export_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_inherited_export_mode) {
  682. EditorExportPreset::FileExportMode file_export_mode = (EditorExportPreset::FileExportMode)(int)p_item->get_metadata(1);
  683. if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
  684. file_export_mode = p_inherited_export_mode;
  685. }
  686. if (file_export_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) {
  687. p_item->set_text(1, "");
  688. } else {
  689. p_item->set_text(1, file_mode_popup->get_item_text(file_mode_popup->get_item_index(file_export_mode)));
  690. }
  691. for (int i = 0; i < p_item->get_child_count(); i++) {
  692. _propagate_file_export_mode(p_item->get_child(i), file_export_mode);
  693. }
  694. }
  695. void ProjectExportDialog::_tree_changed() {
  696. if (updating) {
  697. return;
  698. }
  699. Ref<EditorExportPreset> current = get_current_preset();
  700. if (current.is_null()) {
  701. return;
  702. }
  703. TreeItem *item = include_files->get_edited();
  704. if (!item) {
  705. return;
  706. }
  707. if (current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED) {
  708. EditorExportPreset::FileExportMode file_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED;
  709. String path = item->get_metadata(0);
  710. if (item->is_checked(0)) {
  711. file_mode = current->get_file_export_mode(path, EditorExportPreset::MODE_FILE_STRIP);
  712. }
  713. current->set_file_export_mode(path, file_mode);
  714. _setup_item_for_file_mode(item, file_mode);
  715. _propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
  716. } else {
  717. item->propagate_check(0);
  718. }
  719. }
  720. void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) {
  721. Ref<EditorExportPreset> current = get_current_preset();
  722. if (current.is_null()) {
  723. return;
  724. }
  725. TreeItem *item = Object::cast_to<TreeItem>(p_obj);
  726. String path = item->get_metadata(0);
  727. if (item && !path.ends_with("/")) {
  728. bool added = item->is_checked(0);
  729. if (added) {
  730. current->add_export_file(path);
  731. } else {
  732. current->remove_export_file(path);
  733. }
  734. }
  735. }
  736. void ProjectExportDialog::_tree_popup_edited(bool p_arrow_clicked) {
  737. Rect2 bounds = include_files->get_custom_popup_rect();
  738. bounds.position += get_global_canvas_transform().get_origin();
  739. bounds.size *= get_global_canvas_transform().get_scale();
  740. if (!is_embedding_subwindows()) {
  741. bounds.position += get_position();
  742. }
  743. file_mode_popup->popup(bounds);
  744. }
  745. void ProjectExportDialog::_set_file_export_mode(int p_id) {
  746. Ref<EditorExportPreset> current = get_current_preset();
  747. if (current.is_null()) {
  748. return;
  749. }
  750. TreeItem *item = include_files->get_edited();
  751. String path = item->get_metadata(0);
  752. EditorExportPreset::FileExportMode file_export_mode = (EditorExportPreset::FileExportMode)p_id;
  753. current->set_file_export_mode(path, file_export_mode);
  754. item->set_metadata(1, file_export_mode);
  755. _propagate_file_export_mode(include_files->get_root(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED);
  756. }
  757. void ProjectExportDialog::_export_pck_zip() {
  758. Ref<EditorExportPreset> current = get_current_preset();
  759. ERR_FAIL_COND(current.is_null());
  760. String dir = current->get_export_path().get_base_dir();
  761. export_pck_zip->set_current_dir(dir);
  762. export_pck_zip->popup_file_dialog();
  763. }
  764. void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
  765. Ref<EditorExportPreset> current = get_current_preset();
  766. ERR_FAIL_COND(current.is_null());
  767. Ref<EditorExportPlatform> platform = current->get_platform();
  768. ERR_FAIL_COND(platform.is_null());
  769. if (p_path.ends_with(".zip")) {
  770. platform->export_zip(current, export_pck_zip_debug->is_pressed(), p_path);
  771. } else if (p_path.ends_with(".pck")) {
  772. platform->export_pack(current, export_pck_zip_debug->is_pressed(), p_path);
  773. }
  774. }
  775. void ProjectExportDialog::_open_export_template_manager() {
  776. hide();
  777. EditorNode::get_singleton()->open_export_template_manager();
  778. }
  779. void ProjectExportDialog::_validate_export_path(const String &p_path) {
  780. // Disable export via OK button or Enter key if LineEdit has an empty filename
  781. bool invalid_path = (p_path.get_file().get_basename().is_empty());
  782. // Check if state change before needlessly messing with signals
  783. if (invalid_path && export_project->get_ok_button()->is_disabled()) {
  784. return;
  785. }
  786. if (!invalid_path && !export_project->get_ok_button()->is_disabled()) {
  787. return;
  788. }
  789. if (invalid_path) {
  790. export_project->get_ok_button()->set_disabled(true);
  791. export_project->get_line_edit()->disconnect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted));
  792. } else {
  793. export_project->get_ok_button()->set_disabled(false);
  794. export_project->get_line_edit()->connect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted));
  795. }
  796. }
  797. void ProjectExportDialog::_export_project() {
  798. Ref<EditorExportPreset> current = get_current_preset();
  799. ERR_FAIL_COND(current.is_null());
  800. Ref<EditorExportPlatform> platform = current->get_platform();
  801. ERR_FAIL_COND(platform.is_null());
  802. export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  803. export_project->clear_filters();
  804. List<String> extension_list = platform->get_binary_extensions(current);
  805. for (int i = 0; i < extension_list.size(); i++) {
  806. // TRANSLATORS: This is the name of a project export file format. %s will be replaced by the platform name.
  807. export_project->add_filter("*." + extension_list[i], vformat(TTR("%s Export"), platform->get_name()));
  808. }
  809. if (!current->get_export_path().is_empty()) {
  810. export_project->set_current_path(current->get_export_path());
  811. } else {
  812. if (extension_list.size() >= 1) {
  813. export_project->set_current_file(default_filename + "." + extension_list[0]);
  814. } else {
  815. export_project->set_current_file(default_filename);
  816. }
  817. }
  818. // Ensure that signal is connected if previous attempt left it disconnected
  819. // with _validate_export_path.
  820. // FIXME: This is a hack, we should instead change EditorFileDialog to allow
  821. // disabling validation by the "text_submitted" signal.
  822. if (!export_project->get_line_edit()->is_connected("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted))) {
  823. export_project->get_ok_button()->set_disabled(false);
  824. export_project->get_line_edit()->connect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted));
  825. }
  826. export_project->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
  827. export_project->popup_file_dialog();
  828. }
  829. void ProjectExportDialog::_export_project_to_path(const String &p_path) {
  830. // Save this name for use in future exports (but drop the file extension)
  831. default_filename = p_path.get_file().get_basename();
  832. EditorSettings::get_singleton()->set_project_metadata("export_options", "default_filename", default_filename);
  833. Ref<EditorExportPreset> current = get_current_preset();
  834. ERR_FAIL_COND(current.is_null());
  835. Ref<EditorExportPlatform> platform = current->get_platform();
  836. ERR_FAIL_COND(platform.is_null());
  837. current->set_export_path(p_path);
  838. platform->clear_messages();
  839. Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0);
  840. result_dialog_log->clear();
  841. if (err != ERR_SKIP) {
  842. if (platform->fill_log_messages(result_dialog_log, err)) {
  843. result_dialog->popup_centered_ratio(0.5);
  844. }
  845. }
  846. }
  847. void ProjectExportDialog::_export_all_dialog() {
  848. #ifndef ANDROID_ENABLED
  849. export_all_dialog->show();
  850. export_all_dialog->popup_centered(Size2(300, 80));
  851. #endif
  852. }
  853. void ProjectExportDialog::_export_all_dialog_action(const String &p_str) {
  854. export_all_dialog->hide();
  855. _export_all(p_str != "release");
  856. }
  857. void ProjectExportDialog::_export_all(bool p_debug) {
  858. String export_target = p_debug ? TTR("Debug") : TTR("Release");
  859. EditorProgress ep("exportall", TTR("Exporting All") + " " + export_target, EditorExport::get_singleton()->get_export_preset_count(), true);
  860. bool show_dialog = false;
  861. result_dialog_log->clear();
  862. for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
  863. Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
  864. ERR_FAIL_COND(preset.is_null());
  865. Ref<EditorExportPlatform> platform = preset->get_platform();
  866. ERR_FAIL_COND(platform.is_null());
  867. ep.step(preset->get_name(), i);
  868. platform->clear_messages();
  869. Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0);
  870. if (err == ERR_SKIP) {
  871. return;
  872. }
  873. bool has_messages = platform->fill_log_messages(result_dialog_log, err);
  874. show_dialog = show_dialog || has_messages;
  875. }
  876. if (show_dialog) {
  877. result_dialog->popup_centered_ratio(0.5);
  878. }
  879. }
  880. void ProjectExportDialog::_bind_methods() {
  881. ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all);
  882. ClassDB::bind_method("set_export_path", &ProjectExportDialog::set_export_path);
  883. ClassDB::bind_method("get_export_path", &ProjectExportDialog::get_export_path);
  884. ClassDB::bind_method("get_current_preset", &ProjectExportDialog::get_current_preset);
  885. ADD_PROPERTY(PropertyInfo(Variant::STRING, "export_path"), "set_export_path", "get_export_path");
  886. }
  887. ProjectExportDialog::ProjectExportDialog() {
  888. set_title(TTR("Export"));
  889. VBoxContainer *main_vb = memnew(VBoxContainer);
  890. main_vb->connect("theme_changed", callable_mp(this, &ProjectExportDialog::_theme_changed));
  891. add_child(main_vb);
  892. HSplitContainer *hbox = memnew(HSplitContainer);
  893. main_vb->add_child(hbox);
  894. hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  895. // Presets list.
  896. VBoxContainer *preset_vb = memnew(VBoxContainer);
  897. preset_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  898. hbox->add_child(preset_vb);
  899. Label *l = memnew(Label(TTR("Presets")));
  900. l->set_theme_type_variation("HeaderSmall");
  901. HBoxContainer *preset_hb = memnew(HBoxContainer);
  902. preset_hb->add_child(l);
  903. preset_hb->add_spacer();
  904. preset_vb->add_child(preset_hb);
  905. add_preset = memnew(MenuButton);
  906. add_preset->set_text(TTR("Add..."));
  907. add_preset->get_popup()->connect("index_pressed", callable_mp(this, &ProjectExportDialog::_add_preset));
  908. preset_hb->add_child(add_preset);
  909. MarginContainer *mc = memnew(MarginContainer);
  910. preset_vb->add_child(mc);
  911. mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  912. presets = memnew(ItemList);
  913. SET_DRAG_FORWARDING_GCD(presets, ProjectExportDialog);
  914. mc->add_child(presets);
  915. presets->connect("item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset));
  916. duplicate_preset = memnew(Button);
  917. duplicate_preset->set_tooltip_text(TTR("Duplicate"));
  918. duplicate_preset->set_flat(true);
  919. preset_hb->add_child(duplicate_preset);
  920. duplicate_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_duplicate_preset));
  921. delete_preset = memnew(Button);
  922. delete_preset->set_tooltip_text(TTR("Delete"));
  923. delete_preset->set_flat(true);
  924. preset_hb->add_child(delete_preset);
  925. delete_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_delete_preset));
  926. // Preset settings.
  927. VBoxContainer *settings_vb = memnew(VBoxContainer);
  928. settings_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  929. hbox->add_child(settings_vb);
  930. name = memnew(LineEdit);
  931. settings_vb->add_margin_child(TTR("Name:"), name);
  932. name->connect("text_changed", callable_mp(this, &ProjectExportDialog::_name_changed));
  933. runnable = memnew(CheckButton);
  934. runnable->set_text(TTR("Runnable"));
  935. runnable->set_tooltip_text(TTR("If checked, the preset will be available for use in one-click deploy.\nOnly one preset per platform may be marked as runnable."));
  936. runnable->connect("pressed", callable_mp(this, &ProjectExportDialog::_runnable_pressed));
  937. settings_vb->add_child(runnable);
  938. export_path = memnew(EditorPropertyPath);
  939. settings_vb->add_child(export_path);
  940. export_path->set_label(TTR("Export Path"));
  941. export_path->set_object_and_property(this, "export_path");
  942. export_path->set_save_mode();
  943. export_path->connect("property_changed", callable_mp(this, &ProjectExportDialog::_export_path_changed));
  944. // Subsections.
  945. sections = memnew(TabContainer);
  946. sections->set_use_hidden_tabs_for_min_size(true);
  947. sections->set_theme_type_variation("TabContainerOdd");
  948. settings_vb->add_child(sections);
  949. sections->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  950. // Main preset parameters.
  951. parameters = memnew(EditorInspector);
  952. sections->add_child(parameters);
  953. parameters->set_name(TTR("Options"));
  954. parameters->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  955. parameters->set_property_name_style(EditorPropertyNameProcessor::get_settings_style());
  956. parameters->connect("property_edited", callable_mp(this, &ProjectExportDialog::_update_parameters));
  957. EditorExport::get_singleton()->connect("export_presets_updated", callable_mp(this, &ProjectExportDialog::_force_update_current_preset_parameters));
  958. // Resources export parameters.
  959. VBoxContainer *resources_vb = memnew(VBoxContainer);
  960. sections->add_child(resources_vb);
  961. resources_vb->set_name(TTR("Resources"));
  962. export_filter = memnew(OptionButton);
  963. export_filter->add_item(TTR("Export all resources in the project"));
  964. export_filter->add_item(TTR("Export selected scenes (and dependencies)"));
  965. export_filter->add_item(TTR("Export selected resources (and dependencies)"));
  966. export_filter->add_item(TTR("Export all resources in the project except resources checked below"));
  967. export_filter->add_item(TTR("Export as dedicated server"));
  968. resources_vb->add_margin_child(TTR("Export Mode:"), export_filter);
  969. export_filter->connect("item_selected", callable_mp(this, &ProjectExportDialog::_export_type_changed));
  970. include_label = memnew(Label);
  971. include_label->set_text(TTR("Resources to export:"));
  972. resources_vb->add_child(include_label);
  973. include_margin = memnew(MarginContainer);
  974. include_margin->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  975. resources_vb->add_child(include_margin);
  976. include_files = memnew(Tree);
  977. include_margin->add_child(include_files);
  978. include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed));
  979. include_files->connect("check_propagated_to_item", callable_mp(this, &ProjectExportDialog::_check_propagated_to_item));
  980. include_files->connect("custom_popup_edited", callable_mp(this, &ProjectExportDialog::_tree_popup_edited));
  981. server_strip_message = memnew(Label);
  982. server_strip_message->set_visible(false);
  983. server_strip_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
  984. resources_vb->add_child(server_strip_message);
  985. {
  986. List<StringName> resource_names;
  987. ClassDB::get_inheriters_from_class("Resource", &resource_names);
  988. PackedStringArray strippable;
  989. for (StringName resource_name : resource_names) {
  990. if (ClassDB::has_method(resource_name, "create_placeholder", true)) {
  991. strippable.push_back(resource_name);
  992. }
  993. }
  994. strippable.sort();
  995. String message = TTR("\"Strip Visuals\" will replace the following resources with placeholders:") + " ";
  996. message += String(", ").join(strippable);
  997. server_strip_message->set_text(message);
  998. }
  999. file_mode_popup = memnew(PopupMenu);
  1000. add_child(file_mode_popup);
  1001. file_mode_popup->add_item(TTR("Strip Visuals"), EditorExportPreset::MODE_FILE_STRIP);
  1002. file_mode_popup->add_item(TTR("Keep"), EditorExportPreset::MODE_FILE_KEEP);
  1003. file_mode_popup->add_item(TTR("Remove"), EditorExportPreset::MODE_FILE_REMOVE);
  1004. file_mode_popup->connect("id_pressed", callable_mp(this, &ProjectExportDialog::_set_file_export_mode));
  1005. include_filters = memnew(LineEdit);
  1006. resources_vb->add_margin_child(
  1007. TTR("Filters to export non-resource files/folders\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
  1008. include_filters);
  1009. include_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_filter_changed));
  1010. exclude_filters = memnew(LineEdit);
  1011. resources_vb->add_margin_child(
  1012. TTR("Filters to exclude files/folders from project\n(comma-separated, e.g: *.json, *.txt, docs/*)"),
  1013. exclude_filters);
  1014. exclude_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_filter_changed));
  1015. // Feature tags.
  1016. VBoxContainer *feature_vb = memnew(VBoxContainer);
  1017. feature_vb->set_name(TTR("Features"));
  1018. custom_features = memnew(LineEdit);
  1019. custom_features->connect("text_changed", callable_mp(this, &ProjectExportDialog::_custom_features_changed));
  1020. feature_vb->add_margin_child(TTR("Custom (comma-separated):"), custom_features);
  1021. custom_feature_display = memnew(RichTextLabel);
  1022. custom_feature_display->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1023. feature_vb->add_margin_child(TTR("Feature List:"), custom_feature_display, true);
  1024. sections->add_child(feature_vb);
  1025. // Script export parameters.
  1026. VBoxContainer *sec_vb = memnew(VBoxContainer);
  1027. sec_vb->set_name(TTR("Encryption"));
  1028. enc_pck = memnew(CheckButton);
  1029. enc_pck->connect("toggled", callable_mp(this, &ProjectExportDialog::_enc_pck_changed));
  1030. enc_pck->set_text(TTR("Encrypt Exported PCK"));
  1031. sec_vb->add_child(enc_pck);
  1032. enc_directory = memnew(CheckButton);
  1033. enc_directory->connect("toggled", callable_mp(this, &ProjectExportDialog::_enc_directory_changed));
  1034. enc_directory->set_text(TTR("Encrypt Index (File Names and Info)"));
  1035. sec_vb->add_child(enc_directory);
  1036. enc_in_filters = memnew(LineEdit);
  1037. enc_in_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_enc_filters_changed));
  1038. sec_vb->add_margin_child(
  1039. TTR("Filters to include files/folders\n(comma-separated, e.g: *.tscn, *.tres, scenes/*)"),
  1040. enc_in_filters);
  1041. enc_ex_filters = memnew(LineEdit);
  1042. enc_ex_filters->connect("text_changed", callable_mp(this, &ProjectExportDialog::_enc_filters_changed));
  1043. sec_vb->add_margin_child(
  1044. TTR("Filters to exclude files/folders\n(comma-separated, e.g: *.ctex, *.import, music/*)"),
  1045. enc_ex_filters);
  1046. script_key = memnew(LineEdit);
  1047. script_key->connect("text_changed", callable_mp(this, &ProjectExportDialog::_script_encryption_key_changed));
  1048. script_key_error = memnew(Label);
  1049. script_key_error->set_text(String::utf8("• ") + TTR("Invalid Encryption Key (must be 64 hexadecimal characters long)"));
  1050. script_key_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
  1051. sec_vb->add_margin_child(TTR("Encryption Key (256-bits as hexadecimal):"), script_key);
  1052. sec_vb->add_child(script_key_error);
  1053. sections->add_child(sec_vb);
  1054. Label *sec_info = memnew(Label);
  1055. sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source."));
  1056. sec_vb->add_child(sec_info);
  1057. LinkButton *sec_more_info = memnew(LinkButton);
  1058. sec_more_info->set_text(TTR("More Info..."));
  1059. sec_more_info->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_key_help_link));
  1060. sec_vb->add_child(sec_more_info);
  1061. sections->connect("tab_changed", callable_mp(this, &ProjectExportDialog::_tab_changed));
  1062. // Disable by default.
  1063. name->set_editable(false);
  1064. export_path->hide();
  1065. runnable->set_disabled(true);
  1066. duplicate_preset->set_disabled(true);
  1067. delete_preset->set_disabled(true);
  1068. script_key_error->hide();
  1069. sections->hide();
  1070. parameters->edit(nullptr);
  1071. // Deletion dialog.
  1072. delete_confirm = memnew(ConfirmationDialog);
  1073. add_child(delete_confirm);
  1074. delete_confirm->set_ok_button_text(TTR("Delete"));
  1075. delete_confirm->connect("confirmed", callable_mp(this, &ProjectExportDialog::_delete_preset_confirm));
  1076. // Export buttons, dialogs and errors.
  1077. set_cancel_button_text(TTR("Close"));
  1078. set_ok_button_text(TTR("Export PCK/ZIP..."));
  1079. get_ok_button()->set_disabled(true);
  1080. #ifdef ANDROID_ENABLED
  1081. export_button = memnew(Button);
  1082. export_button->hide();
  1083. #else
  1084. export_button = add_button(TTR("Export Project..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export");
  1085. #endif
  1086. export_button->connect("pressed", callable_mp(this, &ProjectExportDialog::_export_project));
  1087. // Disable initially before we select a valid preset
  1088. export_button->set_disabled(true);
  1089. export_all_dialog = memnew(ConfirmationDialog);
  1090. add_child(export_all_dialog);
  1091. export_all_dialog->set_title(TTR("Export All"));
  1092. export_all_dialog->set_text(TTR("Choose an export mode:"));
  1093. export_all_dialog->get_ok_button()->hide();
  1094. export_all_dialog->add_button(TTR("Debug"), true, "debug");
  1095. export_all_dialog->add_button(TTR("Release"), true, "release");
  1096. export_all_dialog->connect("custom_action", callable_mp(this, &ProjectExportDialog::_export_all_dialog_action));
  1097. #ifdef ANDROID_ENABLED
  1098. export_all_dialog->hide();
  1099. export_all_button = memnew(Button);
  1100. export_all_button->hide();
  1101. #else
  1102. export_all_button = add_button(TTR("Export All..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export");
  1103. #endif
  1104. export_all_button->connect("pressed", callable_mp(this, &ProjectExportDialog::_export_all_dialog));
  1105. export_all_button->set_disabled(true);
  1106. export_pck_zip = memnew(EditorFileDialog);
  1107. export_pck_zip->add_filter("*.zip", TTR("ZIP File"));
  1108. export_pck_zip->add_filter("*.pck", TTR("Godot Project Pack"));
  1109. export_pck_zip->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  1110. export_pck_zip->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
  1111. add_child(export_pck_zip);
  1112. export_pck_zip->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_pck_zip_selected));
  1113. export_error = memnew(Label);
  1114. main_vb->add_child(export_error);
  1115. export_error->hide();
  1116. export_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
  1117. export_warning = memnew(Label);
  1118. main_vb->add_child(export_warning);
  1119. export_warning->hide();
  1120. export_warning->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor")));
  1121. export_templates_error = memnew(HBoxContainer);
  1122. main_vb->add_child(export_templates_error);
  1123. export_templates_error->hide();
  1124. Label *export_error2 = memnew(Label);
  1125. export_templates_error->add_child(export_error2);
  1126. export_error2->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
  1127. export_error2->set_text(String::utf8("• ") + TTR("Export templates for this platform are missing:") + " ");
  1128. result_dialog = memnew(AcceptDialog);
  1129. result_dialog->set_title(TTR("Project Export"));
  1130. result_dialog_log = memnew(RichTextLabel);
  1131. result_dialog_log->set_custom_minimum_size(Size2(300, 80) * EDSCALE);
  1132. result_dialog->add_child(result_dialog_log);
  1133. main_vb->add_child(result_dialog);
  1134. result_dialog->hide();
  1135. LinkButton *download_templates = memnew(LinkButton);
  1136. download_templates->set_text(TTR("Manage Export Templates"));
  1137. download_templates->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
  1138. export_templates_error->add_child(download_templates);
  1139. download_templates->connect("pressed", callable_mp(this, &ProjectExportDialog::_open_export_template_manager));
  1140. export_project = memnew(EditorFileDialog);
  1141. export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
  1142. add_child(export_project);
  1143. export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path));
  1144. export_project->get_line_edit()->connect("text_changed", callable_mp(this, &ProjectExportDialog::_validate_export_path));
  1145. export_debug = memnew(CheckBox);
  1146. export_debug->set_text(TTR("Export With Debug"));
  1147. export_debug->set_pressed(true);
  1148. export_debug->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
  1149. export_project->get_vbox()->add_child(export_debug);
  1150. export_pck_zip_debug = memnew(CheckBox);
  1151. export_pck_zip_debug->set_text(TTR("Export With Debug"));
  1152. export_pck_zip_debug->set_pressed(true);
  1153. export_pck_zip_debug->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
  1154. export_pck_zip->get_vbox()->add_child(export_pck_zip_debug);
  1155. set_hide_on_ok(false);
  1156. default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", "");
  1157. // If no default set, use project name
  1158. if (default_filename.is_empty()) {
  1159. // If no project name defined, use a sane default
  1160. default_filename = GLOBAL_GET("application/config/name");
  1161. if (default_filename.is_empty()) {
  1162. default_filename = "UnnamedProject";
  1163. }
  1164. }
  1165. }
  1166. ProjectExportDialog::~ProjectExportDialog() {
  1167. }