ProjectWindow.cs 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using BansheeEngine;
  5. namespace BansheeEditor
  6. {
  7. internal enum ProjectViewType
  8. {
  9. Grid64, Grid48, Grid32, List16
  10. }
  11. internal sealed class ProjectWindow : EditorWindow
  12. {
  13. private class ContentInfo
  14. {
  15. public ContentInfo(ProjectWindow window, ProjectViewType viewType, int numEntries)
  16. {
  17. GUIPanel parentPanel = window.scrollAreaPanel;
  18. GUIPanel contentPanel = parentPanel.AddPanel(1);
  19. overlay = parentPanel.AddPanel(0);
  20. underlay = parentPanel.AddPanel(2);
  21. main = contentPanel.AddLayoutY();
  22. if (viewType == ProjectViewType.List16)
  23. {
  24. tileSize = 16;
  25. gridLayout = false;
  26. elementsPerRow = 1;
  27. }
  28. else
  29. {
  30. switch (viewType)
  31. {
  32. case ProjectViewType.Grid64:
  33. tileSize = 64;
  34. break;
  35. case ProjectViewType.Grid48:
  36. tileSize = 48;
  37. break;
  38. case ProjectViewType.Grid32:
  39. tileSize = 32;
  40. break;
  41. }
  42. gridLayout = true;
  43. Rect2I scrollBounds = window.contentScrollArea.Bounds;
  44. int availableWidth = scrollBounds.width;
  45. int elemSize = tileSize + GRID_ENTRY_SPACING;
  46. elementsPerRow = (availableWidth - GRID_ENTRY_SPACING * 2) / elemSize;
  47. int numRows = MathEx.CeilToInt(numEntries / (float)elementsPerRow);
  48. int neededHeight = numRows * (elemSize);
  49. bool requiresScrollbar = neededHeight > scrollBounds.height;
  50. if (requiresScrollbar)
  51. {
  52. availableWidth -= window.contentScrollArea.ScrollBarWidth;
  53. elementsPerRow = (availableWidth - GRID_ENTRY_SPACING * 2) / elemSize;
  54. }
  55. labelWidth = (availableWidth - (elementsPerRow + 1)*MIN_HORZ_SPACING) / elementsPerRow;
  56. }
  57. this.window = window;
  58. }
  59. public GUILayout main;
  60. public GUIPanel overlay;
  61. public GUIPanel underlay;
  62. public ProjectWindow window;
  63. public int tileSize;
  64. public bool gridLayout;
  65. public int elementsPerRow;
  66. public int labelWidth;
  67. }
  68. private class ElementEntry
  69. {
  70. // Note: Order of these is relevant
  71. enum UnderlayState
  72. {
  73. None, Hovered, Selected, Pinged
  74. }
  75. public int index;
  76. public string path;
  77. public GUITexture icon;
  78. public GUILabel label;
  79. public Rect2I bounds;
  80. private GUITexture underlay;
  81. private ContentInfo info;
  82. private UnderlayState underlayState;
  83. private GUITextBox renameTextBox;
  84. public ElementEntry(ContentInfo info, GUILayout parent, LibraryEntry entry, int index)
  85. {
  86. GUILayout entryLayout;
  87. if (info.gridLayout)
  88. entryLayout = parent.AddLayoutY();
  89. else
  90. entryLayout = parent.AddLayoutX();
  91. SpriteTexture iconTexture = GetIcon(entry);
  92. icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit,
  93. true, GUIOption.FixedHeight(info.tileSize), GUIOption.FixedWidth(info.tileSize));
  94. label = null;
  95. if (info.gridLayout)
  96. {
  97. label = new GUILabel(entry.Name, EditorStyles.MultiLineLabel,
  98. GUIOption.FixedWidth(info.labelWidth), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
  99. }
  100. else
  101. {
  102. label = new GUILabel(entry.Name);
  103. }
  104. entryLayout.AddElement(icon);
  105. entryLayout.AddElement(label);
  106. this.info = info;
  107. this.index = index;
  108. this.path = entry.Path;
  109. this.bounds = new Rect2I();
  110. this.underlay = null;
  111. }
  112. public void Initialize()
  113. {
  114. bounds = icon.Bounds;
  115. Rect2I labelBounds = label.Bounds;
  116. bounds.x = MathEx.Min(bounds.x, labelBounds.x);
  117. bounds.y = MathEx.Min(bounds.y, labelBounds.y) - 5; // 5 - Just padding for better look
  118. bounds.width = MathEx.Max(bounds.x + bounds.width,
  119. labelBounds.x + labelBounds.width) - bounds.x;
  120. bounds.height = MathEx.Max(bounds.y + bounds.height,
  121. labelBounds.y + labelBounds.height) - bounds.y;
  122. ProjectWindow hoistedWindow = info.window;
  123. string hoistedPath = path;
  124. GUIButton overlayBtn = new GUIButton("", EditorStyles.Blank);
  125. overlayBtn.Bounds = bounds;
  126. overlayBtn.OnClick += () => hoistedWindow.OnEntryClicked(hoistedPath);
  127. overlayBtn.OnDoubleClick += () => hoistedWindow.OnEntryDoubleClicked(hoistedPath);
  128. overlayBtn.SetContextMenu(info.window.entryContextMenu);
  129. info.overlay.AddElement(overlayBtn);
  130. }
  131. public Rect2I Bounds
  132. {
  133. get { return bounds; }
  134. }
  135. public void MarkAsCut(bool enable)
  136. {
  137. if (enable)
  138. icon.SetTint(CUT_COLOR);
  139. else
  140. icon.SetTint(Color.White);
  141. }
  142. public void MarkAsSelected(bool enable)
  143. {
  144. if ((int)underlayState > (int) UnderlayState.Selected)
  145. return;
  146. if (enable)
  147. {
  148. CreateUnderlay();
  149. underlay.SetTint(SELECTION_COLOR);
  150. }
  151. else
  152. ClearUnderlay();
  153. underlayState = UnderlayState.Selected;
  154. }
  155. public void MarkAsPinged(bool enable)
  156. {
  157. if ((int)underlayState > (int)UnderlayState.Pinged)
  158. return;
  159. if (enable)
  160. {
  161. CreateUnderlay();
  162. underlay.SetTint(PING_COLOR);
  163. }
  164. else
  165. ClearUnderlay();
  166. underlayState = UnderlayState.Pinged;
  167. }
  168. public void MarkAsHovered(bool enable)
  169. {
  170. if ((int)underlayState > (int)UnderlayState.Hovered)
  171. return;
  172. if (enable)
  173. {
  174. CreateUnderlay();
  175. underlay.SetTint(HOVER_COLOR);
  176. }
  177. else
  178. ClearUnderlay();
  179. underlayState = UnderlayState.Hovered;
  180. }
  181. public void StartRename()
  182. {
  183. if (renameTextBox != null)
  184. return;
  185. renameTextBox = new GUITextBox(false);
  186. renameTextBox.Bounds = label.Bounds;
  187. info.overlay.AddElement(renameTextBox);
  188. label.Visible = false;
  189. }
  190. public void StopRename()
  191. {
  192. if (renameTextBox != null)
  193. {
  194. renameTextBox.Destroy();
  195. renameTextBox = null;
  196. }
  197. label.Visible = true;
  198. }
  199. public string GetRenamedName()
  200. {
  201. if (renameTextBox != null)
  202. return renameTextBox.Text;
  203. return "";
  204. }
  205. private void ClearUnderlay()
  206. {
  207. if (underlay != null)
  208. {
  209. underlay.Destroy();
  210. underlay = null;
  211. }
  212. underlayState = UnderlayState.None;
  213. }
  214. private void CreateUnderlay()
  215. {
  216. if (underlay == null)
  217. {
  218. underlay = new GUITexture(Builtin.WhiteTexture);
  219. underlay.Bounds = Bounds;
  220. info.underlay.AddElement(underlay);
  221. }
  222. }
  223. private static SpriteTexture GetIcon(LibraryEntry entry)
  224. {
  225. if (entry.Type == LibraryEntryType.Directory)
  226. {
  227. return EditorBuiltin.FolderIcon;
  228. }
  229. else
  230. {
  231. FileEntry fileEntry = (FileEntry)entry;
  232. switch (fileEntry.ResType)
  233. {
  234. case ResourceType.Font:
  235. return EditorBuiltin.FontIcon;
  236. case ResourceType.Mesh:
  237. return EditorBuiltin.MeshIcon;
  238. case ResourceType.Texture:
  239. return EditorBuiltin.TextureIcon;
  240. case ResourceType.PlainText:
  241. return EditorBuiltin.PlainTextIcon;
  242. case ResourceType.ScriptCode:
  243. return EditorBuiltin.ScriptCodeIcon;
  244. case ResourceType.SpriteTexture:
  245. return EditorBuiltin.SpriteTextureIcon;
  246. case ResourceType.Shader:
  247. return EditorBuiltin.ShaderIcon;
  248. case ResourceType.Material:
  249. return EditorBuiltin.MaterialIcon;
  250. }
  251. }
  252. return null;
  253. }
  254. }
  255. enum MoveDirection
  256. {
  257. Up, Down, Left, Right
  258. }
  259. private const int GRID_ENTRY_SPACING = 15;
  260. private const int LIST_ENTRY_SPACING = 7;
  261. private const int MAX_LABEL_HEIGHT = 50;
  262. private const int MIN_HORZ_SPACING = 5;
  263. private const int DRAG_SCROLL_HEIGHT = 20;
  264. private const int DRAG_SCROLL_AMOUNT_PER_SECOND = 100;
  265. private const int FOLDER_BUTTON_WIDTH = 20;
  266. private const int FOLDER_SEPARATOR_WIDTH = 7;
  267. private static readonly Color PING_COLOR = Color.BansheeOrange;
  268. private static readonly Color SELECTION_COLOR = Color.DarkCyan;
  269. private static readonly Color HOVER_COLOR = new Color(Color.DarkCyan.r, Color.DarkCyan.g, Color.DarkCyan.b, 0.5f);
  270. private static readonly Color CUT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
  271. private bool hasContentFocus = false;
  272. private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
  273. private string searchQuery;
  274. private bool IsSearchActive { get { return !string.IsNullOrEmpty(searchQuery); } }
  275. private ProjectViewType viewType = ProjectViewType.Grid32;
  276. private bool requiresRefresh;
  277. private string currentDirectory = "";
  278. private List<string> selectionPaths = new List<string>();
  279. private int selectionAnchorStart = -1;
  280. private int selectionAnchorEnd = -1;
  281. private string pingPath = "";
  282. private string hoverHighlightPath = "";
  283. private ContentInfo contentInfo;
  284. private GUIScrollArea contentScrollArea;
  285. private GUIPanel scrollAreaPanel;
  286. private GUILayoutX searchBarLayout;
  287. private GUIButton optionsButton;
  288. private GUILayout folderBarLayout;
  289. private GUILayout folderListLayout;
  290. private GUITextField searchField;
  291. private GUITexture dragSelection;
  292. private ContextMenu entryContextMenu;
  293. private ProjectDropTarget dropTarget;
  294. private List<ElementEntry> entries = new List<ElementEntry>();
  295. private Dictionary<string, ElementEntry> entryLookup = new Dictionary<string, ElementEntry>();
  296. private int autoScrollAmount;
  297. private bool isDraggingSelection;
  298. private Vector2I dragSelectionStart;
  299. private Vector2I dragSelectionEnd;
  300. private ElementEntry inProgressRenameElement;
  301. // Cut/Copy/Paste
  302. private List<string> copyPaths = new List<string>();
  303. private List<string> cutPaths = new List<string>();
  304. internal ProjectViewType ViewType
  305. {
  306. get { return viewType; }
  307. set { viewType = value; Refresh(); }
  308. }
  309. [MenuItem("Windows/Project", ButtonModifier.Ctrl, ButtonCode.P)]
  310. private static void OpenProjectWindow()
  311. {
  312. OpenWindow<ProjectWindow>();
  313. }
  314. private void OnInitialize()
  315. {
  316. ProjectLibrary.OnEntryAdded += OnEntryChanged;
  317. ProjectLibrary.OnEntryRemoved += OnEntryChanged;
  318. GUILayoutY contentLayout = GUI.AddLayoutY();
  319. searchBarLayout = contentLayout.AddLayoutX();
  320. searchField = new GUITextField();
  321. searchField.OnChanged += OnSearchChanged;
  322. GUIButton clearSearchBtn = new GUIButton("C");
  323. clearSearchBtn.OnClick += ClearSearch;
  324. clearSearchBtn.SetWidth(40);
  325. optionsButton = new GUIButton("O");
  326. optionsButton.OnClick += OpenOptionsWindow;
  327. optionsButton.SetWidth(40);
  328. searchBarLayout.AddElement(searchField);
  329. searchBarLayout.AddElement(clearSearchBtn);
  330. searchBarLayout.AddElement(optionsButton);
  331. folderBarLayout = contentLayout.AddLayoutX();
  332. GUIButton homeButton = new GUIButton("H", GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
  333. homeButton.OnClick += OnHomeClicked;
  334. GUIButton upButton = new GUIButton("U", GUIOption.FixedWidth(FOLDER_BUTTON_WIDTH));
  335. upButton.OnClick += OnUpClicked;
  336. folderBarLayout.AddElement(homeButton);
  337. folderBarLayout.AddElement(upButton);
  338. folderBarLayout.AddSpace(10);
  339. contentScrollArea = new GUIScrollArea(GUIOption.FlexibleWidth(), GUIOption.FlexibleHeight());
  340. contentLayout.AddElement(contentScrollArea);
  341. contentLayout.AddFlexibleSpace();
  342. entryContextMenu = new ContextMenu();
  343. entryContextMenu.AddItem("Rename", RenameSelection, new ShortcutKey(ButtonModifier.None, ButtonCode.F2));
  344. entryContextMenu.AddSeparator("");
  345. entryContextMenu.AddItem("Cut", CutSelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.X));
  346. entryContextMenu.AddItem("Copy", CopySelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.C));
  347. entryContextMenu.AddItem("Duplicate", DuplicateSelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.D));
  348. entryContextMenu.AddItem("Paste", PasteToSelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.V));
  349. entryContextMenu.AddSeparator("");
  350. entryContextMenu.AddItem("Delete", DeleteSelection, new ShortcutKey(ButtonModifier.None, ButtonCode.Delete));
  351. Reset();
  352. dropTarget = new ProjectDropTarget(this);
  353. dropTarget.Bounds = contentScrollArea.Bounds;
  354. dropTarget.OnStart += DoOnDragStart;
  355. dropTarget.OnDrag += DoOnDragMove;
  356. dropTarget.OnLeave += DoOnDragLeave;
  357. dropTarget.OnDrop += DoOnDragDropped;
  358. dropTarget.OnEnd += DoOnDragEnd;
  359. }
  360. private ElementEntry FindElementAt(Vector2I windowPos)
  361. {
  362. Vector2I scrollPos = WindowToScrollAreaCoords(windowPos);
  363. foreach (var element in entries)
  364. {
  365. if (element.bounds.Contains(scrollPos))
  366. return element;
  367. }
  368. return null;
  369. }
  370. private ElementEntry[] FindElementsOverlapping(Rect2I scrollBounds)
  371. {
  372. List<ElementEntry> elements = new List<ElementEntry>();
  373. foreach (var element in entries)
  374. {
  375. if(element.Bounds.Overlaps(scrollBounds))
  376. elements.Add(element);
  377. }
  378. return elements.ToArray();
  379. }
  380. private void DoOnDragStart(Vector2I windowPos)
  381. {
  382. ElementEntry underCursorElem = FindElementAt(windowPos);
  383. if (underCursorElem == null || !selectionPaths.Contains(underCursorElem.path))
  384. {
  385. StartDragSelection(windowPos);
  386. return;
  387. }
  388. ResourceDragDropData dragDropData = new ResourceDragDropData(selectionPaths.ToArray());
  389. DragDrop.StartDrag(dragDropData);
  390. }
  391. private void DoOnDragMove(Vector2I windowPos)
  392. {
  393. // Auto-scroll
  394. Rect2I scrollAreaBounds = contentScrollArea.Bounds;
  395. int scrollAreaTop = scrollAreaBounds.y;
  396. int scrollAreaBottom = scrollAreaBounds.y + scrollAreaBounds.height;
  397. if (windowPos.y > scrollAreaTop && windowPos.y <= (scrollAreaTop + DRAG_SCROLL_HEIGHT))
  398. autoScrollAmount = -DRAG_SCROLL_AMOUNT_PER_SECOND;
  399. else if (windowPos.y >= (scrollAreaBottom - DRAG_SCROLL_HEIGHT) && windowPos.y < scrollAreaBottom)
  400. autoScrollAmount = DRAG_SCROLL_AMOUNT_PER_SECOND;
  401. else
  402. autoScrollAmount = 0;
  403. // Selection box
  404. if (UpdateDragSelection(windowPos))
  405. return;
  406. // Drag and drop (hover element under cursor)
  407. ElementEntry underCursorElem = FindElementAt(windowPos);
  408. if (underCursorElem == null)
  409. {
  410. ClearHoverHighlight();
  411. }
  412. else
  413. {
  414. if (underCursorElem.path != hoverHighlightPath)
  415. {
  416. ClearHoverHighlight();
  417. hoverHighlightPath = underCursorElem.path;
  418. underCursorElem.MarkAsHovered(true);
  419. }
  420. }
  421. }
  422. private void DoOnDragLeave()
  423. {
  424. ClearHoverHighlight();
  425. autoScrollAmount = 0;
  426. }
  427. private void DoOnDragDropped(Vector2I windowPos, string[] paths)
  428. {
  429. ClearHoverHighlight();
  430. autoScrollAmount = 0;
  431. if (EndDragSelection())
  432. return;
  433. string resourceDir = ProjectLibrary.ResourceFolder;
  434. string destinationFolder = Path.Combine(resourceDir, currentDirectory);
  435. ElementEntry underCursorElement = FindElementAt(windowPos);
  436. if (underCursorElement != null)
  437. {
  438. LibraryEntry entry = ProjectLibrary.GetEntry(underCursorElement.path);
  439. if (entry != null && entry.Type == LibraryEntryType.Directory)
  440. destinationFolder = Path.Combine(resourceDir, entry.Path);
  441. }
  442. if (paths != null)
  443. {
  444. foreach (var path in paths)
  445. {
  446. if (path == null)
  447. continue;
  448. string absolutePath = path;
  449. if (!Path.IsPathRooted(absolutePath))
  450. absolutePath = Path.Combine(resourceDir, path);
  451. if (string.IsNullOrEmpty(absolutePath))
  452. continue;
  453. if (PathEx.IsPartOf(destinationFolder, absolutePath) || PathEx.Compare(absolutePath, destinationFolder))
  454. continue;
  455. string pathTail = PathEx.GetTail(absolutePath);
  456. string destination = Path.Combine(destinationFolder, pathTail);
  457. bool doCopy = !ProjectLibrary.Exists(path);
  458. if (Directory.Exists(path))
  459. {
  460. if (doCopy)
  461. DirectoryEx.Copy(path, GetUniquePath(destination));
  462. else
  463. DirectoryEx.Move(path, GetUniquePath(destination));
  464. }
  465. else if (File.Exists(path))
  466. {
  467. if (doCopy)
  468. FileEx.Copy(path, GetUniquePath(destination));
  469. else
  470. FileEx.Move(path, GetUniquePath(destination));
  471. }
  472. ProjectLibrary.Refresh();
  473. }
  474. }
  475. }
  476. private void DoOnDragEnd(Vector2I windowPos)
  477. {
  478. EndDragSelection();
  479. }
  480. private void ClearHoverHighlight()
  481. {
  482. if (!string.IsNullOrEmpty(hoverHighlightPath))
  483. {
  484. ElementEntry previousUnderCursorElem;
  485. if (entryLookup.TryGetValue(hoverHighlightPath, out previousUnderCursorElem))
  486. previousUnderCursorElem.MarkAsHovered(false);
  487. }
  488. hoverHighlightPath = "";
  489. }
  490. public void Ping(Resource resource)
  491. {
  492. if (!string.IsNullOrEmpty(pingPath))
  493. {
  494. ElementEntry entry;
  495. if (entryLookup.TryGetValue(pingPath, out entry))
  496. entry.MarkAsPinged(false);
  497. }
  498. if (resource != null)
  499. pingPath = ProjectLibrary.GetPath(resource);
  500. else
  501. pingPath = "";
  502. if (!string.IsNullOrEmpty(pingPath))
  503. {
  504. ElementEntry entry;
  505. if (entryLookup.TryGetValue(pingPath, out entry))
  506. entry.MarkAsPinged(true);
  507. ScrollToEntry(pingPath);
  508. }
  509. }
  510. private void DeselectAll()
  511. {
  512. SetSelection(new List<string>());
  513. selectionAnchorStart = -1;
  514. selectionAnchorEnd = -1;
  515. }
  516. private void Select(string path)
  517. {
  518. ElementEntry entry;
  519. if (!entryLookup.TryGetValue(path, out entry))
  520. return;
  521. bool ctrlDown = Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl);
  522. bool shiftDown = Input.IsButtonHeld(ButtonCode.LeftShift) || Input.IsButtonHeld(ButtonCode.RightShift);
  523. if (shiftDown)
  524. {
  525. if (selectionAnchorStart != -1 && selectionAnchorStart < entries.Count)
  526. {
  527. int start = Math.Min(entry.index, selectionAnchorStart);
  528. int end = Math.Max(entry.index, selectionAnchorStart);
  529. List<string> newSelection = new List<string>();
  530. for(int i = start; i <= end; i++)
  531. newSelection.Add(entries[i].path);
  532. SetSelection(newSelection);
  533. selectionAnchorEnd = entry.index;
  534. }
  535. else
  536. {
  537. SetSelection(new List<string>() {path});
  538. selectionAnchorStart = entry.index;
  539. selectionAnchorEnd = entry.index;
  540. }
  541. }
  542. else if (ctrlDown)
  543. {
  544. List<string> newSelection = new List<string>(selectionPaths);
  545. if (selectionPaths.Contains(path))
  546. {
  547. newSelection.Remove(path);
  548. if (newSelection.Count == 0)
  549. DeselectAll();
  550. else
  551. {
  552. if (selectionAnchorStart == entry.index)
  553. {
  554. ElementEntry newAnchorEntry;
  555. if (!entryLookup.TryGetValue(newSelection[0], out newAnchorEntry))
  556. selectionAnchorStart = -1;
  557. else
  558. selectionAnchorStart = newAnchorEntry.index;
  559. }
  560. if (selectionAnchorEnd == entry.index)
  561. {
  562. ElementEntry newAnchorEntry;
  563. if (!entryLookup.TryGetValue(newSelection[newSelection.Count - 1], out newAnchorEntry))
  564. selectionAnchorEnd = -1;
  565. else
  566. selectionAnchorEnd = newAnchorEntry.index;
  567. }
  568. SetSelection(newSelection);
  569. }
  570. }
  571. else
  572. {
  573. newSelection.Add(path);
  574. SetSelection(newSelection);
  575. selectionAnchorEnd = entry.index;
  576. }
  577. }
  578. else
  579. {
  580. SetSelection(new List<string>() { path });
  581. selectionAnchorStart = entry.index;
  582. selectionAnchorEnd = entry.index;
  583. }
  584. }
  585. private void MoveSelection(MoveDirection dir)
  586. {
  587. string newPath = "";
  588. if (selectionPaths.Count == 0 || selectionAnchorEnd == -1)
  589. {
  590. // Nothing is selected so we arbitrarily select first or last element
  591. if (entries.Count > 0)
  592. {
  593. switch (dir)
  594. {
  595. case MoveDirection.Left:
  596. case MoveDirection.Up:
  597. newPath = entries[entries.Count - 1].path;
  598. break;
  599. case MoveDirection.Right:
  600. case MoveDirection.Down:
  601. newPath = entries[0].path;
  602. break;
  603. }
  604. }
  605. }
  606. else
  607. {
  608. switch (dir)
  609. {
  610. case MoveDirection.Left:
  611. if (selectionAnchorEnd - 1 >= 0)
  612. newPath = entries[selectionAnchorEnd - 1].path;
  613. break;
  614. case MoveDirection.Up:
  615. if (selectionAnchorEnd - contentInfo.elementsPerRow >= 0)
  616. newPath = entries[selectionAnchorEnd - contentInfo.elementsPerRow].path;
  617. break;
  618. case MoveDirection.Right:
  619. if (selectionAnchorEnd + 1 < entries.Count)
  620. newPath = entries[selectionAnchorEnd + 1].path;
  621. break;
  622. case MoveDirection.Down:
  623. if (selectionAnchorEnd + contentInfo.elementsPerRow < entries.Count)
  624. newPath = entries[selectionAnchorEnd + contentInfo.elementsPerRow].path;
  625. break;
  626. }
  627. }
  628. if (!string.IsNullOrEmpty(newPath))
  629. {
  630. Select(newPath);
  631. ScrollToEntry(newPath);
  632. }
  633. }
  634. private void SetSelection(List<string> paths)
  635. {
  636. if (selectionPaths != null)
  637. {
  638. foreach (var path in selectionPaths)
  639. {
  640. ElementEntry entry;
  641. if (entryLookup.TryGetValue(path, out entry))
  642. entry.MarkAsSelected(false);
  643. }
  644. }
  645. selectionPaths = paths;
  646. if (selectionPaths != null)
  647. {
  648. foreach (var path in selectionPaths)
  649. {
  650. ElementEntry entry;
  651. if (entryLookup.TryGetValue(path, out entry))
  652. entry.MarkAsSelected(true);
  653. }
  654. }
  655. Ping(null);
  656. StopRename();
  657. if (selectionPaths != null)
  658. Selection.resourcePaths = selectionPaths.ToArray();
  659. else
  660. Selection.resourcePaths = new string[0];
  661. }
  662. private void EnterDirectory(string directory)
  663. {
  664. currentDirectory = directory;
  665. DeselectAll();
  666. Refresh();
  667. }
  668. private void Cut(IEnumerable<string> sourcePaths)
  669. {
  670. foreach (var path in cutPaths)
  671. {
  672. ElementEntry entry;
  673. if (entryLookup.TryGetValue(path, out entry))
  674. entry.MarkAsCut(false);
  675. }
  676. cutPaths.Clear();
  677. cutPaths.AddRange(sourcePaths);
  678. foreach (var path in cutPaths)
  679. {
  680. ElementEntry entry;
  681. if (entryLookup.TryGetValue(path, out entry))
  682. entry.MarkAsCut(true);
  683. }
  684. copyPaths.Clear();
  685. }
  686. private void Copy(IEnumerable<string> sourcePaths)
  687. {
  688. copyPaths.Clear();
  689. copyPaths.AddRange(sourcePaths);
  690. foreach (var path in cutPaths)
  691. {
  692. ElementEntry entry;
  693. if (entryLookup.TryGetValue(path, out entry))
  694. entry.MarkAsCut(false);
  695. }
  696. cutPaths.Clear();
  697. }
  698. private void Duplicate(IEnumerable<string> sourcePaths)
  699. {
  700. foreach (var source in sourcePaths)
  701. {
  702. if (Directory.Exists(source))
  703. DirectoryEx.Copy(source, GetUniquePath(source));
  704. else if (File.Exists(source))
  705. FileEx.Copy(source, GetUniquePath(source));
  706. ProjectLibrary.Refresh();
  707. }
  708. }
  709. private void Paste(string destinationFolder)
  710. {
  711. if (copyPaths.Count > 0)
  712. {
  713. for (int i = 0; i < copyPaths.Count; i++)
  714. {
  715. string destination = Path.Combine(destinationFolder, PathEx.GetTail(copyPaths[i]));
  716. if (Directory.Exists(copyPaths[i]))
  717. DirectoryEx.Copy(copyPaths[i], GetUniquePath(destination));
  718. else if (File.Exists(copyPaths[i]))
  719. FileEx.Copy(copyPaths[i], GetUniquePath(destination));
  720. }
  721. ProjectLibrary.Refresh();
  722. }
  723. else if (cutPaths.Count > 0)
  724. {
  725. for (int i = 0; i < cutPaths.Count; i++)
  726. {
  727. string destination = Path.Combine(destinationFolder, PathEx.GetTail(cutPaths[i]));
  728. if (Directory.Exists(cutPaths[i]))
  729. DirectoryEx.Move(cutPaths[i], GetUniquePath(destination));
  730. else if (File.Exists(cutPaths[i]))
  731. FileEx.Move(cutPaths[i], GetUniquePath(destination));
  732. }
  733. cutPaths.Clear();
  734. ProjectLibrary.Refresh();
  735. }
  736. }
  737. private string GetUniquePath(string path)
  738. {
  739. string extension = Path.GetExtension(path);
  740. string pathNoExtension = path;
  741. if (!string.IsNullOrEmpty(extension))
  742. pathNoExtension = path.Remove(path.Length - extension.Length);
  743. int idx = 0;
  744. string destination = pathNoExtension;
  745. while (ProjectLibrary.Exists(destination))
  746. {
  747. destination = pathNoExtension + "_" + idx;
  748. idx++;
  749. }
  750. return destination + extension;
  751. }
  752. private void OnEditorUpdate()
  753. {
  754. bool isRenameInProgress = inProgressRenameElement != null;
  755. if (HasContentFocus)
  756. {
  757. if (!isRenameInProgress)
  758. {
  759. if (Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl))
  760. {
  761. if (Input.IsButtonUp(ButtonCode.C))
  762. {
  763. CopySelection();
  764. }
  765. else if (Input.IsButtonUp(ButtonCode.X))
  766. {
  767. CutSelection();
  768. }
  769. else if (Input.IsButtonUp(ButtonCode.D))
  770. {
  771. DuplicateSelection();
  772. }
  773. else if (Input.IsButtonUp(ButtonCode.V))
  774. {
  775. PasteToSelection();
  776. }
  777. }
  778. if (Input.IsButtonDown(ButtonCode.Return))
  779. {
  780. if (selectionPaths.Count == 1)
  781. {
  782. LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
  783. if (entry != null && entry.Type == LibraryEntryType.Directory)
  784. {
  785. EnterDirectory(entry.Path);
  786. }
  787. }
  788. }
  789. else if (Input.IsButtonDown(ButtonCode.Back))
  790. {
  791. LibraryEntry entry = ProjectLibrary.GetEntry(currentDirectory);
  792. if (entry != null && entry.Parent != null)
  793. {
  794. EnterDirectory(entry.Parent.Path);
  795. }
  796. }
  797. else if (Input.IsButtonDown(ButtonCode.Up))
  798. {
  799. MoveSelection(MoveDirection.Up);
  800. }
  801. else if (Input.IsButtonDown(ButtonCode.Down))
  802. {
  803. MoveSelection(MoveDirection.Down);
  804. }
  805. else if (Input.IsButtonDown(ButtonCode.Left))
  806. {
  807. MoveSelection(MoveDirection.Left);
  808. }
  809. else if (Input.IsButtonDown(ButtonCode.Right))
  810. {
  811. MoveSelection(MoveDirection.Right);
  812. }
  813. else if (Input.IsButtonDown(ButtonCode.F2))
  814. {
  815. RenameSelection();
  816. }
  817. else if (Input.IsButtonDown(ButtonCode.Delete))
  818. {
  819. DeleteSelection();
  820. }
  821. }
  822. else
  823. {
  824. if (Input.IsButtonDown(ButtonCode.Return))
  825. {
  826. string newName = inProgressRenameElement.GetRenamedName();
  827. string originalPath = inProgressRenameElement.path;
  828. string newPath = Path.GetDirectoryName(originalPath);
  829. newPath = Path.Combine(newPath, newName + Path.GetExtension(originalPath));
  830. bool renameOK = true;
  831. if (!PathEx.IsValidFileName(newName))
  832. {
  833. DialogBox.Open("Error", "The name you specified is not a valid file name. Try another.", DialogBox.Type.OK);
  834. renameOK = false;
  835. }
  836. if (renameOK)
  837. {
  838. if (ProjectLibrary.Exists(newPath))
  839. {
  840. DialogBox.Open("Error", "File/folder with that name already exists in this folder.", DialogBox.Type.OK);
  841. renameOK = false;
  842. }
  843. }
  844. if (renameOK)
  845. {
  846. ProjectLibrary.Rename(originalPath, newPath);
  847. StopRename();
  848. }
  849. }
  850. else if (Input.IsButtonDown(ButtonCode.Escape))
  851. {
  852. StopRename();
  853. }
  854. }
  855. }
  856. else
  857. {
  858. if(isRenameInProgress)
  859. StopRename();
  860. }
  861. if (autoScrollAmount != 0)
  862. {
  863. Rect2I contentBounds = contentScrollArea.ContentBounds;
  864. float scrollPct = autoScrollAmount / (float)contentBounds.height;
  865. contentScrollArea.VerticalScroll += scrollPct * Time.FrameDelta;
  866. }
  867. if (requiresRefresh)
  868. Refresh();
  869. dropTarget.Update();
  870. }
  871. private void OnEntryChanged(string entry)
  872. {
  873. requiresRefresh = true;
  874. }
  875. private void ScrollToEntry(string path)
  876. {
  877. ElementEntry entryGUI;
  878. if (!entryLookup.TryGetValue(path, out entryGUI))
  879. return;
  880. Rect2I entryBounds = entryGUI.Bounds;
  881. Rect2I contentBounds = contentScrollArea.Layout.Bounds;
  882. Rect2I windowEntryBounds = entryBounds;
  883. windowEntryBounds.x += contentBounds.x;
  884. windowEntryBounds.y += contentBounds.y;
  885. Rect2I scrollAreaBounds = contentScrollArea.Bounds;
  886. bool requiresScroll = windowEntryBounds.y < scrollAreaBounds.y ||
  887. (windowEntryBounds.y + windowEntryBounds.height) > (scrollAreaBounds.y + scrollAreaBounds.height);
  888. if (!requiresScroll)
  889. return;
  890. int scrollableSize = contentBounds.height - scrollAreaBounds.height;
  891. float percent = (((entryBounds.y + entryBounds.height * 0.5f) - scrollAreaBounds.height * 0.5f) / (float)scrollableSize);
  892. percent = MathEx.Clamp01(percent);
  893. contentScrollArea.VerticalScroll = percent;
  894. }
  895. private void Refresh()
  896. {
  897. requiresRefresh = false;
  898. LibraryEntry[] entriesToDisplay = new LibraryEntry[0];
  899. if (IsSearchActive)
  900. {
  901. entriesToDisplay = ProjectLibrary.Search("*" + searchQuery + "*");
  902. }
  903. else
  904. {
  905. DirectoryEntry entry = ProjectLibrary.GetEntry(currentDirectory) as DirectoryEntry;
  906. if (entry == null)
  907. {
  908. currentDirectory = ProjectLibrary.Root.Path;
  909. entry = ProjectLibrary.GetEntry(currentDirectory) as DirectoryEntry;
  910. }
  911. if(entry != null)
  912. entriesToDisplay = entry.Children;
  913. }
  914. if (scrollAreaPanel != null)
  915. scrollAreaPanel.Destroy();
  916. entries.Clear();
  917. entryLookup.Clear();
  918. inProgressRenameElement = null;
  919. scrollAreaPanel = contentScrollArea.Layout.AddPanel();
  920. RefreshDirectoryBar();
  921. SortEntries(entriesToDisplay);
  922. if (entriesToDisplay.Length == 0)
  923. return;
  924. contentInfo = new ContentInfo(this, viewType, entriesToDisplay.Length);
  925. if (viewType == ProjectViewType.List16)
  926. {
  927. for (int i = 0; i < entriesToDisplay.Length; i++)
  928. {
  929. ElementEntry guiEntry = new ElementEntry(contentInfo, contentInfo.main, entriesToDisplay[i], i);
  930. entries.Add(guiEntry);
  931. entryLookup[guiEntry.path] = guiEntry;
  932. if (i != entriesToDisplay.Length - 1)
  933. contentInfo.main.AddSpace(LIST_ENTRY_SPACING);
  934. }
  935. contentInfo.main.AddFlexibleSpace();
  936. }
  937. else
  938. {
  939. int tileSize = 64;
  940. switch (viewType)
  941. {
  942. case ProjectViewType.Grid64: tileSize = 64; break;
  943. case ProjectViewType.Grid48: tileSize = 48; break;
  944. case ProjectViewType.Grid32: tileSize = 32; break;
  945. }
  946. contentInfo.main.AddSpace(GRID_ENTRY_SPACING / 2);
  947. GUILayoutX rowLayout = contentInfo.main.AddLayoutX();
  948. contentInfo.main.AddSpace(GRID_ENTRY_SPACING);
  949. rowLayout.AddFlexibleSpace();
  950. int elemsInRow = 0;
  951. for (int i = 0; i < entriesToDisplay.Length; i++)
  952. {
  953. if (elemsInRow == contentInfo.elementsPerRow && elemsInRow > 0)
  954. {
  955. rowLayout = contentInfo.main.AddLayoutX();
  956. contentInfo.main.AddSpace(GRID_ENTRY_SPACING);
  957. rowLayout.AddFlexibleSpace();
  958. elemsInRow = 0;
  959. }
  960. ElementEntry guiEntry = new ElementEntry(contentInfo, rowLayout, entriesToDisplay[i], i);
  961. entries.Add(guiEntry);
  962. entryLookup[guiEntry.path] = guiEntry;
  963. rowLayout.AddFlexibleSpace();
  964. elemsInRow++;
  965. }
  966. int extraElements = contentInfo.elementsPerRow - elemsInRow;
  967. for (int i = 0; i < extraElements; i++)
  968. {
  969. rowLayout.AddSpace(contentInfo.labelWidth);
  970. rowLayout.AddFlexibleSpace();
  971. }
  972. contentInfo.main.AddFlexibleSpace();
  973. }
  974. for (int i = 0; i < entries.Count; i++)
  975. {
  976. ElementEntry guiEntry = entries[i];
  977. guiEntry.Initialize();
  978. if (cutPaths.Contains(guiEntry.path))
  979. guiEntry.MarkAsCut(true);
  980. if (selectionPaths.Contains(guiEntry.path))
  981. guiEntry.MarkAsSelected(true);
  982. else if (pingPath == guiEntry.path)
  983. guiEntry.MarkAsPinged(true);
  984. }
  985. Rect2I contentBounds = contentInfo.main.Bounds;
  986. Rect2I minimalBounds = GetScrollAreaBounds();
  987. contentBounds.height = Math.Max(contentBounds.height, minimalBounds.height);
  988. GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
  989. catchAll.Bounds = contentBounds;
  990. catchAll.OnClick += OnCatchAllClicked;
  991. catchAll.SetContextMenu(entryContextMenu);
  992. contentInfo.underlay.AddElement(catchAll);
  993. Rect2I focusBounds = contentBounds; // Contents + Folder bar
  994. Rect2I scrollBounds = contentScrollArea.Bounds;
  995. focusBounds.x += scrollBounds.x;
  996. focusBounds.y += scrollBounds.y;
  997. Rect2I folderBarBounds = folderListLayout.Bounds;
  998. focusBounds.y -= folderBarBounds.height;
  999. focusBounds.height += folderBarBounds.height;
  1000. GUIButton focusCatcher = new GUIButton("", EditorStyles.Blank);
  1001. focusCatcher.OnFocusChanged += OnContentsFocusChanged;
  1002. focusCatcher.Bounds = focusBounds;
  1003. GUIPanel focusPanel = GUI.AddPanel(3);
  1004. focusPanel.AddElement(focusCatcher);
  1005. UpdateDragSelection(dragSelectionEnd);
  1006. }
  1007. private Vector2I WindowToScrollAreaCoords(Vector2I windowPos)
  1008. {
  1009. Rect2I scrollBounds = contentScrollArea.Layout.Bounds;
  1010. Vector2I scrollPos = windowPos;
  1011. scrollPos.x -= scrollBounds.x;
  1012. scrollPos.y -= scrollBounds.y;
  1013. return scrollPos;
  1014. }
  1015. private void StartDragSelection(Vector2I windowPos)
  1016. {
  1017. isDraggingSelection = true;
  1018. dragSelectionStart = WindowToScrollAreaCoords(windowPos);
  1019. dragSelectionEnd = dragSelectionStart;
  1020. }
  1021. private bool UpdateDragSelection(Vector2I windowPos)
  1022. {
  1023. if (!isDraggingSelection)
  1024. return false;
  1025. if (dragSelection == null)
  1026. {
  1027. dragSelection = new GUITexture(null, true, EditorStyles.SelectionArea);
  1028. contentInfo.overlay.AddElement(dragSelection);
  1029. }
  1030. dragSelectionEnd = WindowToScrollAreaCoords(windowPos);
  1031. Rect2I selectionArea = CalculateSelectionArea();
  1032. SelectInArea(selectionArea);
  1033. dragSelection.Bounds = selectionArea;
  1034. return true;
  1035. }
  1036. private bool EndDragSelection()
  1037. {
  1038. if (!isDraggingSelection)
  1039. return false;
  1040. if (dragSelection != null)
  1041. {
  1042. dragSelection.Destroy();
  1043. dragSelection = null;
  1044. }
  1045. Rect2I selectionArea = CalculateSelectionArea();
  1046. SelectInArea(selectionArea);
  1047. isDraggingSelection = false;
  1048. return false;
  1049. }
  1050. private Rect2I CalculateSelectionArea()
  1051. {
  1052. Rect2I selectionArea = new Rect2I();
  1053. if (dragSelectionStart.x < dragSelectionEnd.x)
  1054. {
  1055. selectionArea.x = dragSelectionStart.x;
  1056. selectionArea.width = dragSelectionEnd.x - dragSelectionStart.x;
  1057. }
  1058. else
  1059. {
  1060. selectionArea.x = dragSelectionEnd.x;
  1061. selectionArea.width = dragSelectionStart.x - dragSelectionEnd.x;
  1062. }
  1063. if (dragSelectionStart.y < dragSelectionEnd.y)
  1064. {
  1065. selectionArea.y = dragSelectionStart.y;
  1066. selectionArea.height = dragSelectionEnd.y - dragSelectionStart.y;
  1067. }
  1068. else
  1069. {
  1070. selectionArea.y = dragSelectionEnd.y;
  1071. selectionArea.height = dragSelectionStart.y - dragSelectionEnd.y;
  1072. }
  1073. Rect2I maxBounds = contentScrollArea.Layout.Bounds;
  1074. maxBounds.x = 0;
  1075. maxBounds.y = 0;
  1076. selectionArea.Clip(maxBounds);
  1077. return selectionArea;
  1078. }
  1079. private void SelectInArea(Rect2I scrollBounds)
  1080. {
  1081. ElementEntry[] foundElements = FindElementsOverlapping(scrollBounds);
  1082. if (foundElements.Length > 0)
  1083. {
  1084. selectionAnchorStart = foundElements[0].index;
  1085. selectionAnchorEnd = foundElements[foundElements.Length - 1].index;
  1086. }
  1087. else
  1088. {
  1089. selectionAnchorStart = -1;
  1090. selectionAnchorEnd = -1;
  1091. }
  1092. List<string> elementPaths = new List<string>();
  1093. foreach (var elem in foundElements)
  1094. elementPaths.Add(elem.path);
  1095. SetSelection(elementPaths);
  1096. }
  1097. private void RefreshDirectoryBar()
  1098. {
  1099. if (folderListLayout != null)
  1100. {
  1101. folderListLayout.Destroy();
  1102. folderListLayout = null;
  1103. }
  1104. folderListLayout = folderBarLayout.AddLayoutX();
  1105. string[] folders = null;
  1106. string[] fullPaths = null;
  1107. if (IsSearchActive)
  1108. {
  1109. folders = new[] {searchQuery};
  1110. fullPaths = new[] { searchQuery };
  1111. }
  1112. else
  1113. {
  1114. string currentDir = Path.Combine("Resources", currentDirectory);
  1115. folders = currentDir.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar },
  1116. StringSplitOptions.RemoveEmptyEntries);
  1117. fullPaths = new string[folders.Length];
  1118. for (int i = 0; i < folders.Length; i++)
  1119. {
  1120. if (i == 0)
  1121. fullPaths[i] = "";
  1122. else
  1123. fullPaths[i] = Path.Combine(fullPaths[i - 1], folders[i]);
  1124. }
  1125. }
  1126. int availableWidth = folderBarLayout.Bounds.width - FOLDER_BUTTON_WIDTH * 2;
  1127. int numFolders = 0;
  1128. for (int i = folders.Length - 1; i >= 0; i--)
  1129. {
  1130. GUIButton folderButton = new GUIButton(folders[i]);
  1131. if (!IsSearchActive)
  1132. {
  1133. string fullPath = fullPaths[i];
  1134. folderButton.OnClick += () => OnFolderButtonClicked(fullPath);
  1135. }
  1136. GUILabel separator = new GUILabel("/", GUIOption.FixedWidth(FOLDER_SEPARATOR_WIDTH));
  1137. folderListLayout.InsertElement(0, separator);
  1138. folderListLayout.InsertElement(0, folderButton);
  1139. numFolders++;
  1140. Rect2I folderListBounds = folderListLayout.Bounds;
  1141. if (folderListBounds.width > availableWidth)
  1142. {
  1143. if (numFolders > 2)
  1144. {
  1145. separator.Destroy();
  1146. folderButton.Destroy();
  1147. break;
  1148. }
  1149. }
  1150. }
  1151. }
  1152. private void SortEntries(LibraryEntry[] input)
  1153. {
  1154. Array.Sort(input, (x, y) =>
  1155. {
  1156. if (x.Type == y.Type)
  1157. return x.Name.CompareTo(y.Name);
  1158. else
  1159. return x.Type == LibraryEntryType.File ? 1 : -1;
  1160. });
  1161. }
  1162. private void OnFolderButtonClicked(string path)
  1163. {
  1164. EnterDirectory(path);
  1165. }
  1166. private void OnContentsFocusChanged(bool focus)
  1167. {
  1168. hasContentFocus = focus;
  1169. }
  1170. private void OnEntryClicked(string path)
  1171. {
  1172. Select(path);
  1173. }
  1174. private void OnEntryDoubleClicked(string path)
  1175. {
  1176. LibraryEntry entry = ProjectLibrary.GetEntry(path);
  1177. if (entry != null && entry.Type == LibraryEntryType.Directory)
  1178. {
  1179. EnterDirectory(path);
  1180. }
  1181. }
  1182. private void OnCatchAllClicked()
  1183. {
  1184. DeselectAll();
  1185. }
  1186. private void OnHomeClicked()
  1187. {
  1188. currentDirectory = ProjectLibrary.Root.Path;
  1189. Refresh();
  1190. }
  1191. private void OnUpClicked()
  1192. {
  1193. currentDirectory = currentDirectory.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
  1194. if (!string.IsNullOrEmpty(currentDirectory))
  1195. {
  1196. string parent = Path.GetDirectoryName(currentDirectory);
  1197. currentDirectory = parent;
  1198. Refresh();
  1199. }
  1200. }
  1201. private void CutSelection()
  1202. {
  1203. if (selectionPaths.Count > 0)
  1204. Cut(selectionPaths);
  1205. }
  1206. private void CopySelection()
  1207. {
  1208. if (selectionPaths.Count > 0)
  1209. Copy(selectionPaths);
  1210. }
  1211. private void DuplicateSelection()
  1212. {
  1213. if (selectionPaths.Count > 0)
  1214. Duplicate(selectionPaths);
  1215. }
  1216. private void PasteToSelection()
  1217. {
  1218. DirectoryEntry selectedDirectory = null;
  1219. if (selectionPaths.Count == 1)
  1220. {
  1221. LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
  1222. if (entry != null && entry.Type == LibraryEntryType.Directory)
  1223. selectedDirectory = (DirectoryEntry) entry;
  1224. }
  1225. if(selectedDirectory != null)
  1226. Paste(selectedDirectory.Path);
  1227. else
  1228. Paste(currentDirectory);
  1229. }
  1230. private void RenameSelection()
  1231. {
  1232. if (selectionPaths.Count == 0)
  1233. return;
  1234. if (selectionPaths.Count > 1)
  1235. {
  1236. DeselectAll();
  1237. Select(selectionPaths[0]);
  1238. }
  1239. ElementEntry entry;
  1240. if (entryLookup.TryGetValue(pingPath, out entry))
  1241. {
  1242. entry.StartRename();
  1243. inProgressRenameElement = entry;
  1244. }
  1245. }
  1246. private void StopRename()
  1247. {
  1248. if (inProgressRenameElement != null)
  1249. {
  1250. inProgressRenameElement.StopRename();
  1251. inProgressRenameElement = null;
  1252. }
  1253. }
  1254. private void DeleteSelection()
  1255. {
  1256. if (selectionPaths.Count == 0)
  1257. return;
  1258. DialogBox.Open("Confirm deletion", "Are you sure you want to delete the selected object(s)?",
  1259. DialogBox.Type.YesNo,
  1260. type =>
  1261. {
  1262. if (type == DialogBox.ResultType.Yes)
  1263. {
  1264. foreach (var path in selectionPaths)
  1265. {
  1266. ProjectLibrary.Delete(path);
  1267. }
  1268. DeselectAll();
  1269. Refresh();
  1270. }
  1271. });
  1272. }
  1273. private void OnSearchChanged(string newValue)
  1274. {
  1275. searchQuery = newValue;
  1276. Refresh();
  1277. }
  1278. private void ClearSearch()
  1279. {
  1280. searchField.Value = "";
  1281. searchQuery = "";
  1282. Refresh();
  1283. }
  1284. private void OpenOptionsWindow()
  1285. {
  1286. Vector2I openPosition;
  1287. Rect2I buttonBounds = GUILayoutUtility.CalculateBounds(optionsButton, GUI);
  1288. openPosition.x = buttonBounds.x + buttonBounds.width / 2;
  1289. openPosition.y = buttonBounds.y + buttonBounds.height / 2;
  1290. ProjectDropDown dropDown = DropDownWindow.Open<ProjectDropDown>(this, openPosition);
  1291. dropDown.SetParent(this);
  1292. }
  1293. private void Reset()
  1294. {
  1295. currentDirectory = ProjectLibrary.Root.Path;
  1296. selectionAnchorStart = -1;
  1297. selectionAnchorEnd = -1;
  1298. selectionPaths.Clear();
  1299. pingPath = "";
  1300. hoverHighlightPath = "";
  1301. Refresh();
  1302. }
  1303. private Rect2I GetScrollAreaBounds()
  1304. {
  1305. Rect2I bounds = GUI.Bounds;
  1306. Rect2I folderListBounds = folderListLayout.Bounds;
  1307. Rect2I searchBarBounds = searchBarLayout.Bounds;
  1308. bounds.y += folderListBounds.height + searchBarBounds.height;
  1309. bounds.height -= folderListBounds.height + searchBarBounds.height;
  1310. return bounds;
  1311. }
  1312. protected override void WindowResized(int width, int height)
  1313. {
  1314. base.WindowResized(width, height);
  1315. Refresh();
  1316. dropTarget.Bounds = contentScrollArea.Bounds;
  1317. }
  1318. }
  1319. internal class ProjectDropDown : DropDownWindow
  1320. {
  1321. private ProjectWindow parent;
  1322. public ProjectDropDown()
  1323. :base(150, 30)
  1324. { }
  1325. internal void SetParent(ProjectWindow parent)
  1326. {
  1327. this.parent = parent;
  1328. GUIToggleGroup group = new GUIToggleGroup();
  1329. GUIToggle list16 = new GUIToggle("16", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  1330. GUIToggle grid32 = new GUIToggle("32", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  1331. GUIToggle grid48 = new GUIToggle("48", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  1332. GUIToggle grid64 = new GUIToggle("64", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  1333. ProjectViewType activeType = parent.ViewType;
  1334. switch (activeType)
  1335. {
  1336. case ProjectViewType.List16:
  1337. list16.ToggleOn();
  1338. break;
  1339. case ProjectViewType.Grid32:
  1340. grid32.ToggleOn();
  1341. break;
  1342. case ProjectViewType.Grid48:
  1343. grid48.ToggleOn();
  1344. break;
  1345. case ProjectViewType.Grid64:
  1346. grid64.ToggleOn();
  1347. break;
  1348. }
  1349. list16.OnToggled += (active) =>
  1350. {
  1351. if (active)
  1352. ChangeViewType(ProjectViewType.List16);
  1353. };
  1354. grid32.OnToggled += (active) =>
  1355. {
  1356. if (active)
  1357. ChangeViewType(ProjectViewType.Grid32);
  1358. };
  1359. grid48.OnToggled += (active) =>
  1360. {
  1361. if (active)
  1362. ChangeViewType(ProjectViewType.Grid48);
  1363. };
  1364. grid64.OnToggled += (active) =>
  1365. {
  1366. if (active)
  1367. ChangeViewType(ProjectViewType.Grid64);
  1368. };
  1369. GUILayoutY vertLayout = GUI.AddLayoutY();
  1370. vertLayout.AddFlexibleSpace();
  1371. GUILayoutX contentLayout = vertLayout.AddLayoutX();
  1372. contentLayout.AddFlexibleSpace();
  1373. contentLayout.AddElement(list16);
  1374. contentLayout.AddElement(grid32);
  1375. contentLayout.AddElement(grid48);
  1376. contentLayout.AddElement(grid64);
  1377. contentLayout.AddFlexibleSpace();
  1378. vertLayout.AddFlexibleSpace();
  1379. }
  1380. private void ChangeViewType(ProjectViewType viewType)
  1381. {
  1382. parent.ViewType = viewType;
  1383. }
  1384. }
  1385. }