ProjectWindow.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  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)
  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. }
  27. else
  28. {
  29. switch (viewType)
  30. {
  31. case ProjectViewType.Grid64:
  32. tileSize = 64;
  33. break;
  34. case ProjectViewType.Grid48:
  35. tileSize = 48;
  36. break;
  37. case ProjectViewType.Grid32:
  38. tileSize = 32;
  39. break;
  40. }
  41. gridLayout = true;
  42. }
  43. this.window = window;
  44. }
  45. public GUILayout main;
  46. public GUIPanel overlay;
  47. public GUIPanel underlay;
  48. public ProjectWindow window;
  49. public int tileSize;
  50. public bool gridLayout;
  51. }
  52. private class ElementEntry
  53. {
  54. // Note: Order of these is relevant
  55. enum UnderlayState
  56. {
  57. None, Hovered, Selected, Pinged
  58. }
  59. public string path;
  60. public GUITexture icon;
  61. public GUILabel label;
  62. public Rect2I bounds;
  63. private GUITexture underlay;
  64. private ContentInfo info;
  65. private UnderlayState underlayState;
  66. public ElementEntry(ContentInfo info, GUILayout parent, LibraryEntry entry)
  67. {
  68. GUILayout entryLayout;
  69. if (info.gridLayout)
  70. entryLayout = parent.AddLayoutY();
  71. else
  72. entryLayout = parent.AddLayoutX();
  73. SpriteTexture iconTexture = GetIcon(entry);
  74. icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit,
  75. true, GUIOption.FixedHeight(info.tileSize), GUIOption.FixedWidth(info.tileSize));
  76. label = null;
  77. if (info.gridLayout)
  78. {
  79. label = new GUILabel(entry.Name, EditorStyles.MultiLineLabel,
  80. GUIOption.FixedWidth(info.tileSize), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
  81. }
  82. else
  83. {
  84. label = new GUILabel(entry.Name);
  85. }
  86. entryLayout.AddElement(icon);
  87. entryLayout.AddElement(label);
  88. this.info = info;
  89. this.path = entry.Path;
  90. this.bounds = new Rect2I();
  91. this.underlay = null;
  92. }
  93. public void Initialize()
  94. {
  95. bounds = icon.Bounds;
  96. Rect2I labelBounds = label.Bounds;
  97. bounds.x = MathEx.Min(bounds.x, labelBounds.x);
  98. bounds.y = MathEx.Min(bounds.y, labelBounds.y);
  99. bounds.width = MathEx.Max(bounds.x + bounds.width,
  100. labelBounds.x + labelBounds.width) - bounds.x;
  101. bounds.height = MathEx.Max(bounds.y + bounds.height,
  102. labelBounds.y + labelBounds.height) - bounds.y;
  103. ProjectWindow hoistedWindow = info.window;
  104. string hoistedPath = path;
  105. GUIButton overlayBtn = new GUIButton("", EditorStyles.Blank);
  106. overlayBtn.Bounds = bounds;
  107. overlayBtn.OnClick += () => hoistedWindow.OnEntryClicked(hoistedPath);
  108. overlayBtn.OnDoubleClick += () => hoistedWindow.OnEntryDoubleClicked(hoistedPath);
  109. overlayBtn.SetContextMenu(info.window.entryContextMenu);
  110. info.overlay.AddElement(overlayBtn);
  111. }
  112. public Rect2I Bounds
  113. {
  114. get { return bounds; }
  115. }
  116. public void MarkAsCut(bool enable)
  117. {
  118. if (enable)
  119. icon.SetTint(CUT_COLOR);
  120. else
  121. icon.SetTint(Color.White);
  122. }
  123. public void MarkAsSelected(bool enable)
  124. {
  125. if ((int)underlayState > (int) UnderlayState.Selected)
  126. return;
  127. if (enable)
  128. {
  129. CreateUnderlay();
  130. underlay.SetTint(SELECTION_COLOR);
  131. }
  132. else
  133. ClearUnderlay();
  134. underlayState = UnderlayState.Selected;
  135. }
  136. public void MarkAsPinged(bool enable)
  137. {
  138. if ((int)underlayState > (int)UnderlayState.Pinged)
  139. return;
  140. if (enable)
  141. {
  142. CreateUnderlay();
  143. underlay.SetTint(PING_COLOR);
  144. }
  145. else
  146. ClearUnderlay();
  147. underlayState = UnderlayState.Pinged;
  148. }
  149. public void MarkAsHovered(bool enable)
  150. {
  151. if ((int)underlayState > (int)UnderlayState.Hovered)
  152. return;
  153. if (enable)
  154. {
  155. CreateUnderlay();
  156. underlay.SetTint(HOVER_COLOR);
  157. }
  158. else
  159. ClearUnderlay();
  160. underlayState = UnderlayState.Hovered;
  161. }
  162. private void ClearUnderlay()
  163. {
  164. if (underlay != null)
  165. {
  166. underlay.Destroy();
  167. underlay = null;
  168. }
  169. underlayState = UnderlayState.None;
  170. }
  171. private void CreateUnderlay()
  172. {
  173. if (underlay == null)
  174. {
  175. underlay = new GUITexture(Builtin.WhiteTexture);
  176. underlay.Bounds = Bounds;
  177. info.underlay.AddElement(underlay);
  178. }
  179. }
  180. private static SpriteTexture GetIcon(LibraryEntry entry)
  181. {
  182. if (entry.Type == LibraryEntryType.Directory)
  183. {
  184. return EditorBuiltin.FolderIcon;
  185. }
  186. else
  187. {
  188. FileEntry fileEntry = (FileEntry)entry;
  189. switch (fileEntry.ResType)
  190. {
  191. case ResourceType.Font:
  192. return EditorBuiltin.FontIcon;
  193. case ResourceType.Mesh:
  194. return EditorBuiltin.MeshIcon;
  195. case ResourceType.Texture:
  196. return EditorBuiltin.TextureIcon;
  197. case ResourceType.PlainText:
  198. return EditorBuiltin.PlainTextIcon;
  199. case ResourceType.ScriptCode:
  200. return EditorBuiltin.ScriptCodeIcon;
  201. case ResourceType.SpriteTexture:
  202. return EditorBuiltin.SpriteTextureIcon;
  203. case ResourceType.Shader:
  204. return EditorBuiltin.ShaderIcon;
  205. case ResourceType.Material:
  206. return EditorBuiltin.MaterialIcon;
  207. }
  208. }
  209. return null;
  210. }
  211. }
  212. private const int GRID_ENTRY_SPACING = 15;
  213. private const int LIST_ENTRY_SPACING = 7;
  214. private const int MAX_LABEL_HEIGHT = 50;
  215. private const int DRAG_SCROLL_HEIGHT = 20;
  216. private const int DRAG_SCROLL_AMOUNT_PER_SECOND = 100;
  217. private static readonly Color PING_COLOR = Color.BansheeOrange;
  218. private static readonly Color SELECTION_COLOR = Color.DarkCyan;
  219. private static readonly Color HOVER_COLOR = new Color(Color.DarkCyan.r, Color.DarkCyan.g, Color.DarkCyan.b, 0.5f);
  220. private static readonly Color CUT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
  221. private bool hasContentFocus = false;
  222. private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
  223. private ProjectViewType viewType = ProjectViewType.Grid32;
  224. private string currentDirectory = "";
  225. private List<string> selectionPaths = new List<string>();
  226. private string pingPath = "";
  227. private string hoverHighlightPath = "";
  228. private GUIScrollArea contentScrollArea;
  229. private GUIPanel scrollAreaPanel;
  230. private GUILayoutX searchBarLayout;
  231. private GUIButton optionsButton;
  232. private ContextMenu entryContextMenu;
  233. private ProjectDropTarget dropTarget;
  234. private List<ElementEntry> entries = new List<ElementEntry>();
  235. private Dictionary<string, ElementEntry> entryLookup = new Dictionary<string, ElementEntry>();
  236. private int autoScrollAmount;
  237. // Cut/Copy/Paste
  238. private List<string> copyPaths = new List<string>();
  239. private List<string> cutPaths = new List<string>();
  240. internal ProjectViewType ViewType
  241. {
  242. get { return viewType; }
  243. set { viewType = value; Refresh(); }
  244. }
  245. [MenuItem("Windows/Project", ButtonModifier.Ctrl, ButtonCode.P)]
  246. private static void OpenProjectWindow()
  247. {
  248. OpenWindow<ProjectWindow>();
  249. }
  250. private void OnInitialize()
  251. {
  252. ProjectLibrary.OnEntryAdded += OnEntryChanged;
  253. ProjectLibrary.OnEntryRemoved += OnEntryChanged;
  254. GUILayoutY contentLayout = GUI.AddLayoutY();
  255. searchBarLayout = contentLayout.AddLayoutX();
  256. GUITextField searchField = new GUITextField();
  257. searchField.OnChanged += OnSearchChanged;
  258. GUIButton clearSearchBtn = new GUIButton("C");
  259. clearSearchBtn.OnClick += ClearSearch;
  260. clearSearchBtn.SetWidth(40);
  261. optionsButton = new GUIButton("O");
  262. optionsButton.OnClick += OpenOptionsWindow;
  263. optionsButton.SetWidth(40);
  264. searchBarLayout.AddElement(searchField);
  265. searchBarLayout.AddElement(clearSearchBtn);
  266. searchBarLayout.AddElement(optionsButton);
  267. // TODO - Add search bar + options button with drop-down
  268. // TODO - Add directory bar + home button
  269. contentScrollArea = new GUIScrollArea(GUIOption.FlexibleWidth(), GUIOption.FlexibleHeight());
  270. contentLayout.AddElement(contentScrollArea);
  271. contentLayout.AddFlexibleSpace();
  272. entryContextMenu = new ContextMenu();
  273. entryContextMenu.AddItem("Cut", CutSelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.X));
  274. entryContextMenu.AddItem("Copy", CopySelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.C));
  275. entryContextMenu.AddItem("Duplicate", DuplicateSelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.D));
  276. entryContextMenu.AddItem("Paste", PasteToSelection, new ShortcutKey(ButtonModifier.Ctrl, ButtonCode.V));
  277. Reset();
  278. dropTarget = new ProjectDropTarget(this);
  279. dropTarget.Bounds = contentScrollArea.Bounds;
  280. dropTarget.OnStart += DoOnDragStart;
  281. dropTarget.OnDrag += DoOnDragMove;
  282. dropTarget.OnLeave += DoOnDragLeave;
  283. dropTarget.OnDrop += DoOnDragDropped;
  284. }
  285. private ElementEntry FindElementAt(Vector2I windowPos)
  286. {
  287. Rect2I scrollBounds = contentScrollArea.Layout.Bounds;
  288. Vector2I scrollPos = windowPos;
  289. scrollPos.x -= scrollBounds.x;
  290. scrollPos.y -= scrollBounds.y;
  291. foreach (var element in entries)
  292. {
  293. if (element.bounds.Contains(scrollPos))
  294. return element;
  295. }
  296. return null;
  297. }
  298. private void DoOnDragStart(Vector2I windowPos)
  299. {
  300. ElementEntry underCursorElem = FindElementAt(windowPos);
  301. if (underCursorElem == null)
  302. return;
  303. if (!selectionPaths.Contains(underCursorElem.path))
  304. Select(new List<string> { underCursorElem.path });
  305. ResourceDragDropData dragDropData = new ResourceDragDropData(selectionPaths.ToArray());
  306. DragDrop.StartDrag(dragDropData);
  307. }
  308. private void DoOnDragMove(Vector2I windowPos)
  309. {
  310. ElementEntry underCursorElem = FindElementAt(windowPos);
  311. if (underCursorElem == null)
  312. {
  313. ClearHoverHighlight();
  314. }
  315. else
  316. {
  317. if (underCursorElem.path != hoverHighlightPath)
  318. {
  319. ClearHoverHighlight();
  320. hoverHighlightPath = underCursorElem.path;
  321. underCursorElem.MarkAsHovered(true);
  322. }
  323. }
  324. Rect2I scrollAreaBounds = contentScrollArea.Bounds;
  325. int scrollAreaTop = scrollAreaBounds.y;
  326. int scrollAreaBottom = scrollAreaBounds.y + scrollAreaBounds.height;
  327. if (windowPos.y > scrollAreaTop && windowPos.y <= (scrollAreaTop + DRAG_SCROLL_HEIGHT))
  328. autoScrollAmount = -DRAG_SCROLL_AMOUNT_PER_SECOND;
  329. else if (windowPos.y >= (scrollAreaBottom - DRAG_SCROLL_HEIGHT) && windowPos.y < scrollAreaBottom)
  330. autoScrollAmount = DRAG_SCROLL_AMOUNT_PER_SECOND;
  331. else
  332. autoScrollAmount = 0;
  333. }
  334. private void DoOnDragLeave()
  335. {
  336. ClearHoverHighlight();
  337. autoScrollAmount = 0;
  338. }
  339. private void DoOnDragDropped(Vector2I windowPos, string[] paths)
  340. {
  341. string resourceDir = ProjectLibrary.ResourceFolder;
  342. string destinationFolder = Path.Combine(resourceDir, currentDirectory);
  343. ElementEntry underCursorElement = FindElementAt(windowPos);
  344. if (underCursorElement != null)
  345. {
  346. LibraryEntry entry = ProjectLibrary.GetEntry(underCursorElement.path);
  347. if (entry != null && entry.Type == LibraryEntryType.Directory)
  348. destinationFolder = Path.Combine(resourceDir, entry.Path);
  349. }
  350. if (paths != null)
  351. {
  352. foreach (var path in paths)
  353. {
  354. if (path == null)
  355. continue;
  356. string absolutePath = path;
  357. if (!Path.IsPathRooted(absolutePath))
  358. absolutePath = Path.Combine(resourceDir, path);
  359. if (string.IsNullOrEmpty(absolutePath))
  360. continue;
  361. if (PathEx.IsPartOf(destinationFolder, absolutePath) || PathEx.Compare(absolutePath, destinationFolder))
  362. continue;
  363. string destination = Path.Combine(destinationFolder, Path.GetFileName(absolutePath));
  364. if (ProjectLibrary.Exists(path))
  365. ProjectLibrary.Move(path, destination, true);
  366. else
  367. ProjectLibrary.Copy(path, destination, true);
  368. }
  369. }
  370. ClearHoverHighlight();
  371. autoScrollAmount = 0;
  372. }
  373. private void ClearHoverHighlight()
  374. {
  375. if (!string.IsNullOrEmpty(hoverHighlightPath))
  376. {
  377. ElementEntry previousUnderCursorElem;
  378. if (entryLookup.TryGetValue(hoverHighlightPath, out previousUnderCursorElem))
  379. previousUnderCursorElem.MarkAsHovered(false);
  380. }
  381. hoverHighlightPath = "";
  382. }
  383. public void Ping(Resource resource)
  384. {
  385. if (!string.IsNullOrEmpty(pingPath))
  386. {
  387. ElementEntry entry;
  388. if (entryLookup.TryGetValue(pingPath, out entry))
  389. entry.MarkAsPinged(false);
  390. }
  391. if (resource != null)
  392. pingPath = ProjectLibrary.GetPath(resource);
  393. else
  394. pingPath = "";
  395. if (!string.IsNullOrEmpty(pingPath))
  396. {
  397. ElementEntry entry;
  398. if (entryLookup.TryGetValue(pingPath, out entry))
  399. entry.MarkAsPinged(true);
  400. ScrollToEntry(pingPath);
  401. }
  402. }
  403. private void Select(List<string> paths)
  404. {
  405. if (selectionPaths != null)
  406. {
  407. foreach (var path in selectionPaths)
  408. {
  409. ElementEntry entry;
  410. if (entryLookup.TryGetValue(path, out entry))
  411. entry.MarkAsSelected(false);
  412. }
  413. }
  414. selectionPaths = paths;
  415. if (selectionPaths != null)
  416. {
  417. foreach (var path in selectionPaths)
  418. {
  419. ElementEntry entry;
  420. if (entryLookup.TryGetValue(path, out entry))
  421. entry.MarkAsSelected(true);
  422. }
  423. }
  424. Ping(null);
  425. }
  426. private void EnterDirectory(string directory)
  427. {
  428. currentDirectory = directory;
  429. Ping(null);
  430. Select(new List<string>());
  431. Refresh();
  432. }
  433. private void Cut(IEnumerable<string> sourcePaths)
  434. {
  435. foreach (var path in cutPaths)
  436. {
  437. ElementEntry entry;
  438. if (entryLookup.TryGetValue(path, out entry))
  439. entry.MarkAsCut(false);
  440. }
  441. cutPaths.Clear();
  442. cutPaths.AddRange(sourcePaths);
  443. foreach (var path in cutPaths)
  444. {
  445. ElementEntry entry;
  446. if (entryLookup.TryGetValue(path, out entry))
  447. entry.MarkAsCut(true);
  448. }
  449. copyPaths.Clear();
  450. }
  451. private void Copy(IEnumerable<string> sourcePaths)
  452. {
  453. copyPaths.Clear();
  454. copyPaths.AddRange(sourcePaths);
  455. foreach (var path in cutPaths)
  456. {
  457. ElementEntry entry;
  458. if (entryLookup.TryGetValue(path, out entry))
  459. entry.MarkAsCut(false);
  460. }
  461. cutPaths.Clear();
  462. }
  463. private void Duplicate(IEnumerable<string> sourcePaths)
  464. {
  465. foreach (var source in sourcePaths)
  466. {
  467. int idx = 0;
  468. string destination;
  469. do
  470. {
  471. destination = source + "_" + idx;
  472. idx++;
  473. } while (!ProjectLibrary.Exists(destination));
  474. ProjectLibrary.Copy(source, destination);
  475. }
  476. }
  477. private void Paste(string destinationFolder)
  478. {
  479. if (copyPaths.Count > 0)
  480. {
  481. for (int i = 0; i < copyPaths.Count; i++)
  482. {
  483. string destination = Path.Combine(destinationFolder, Path.GetFileName(copyPaths[i]));
  484. ProjectLibrary.Copy(copyPaths[i], destination, true);
  485. }
  486. Refresh();
  487. }
  488. else if (cutPaths.Count > 0)
  489. {
  490. for (int i = 0; i < cutPaths.Count; i++)
  491. {
  492. string destination = Path.Combine(destinationFolder, Path.GetFileName(cutPaths[i]));
  493. ProjectLibrary.Move(cutPaths[i], destination, true);
  494. }
  495. cutPaths.Clear();
  496. Refresh();
  497. }
  498. }
  499. private void EditorUpdate()
  500. {
  501. if (HasContentFocus)
  502. {
  503. if (Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl))
  504. {
  505. if (Input.IsButtonUp(ButtonCode.C))
  506. {
  507. CopySelection();
  508. }
  509. else if (Input.IsButtonUp(ButtonCode.X))
  510. {
  511. CutSelection();
  512. }
  513. else if (Input.IsButtonUp(ButtonCode.D))
  514. {
  515. DuplicateSelection();
  516. }
  517. else if (Input.IsButtonUp(ButtonCode.V))
  518. {
  519. PasteToSelection();
  520. }
  521. }
  522. }
  523. if (autoScrollAmount != 0)
  524. {
  525. Rect2I contentBounds = contentScrollArea.ContentBounds;
  526. float scrollPct = autoScrollAmount / (float)contentBounds.height;
  527. contentScrollArea.VerticalScroll += scrollPct * Time.FrameDelta;
  528. }
  529. dropTarget.Update();
  530. }
  531. private void OnEntryChanged(string entry)
  532. {
  533. Refresh();
  534. }
  535. private void ScrollToEntry(string path)
  536. {
  537. Rect2I contentBounds = scrollAreaPanel.Bounds;
  538. Rect2I scrollAreaBounds = contentScrollArea.ContentBounds;
  539. ElementEntry entryGUI;
  540. if (!entryLookup.TryGetValue(path, out entryGUI))
  541. return;
  542. Rect2I entryBounds = entryGUI.icon.Bounds;
  543. float percent = (entryBounds.x - scrollAreaBounds.height * 0.5f) / contentBounds.height;
  544. percent = MathEx.Clamp01(percent);
  545. contentScrollArea.VerticalScroll = percent;
  546. }
  547. private void Refresh()
  548. {
  549. DirectoryEntry entry = ProjectLibrary.GetEntry(currentDirectory) as DirectoryEntry;
  550. if (entry == null)
  551. {
  552. Reset();
  553. return;
  554. }
  555. if (scrollAreaPanel != null)
  556. scrollAreaPanel.Destroy();
  557. entries.Clear();
  558. entryLookup.Clear();
  559. scrollAreaPanel = contentScrollArea.Layout.AddPanel();
  560. ContentInfo contentInfo = new ContentInfo(this, viewType);
  561. Rect2I scrollBounds = contentScrollArea.Bounds;
  562. int availableWidth = scrollBounds.width;
  563. LibraryEntry[] childEntries = entry.Children;
  564. if (childEntries.Length == 0)
  565. return;
  566. if (viewType == ProjectViewType.List16)
  567. {
  568. for (int i = 0; i < childEntries.Length; i++)
  569. {
  570. ElementEntry guiEntry = new ElementEntry(contentInfo, contentInfo.main, childEntries[i]);
  571. entries.Add(guiEntry);
  572. entryLookup[guiEntry.path] = guiEntry;
  573. if (i != childEntries.Length - 1)
  574. contentInfo.main.AddSpace(LIST_ENTRY_SPACING);
  575. }
  576. contentInfo.main.AddFlexibleSpace();
  577. }
  578. else
  579. {
  580. int tileSize = 64;
  581. switch (viewType)
  582. {
  583. case ProjectViewType.Grid64: tileSize = 64; break;
  584. case ProjectViewType.Grid48: tileSize = 48; break;
  585. case ProjectViewType.Grid32: tileSize = 32; break;
  586. }
  587. GUILayoutX rowLayout = contentInfo.main.AddLayoutX();
  588. rowLayout.AddFlexibleSpace();
  589. int elemSize = tileSize + GRID_ENTRY_SPACING;
  590. int elemsPerRow = (availableWidth - GRID_ENTRY_SPACING * 2) / elemSize;
  591. int numRows = MathEx.CeilToInt(childEntries.Length/(float) elemsPerRow);
  592. int neededHeight = numRows*(elemSize);
  593. bool requiresScrollbar = neededHeight > scrollBounds.height;
  594. if (requiresScrollbar)
  595. {
  596. availableWidth -= contentScrollArea.ScrollBarWidth;
  597. elemsPerRow = (availableWidth - GRID_ENTRY_SPACING * 2) / elemSize;
  598. }
  599. int elemsInRow = 0;
  600. for (int i = 0; i < childEntries.Length; i++)
  601. {
  602. if (elemsInRow == elemsPerRow && elemsInRow > 0)
  603. {
  604. rowLayout = contentInfo.main.AddLayoutX();
  605. contentInfo.main.AddSpace(GRID_ENTRY_SPACING);
  606. rowLayout.AddFlexibleSpace();
  607. elemsInRow = 0;
  608. }
  609. ElementEntry guiEntry = new ElementEntry(contentInfo, rowLayout, childEntries[i]);
  610. entries.Add(guiEntry);
  611. entryLookup[guiEntry.path] = guiEntry;
  612. rowLayout.AddFlexibleSpace();
  613. elemsInRow++;
  614. }
  615. int extraElements = elemsPerRow - elemsInRow;
  616. for (int i = 0; i < extraElements; i++)
  617. {
  618. rowLayout.AddSpace(tileSize);
  619. rowLayout.AddFlexibleSpace();
  620. }
  621. contentInfo.main.AddFlexibleSpace();
  622. }
  623. for (int i = 0; i < entries.Count; i++)
  624. {
  625. ElementEntry guiEntry = entries[i];
  626. guiEntry.Initialize();
  627. if (cutPaths.Contains(guiEntry.path))
  628. guiEntry.MarkAsCut(true);
  629. if (selectionPaths.Contains(guiEntry.path))
  630. guiEntry.MarkAsSelected(true);
  631. else if (pingPath == guiEntry.path)
  632. guiEntry.MarkAsPinged(true);
  633. }
  634. Rect2I contentBounds = contentInfo.main.Bounds;
  635. Rect2I minimalBounds = GetScrollAreaBounds();
  636. contentBounds.height = Math.Max(contentBounds.height, minimalBounds.height);
  637. GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
  638. catchAll.Bounds = contentBounds;
  639. catchAll.OnClick += OnCatchAllClicked;
  640. catchAll.SetContextMenu(entryContextMenu);
  641. catchAll.OnFocusChanged += OnContentsFocusChanged;
  642. contentInfo.underlay.AddElement(catchAll);
  643. }
  644. private void OnContentsFocusChanged(bool focus)
  645. {
  646. hasContentFocus = focus;
  647. }
  648. private void OnEntryClicked(string path)
  649. {
  650. Select(new List<string> { path });
  651. Selection.resourcePaths = new string[] {path};
  652. }
  653. private void OnEntryDoubleClicked(string path)
  654. {
  655. LibraryEntry entry = ProjectLibrary.GetEntry(path);
  656. if (entry != null && entry.Type == LibraryEntryType.Directory)
  657. {
  658. EnterDirectory(path);
  659. }
  660. }
  661. private void OnCatchAllClicked()
  662. {
  663. Select(new List<string> { });
  664. Selection.resourcePaths = new string[] { };
  665. }
  666. private void CutSelection()
  667. {
  668. if (selectionPaths.Count > 0)
  669. Cut(selectionPaths);
  670. }
  671. private void CopySelection()
  672. {
  673. if (selectionPaths.Count > 0)
  674. Copy(selectionPaths);
  675. }
  676. private void DuplicateSelection()
  677. {
  678. if (selectionPaths.Count > 0)
  679. Duplicate(selectionPaths);
  680. }
  681. private void PasteToSelection()
  682. {
  683. DirectoryEntry selectedDirectory = null;
  684. if (selectionPaths.Count == 1)
  685. {
  686. LibraryEntry entry = ProjectLibrary.GetEntry(selectionPaths[0]);
  687. if (entry != null && entry.Type == LibraryEntryType.Directory)
  688. selectedDirectory = (DirectoryEntry) entry;
  689. }
  690. if(selectedDirectory != null)
  691. Paste(selectedDirectory.Path);
  692. else
  693. Paste(currentDirectory);
  694. }
  695. private void OnSearchChanged(string newValue)
  696. {
  697. // TODO
  698. }
  699. private void ClearSearch()
  700. {
  701. // TODO
  702. }
  703. private void OpenOptionsWindow()
  704. {
  705. Vector2I openPosition;
  706. Rect2I buttonBounds = GUILayoutUtility.CalculateBounds(optionsButton, GUI);
  707. openPosition.x = buttonBounds.x + buttonBounds.width / 2;
  708. openPosition.y = buttonBounds.y + buttonBounds.height / 2;
  709. ProjectDropDown dropDown = DropDownWindow.Open<ProjectDropDown>(this, openPosition);
  710. dropDown.SetParent(this);
  711. }
  712. private void Reset()
  713. {
  714. currentDirectory = ProjectLibrary.Root.Path;
  715. Refresh();
  716. }
  717. private Rect2I GetScrollAreaBounds()
  718. {
  719. Rect2I bounds = GUI.Bounds;
  720. Rect2I searchBarBounds = searchBarLayout.Bounds;
  721. bounds.y += searchBarBounds.height;
  722. bounds.height -= searchBarBounds.height;
  723. return bounds;
  724. }
  725. protected override void WindowResized(int width, int height)
  726. {
  727. base.WindowResized(width, height);
  728. Refresh();
  729. dropTarget.Bounds = contentScrollArea.Bounds;
  730. }
  731. }
  732. internal class ProjectDropDown : DropDownWindow
  733. {
  734. private ProjectWindow parent;
  735. public ProjectDropDown()
  736. :base(150, 30)
  737. { }
  738. internal void SetParent(ProjectWindow parent)
  739. {
  740. this.parent = parent;
  741. GUIToggleGroup group = new GUIToggleGroup();
  742. GUIToggle list16 = new GUIToggle("16", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  743. GUIToggle grid32 = new GUIToggle("32", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  744. GUIToggle grid48 = new GUIToggle("48", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  745. GUIToggle grid64 = new GUIToggle("64", group, EditorStyles.Button, GUIOption.FixedWidth(30));
  746. ProjectViewType activeType = parent.ViewType;
  747. switch (activeType)
  748. {
  749. case ProjectViewType.List16:
  750. list16.ToggleOn();
  751. break;
  752. case ProjectViewType.Grid32:
  753. grid32.ToggleOn();
  754. break;
  755. case ProjectViewType.Grid48:
  756. grid48.ToggleOn();
  757. break;
  758. case ProjectViewType.Grid64:
  759. grid64.ToggleOn();
  760. break;
  761. }
  762. list16.OnToggled += (active) =>
  763. {
  764. if (active)
  765. ChangeViewType(ProjectViewType.List16);
  766. };
  767. grid32.OnToggled += (active) =>
  768. {
  769. if (active)
  770. ChangeViewType(ProjectViewType.Grid32);
  771. };
  772. grid48.OnToggled += (active) =>
  773. {
  774. if (active)
  775. ChangeViewType(ProjectViewType.Grid48);
  776. };
  777. grid64.OnToggled += (active) =>
  778. {
  779. if (active)
  780. ChangeViewType(ProjectViewType.Grid64);
  781. };
  782. GUILayoutY vertLayout = GUI.AddLayoutY();
  783. vertLayout.AddFlexibleSpace();
  784. GUILayoutX contentLayout = vertLayout.AddLayoutX();
  785. contentLayout.AddFlexibleSpace();
  786. contentLayout.AddElement(list16);
  787. contentLayout.AddElement(grid32);
  788. contentLayout.AddElement(grid48);
  789. contentLayout.AddElement(grid64);
  790. contentLayout.AddFlexibleSpace();
  791. vertLayout.AddFlexibleSpace();
  792. }
  793. private void ChangeViewType(ProjectViewType viewType)
  794. {
  795. parent.ViewType = viewType;
  796. }
  797. }
  798. }