Jelajahi Sumber

Refactored animation window so it is more clearly organized

BearishSun 9 tahun lalu
induk
melakukan
b7b0606668
1 mengubah file dengan 500 tambahan dan 451 penghapusan
  1. 500 451
      Source/MBansheeEditor/Windows/AnimationWindow.cs

+ 500 - 451
Source/MBansheeEditor/Windows/AnimationWindow.cs

@@ -2,7 +2,6 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 using System;
 using System.Collections.Generic;
-using System.Runtime.InteropServices;
 using BansheeEngine;
 
 namespace BansheeEditor
@@ -17,68 +16,15 @@ namespace BansheeEditor
     [DefaultSize(900, 500)]
     internal class AnimationWindow : EditorWindow
     {
-        /// <summary>
-        /// A set of animation curves for a field of a certain type.
-        /// </summary>
-        private struct FieldCurves
-        {
-            public SerializableProperty.FieldType type;
-            public EdAnimationCurve[] curves;
-        }
-
         private const int FIELD_DISPLAY_WIDTH = 200;
         private const int DRAG_START_DISTANCE = 3;
         private const float DRAG_SCALE = 10.0f;
         private const float ZOOM_SCALE = 15.0f;
 
         private bool isInitialized;
-        private GUIButton playButton;
-        private GUIButton recordButton;
-
-        private GUIButton prevFrameButton;
-        private GUIIntField frameInputField;
-        private GUIButton nextFrameButton;
-
-        private GUIButton addKeyframeButton;
-        private GUIButton addEventButton;
-
-        private GUIButton optionsButton;
-
-        private GUIButton addPropertyBtn;
-        private GUIButton delPropertyBtn;
-
-        private GUILayout buttonLayout;
-
-        private int buttonLayoutHeight;
-        private int scrollBarWidth;
-        private int scrollBarHeight;
-
-        private GUIResizeableScrollBarH horzScrollBar;
-        private GUIResizeableScrollBarV vertScrollBar;
-
-        private GUIPanel editorPanel;
-        private GUIAnimFieldDisplay guiFieldDisplay;
-        private GUICurveEditor guiCurveEditor;
-
         private SceneObject selectedSO;
-        private int currentFrameIdx;
-        private int fps = 1;
-
-        private Vector2I dragStartPos;
-        private bool isButtonHeld;
-        private bool isDragInProgress;
-
-        private Vector2 minimalRange;
-        private Vector2 visibleOffset;
-
-        private Dictionary<string, FieldCurves> curves = new Dictionary<string, FieldCurves>();
-        private List<string> selectedFields = new List<string>();
 
-        internal int FPS
-        {
-            get { return fps; }
-            set { guiCurveEditor.SetFPS(value); fps = MathEx.Max(value, 1); }
-        }
+        #region Overrides
 
         /// <summary>
         /// Opens the animation window.
@@ -103,108 +49,7 @@ namespace BansheeEditor
             EditorInput.OnPointerReleased += OnPointerReleased;
             EditorInput.OnButtonUp += OnButtonUp;
 
-            Rebuild();
-        }
-
-        private Vector2 GetVisibleRange()
-        {
-            float unitsPerXPixel = guiCurveEditor.XRange / guiCurveEditor.Width;
-            float unitsPerYPixel = guiCurveEditor.YRange / guiCurveEditor.Height;
-
-            Vector2I visibleSize = GetCurveEditorSize();
-            return new Vector2(unitsPerXPixel * visibleSize.x, unitsPerYPixel * visibleSize.y);
-        }
-
-        private void SetVisibleOffset(Vector2 offset)
-        {
-            visibleOffset = offset;
-
-            float pixelsPerXUnit = guiCurveEditor.Width / guiCurveEditor.XRange;
-            float pixelsPerYUnit = guiCurveEditor.Height / (guiCurveEditor.YRange * 2.0f);
-
-            int x = (int) (pixelsPerXUnit*visibleOffset.x);
-            int y = (int) (pixelsPerYUnit*visibleOffset.y);
-
-            guiCurveEditor.SetPosition(x, y);
-        }
-
-        // Increases range without zooming in (increasing width/height accordingly)
-        private void SetTotalRange(float x, float y)
-        {
-            float pixelsPerXUnit = guiCurveEditor.Width / guiCurveEditor.XRange;
-            float pixelsPerYUnit = guiCurveEditor.Height / (guiCurveEditor.YRange * 2.0f);
-
-            int width = (int) (pixelsPerXUnit * x);
-            int height = (int) (pixelsPerYUnit * y);
-
-            guiCurveEditor.SetRange(x, y);
-            guiCurveEditor.SetSize(width, height);
-
-            UpdateScrollBarSize();
-        }
-
-        private void UpdateScrollBarSize()
-        {
-            Vector2 visibleRange = GetVisibleRange();
-            Vector2 totalRange = new Vector2(guiCurveEditor.XRange, guiCurveEditor.YRange);
-
-            horzScrollBar.HandleSize = visibleRange.x / totalRange.x;
-            vertScrollBar.HandleSize = visibleRange.y / totalRange.y;
-        }
-
-        private void UpdateScrollBarPosition()
-        {
-            Vector2 visibleRange = GetVisibleRange();
-            Vector2 totalRange = new Vector2(guiCurveEditor.XRange, guiCurveEditor.YRange);
-            Vector2 scrollableRange = totalRange - visibleRange;
-
-            horzScrollBar.Position = visibleOffset.x / scrollableRange.x;
-            vertScrollBar.Position = visibleOffset.y / scrollableRange.y;
-        }
-
-        private void Zoom(Vector2 curvePos, float amount)
-        {
-            Vector2 relativePos = curvePos - visibleOffset;
-            Vector2 visibleRange = GetVisibleRange();
-
-            relativePos.x /= visibleRange.x;
-            relativePos.y /= visibleRange.y;
-
-            relativePos.x = relativePos.x * 2.0f - 1.0f;
-            relativePos.y = relativePos.y * 2.0f - 1.0f;
-
-            Vector2 offset = visibleOffset;
-            offset.x += relativePos.x * amount;
-            offset.y += relativePos.y * amount;
-
-            offset.x = Math.Max(0.0f, offset.x);
-
-            SetVisibleOffset(offset);
-            UpdateScrollBarPosition();
-
-            int width = guiCurveEditor.Width + (int)amount;
-            int height = guiCurveEditor.Height + (int)amount;
-
-            // If we aren't at the minimum size, modify size and offset
-            Vector2I visibleSize = GetCurveEditorSize();
-            if (width > visibleSize.x || height > visibleSize.y)
-            {
-                width = Math.Max(width, visibleSize.x);
-                height = Math.Max(height, visibleSize.y);
-
-                guiCurveEditor.SetSize(width, height);
-                UpdateScrollBarSize();
-            }
-            else // Otherwise start increasing range for zoom in
-            {
-                float unitsPerXPixel = guiCurveEditor.XRange / guiCurveEditor.Width;
-                float unitsPerYPixel = guiCurveEditor.YRange / guiCurveEditor.Height;
-
-                float rangeX = guiCurveEditor.XRange + unitsPerXPixel * amount;
-                float rangeY = guiCurveEditor.YRange + unitsPerYPixel * amount;
-
-                SetTotalRange(rangeX, rangeY);
-            }
+            RebuildGUI();
         }
 
         private void OnEditorUpdate()
@@ -212,67 +57,7 @@ namespace BansheeEditor
             if (!isInitialized)
                 return;
 
-            // Handle middle mouse dragging
-            if (isDragInProgress)
-            {
-                float dragX = Input.GetAxisValue(InputAxis.MouseX) * DRAG_SCALE;
-                float dragY = Input.GetAxisValue(InputAxis.MouseY) * DRAG_SCALE;
-
-                Vector2 totalRange = new Vector2(guiCurveEditor.XRange, guiCurveEditor.YRange);
-                Vector2 visibleRange = GetVisibleRange();
-                Vector2 offset = visibleOffset;
-
-                if (dragX > 0.0f)
-                {
-                    offset.x += dragX;
-
-                    float visibleRight = offset.x + visibleRange.x;
-                    if (visibleRight > totalRange.x)
-                        totalRange.x = visibleRight;
-                }
-                else
-                {
-                    float actualDragX = offset.x - Math.Max(0.0f, offset.x + dragX);
-
-                    offset.x -= actualDragX;
-
-                    float visibleRight = offset.x + visibleRange.x;
-                    totalRange.x = Math.Max(minimalRange.x, visibleRight);
-                }
-
-                if (dragY > 0.0f)
-                {
-                    offset.y += dragY;
-
-                    float visibleTop = offset.y + visibleRange.y;
-                    if (visibleTop > totalRange.y)
-                        totalRange.y = visibleTop;
-                }
-                else
-                {
-                    offset.y += dragY;
-
-                    float visibleYMax = Math.Abs(offset.y) + visibleRange.y;
-                    totalRange.y = Math.Max(minimalRange.y, visibleYMax);
-                }
-
-                SetTotalRange(totalRange.x, totalRange.y);
-                SetVisibleOffset(offset);
-                UpdateScrollBarPosition();
-            }
-
-            // Handle zoom in/out
-            float scroll = Input.GetAxisValue(InputAxis.MouseZ);
-            if (scroll != 0.0f)
-            {
-                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
-                Vector2 curvePos;
-                if (guiCurveEditor.WindowToCurveSpace(windowPos, out curvePos))
-                {
-                    float zoom = scroll*ZOOM_SCALE;
-                    Zoom(curvePos, zoom);
-                }
-            }
+            HandleDragAndZoomInput();
         }
 
         private void OnDestroy()
@@ -289,20 +74,40 @@ namespace BansheeEditor
             if (!isInitialized)
                 return;
 
-            guiFieldDisplay.SetSize(FIELD_DISPLAY_WIDTH, height - buttonLayoutHeight*2);
+            ResizeGUI(width, height);
+        }
+        #endregion
 
-            Vector2I curveEditorSize = GetCurveEditorSize();
-            guiCurveEditor.SetSize(curveEditorSize.x, curveEditorSize.y);
-            guiCurveEditor.Redraw();
+        #region GUI
+        private GUIButton playButton;
+        private GUIButton recordButton;
 
-            horzScrollBar.SetWidth(curveEditorSize.x);
-            vertScrollBar.SetHeight(curveEditorSize.y);
+        private GUIButton prevFrameButton;
+        private GUIIntField frameInputField;
+        private GUIButton nextFrameButton;
 
-            UpdateScrollBarSize();
-            UpdateScrollBarPosition();
-        }
+        private GUIButton addKeyframeButton;
+        private GUIButton addEventButton;
+
+        private GUIButton optionsButton;
+
+        private GUIButton addPropertyBtn;
+        private GUIButton delPropertyBtn;
+
+        private GUILayout buttonLayout;
+
+        private int buttonLayoutHeight;
+        private int scrollBarWidth;
+        private int scrollBarHeight;
+
+        private GUIResizeableScrollBarH horzScrollBar;
+        private GUIResizeableScrollBarV vertScrollBar;
+
+        private GUIPanel editorPanel;
+        private GUIAnimFieldDisplay guiFieldDisplay;
+        private GUICurveEditor guiCurveEditor;
 
-        private void Rebuild()
+        private void RebuildGUI()
         {
             GUI.Clear();
             selectedFields.Clear();
@@ -322,7 +127,7 @@ namespace BansheeEditor
                 horzLayout.AddFlexibleSpace();
                 horzLayout.AddElement(warningLbl);
                 horzLayout.AddFlexibleSpace();
-                
+
                 return;
             }
 
@@ -448,7 +253,7 @@ namespace BansheeEditor
             buttonLayout.AddFlexibleSpace();
 
             buttonLayoutHeight = playButton.Bounds.height;
-            
+
             GUILayout contentLayout = mainLayout.AddLayoutX();
             GUILayout fieldDisplayLayout = contentLayout.AddLayoutY(GUIOption.FixedWidth(FIELD_DISPLAY_WIDTH));
 
@@ -462,10 +267,10 @@ namespace BansheeEditor
 
             horzScrollBar = new GUIResizeableScrollBarH();
             horzScrollBar.OnScrollOrResize += OnHorzScrollOrResize;
-            
+
             vertScrollBar = new GUIResizeableScrollBarV();
             vertScrollBar.OnScrollOrResize += OnVertScrollOrResize;
-            
+
             GUILayout curveLayout = contentLayout.AddLayoutY();
             GUILayout curveLayoutHorz = curveLayout.AddLayoutX();
             GUILayout horzScrollBarLayout = curveLayout.AddLayoutX();
@@ -494,12 +299,251 @@ namespace BansheeEditor
             isInitialized = true;
         }
 
-        private void SetCurrentFrame(int frameIdx)
+        private void ResizeGUI(int width, int height)
         {
-            currentFrameIdx = Math.Max(0, frameIdx);
+            guiFieldDisplay.SetSize(FIELD_DISPLAY_WIDTH, height - buttonLayoutHeight * 2);
 
-            frameInputField.Value = currentFrameIdx;
-            guiCurveEditor.SetMarkedFrame(currentFrameIdx);
+            Vector2I curveEditorSize = GetCurveEditorSize();
+            guiCurveEditor.SetSize(curveEditorSize.x, curveEditorSize.y);
+            guiCurveEditor.Redraw();
+
+            horzScrollBar.SetWidth(curveEditorSize.x);
+            vertScrollBar.SetHeight(curveEditorSize.y);
+
+            UpdateScrollBarSize();
+            UpdateScrollBarPosition();
+        }
+        #endregion
+
+        #region Scroll, drag, zoom
+        private Vector2I dragStartPos;
+        private bool isButtonHeld;
+        private bool isDragInProgress;
+
+        private Vector2 minimalRange;
+        private Vector2 visibleOffset;
+
+        private void HandleDragAndZoomInput()
+        {
+            // Handle middle mouse dragging
+            if (isDragInProgress)
+            {
+                float dragX = Input.GetAxisValue(InputAxis.MouseX) * DRAG_SCALE;
+                float dragY = Input.GetAxisValue(InputAxis.MouseY) * DRAG_SCALE;
+
+                Vector2 totalRange = new Vector2(guiCurveEditor.XRange, guiCurveEditor.YRange);
+                Vector2 visibleRange = GetVisibleRange();
+                Vector2 offset = visibleOffset;
+
+                if (dragX > 0.0f)
+                {
+                    offset.x += dragX;
+
+                    float visibleRight = offset.x + visibleRange.x;
+                    if (visibleRight > totalRange.x)
+                        totalRange.x = visibleRight;
+                }
+                else
+                {
+                    float actualDragX = offset.x - Math.Max(0.0f, offset.x + dragX);
+
+                    offset.x -= actualDragX;
+
+                    float visibleRight = offset.x + visibleRange.x;
+                    totalRange.x = Math.Max(minimalRange.x, visibleRight);
+                }
+
+                if (dragY > 0.0f)
+                {
+                    offset.y += dragY;
+
+                    float visibleTop = offset.y + visibleRange.y;
+                    if (visibleTop > totalRange.y)
+                        totalRange.y = visibleTop;
+                }
+                else
+                {
+                    offset.y += dragY;
+
+                    float visibleYMax = Math.Abs(offset.y) + visibleRange.y;
+                    totalRange.y = Math.Max(minimalRange.y, visibleYMax);
+                }
+
+                SetTotalRange(totalRange.x, totalRange.y);
+                SetVisibleOffset(offset);
+                UpdateScrollBarPosition();
+            }
+
+            // Handle zoom in/out
+            float scroll = Input.GetAxisValue(InputAxis.MouseZ);
+            if (scroll != 0.0f)
+            {
+                Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
+                Vector2 curvePos;
+                if (guiCurveEditor.WindowToCurveSpace(windowPos, out curvePos))
+                {
+                    float zoom = scroll * ZOOM_SCALE;
+                    Zoom(curvePos, zoom);
+                }
+            }
+        }
+
+        private void SetVertScrollbarProperties(float position, float size)
+        {
+            Vector2 visibleRange = GetVisibleRange();
+            float scrollableRange = guiCurveEditor.YRange - visibleRange.y;
+
+            Vector2 offset = visibleOffset;
+            offset.y = scrollableRange * position;
+
+            SetVisibleOffset(offset);
+
+            int height = (int)(guiCurveEditor.Height / size);
+            guiCurveEditor.SetSize(guiCurveEditor.Width, height);
+        }
+
+        private void SetHorzScrollbarProperties(float position, float size)
+        {
+            Vector2 visibleRange = GetVisibleRange();
+            float scrollableRange = guiCurveEditor.XRange - visibleRange.x;
+
+            Vector2 offset = visibleOffset;
+            offset.x = scrollableRange * position;
+
+            SetVisibleOffset(offset);
+
+            int width = (int)(guiCurveEditor.Width / size);
+            guiCurveEditor.SetSize(width, guiCurveEditor.Height);
+        }
+
+        private Vector2 GetVisibleRange()
+        {
+            float unitsPerXPixel = guiCurveEditor.XRange / guiCurveEditor.Width;
+            float unitsPerYPixel = guiCurveEditor.YRange / guiCurveEditor.Height;
+
+            Vector2I visibleSize = GetCurveEditorSize();
+            return new Vector2(unitsPerXPixel * visibleSize.x, unitsPerYPixel * visibleSize.y);
+        }
+
+        private void SetVisibleOffset(Vector2 offset)
+        {
+            visibleOffset = offset;
+
+            float pixelsPerXUnit = guiCurveEditor.Width / guiCurveEditor.XRange;
+            float pixelsPerYUnit = guiCurveEditor.Height / (guiCurveEditor.YRange * 2.0f);
+
+            int x = (int)(pixelsPerXUnit * visibleOffset.x);
+            int y = (int)(pixelsPerYUnit * visibleOffset.y);
+
+            guiCurveEditor.SetPosition(x, y);
+        }
+
+        // Increases range without zooming in (increasing width/height accordingly)
+        private void SetTotalRange(float x, float y)
+        {
+            float pixelsPerXUnit = guiCurveEditor.Width / guiCurveEditor.XRange;
+            float pixelsPerYUnit = guiCurveEditor.Height / (guiCurveEditor.YRange * 2.0f);
+
+            int width = (int)(pixelsPerXUnit * x);
+            int height = (int)(pixelsPerYUnit * y);
+
+            guiCurveEditor.SetRange(x, y);
+            guiCurveEditor.SetSize(width, height);
+
+            UpdateScrollBarSize();
+        }
+
+        private void UpdateScrollBarSize()
+        {
+            Vector2 visibleRange = GetVisibleRange();
+            Vector2 totalRange = new Vector2(guiCurveEditor.XRange, guiCurveEditor.YRange);
+
+            horzScrollBar.HandleSize = visibleRange.x / totalRange.x;
+            vertScrollBar.HandleSize = visibleRange.y / totalRange.y;
+        }
+
+        private void UpdateScrollBarPosition()
+        {
+            Vector2 visibleRange = GetVisibleRange();
+            Vector2 totalRange = new Vector2(guiCurveEditor.XRange, guiCurveEditor.YRange);
+            Vector2 scrollableRange = totalRange - visibleRange;
+
+            horzScrollBar.Position = visibleOffset.x / scrollableRange.x;
+            vertScrollBar.Position = visibleOffset.y / scrollableRange.y;
+        }
+
+        private void Zoom(Vector2 curvePos, float amount)
+        {
+            Vector2 relativePos = curvePos - visibleOffset;
+            Vector2 visibleRange = GetVisibleRange();
+
+            relativePos.x /= visibleRange.x;
+            relativePos.y /= visibleRange.y;
+
+            relativePos.x = relativePos.x * 2.0f - 1.0f;
+            relativePos.y = relativePos.y * 2.0f - 1.0f;
+
+            Vector2 offset = visibleOffset;
+            offset.x += relativePos.x * amount;
+            offset.y += relativePos.y * amount;
+
+            offset.x = Math.Max(0.0f, offset.x);
+
+            SetVisibleOffset(offset);
+            UpdateScrollBarPosition();
+
+            int width = guiCurveEditor.Width + (int)amount;
+            int height = guiCurveEditor.Height + (int)amount;
+
+            // If we aren't at the minimum size, modify size and offset
+            Vector2I visibleSize = GetCurveEditorSize();
+            if (width > visibleSize.x || height > visibleSize.y)
+            {
+                width = Math.Max(width, visibleSize.x);
+                height = Math.Max(height, visibleSize.y);
+
+                guiCurveEditor.SetSize(width, height);
+                UpdateScrollBarSize();
+            }
+            else // Otherwise start increasing range for zoom in
+            {
+                float unitsPerXPixel = guiCurveEditor.XRange / guiCurveEditor.Width;
+                float unitsPerYPixel = guiCurveEditor.YRange / guiCurveEditor.Height;
+
+                float rangeX = guiCurveEditor.XRange + unitsPerXPixel * amount;
+                float rangeY = guiCurveEditor.YRange + unitsPerYPixel * amount;
+
+                SetTotalRange(rangeX, rangeY);
+            }
+        }
+        #endregion
+
+        #region Curve display
+        /// <summary>
+        /// A set of animation curves for a field of a certain type.
+        /// </summary>
+        private struct FieldCurves
+        {
+            public SerializableProperty.FieldType type;
+            public EdAnimationCurve[] curves;
+        }
+
+        private int currentFrameIdx;
+        private int fps = 1;
+        private Dictionary<string, FieldCurves> curves = new Dictionary<string, FieldCurves>();
+
+        internal int FPS
+        {
+            get { return fps; }
+            set { guiCurveEditor.SetFPS(value); fps = MathEx.Max(value, 1); }
+        }
+
+        private void SetCurrentFrame(int frameIdx)
+        {
+            currentFrameIdx = Math.Max(0, frameIdx);
+
+            frameInputField.Value = currentFrameIdx;
+            guiCurveEditor.SetMarkedFrame(currentFrameIdx);
 
             float time = guiCurveEditor.GetTimeForFrame(currentFrameIdx);
 
@@ -515,14 +559,14 @@ namespace BansheeEditor
                     switch (kvp.Value.type)
                     {
                         case SerializableProperty.FieldType.Vector2:
-                        {
-                            Vector2 value = new Vector2();
+                            {
+                                Vector2 value = new Vector2();
 
-                            for(int i = 0; i < 2; i++)
-                                value[i] = kvp.Value.curves[i].Evaluate(time, false);
+                                for (int i = 0; i < 2; i++)
+                                    value[i] = kvp.Value.curves[i].Evaluate(time, false);
 
-                            fieldValue.value = value;
-                        }
+                                fieldValue.value = value;
+                            }
                             break;
                         case SerializableProperty.FieldType.Vector3:
                             {
@@ -568,116 +612,174 @@ namespace BansheeEditor
             guiFieldDisplay.SetDisplayValues(values.ToArray());
         }
 
-        private void OnPointerPressed(PointerEvent ev)
+        private void UpdateDisplayedCurves()
         {
-            if (!isInitialized)
-                return;
-
-            guiCurveEditor.OnPointerPressed(ev);
-
-            if (ev.button == PointerButton.Middle)
+            List<EdAnimationCurve> curvesToDisplay = new List<EdAnimationCurve>();
+            for (int i = 0; i < selectedFields.Count; i++)
             {
-                Vector2I windowPos = ScreenToWindowPos(ev.ScreenPos);
-                Vector2 curvePos;
-                if (guiCurveEditor.WindowToCurveSpace(windowPos, out curvePos))
-                {
-                    dragStartPos = windowPos;
-                    isButtonHeld = true;
-                }
+                EdAnimationCurve curve;
+                if (TryGetCurve(selectedFields[i], out curve))
+                    curvesToDisplay.Add(curve);
             }
+
+            guiCurveEditor.SetCurves(curvesToDisplay.ToArray());
+
+            float xRange;
+            float yRange;
+            CalculateRange(curvesToDisplay, out xRange, out yRange);
+
+            // Don't allow zero range
+            if (xRange == 0.0f)
+                xRange = 60.0f;
+
+            if (yRange == 0.0f)
+                yRange = 10.0f;
+
+            // Add padding to y range
+            yRange *= 1.05f;
+
+            // Don't reduce visible range
+            minimalRange.x = Math.Max(xRange, minimalRange.x);
+            minimalRange.y = Math.Max(yRange, minimalRange.y);
+
+            xRange = Math.Max(xRange, guiCurveEditor.XRange);
+            yRange = Math.Max(yRange, guiCurveEditor.YRange);
+
+            guiCurveEditor.SetRange(xRange, yRange);
+            UpdateScrollBarSize();
         }
+        #endregion 
 
-        private void OnPointerMoved(PointerEvent ev)
-        {
-            if (!isInitialized)
-                return;
+        #region Field display
+        private List<string> selectedFields = new List<string>();
 
-            guiCurveEditor.OnPointerMoved(ev);
+        private void AddNewField(string path, SerializableProperty.FieldType type)
+        {
+            guiFieldDisplay.AddField(path);
 
-            if (isButtonHeld)
+            switch (type)
             {
-                Vector2I windowPos = ScreenToWindowPos(ev.ScreenPos);
+                case SerializableProperty.FieldType.Vector4:
+                    {
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[4];
 
-                int distance = Vector2I.Distance(dragStartPos, windowPos);
-                if (distance >= DRAG_START_DISTANCE)
-                {
-                    isDragInProgress = true;
+                        string[] subPaths = { ".x", ".y", ".z", ".w" };
+                        for (int i = 0; i < subPaths.Length; i++)
+                        {
+                            string subFieldPath = path + subPaths[i];
+                            fieldCurves.curves[i] = new EdAnimationCurve();
+                            selectedFields.Add(subFieldPath);
+                        }
 
-                    Cursor.Hide();
+                        curves[path] = fieldCurves;
+                    }
+                    break;
+                case SerializableProperty.FieldType.Vector3:
+                    {
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[3];
 
-                    Rect2I clipRect;
-                    clipRect.x = ev.ScreenPos.x - 2;
-                    clipRect.y = ev.ScreenPos.y - 2;
-                    clipRect.width = 4;
-                    clipRect.height = 4;
+                        string[] subPaths = { ".x", ".y", ".z" };
+                        for (int i = 0; i < subPaths.Length; i++)
+                        {
+                            string subFieldPath = path + subPaths[i];
+                            fieldCurves.curves[i] = new EdAnimationCurve();
+                            selectedFields.Add(subFieldPath);
+                        }
+
+                        curves[path] = fieldCurves;
+                    }
+                    break;
+                case SerializableProperty.FieldType.Vector2:
+                    {
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[2];
+
+                        string[] subPaths = { ".x", ".y" };
+                        for (int i = 0; i < subPaths.Length; i++)
+                        {
+                            string subFieldPath = path + subPaths[i];
+                            fieldCurves.curves[i] = new EdAnimationCurve();
+                            selectedFields.Add(subFieldPath);
+                        }
+
+                        curves[path] = fieldCurves;
+                    }
+                    break;
+                case SerializableProperty.FieldType.Color:
+                    {
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[4];
+
+                        string[] subPaths = { ".r", ".g", ".b", ".a" };
+                        for (int i = 0; i < subPaths.Length; i++)
+                        {
+                            string subFieldPath = path + subPaths[i];
+                            fieldCurves.curves[i] = new EdAnimationCurve();
+                            selectedFields.Add(subFieldPath);
+                        }
+
+                        curves[path] = fieldCurves;
+                    }
+                    break;
+                default: // Primitive type
+                    {
+                        FieldCurves fieldCurves = new FieldCurves();
+                        fieldCurves.type = type;
+                        fieldCurves.curves = new EdAnimationCurve[1];
 
-                    Cursor.ClipToRect(clipRect);
-                }
+                        fieldCurves.curves[0] = new EdAnimationCurve();
+                        selectedFields.Add(path);
+
+                        curves[path] = fieldCurves;
+                    }
+                    break;
             }
+
+            UpdateDisplayedCurves();
         }
 
-        private void OnPointerReleased(PointerEvent ev)
+        private void SelectField(string path, bool additive)
         {
-            if (isDragInProgress)
+            if (!additive)
+                selectedFields.Clear();
+
+            if (!string.IsNullOrEmpty(path))
             {
-                Cursor.Show();
-                Cursor.ClipDisable();
+                selectedFields.RemoveAll(x => { return x == path || IsPathParent(x, path); });
+                selectedFields.Add(path);
             }
 
-            isButtonHeld = false;
-            isDragInProgress = false;
-
-            if (!isInitialized)
-                return;
-
-            guiCurveEditor.OnPointerReleased(ev);
-        }
-
-        private void OnButtonUp(ButtonEvent ev)
-        {
-            if (!isInitialized)
-                return;
+            guiFieldDisplay.SetSelection(selectedFields.ToArray());
 
-            guiCurveEditor.OnButtonUp(ev);
+            UpdateDisplayedCurves();
         }
 
-        private void UpdateDisplayedCurves()
+        private void RemoveSelectedFields()
         {
-            List<EdAnimationCurve> curvesToDisplay = new List<EdAnimationCurve>();
             for (int i = 0; i < selectedFields.Count; i++)
             {
-                EdAnimationCurve curve;
-                if(TryGetCurve(selectedFields[i], out curve))
-                    curvesToDisplay.Add(curve);
+                selectedFields.Remove(selectedFields[i]);
+                curves.Remove(GetSubPathParent(selectedFields[i]));
             }
 
-            guiCurveEditor.SetCurves(curvesToDisplay.ToArray());
-
-            float xRange;
-            float yRange;
-            CalculateRange(curvesToDisplay, out xRange, out yRange);
-
-            // Don't allow zero range
-            if (xRange == 0.0f)
-                xRange = 60.0f;
-
-            if (yRange == 0.0f)
-                yRange = 10.0f;
-
-            // Add padding to y range
-            yRange *= 1.05f;
-
-            // Don't reduce visible range
-            minimalRange.x = Math.Max(xRange, minimalRange.x);
-            minimalRange.y = Math.Max(yRange, minimalRange.y);
+            List<string> existingFields = new List<string>();
+            foreach (var KVP in curves)
+                existingFields.Add(KVP.Key);
 
-            xRange = Math.Max(xRange, guiCurveEditor.XRange);
-            yRange = Math.Max(yRange, guiCurveEditor.YRange);
+            guiFieldDisplay.SetFields(existingFields.ToArray());
 
-            guiCurveEditor.SetRange(xRange, yRange);
-            UpdateScrollBarSize();
+            selectedFields.Clear();
+            UpdateDisplayedCurves();
         }
+        #endregion
 
+        #region Helpers
         private Vector2I GetCurveEditorSize()
         {
             Vector2I output = new Vector2I();
@@ -756,97 +858,6 @@ namespace BansheeEditor
             return false;
         }
 
-        private void OnFieldAdded(string path, SerializableProperty.FieldType type)
-        {
-            guiFieldDisplay.AddField(path);
-
-            switch (type)
-            {
-                case SerializableProperty.FieldType.Vector4:
-                {
-                    FieldCurves fieldCurves = new FieldCurves();
-                    fieldCurves.type = type;
-                    fieldCurves.curves = new EdAnimationCurve[4];
-
-                    string[] subPaths = { ".x", ".y", ".z", ".w" };
-                    for (int i = 0; i < subPaths.Length; i++)
-                    {
-                        string subFieldPath = path + subPaths[i];
-                        fieldCurves.curves[i] = new EdAnimationCurve();
-                        selectedFields.Add(subFieldPath);
-                    }
-
-                    curves[path] = fieldCurves;
-                }
-                    break;
-                case SerializableProperty.FieldType.Vector3:
-                    {
-                        FieldCurves fieldCurves = new FieldCurves();
-                        fieldCurves.type = type;
-                        fieldCurves.curves = new EdAnimationCurve[3];
-
-                        string[] subPaths = { ".x", ".y", ".z" };
-                        for (int i = 0; i < subPaths.Length; i++)
-                        {
-                            string subFieldPath = path + subPaths[i];
-                            fieldCurves.curves[i] = new EdAnimationCurve();
-                            selectedFields.Add(subFieldPath);
-                        }
-
-                        curves[path] = fieldCurves;
-                    }
-                    break;
-                case SerializableProperty.FieldType.Vector2:
-                    {
-                        FieldCurves fieldCurves = new FieldCurves();
-                        fieldCurves.type = type;
-                        fieldCurves.curves = new EdAnimationCurve[2];
-
-                        string[] subPaths = { ".x", ".y" };
-                        for (int i = 0; i < subPaths.Length; i++)
-                        {
-                            string subFieldPath = path + subPaths[i];
-                            fieldCurves.curves[i] = new EdAnimationCurve();
-                            selectedFields.Add(subFieldPath);
-                        }
-
-                        curves[path] = fieldCurves;
-                    }
-                    break;
-                case SerializableProperty.FieldType.Color:
-                    {
-                        FieldCurves fieldCurves = new FieldCurves();
-                        fieldCurves.type = type;
-                        fieldCurves.curves = new EdAnimationCurve[4];
-
-                        string[] subPaths = { ".r", ".g", ".b", ".a" };
-                        for (int i = 0; i < subPaths.Length; i++)
-                        {
-                            string subFieldPath = path + subPaths[i];
-                            fieldCurves.curves[i] = new EdAnimationCurve();
-                            selectedFields.Add(subFieldPath);
-                        }
-
-                        curves[path] = fieldCurves;
-                    }
-                    break;
-                default: // Primitive type
-                    {
-                        FieldCurves fieldCurves = new FieldCurves();
-                        fieldCurves.type = type;
-                        fieldCurves.curves = new EdAnimationCurve[1];
-
-                        fieldCurves.curves[0] = new EdAnimationCurve();
-                        selectedFields.Add(path);
-
-                        curves[path] = fieldCurves;
-                    }
-                    break;
-            }
-
-            UpdateDisplayedCurves();
-        }
-
         private bool IsPathParent(string child, string parent)
         {
             string[] childEntries = child.Split('/', '.');
@@ -874,77 +885,115 @@ namespace BansheeEditor
             return path.Substring(0, index);
         }
 
-        private void OnHorzScrollOrResize(float position, float size)
-        {
-            Vector2 visibleRange = GetVisibleRange();
-            float scrollableRange = guiCurveEditor.XRange - visibleRange.x;
+        #endregion
 
-            Vector2 offset = visibleOffset;
-            offset.x = scrollableRange * position;
+        #region Input callbacks
+        private void OnPointerPressed(PointerEvent ev)
+        {
+            if (!isInitialized)
+                return;
 
-            SetVisibleOffset(offset);
+            guiCurveEditor.OnPointerPressed(ev);
 
-            int width = (int)(guiCurveEditor.Width / size);
-            guiCurveEditor.SetSize(width, guiCurveEditor.Height);
+            if (ev.button == PointerButton.Middle)
+            {
+                Vector2I windowPos = ScreenToWindowPos(ev.ScreenPos);
+                Vector2 curvePos;
+                if (guiCurveEditor.WindowToCurveSpace(windowPos, out curvePos))
+                {
+                    dragStartPos = windowPos;
+                    isButtonHeld = true;
+                }
+            }
         }
 
-        private void OnVertScrollOrResize(float position, float size)
+        private void OnPointerMoved(PointerEvent ev)
         {
-            Vector2 visibleRange = GetVisibleRange();
-            float scrollableRange = guiCurveEditor.YRange - visibleRange.y;
+            if (!isInitialized)
+                return;
 
-            Vector2 offset = visibleOffset;
-            offset.y = scrollableRange*position;
+            guiCurveEditor.OnPointerMoved(ev);
 
-            SetVisibleOffset(offset);
+            if (isButtonHeld)
+            {
+                Vector2I windowPos = ScreenToWindowPos(ev.ScreenPos);
 
-            int height = (int)(guiCurveEditor.Height / size);
-            guiCurveEditor.SetSize(guiCurveEditor.Width, height);
+                int distance = Vector2I.Distance(dragStartPos, windowPos);
+                if (distance >= DRAG_START_DISTANCE)
+                {
+                    isDragInProgress = true;
+
+                    Cursor.Hide();
+
+                    Rect2I clipRect;
+                    clipRect.x = ev.ScreenPos.x - 2;
+                    clipRect.y = ev.ScreenPos.y - 2;
+                    clipRect.width = 4;
+                    clipRect.height = 4;
+
+                    Cursor.ClipToRect(clipRect);
+                }
+            }
         }
 
-        private void OnFieldSelected(string path)
+        private void OnPointerReleased(PointerEvent ev)
         {
-            if (!Input.IsButtonHeld(ButtonCode.LeftShift) && !Input.IsButtonHeld(ButtonCode.RightShift))
-                selectedFields.Clear();
-
-            if (!string.IsNullOrEmpty(path))
+            if (isDragInProgress)
             {
-                selectedFields.RemoveAll(x => { return x == path || IsPathParent(x, path); });
-                selectedFields.Add(path);
+                Cursor.Show();
+                Cursor.ClipDisable();
             }
 
-            guiFieldDisplay.SetSelection(selectedFields.ToArray());
+            isButtonHeld = false;
+            isDragInProgress = false;
 
-            UpdateDisplayedCurves();
+            if (!isInitialized)
+                return;
+
+            guiCurveEditor.OnPointerReleased(ev);
         }
 
-        private void RemoveSelectedFields()
+        private void OnButtonUp(ButtonEvent ev)
         {
-            for (int i = 0; i < selectedFields.Count; i++)
-            {
-                selectedFields.Remove(selectedFields[i]);
-                curves.Remove(GetSubPathParent(selectedFields[i]));
-            }
+            if (!isInitialized)
+                return;
 
-            List<string> existingFields = new List<string>();
-            foreach(var KVP in curves)
-                existingFields.Add(KVP.Key);
+            guiCurveEditor.OnButtonUp(ev);
+        }
+        #endregion
 
-            guiFieldDisplay.SetFields(existingFields.ToArray());
+        #region General callbacks
+        private void OnFieldAdded(string path, SerializableProperty.FieldType type)
+        {
+            AddNewField(path, type);
+        }
 
-            selectedFields.Clear();
-            UpdateDisplayedCurves();
+        private void OnHorzScrollOrResize(float position, float size)
+        {
+            SetHorzScrollbarProperties(position, size);
+        }
+
+        private void OnVertScrollOrResize(float position, float size)
+        {
+            SetVertScrollbarProperties(position, size);
+        }
+
+        private void OnFieldSelected(string path)
+        {
+            bool additive = Input.IsButtonHeld(ButtonCode.LeftShift) || Input.IsButtonHeld(ButtonCode.RightShift);
+            SelectField(path, additive);
         }
 
         private void OnSelectionChanged(SceneObject[] sceneObjects, string[] resourcePaths)
         {
-            Rebuild();
+            RebuildGUI();
         }
 
         private void OnFrameSelected(int frameIdx)
         {
             SetCurrentFrame(frameIdx);
         }
+        #endregion
     }
 
     /// <summary>