Browse Source

Refactor: GUITimeline and GUIGraphTicks moved into its own types

BearishSun 7 years ago
parent
commit
d3e8aa6f3b

+ 3 - 0
Source/EditorCore/CMakeSources.cmake

@@ -67,6 +67,7 @@ set(BS_BANSHEEEDITOR_SRC_GUI
 	"GUI/BsGUIFloatDistributionField.cpp"
 	"GUI/BsGUIColorDistributionField.cpp"
 	"GUI/BsGUIColorGradientField.cpp"
+	"GUI/BsGUITimeline.cpp"
 )
 
 set(BS_BANSHEEEDITOR_INC_LIBRARY
@@ -108,6 +109,8 @@ set(BS_BANSHEEEDITOR_INC_GUI
 	"GUI/BsGUIFloatDistributionField.h"
 	"GUI/BsGUIColorDistributionField.h"
 	"GUI/BsGUIColorGradientField.h"
+	"GUI/BsGUITimeline.h"
+	"GUI/BsGUIGraphTicks.h"
 )
 
 set(BS_BANSHEEEDITOR_INC_UNDOREDO

+ 0 - 135
Source/EditorCore/GUI/BsGUICurves.cpp

@@ -11,141 +11,6 @@
 
 namespace bs
 {
-	GUITimeline::GUITimeline(const String& styleName, const GUIDimensions& dimensions)
-		:GUIElementContainer(dimensions, styleName)
-	{
-		mCanvas = GUICanvas::create();
-		_registerChildElement(mCanvas);
-	}
-
-	const String& GUITimeline::getGUITypeName()
-	{
-		static String name = "Timeline";
-		return name;
-	}
-
-	void GUITimeline::setRange(float range)
-	{
-		mRange = std::max(0.0f, range);
-		_markContentAsDirty();
-	}
-
-	float GUITimeline::getRange() const
-	{
-		const float spf = 1.0f / (float)mFPS;
-		return std::max(1.0f, mRange / spf) * spf;
-	}
-
-	void GUITimeline::setOffset(float offset)
-	{
-		mOffset = offset;
-		_markContentAsDirty();
-	}
-
-	void GUITimeline::setFPS(UINT32 FPS)
-	{
-		mFPS = std::max(1U, FPS);
-		_markContentAsDirty();
-	}
-
-	void GUITimeline::setMarkedFrame(UINT32 index)
-	{
-		mMarkedFrame = index;
-		_markContentAsDirty();
-	}
-
-	void GUITimeline::setPadding(UINT32 padding)
-	{
-		mPadding = padding;
-		_markContentAsDirty();
-	}
-
-	UINT32 GUITimeline::getFrame(const Vector2I& pixelCoords) const
-	{
-		const Rect2I& bounds = mLayoutData.area;
-
-		if (pixelCoords.x < (INT32)mPadding || 
-			pixelCoords.x >= ((INT32)bounds.width - (INT32)mPadding) ||
-			pixelCoords.y < 0 || 
-			pixelCoords.y >= (INT32)bounds.height)
-		{
-			return -1;
-		}
-
-		const Vector2I relativeCoords = pixelCoords - Vector2I(bounds.x + mPadding, bounds.y);
-
-		const float lengthPerPixel = getRange() / getDrawableWidth();
-		const float time = mOffset + relativeCoords.x * lengthPerPixel;
-
-		return Math::roundToPosInt(time * mFPS);
-	}
-
-	float GUITimeline::getTime(INT32 pixel) const
-	{
-		const Rect2I& bounds = mLayoutData.area;
-		const INT32 relativeCoords = pixel - (bounds.x + mPadding);
-
-		const float lengthPerPixel = getRange() / getDrawableWidth();
-		return mOffset + relativeCoords * lengthPerPixel;
-	}
-
-	INT32 GUITimeline::getOffset(float time) const
-	{
-		return (INT32)(((time - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
-	}
-
-	float GUITimeline::getTimeForFrame(INT32 index) const
-	{
-		return index / (float)mFPS;
-	}
-
-	UINT32 GUITimeline::getDrawableWidth() const
-	{
-		return std::max(0, (INT32)mLayoutData.area.width - (INT32)mPadding * 2);
-	}
-
-	float GUITimeline::getRangeWithPadding() const
-	{
-		const float spf = 1.0f / (float)mFPS;
-
-		const float lengthPerPixel = mRange / getDrawableWidth();
-		const float range = mRange + lengthPerPixel * mPadding;
-
-		return std::max(1.0f, range / spf) * spf;
-	}
-
-	void GUITimeline::drawFrameMarker(float t)
-	{
-		const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
-
-		const Vector2I start(xPos, 0);
-		const Vector2I end(xPos, mLayoutData.area.height);
-
-		mCanvas->drawLine(start, end, Color::BansheeOrange);
-	}
-
-	void GUITimeline::drawFrameMarker()
-	{
-		if(mMarkedFrame != (UINT32)-1)
-			drawFrameMarker(mMarkedFrame / (float)mFPS);
-	}
-
-	void GUITimeline::_updateLayoutInternal(const GUILayoutData& data)
-	{
-		mCanvas->_setLayoutData(data);
-		mCanvas->_updateLayoutInternal(data);
-	}
-
-	Vector2I GUITimeline::_getOptimalSize() const
-	{
-		return mCanvas->_getOptimalSize();
-	}
-
-	void GUITimeline::styleUpdated()
-	{
-		mCanvas->setStyle(GUICanvas::getGUITypeName());
-	}
-
 	static constexpr int LINE_SPLIT_WIDTH = 2;
 	static constexpr int TANGENT_LINE_DISTANCE = 30;
 	static constexpr Color COLOR_MID_GRAY = Color(90.0f / 255.0f, 90.0f / 255.0f, 90.0f / 255.0f, 1.0f);

+ 2 - 314
Source/EditorCore/GUI/BsGUICurves.h

@@ -4,9 +4,9 @@
 
 #include "BsEditorPrerequisites.h"
 #include "GUI/BsGUIElement.h"
+#include "GUI/BsGUITimeline.h"
+#include "GUI/BsGUIGraphTicks.h"
 #include "2D/BsImageSprite.h"
-#include "GUI/BsGUIContent.h"
-#include "GUI/BsGUIElementContainer.h"
 #include "Animation/BsAnimationUtility.h"
 
 namespace bs
@@ -43,318 +43,6 @@ namespace bs
 		Color color;
 	};
 
-	/** Base class that can be implemented by GUI elements needing to elements along draw a horizontal timeline. */
-	class BS_ED_EXPORT BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) GUITimeline : public GUIElementContainer
-	{
-	public:
-		/** Returns type name of the GUI element used for finding GUI element styles. */
-		static const String& getGUITypeName();
-
-		/**	Determines the range of values to display on the timeline, in seconds. */
-		BS_SCRIPT_EXPORT(pr:setter,n:Range)
-		void setRange(float range);
-
-		/** @copydoc setRange */
-		BS_SCRIPT_EXPORT(pr:getter,n:Range)
-		float getRange() const;
-
-		/**	Determines the offset at which the displayed timeline values start at, in seconds. */
-		BS_SCRIPT_EXPORT(pr:setter,n:Offset)
-		void setOffset(float offset);
-
-		/** @copydoc setOffset */
-		BS_SCRIPT_EXPORT(pr:getter,n:Offset)
-		float getOffset() const { return mOffset; }
-
-		/**	Number of frames per second, used for frame selection and marking. */
-		BS_SCRIPT_EXPORT(pr:setter,n:FPS)
-		void setFPS(UINT32 FPS);
-
-		/** @copydoc setFPS */
-		BS_SCRIPT_EXPORT(pr:getter,n:FPS)
-		UINT32 getFPS() const { return mFPS; }
-
-		/**	Frame to display the frame marker on. Set to -1 to clear the frame marker. */
-		BS_SCRIPT_EXPORT(pr:setter,n:MarkedFrame)
-		void setMarkedFrame(UINT32 index);
-
-		/** @copydoc setFPS */
-		BS_SCRIPT_EXPORT(pr:getter,n:MarkedFrame)
-		UINT32 setMarkedFrame() const { return mFPS; }
-
-		/**
-		 * Uses the assigned FPS, range and physical size to calculate the frame that is under the provided coordinates.
-		 *
-		 * @param[in]	pixelCoords		Coordinates relative to this GUI element.
-		 * @return						Frame that was clicked on, or -1 if the coordinates are outside of valid bounds.
-		 */
-		BS_SCRIPT_EXPORT()
-		UINT32 getFrame(const Vector2I& pixelCoords) const;
-
-		/**
-		 * Returns the time at the specified pixel value along the timeline.
-		 * 
-		 * @param[in]	pixel	X coordinate to sample at, relative to this GUI element in pixels.
-		 * @return				Time along the curve at the specified coordinate.
-		 */
-		BS_SCRIPT_EXPORT()
-		float getTime(INT32 pixel) const;
-
-		/**
-		 * Finds the pixel offset relative to the GUI element's origin for the specified time.
-		 *
-		 * @param[in]	time	Time value to return the offset for.
-		 * @return				Offset in pixels relative to GUI element's origin
-		 */
-		BS_SCRIPT_EXPORT()
-		INT32 getOffset(float time) const;
-
-		/**
-		 * Returns time for a frame with the specified index. Depends on set range and FPS.
-		 * 
-		 * @param[in]	index	Index of the frame (not a key-frame) to get the time for.
-		 * @return				Time of the frame with the provided index.	
-		 */
-		BS_SCRIPT_EXPORT()
-		float getTimeForFrame(INT32 index) const;
-
-		/** Sets the size of padding to apply to the left and right sides of the curve drawing, in pixels. */
-		BS_SCRIPT_EXPORT(pr:setter,n:Padding)
-		void setPadding(UINT32 padding);
-
-		/** @copydoc setPadding */
-		BS_SCRIPT_EXPORT(pr:getter,n:Padding)
-		UINT32 getPadding() const { return mPadding; };
-
-		/**
-		 * @name Internal
-		 * @{
-		 */
-
-		/** @copydoc GUIElement::_getOptimalSize */
-		Vector2I _getOptimalSize() const override;
-
-		/** @} */
-	protected:
-		GUITimeline(const String& styleName, const GUIDimensions& dimensions);
-		~GUITimeline() override = default;
-
-		/** Returns the width of the GUI element that can be drawn to (width minus padding). */
-		UINT32 getDrawableWidth() const;
-
-		/** Similar to getRange() but expands the range so it's expanded to encompas the right-most padding area. */
-		float getRangeWithPadding() const;
-
-		/** Draws a vertical frame marker at the specified time. */
-		void drawFrameMarker(float t);
-
-		/** Draws a frame marker at the currently selected frame. */
-		void drawFrameMarker();
-
-		/** @copydoc GUIElement::_updateLayoutInternal */
-		void _updateLayoutInternal(const GUILayoutData& data) override;
-
-		/** @copydoc GUIElement::styleUpdated */
-		void styleUpdated() override;
-
-		GUICanvas* mCanvas = nullptr;
-
-		float mRange = 20.0f;
-		float mOffset = 0.0f;
-		UINT32 mFPS = 1;
-		UINT32 mMarkedFrame = (UINT32)-1;
-		UINT32 mPadding = 30;
-	};
-
-	/** Determines how should ticks reported by <see cref="GUIGraphTicks"/> be distributed. */
-	enum class BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) GUITickStepType
-	{
-		/** Ticks represent time values (Multiples of 60). */
-		Time,
-		/** Ticks represent generic values (Multiples of 10). */
-		Generic
-	};
-
-	/**
-	 * Generates a set of locations that can be used for rendering ticks on a graph. As input the class takes valid range,
-	 * size of the area the ticks will be displayed on, type of ticks and minimum/maximum spacing and outputs a set of
-	 * coordinates along which ticks should be positioned. Ticks are reported as multiple separate levels with different
-	 * strengths, depending on how close their distribution is to the upper valid range.
-	 */
-	class BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) GUIGraphTicks
-	{
-	public:
-		/**
-		 * Contructs a new tick generating object.
-		 *
-		 * @param[in]	stepType	Determines how will ticks be distributed.
-		 */
-		BS_SCRIPT_EXPORT()
-		GUIGraphTicks(GUITickStepType stepType = GUITickStepType::Generic)
-		{
-			if(stepType == GUITickStepType::Generic)
-				setGenericSteps();
-			else
-				setTimeSteps();
-
-			rebuild();
-		}
-
-		/** Number of tick levels that will be generated. */
-		BS_SCRIPT_EXPORT(pr:getter,n:NumLevels)
-		UINT32 getNumLevels() const
-		{
-			return mNumLevels;
-		}
-
-		/**
-		 * Sets the range which ticks are to be displayed for, and the range along which the ticks will be displayed.
-		 *
-		 * @param[in]	valueRangeStart		Start of the range the ticks are to display.
-		 * @param[in]	valueRangeEnd		End of the range the ticks are to display.
-		 * @param[in]	pixelRange			Width or height on which the ticks will be rendered. In pixels.
-		 */
-		BS_SCRIPT_EXPORT()
-		void setRange(float valueRangeStart, float valueRangeEnd, UINT32 pixelRange)
-		{
-			this->mValueRangeStart = valueRangeStart;
-			this->mValueRangeEnd = valueRangeEnd;
-			this->mPixelRange = pixelRange;
-
-			rebuild();
-		}
-
-		/**
-		 * Sets valid spacing between two ticks. Tick strength will be determined by how far away are they from either
-		 * end of this range.
-		 *
-		 * @param[in]	minPx		Minimum spacing between two ticks, in pixels.
-		 * @param[in]	maxPx		Maximum spacing between two ticks, in pixels.
-		 */
-		BS_SCRIPT_EXPORT()
-		void setTickSpacing(int minPx, int maxPx)
-		{
-			mMinTickSpacingPx = minPx;
-			mMaxTickSpacingPx = maxPx;
-
-			rebuild();
-		}
-
-		/**
-		 * Returns the strength of a particular tick level. Levels are ordered in descending order of strength (level 0 is
-		 * the strongest).
-		 *
-		 * @param[in]	level	Level for which to retrieve the strength. Must not be larger than getNumLevels() - 1.
-		 * @return				Strength of the ticks at this level, in range [0, 1].
-		 */
-		BS_SCRIPT_EXPORT()
-		float getLevelStrength(UINT32 level)
-		{
-			if (level >= mNumLevels)
-				return 0.0f;
-
-			return Math::clamp01(mLevelStrengths[mMaxLevel + level]);
-		}
-
-		/**
-		 * Returns positions of all ticks of the provided level. The ticks will be within the range provided to setRange().
-		 * 
-		 * @param[in]	level	Level for which to retrieve the positions. Must not be larger than getNumLevels() - 1.
-		 * @return				Positions of all ticks of the provided level.
-		 */
-		BS_SCRIPT_EXPORT()
-		Vector<float> getTicks(UINT32 level)
-		{
-			if (level < 0 || level >= mNumLevels)
-				return { };
-
-			const float step = mValidSteps[mMaxLevel + level];
-
-			// Round up and down so we get one extra tick on either side (outside of value range)
-			// (Useful when rendering text above the ticks, so the text doesn't just pop-in when the tick appears, instead
-			// it is slowly clipped-in.)
-			const INT32 startTick = Math::ceilToInt(mValueRangeStart / step);
-			const INT32 endTick = Math::floorToInt(mValueRangeEnd / step);
-
-			const INT32 numTicks = endTick - startTick + 1;
-
-			Vector<float> ticks(numTicks);
-			for (INT32 i = startTick; i <= endTick; i++)
-				ticks[i - startTick] = i*step;
-
-			return ticks;
-		}
-
-	protected:
-		/** Rebuilds the tick positions and strengths after some relevant parameter changes. */
-		void rebuild()
-		{
-			mLevelStrengths = Vector<float>(mValidSteps.size());
-			mMaxLevel = 0;
-
-			const float valueRange = mValueRangeEnd - mValueRangeStart;
-			const INT32 tickSpacing = (INT32)mMaxTickSpacingPx - (INT32)mMinTickSpacingPx;
-			UINT32 i = 0;
-			for (; i < (UINT32)mValidSteps.size(); i++)
-			{
-				const float tickSpacingPx = (mValidSteps[i]/valueRange) * mPixelRange;
-				mLevelStrengths[i] = (tickSpacingPx - mMinTickSpacingPx)/tickSpacing;
-
-				if (mLevelStrengths[i] > 1.0f)
-					mMaxLevel = i;
-				else if (mLevelStrengths[i] < 0.0f)
-					break;
-			}
-
-			if (i > 0)
-				mNumLevels = i - mMaxLevel;
-			else
-				mNumLevels = 0;
-		}
-
-		/** 
-		 * Sets tick steps corresponding to time values. This will split the ticks into intervals relevant for displaying
-		 * times.
-		 */
-		void setTimeSteps()
-		{
-			mValidSteps = 
-			{
-				3600.0f, 1800.0f, 600.0f, 300.0f,
-				60.0f, 30.0f, 10.0f, 5.0f,
-				1.0f, 0.5f, 0.25f, 0.1f, 0.05f, 0.01f
-			};
-		}
-
-		/** Sets tick steps corresponding to generic values (as opposed to displaying time values). */
-		void setGenericSteps()
-		{
-			constexpr UINT32 numSteps = 15;
-			float minStep = 0.0000001f;
-
-			mValidSteps = Vector<float>(numSteps * 2);
-			for (int i = numSteps - 1; i >= 0; i--)
-			{
-				mValidSteps[i * 2 + 1] = minStep;
-				mValidSteps[i * 2 + 0] = minStep * 5;
-
-				minStep *= 10.0f;
-			}
-		}
-
-		UINT32 mPixelRange = 100;
-		float mValueRangeStart = 0.0f;
-		float mValueRangeEnd = 1.0f;
-
-		UINT32 mMinTickSpacingPx = 5;
-		UINT32 mMaxTickSpacingPx = 30;
-
-		Vector<float> mValidSteps = { 1.0f };
-		Vector<float> mLevelStrengths = { 1.0f };
-		UINT32 mNumLevels = 1;
-		UINT32 mMaxLevel = 0;
-	};
-
 	/** GUI element that displays one or multiple curves. */
 	class BS_ED_EXPORT BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) GUICurves : public GUITimeline
 	{

+ 204 - 0
Source/EditorCore/GUI/BsGUIGraphTicks.h

@@ -0,0 +1,204 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "Math/BsMath.h"
+
+namespace bs
+{
+	/** @addtogroup GUI-Editor
+	 *  @{
+	 */
+
+	/** Determines how should ticks reported by <see cref="GUIGraphTicks"/> be distributed. */
+	enum class BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) GUITickStepType
+	{
+		/** Ticks represent time values (Multiples of 60). */
+		Time,
+		/** Ticks represent generic values (Multiples of 10). */
+		Generic
+	};
+
+	/**
+	 * Generates a set of locations that can be used for rendering ticks on a graph. As input the class takes valid range,
+	 * size of the area the ticks will be displayed on, type of ticks and minimum/maximum spacing and outputs a set of
+	 * coordinates along which ticks should be positioned. Ticks are reported as multiple separate levels with different
+	 * strengths, depending on how close their distribution is to the upper valid range.
+	 */
+	class BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) GUIGraphTicks
+	{
+	public:
+		/**
+		 * Contructs a new tick generating object.
+		 *
+		 * @param[in]	stepType	Determines how will ticks be distributed.
+		 */
+		BS_SCRIPT_EXPORT()
+		GUIGraphTicks(GUITickStepType stepType = GUITickStepType::Generic)
+		{
+			if(stepType == GUITickStepType::Generic)
+				setGenericSteps();
+			else
+				setTimeSteps();
+
+			rebuild();
+		}
+
+		/** Number of tick levels that will be generated. */
+		BS_SCRIPT_EXPORT(pr:getter,n:NumLevels)
+		UINT32 getNumLevels() const
+		{
+			return mNumLevels;
+		}
+
+		/**
+		 * Sets the range which ticks are to be displayed for, and the range along which the ticks will be displayed.
+		 *
+		 * @param[in]	valueRangeStart		Start of the range the ticks are to display.
+		 * @param[in]	valueRangeEnd		End of the range the ticks are to display.
+		 * @param[in]	pixelRange			Width or height on which the ticks will be rendered. In pixels.
+		 */
+		BS_SCRIPT_EXPORT()
+		void setRange(float valueRangeStart, float valueRangeEnd, UINT32 pixelRange)
+		{
+			this->mValueRangeStart = valueRangeStart;
+			this->mValueRangeEnd = valueRangeEnd;
+			this->mPixelRange = pixelRange;
+
+			rebuild();
+		}
+
+		/**
+		 * Sets valid spacing between two ticks. Tick strength will be determined by how far away are they from either
+		 * end of this range.
+		 *
+		 * @param[in]	minPx		Minimum spacing between two ticks, in pixels.
+		 * @param[in]	maxPx		Maximum spacing between two ticks, in pixels.
+		 */
+		BS_SCRIPT_EXPORT()
+		void setTickSpacing(int minPx, int maxPx)
+		{
+			mMinTickSpacingPx = minPx;
+			mMaxTickSpacingPx = maxPx;
+
+			rebuild();
+		}
+
+		/**
+		 * Returns the strength of a particular tick level. Levels are ordered in descending order of strength (level 0 is
+		 * the strongest).
+		 *
+		 * @param[in]	level	Level for which to retrieve the strength. Must not be larger than getNumLevels() - 1.
+		 * @return				Strength of the ticks at this level, in range [0, 1].
+		 */
+		BS_SCRIPT_EXPORT()
+		float getLevelStrength(UINT32 level)
+		{
+			if (level >= mNumLevels)
+				return 0.0f;
+
+			return Math::clamp01(mLevelStrengths[mMaxLevel + level]);
+		}
+
+		/**
+		 * Returns positions of all ticks of the provided level. The ticks will be within the range provided to setRange().
+		 * 
+		 * @param[in]	level	Level for which to retrieve the positions. Must not be larger than getNumLevels() - 1.
+		 * @return				Positions of all ticks of the provided level.
+		 */
+		BS_SCRIPT_EXPORT()
+		Vector<float> getTicks(UINT32 level)
+		{
+			if (level < 0 || level >= mNumLevels)
+				return { };
+
+			const float step = mValidSteps[mMaxLevel + level];
+
+			// Round up and down so we get one extra tick on either side (outside of value range)
+			// (Useful when rendering text above the ticks, so the text doesn't just pop-in when the tick appears, instead
+			// it is slowly clipped-in.)
+			const INT32 startTick = Math::ceilToInt(mValueRangeStart / step);
+			const INT32 endTick = Math::floorToInt(mValueRangeEnd / step);
+
+			const INT32 numTicks = endTick - startTick + 1;
+
+			Vector<float> ticks(numTicks);
+			for (INT32 i = startTick; i <= endTick; i++)
+				ticks[i - startTick] = i*step;
+
+			return ticks;
+		}
+
+	protected:
+		/** Rebuilds the tick positions and strengths after some relevant parameter changes. */
+		void rebuild()
+		{
+			mLevelStrengths = Vector<float>(mValidSteps.size());
+			mMaxLevel = 0;
+
+			const float valueRange = mValueRangeEnd - mValueRangeStart;
+			const INT32 tickSpacing = (INT32)mMaxTickSpacingPx - (INT32)mMinTickSpacingPx;
+			UINT32 i = 0;
+			for (; i < (UINT32)mValidSteps.size(); i++)
+			{
+				const float tickSpacingPx = (mValidSteps[i]/valueRange) * mPixelRange;
+				mLevelStrengths[i] = (tickSpacingPx - mMinTickSpacingPx)/tickSpacing;
+
+				if (mLevelStrengths[i] > 1.0f)
+					mMaxLevel = i;
+				else if (mLevelStrengths[i] < 0.0f)
+					break;
+			}
+
+			if (i > 0)
+				mNumLevels = i - mMaxLevel;
+			else
+				mNumLevels = 0;
+		}
+
+		/** 
+		 * Sets tick steps corresponding to time values. This will split the ticks into intervals relevant for displaying
+		 * times.
+		 */
+		void setTimeSteps()
+		{
+			mValidSteps = 
+			{
+				3600.0f, 1800.0f, 600.0f, 300.0f,
+				60.0f, 30.0f, 10.0f, 5.0f,
+				1.0f, 0.5f, 0.25f, 0.1f, 0.05f, 0.01f
+			};
+		}
+
+		/** Sets tick steps corresponding to generic values (as opposed to displaying time values). */
+		void setGenericSteps()
+		{
+			constexpr UINT32 numSteps = 15;
+			float minStep = 0.0000001f;
+
+			mValidSteps = Vector<float>(numSteps * 2);
+			for (int i = numSteps - 1; i >= 0; i--)
+			{
+				mValidSteps[i * 2 + 1] = minStep;
+				mValidSteps[i * 2 + 0] = minStep * 5;
+
+				minStep *= 10.0f;
+			}
+		}
+
+		UINT32 mPixelRange = 100;
+		float mValueRangeStart = 0.0f;
+		float mValueRangeEnd = 1.0f;
+
+		UINT32 mMinTickSpacingPx = 5;
+		UINT32 mMaxTickSpacingPx = 30;
+
+		Vector<float> mValidSteps = { 1.0f };
+		Vector<float> mLevelStrengths = { 1.0f };
+		UINT32 mNumLevels = 1;
+		UINT32 mMaxLevel = 0;
+	};
+
+	/** @} */
+}

+ 147 - 0
Source/EditorCore/GUI/BsGUITimeline.cpp

@@ -0,0 +1,147 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include <utility>
+#include "GUI/BsGUITimeline.h"
+#include "GUI/BsGUIDimensions.h"
+#include "GUI/BsGUIMouseEvent.h"
+#include "GUI/BsGUICanvas.h"
+#include "Math/BsLine2.h"
+#include "GUI/BsGUIWidget.h"
+
+namespace bs
+{
+	GUITimeline::GUITimeline(const String& styleName, const GUIDimensions& dimensions)
+		:GUIElementContainer(dimensions, styleName)
+	{
+		mCanvas = GUICanvas::create();
+		_registerChildElement(mCanvas);
+	}
+
+	const String& GUITimeline::getGUITypeName()
+	{
+		static String name = "Timeline";
+		return name;
+	}
+
+	void GUITimeline::setRange(float range)
+	{
+		mRange = std::max(0.0f, range);
+		_markContentAsDirty();
+	}
+
+	float GUITimeline::getRange() const
+	{
+		const float spf = 1.0f / (float)mFPS;
+		return std::max(1.0f, mRange / spf) * spf;
+	}
+
+	void GUITimeline::setOffset(float offset)
+	{
+		mOffset = offset;
+		_markContentAsDirty();
+	}
+
+	void GUITimeline::setFPS(UINT32 FPS)
+	{
+		mFPS = std::max(1U, FPS);
+		_markContentAsDirty();
+	}
+
+	void GUITimeline::setMarkedFrame(UINT32 index)
+	{
+		mMarkedFrame = index;
+		_markContentAsDirty();
+	}
+
+	void GUITimeline::setPadding(UINT32 padding)
+	{
+		mPadding = padding;
+		_markContentAsDirty();
+	}
+
+	UINT32 GUITimeline::getFrame(const Vector2I& pixelCoords) const
+	{
+		const Rect2I& bounds = mLayoutData.area;
+
+		if (pixelCoords.x < (INT32)mPadding || 
+			pixelCoords.x >= ((INT32)bounds.width - (INT32)mPadding) ||
+			pixelCoords.y < 0 || 
+			pixelCoords.y >= (INT32)bounds.height)
+		{
+			return -1;
+		}
+
+		const Vector2I relativeCoords = pixelCoords - Vector2I(bounds.x + mPadding, bounds.y);
+
+		const float lengthPerPixel = getRange() / getDrawableWidth();
+		const float time = mOffset + relativeCoords.x * lengthPerPixel;
+
+		return Math::roundToPosInt(time * mFPS);
+	}
+
+	float GUITimeline::getTime(INT32 pixel) const
+	{
+		const Rect2I& bounds = mLayoutData.area;
+		const INT32 relativeCoords = pixel - (bounds.x + mPadding);
+
+		const float lengthPerPixel = getRange() / getDrawableWidth();
+		return mOffset + relativeCoords * lengthPerPixel;
+	}
+
+	INT32 GUITimeline::getOffset(float time) const
+	{
+		return (INT32)(((time - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
+	}
+
+	float GUITimeline::getTimeForFrame(INT32 index) const
+	{
+		return index / (float)mFPS;
+	}
+
+	UINT32 GUITimeline::getDrawableWidth() const
+	{
+		return std::max(0, (INT32)mLayoutData.area.width - (INT32)mPadding * 2);
+	}
+
+	float GUITimeline::getRangeWithPadding() const
+	{
+		const float spf = 1.0f / (float)mFPS;
+
+		const float lengthPerPixel = mRange / getDrawableWidth();
+		const float range = mRange + lengthPerPixel * mPadding;
+
+		return std::max(1.0f, range / spf) * spf;
+	}
+
+	void GUITimeline::drawFrameMarker(float t)
+	{
+		const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
+
+		const Vector2I start(xPos, 0);
+		const Vector2I end(xPos, mLayoutData.area.height);
+
+		mCanvas->drawLine(start, end, Color::BansheeOrange);
+	}
+
+	void GUITimeline::drawFrameMarker()
+	{
+		if(mMarkedFrame != (UINT32)-1)
+			drawFrameMarker(mMarkedFrame / (float)mFPS);
+	}
+
+	void GUITimeline::_updateLayoutInternal(const GUILayoutData& data)
+	{
+		mCanvas->_setLayoutData(data);
+		mCanvas->_updateLayoutInternal(data);
+	}
+
+	Vector2I GUITimeline::_getOptimalSize() const
+	{
+		return mCanvas->_getOptimalSize();
+	}
+
+	void GUITimeline::styleUpdated()
+	{
+		mCanvas->setStyle(GUICanvas::getGUITypeName());
+	}
+}

+ 140 - 0
Source/EditorCore/GUI/BsGUITimeline.h

@@ -0,0 +1,140 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "GUI/BsGUIElement.h"
+#include "2D/BsImageSprite.h"
+#include "GUI/BsGUIElementContainer.h"
+
+namespace bs
+{
+	/** @addtogroup GUI-Editor
+	 *  @{
+	 */
+
+	/** Base class that can be implemented by GUI elements needing to elements along draw a horizontal timeline. */
+	class BS_ED_EXPORT BS_SCRIPT_EXPORT(ed:true,m:GUIEditor) GUITimeline : public GUIElementContainer
+	{
+	public:
+		/** Returns type name of the GUI element used for finding GUI element styles. */
+		static const String& getGUITypeName();
+
+		/**	Determines the range of values to display on the timeline, in seconds. */
+		BS_SCRIPT_EXPORT(pr:setter,n:Range)
+		void setRange(float range);
+
+		/** @copydoc setRange */
+		BS_SCRIPT_EXPORT(pr:getter,n:Range)
+		float getRange() const;
+
+		/**	Determines the offset at which the displayed timeline values start at, in seconds. */
+		BS_SCRIPT_EXPORT(pr:setter,n:Offset)
+		void setOffset(float offset);
+
+		/** @copydoc setOffset */
+		BS_SCRIPT_EXPORT(pr:getter,n:Offset)
+		float getOffset() const { return mOffset; }
+
+		/**	Number of frames per second, used for frame selection and marking. */
+		BS_SCRIPT_EXPORT(pr:setter,n:FPS)
+		void setFPS(UINT32 FPS);
+
+		/** @copydoc setFPS */
+		BS_SCRIPT_EXPORT(pr:getter,n:FPS)
+		UINT32 getFPS() const { return mFPS; }
+
+		/**	Frame to display the frame marker on. Set to -1 to clear the frame marker. */
+		BS_SCRIPT_EXPORT(pr:setter,n:MarkedFrame)
+		void setMarkedFrame(UINT32 index);
+
+		/** @copydoc setFPS */
+		BS_SCRIPT_EXPORT(pr:getter,n:MarkedFrame)
+		UINT32 setMarkedFrame() const { return mFPS; }
+
+		/**
+		 * Uses the assigned FPS, range and physical size to calculate the frame that is under the provided coordinates.
+		 *
+		 * @param[in]	pixelCoords		Coordinates relative to this GUI element.
+		 * @return						Frame that was clicked on, or -1 if the coordinates are outside of valid bounds.
+		 */
+		BS_SCRIPT_EXPORT()
+		UINT32 getFrame(const Vector2I& pixelCoords) const;
+
+		/**
+		 * Returns the time at the specified pixel value along the timeline.
+		 * 
+		 * @param[in]	pixel	X coordinate to sample at, relative to this GUI element in pixels.
+		 * @return				Time along the curve at the specified coordinate.
+		 */
+		BS_SCRIPT_EXPORT()
+		float getTime(INT32 pixel) const;
+
+		/**
+		 * Finds the pixel offset relative to the GUI element's origin for the specified time.
+		 *
+		 * @param[in]	time	Time value to return the offset for.
+		 * @return				Offset in pixels relative to GUI element's origin
+		 */
+		BS_SCRIPT_EXPORT()
+		INT32 getOffset(float time) const;
+
+		/**
+		 * Returns time for a frame with the specified index. Depends on set range and FPS.
+		 * 
+		 * @param[in]	index	Index of the frame (not a key-frame) to get the time for.
+		 * @return				Time of the frame with the provided index.	
+		 */
+		BS_SCRIPT_EXPORT()
+		float getTimeForFrame(INT32 index) const;
+
+		/** Sets the size of padding to apply to the left and right sides of the curve drawing, in pixels. */
+		BS_SCRIPT_EXPORT(pr:setter,n:Padding)
+		void setPadding(UINT32 padding);
+
+		/** @copydoc setPadding */
+		BS_SCRIPT_EXPORT(pr:getter,n:Padding)
+		UINT32 getPadding() const { return mPadding; };
+
+		/**
+		 * @name Internal
+		 * @{
+		 */
+
+		/** @copydoc GUIElement::_getOptimalSize */
+		Vector2I _getOptimalSize() const override;
+
+		/** @} */
+	protected:
+		GUITimeline(const String& styleName, const GUIDimensions& dimensions);
+		~GUITimeline() override = default;
+
+		/** Returns the width of the GUI element that can be drawn to (width minus padding). */
+		UINT32 getDrawableWidth() const;
+
+		/** Similar to getRange() but expands the range so it's expanded to encompas the right-most padding area. */
+		float getRangeWithPadding() const;
+
+		/** Draws a vertical frame marker at the specified time. */
+		void drawFrameMarker(float t);
+
+		/** Draws a frame marker at the currently selected frame. */
+		void drawFrameMarker();
+
+		/** @copydoc GUIElement::_updateLayoutInternal */
+		void _updateLayoutInternal(const GUILayoutData& data) override;
+
+		/** @copydoc GUIElement::styleUpdated */
+		void styleUpdated() override;
+
+		GUICanvas* mCanvas = nullptr;
+
+		float mRange = 20.0f;
+		float mOffset = 0.0f;
+		UINT32 mFPS = 1;
+		UINT32 mMarkedFrame = (UINT32)-1;
+		UINT32 mPadding = 30;
+	};
+
+	/** @} */
+}

+ 1 - 1
Source/Scripting/SBansheeEditor/Generated/BsScriptGUIGraphTicks.generated.cpp

@@ -2,7 +2,7 @@
 #include "BsMonoMethod.h"
 #include "BsMonoClass.h"
 #include "BsMonoUtil.h"
-#include "../../../EditorCore/GUI/BsGUICurves.h"
+#include "../../../EditorCore/GUI/BsGUIGraphTicks.h"
 
 namespace bs
 {

+ 1 - 1
Source/Scripting/SBansheeEditor/Generated/BsScriptGUIGraphTicks.generated.h

@@ -2,7 +2,7 @@
 
 #include "BsScriptEditorPrerequisites.h"
 #include "BsScriptObject.h"
-#include "../../../EditorCore/GUI/BsGUICurves.h"
+#include "../../../EditorCore/GUI/BsGUIGraphTicks.h"
 
 namespace bs
 {

+ 1 - 1
Source/Scripting/SBansheeEditor/Generated/BsScriptGUITimeline.generated.cpp

@@ -2,7 +2,7 @@
 #include "BsMonoMethod.h"
 #include "BsMonoClass.h"
 #include "BsMonoUtil.h"
-#include "../../../EditorCore/GUI/BsGUICurves.h"
+#include "../../../EditorCore/GUI/BsGUITimeline.h"
 #include "Wrappers/BsScriptVector2I.h"
 
 namespace bs