//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// using System; using System.Collections.Generic; using System.IO; using BansheeEngine; namespace BansheeEditor { /** @addtogroup Library * @{ */ /// /// Manages GUI for the content area of the library window. Content area displays resources as a grid or list of /// resource icons. /// internal class LibraryGUIContent { internal const int TOP_MARGIN = 8; internal const int LIST_ENTRY_SPACING = 6; private GUIPanel mainPanel; private GUILayout main; private GUIPanel overlay; private GUIPanel underlay; private GUIPanel deepUnderlay; private GUIPanel renameOverlay; private LibraryWindow window; private GUIScrollArea parent; private int tileSize; private bool gridLayout; private int elementsPerRow; private int horzElementSpacing; private List entries = new List(); private Dictionary entryLookup = new Dictionary(); /// /// Area of the content area relative to the parent window. /// public Rect2I Bounds { get { return main.Bounds; } } /// /// Number of elements per row. Only relevant for grid layouts. /// public int ElementsPerRow { get { return elementsPerRow; } } /// /// Returns horizontal spacing between elements in a grid layout. /// public int HorzElementSpacing { get { return horzElementSpacing; } } /// /// Determines is the content display in a grid (true) or list (false) layout. /// public bool GridLayout { get { return gridLayout; } } /// /// Sizes of a single resource tile in grid layout. Size in list layout is fixed. /// public int TileSize { get { return tileSize; } } /// /// Returns objects representing each of the displayed resource icons. /// public List Entries { get { return entries; } } /// /// Returns parent window the content area is part of. /// public LibraryWindow Window { get { return window; } } /// /// Returns a GUI panel that can be used for displaying elements underneath the resource tiles. /// public GUIPanel Underlay { get { return underlay; } } /// /// Returns a GUI panel that can be used for displaying elements underneath the resource tiles. Displays under /// . /// public GUIPanel DeepUnderlay { get { return deepUnderlay; } } /// /// Returns a GUI panel that can be used for displaying elements above the resource tiles. /// public GUIPanel Overlay { get { return overlay; } } /// /// Returns a GUI panel that can be used for displaying rename input box. Displayed on top of the resource tiles /// and the standard overlay. /// public GUIPanel RenameOverlay { get { return renameOverlay; } } /// /// Constructs a new GUI library content object. /// /// Parent window the content area is part of. /// Scroll area the content area is part of. public LibraryGUIContent(LibraryWindow window, GUIScrollArea parent) { this.window = window; this.parent = parent; } /// /// Refreshes the contents of the content area. Must be called at least once after construction. /// /// Determines how to display the resource tiles. /// Project library entries to display. /// Bounds within which to lay out the content entries. public void Refresh(ProjectViewType viewType, LibraryEntry[] entriesToDisplay, Rect2I bounds) { if (mainPanel != null) mainPanel.Destroy(); entries.Clear(); entryLookup.Clear(); mainPanel = parent.Layout.AddPanel(); GUIPanel contentPanel = mainPanel.AddPanel(1); overlay = mainPanel.AddPanel(0); underlay = mainPanel.AddPanel(2); deepUnderlay = mainPanel.AddPanel(3); renameOverlay = mainPanel.AddPanel(-1); main = contentPanel.AddLayoutY(); List resourcesToDisplay = new List(); foreach (var entry in entriesToDisplay) { if (entry.Type == LibraryEntryType.Directory) resourcesToDisplay.Add(new ResourceToDisplay(entry.Path, LibraryGUIEntryType.Single)); else { FileEntry fileEntry = (FileEntry)entry; ResourceMeta[] metas = fileEntry.ResourceMetas; if (metas.Length > 0) { if (metas.Length == 1) resourcesToDisplay.Add(new ResourceToDisplay(entry.Path, LibraryGUIEntryType.Single)); else { resourcesToDisplay.Add(new ResourceToDisplay(entry.Path, LibraryGUIEntryType.MultiFirst)); for (int i = 1; i < metas.Length - 1; i++) { string path = Path.Combine(entry.Path, metas[i].SubresourceName); resourcesToDisplay.Add(new ResourceToDisplay(path, LibraryGUIEntryType.MultiElement)); } string lastPath = Path.Combine(entry.Path, metas[metas.Length - 1].SubresourceName); resourcesToDisplay.Add(new ResourceToDisplay(lastPath, LibraryGUIEntryType.MultiLast)); } } } } if (viewType == ProjectViewType.List16) { tileSize = 16; gridLayout = false; elementsPerRow = 1; horzElementSpacing = 0; int elemWidth = bounds.width; int elemHeight = tileSize; main.AddSpace(TOP_MARGIN); for (int i = 0; i < resourcesToDisplay.Count; i++) { ResourceToDisplay entry = resourcesToDisplay[i]; LibraryGUIEntry guiEntry = new LibraryGUIEntry(this, main, entry.path, i, elemWidth, elemHeight, entry.type); entries.Add(guiEntry); entryLookup[guiEntry.path] = guiEntry; if (i != resourcesToDisplay.Count - 1) main.AddSpace(LIST_ENTRY_SPACING); } main.AddFlexibleSpace(); } else { int elemWidth = 0; int elemHeight = 0; int vertElemSpacing = 0; switch (viewType) { case ProjectViewType.Grid64: tileSize = 64; elemWidth = tileSize; elemHeight = tileSize + 36; horzElementSpacing = 10; vertElemSpacing = 12; break; case ProjectViewType.Grid48: tileSize = 48; elemWidth = tileSize; elemHeight = tileSize + 36; horzElementSpacing = 8; vertElemSpacing = 10; break; case ProjectViewType.Grid32: tileSize = 32; elemWidth = tileSize + 16; elemHeight = tileSize + 48; horzElementSpacing = 6; vertElemSpacing = 10; break; } gridLayout = true; int availableWidth = bounds.width; elementsPerRow = MathEx.FloorToInt((availableWidth - horzElementSpacing) / (float)(elemWidth + horzElementSpacing)); elementsPerRow = Math.Max(elementsPerRow, 1); int numRows = MathEx.CeilToInt(resourcesToDisplay.Count / (float)elementsPerRow); int neededHeight = numRows * elemHeight + TOP_MARGIN; if (numRows > 0) neededHeight += (numRows - 1)* vertElemSpacing; bool requiresScrollbar = neededHeight > bounds.height; if (requiresScrollbar) { availableWidth -= parent.ScrollBarWidth; elementsPerRow = MathEx.FloorToInt((availableWidth - horzElementSpacing) / (float)(elemWidth + horzElementSpacing)); elementsPerRow = Math.Max(elementsPerRow, 1); } int extraRowSpace = availableWidth - (elementsPerRow * (elemWidth + horzElementSpacing) + horzElementSpacing); main.AddSpace(TOP_MARGIN); GUILayoutX rowLayout = main.AddLayoutX(); rowLayout.AddSpace(horzElementSpacing); int elemsInRow = 0; for (int i = 0; i < resourcesToDisplay.Count; i++) { if (elemsInRow == elementsPerRow && elemsInRow > 0) { main.AddSpace(vertElemSpacing); rowLayout.AddSpace(extraRowSpace); rowLayout = main.AddLayoutX(); rowLayout.AddSpace(horzElementSpacing); elemsInRow = 0; } ResourceToDisplay entry = resourcesToDisplay[i]; LibraryGUIEntry guiEntry = new LibraryGUIEntry(this, rowLayout, entry.path, i, elemWidth, elemHeight, entry.type); entries.Add(guiEntry); entryLookup[guiEntry.path] = guiEntry; rowLayout.AddSpace(horzElementSpacing); elemsInRow++; } int extraElements = elementsPerRow - elemsInRow; rowLayout.AddSpace((elemWidth + horzElementSpacing) * extraElements + extraRowSpace); main.AddFlexibleSpace(); } for (int i = 0; i < entries.Count; i++) { LibraryGUIEntry guiEntry = entries[i]; guiEntry.Initialize(); } } /// /// Called every frame. /// public void Update() { for (int i = 0; i < entries.Count; i++) entries[i].Update(); } /// /// Changes the visual representation of an element at the specified path as being hovered over. /// /// Project library path to the element to mark. /// True if mark as hovered, false to reset to normal. public void MarkAsHovered(string path, bool hovered) { if (!string.IsNullOrEmpty(path)) { LibraryGUIEntry previousUnderCursorElem; if (entryLookup.TryGetValue(path, out previousUnderCursorElem)) previousUnderCursorElem.MarkAsHovered(hovered); } } /// /// Changes the visual representation of an element at the specified path as being pinged. /// /// Project library path to the element to mark. /// True if mark as pinged, false to reset to normal. public void MarkAsPinged(string path, bool pinged) { if (!string.IsNullOrEmpty(path)) { LibraryGUIEntry previousUnderCursorElem; if (entryLookup.TryGetValue(path, out previousUnderCursorElem)) previousUnderCursorElem.MarkAsPinged(pinged); } } /// /// Changes the visual representation of an element at the specified path as being cut. /// /// Project library path to the element to mark. /// True if mark as cut, false to reset to normal. public void MarkAsCut(string path, bool cut) { if (!string.IsNullOrEmpty(path)) { LibraryGUIEntry previousUnderCursorElem; if (entryLookup.TryGetValue(path, out previousUnderCursorElem)) previousUnderCursorElem.MarkAsCut(cut); } } /// /// Changes the visual representation of an element at the specified path as being selected. /// /// Project library path to the element to mark. /// True if mark as selected, false to reset to normal. public void MarkAsSelected(string path, bool selected) { if (!string.IsNullOrEmpty(path)) { LibraryGUIEntry previousUnderCursorElem; if (entryLookup.TryGetValue(path, out previousUnderCursorElem)) previousUnderCursorElem.MarkAsSelected(selected); } } /// /// Attempts to find a resource tile element at the specified coordinates. /// /// Coordinates relative to the scroll area the content area is part of. /// True if found an entry, false otherwise. public LibraryGUIEntry FindElementAt(Vector2I scrollPos) { foreach (var element in entries) { if (element.bounds.Contains(scrollPos)) return element; } return null; } /// /// Attempts to find all resource tile elements overlapping the specified bounds. /// /// Bounds to check for overlap, specified relative to the scroll area the content area /// is part of. /// A list of found entries. public LibraryGUIEntry[] FindElementsOverlapping(Rect2I scrollBounds) { List elements = new List(); foreach (var element in entries) { if (element.Bounds.Overlaps(scrollBounds)) elements.Add(element); } return elements.ToArray(); } /// /// Attempts to find a resource tile element with the specified path. /// /// Project library path to the element. /// Found element, or null if none found. /// True if an element was found, false otherwise. public bool TryGetEntry(string path, out LibraryGUIEntry entry) { return entryLookup.TryGetValue(path, out entry); } /// /// Helper structure containing information about a single entry to display in the library. /// private struct ResourceToDisplay { public ResourceToDisplay(string path, LibraryGUIEntryType type) { this.path = path; this.type = type; } public string path; public LibraryGUIEntryType type; } } /** @} */ }