LibraryGUIEntry.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using BansheeEngine;
  8. namespace BansheeEditor
  9. {
  10. /// <summary>
  11. /// Represents GUI for a single resource tile used in <see cref="LibraryGUIContent"/>.
  12. /// </summary>
  13. internal class LibraryGUIEntry
  14. {
  15. private const int MAX_LABEL_HEIGHT = 50;
  16. private static readonly Color PING_COLOR = Color.BansheeOrange;
  17. private static readonly Color SELECTION_COLOR = Color.DarkCyan;
  18. private static readonly Color HOVER_COLOR = new Color(Color.DarkCyan.r, Color.DarkCyan.g, Color.DarkCyan.b, 0.5f);
  19. private static readonly Color CUT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
  20. private const int SELECTION_EXTRA_WIDTH = 3;
  21. /// <summary>
  22. /// Possible visual states for the resource tile.
  23. /// </summary>
  24. enum UnderlayState // Note: Order of these is relevant
  25. {
  26. None, Hovered, Selected, Pinged
  27. }
  28. public int index;
  29. public string path;
  30. public GUITexture icon;
  31. public GUILabel label;
  32. public Rect2I bounds;
  33. private GUITexture underlay;
  34. private LibraryGUIContent owner;
  35. private UnderlayState underlayState;
  36. private GUITextBox renameTextBox;
  37. private bool delayedSelect;
  38. private float delayedSelectTime;
  39. /// <summary>
  40. /// Bounds of the entry relative to part content area.
  41. /// </summary>
  42. public Rect2I Bounds
  43. {
  44. get { return bounds; }
  45. }
  46. /// <summary>
  47. /// Constructs a new resource tile entry.
  48. /// </summary>
  49. /// <param name="owner">Content area this entry is part of.</param>
  50. /// <param name="parent">Parent layout to add this entry's GUI elements to.</param>
  51. /// <param name="entry">Project library entry this entry displays data for.</param>
  52. /// <param name="index">Sequential index of the entry in the conent area.</param>
  53. /// <param name="labelWidth">Width of the GUI labels that display the elements.</param>
  54. public LibraryGUIEntry(LibraryGUIContent owner, GUILayout parent, LibraryEntry entry, int index, int labelWidth)
  55. {
  56. GUILayout entryLayout;
  57. if (owner.GridLayout)
  58. entryLayout = parent.AddLayoutY();
  59. else
  60. entryLayout = parent.AddLayoutX();
  61. SpriteTexture iconTexture = GetIcon(entry, owner.TileSize);
  62. icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit,
  63. true, GUIOption.FixedHeight(owner.TileSize), GUIOption.FixedWidth(owner.TileSize));
  64. label = null;
  65. if (owner.GridLayout)
  66. {
  67. label = new GUILabel(entry.Name, EditorStyles.MultiLineLabelCentered,
  68. GUIOption.FixedWidth(labelWidth), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
  69. }
  70. else
  71. {
  72. label = new GUILabel(entry.Name);
  73. }
  74. entryLayout.AddElement(icon);
  75. entryLayout.AddElement(label);
  76. this.owner = owner;
  77. this.index = index;
  78. this.path = entry.Path;
  79. this.bounds = new Rect2I();
  80. this.underlay = null;
  81. }
  82. /// <summary>
  83. /// Positions the GUI elements. Must be called after construction, but only after all content area entries have
  84. /// been constructed so that entry's final bounds are known.
  85. /// </summary>
  86. public void Initialize()
  87. {
  88. bounds = icon.Bounds;
  89. Rect2I labelBounds = label.Bounds;
  90. bounds.x = MathEx.Min(bounds.x, labelBounds.x - SELECTION_EXTRA_WIDTH);
  91. bounds.y = MathEx.Min(bounds.y, labelBounds.y) - 5; // 5 - Just padding for better look
  92. bounds.width = MathEx.Max(bounds.x + bounds.width,
  93. labelBounds.x + labelBounds.width) - bounds.x + SELECTION_EXTRA_WIDTH;
  94. bounds.height = MathEx.Max(bounds.y + bounds.height,
  95. labelBounds.y + labelBounds.height) - bounds.y;
  96. string hoistedPath = path;
  97. GUIButton overlayBtn = new GUIButton("", EditorStyles.Blank);
  98. overlayBtn.Bounds = bounds;
  99. overlayBtn.OnClick += () => OnEntryClicked(hoistedPath);
  100. overlayBtn.OnDoubleClick += () => OnEntryDoubleClicked(hoistedPath);
  101. overlayBtn.SetContextMenu(owner.Window.ContextMenu);
  102. owner.Overlay.AddElement(overlayBtn);
  103. }
  104. /// <summary>
  105. /// Called every frame.
  106. /// </summary>
  107. public void Update()
  108. {
  109. if (delayedSelect && Time.Elapsed > delayedSelectTime)
  110. {
  111. owner.Window.Select(path);
  112. delayedSelect = false;
  113. }
  114. }
  115. /// <summary>
  116. /// Changes the visual representation of the element as being cut.
  117. /// </summary>
  118. /// <param name="enable">True if mark as cut, false to reset to normal.</param>
  119. public void MarkAsCut(bool enable)
  120. {
  121. if (enable)
  122. icon.SetTint(CUT_COLOR);
  123. else
  124. icon.SetTint(Color.White);
  125. }
  126. /// <summary>
  127. /// Changes the visual representation of the element as being selected.
  128. /// </summary>
  129. /// <param name="enable">True if mark as selected, false to reset to normal.</param>
  130. public void MarkAsSelected(bool enable)
  131. {
  132. if ((int)underlayState > (int)UnderlayState.Selected)
  133. return;
  134. if (enable)
  135. {
  136. CreateUnderlay();
  137. underlay.SetTint(SELECTION_COLOR);
  138. underlayState = UnderlayState.Selected;
  139. }
  140. else
  141. {
  142. ClearUnderlay();
  143. underlayState = UnderlayState.None;
  144. }
  145. }
  146. /// <summary>
  147. /// Changes the visual representation of the element as being pinged.
  148. /// </summary>
  149. /// <param name="enable">True if mark as pinged, false to reset to normal.</param>
  150. public void MarkAsPinged(bool enable)
  151. {
  152. if ((int)underlayState > (int)UnderlayState.Pinged)
  153. return;
  154. if (enable)
  155. {
  156. CreateUnderlay();
  157. underlay.SetTint(PING_COLOR);
  158. underlayState = UnderlayState.Pinged;
  159. }
  160. else
  161. {
  162. ClearUnderlay();
  163. underlayState = UnderlayState.None;
  164. }
  165. }
  166. /// <summary>
  167. /// Changes the visual representation of the element as being hovered over.
  168. /// </summary>
  169. /// <param name="enable">True if mark as hovered, false to reset to normal.</param>
  170. public void MarkAsHovered(bool enable)
  171. {
  172. if ((int)underlayState > (int)UnderlayState.Hovered)
  173. return;
  174. if (enable)
  175. {
  176. CreateUnderlay();
  177. underlay.SetTint(HOVER_COLOR);
  178. underlayState = UnderlayState.Hovered;
  179. }
  180. else
  181. {
  182. ClearUnderlay();
  183. underlayState = UnderlayState.None;
  184. }
  185. }
  186. /// <summary>
  187. /// Starts a rename operation over the entry, displaying the rename input box.
  188. /// </summary>
  189. public void StartRename()
  190. {
  191. if (renameTextBox != null)
  192. return;
  193. renameTextBox = new GUITextBox(true);
  194. renameTextBox.Bounds = label.Bounds;
  195. owner.RenameOverlay.AddElement(renameTextBox);
  196. string name = Path.GetFileNameWithoutExtension(PathEx.GetTail(path));
  197. renameTextBox.Text = name;
  198. renameTextBox.Focus = true;
  199. label.Active = false;
  200. }
  201. /// <summary>
  202. /// Stops a rename operation over the entry, hiding the rename input box.
  203. /// </summary>
  204. public void StopRename()
  205. {
  206. if (renameTextBox != null)
  207. {
  208. renameTextBox.Destroy();
  209. renameTextBox = null;
  210. }
  211. label.Active = true;
  212. }
  213. /// <summary>
  214. /// Gets the new name of the entry. Only valid while a rename operation is in progress.
  215. /// </summary>
  216. /// <returns>New name of the entry currently entered in the rename input box.</returns>
  217. public string GetRenamedName()
  218. {
  219. if (renameTextBox != null)
  220. return renameTextBox.Text;
  221. return "";
  222. }
  223. /// <summary>
  224. /// Clears the underlay GUI element (e.g. ping, hover, select).
  225. /// </summary>
  226. private void ClearUnderlay()
  227. {
  228. if (underlay != null)
  229. {
  230. underlay.Destroy();
  231. underlay = null;
  232. }
  233. underlayState = UnderlayState.None;
  234. }
  235. /// <summary>
  236. /// Creates a GUI elements that may be used for underlay effects (e.g. ping, hover, select).
  237. /// </summary>
  238. private void CreateUnderlay()
  239. {
  240. if (underlay == null)
  241. {
  242. underlay = new GUITexture(Builtin.WhiteTexture);
  243. underlay.Bounds = Bounds;
  244. owner.Underlay.AddElement(underlay);
  245. }
  246. }
  247. /// <summary>
  248. /// Triggered when the user clicks on the entry.
  249. /// </summary>
  250. /// <param name="path">Project library path of the clicked entry.</param>
  251. private void OnEntryClicked(string path)
  252. {
  253. LibraryEntry entry = ProjectLibrary.GetEntry(path);
  254. if (entry != null && entry.Type == LibraryEntryType.Directory)
  255. {
  256. // If entry is a directory delay selection as it might be a double-click, in which case we want to keep
  257. // whatever selection is active currently so that user can perform drag and drop with its inspector
  258. // from the folder he is browsing to.
  259. delayedSelect = true;
  260. delayedSelectTime = Time.Elapsed + 0.5f;
  261. }
  262. else
  263. owner.Window.Select(path);
  264. }
  265. /// <summary>
  266. /// Triggered when the user double-clicked on the entry.
  267. /// </summary>
  268. /// <param name="path">Project library path of the double-clicked entry.</param>
  269. private void OnEntryDoubleClicked(string path)
  270. {
  271. delayedSelect = false;
  272. LibraryEntry entry = ProjectLibrary.GetEntry(path);
  273. if (entry != null)
  274. {
  275. if (entry.Type == LibraryEntryType.Directory)
  276. owner.Window.EnterDirectory(path);
  277. else
  278. {
  279. FileEntry resEntry = (FileEntry)entry;
  280. if (resEntry.ResType == ResourceType.Prefab)
  281. {
  282. EditorApplication.LoadScene(resEntry.Path);
  283. }
  284. else if (resEntry.ResType == ResourceType.ScriptCode)
  285. {
  286. ProgressBar.Show("Opening Visual Studio...", 1.0f);
  287. CodeEditor.OpenFile(resEntry.Path, 0);
  288. ProgressBar.Hide();
  289. }
  290. }
  291. }
  292. }
  293. /// <summary>
  294. /// Returns an icon that can be used for displaying a resource of the specified type.
  295. /// </summary>
  296. /// <param name="entry">Project library entry of the resource to retrieve icon for.</param>
  297. /// <param name="size">Size of the icon to retrieve, in pixels.</param>
  298. /// <returns>Icon to display for the specified entry.</returns>
  299. private static SpriteTexture GetIcon(LibraryEntry entry, int size)
  300. {
  301. if (entry.Type == LibraryEntryType.Directory)
  302. {
  303. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Folder, size);
  304. }
  305. else
  306. {
  307. FileEntry fileEntry = (FileEntry)entry;
  308. switch (fileEntry.ResType)
  309. {
  310. case ResourceType.Font:
  311. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Font, size);
  312. case ResourceType.Mesh:
  313. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Mesh, size);
  314. case ResourceType.Texture:
  315. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Texture, size);
  316. case ResourceType.PlainText:
  317. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.PlainText, size);
  318. case ResourceType.ScriptCode:
  319. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.ScriptCode, size);
  320. case ResourceType.SpriteTexture:
  321. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.SpriteTexture, size);
  322. case ResourceType.Shader:
  323. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Shader, size);
  324. case ResourceType.ShaderInclude:
  325. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Shader, size);
  326. case ResourceType.Material:
  327. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Material, size);
  328. case ResourceType.Prefab:
  329. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.Prefab, size);
  330. case ResourceType.GUISkin:
  331. return EditorBuiltin.GetLibraryItemIcon(LibraryItemIcon.GUISkin, size);
  332. }
  333. }
  334. return null;
  335. }
  336. }
  337. }