Browse Source

Refactoring library window

BearishSun 10 years ago
parent
commit
bf5674890d

+ 83 - 0
MBansheeEditor/Library/LibraryDropDown.cs

@@ -0,0 +1,83 @@
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    internal class LibraryDropDown : DropDownWindow
+    {
+        private LibraryWindow parent;
+
+        public LibraryDropDown()
+            : base(150, 30)
+        { }
+
+        internal void SetParent(LibraryWindow parent)
+        {
+            this.parent = parent;
+
+            GUIToggleGroup group = new GUIToggleGroup();
+
+            GUIToggle list16 = new GUIToggle(new LocEdString("16"), group, EditorStyles.Button, GUIOption.FixedWidth(30));
+            GUIToggle grid32 = new GUIToggle(new LocEdString("32"), group, EditorStyles.Button, GUIOption.FixedWidth(30));
+            GUIToggle grid48 = new GUIToggle(new LocEdString("48"), group, EditorStyles.Button, GUIOption.FixedWidth(30));
+            GUIToggle grid64 = new GUIToggle(new LocEdString("64"), group, EditorStyles.Button, GUIOption.FixedWidth(30));
+
+            ProjectViewType activeType = parent.ViewType;
+            switch (activeType)
+            {
+                case ProjectViewType.List16:
+                    list16.Value = true;
+                    break;
+                case ProjectViewType.Grid32:
+                    grid32.Value = true;
+                    break;
+                case ProjectViewType.Grid48:
+                    grid48.Value = true;
+                    break;
+                case ProjectViewType.Grid64:
+                    grid64.Value = true;
+                    break;
+            }
+
+            list16.OnToggled += (active) =>
+            {
+                if (active)
+                    ChangeViewType(ProjectViewType.List16);
+            };
+
+            grid32.OnToggled += (active) =>
+            {
+                if (active)
+                    ChangeViewType(ProjectViewType.Grid32);
+            };
+
+            grid48.OnToggled += (active) =>
+            {
+                if (active)
+                    ChangeViewType(ProjectViewType.Grid48);
+            };
+
+            grid64.OnToggled += (active) =>
+            {
+                if (active)
+                    ChangeViewType(ProjectViewType.Grid64);
+            };
+
+            GUILayoutY vertLayout = GUI.AddLayoutY();
+
+            vertLayout.AddFlexibleSpace();
+            GUILayoutX contentLayout = vertLayout.AddLayoutX();
+            contentLayout.AddFlexibleSpace();
+            contentLayout.AddElement(list16);
+            contentLayout.AddElement(grid32);
+            contentLayout.AddElement(grid48);
+            contentLayout.AddElement(grid64);
+            contentLayout.AddFlexibleSpace();
+            vertLayout.AddFlexibleSpace();
+        }
+
+        private void ChangeViewType(ProjectViewType viewType)
+        {
+            parent.ViewType = viewType;
+        }
+    }
+}

+ 0 - 0
MBansheeEditor/LibraryDropTarget.cs → MBansheeEditor/Library/LibraryDropTarget.cs


+ 281 - 0
MBansheeEditor/Library/LibraryGUIContent.cs

@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    internal class LibraryGUIContent
+    {
+        private const int MIN_HORZ_SPACING = 8;
+        private const int GRID_ENTRY_SPACING = 15;
+        private const int LIST_ENTRY_SPACING = 7;
+
+        private GUIPanel mainPanel;
+        private GUILayout main;
+        private GUIPanel overlay;
+        private GUIPanel underlay;
+        private GUIPanel inputOverlay;
+
+        private LibraryWindow window;
+        private GUIScrollArea parent;
+        private int tileSize;
+        private bool gridLayout;
+
+        private int elementsPerRow;
+        private int labelWidth;
+
+        private LibraryGUIEntry[] entries = new LibraryGUIEntry[0];
+        private Dictionary<string, LibraryGUIEntry> entryLookup = new Dictionary<string, LibraryGUIEntry>();
+
+        public Rect2I Bounds
+        {
+            get { return main.Bounds; }
+        }
+
+        public int ElementsPerRow
+        {
+            get { return elementsPerRow; }
+        }
+
+        public int LabelWidth
+        {
+            get { return labelWidth; }
+        }
+
+        public bool GridLayout
+        {
+            get { return gridLayout; }
+        }
+
+        public int TileSize
+        {
+            get { return tileSize; }
+        }
+
+        public LibraryGUIEntry[] Entries
+        {
+            get { return entries; }
+        }
+
+        // TODO: This doesn't feel like it should be public
+        public LibraryWindow Window
+        {
+            get { return window; }
+        }
+
+        // TODO: This doesn't feel like it should be public
+        public GUIPanel Underlay
+        {
+            get { return underlay; }
+        }
+
+        // TODO: This doesn't feel like it should be public
+        public GUIPanel Overlay
+        {
+            get { return overlay; }
+        }
+
+        // TODO: This doesn't feel like it should be public
+        public GUIPanel InputOverlay
+        {
+            get { return inputOverlay; }
+        }
+
+        public LibraryGUIContent(LibraryWindow window, GUIScrollArea parent)
+        {
+            this.window = window;
+            this.parent = parent;
+        }
+
+        public void Refresh(ProjectViewType viewType, LibraryEntry[] entriesToDisplay)
+        {
+            if (mainPanel != null)
+                mainPanel.Destroy();
+
+            entries = new LibraryGUIEntry[entriesToDisplay.Length];
+            entryLookup.Clear();
+
+            mainPanel = parent.Layout.AddPanel();
+
+            GUIPanel contentPanel = mainPanel.AddPanel(1);
+            overlay = mainPanel.AddPanel(0);
+            underlay = mainPanel.AddPanel(2);
+            inputOverlay = mainPanel.AddPanel(-1);
+
+            main = contentPanel.AddLayoutY();
+
+            if (viewType == ProjectViewType.List16)
+            {
+                tileSize = 16;
+                gridLayout = false;
+                elementsPerRow = 1;
+            }
+            else
+            {
+                switch (viewType)
+                {
+                    case ProjectViewType.Grid64:
+                        tileSize = 64;
+                        break;
+                    case ProjectViewType.Grid48:
+                        tileSize = 48;
+                        break;
+                    case ProjectViewType.Grid32:
+                        tileSize = 32;
+                        break;
+                }
+
+                gridLayout = true;
+
+                Rect2I scrollBounds = parent.Bounds;
+                int availableWidth = scrollBounds.width;
+
+                int elemSize = tileSize + GRID_ENTRY_SPACING;
+                elementsPerRow = (availableWidth - GRID_ENTRY_SPACING * 2) / elemSize;
+                elementsPerRow = Math.Max(elementsPerRow, 1);
+
+                int numRows = MathEx.CeilToInt(entriesToDisplay.Length / (float)elementsPerRow);
+                int neededHeight = numRows * (elemSize);
+
+                bool requiresScrollbar = neededHeight > scrollBounds.height;
+                if (requiresScrollbar)
+                {
+                    availableWidth -= parent.ScrollBarWidth;
+                    elementsPerRow = (availableWidth - GRID_ENTRY_SPACING * 2) / elemSize;
+                }
+
+                if (elementsPerRow > 0)
+                    labelWidth = (availableWidth - (elementsPerRow + 1) * MIN_HORZ_SPACING) / elementsPerRow;
+                else
+                    labelWidth = 0;
+            }
+
+            if (viewType == ProjectViewType.List16)
+            {
+                for (int i = 0; i < entriesToDisplay.Length; i++)
+                {
+                    LibraryGUIEntry guiEntry = new LibraryGUIEntry(this, main, entriesToDisplay[i], i);
+                    entries[i] = guiEntry;
+                    entryLookup[guiEntry.path] = guiEntry;
+
+                    if (i != entriesToDisplay.Length - 1)
+                        main.AddSpace(LIST_ENTRY_SPACING);
+                }
+
+                main.AddFlexibleSpace();
+            }
+            else
+            {
+                main.AddSpace(GRID_ENTRY_SPACING / 2);
+                GUILayoutX rowLayout = main.AddLayoutX();
+                main.AddSpace(GRID_ENTRY_SPACING);
+                rowLayout.AddFlexibleSpace();
+
+                int elemsInRow = 0;
+
+                for (int i = 0; i < entriesToDisplay.Length; i++)
+                {
+                    if (elemsInRow == elementsPerRow && elemsInRow > 0)
+                    {
+                        rowLayout = main.AddLayoutX();
+                        main.AddSpace(GRID_ENTRY_SPACING);
+
+                        rowLayout.AddFlexibleSpace();
+                        elemsInRow = 0;
+                    }
+
+                    LibraryGUIEntry guiEntry = new LibraryGUIEntry(this, rowLayout, entriesToDisplay[i], i);
+                    entries[i] = guiEntry;
+                    entryLookup[guiEntry.path] = guiEntry;
+
+                    rowLayout.AddFlexibleSpace();
+
+                    elemsInRow++;
+                }
+
+                int extraElements = elementsPerRow - elemsInRow;
+                for (int i = 0; i < extraElements; i++)
+                {
+                    rowLayout.AddSpace(labelWidth);
+                    rowLayout.AddFlexibleSpace();
+                }
+
+                main.AddFlexibleSpace();
+            }
+
+            for (int i = 0; i < entries.Length; i++)
+            {
+                LibraryGUIEntry guiEntry = entries[i];
+                guiEntry.Initialize();
+            }
+        }
+
+        public void MarkAsHovered(string path, bool hovered)
+        {
+            if (!string.IsNullOrEmpty(path))
+            {
+                LibraryGUIEntry previousUnderCursorElem;
+                if (entryLookup.TryGetValue(path, out previousUnderCursorElem))
+                    previousUnderCursorElem.MarkAsHovered(hovered);
+            }
+        }
+
+        public void MarkAsPinged(string path, bool pinged)
+        {
+            if (!string.IsNullOrEmpty(path))
+            {
+                LibraryGUIEntry previousUnderCursorElem;
+                if (entryLookup.TryGetValue(path, out previousUnderCursorElem))
+                    previousUnderCursorElem.MarkAsPinged(pinged);
+            }
+        }
+
+        public void MarkAsCut(string path, bool cut)
+        {
+            if (!string.IsNullOrEmpty(path))
+            {
+                LibraryGUIEntry previousUnderCursorElem;
+                if (entryLookup.TryGetValue(path, out previousUnderCursorElem))
+                    previousUnderCursorElem.MarkAsCut(cut);
+            }
+        }
+
+        public void MarkAsSelected(string path, bool selected)
+        {
+            if (!string.IsNullOrEmpty(path))
+            {
+                LibraryGUIEntry previousUnderCursorElem;
+                if (entryLookup.TryGetValue(path, out previousUnderCursorElem))
+                    previousUnderCursorElem.MarkAsSelected(selected);
+            }
+        }
+
+        public LibraryGUIEntry FindElementAt(Vector2I scrollPos)
+        {
+            foreach (var element in entries)
+            {
+                if (element.bounds.Contains(scrollPos))
+                    return element;
+            }
+
+            return null;
+        }
+
+        public LibraryGUIEntry[] FindElementsOverlapping(Rect2I scrollBounds)
+        {
+            List<LibraryGUIEntry> elements = new List<LibraryGUIEntry>();
+            foreach (var element in entries)
+            {
+                if (element.Bounds.Overlaps(scrollBounds))
+                    elements.Add(element);
+            }
+
+            return elements.ToArray();
+        }
+
+        public bool TryGetEntry(string path, out LibraryGUIEntry entry)
+        {
+            return entryLookup.TryGetValue(path, out entry);
+        }
+    }
+}

+ 273 - 0
MBansheeEditor/Library/LibraryGUIEntry.cs

@@ -0,0 +1,273 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    internal class LibraryGUIEntry
+    {
+        private const int MAX_LABEL_HEIGHT = 50;
+
+        private static readonly Color PING_COLOR = Color.BansheeOrange;
+        private static readonly Color SELECTION_COLOR = Color.DarkCyan;
+        private static readonly Color HOVER_COLOR = new Color(Color.DarkCyan.r, Color.DarkCyan.g, Color.DarkCyan.b, 0.5f);
+        private static readonly Color CUT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
+        private const int SELECTION_EXTRA_WIDTH = 3;
+
+        // Note: Order of these is relevant
+        enum UnderlayState
+        {
+            None, Hovered, Selected, Pinged
+        }
+
+        public int index;
+        public string path;
+        public GUITexture icon;
+        public GUILabel label;
+        public Rect2I bounds;
+
+        private GUITexture underlay;
+        private LibraryGUIContent owner;
+        private UnderlayState underlayState;
+        private GUITextBox renameTextBox;
+
+        public LibraryGUIEntry(LibraryGUIContent owner, GUILayout parent, LibraryEntry entry, int index)
+        {
+            GUILayout entryLayout;
+
+            if (owner.GridLayout)
+                entryLayout = parent.AddLayoutY();
+            else
+                entryLayout = parent.AddLayoutX();
+
+            SpriteTexture iconTexture = GetIcon(entry);
+
+            icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit,
+                true, GUIOption.FixedHeight(owner.TileSize), GUIOption.FixedWidth(owner.TileSize));
+
+            label = null;
+
+            if (owner.GridLayout)
+            {
+                label = new GUILabel(entry.Name, EditorStyles.MultiLineLabelCentered,
+                    GUIOption.FixedWidth(owner.LabelWidth), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
+            }
+            else
+            {
+                label = new GUILabel(entry.Name);
+            }
+
+            entryLayout.AddElement(icon);
+            entryLayout.AddElement(label);
+
+            this.owner = owner;
+            this.index = index;
+            this.path = entry.Path;
+            this.bounds = new Rect2I();
+            this.underlay = null;
+        }
+
+        public void Initialize()
+        {
+            bounds = icon.Bounds;
+            Rect2I labelBounds = label.Bounds;
+
+            bounds.x = MathEx.Min(bounds.x, labelBounds.x - SELECTION_EXTRA_WIDTH);
+            bounds.y = MathEx.Min(bounds.y, labelBounds.y) - 5; // 5 - Just padding for better look
+            bounds.width = MathEx.Max(bounds.x + bounds.width,
+                labelBounds.x + labelBounds.width) - bounds.x + SELECTION_EXTRA_WIDTH;
+            bounds.height = MathEx.Max(bounds.y + bounds.height,
+                labelBounds.y + labelBounds.height) - bounds.y;
+
+            string hoistedPath = path;
+
+            GUIButton overlayBtn = new GUIButton("", EditorStyles.Blank);
+            overlayBtn.Bounds = bounds;
+            overlayBtn.OnClick += () => OnEntryClicked(hoistedPath);
+            overlayBtn.OnDoubleClick += () => OnEntryDoubleClicked(hoistedPath);
+            overlayBtn.SetContextMenu(owner.Window.ContextMenu);
+
+            owner.Overlay.AddElement(overlayBtn);
+        }
+
+        public Rect2I Bounds
+        {
+            get { return bounds; }
+        }
+
+        public void MarkAsCut(bool enable)
+        {
+            if (enable)
+                icon.SetTint(CUT_COLOR);
+            else
+                icon.SetTint(Color.White);
+        }
+
+        public void MarkAsSelected(bool enable)
+        {
+            if ((int)underlayState > (int)UnderlayState.Selected)
+                return;
+
+            if (enable)
+            {
+                CreateUnderlay();
+                underlay.SetTint(SELECTION_COLOR);
+            }
+            else
+                ClearUnderlay();
+
+            underlayState = UnderlayState.Selected;
+        }
+
+        public void MarkAsPinged(bool enable)
+        {
+            if ((int)underlayState > (int)UnderlayState.Pinged)
+                return;
+
+            if (enable)
+            {
+                CreateUnderlay();
+                underlay.SetTint(PING_COLOR);
+            }
+            else
+                ClearUnderlay();
+
+            underlayState = UnderlayState.Pinged;
+        }
+
+        public void MarkAsHovered(bool enable)
+        {
+            if ((int)underlayState > (int)UnderlayState.Hovered)
+                return;
+
+            if (enable)
+            {
+                CreateUnderlay();
+                underlay.SetTint(HOVER_COLOR);
+            }
+            else
+                ClearUnderlay();
+
+            underlayState = UnderlayState.Hovered;
+        }
+
+        public void StartRename()
+        {
+            if (renameTextBox != null)
+                return;
+
+            renameTextBox = new GUITextBox(false);
+            renameTextBox.Bounds = label.Bounds;
+            owner.InputOverlay.AddElement(renameTextBox);
+
+            string name = Path.GetFileNameWithoutExtension(PathEx.GetTail(path));
+            renameTextBox.Text = name;
+            renameTextBox.Focus = true;
+
+            label.Visible = false;
+        }
+
+        public void StopRename()
+        {
+            if (renameTextBox != null)
+            {
+                renameTextBox.Destroy();
+                renameTextBox = null;
+            }
+
+            label.Visible = true;
+        }
+
+        public string GetRenamedName()
+        {
+            if (renameTextBox != null)
+                return renameTextBox.Text;
+
+            return "";
+        }
+
+        private void ClearUnderlay()
+        {
+            if (underlay != null)
+            {
+                underlay.Destroy();
+                underlay = null;
+            }
+
+            underlayState = UnderlayState.None;
+        }
+
+        private void CreateUnderlay()
+        {
+            if (underlay == null)
+            {
+                underlay = new GUITexture(Builtin.WhiteTexture);
+                underlay.Bounds = Bounds;
+
+                owner.Underlay.AddElement(underlay);
+            }
+        }
+
+        private void OnEntryClicked(string path)
+        {
+            owner.Window.Select(path);
+        }
+
+        private void OnEntryDoubleClicked(string path)
+        {
+            LibraryEntry entry = ProjectLibrary.GetEntry(path);
+            if (entry != null)
+            {
+                if (entry.Type == LibraryEntryType.Directory)
+                    owner.Window.EnterDirectory(path);
+                else
+                {
+                    FileEntry resEntry = (FileEntry)entry;
+                    if (resEntry.ResType == ResourceType.Prefab)
+                    {
+                        EditorApplication.LoadScene(resEntry.Path);
+                    }
+                }
+            }
+        }
+
+        private static SpriteTexture GetIcon(LibraryEntry entry)
+        {
+            if (entry.Type == LibraryEntryType.Directory)
+            {
+                return EditorBuiltin.FolderIcon;
+            }
+            else
+            {
+                FileEntry fileEntry = (FileEntry)entry;
+                switch (fileEntry.ResType)
+                {
+                    case ResourceType.Font:
+                        return EditorBuiltin.FontIcon;
+                    case ResourceType.Mesh:
+                        return EditorBuiltin.MeshIcon;
+                    case ResourceType.Texture:
+                        return EditorBuiltin.TextureIcon;
+                    case ResourceType.PlainText:
+                        return EditorBuiltin.PlainTextIcon;
+                    case ResourceType.ScriptCode:
+                        return EditorBuiltin.ScriptCodeIcon;
+                    case ResourceType.SpriteTexture:
+                        return EditorBuiltin.SpriteTextureIcon;
+                    case ResourceType.Shader:
+                        return EditorBuiltin.ShaderIcon;
+                    case ResourceType.Material:
+                        return EditorBuiltin.MaterialIcon;
+                    case ResourceType.Prefab:
+                        return EditorBuiltin.PrefabIcon;
+                }
+            }
+
+            return null;
+        }
+    }
+}

+ 0 - 0
MBansheeEditor/LibraryMenu.cs → MBansheeEditor/Library/LibraryMenu.cs


+ 0 - 0
MBansheeEditor/LibraryUtility.cs → MBansheeEditor/Library/LibraryUtility.cs


File diff suppressed because it is too large
+ 223 - 524
MBansheeEditor/Library/LibraryWindow.cs


+ 7 - 4
MBansheeEditor/MBansheeEditor.csproj

@@ -55,12 +55,15 @@
     <Compile Include="HierarchyWindow.cs" />
     <Compile Include="HierarchyWindow.cs" />
     <Compile Include="Inspectors\CameraInspector.cs" />
     <Compile Include="Inspectors\CameraInspector.cs" />
     <Compile Include="Inspector\InspectorUtility.cs" />
     <Compile Include="Inspector\InspectorUtility.cs" />
-    <Compile Include="LibraryMenu.cs" />
-    <Compile Include="LibraryUtility.cs" />
+    <Compile Include="Library\LibraryGUIContent.cs" />
+    <Compile Include="Library\LibraryDropDown.cs" />
+    <Compile Include="Library\LibraryGUIEntry.cs" />
+    <Compile Include="Library\LibraryMenu.cs" />
+    <Compile Include="Library\LibraryUtility.cs" />
     <Compile Include="LocEdString.cs" />
     <Compile Include="LocEdString.cs" />
     <Compile Include="MenuItems.cs" />
     <Compile Include="MenuItems.cs" />
     <Compile Include="PrefabUtility.cs" />
     <Compile Include="PrefabUtility.cs" />
-    <Compile Include="LibraryDropTarget.cs" />
+    <Compile Include="Library\LibraryDropTarget.cs" />
     <Compile Include="OSDropTarget.cs" />
     <Compile Include="OSDropTarget.cs" />
     <Compile Include="EditorApplication.cs" />
     <Compile Include="EditorApplication.cs" />
     <Compile Include="EditorBuiltin.cs" />
     <Compile Include="EditorBuiltin.cs" />
@@ -102,7 +105,7 @@
     <Compile Include="ModalWindow.cs" />
     <Compile Include="ModalWindow.cs" />
     <Compile Include="ProgressBar.cs" />
     <Compile Include="ProgressBar.cs" />
     <Compile Include="ProjectSettings.cs" />
     <Compile Include="ProjectSettings.cs" />
-    <Compile Include="LibraryWindow.cs" />
+    <Compile Include="Library\LibraryWindow.cs" />
     <Compile Include="ProjectWindow.cs" />
     <Compile Include="ProjectWindow.cs" />
     <Compile Include="Scene\SceneCamera.cs" />
     <Compile Include="Scene\SceneCamera.cs" />
     <Compile Include="Scene\SceneViewHandler.cs" />
     <Compile Include="Scene\SceneViewHandler.cs" />

+ 1 - 0
TODO.txt

@@ -53,6 +53,7 @@ Code quality improvements:
 Polish
 Polish
 
 
 Ribek use:
 Ribek use:
+ - Document Library stuff (I moved it to a different folder so I might miss it)
  - Hook up color picker to guicolor field
  - Hook up color picker to guicolor field
  - When hiding a component in inspector, it doesn't immediately reposition the components below it
  - When hiding a component in inspector, it doesn't immediately reposition the components below it
  - Having en empty component in inspector shows a small empty background, it shouldn't show anything
  - Having en empty component in inspector shows a small empty background, it shouldn't show anything

Some files were not shown because too many files changed in this diff