Forráskód Böngészése

Moved relevant animation editor code in GUICurveEditor, and refactored everything so its designed more nicely
Fixed context menu placement within non-root GUIPanels
Added missing method to C# GUIElement

BearishSun 9 éve
szülő
commit
57bec2f508

+ 1 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -50,6 +50,7 @@
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
     <Compile Include="Windows\Animation\GUICurveDrawing.cs" />
+    <Compile Include="Windows\Animation\GUICurveEditor.cs" />
     <Compile Include="Windows\Animation\GUIFieldSelector.cs" />
     <Compile Include="Windows\Animation\GUIGraphValues.cs" />
     <Compile Include="Windows\Animation\GUIGraphTicks.cs" />

+ 0 - 2
Source/MBansheeEditor/Tests/UnitTests.cs

@@ -2,10 +2,8 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Text;
-using System.Threading.Tasks;
 using BansheeEngine;
 using DebugUnit = System.Diagnostics.Debug;
 

+ 328 - 0
Source/MBansheeEditor/Windows/Animation/GUICurveEditor.cs

@@ -0,0 +1,328 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup Windows
+     *  @{
+     */
+
+    internal class GUICurveEditor
+    {
+        public struct KeyframeRef
+        {
+            public KeyframeRef(int curveIdx, int keyIdx)
+            {
+                this.curveIdx = curveIdx;
+                this.keyIdx = keyIdx;
+            }
+
+            public int curveIdx;
+            public int keyIdx;
+        }
+
+        private const int TIMELINE_HEIGHT = 20;
+        private const int SIDEBAR_WIDTH = 30;
+
+        private GUILayout gui;
+        private GUIPanel drawingPanel;
+        private GUIGraphTime guiTimeline;
+        private GUICurveDrawing guiCurveDrawing;
+        private GUIGraphValues guiSidebar;
+
+        private ContextMenu blankContextMenu;
+        private ContextMenu keyframeContextMenu;
+        private Vector2I contextClickPosition;
+
+        private EdAnimationCurve[] curves = new EdAnimationCurve[0];
+
+        private int markedFrameIdx;
+        private List<KeyframeRef> selectedKeyframes = new List<KeyframeRef>();
+
+        private bool isMousePressedOverKey;
+        private KeyFrame[] draggedKeyframes;
+        private Vector2 dragStart;
+
+        public GUICurveEditor(GUILayout gui, int width, int height)
+        {
+            this.gui = gui;
+
+            blankContextMenu = new ContextMenu();
+            blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);
+            blankContextMenu.AddItem("Add event", AddEventAtPosition);
+
+            keyframeContextMenu = new ContextMenu();
+            keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
+            keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
+            keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
+            keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
+            keyframeContextMenu.AddItem("Tangents/In/Free", () => { ChangeSelectionTangentMode(TangentMode.InFree); });
+            keyframeContextMenu.AddItem("Tangents/In/Linear", () => { ChangeSelectionTangentMode(TangentMode.InLinear); });
+            keyframeContextMenu.AddItem("Tangents/In/Step", () => { ChangeSelectionTangentMode(TangentMode.InStep); });
+            keyframeContextMenu.AddItem("Tangents/Out/Auto", () => { ChangeSelectionTangentMode(TangentMode.OutAuto); });
+            keyframeContextMenu.AddItem("Tangents/Out/Free", () => { ChangeSelectionTangentMode(TangentMode.OutFree); });
+            keyframeContextMenu.AddItem("Tangents/Out/Linear", () => { ChangeSelectionTangentMode(TangentMode.OutLinear); });
+            keyframeContextMenu.AddItem("Tangents/Out/Step", () => { ChangeSelectionTangentMode(TangentMode.OutStep); });
+
+            guiTimeline = new GUIGraphTime(gui, width, TIMELINE_HEIGHT);
+
+            drawingPanel = gui.AddPanel();
+            drawingPanel.SetPosition(0, TIMELINE_HEIGHT);
+
+            guiCurveDrawing = new GUICurveDrawing(drawingPanel, width, height - TIMELINE_HEIGHT, curves);
+            guiCurveDrawing.SetRange(60.0f, 20.0f);
+
+            GUIPanel sidebarPanel = gui.AddPanel(-10);
+            sidebarPanel.SetPosition(0, TIMELINE_HEIGHT);
+
+            guiSidebar = new GUIGraphValues(sidebarPanel, SIDEBAR_WIDTH, height - TIMELINE_HEIGHT);
+            guiSidebar.SetRange(-10.0f, 10.0f);
+        }
+
+        /// <summary>
+        /// Change the set of curves to display.
+        /// </summary>
+        /// <param name="curves">New set of curves to draw on the GUI element.</param>
+        public void SetCurves(EdAnimationCurve[] curves)
+        {
+            this.curves = curves;
+            guiCurveDrawing.SetCurves(curves);
+
+            // TODO - Recalculate valid size
+
+            Redraw();
+        }
+
+        /// <summary>
+        /// Change the physical size of the GUI element.
+        /// </summary>
+        /// <param name="width">Width of the element in pixels.</param>
+        /// <param name="height">Height of the element in pixels.</param>
+        public void SetSize(int width, int height)
+        {
+            guiTimeline.SetSize(width, TIMELINE_HEIGHT);
+            guiCurveDrawing.SetSize(width, height - TIMELINE_HEIGHT);
+            guiSidebar.SetSize(SIDEBAR_WIDTH, height - TIMELINE_HEIGHT);
+
+            Redraw();
+        }
+
+        /// <summary>
+        /// Changes the visible range that the GUI element displays.
+        /// </summary>
+        /// <param name="xRange">Range of the horizontal area. Displayed area will range from [0, xRange].</param>
+        /// <param name="yRange">Range of the vertical area. Displayed area will range from 
+        ///                      [-yRange * 0.5, yRange * 0.5]</param>
+        public void SetRange(float xRange, float yRange)
+        {
+            guiTimeline.SetRange(xRange);
+            guiCurveDrawing.SetRange(xRange, yRange);
+            guiSidebar.SetRange(yRange * -0.5f, yRange * 0.5f);
+
+            Redraw();
+        }
+
+        /// <summary>
+        /// Number of frames per second, used for frame selection and marking.
+        /// </summary>
+        /// <param name="fps">Number of prames per second.</param>
+        public void SetFPS(int fps)
+        {
+            guiTimeline.SetFPS(fps);
+            guiCurveDrawing.SetFPS(fps);
+
+            Redraw();
+        }
+
+        /// <summary>
+        /// Sets the frame at which to display the frame marker.
+        /// </summary>
+        /// <param name="frameIdx">Index of the frame to display the marker on, or -1 to clear the marker.</param>
+        public void SetMarkedFrame(int frameIdx)
+        {
+            markedFrameIdx = frameIdx;
+
+            guiTimeline.SetMarkedFrame(frameIdx);
+            guiCurveDrawing.SetMarkedFrame(frameIdx);
+
+            Redraw();
+        }
+
+        public void AddKeyFrameAtMarker()
+        {
+            ClearSelection();
+
+            foreach (var curve in curves)
+            {
+                float t = guiCurveDrawing.GetTimeForFrame(markedFrameIdx);
+                float value = curve.Native.Evaluate(t);
+
+                curve.AddKeyframe(t, value);
+            }
+
+            // TODO - UNDOREDO
+
+            guiCurveDrawing.Rebuild();
+        }
+
+        public void Redraw()
+        {
+            guiCurveDrawing.Rebuild();
+            guiTimeline.Rebuild();
+            guiSidebar.Rebuild();
+        }
+
+        public void HandleInput(Vector2I pointerPos)
+        {
+            Rect2I drawingBounds = drawingPanel.Bounds;
+            Vector2I drawingPos = pointerPos - new Vector2I(drawingBounds.x, drawingBounds.y);
+
+            if (Input.IsPointerButtonDown(PointerButton.Left))
+            {
+                Vector2 curveCoord;
+                int curveIdx;
+                int keyIdx;
+                if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
+                {
+                    if (keyIdx == -1)
+                        ClearSelection();
+                    else
+                    {
+                        if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
+                            ClearSelection();
+
+                        SelectKeyframe(curveIdx, keyIdx);
+
+                        isMousePressedOverKey = true;
+                        dragStart = curveCoord;
+                    }
+
+                    guiCurveDrawing.Rebuild();
+                }
+            }
+
+            if (Input.IsPointerButtonHeld(PointerButton.Left))
+            {
+                if (isMousePressedOverKey)
+                {
+                    // TODO - Check if pointer moves some minimal amount
+                    // - If so, start drag. Record all current positions
+                    // - Calculate offset in curve space and apply to all keyframes
+                }
+                else
+                {
+                    int frameIdx = guiTimeline.GetFrame(pointerPos);
+
+                    if (frameIdx != -1)
+                        SetMarkedFrame(frameIdx);
+                }
+            }
+
+            if (Input.IsPointerButtonUp(PointerButton.Left))
+            {
+                isMousePressedOverKey = false;
+            }
+
+            if (Input.IsPointerButtonDown(PointerButton.Right))
+            {
+                Vector2 curveCoord;
+                int curveIdx;
+                int keyIdx;
+                if (guiCurveDrawing.GetCoordInfo(drawingPos, out curveCoord, out curveIdx, out keyIdx))
+                {
+                    contextClickPosition = pointerPos;
+
+                    if (keyIdx == -1)
+                    {
+                        ClearSelection();
+
+                        blankContextMenu.Open(contextClickPosition, gui);
+                    }
+                    else
+                    {
+                        // If clicked outside of current selection, just select the one keyframe
+                        if (!IsSelected(curveIdx, keyIdx))
+                        {
+                            ClearSelection();
+                            SelectKeyframe(curveIdx, keyIdx);
+
+                            guiCurveDrawing.Rebuild();
+                        }
+
+                        keyframeContextMenu.Open(contextClickPosition, gui);
+                    }
+                }
+            }
+
+            if (Input.IsButtonUp(ButtonCode.Delete))
+                DeleteSelectedKeyframes();
+        }
+
+        private void ChangeSelectionTangentMode(TangentMode mode)
+        {
+            // TODO - When changing just left or right, make sure to persist opposite tangent mode (or switch to equivalent broken mode if it wasn't broken)
+        }
+
+        private void AddKeyframeAtPosition()
+        {
+            // TODO
+        }
+
+        private void AddEventAtPosition()
+        {
+            // TODO
+        }
+
+        private void DeleteSelectedKeyframes()
+        {
+            // Sort keys from highest to lowest so they can be removed without changing the indices of the keys
+            // after them
+            selectedKeyframes.Sort((x, y) =>
+            {
+                if (x.curveIdx.Equals(y.curveIdx))
+                    return y.keyIdx.CompareTo(x.keyIdx);
+
+                return x.curveIdx.CompareTo(y.curveIdx);
+            });
+
+            foreach (var keyframe in selectedKeyframes)
+                curves[keyframe.curveIdx].RemoveKeyframe(keyframe.keyIdx);
+
+            // TODO - UNDOREDO
+
+            ClearSelection();
+
+            guiCurveDrawing.Rebuild();
+        }
+
+        private void ClearSelection()
+        {
+            guiCurveDrawing.ClearSelectedKeyframes();
+            selectedKeyframes.Clear();
+            isMousePressedOverKey = false;
+        }
+
+        private void SelectKeyframe(int curveIdx, int keyIdx)
+        {
+            guiCurveDrawing.SelectKeyframe(curveIdx, keyIdx, true);
+
+            if (!IsSelected(curveIdx, keyIdx))
+                selectedKeyframes.Add(new GUICurveEditor.KeyframeRef(curveIdx, keyIdx));
+        }
+
+        private bool IsSelected(int curveIdx, int keyIdx)
+        {
+            int existingIdx = selectedKeyframes.FindIndex(x =>
+            {
+                return x.curveIdx == curveIdx && x.keyIdx == keyIdx;
+            });
+
+            return (existingIdx != -1);
+        }
+    }
+
+    /** @} */
+}

+ 1 - 9
Source/MBansheeEditor/Windows/Animation/GUIGraphTime.cs

@@ -77,8 +77,6 @@ namespace BansheeEditor
         public void SetMarkedFrame(int frameIdx)
         {
             markedFrameIdx = frameIdx;
-
-            Rebuild();
         }
 
         /// <summary>
@@ -98,8 +96,6 @@ namespace BansheeEditor
             drawableWidth = Math.Max(0, width - PADDING * 2);
 
             tickHandler.SetRange(0.0f, GetRange(true), drawableWidth + PADDING);
-
-            Rebuild();
         }
 
         /// <summary>
@@ -111,8 +107,6 @@ namespace BansheeEditor
             rangeLength = Math.Max(0.0f, length);
 
             tickHandler.SetRange(0.0f, GetRange(true), drawableWidth + PADDING);
-
-            Rebuild();
         }
 
         /// <summary>
@@ -124,8 +118,6 @@ namespace BansheeEditor
             this.fps = Math.Max(1, fps);
 
             tickHandler.SetRange(0.0f, GetRange(true), drawableWidth + PADDING);
-
-            Rebuild();
         }
         
         /// <summary>
@@ -217,7 +209,7 @@ namespace BansheeEditor
         /// <summary>
         /// Rebuilds the internal GUI elements. Should be called whenever timeline properties change.
         /// </summary>
-        private void Rebuild()
+        public void Rebuild()
         {
             canvas.Clear();
 

+ 1 - 5
Source/MBansheeEditor/Windows/Animation/GUIGraphValues.cs

@@ -56,8 +56,6 @@ namespace BansheeEditor
             canvas.SetHeight(height);
 
             tickHandler.SetRange(rangeStart, rangeEnd, height);
-
-            Rebuild();
         }
 
         /// <summary>
@@ -78,8 +76,6 @@ namespace BansheeEditor
             rangeEnd = end;
 
             tickHandler.SetRange(rangeStart, rangeEnd, height);
-
-            Rebuild();
         }
 
         /// <summary>
@@ -119,7 +115,7 @@ namespace BansheeEditor
         /// <summary>
         /// Rebuilds the internal GUI elements. Should be called whenever timeline properties change.
         /// </summary>
-        private void Rebuild()
+        public void Rebuild()
         {
             canvas.Clear();
 

+ 19 - 241
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -1,6 +1,6 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-
+using System;
 using System.Collections.Generic;
 using BansheeEngine;
 
@@ -15,41 +15,16 @@ namespace BansheeEditor
     /// </summary>
     internal class AnimationWindow : EditorWindow
     {
-        public struct KeyframeRef
-        {
-            public KeyframeRef(int curveIdx, int keyIdx)
-            {
-                this.curveIdx = curveIdx;
-                this.keyIdx = keyIdx;
-            }
-
-            public int curveIdx;
-            public int keyIdx;
-        }
-
-        private GUIGraphTime timeline;
-        private GUICurveDrawing curveDrawing;
-        private GUIGraphValues sidebar;
-
         private GUIFloatField lengthField;
         private GUIIntField fpsField;
         private GUIFloatField yRangeField;
         private GUIButton addKeyframeBtn;
 
         private GUILayout buttonLayout;
+        private int buttonLayoutHeight;
 
-        private EdAnimationCurve[] curves = new EdAnimationCurve[0];
-        private int markedFrameIdx;
-        private List<KeyframeRef> selectedKeyframes = new List<KeyframeRef>();
-
-        private ContextMenu blankContextMenu;
-        private ContextMenu keyframeContextMenu;
-        private Vector2I contextClickPosition;
-
-        // Keyframe drag
-        private bool isMousePressedOverKey;
-        private KeyFrame[] draggedKeyframes;
-        private Vector2 dragStart;
+        private GUIPanel editorPanel;
+        private GUICurveEditor guiCurveEditor;
 
         /// <summary>
         /// Opens the animation window.
@@ -79,22 +54,19 @@ namespace BansheeEditor
 
             lengthField.OnChanged += x =>
             {
-                timeline.SetRange(lengthField.Value);
-                curveDrawing.SetRange(lengthField.Value, yRangeField.Value);
+                guiCurveEditor.SetRange(lengthField.Value, yRangeField.Value);
             };
             fpsField.OnChanged += x =>
             {
-                timeline.SetFPS(x);
-                curveDrawing.SetFPS(x);
+                guiCurveEditor.SetFPS(x);
             };
             yRangeField.OnChanged += x =>
             {
-                curveDrawing.SetRange(lengthField.Value, x);
-                sidebar.SetRange(x * -0.5f, x * 0.5f);
+                guiCurveEditor.SetRange(lengthField.Value, yRangeField.Value);
             };
             addKeyframeBtn.OnClick += () =>
             {
-                AddKeyframeAtMarker();
+                guiCurveEditor.AddKeyFrameAtMarker();
             };
 
             GUILayout mainLayout = GUI.AddLayoutY();
@@ -110,94 +82,12 @@ namespace BansheeEditor
             buttonLayout.AddElement(addKeyframeBtn);
             buttonLayout.AddSpace(5);
 
-            timeline = new GUIGraphTime(mainLayout, Width, 20);
-
-            curves = CreateDummyCurves();
-            curveDrawing = new GUICurveDrawing(mainLayout, Width, Height - 20, curves);
-            curveDrawing.SetRange(60.0f, 20.0f);
-
-            GUIPanel sidebarPanel = GUI.AddPanel(-10);
-            sidebarPanel.SetPosition(0, 20 + buttonLayout.Bounds.height);
-
-            sidebar = new GUIGraphValues(sidebarPanel, 30, Height - 20 - buttonLayout.Bounds.height);
-            sidebar.SetRange(-10.0f, 10.0f);
-
-            curveDrawing.SetSize(Width, Height - 20 - buttonLayout.Bounds.height);
-            curveDrawing.Rebuild();
-
-            blankContextMenu = new ContextMenu();
-            blankContextMenu.AddItem("Add keyframe", AddKeyframeAtPosition);
-            blankContextMenu.AddItem("Add event", AddEventAtPosition);
-
-            keyframeContextMenu = new ContextMenu();
-            keyframeContextMenu.AddItem("Delete", DeleteSelectedKeyframes);
-            keyframeContextMenu.AddItem("Tangents/Auto", () => { ChangeSelectionTangentMode(TangentMode.Auto); });
-            keyframeContextMenu.AddItem("Tangents/Free", () => { ChangeSelectionTangentMode(TangentMode.Free); });
-            keyframeContextMenu.AddItem("Tangents/In/Auto", () => { ChangeSelectionTangentMode(TangentMode.InAuto); });
-            keyframeContextMenu.AddItem("Tangents/In/Free", () => { ChangeSelectionTangentMode(TangentMode.InFree); });
-            keyframeContextMenu.AddItem("Tangents/In/Linear", () => { ChangeSelectionTangentMode(TangentMode.InLinear); });
-            keyframeContextMenu.AddItem("Tangents/In/Step", () => { ChangeSelectionTangentMode(TangentMode.InStep); });
-            keyframeContextMenu.AddItem("Tangents/Out/Auto", () => { ChangeSelectionTangentMode(TangentMode.OutAuto); });
-            keyframeContextMenu.AddItem("Tangents/Out/Free", () => { ChangeSelectionTangentMode(TangentMode.OutFree); });
-            keyframeContextMenu.AddItem("Tangents/Out/Linear", () => { ChangeSelectionTangentMode(TangentMode.OutLinear); });
-            keyframeContextMenu.AddItem("Tangents/Out/Step", () => { ChangeSelectionTangentMode(TangentMode.OutStep); });
-
-            // TODO - Calculate min/max Y and range to set as default
-            //  - Also recalculate whenever curves change and increase as needed
-        }
-
-        private void ChangeSelectionTangentMode(TangentMode mode)
-        {
-            // TODO - When changing just left or right, make sure to persist opposite tangent mode (or switch to equivalent broken mode if it wasn't broken)
-        }
-
-        private void AddKeyframeAtPosition()
-        {
-            // TODO
-        }
-
-        private void AddEventAtPosition()
-        {
-            // TODO
-        }
-
-        private void AddKeyframeAtMarker()
-        {
-            ClearSelection();
-
-            foreach (var curve in curves)
-            {
-                float t = curveDrawing.GetTimeForFrame(markedFrameIdx);
-                float value = curve.Native.Evaluate(t);
-
-                curve.AddKeyframe(t, value);
-            }
-
-            // TODO - UNDOREDO
+            editorPanel = mainLayout.AddPanel();
+            buttonLayoutHeight = lengthField.Bounds.height;
 
-            curveDrawing.Rebuild();
-        }
-
-        private void DeleteSelectedKeyframes()
-        {
-            // Sort keys from highest to lowest so they can be removed without changing the indices of the keys
-            // after them
-            selectedKeyframes.Sort((x, y) =>
-            {
-                if (x.curveIdx.Equals(y.curveIdx))
-                    return y.keyIdx.CompareTo(x.keyIdx);
-
-                return x.curveIdx.CompareTo(y.curveIdx);
-            });
-
-            foreach (var keyframe in selectedKeyframes)
-                curves[keyframe.curveIdx].RemoveKeyframe(keyframe.keyIdx);
-
-            // TODO - UNDOREDO
-
-            ClearSelection();
-
-            curveDrawing.Rebuild();
+            guiCurveEditor = new GUICurveEditor(editorPanel, Width, Height - buttonLayoutHeight);
+            guiCurveEditor.SetCurves(CreateDummyCurves());
+            guiCurveEditor.Redraw();
         }
 
         private EdAnimationCurve[] CreateDummyCurves()
@@ -215,129 +105,17 @@ namespace BansheeEditor
 
         protected override void WindowResized(int width, int height)
         {
-            timeline.SetSize(width, 20);
-            curveDrawing.SetSize(width, height - 20 - buttonLayout.Bounds.height);
-            sidebar.SetSize(30, height - 20 - buttonLayout.Bounds.height);
-
-            curveDrawing.Rebuild();
-        }
-
-        private void ClearSelection()
-        {
-            curveDrawing.ClearSelectedKeyframes();
-            selectedKeyframes.Clear();
-            isMousePressedOverKey = false;
-        }
-
-        private void SelectKeyframe(int curveIdx, int keyIdx)
-        {
-            curveDrawing.SelectKeyframe(curveIdx, keyIdx, true);
-
-            if (!IsSelected(curveIdx, keyIdx))
-                selectedKeyframes.Add(new KeyframeRef(curveIdx, keyIdx));
-        }
-
-        private bool IsSelected(int curveIdx, int keyIdx)
-        {
-            int existingIdx = selectedKeyframes.FindIndex(x =>
-            {
-                return x.curveIdx == curveIdx && x.keyIdx == keyIdx;
-            });
-
-            return (existingIdx != -1);
+            guiCurveEditor.SetSize(width, height - buttonLayoutHeight);
+            guiCurveEditor.Redraw();
         }
 
         private void OnEditorUpdate()
         {
-            if (Input.IsPointerButtonDown(PointerButton.Left))
-            {
-                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
-
-                Vector2 curveCoord;
-                int curveIdx;
-                int keyIdx;
-                if (curveDrawing.GetCoordInfo(windowPos, out curveCoord, out curveIdx, out keyIdx))
-                {
-                    if (keyIdx == -1)
-                        ClearSelection();
-                    else
-                    {
-                        if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
-                            ClearSelection();
-
-                        SelectKeyframe(curveIdx, keyIdx);
-
-                        isMousePressedOverKey = true;
-                        dragStart = curveCoord;
-                    }
-
-                    curveDrawing.Rebuild();
-                }
-            }
-            else if (Input.IsPointerButtonHeld(PointerButton.Left))
-            {
-                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
-
-                if (isMousePressedOverKey)
-                {
-                    // TODO - Check if pointer moves some minimal amount
-                    // - If so, start drag. Record all current positions
-                    // - Calculate offset in curve space and apply to all keyframes
-                }
-                else
-                {
-                    int frameIdx = timeline.GetFrame(windowPos);
-
-                    if (frameIdx != -1)
-                    {
-                        timeline.SetMarkedFrame(frameIdx);
-                        curveDrawing.SetMarkedFrame(frameIdx);
-
-                        markedFrameIdx = frameIdx;
-                        curveDrawing.Rebuild();
-                    }
-                }
-            }
-            else if (Input.IsPointerButtonUp(PointerButton.Left))
-            {
-                isMousePressedOverKey = false;
-            }
-
-            if (Input.IsPointerButtonDown(PointerButton.Right))
-            {
-                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
-
-                Vector2 curveCoord;
-                int curveIdx;
-                int keyIdx;
-                if (curveDrawing.GetCoordInfo(windowPos, out curveCoord, out curveIdx, out keyIdx))
-                {
-                    contextClickPosition = windowPos;
-
-                    if (keyIdx == -1)
-                    {
-                        ClearSelection();
-
-                        blankContextMenu.Open(contextClickPosition, GUI);
-                    }
-                    else
-                    {
-                        // If clicked outside of current selection, just select the one keyframe
-                        if (!IsSelected(curveIdx, keyIdx))
-                        {
-                            ClearSelection();
-                            SelectKeyframe(curveIdx, keyIdx);
-
-                            curveDrawing.Rebuild();
-                        }
-
-                        keyframeContextMenu.Open(contextClickPosition, GUI);
-                    }
-                }
-            }
+            Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
+            Rect2I curveEditorBounds = editorPanel.Bounds;
 
-            if(Input.IsButtonUp(ButtonCode.Delete))
-                DeleteSelectedKeyframes();
+            Vector2I offset = new Vector2I(curveEditorBounds.x, curveEditorBounds.y);
+            guiCurveEditor.HandleInput(windowPos - offset);
         }
     }
 

+ 1 - 4
Source/MBansheeEngine/GUI/ContextMenu.cs

@@ -41,11 +41,8 @@ namespace BansheeEngine
             if (parent == null)
                 return;
 
-            Rect2I bounds = GUIUtility.CalculateBounds(parent, null);
-            Vector2I windowPosition = position + new Vector2I(bounds.x, bounds.y);
-
             IntPtr parentPtr = parent.GetCachedPtr();
-            Internal_Open(mCachedPtr, ref windowPosition, parentPtr);
+            Internal_Open(mCachedPtr, ref position, parentPtr);
         }
 
         /// <summary>

+ 8 - 2
Source/SBansheeEngine/Source/BsScriptContextMenu.cpp

@@ -10,6 +10,7 @@
 #include "BsGUIContextMenu.h"
 #include "BsScriptHString.h"
 #include "BsScriptGUILayout.h"
+#include "BsGUILayout.h"
 
 using namespace std::placeholders;
 
@@ -41,12 +42,17 @@ namespace BansheeEngine
 
 	void ScriptContextMenu::internal_Open(ScriptContextMenu* instance, Vector2I* position, ScriptGUILayout* layoutPtr)
 	{
-		GUIWidget* widget = layoutPtr->getGUIElement()->_getParentWidget();
+		GUIElementBase* layout = layoutPtr->getGUIElement();
+
+		GUIWidget* widget = layout->_getParentWidget();
 		if (widget == nullptr)
 			return;
 
+		Rect2I bounds = layout->getGlobalBounds();
+		Vector2I windowPosition = *position + Vector2I(bounds.x, bounds.y);
+
 		SPtr<GUIContextMenu> contextMenu = instance->getInternal();
-		contextMenu->open(*position, *widget);
+		contextMenu->open(windowPosition, *widget);
 	}
 
 	void ScriptContextMenu::internal_AddItem(ScriptContextMenu* instance, MonoString* path, UINT32 callbackIdx,

+ 1 - 0
Source/SBansheeEngine/Source/BsScriptGUIElement.cpp

@@ -103,6 +103,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetContextMenu", &ScriptGUIElement::internal_SetContextMenu);
 		metaData.scriptClass->addInternalCall("Internal_GetStyle", &ScriptGUIElement::internal_GetStyle);
 		metaData.scriptClass->addInternalCall("Internal_SetStyle", &ScriptGUIElement::internal_SetStyle);
+		metaData.scriptClass->addInternalCall("Internal_GetParent", &ScriptGUIElement::internal_getParent);
 
 		onFocusGainedThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusGained", 0)->getThunk();
 		onFocusLostThunk = (OnFocusChangedThunkDef)metaData.scriptClass->getMethod("Internal_OnFocusLost", 0)->getThunk();