editor_file_dialog.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737
  1. /*************************************************************************/
  2. /* editor_file_dialog.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2020 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 "editor_file_dialog.h"
  31. #include "core/os/file_access.h"
  32. #include "core/os/keyboard.h"
  33. #include "core/os/os.h"
  34. #include "core/print_string.h"
  35. #include "dependency_editor.h"
  36. #include "editor_file_system.h"
  37. #include "editor_resource_preview.h"
  38. #include "editor_scale.h"
  39. #include "editor_settings.h"
  40. #include "scene/gui/center_container.h"
  41. #include "scene/gui/label.h"
  42. #include "scene/gui/margin_container.h"
  43. #include "servers/display_server.h"
  44. EditorFileDialog::GetIconFunc EditorFileDialog::get_icon_func = nullptr;
  45. EditorFileDialog::GetIconFunc EditorFileDialog::get_large_icon_func = nullptr;
  46. EditorFileDialog::RegisterFunc EditorFileDialog::register_func = nullptr;
  47. EditorFileDialog::RegisterFunc EditorFileDialog::unregister_func = nullptr;
  48. VBoxContainer *EditorFileDialog::get_vbox() {
  49. return vbox;
  50. }
  51. void EditorFileDialog::_notification(int p_what) {
  52. if (p_what == NOTIFICATION_ENTER_TREE) {
  53. // update icons
  54. mode_thumbnails->set_icon(item_list->get_theme_icon("FileThumbnail", "EditorIcons"));
  55. mode_list->set_icon(item_list->get_theme_icon("FileList", "EditorIcons"));
  56. dir_prev->set_icon(item_list->get_theme_icon("Back", "EditorIcons"));
  57. dir_next->set_icon(item_list->get_theme_icon("Forward", "EditorIcons"));
  58. dir_up->set_icon(item_list->get_theme_icon("ArrowUp", "EditorIcons"));
  59. refresh->set_icon(item_list->get_theme_icon("Reload", "EditorIcons"));
  60. favorite->set_icon(item_list->get_theme_icon("Favorites", "EditorIcons"));
  61. show_hidden->set_icon(item_list->get_theme_icon("GuiVisibilityVisible", "EditorIcons"));
  62. fav_up->set_icon(item_list->get_theme_icon("MoveUp", "EditorIcons"));
  63. fav_down->set_icon(item_list->get_theme_icon("MoveDown", "EditorIcons"));
  64. } else if (p_what == NOTIFICATION_PROCESS) {
  65. if (preview_waiting) {
  66. preview_wheel_timeout -= get_process_delta_time();
  67. if (preview_wheel_timeout <= 0) {
  68. preview_wheel_index++;
  69. if (preview_wheel_index >= 8)
  70. preview_wheel_index = 0;
  71. Ref<Texture2D> frame = item_list->get_theme_icon("Progress" + itos(preview_wheel_index + 1), "EditorIcons");
  72. preview->set_texture(frame);
  73. preview_wheel_timeout = 0.1;
  74. }
  75. }
  76. } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
  77. bool is_showing_hidden = EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files");
  78. if (show_hidden_files != is_showing_hidden)
  79. set_show_hidden_files(is_showing_hidden);
  80. set_display_mode((DisplayMode)EditorSettings::get_singleton()->get("filesystem/file_dialog/display_mode").operator int());
  81. // update icons
  82. mode_thumbnails->set_icon(item_list->get_theme_icon("FileThumbnail", "EditorIcons"));
  83. mode_list->set_icon(item_list->get_theme_icon("FileList", "EditorIcons"));
  84. dir_prev->set_icon(item_list->get_theme_icon("Back", "EditorIcons"));
  85. dir_next->set_icon(item_list->get_theme_icon("Forward", "EditorIcons"));
  86. dir_up->set_icon(item_list->get_theme_icon("ArrowUp", "EditorIcons"));
  87. refresh->set_icon(item_list->get_theme_icon("Reload", "EditorIcons"));
  88. favorite->set_icon(item_list->get_theme_icon("Favorites", "EditorIcons"));
  89. fav_up->set_icon(item_list->get_theme_icon("MoveUp", "EditorIcons"));
  90. fav_down->set_icon(item_list->get_theme_icon("MoveDown", "EditorIcons"));
  91. // DO NOT CALL UPDATE FILE LIST HERE, ALL HUNDREDS OF HIDDEN DIALOGS WILL RESPOND, CALL INVALIDATE INSTEAD
  92. invalidate();
  93. } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
  94. if (!is_visible()) {
  95. set_process_unhandled_input(false);
  96. }
  97. }
  98. }
  99. void EditorFileDialog::_unhandled_input(const Ref<InputEvent> &p_event) {
  100. Ref<InputEventKey> k = p_event;
  101. if (k.is_valid()) {
  102. if (k->is_pressed()) {
  103. bool handled = false;
  104. if (ED_IS_SHORTCUT("file_dialog/go_back", p_event)) {
  105. _go_back();
  106. handled = true;
  107. }
  108. if (ED_IS_SHORTCUT("file_dialog/go_forward", p_event)) {
  109. _go_forward();
  110. handled = true;
  111. }
  112. if (ED_IS_SHORTCUT("file_dialog/go_up", p_event)) {
  113. _go_up();
  114. handled = true;
  115. }
  116. if (ED_IS_SHORTCUT("file_dialog/refresh", p_event)) {
  117. invalidate();
  118. handled = true;
  119. }
  120. if (ED_IS_SHORTCUT("file_dialog/toggle_hidden_files", p_event)) {
  121. bool show = !show_hidden_files;
  122. set_show_hidden_files(show);
  123. EditorSettings::get_singleton()->set("filesystem/file_dialog/show_hidden_files", show);
  124. handled = true;
  125. }
  126. if (ED_IS_SHORTCUT("file_dialog/toggle_favorite", p_event)) {
  127. _favorite_pressed();
  128. handled = true;
  129. }
  130. if (ED_IS_SHORTCUT("file_dialog/toggle_mode", p_event)) {
  131. if (mode_thumbnails->is_pressed()) {
  132. set_display_mode(DISPLAY_LIST);
  133. } else {
  134. set_display_mode(DISPLAY_THUMBNAILS);
  135. }
  136. handled = true;
  137. }
  138. if (ED_IS_SHORTCUT("file_dialog/create_folder", p_event)) {
  139. _make_dir();
  140. handled = true;
  141. }
  142. if (ED_IS_SHORTCUT("file_dialog/delete", p_event)) {
  143. _delete_items();
  144. handled = true;
  145. }
  146. if (ED_IS_SHORTCUT("file_dialog/focus_path", p_event)) {
  147. dir->grab_focus();
  148. handled = true;
  149. }
  150. if (ED_IS_SHORTCUT("file_dialog/move_favorite_up", p_event)) {
  151. _favorite_move_up();
  152. handled = true;
  153. }
  154. if (ED_IS_SHORTCUT("file_dialog/move_favorite_down", p_event)) {
  155. _favorite_move_down();
  156. handled = true;
  157. }
  158. if (handled) {
  159. set_input_as_handled();
  160. }
  161. }
  162. }
  163. }
  164. void EditorFileDialog::set_enable_multiple_selection(bool p_enable) {
  165. item_list->set_select_mode(p_enable ? ItemList::SELECT_MULTI : ItemList::SELECT_SINGLE);
  166. };
  167. Vector<String> EditorFileDialog::get_selected_files() const {
  168. Vector<String> list;
  169. for (int i = 0; i < item_list->get_item_count(); i++) {
  170. if (item_list->is_selected(i))
  171. list.push_back(item_list->get_item_text(i));
  172. }
  173. return list;
  174. };
  175. void EditorFileDialog::update_dir() {
  176. if (drives->is_visible()) {
  177. drives->select(dir_access->get_current_drive());
  178. }
  179. dir->set_text(dir_access->get_current_dir(false));
  180. // Disable "Open" button only when selecting file(s) mode.
  181. get_ok()->set_disabled(_is_open_should_be_disabled());
  182. switch (mode) {
  183. case FILE_MODE_OPEN_FILE:
  184. case FILE_MODE_OPEN_FILES:
  185. get_ok()->set_text(TTR("Open"));
  186. break;
  187. case FILE_MODE_OPEN_DIR:
  188. get_ok()->set_text(TTR("Select Current Folder"));
  189. break;
  190. case FILE_MODE_OPEN_ANY:
  191. case FILE_MODE_SAVE_FILE:
  192. // FIXME: Implement, or refactor to avoid duplication with set_mode
  193. break;
  194. }
  195. }
  196. void EditorFileDialog::_dir_entered(String p_dir) {
  197. dir_access->change_dir(p_dir);
  198. file->set_text("");
  199. invalidate();
  200. update_dir();
  201. _push_history();
  202. }
  203. void EditorFileDialog::_file_entered(const String &p_file) {
  204. _action_pressed();
  205. }
  206. void EditorFileDialog::_save_confirm_pressed() {
  207. String f = dir_access->get_current_dir().plus_file(file->get_text());
  208. _save_to_recent();
  209. hide();
  210. emit_signal("file_selected", f);
  211. }
  212. void EditorFileDialog::_post_popup() {
  213. ConfirmationDialog::_post_popup();
  214. if (invalidated) {
  215. update_file_list();
  216. invalidated = false;
  217. }
  218. if (mode == FILE_MODE_SAVE_FILE)
  219. file->grab_focus();
  220. else
  221. item_list->grab_focus();
  222. if (mode == FILE_MODE_OPEN_DIR) {
  223. file_box->set_visible(false);
  224. } else {
  225. file_box->set_visible(true);
  226. }
  227. if (is_visible() && get_current_file() != "")
  228. _request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
  229. if (is_visible()) {
  230. Ref<Texture2D> folder = item_list->get_theme_icon("folder", "FileDialog");
  231. const Color folder_color = item_list->get_theme_color("folder_icon_modulate", "FileDialog");
  232. recent->clear();
  233. bool res = access == ACCESS_RESOURCES;
  234. Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs();
  235. for (int i = 0; i < recentd.size(); i++) {
  236. bool cres = recentd[i].begins_with("res://");
  237. if (cres != res)
  238. continue;
  239. String name = recentd[i];
  240. if (res && name == "res://") {
  241. name = "/";
  242. } else {
  243. name = name.get_file() + "/";
  244. }
  245. recent->add_item(name, folder);
  246. recent->set_item_metadata(recent->get_item_count() - 1, recentd[i]);
  247. recent->set_item_icon_modulate(recent->get_item_count() - 1, folder_color);
  248. }
  249. local_history.clear();
  250. local_history_pos = -1;
  251. _push_history();
  252. _update_favorites();
  253. }
  254. set_process_unhandled_input(true);
  255. }
  256. void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) {
  257. if (display_mode == DISPLAY_LIST || p_preview.is_null())
  258. return;
  259. for (int i = 0; i < item_list->get_item_count(); i++) {
  260. Dictionary d = item_list->get_item_metadata(i);
  261. String pname = d["path"];
  262. if (pname == p_path) {
  263. item_list->set_item_icon(i, p_preview);
  264. item_list->set_item_tag_icon(i, Ref<Texture2D>());
  265. }
  266. }
  267. }
  268. void EditorFileDialog::_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata) {
  269. set_process(false);
  270. preview_waiting = false;
  271. if (p_preview.is_valid() && get_current_path() == p_path) {
  272. preview->set_texture(p_preview);
  273. if (display_mode == DISPLAY_THUMBNAILS) {
  274. preview_vb->hide();
  275. } else {
  276. preview_vb->show();
  277. }
  278. } else {
  279. preview_vb->hide();
  280. preview->set_texture(Ref<Texture2D>());
  281. }
  282. }
  283. void EditorFileDialog::_request_single_thumbnail(const String &p_path) {
  284. if (!FileAccess::exists(p_path))
  285. return;
  286. set_process(true);
  287. preview_waiting = true;
  288. preview_wheel_timeout = 0;
  289. EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, this, "_thumbnail_done", p_path);
  290. }
  291. void EditorFileDialog::_action_pressed() {
  292. if (mode == FILE_MODE_OPEN_FILES) {
  293. String fbase = dir_access->get_current_dir();
  294. Vector<String> files;
  295. for (int i = 0; i < item_list->get_item_count(); i++) {
  296. if (item_list->is_selected(i))
  297. files.push_back(fbase.plus_file(item_list->get_item_text(i)));
  298. }
  299. if (files.size()) {
  300. _save_to_recent();
  301. hide();
  302. emit_signal("files_selected", files);
  303. }
  304. return;
  305. }
  306. String f = dir_access->get_current_dir().plus_file(file->get_text());
  307. if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
  308. _save_to_recent();
  309. hide();
  310. emit_signal("file_selected", f);
  311. } else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) {
  312. String path = dir_access->get_current_dir();
  313. path = path.replace("\\", "/");
  314. for (int i = 0; i < item_list->get_item_count(); i++) {
  315. if (item_list->is_selected(i)) {
  316. Dictionary d = item_list->get_item_metadata(i);
  317. if (d["dir"]) {
  318. path = path.plus_file(d["name"]);
  319. break;
  320. }
  321. }
  322. }
  323. _save_to_recent();
  324. hide();
  325. emit_signal("dir_selected", path);
  326. }
  327. if (mode == FILE_MODE_SAVE_FILE) {
  328. bool valid = false;
  329. if (filter->get_selected() == filter->get_item_count() - 1) {
  330. valid = true; // match none
  331. } else if (filters.size() > 1 && filter->get_selected() == 0) {
  332. // match all filters
  333. for (int i = 0; i < filters.size(); i++) {
  334. String flt = filters[i].get_slice(";", 0);
  335. for (int j = 0; j < flt.get_slice_count(","); j++) {
  336. String str = flt.get_slice(",", j).strip_edges();
  337. if (f.match(str)) {
  338. valid = true;
  339. break;
  340. }
  341. }
  342. if (valid)
  343. break;
  344. }
  345. } else {
  346. int idx = filter->get_selected();
  347. if (filters.size() > 1)
  348. idx--;
  349. if (idx >= 0 && idx < filters.size()) {
  350. String flt = filters[idx].get_slice(";", 0);
  351. int filterSliceCount = flt.get_slice_count(",");
  352. for (int j = 0; j < filterSliceCount; j++) {
  353. String str = (flt.get_slice(",", j).strip_edges());
  354. if (f.match(str)) {
  355. valid = true;
  356. break;
  357. }
  358. }
  359. if (!valid && filterSliceCount > 0) {
  360. String str = (flt.get_slice(",", 0).strip_edges());
  361. f += str.substr(1, str.length() - 1);
  362. _request_single_thumbnail(get_current_dir().plus_file(f.get_file()));
  363. file->set_text(f.get_file());
  364. valid = true;
  365. }
  366. } else {
  367. valid = true;
  368. }
  369. }
  370. if (!valid) {
  371. exterr->popup_centered(Size2(250, 80) * EDSCALE);
  372. return;
  373. }
  374. if (dir_access->file_exists(f) && !disable_overwrite_warning) {
  375. confirm_save->set_text(TTR("File Exists, Overwrite?"));
  376. confirm_save->popup_centered(Size2(200, 80));
  377. } else {
  378. _save_to_recent();
  379. hide();
  380. emit_signal("file_selected", f);
  381. }
  382. }
  383. }
  384. void EditorFileDialog::_cancel_pressed() {
  385. file->set_text("");
  386. invalidate();
  387. hide();
  388. }
  389. void EditorFileDialog::_item_selected(int p_item) {
  390. int current = p_item;
  391. if (current < 0 || current >= item_list->get_item_count())
  392. return;
  393. Dictionary d = item_list->get_item_metadata(current);
  394. if (!d["dir"]) {
  395. file->set_text(d["name"]);
  396. _request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
  397. } else if (mode == FILE_MODE_OPEN_DIR) {
  398. get_ok()->set_text(TTR("Select This Folder"));
  399. }
  400. get_ok()->set_disabled(_is_open_should_be_disabled());
  401. }
  402. void EditorFileDialog::_multi_selected(int p_item, bool p_selected) {
  403. int current = p_item;
  404. if (current < 0 || current >= item_list->get_item_count())
  405. return;
  406. Dictionary d = item_list->get_item_metadata(current);
  407. if (!d["dir"] && p_selected) {
  408. file->set_text(d["name"]);
  409. _request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
  410. }
  411. get_ok()->set_disabled(_is_open_should_be_disabled());
  412. }
  413. void EditorFileDialog::_items_clear_selection() {
  414. item_list->unselect_all();
  415. // If nothing is selected, then block Open button.
  416. switch (mode) {
  417. case FILE_MODE_OPEN_FILE:
  418. case FILE_MODE_OPEN_FILES:
  419. get_ok()->set_text(TTR("Open"));
  420. get_ok()->set_disabled(!item_list->is_anything_selected());
  421. break;
  422. case FILE_MODE_OPEN_DIR:
  423. get_ok()->set_disabled(false);
  424. get_ok()->set_text(TTR("Select Current Folder"));
  425. break;
  426. case FILE_MODE_OPEN_ANY:
  427. case FILE_MODE_SAVE_FILE:
  428. // FIXME: Implement, or refactor to avoid duplication with set_mode
  429. break;
  430. }
  431. }
  432. void EditorFileDialog::_push_history() {
  433. local_history.resize(local_history_pos + 1);
  434. String new_path = dir_access->get_current_dir();
  435. if (local_history.size() == 0 || new_path != local_history[local_history_pos]) {
  436. local_history.push_back(new_path);
  437. local_history_pos++;
  438. dir_prev->set_disabled(local_history_pos == 0);
  439. dir_next->set_disabled(true);
  440. }
  441. }
  442. void EditorFileDialog::_item_dc_selected(int p_item) {
  443. int current = p_item;
  444. if (current < 0 || current >= item_list->get_item_count())
  445. return;
  446. Dictionary d = item_list->get_item_metadata(current);
  447. if (d["dir"]) {
  448. dir_access->change_dir(d["name"]);
  449. call_deferred("_update_file_list");
  450. call_deferred("_update_dir");
  451. _push_history();
  452. } else {
  453. _action_pressed();
  454. }
  455. }
  456. void EditorFileDialog::_item_list_item_rmb_selected(int p_item, const Vector2 &p_pos) {
  457. // Right click on specific file(s) or folder(s).
  458. item_menu->clear();
  459. item_menu->set_size(Size2(1, 1));
  460. // Allow specific actions only on one item.
  461. bool single_item_selected = item_list->get_selected_items().size() == 1;
  462. // Disallow deleting the .import folder, Godot kills a cat if you do and it is possibly a senseless novice action.
  463. bool allow_delete = true;
  464. for (int i = 0; i < item_list->get_item_count(); i++) {
  465. if (!item_list->is_selected(i)) {
  466. continue;
  467. }
  468. Dictionary item_meta = item_list->get_item_metadata(i);
  469. if (item_meta["path"] == "res://.import") {
  470. allow_delete = false;
  471. break;
  472. }
  473. }
  474. if (single_item_selected) {
  475. item_menu->add_icon_item(item_list->get_theme_icon("ActionCopy", "EditorIcons"), TTR("Copy Path"), ITEM_MENU_COPY_PATH);
  476. }
  477. if (allow_delete) {
  478. item_menu->add_icon_item(item_list->get_theme_icon("Remove", "EditorIcons"), TTR("Delete"), ITEM_MENU_DELETE, KEY_DELETE);
  479. }
  480. if (single_item_selected) {
  481. item_menu->add_separator();
  482. Dictionary item_meta = item_list->get_item_metadata(p_item);
  483. String item_text = item_meta["dir"] ? TTR("Open in File Manager") : TTR("Show in File Manager");
  484. item_menu->add_icon_item(item_list->get_theme_icon("Filesystem", "EditorIcons"), item_text, ITEM_MENU_SHOW_IN_EXPLORER);
  485. }
  486. if (item_menu->get_item_count() > 0) {
  487. item_menu->set_position(item_list->get_global_position() + p_pos);
  488. item_menu->popup();
  489. }
  490. }
  491. void EditorFileDialog::_item_list_rmb_clicked(const Vector2 &p_pos) {
  492. // Right click on folder background. Deselect all files so that actions are applied on the current folder.
  493. for (int i = 0; i < item_list->get_item_count(); i++) {
  494. item_list->unselect(i);
  495. }
  496. item_menu->clear();
  497. item_menu->set_size(Size2(1, 1));
  498. if (can_create_dir) {
  499. item_menu->add_icon_item(item_list->get_theme_icon("folder", "FileDialog"), TTR("New Folder..."), ITEM_MENU_NEW_FOLDER, KEY_MASK_CMD | KEY_N);
  500. }
  501. item_menu->add_icon_item(item_list->get_theme_icon("Reload", "EditorIcons"), TTR("Refresh"), ITEM_MENU_REFRESH, KEY_F5);
  502. item_menu->add_separator();
  503. item_menu->add_icon_item(item_list->get_theme_icon("Filesystem", "EditorIcons"), TTR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
  504. item_menu->set_position(item_list->get_global_position() + p_pos);
  505. item_menu->popup();
  506. }
  507. void EditorFileDialog::_item_menu_id_pressed(int p_option) {
  508. switch (p_option) {
  509. case ITEM_MENU_COPY_PATH: {
  510. Dictionary item_meta = item_list->get_item_metadata(item_list->get_current());
  511. DisplayServer::get_singleton()->clipboard_set(item_meta["path"]);
  512. } break;
  513. case ITEM_MENU_DELETE: {
  514. _delete_items();
  515. } break;
  516. case ITEM_MENU_REFRESH: {
  517. invalidate();
  518. } break;
  519. case ITEM_MENU_NEW_FOLDER: {
  520. _make_dir();
  521. } break;
  522. case ITEM_MENU_SHOW_IN_EXPLORER: {
  523. String path;
  524. int idx = item_list->get_current();
  525. if (idx == -1 || item_list->get_selected_items().size() == 0) {
  526. // Folder background was clicked. Open this folder.
  527. path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir());
  528. } else {
  529. // Specific item was clicked. Open folders directly, or the folder containing a selected file.
  530. Dictionary item_meta = item_list->get_item_metadata(idx);
  531. path = ProjectSettings::get_singleton()->globalize_path(item_meta["path"]);
  532. if (!item_meta["dir"]) {
  533. path = path.get_base_dir();
  534. }
  535. }
  536. OS::get_singleton()->shell_open(String("file://") + path);
  537. } break;
  538. }
  539. }
  540. bool EditorFileDialog::_is_open_should_be_disabled() {
  541. if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_SAVE_FILE)
  542. return false;
  543. Vector<int> items = item_list->get_selected_items();
  544. if (items.size() == 0)
  545. return mode != FILE_MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder.
  546. for (int i = 0; i < items.size(); i++) {
  547. Dictionary d = item_list->get_item_metadata(items.get(i));
  548. if (((mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES) && d["dir"]) || (mode == FILE_MODE_OPEN_DIR && !d["dir"]))
  549. return true;
  550. }
  551. return false;
  552. }
  553. void EditorFileDialog::update_file_name() {
  554. int idx = filter->get_selected() - 1;
  555. if ((idx == -1 && filter->get_item_count() == 2) || (filter->get_item_count() > 2 && idx >= 0 && idx < filter->get_item_count() - 2)) {
  556. if (idx == -1)
  557. idx += 1;
  558. String filter_str = filters[idx];
  559. String file_str = file->get_text();
  560. String base_name = file_str.get_basename();
  561. Vector<String> filter_substr = filter_str.split(";");
  562. if (filter_substr.size() >= 2) {
  563. file_str = base_name + "." + filter_substr[0].strip_edges().lstrip("*.").to_lower();
  564. } else {
  565. file_str = base_name + "." + filter_str.get_extension().strip_edges().to_lower();
  566. }
  567. file->set_text(file_str);
  568. }
  569. }
  570. // DO NOT USE THIS FUNCTION UNLESS NEEDED, CALL INVALIDATE() INSTEAD.
  571. void EditorFileDialog::update_file_list() {
  572. int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
  573. thumbnail_size *= EDSCALE;
  574. Ref<Texture2D> folder_thumbnail;
  575. Ref<Texture2D> file_thumbnail;
  576. item_list->clear();
  577. // Scroll back to the top after opening a directory
  578. item_list->get_v_scroll()->set_value(0);
  579. if (display_mode == DISPLAY_THUMBNAILS) {
  580. item_list->set_max_columns(0);
  581. item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
  582. item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
  583. item_list->set_max_text_lines(2);
  584. item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
  585. if (thumbnail_size < 64) {
  586. folder_thumbnail = item_list->get_theme_icon("FolderMediumThumb", "EditorIcons");
  587. file_thumbnail = item_list->get_theme_icon("FileMediumThumb", "EditorIcons");
  588. } else {
  589. folder_thumbnail = item_list->get_theme_icon("FolderBigThumb", "EditorIcons");
  590. file_thumbnail = item_list->get_theme_icon("FileBigThumb", "EditorIcons");
  591. }
  592. preview_vb->hide();
  593. } else {
  594. item_list->set_icon_mode(ItemList::ICON_MODE_LEFT);
  595. item_list->set_max_columns(1);
  596. item_list->set_max_text_lines(1);
  597. item_list->set_fixed_column_width(0);
  598. item_list->set_fixed_icon_size(Size2());
  599. if (preview->get_texture().is_valid())
  600. preview_vb->show();
  601. }
  602. String cdir = dir_access->get_current_dir();
  603. dir_access->list_dir_begin();
  604. Ref<Texture2D> folder = item_list->get_theme_icon("folder", "FileDialog");
  605. const Color folder_color = item_list->get_theme_color("folder_icon_modulate", "FileDialog");
  606. List<String> files;
  607. List<String> dirs;
  608. String item;
  609. while ((item = dir_access->get_next()) != "") {
  610. if (item == "." || item == "..")
  611. continue;
  612. if (show_hidden_files || !dir_access->current_is_hidden()) {
  613. if (!dir_access->current_is_dir())
  614. files.push_back(item);
  615. else
  616. dirs.push_back(item);
  617. }
  618. }
  619. dirs.sort_custom<NaturalNoCaseComparator>();
  620. files.sort_custom<NaturalNoCaseComparator>();
  621. while (!dirs.empty()) {
  622. const String &dir_name = dirs.front()->get();
  623. item_list->add_item(dir_name);
  624. if (display_mode == DISPLAY_THUMBNAILS) {
  625. item_list->set_item_icon(item_list->get_item_count() - 1, folder_thumbnail);
  626. } else {
  627. item_list->set_item_icon(item_list->get_item_count() - 1, folder);
  628. }
  629. Dictionary d;
  630. d["name"] = dir_name;
  631. d["path"] = cdir.plus_file(dir_name);
  632. d["dir"] = true;
  633. item_list->set_item_metadata(item_list->get_item_count() - 1, d);
  634. item_list->set_item_icon_modulate(item_list->get_item_count() - 1, folder_color);
  635. dirs.pop_front();
  636. }
  637. List<String> patterns;
  638. // build filter
  639. if (filter->get_selected() == filter->get_item_count() - 1) {
  640. // match all
  641. } else if (filters.size() > 1 && filter->get_selected() == 0) {
  642. // match all filters
  643. for (int i = 0; i < filters.size(); i++) {
  644. String f = filters[i].get_slice(";", 0);
  645. for (int j = 0; j < f.get_slice_count(","); j++) {
  646. patterns.push_back(f.get_slice(",", j).strip_edges());
  647. }
  648. }
  649. } else {
  650. int idx = filter->get_selected();
  651. if (filters.size() > 1)
  652. idx--;
  653. if (idx >= 0 && idx < filters.size()) {
  654. String f = filters[idx].get_slice(";", 0);
  655. for (int j = 0; j < f.get_slice_count(","); j++) {
  656. patterns.push_back(f.get_slice(",", j).strip_edges());
  657. }
  658. }
  659. }
  660. while (!files.empty()) {
  661. bool match = patterns.empty();
  662. for (List<String>::Element *E = patterns.front(); E; E = E->next()) {
  663. if (files.front()->get().matchn(E->get())) {
  664. match = true;
  665. break;
  666. }
  667. }
  668. if (match) {
  669. item_list->add_item(files.front()->get());
  670. if (get_icon_func) {
  671. Ref<Texture2D> icon = get_icon_func(cdir.plus_file(files.front()->get()));
  672. if (display_mode == DISPLAY_THUMBNAILS) {
  673. item_list->set_item_icon(item_list->get_item_count() - 1, file_thumbnail);
  674. item_list->set_item_tag_icon(item_list->get_item_count() - 1, icon);
  675. } else {
  676. item_list->set_item_icon(item_list->get_item_count() - 1, icon);
  677. }
  678. }
  679. Dictionary d;
  680. d["name"] = files.front()->get();
  681. d["dir"] = false;
  682. String fullpath = cdir.plus_file(files.front()->get());
  683. d["path"] = fullpath;
  684. item_list->set_item_metadata(item_list->get_item_count() - 1, d);
  685. if (display_mode == DISPLAY_THUMBNAILS) {
  686. EditorResourcePreview::get_singleton()->queue_resource_preview(fullpath, this, "_thumbnail_result", fullpath);
  687. }
  688. if (file->get_text() == files.front()->get())
  689. item_list->set_current(item_list->get_item_count() - 1);
  690. }
  691. files.pop_front();
  692. }
  693. if (favorites->get_current() >= 0) {
  694. favorites->unselect(favorites->get_current());
  695. }
  696. favorite->set_pressed(false);
  697. fav_up->set_disabled(true);
  698. fav_down->set_disabled(true);
  699. get_ok()->set_disabled(_is_open_should_be_disabled());
  700. for (int i = 0; i < favorites->get_item_count(); i++) {
  701. if (favorites->get_item_metadata(i) == cdir || favorites->get_item_metadata(i) == cdir + "/") {
  702. favorites->select(i);
  703. favorite->set_pressed(true);
  704. if (i > 0) {
  705. fav_up->set_disabled(false);
  706. }
  707. if (i < favorites->get_item_count() - 1) {
  708. fav_down->set_disabled(false);
  709. }
  710. break;
  711. }
  712. }
  713. }
  714. void EditorFileDialog::_filter_selected(int) {
  715. update_file_name();
  716. update_file_list();
  717. }
  718. void EditorFileDialog::update_filters() {
  719. filter->clear();
  720. if (filters.size() > 1) {
  721. String all_filters;
  722. const int max_filters = 5;
  723. for (int i = 0; i < MIN(max_filters, filters.size()); i++) {
  724. String flt = filters[i].get_slice(";", 0).strip_edges();
  725. if (i > 0)
  726. all_filters += ", ";
  727. all_filters += flt;
  728. }
  729. if (max_filters < filters.size())
  730. all_filters += ", ...";
  731. filter->add_item(TTR("All Recognized") + " (" + all_filters + ")");
  732. }
  733. for (int i = 0; i < filters.size(); i++) {
  734. String flt = filters[i].get_slice(";", 0).strip_edges();
  735. String desc = filters[i].get_slice(";", 1).strip_edges();
  736. if (desc.length())
  737. filter->add_item(desc + " (" + flt + ")");
  738. else
  739. filter->add_item("(" + flt + ")");
  740. }
  741. filter->add_item(TTR("All Files (*)"));
  742. }
  743. void EditorFileDialog::clear_filters() {
  744. filters.clear();
  745. update_filters();
  746. invalidate();
  747. }
  748. void EditorFileDialog::add_filter(const String &p_filter) {
  749. filters.push_back(p_filter);
  750. update_filters();
  751. invalidate();
  752. }
  753. String EditorFileDialog::get_current_dir() const {
  754. return dir_access->get_current_dir();
  755. }
  756. String EditorFileDialog::get_current_file() const {
  757. return file->get_text();
  758. }
  759. String EditorFileDialog::get_current_path() const {
  760. return dir_access->get_current_dir().plus_file(file->get_text());
  761. }
  762. void EditorFileDialog::set_current_dir(const String &p_dir) {
  763. if (p_dir.is_rel_path())
  764. dir_access->change_dir(OS::get_singleton()->get_resource_dir());
  765. dir_access->change_dir(p_dir);
  766. update_dir();
  767. invalidate();
  768. }
  769. void EditorFileDialog::set_current_file(const String &p_file) {
  770. file->set_text(p_file);
  771. update_dir();
  772. invalidate();
  773. int lp = p_file.find_last(".");
  774. if (lp != -1) {
  775. file->select(0, lp);
  776. file->grab_focus();
  777. }
  778. if (is_visible())
  779. _request_single_thumbnail(get_current_dir().plus_file(get_current_file()));
  780. }
  781. void EditorFileDialog::set_current_path(const String &p_path) {
  782. if (!p_path.size())
  783. return;
  784. int pos = MAX(p_path.find_last("/"), p_path.find_last("\\"));
  785. if (pos == -1) {
  786. set_current_file(p_path);
  787. } else {
  788. String dir = p_path.substr(0, pos);
  789. String file = p_path.substr(pos + 1, p_path.length());
  790. set_current_dir(dir);
  791. set_current_file(file);
  792. }
  793. }
  794. void EditorFileDialog::set_file_mode(FileMode p_mode) {
  795. mode = p_mode;
  796. switch (mode) {
  797. case FILE_MODE_OPEN_FILE:
  798. get_ok()->set_text(TTR("Open"));
  799. set_title(TTR("Open a File"));
  800. can_create_dir = false;
  801. break;
  802. case FILE_MODE_OPEN_FILES:
  803. get_ok()->set_text(TTR("Open"));
  804. set_title(TTR("Open File(s)"));
  805. can_create_dir = false;
  806. break;
  807. case FILE_MODE_OPEN_DIR:
  808. get_ok()->set_text(TTR("Open"));
  809. set_title(TTR("Open a Directory"));
  810. can_create_dir = true;
  811. break;
  812. case FILE_MODE_OPEN_ANY:
  813. get_ok()->set_text(TTR("Open"));
  814. set_title(TTR("Open a File or Directory"));
  815. can_create_dir = true;
  816. break;
  817. case FILE_MODE_SAVE_FILE:
  818. get_ok()->set_text(TTR("Save"));
  819. set_title(TTR("Save a File"));
  820. can_create_dir = true;
  821. break;
  822. }
  823. if (mode == FILE_MODE_OPEN_FILES) {
  824. item_list->set_select_mode(ItemList::SELECT_MULTI);
  825. } else {
  826. item_list->set_select_mode(ItemList::SELECT_SINGLE);
  827. }
  828. if (can_create_dir) {
  829. makedir->show();
  830. } else {
  831. makedir->hide();
  832. }
  833. }
  834. EditorFileDialog::FileMode EditorFileDialog::get_file_mode() const {
  835. return mode;
  836. }
  837. void EditorFileDialog::set_access(Access p_access) {
  838. ERR_FAIL_INDEX(p_access, 3);
  839. if (access == p_access)
  840. return;
  841. memdelete(dir_access);
  842. switch (p_access) {
  843. case ACCESS_FILESYSTEM: {
  844. dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
  845. } break;
  846. case ACCESS_RESOURCES: {
  847. dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
  848. } break;
  849. case ACCESS_USERDATA: {
  850. dir_access = DirAccess::create(DirAccess::ACCESS_USERDATA);
  851. } break;
  852. }
  853. access = p_access;
  854. _update_drives();
  855. invalidate();
  856. update_filters();
  857. update_dir();
  858. }
  859. void EditorFileDialog::invalidate() {
  860. if (is_visible()) {
  861. update_file_list();
  862. _update_favorites();
  863. invalidated = false;
  864. } else {
  865. invalidated = true;
  866. }
  867. }
  868. EditorFileDialog::Access EditorFileDialog::get_access() const {
  869. return access;
  870. }
  871. void EditorFileDialog::_make_dir_confirm() {
  872. Error err = dir_access->make_dir(makedirname->get_text());
  873. if (err == OK) {
  874. dir_access->change_dir(makedirname->get_text());
  875. invalidate();
  876. update_filters();
  877. update_dir();
  878. _push_history();
  879. EditorFileSystem::get_singleton()->scan_changes(); //we created a dir, so rescan changes
  880. } else {
  881. mkdirerr->popup_centered(Size2(250, 50) * EDSCALE);
  882. }
  883. makedirname->set_text(""); // reset label
  884. }
  885. void EditorFileDialog::_make_dir() {
  886. makedialog->popup_centered(Size2(250, 80) * EDSCALE);
  887. makedirname->grab_focus();
  888. }
  889. void EditorFileDialog::_delete_items() {
  890. // Collect the selected folders and files to delete and check them in the deletion dependency dialog.
  891. Vector<String> folders;
  892. Vector<String> files;
  893. for (int i = 0; i < item_list->get_item_count(); i++) {
  894. if (!item_list->is_selected(i)) {
  895. continue;
  896. }
  897. Dictionary item_meta = item_list->get_item_metadata(i);
  898. if (item_meta["dir"]) {
  899. folders.push_back(item_meta["path"]);
  900. } else {
  901. files.push_back(item_meta["path"]);
  902. }
  903. }
  904. if (folders.size() + files.size() > 0) {
  905. remove_dialog->set_size(Size2(1, 1));
  906. remove_dialog->show(folders, files);
  907. }
  908. }
  909. void EditorFileDialog::_select_drive(int p_idx) {
  910. String d = drives->get_item_text(p_idx);
  911. dir_access->change_dir(d);
  912. file->set_text("");
  913. invalidate();
  914. update_dir();
  915. _push_history();
  916. }
  917. void EditorFileDialog::_update_drives() {
  918. int dc = dir_access->get_drive_count();
  919. if (dc == 0 || access != ACCESS_FILESYSTEM) {
  920. drives->hide();
  921. } else {
  922. drives->clear();
  923. Node *dp = drives->get_parent();
  924. if (dp) {
  925. dp->remove_child(drives);
  926. }
  927. dp = dir_access->drives_are_shortcuts() ? shortcuts_container : drives_container;
  928. dp->add_child(drives);
  929. drives->show();
  930. for (int i = 0; i < dir_access->get_drive_count(); i++) {
  931. String d = dir_access->get_drive(i);
  932. drives->add_item(dir_access->get_drive(i));
  933. }
  934. drives->select(dir_access->get_current_drive());
  935. }
  936. }
  937. void EditorFileDialog::_favorite_selected(int p_idx) {
  938. dir_access->change_dir(favorites->get_item_metadata(p_idx));
  939. file->set_text("");
  940. update_dir();
  941. invalidate();
  942. _push_history();
  943. }
  944. void EditorFileDialog::_favorite_move_up() {
  945. int current = favorites->get_current();
  946. if (current > 0 && current < favorites->get_item_count()) {
  947. Vector<String> favorited = EditorSettings::get_singleton()->get_favorites();
  948. int a_idx = favorited.find(String(favorites->get_item_metadata(current - 1)));
  949. int b_idx = favorited.find(String(favorites->get_item_metadata(current)));
  950. if (a_idx == -1 || b_idx == -1)
  951. return;
  952. SWAP(favorited.write[a_idx], favorited.write[b_idx]);
  953. EditorSettings::get_singleton()->set_favorites(favorited);
  954. _update_favorites();
  955. update_file_list();
  956. }
  957. }
  958. void EditorFileDialog::_favorite_move_down() {
  959. int current = favorites->get_current();
  960. if (current >= 0 && current < favorites->get_item_count() - 1) {
  961. Vector<String> favorited = EditorSettings::get_singleton()->get_favorites();
  962. int a_idx = favorited.find(String(favorites->get_item_metadata(current + 1)));
  963. int b_idx = favorited.find(String(favorites->get_item_metadata(current)));
  964. if (a_idx == -1 || b_idx == -1)
  965. return;
  966. SWAP(favorited.write[a_idx], favorited.write[b_idx]);
  967. EditorSettings::get_singleton()->set_favorites(favorited);
  968. _update_favorites();
  969. update_file_list();
  970. }
  971. }
  972. void EditorFileDialog::_update_favorites() {
  973. bool res = access == ACCESS_RESOURCES;
  974. String current = get_current_dir();
  975. Ref<Texture2D> folder_icon = item_list->get_theme_icon("Folder", "EditorIcons");
  976. const Color folder_color = item_list->get_theme_color("folder_icon_modulate", "FileDialog");
  977. favorites->clear();
  978. favorite->set_pressed(false);
  979. Vector<String> favorited = EditorSettings::get_singleton()->get_favorites();
  980. for (int i = 0; i < favorited.size(); i++) {
  981. bool cres = favorited[i].begins_with("res://");
  982. if (cres != res)
  983. continue;
  984. String name = favorited[i];
  985. bool setthis = false;
  986. if (res && name == "res://") {
  987. if (name == current)
  988. setthis = true;
  989. name = "/";
  990. favorites->add_item(name, folder_icon);
  991. } else if (name.ends_with("/")) {
  992. if (name == current || name == current + "/")
  993. setthis = true;
  994. name = name.substr(0, name.length() - 1);
  995. name = name.get_file();
  996. favorites->add_item(name, folder_icon);
  997. } else {
  998. continue; // We don't handle favorite files here.
  999. }
  1000. favorites->set_item_metadata(favorites->get_item_count() - 1, favorited[i]);
  1001. favorites->set_item_icon_modulate(favorites->get_item_count() - 1, folder_color);
  1002. if (setthis) {
  1003. favorite->set_pressed(true);
  1004. favorites->set_current(favorites->get_item_count() - 1);
  1005. recent->unselect_all();
  1006. }
  1007. }
  1008. }
  1009. void EditorFileDialog::_favorite_pressed() {
  1010. bool res = access == ACCESS_RESOURCES;
  1011. String cd = get_current_dir();
  1012. if (!cd.ends_with("/"))
  1013. cd += "/";
  1014. Vector<String> favorited = EditorSettings::get_singleton()->get_favorites();
  1015. bool found = false;
  1016. for (int i = 0; i < favorited.size(); i++) {
  1017. bool cres = favorited[i].begins_with("res://");
  1018. if (cres != res)
  1019. continue;
  1020. if (favorited[i] == cd) {
  1021. found = true;
  1022. break;
  1023. }
  1024. }
  1025. if (found)
  1026. favorited.erase(cd);
  1027. else
  1028. favorited.push_back(cd);
  1029. EditorSettings::get_singleton()->set_favorites(favorited);
  1030. _update_favorites();
  1031. }
  1032. void EditorFileDialog::_recent_selected(int p_idx) {
  1033. Vector<String> recentd = EditorSettings::get_singleton()->get_recent_dirs();
  1034. ERR_FAIL_INDEX(p_idx, recentd.size());
  1035. dir_access->change_dir(recent->get_item_metadata(p_idx));
  1036. update_file_list();
  1037. update_dir();
  1038. _push_history();
  1039. }
  1040. void EditorFileDialog::_go_up() {
  1041. dir_access->change_dir("..");
  1042. update_file_list();
  1043. update_dir();
  1044. _push_history();
  1045. }
  1046. void EditorFileDialog::_go_back() {
  1047. if (local_history_pos <= 0) {
  1048. return;
  1049. }
  1050. local_history_pos--;
  1051. dir_access->change_dir(local_history[local_history_pos]);
  1052. update_file_list();
  1053. update_dir();
  1054. dir_prev->set_disabled(local_history_pos == 0);
  1055. dir_next->set_disabled(local_history_pos == local_history.size() - 1);
  1056. }
  1057. void EditorFileDialog::_go_forward() {
  1058. if (local_history_pos == local_history.size() - 1) {
  1059. return;
  1060. }
  1061. local_history_pos++;
  1062. dir_access->change_dir(local_history[local_history_pos]);
  1063. update_file_list();
  1064. update_dir();
  1065. dir_prev->set_disabled(local_history_pos == 0);
  1066. dir_next->set_disabled(local_history_pos == local_history.size() - 1);
  1067. }
  1068. bool EditorFileDialog::default_show_hidden_files = false;
  1069. EditorFileDialog::DisplayMode EditorFileDialog::default_display_mode = DISPLAY_THUMBNAILS;
  1070. void EditorFileDialog::set_display_mode(DisplayMode p_mode) {
  1071. if (display_mode == p_mode)
  1072. return;
  1073. if (p_mode == DISPLAY_THUMBNAILS) {
  1074. mode_list->set_pressed(false);
  1075. mode_thumbnails->set_pressed(true);
  1076. } else {
  1077. mode_thumbnails->set_pressed(false);
  1078. mode_list->set_pressed(true);
  1079. }
  1080. display_mode = p_mode;
  1081. invalidate();
  1082. }
  1083. EditorFileDialog::DisplayMode EditorFileDialog::get_display_mode() const {
  1084. return display_mode;
  1085. }
  1086. void EditorFileDialog::_bind_methods() {
  1087. ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorFileDialog::_unhandled_input);
  1088. ClassDB::bind_method(D_METHOD("_cancel_pressed"), &EditorFileDialog::_cancel_pressed);
  1089. ClassDB::bind_method(D_METHOD("clear_filters"), &EditorFileDialog::clear_filters);
  1090. ClassDB::bind_method(D_METHOD("add_filter", "filter"), &EditorFileDialog::add_filter);
  1091. ClassDB::bind_method(D_METHOD("get_current_dir"), &EditorFileDialog::get_current_dir);
  1092. ClassDB::bind_method(D_METHOD("get_current_file"), &EditorFileDialog::get_current_file);
  1093. ClassDB::bind_method(D_METHOD("get_current_path"), &EditorFileDialog::get_current_path);
  1094. ClassDB::bind_method(D_METHOD("set_current_dir", "dir"), &EditorFileDialog::set_current_dir);
  1095. ClassDB::bind_method(D_METHOD("set_current_file", "file"), &EditorFileDialog::set_current_file);
  1096. ClassDB::bind_method(D_METHOD("set_current_path", "path"), &EditorFileDialog::set_current_path);
  1097. ClassDB::bind_method(D_METHOD("set_file_mode", "mode"), &EditorFileDialog::set_file_mode);
  1098. ClassDB::bind_method(D_METHOD("get_file_mode"), &EditorFileDialog::get_file_mode);
  1099. ClassDB::bind_method(D_METHOD("get_vbox"), &EditorFileDialog::get_vbox);
  1100. ClassDB::bind_method(D_METHOD("set_access", "access"), &EditorFileDialog::set_access);
  1101. ClassDB::bind_method(D_METHOD("get_access"), &EditorFileDialog::get_access);
  1102. ClassDB::bind_method(D_METHOD("set_show_hidden_files", "show"), &EditorFileDialog::set_show_hidden_files);
  1103. ClassDB::bind_method(D_METHOD("is_showing_hidden_files"), &EditorFileDialog::is_showing_hidden_files);
  1104. ClassDB::bind_method(D_METHOD("_update_file_name"), &EditorFileDialog::update_file_name);
  1105. ClassDB::bind_method(D_METHOD("_update_dir"), &EditorFileDialog::update_dir);
  1106. ClassDB::bind_method(D_METHOD("_update_file_list"), &EditorFileDialog::update_file_list);
  1107. ClassDB::bind_method(D_METHOD("_thumbnail_done"), &EditorFileDialog::_thumbnail_done);
  1108. ClassDB::bind_method(D_METHOD("set_display_mode", "mode"), &EditorFileDialog::set_display_mode);
  1109. ClassDB::bind_method(D_METHOD("get_display_mode"), &EditorFileDialog::get_display_mode);
  1110. ClassDB::bind_method(D_METHOD("_thumbnail_result"), &EditorFileDialog::_thumbnail_result);
  1111. ClassDB::bind_method(D_METHOD("set_disable_overwrite_warning", "disable"), &EditorFileDialog::set_disable_overwrite_warning);
  1112. ClassDB::bind_method(D_METHOD("is_overwrite_warning_disabled"), &EditorFileDialog::is_overwrite_warning_disabled);
  1113. ClassDB::bind_method(D_METHOD("invalidate"), &EditorFileDialog::invalidate);
  1114. ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path")));
  1115. ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths")));
  1116. ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir")));
  1117. ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access");
  1118. ADD_PROPERTY(PropertyInfo(Variant::INT, "display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"), "set_display_mode", "get_display_mode");
  1119. ADD_PROPERTY(PropertyInfo(Variant::INT, "file_mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"), "set_file_mode", "get_file_mode");
  1120. ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR), "set_current_dir", "get_current_dir");
  1121. ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file", PROPERTY_HINT_FILE, "*"), "set_current_file", "get_current_file");
  1122. ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path"), "set_current_path", "get_current_path");
  1123. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files");
  1124. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_overwrite_warning"), "set_disable_overwrite_warning", "is_overwrite_warning_disabled");
  1125. BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILE);
  1126. BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILES);
  1127. BIND_ENUM_CONSTANT(FILE_MODE_OPEN_DIR);
  1128. BIND_ENUM_CONSTANT(FILE_MODE_OPEN_ANY);
  1129. BIND_ENUM_CONSTANT(FILE_MODE_SAVE_FILE);
  1130. BIND_ENUM_CONSTANT(ACCESS_RESOURCES);
  1131. BIND_ENUM_CONSTANT(ACCESS_USERDATA);
  1132. BIND_ENUM_CONSTANT(ACCESS_FILESYSTEM);
  1133. BIND_ENUM_CONSTANT(DISPLAY_THUMBNAILS);
  1134. BIND_ENUM_CONSTANT(DISPLAY_LIST);
  1135. }
  1136. void EditorFileDialog::set_show_hidden_files(bool p_show) {
  1137. show_hidden_files = p_show;
  1138. show_hidden->set_pressed(p_show);
  1139. invalidate();
  1140. }
  1141. bool EditorFileDialog::is_showing_hidden_files() const {
  1142. return show_hidden_files;
  1143. }
  1144. void EditorFileDialog::set_default_show_hidden_files(bool p_show) {
  1145. default_show_hidden_files = p_show;
  1146. }
  1147. void EditorFileDialog::set_default_display_mode(DisplayMode p_mode) {
  1148. default_display_mode = p_mode;
  1149. }
  1150. void EditorFileDialog::_save_to_recent() {
  1151. String dir = get_current_dir();
  1152. Vector<String> recent = EditorSettings::get_singleton()->get_recent_dirs();
  1153. const int max = 20;
  1154. int count = 0;
  1155. bool res = dir.begins_with("res://");
  1156. for (int i = 0; i < recent.size(); i++) {
  1157. bool cres = recent[i].begins_with("res://");
  1158. if (recent[i] == dir || (res == cres && count > max)) {
  1159. recent.remove(i);
  1160. i--;
  1161. } else {
  1162. count++;
  1163. }
  1164. }
  1165. recent.insert(0, dir);
  1166. EditorSettings::get_singleton()->set_recent_dirs(recent);
  1167. }
  1168. void EditorFileDialog::set_disable_overwrite_warning(bool p_disable) {
  1169. disable_overwrite_warning = p_disable;
  1170. }
  1171. bool EditorFileDialog::is_overwrite_warning_disabled() const {
  1172. return disable_overwrite_warning;
  1173. }
  1174. EditorFileDialog::EditorFileDialog() {
  1175. show_hidden_files = default_show_hidden_files;
  1176. display_mode = default_display_mode;
  1177. local_history_pos = 0;
  1178. disable_overwrite_warning = false;
  1179. VBoxContainer *vbc = memnew(VBoxContainer);
  1180. add_child(vbc);
  1181. mode = FILE_MODE_SAVE_FILE;
  1182. set_title(TTR("Save a File"));
  1183. ED_SHORTCUT("file_dialog/go_back", TTR("Go Back"), KEY_MASK_ALT | KEY_LEFT);
  1184. ED_SHORTCUT("file_dialog/go_forward", TTR("Go Forward"), KEY_MASK_ALT | KEY_RIGHT);
  1185. ED_SHORTCUT("file_dialog/go_up", TTR("Go Up"), KEY_MASK_ALT | KEY_UP);
  1186. ED_SHORTCUT("file_dialog/refresh", TTR("Refresh"), KEY_F5);
  1187. ED_SHORTCUT("file_dialog/toggle_hidden_files", TTR("Toggle Hidden Files"), KEY_MASK_CMD | KEY_H);
  1188. ED_SHORTCUT("file_dialog/toggle_favorite", TTR("Toggle Favorite"), KEY_MASK_ALT | KEY_F);
  1189. ED_SHORTCUT("file_dialog/toggle_mode", TTR("Toggle Mode"), KEY_MASK_ALT | KEY_V);
  1190. ED_SHORTCUT("file_dialog/create_folder", TTR("Create Folder"), KEY_MASK_CMD | KEY_N);
  1191. ED_SHORTCUT("file_dialog/delete", TTR("Delete"), KEY_DELETE);
  1192. ED_SHORTCUT("file_dialog/focus_path", TTR("Focus Path"), KEY_MASK_CMD | KEY_D);
  1193. ED_SHORTCUT("file_dialog/move_favorite_up", TTR("Move Favorite Up"), KEY_MASK_CMD | KEY_UP);
  1194. ED_SHORTCUT("file_dialog/move_favorite_down", TTR("Move Favorite Down"), KEY_MASK_CMD | KEY_DOWN);
  1195. HBoxContainer *pathhb = memnew(HBoxContainer);
  1196. dir_prev = memnew(ToolButton);
  1197. dir_prev->set_tooltip(TTR("Go to previous folder."));
  1198. dir_next = memnew(ToolButton);
  1199. dir_next->set_tooltip(TTR("Go to next folder."));
  1200. dir_up = memnew(ToolButton);
  1201. dir_up->set_tooltip(TTR("Go to parent folder."));
  1202. pathhb->add_child(dir_prev);
  1203. pathhb->add_child(dir_next);
  1204. pathhb->add_child(dir_up);
  1205. dir_prev->connect("pressed", callable_mp(this, &EditorFileDialog::_go_back));
  1206. dir_next->connect("pressed", callable_mp(this, &EditorFileDialog::_go_forward));
  1207. dir_up->connect("pressed", callable_mp(this, &EditorFileDialog::_go_up));
  1208. pathhb->add_child(memnew(Label(TTR("Path:"))));
  1209. drives_container = memnew(HBoxContainer);
  1210. pathhb->add_child(drives_container);
  1211. dir = memnew(LineEdit);
  1212. pathhb->add_child(dir);
  1213. dir->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  1214. refresh = memnew(ToolButton);
  1215. refresh->set_tooltip(TTR("Refresh files."));
  1216. refresh->connect("pressed", callable_mp(this, &EditorFileDialog::update_file_list));
  1217. pathhb->add_child(refresh);
  1218. favorite = memnew(ToolButton);
  1219. favorite->set_toggle_mode(true);
  1220. favorite->set_tooltip(TTR("(Un)favorite current folder."));
  1221. favorite->connect("pressed", callable_mp(this, &EditorFileDialog::_favorite_pressed));
  1222. pathhb->add_child(favorite);
  1223. show_hidden = memnew(ToolButton);
  1224. show_hidden->set_toggle_mode(true);
  1225. show_hidden->set_pressed(is_showing_hidden_files());
  1226. show_hidden->set_tooltip(TTR("Toggle the visibility of hidden files."));
  1227. show_hidden->connect("toggled", callable_mp(this, &EditorFileDialog::set_show_hidden_files));
  1228. pathhb->add_child(show_hidden);
  1229. pathhb->add_child(memnew(VSeparator));
  1230. Ref<ButtonGroup> view_mode_group;
  1231. view_mode_group.instance();
  1232. mode_thumbnails = memnew(ToolButton);
  1233. mode_thumbnails->connect("pressed", callable_mp(this, &EditorFileDialog::set_display_mode), varray(DISPLAY_THUMBNAILS));
  1234. mode_thumbnails->set_toggle_mode(true);
  1235. mode_thumbnails->set_pressed(display_mode == DISPLAY_THUMBNAILS);
  1236. mode_thumbnails->set_button_group(view_mode_group);
  1237. mode_thumbnails->set_tooltip(TTR("View items as a grid of thumbnails."));
  1238. pathhb->add_child(mode_thumbnails);
  1239. mode_list = memnew(ToolButton);
  1240. mode_list->connect("pressed", callable_mp(this, &EditorFileDialog::set_display_mode), varray(DISPLAY_LIST));
  1241. mode_list->set_toggle_mode(true);
  1242. mode_list->set_pressed(display_mode == DISPLAY_LIST);
  1243. mode_list->set_button_group(view_mode_group);
  1244. mode_list->set_tooltip(TTR("View items as a list."));
  1245. pathhb->add_child(mode_list);
  1246. shortcuts_container = memnew(HBoxContainer);
  1247. pathhb->add_child(shortcuts_container);
  1248. drives = memnew(OptionButton);
  1249. drives->connect("item_selected", callable_mp(this, &EditorFileDialog::_select_drive));
  1250. pathhb->add_child(drives);
  1251. makedir = memnew(Button);
  1252. makedir->set_text(TTR("Create Folder"));
  1253. makedir->connect("pressed", callable_mp(this, &EditorFileDialog::_make_dir));
  1254. pathhb->add_child(makedir);
  1255. list_hb = memnew(HSplitContainer);
  1256. vbc->add_child(pathhb);
  1257. vbc->add_child(list_hb);
  1258. list_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1259. VSplitContainer *vsc = memnew(VSplitContainer);
  1260. list_hb->add_child(vsc);
  1261. VBoxContainer *fav_vb = memnew(VBoxContainer);
  1262. vsc->add_child(fav_vb);
  1263. fav_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE);
  1264. fav_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1265. HBoxContainer *fav_hb = memnew(HBoxContainer);
  1266. fav_vb->add_child(fav_hb);
  1267. fav_hb->add_child(memnew(Label(TTR("Favorites:"))));
  1268. fav_hb->add_spacer();
  1269. fav_up = memnew(ToolButton);
  1270. fav_hb->add_child(fav_up);
  1271. fav_up->connect("pressed", callable_mp(this, &EditorFileDialog::_favorite_move_up));
  1272. fav_down = memnew(ToolButton);
  1273. fav_hb->add_child(fav_down);
  1274. fav_down->connect("pressed", callable_mp(this, &EditorFileDialog::_favorite_move_down));
  1275. favorites = memnew(ItemList);
  1276. fav_vb->add_child(favorites);
  1277. favorites->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1278. favorites->connect("item_selected", callable_mp(this, &EditorFileDialog::_favorite_selected));
  1279. VBoxContainer *rec_vb = memnew(VBoxContainer);
  1280. vsc->add_child(rec_vb);
  1281. rec_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE);
  1282. rec_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1283. recent = memnew(ItemList);
  1284. recent->set_allow_reselect(true);
  1285. rec_vb->add_margin_child(TTR("Recent:"), recent, true);
  1286. recent->connect("item_selected", callable_mp(this, &EditorFileDialog::_recent_selected));
  1287. VBoxContainer *item_vb = memnew(VBoxContainer);
  1288. list_hb->add_child(item_vb);
  1289. item_vb->set_custom_minimum_size(Size2(320, 0) * EDSCALE);
  1290. HBoxContainer *preview_hb = memnew(HBoxContainer);
  1291. preview_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1292. item_vb->add_child(preview_hb);
  1293. VBoxContainer *list_vb = memnew(VBoxContainer);
  1294. list_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  1295. list_vb->add_child(memnew(Label(TTR("Directories & Files:"))));
  1296. preview_hb->add_child(list_vb);
  1297. // Item (files and folders) list with context menu.
  1298. item_list = memnew(ItemList);
  1299. item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1300. item_list->connect("item_rmb_selected", callable_mp(this, &EditorFileDialog::_item_list_item_rmb_selected));
  1301. item_list->connect("rmb_clicked", callable_mp(this, &EditorFileDialog::_item_list_rmb_clicked));
  1302. item_list->set_allow_rmb_select(true);
  1303. list_vb->add_child(item_list);
  1304. item_menu = memnew(PopupMenu);
  1305. item_menu->connect("id_pressed", callable_mp(this, &EditorFileDialog::_item_menu_id_pressed));
  1306. add_child(item_menu);
  1307. // Other stuff.
  1308. preview_vb = memnew(VBoxContainer);
  1309. preview_hb->add_child(preview_vb);
  1310. CenterContainer *prev_cc = memnew(CenterContainer);
  1311. preview_vb->add_margin_child(TTR("Preview:"), prev_cc);
  1312. preview = memnew(TextureRect);
  1313. prev_cc->add_child(preview);
  1314. preview_vb->hide();
  1315. file_box = memnew(HBoxContainer);
  1316. file_box->add_child(memnew(Label(TTR("File:"))));
  1317. file = memnew(LineEdit);
  1318. file->set_stretch_ratio(4);
  1319. file->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  1320. file_box->add_child(file);
  1321. filter = memnew(OptionButton);
  1322. filter->set_stretch_ratio(3);
  1323. filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  1324. filter->set_clip_text(true); // Too many extensions overflow it.
  1325. file_box->add_child(filter);
  1326. file_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
  1327. item_vb->add_child(file_box);
  1328. dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
  1329. access = ACCESS_RESOURCES;
  1330. _update_drives();
  1331. connect("confirmed", callable_mp(this, &EditorFileDialog::_action_pressed));
  1332. item_list->connect("item_selected", callable_mp(this, &EditorFileDialog::_item_selected), varray(), CONNECT_DEFERRED);
  1333. item_list->connect("multi_selected", callable_mp(this, &EditorFileDialog::_multi_selected), varray(), CONNECT_DEFERRED);
  1334. item_list->connect("item_activated", callable_mp(this, &EditorFileDialog::_item_dc_selected), varray());
  1335. item_list->connect("nothing_selected", callable_mp(this, &EditorFileDialog::_items_clear_selection));
  1336. dir->connect("text_entered", callable_mp(this, &EditorFileDialog::_dir_entered));
  1337. file->connect("text_entered", callable_mp(this, &EditorFileDialog::_file_entered));
  1338. filter->connect("item_selected", callable_mp(this, &EditorFileDialog::_filter_selected));
  1339. confirm_save = memnew(ConfirmationDialog);
  1340. //confirm_save->set_as_toplevel(true);
  1341. add_child(confirm_save);
  1342. confirm_save->connect("confirmed", callable_mp(this, &EditorFileDialog::_save_confirm_pressed));
  1343. remove_dialog = memnew(DependencyRemoveDialog);
  1344. add_child(remove_dialog);
  1345. makedialog = memnew(ConfirmationDialog);
  1346. makedialog->set_title(TTR("Create Folder"));
  1347. VBoxContainer *makevb = memnew(VBoxContainer);
  1348. makedialog->add_child(makevb);
  1349. makedirname = memnew(LineEdit);
  1350. makevb->add_margin_child(TTR("Name:"), makedirname);
  1351. add_child(makedialog);
  1352. makedialog->register_text_enter(makedirname);
  1353. makedialog->connect("confirmed", callable_mp(this, &EditorFileDialog::_make_dir_confirm));
  1354. mkdirerr = memnew(AcceptDialog);
  1355. mkdirerr->set_text(TTR("Could not create folder."));
  1356. add_child(mkdirerr);
  1357. exterr = memnew(AcceptDialog);
  1358. exterr->set_text(TTR("Must use a valid extension."));
  1359. add_child(exterr);
  1360. update_filters();
  1361. update_dir();
  1362. set_hide_on_ok(false);
  1363. vbox = vbc;
  1364. invalidated = true;
  1365. if (register_func)
  1366. register_func(this);
  1367. preview_wheel_timeout = 0;
  1368. preview_wheel_index = 0;
  1369. preview_waiting = false;
  1370. }
  1371. EditorFileDialog::~EditorFileDialog() {
  1372. if (unregister_func)
  1373. unregister_func(this);
  1374. memdelete(dir_access);
  1375. }