Jelajahi Sumber

GuiFrameSetCtrl Phase 1 Complete

This code completes phase 1 of the GuiFrameSetCtrl which is having frames that can position their children and resize when the divider is dragged by the user.
Peter Robinson 2 tahun lalu
induk
melakukan
a30312193f

+ 2 - 2
editor/EditorCore/Themes/BaseTheme/BaseTheme.cs

@@ -2041,8 +2041,8 @@ function BaseTheme::makeGuiEditorProfile(%this)
 	%this.frameSetProfile = new GuiControlProfile()
 	{
 		fillColor = %this.adjustValue(%this.color1, 10); 
-		fillColorHL = %this.adjustValue(%this.color2, 20); 
-		fillColorSL = %this.color3; 
+		fillColorHL = %this.adjustValue(%this.color1, 20); 
+		fillColorSL = %this.color4; 
 		fillColorNA = %this.color2; 
 		
 		borderDefault = %this.emptyBorder;

+ 0 - 80
engine/source/gui/buttons/guiButtonCtrl.cc

@@ -40,28 +40,11 @@ GuiButtonCtrl::GuiButtonCtrl()
 	mBounds.extent.set(140, 30);
 	mText = StringTable->insert("Button");
 	mTextID = StringTable->EmptyString;
-
-	//fill color
-	mEaseFillColorHL = EasingFunction::Linear;
-	mEaseFillColorSL = EasingFunction::Linear;
-	mEaseTimeFillColorHL = 500;
-	mEaseTimeFillColorSL = 0;
-
-	//control state
-	mPreviousState = GuiControlState::DisabledState;
-	mCurrentState = GuiControlState::DisabledState;
 }
 
 void GuiButtonCtrl::initPersistFields()
 {
 	Parent::initPersistFields();
-
-	addGroup("Gui Button Easing");
-	addField("easeFillColorHL", TypeEnum, Offset(mEaseFillColorHL, GuiButtonCtrl), 1, &gEasingTable);
-	addField("easeFillColorSL", TypeEnum, Offset(mEaseFillColorSL, GuiButtonCtrl), 1, &gEasingTable);
-	addField("easeTimeFillColorHL", TypeS32, Offset(mEaseTimeFillColorHL, GuiButtonCtrl));
-	addField("easeTimeFillColorSL", TypeS32, Offset(mEaseTimeFillColorSL, GuiButtonCtrl));
-	endGroup("Gui Button Easing");
 }
 
 void GuiButtonCtrl::setActive(bool value)
@@ -320,67 +303,4 @@ const char *GuiButtonCtrl::getScriptValue()
 void GuiButtonCtrl::onMessage(GuiControl *sender, S32 msg)
 {
 	Parent::onMessage(sender, msg);
-}
-
-const ColorI& GuiButtonCtrl::getFillColor(const GuiControlState state)
-{
-	if (state != mCurrentState)
-	{
-		//We have just switched states!
-		mPreviousState = mCurrentState;
-		mCurrentState = state;
-		if (mCurrentState == GuiControlState::DisabledState || mPreviousState == GuiControlState::DisabledState)
-		{
-			mFluidFillColor.stopFluidAnimation();
-			mFluidFillColor.set(mProfile->getFillColor(state));
-		}
-		else if (mCurrentState == GuiControlState::SelectedState || mPreviousState == GuiControlState::SelectedState)
-		{
-			mFluidFillColor.setEasingFunction(mEaseFillColorSL);
-			mFluidFillColor.setAnimationLength(mEaseTimeFillColorSL);
-			mFluidFillColor.startFluidAnimation(mProfile->getFillColor(state));
-		}
-		else if (mCurrentState == GuiControlState::HighlightState || mPreviousState == GuiControlState::HighlightState)
-		{
-			mFluidFillColor.setEasingFunction(mEaseFillColorHL);
-			mFluidFillColor.setAnimationLength(mEaseTimeFillColorHL);
-			mFluidFillColor.startFluidAnimation(mProfile->getFillColor(state));
-		}
-		else
-		{
-			//we should never get here...
-			mFluidFillColor.stopFluidAnimation();
-			mFluidFillColor.set(mProfile->getFillColor(state));
-		}
-	}
-
-	if (mFluidFillColor.isAnimating() && !isProcessingTicks())
-	{
-		setProcessTicks(true);
-	}
-
-	if (!mFluidFillColor.isAnimating())
-	{
-		mFluidFillColor.set(mProfile->getFillColor(state));
-	}
-
-	return mFluidFillColor;
-}
-
-void GuiButtonCtrl::processTick()
-{
-	bool shouldWeContinue = false;
-
-	shouldWeContinue |= mFluidFillColor.processTick();
-
-	if (!shouldWeContinue)
-	{
-		setProcessTicks(false);
-	}
-}
-
-void GuiButtonCtrl::setControlProfile(GuiControlProfile *prof)
-{
-	Parent::setControlProfile(prof);
-	mCurrentState = mCurrentState == DisabledState ? NormalState : DisabledState;
 }

+ 2 - 17
engine/source/gui/buttons/guiButtonCtrl.h

@@ -27,17 +27,14 @@
 #include "gui/guiControl.h"
 #endif
 
-class GuiButtonCtrl : public GuiControl
+class GuiButtonCtrl : public GuiEasingSupport
 {
 private:
-   typedef GuiControl Parent;
+   typedef GuiEasingSupport Parent;
 
 protected:
 	bool mDepressed;
 	bool mMouseOver;
-	FluidColorI mFluidFillColor; //The actual fill color as it moves fluidly from one color to another.
-	GuiControlState mPreviousState;
-	GuiControlState mCurrentState;
 	GuiControlState getCurrentState();
 	S32 getBitmapIndex(const GuiControlState state);
 
@@ -47,12 +44,6 @@ public:
 
 	DECLARE_CONOBJECT(GuiButtonCtrl);
 
-	EasingFunction mEaseFillColorHL; //Transitioning to or from HL (if SL is not involved)
-	EasingFunction mEaseFillColorSL; //Transitioning to or from SL (over HL)
-
-	S32 mEaseTimeFillColorHL;
-	S32 mEaseTimeFillColorSL;
-
 	void acceleratorKeyPress(U32 index);
 	void acceleratorKeyRelease(U32 index);
 
@@ -75,12 +66,6 @@ public:
 	virtual void onAction();
    
 	virtual void onRender(Point2I offset, const RectI &updateRect);
-	virtual void setControlProfile(GuiControlProfile *prof);
-
-	const ColorI& getFillColor(const GuiControlState state); //Returns the fill color based on the state.
-	virtual void processTick();
-
-	inline const char* getEasingFunctionDescription(const EasingFunction ease) { return mFluidFillColor.getEasingFunctionDescription(ease); };
 };
 
 #endif //_GUI_BUTTON_CTRL_H

+ 17 - 11
engine/source/gui/containers/guiChainCtrl.cc

@@ -37,6 +37,7 @@ GuiChainCtrl::GuiChainCtrl()
 	mChildSpacing = 0;
 	mIsVertical = true;
 	mBounds.extent.set(140, mEditOpenSpace);
+	mResizeGuard = false;
 }
 
 //------------------------------------------------------------------------------
@@ -101,18 +102,23 @@ void GuiChainCtrl::childrenReordered()
 
 void GuiChainCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
 {
-	Point2I actualNewExtent = newExtent;
-	if (mIsVertical)
+	if (!mResizeGuard)
 	{
-		actualNewExtent.y = getExtent().y;
-	}
-	else
-	{
-		actualNewExtent.x = getExtent().x;
-	}
-	if(newPosition != getPosition() || actualNewExtent != getExtent())
-	{
-		Parent::resize(newPosition, actualNewExtent);
+		mResizeGuard = true;
+		Point2I actualNewExtent = newExtent;
+		if (mIsVertical)
+		{
+			actualNewExtent.y = getExtent().y;
+		}
+		else
+		{
+			actualNewExtent.x = getExtent().x;
+		}
+		if(newPosition != getPosition() || actualNewExtent != getExtent())
+		{
+			Parent::resize(newPosition, actualNewExtent);
+		}
+		mResizeGuard = false;
 	}
 }
 

+ 1 - 0
engine/source/gui/containers/guiChainCtrl.h

@@ -19,6 +19,7 @@ private:
 	typedef GuiControl Parent;
 	bool mPrevIsVertical;
 	const S32 mEditOpenSpace = 30;
+	bool mResizeGuard;
 
 protected:
 	S32 mChildSpacing;

+ 13 - 9
engine/source/gui/containers/guiExpandCtrl.cc

@@ -36,6 +36,7 @@ GuiExpandCtrl::GuiExpandCtrl()
    mExpandedExtent.set(64, 64);
    mEasingFunction = EasingFunction::Linear;
    mAnimationLength = 500;
+   mCalcGuard = false;
 }
 
 void GuiExpandCtrl::initPersistFields()
@@ -101,8 +102,9 @@ void GuiExpandCtrl::parentResized(const Point2I &oldParentExtent, const Point2I
 		setCollapsedExtent(newExtent);
 	}
 
+	mCalcGuard = true;
 	resize(newPosition, newExtent);
-
+	mCalcGuard = false;
 	calcExpandedExtent();
 
 	if (mExpanded)
@@ -134,16 +136,18 @@ bool GuiExpandCtrl::calcExpandedExtent()
 	if (!size())
 		return false;
 
-	mExpandedExtent = Point2I(0, 0);
-	for (iterator itr = begin(); itr != end(); ++itr)
+	if(!mCalcGuard)//Prevent needless calcuations
 	{
-		GuiControl* child = dynamic_cast<GuiControl*>(*itr);
-		mExpandedExtent.setMax(child->getExtent() + child->getPosition());
-	}
-
-	mExpandedExtent = getOuterExtent(mExpandedExtent, GuiControlState::NormalState, mProfile);
-	mExpandedExtent.set(getMax(mCollapsedExtent.x, mExpandedExtent.x), getMax(mCollapsedExtent.y, mExpandedExtent.y));
+		mExpandedExtent = Point2I(0, 0);
+		for (iterator itr = begin(); itr != end(); ++itr)
+		{
+			GuiControl* child = dynamic_cast<GuiControl*>(*itr);
+			mExpandedExtent.setMax(child->getExtent() + child->getPosition());
+		}
 
+		mExpandedExtent = getOuterExtent(mExpandedExtent, GuiControlState::NormalState, mProfile);
+		mExpandedExtent.set(getMax(mCollapsedExtent.x, mExpandedExtent.x), getMax(mCollapsedExtent.y, mExpandedExtent.y));
+	}
 	return true;
 }
 

+ 1 - 0
engine/source/gui/containers/guiExpandCtrl.h

@@ -46,6 +46,7 @@ class GuiExpandCtrl : public GuiControl, public Fluid
 {
 private:
    typedef GuiControl Parent;
+   bool mCalcGuard;
 
    void setCollapsedExtent(const Point2I &extent);
 

+ 164 - 12
engine/source/gui/containers/guiFrameSetCtrl.cc

@@ -29,14 +29,18 @@
 
 void GuiFrameSetCtrl::Frame::resize(const Point2I& newPosition, const Point2I& newExtent)
 {
+	const S32 minSize = owner->minSize;
 	extent.set(newExtent.x, newExtent.y);
 	if (control)
 	{
+		if (control->mMinExtent.x > minSize || control->mMinExtent.y > minSize)
+		{
+			control->mMinExtent.set(minSize, minSize);
+		}
 		control->resize(newPosition, newExtent);
 	}
 	else if (child1 && child2)
 	{
-		const S32 minSize = owner->minSize;
 		S32 spaceX = isVertical ? newExtent.x : newExtent.x - owner->mDividerThickness;
 		S32 spaceY = !isVertical ? newExtent.y : newExtent.y - owner->mDividerThickness;
 
@@ -76,6 +80,15 @@ void GuiFrameSetCtrl::Frame::resize(const Point2I& newPosition, const Point2I& n
 		Point2I ext2 = Point2I(x2, y2);
 		Point2I pos2 = isVertical ? Point2I(newPosition.x, y1 + owner->mDividerThickness) : Point2I(x1 + owner->mDividerThickness, newPosition.y);
 
+		if(isVertical)
+		{
+			dividerRect.set(newPosition.x, newPosition.y + y1, x1, owner->mDividerThickness);
+		}
+		else
+		{
+			dividerRect.set(newPosition.x + x1, newPosition.y, owner->mDividerThickness, y1);
+		}
+
 		child1->resize(newPosition, ext1);
 		child2->resize(pos2, ext2);
 	}
@@ -126,6 +139,24 @@ GuiFrameSetCtrl::Frame* GuiFrameSetCtrl::Frame::twin()
 	return this;
 }
 
+GuiFrameSetCtrl::Frame* GuiFrameSetCtrl::Frame::findHitDivider(const Point2I& position)
+{
+	S32 x = position.x;
+	S32 y = position.y;
+
+	if (child1 && child2)
+	{
+		if (x >= dividerRect.point.x && x <= (dividerRect.point.x + dividerRect.extent.x) && 
+			y >= dividerRect.point.y && y <= (dividerRect.point.y + dividerRect.extent.y))
+		{
+			return this;
+		}
+		Frame* attempt = child1->findHitDivider(position);
+		return attempt ? attempt : child2->findHitDivider(position);
+	}
+	return nullptr;
+}
+
 
 //------------------------------------------------------------------------------
 
@@ -139,9 +170,21 @@ GuiFrameSetCtrl::GuiFrameSetCtrl()
 	setField("profile", "GuiDefaultProfile");
 
 	mRootFrame = Frame(this, nullptr);
+	mHitDivider = nullptr;
 	mDividerThickness = 8;
 	mNextFrameID = 1;
 	mResizeGuard = false;
+	mDepressed = false;
+	mActive = true;
+	mFrameDragAnchor = 0;
+
+	mEaseFillColorHL = EasingFunction::EaseInOut;
+	mEaseFillColorSL = EasingFunction::EaseOut;
+	mEaseTimeFillColorHL = 500;
+	mEaseTimeFillColorSL = 1000;
+
+	mLeftRightCursor = NULL;
+	mUpDownCursor = NULL;
 }
 
 //------------------------------------------------------------------------------
@@ -151,6 +194,8 @@ void GuiFrameSetCtrl::initPersistFields()
 	Parent::initPersistFields();
 
 	addField("DividerThickness", TypeS32, Offset(mDividerThickness, GuiFrameSetCtrl));
+	addField("leftRightCursor", TypeGuiCursor, Offset(mLeftRightCursor, GuiFrameSetCtrl));
+	addField("upDownCursor", TypeGuiCursor, Offset(mUpDownCursor, GuiFrameSetCtrl));
 }
 
 //------------------------------------------------------------------------------
@@ -334,25 +379,132 @@ void GuiFrameSetCtrl::setFrameSize(S32 frameID, S32 size)
 
 void GuiFrameSetCtrl::onRender(Point2I offset, const RectI& updateRect)
 {
-	RectI ctrlRect = applyMargins(offset, mBounds.extent, NormalState, mProfile);
+	Parent::onRender(offset, updateRect);
 
-	if (!ctrlRect.isValidRect())
+	if (mHitDivider)
 	{
+		GuiControlState currentState = mDepressed ? SelectedState : HighlightState;
+
+		mOldHitDivider = mHitDivider;
+		RectI contentRect = RectI(localToGlobalCoord(mHitDivider->dividerRect.point), mHitDivider->dividerRect.extent);
+		renderUniversalRect(contentRect, mProfile, currentState, getFillColor(currentState), true);
+	}
+	else if (mOldHitDivider && (mCurrentState == HighlightState || mFluidFillColor.isAnimating()))
+	{
+		RectI contentRect = RectI(localToGlobalCoord(mOldHitDivider->dividerRect.point), mOldHitDivider->dividerRect.extent);
+		renderUniversalRect(contentRect, mProfile, NormalState, getFillColor(NormalState), true);
+	}
+	else if (mOldHitDivider)
+	{
+		mOldHitDivider = nullptr;
+	}
+}
+
+void GuiFrameSetCtrl::onTouchMove(const GuiEvent& event)
+{
+	if (!mVisible || !mAwake)
 		return;
+
+	if(!mDepressed)
+	{
+		Point2I localPoint = globalToLocalCoord(event.mousePoint);
+		mHitDivider = mRootFrame.findHitDivider(localPoint);
 	}
 
-	renderUniversalRect(ctrlRect, mProfile, NormalState);
+	if (!mHitDivider)
+	{
+		Parent::onTouchMove(event);
+	}
+}
 
-	//Render Text
-	dglSetBitmapModulation(getFontColor(mProfile));
-	RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
-	RectI contentRect = applyPadding(fillRect.point, fillRect.extent, NormalState, mProfile);
+void GuiFrameSetCtrl::onTouchDragged(const GuiEvent& event)
+{
+	if (mDepressed && mHitDivider && mHitDivider->child1 && mHitDivider->child2)
+	{
+		S32 offset = (mHitDivider->isVertical ? event.mousePoint.y : event.mousePoint.x) - mFrameDragAnchor;
+		if(offset != 0)
+		{
+			if (mHitDivider->isVertical)
+			{
+				mHitDivider->child1->extent.y = getMax(minSize, mHitDivider->child1->extent.y + offset);
+				mHitDivider->child2->extent.y = getMax(minSize, mHitDivider->child2->extent.y - offset);
+			}
+			else
+			{
+				mHitDivider->child1->extent.x = getMax(minSize, mHitDivider->child1->extent.x + offset);
+				mHitDivider->child2->extent.x = getMax(minSize, mHitDivider->child2->extent.x - offset);
+			}
+			resize(getPosition(), getExtent());
+			mFrameDragAnchor = mHitDivider->isVertical ? event.mousePoint.y : event.mousePoint.x;
+		}
+	}
+}
+
+void GuiFrameSetCtrl::onTouchDown(const GuiEvent& event)
+{
+ 	if (!mActive)
+		return;
+
+	if(mHitDivider)
+	{
+		mDepressed = true;
+		mFrameDragAnchor = mHitDivider->isVertical ? event.mousePoint.y : event.mousePoint.x;
 
-	if (contentRect.isValidRect())
+		//lock the mouse
+		mouseLock();
+
+		//update
+		setUpdate();
+	}
+}
+
+void GuiFrameSetCtrl::onTouchUp(const GuiEvent& event)
+{
+	if (!mActive)
+		return;
+
+	if(mHitDivider)
 	{
-		renderText(contentRect.point, contentRect.extent, mText, mProfile);
+		mouseUnlock();
 
-		//Render the childen
-		renderChildControls(offset, contentRect, updateRect);
+		mDepressed = false;
+
+		//update
+		setUpdate();
+	}
+}
+
+void GuiFrameSetCtrl::getCursor(GuiCursor*& cursor, bool& showCursor, const GuiEvent& lastGuiEvent)
+{
+	GuiControl* parent = getParent();
+	if (!parent)
+	{
+		return;
+	}
+	if (mHitDivider && mHitDivider->isVertical)
+	{
+		if (mUpDownCursor == NULL)
+		{
+			SimObject* obj;
+			obj = Sim::findObject("UpDownCursor");
+			mUpDownCursor = dynamic_cast<GuiCursor*>(obj);
+		}
+		if (mUpDownCursor != NULL)
+		{
+			cursor = mUpDownCursor;
+		}
+	}
+	else if (mHitDivider && !mHitDivider->isVertical)
+	{
+		if (mLeftRightCursor == NULL)
+		{
+			SimObject* obj;
+			obj = Sim::findObject("LeftRightCursor");
+			mLeftRightCursor = dynamic_cast<GuiCursor*>(obj);
+		}
+		if (mLeftRightCursor != NULL)
+		{
+			cursor = mLeftRightCursor;
+		}
 	}
 }

+ 18 - 3
engine/source/gui/containers/guiFrameSetCtrl.h

@@ -13,27 +13,34 @@
 #include "console/console.h"
 #include "console/consoleTypes.h"
 
-class GuiFrameSetCtrl : public GuiControl
+class GuiFrameSetCtrl : public GuiEasingSupport
 {
 private:
 	typedef GuiControl Parent;
 	U32 mNextFrameID;
 	bool mResizeGuard;
+	bool mDepressed;
+	S32 mFrameDragAnchor;
+	
+	// Sizing Cursors
+	GuiCursor* mLeftRightCursor;
+	GuiCursor* mUpDownCursor;
 
 public:
 	class Frame
 	{
 	public:
 		Frame() : owner(nullptr), parent(nullptr), child1(nullptr), child2(nullptr),
-			control(nullptr), isVertical(true), id(1), extent(Point2I(100, 100)) { }
+			control(nullptr), isVertical(true), id(1), extent(Point2I(100, 100)), dividerRect(RectI()) { }
 		Frame(GuiFrameSetCtrl* theOwner, Frame* theParent) : owner(theOwner), parent(theParent), child1(nullptr), child2(nullptr), 
-			control(nullptr), isVertical(true), id(1), extent(Point2I(100, 100)) { }
+			control(nullptr), isVertical(true), id(1), extent(Point2I(100, 100)), dividerRect(RectI()) { }
 		virtual ~Frame() { }
 
 		bool isVertical;
 		U32 id;
 		Point2I extent;
 		bool isAnchored;
+		RectI dividerRect;
 
 		GuiFrameSetCtrl* owner;
 		Frame* parent;
@@ -46,8 +53,11 @@ public:
 		Frame* findFrame(const S32 frameID);
 		Frame* findEmptyFrame();
 		Frame* twin();
+		Frame* findHitDivider(const Point2I& position);
 	};
 	Frame mRootFrame;
+	Frame* mHitDivider;
+	Frame* mOldHitDivider;
 	U8 mDividerThickness; 
 	const S32 minSize = 20;
 
@@ -75,6 +85,11 @@ public:
 	void anchorFrame(S32 frameID);
 	void setFrameSize(S32 frameID, S32 size);
 	void onRender(Point2I offset, const RectI& updateRect);
+	void onTouchMove(const GuiEvent& event);
+	void onTouchDragged(const GuiEvent& event);
+	void onTouchDown(const GuiEvent& event);
+	void onTouchUp(const GuiEvent& event);
+	void getCursor(GuiCursor*& cursor, bool& showCursor, const GuiEvent& lastGuiEvent);
 
 	DECLARE_CONOBJECT(GuiFrameSetCtrl);
 };

+ 50 - 44
engine/source/gui/containers/guiGridCtrl.cc

@@ -68,6 +68,7 @@ GuiGridCtrl::GuiGridCtrl()
 	mOrderMode = OrderMode::LRTB;
 	mIsExtentDynamic = true;
    setField("profile", "GuiDefaultProfile");
+   mResizeGuard = false;
 }
 
 //------------------------------------------------------------------------------
@@ -117,60 +118,65 @@ void GuiGridCtrl::inspectPostApply()
 
 void GuiGridCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
 {
-	Point2I actualNewExtent = Point2I(getMax(mMinExtent.x, newExtent.x),
-		getMax(mMinExtent.y, newExtent.y));
-
-	//call set update both before and after
-	setUpdate();
-
-	Point2I zero = mBounds.point.Zero;
-	RectI innerRect = getInnerRect(zero, actualNewExtent, NormalState, mProfile);
-	if (!innerRect.isValidRect() && !mIsExtentDynamic)
-	{
-		return;
-	}
-		
-	AdjustGrid(innerRect.extent);
-
-	iterator i;
-	U16 cellNumber = 0;
-	mChainNumber = 0;
-	mRunningChainHeight = 0;
-	mCurrentChainHeight = 0;
-	for (i = begin(); i != end(); i++, cellNumber++)
+	if (!mResizeGuard)//Prevent circular resizing
 	{
-		GuiControl *ctrl = static_cast<GuiControl *>(*i);
-		Point2I cellPos = getCellPosition(cellNumber, innerRect.extent, ctrl);
-		Point2I cellExt = getCellExtent(ctrl);
-		ctrl->resize(cellPos, cellExt);
-	}
-	mRunningChainHeight += mCurrentChainHeight;
+		mResizeGuard = true;
+		Point2I actualNewExtent = Point2I(getMax(mMinExtent.x, newExtent.x),
+			getMax(mMinExtent.y, newExtent.y));
 
-	Point2I actualNewPosition = Point2I(newPosition);
-	if(mIsExtentDynamic)
-	{
-		if(isEditMode())
+		//call set update both before and after
+		setUpdate();
+
+		Point2I zero = mBounds.point.Zero;
+		RectI innerRect = getInnerRect(zero, actualNewExtent, NormalState, mProfile);
+		if (!innerRect.isValidRect() && !mIsExtentDynamic)
 		{
-			mRunningChainHeight += 20; 
+			return;
 		}
-		if (IsVertical())
+		
+		AdjustGrid(innerRect.extent);
+
+		iterator i;
+		U16 cellNumber = 0;
+		mChainNumber = 0;
+		mRunningChainHeight = 0;
+		mCurrentChainHeight = 0;
+		for (i = begin(); i != end(); i++, cellNumber++)
 		{
-			innerRect.extent.y = mRunningChainHeight;
+			GuiControl *ctrl = static_cast<GuiControl *>(*i);
+			Point2I cellPos = getCellPosition(cellNumber, innerRect.extent, ctrl);
+			Point2I cellExt = getCellExtent(ctrl);
+			ctrl->resize(cellPos, cellExt);
 		}
-		else
+		mRunningChainHeight += mCurrentChainHeight;
+
+		Point2I actualNewPosition = Point2I(newPosition);
+		if(mIsExtentDynamic)
 		{
-			innerRect.extent.x = mRunningChainHeight;
-		}
-		actualNewExtent = getOuterExtent(innerRect.extent, NormalState, mProfile);
+			if(isEditMode())
+			{
+				mRunningChainHeight += 20; 
+			}
+			if (IsVertical())
+			{
+				innerRect.extent.y = mRunningChainHeight;
+			}
+			else
+			{
+				innerRect.extent.x = mRunningChainHeight;
+			}
+			actualNewExtent = getOuterExtent(innerRect.extent, NormalState, mProfile);
 
-	}
+		}
 
-	mBounds.set(actualNewPosition, actualNewExtent);
+		mBounds.set(actualNewPosition, actualNewExtent);
 
-	GuiControl *parent = getParent();
-	if (parent)
-		parent->childResized(this);
-	setUpdate();
+		GuiControl *parent = getParent();
+		if (parent)
+			parent->childResized(this);
+		setUpdate();
+		mResizeGuard = false;
+	}
 }
 
 //------------------------------------------------------------------------------

+ 1 - 0
engine/source/gui/containers/guiGridCtrl.h

@@ -23,6 +23,7 @@ private:
 	U32 mRunningChainHeight;
 	U32 mCurrentChainHeight;
 	U8 mChainNumber;
+	bool mResizeGuard;
 
 public:
 	enum CellMode

+ 69 - 53
engine/source/gui/containers/guiScrollCtrl.cc

@@ -76,6 +76,8 @@ GuiScrollCtrl::GuiScrollCtrl()
    setField("profile", "GuiScrollProfile");
 
    mEventBubbled = false;
+   mCalcGuard = false;
+   mResizeGuard = false;
 }
 
 void GuiScrollCtrl::initPersistFields()
@@ -95,26 +97,33 @@ void GuiScrollCtrl::initPersistFields()
 
 void GuiScrollCtrl::resize(const Point2I &newPos, const Point2I &newExt)
 {
-	bool hasH = mHasHScrollBar;
-	bool hasV = mHasVScrollBar;
-	Parent::resize(newPos, newExt);
-	computeSizes();
-
-	if (hasH != mHasHScrollBar || hasV != mHasVScrollBar)
+	if(!mResizeGuard)
 	{
-		S32 deltaY = hasH != mHasHScrollBar ? (mHasHScrollBar ? mScrollBarThickness : -mScrollBarThickness) : 0;
-		S32 deltaX = hasV != mHasVScrollBar ? (mHasVScrollBar ? mScrollBarThickness : -mScrollBarThickness) : 0;
+		mResizeGuard = true;
+		bool hasH = mHasHScrollBar;
+		bool hasV = mHasVScrollBar;
+		mCalcGuard = true;
+		Parent::resize(newPos, newExt);
+		mCalcGuard = false;
+		computeSizes();
 
-		iterator i;
-		for (i = begin(); i != end(); i++)
+		if (hasH != mHasHScrollBar || hasV != mHasVScrollBar)
 		{
-			GuiControl* ctrl = static_cast<GuiControl*>(*i);
-			ctrl->mRenderInsetRB = Point2I(ctrl->mRenderInsetRB.x + deltaX, ctrl->mRenderInsetRB.y + deltaY);
-			ctrl->parentResized(mBounds.extent - (ctrl->mRenderInsetLT + ctrl->mRenderInsetRB), mBounds.extent - (ctrl->mRenderInsetLT + ctrl->mRenderInsetRB));
-		}
+			S32 deltaY = hasH != mHasHScrollBar ? (mHasHScrollBar ? mScrollBarThickness : -mScrollBarThickness) : 0;
+			S32 deltaX = hasV != mHasVScrollBar ? (mHasVScrollBar ? mScrollBarThickness : -mScrollBarThickness) : 0;
 
-		Parent::resize(newPos, newExt);
-		computeSizes();
+			iterator i;
+			for (i = begin(); i != end(); i++)
+			{
+				GuiControl* ctrl = static_cast<GuiControl*>(*i);
+				ctrl->mRenderInsetRB = Point2I(ctrl->mRenderInsetRB.x + deltaX, ctrl->mRenderInsetRB.y + deltaY);
+				ctrl->parentResized(mBounds.extent - (ctrl->mRenderInsetLT + ctrl->mRenderInsetRB), mBounds.extent - (ctrl->mRenderInsetLT + ctrl->mRenderInsetRB));
+			}
+
+			Parent::resize(newPos, newExt);
+			computeSizes();
+		}
+		mResizeGuard = false;
 	}
 }
 
@@ -272,52 +281,55 @@ GuiScrollCtrl::Region GuiScrollCtrl::findHitRegion(const Point2I& pt)
 #pragma region CalculationFunctions
 void GuiScrollCtrl::computeSizes()
 {
-	calcContentExtents();
+	if (!mCalcGuard)//Prevent needless calcuations
+	{
+		calcContentExtents();
 
-	mHBarEnabled = false;
-	mVBarEnabled = false;
-	mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn);
-	mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn);
+		mHBarEnabled = false;
+		mVBarEnabled = false;
+		mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn);
+		mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn);
 
-	setUpdate();
+		setUpdate();
 
-	if (calcChildExtents())
-	{
-		if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic))
-		{
-			mHasHScrollBar = true;
-		}
-		if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic))
+		if (calcChildExtents())
 		{
-			mHasVScrollBar = true;
-
-			// If Extent X Changed, check Horiz Scrollbar.
-			if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic))
+			if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic))
 			{
 				mHasHScrollBar = true;
 			}
-		}
+			if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic))
+			{
+				mHasVScrollBar = true;
 
-		if (mHasVScrollBar)
-			mContentExt.x -= mScrollBarThickness;
-		if (mHasHScrollBar)
-			mContentExt.y -= mScrollBarThickness;
+				// If Extent X Changed, check Horiz Scrollbar.
+				if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic))
+				{
+					mHasHScrollBar = true;
+				}
+			}
 
-		// enable needed scroll bars
-		if (mChildExt.x > mContentExt.x)
-			mHBarEnabled = true;
-		if (mChildExt.y > mContentExt.y)
-			mVBarEnabled = true;
+			if (mHasVScrollBar)
+				mContentExt.x -= mScrollBarThickness;
+			if (mHasHScrollBar)
+				mContentExt.y -= mScrollBarThickness;
 
-		//Are we now over-scrolled?
-		calcScrollOffset();
+			// enable needed scroll bars
+			if (mChildExt.x > mContentExt.x)
+				mHBarEnabled = true;
+			if (mChildExt.y > mContentExt.y)
+				mVBarEnabled = true;
+
+			//Are we now over-scrolled?
+			calcScrollOffset();
+		}
+		// build all the rectangles and such...
+		Point2I zero = mBounds.point.Zero;
+		RectI ctrlRect = applyMargins(zero, mBounds.extent, NormalState, mProfile);
+		RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
+		calcScrollRects(fillRect);
+		calcThumbs();
 	}
-	// build all the rectangles and such...
-	Point2I zero = mBounds.point.Zero;
-	RectI ctrlRect = applyMargins(zero, mBounds.extent, NormalState, mProfile);
-	RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
-	calcScrollRects(fillRect);
-	calcThumbs();
 }
 
 void GuiScrollCtrl::calcContentExtents()
@@ -402,8 +414,10 @@ void GuiScrollCtrl::calcThumbs()
 
 			if (mUseConstantHeightThumb)
 				mHThumbSize = mBaseThumbSize;
-			else
+			else if(mChildExt.x > 0)
 				mHThumbSize = getMax(mBaseThumbSize, S32((mContentExt.x * trackSize) / mChildExt.x));
+			else 
+				mHThumbSize = mBaseThumbSize;
 
 			F32 fraction = (F32)mScrollOffset.x / (F32)totalArea;
 			mHThumbPos = roundf((trackSize - mHThumbSize) * fraction);
@@ -424,8 +438,10 @@ void GuiScrollCtrl::calcThumbs()
 
 			if (mUseConstantHeightThumb)
 				mVThumbSize = mBaseThumbSize;
-			else
+			else if(mChildExt.y > 0)
 				mVThumbSize = getMax(mBaseThumbSize, S32((mContentExt.y * trackSize) / mChildExt.y));
+			else 
+				mVThumbSize = mBaseThumbSize;
 
 			F32 fraction = (F32)mScrollOffset.y / (F32)totalArea;
 			mVThumbPos = roundf((trackSize - mVThumbSize) * fraction);

+ 2 - 0
engine/source/gui/containers/guiScrollCtrl.h

@@ -32,6 +32,8 @@ class GuiScrollCtrl : public GuiControl
 private:
    typedef GuiControl Parent;
    bool mEventBubbled;
+   bool mCalcGuard;
+   bool mResizeGuard;
 
 protected:
 

+ 4 - 1
engine/source/gui/containers/guiTabBookCtrl.cc

@@ -230,7 +230,10 @@ void GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent
 
 void GuiTabBookCtrl::childResized(GuiControl *child)
 {
-   child->resize( Point2I(0,0), mPageRect.extent );
+	if(mPageRect.extent != child->mBounds.extent || child->mBounds.point != Point2I::Zero)
+	{
+		child->resize( Point2I(0,0), mPageRect.extent );
+	}
 }
 
 Point2I GuiTabBookCtrl::getTabLocalCoord(const Point2I &src)

+ 6 - 0
engine/source/gui/containers/guiWindowCtrl.cc

@@ -26,6 +26,7 @@
 #include "gui/guiCanvas.h"
 #include "gui/containers/guiWindowCtrl.h"
 #include "gui/guiDefaultControlRender.h"
+#include "gui/containers/guiFrameSetCtrl.h"
 
 #include "guiWindowCtrl_ScriptBinding.h"
 
@@ -870,6 +871,11 @@ void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEve
 	{
 		return;
 	}
+	GuiFrameSetCtrl* frame = static_cast<GuiFrameSetCtrl*>(parent);
+	if (frame)
+	{
+		return;//Resizing doesn't happen when in a frame set.
+	}
 	Point2I offset = getParent()->localToGlobalCoord(Point2I(0,0));
 	RectI rightRect = RectI( ( ( winRect.extent.x + winRect.point.x ) - mResizeRightWidth + offset.x), winRect.point.y + mTitleHeight + offset.y, mResizeRightWidth, winRect.extent.y );
 	RectI bottomRect = RectI( winRect.point.x + offset.x, ( ( winRect.point.y + winRect.extent.y ) - mResizeBottomHeight) + offset.y, winRect.extent.x , mResizeBottomHeight );

+ 4 - 9
engine/source/gui/editor/guiInspector.cc

@@ -222,15 +222,7 @@ void GuiInspector::resize(const Point2I &newPosition, const Point2I &newExtent)
 
 void GuiInspector::parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent)
 {
-   GuiControl *parent = getParent();
-   if( parent && dynamic_cast<GuiScrollCtrl*>(parent) != NULL )
-   {
-	   // Handle Parent Sizing (We constrain ourself to our parent's width)
-      GuiScrollCtrl *scroll = dynamic_cast<GuiScrollCtrl*>(parent);
-      setWidth(newParentExtent.x - scroll->scrollBarThickness());
-   }
-   else
-      Parent::parentResized(oldParentExtent,newParentExtent);
+   Parent::parentResized(oldParentExtent,newParentExtent);
 }
 
 bool GuiInspector::findExistentGroup( StringTableEntry groupName )
@@ -268,6 +260,9 @@ void GuiInspector::clearGroups()
          (*i)->deleteObject();
 
    mGroups.clear();
+
+   GuiControl* firstRes = getFirstResponder();
+   clearFirstResponder(firstRes);
 }
 
 void GuiInspector::inspectObject( SimObject *object )

+ 98 - 1
engine/source/gui/guiControl.cc

@@ -1807,11 +1807,16 @@ void GuiControl::setFirstResponder()
 }
 
 void GuiControl::clearFirstResponder()
+{
+	clearFirstResponder(this);
+}
+
+void GuiControl::clearFirstResponder(GuiControl* target)
 {
    GuiControl *parent = this;
    while((parent = parent->getParent()) != NULL)
    {
-      if(parent->mFirstResponder == this)
+      if(parent->mFirstResponder == target)
          parent->mFirstResponder = NULL;
       else
          break;
@@ -2324,4 +2329,96 @@ bool GuiControl::isEditMode()
 		}
 		return false;
 	}
+}
+
+//--------------------------------------------------------------------
+
+GuiEasingSupport::GuiEasingSupport()
+{
+	//fill color
+	mEaseFillColorHL = EasingFunction::Linear;
+	mEaseFillColorSL = EasingFunction::Linear;
+	mEaseTimeFillColorHL = 500;
+	mEaseTimeFillColorSL = 0;
+
+	//control state
+	mPreviousState = GuiControlState::DisabledState;
+	mCurrentState = GuiControlState::DisabledState;
+
+	mFluidFillColor = FluidColorI(); //The actual fill color as it moves fluidly from one color to another.
+}
+
+void GuiEasingSupport::initPersistFields()
+{
+	Parent::initPersistFields();
+
+	addGroup("Gui Easing Settings");
+	addField("easeFillColorHL", TypeEnum, Offset(mEaseFillColorHL, GuiEasingSupport), 1, &gEasingTable);
+	addField("easeFillColorSL", TypeEnum, Offset(mEaseFillColorSL, GuiEasingSupport), 1, &gEasingTable);
+	addField("easeTimeFillColorHL", TypeS32, Offset(mEaseTimeFillColorHL, GuiEasingSupport));
+	addField("easeTimeFillColorSL", TypeS32, Offset(mEaseTimeFillColorSL, GuiEasingSupport));
+	endGroup("Gui Easing Settings");
+}
+
+const ColorI& GuiEasingSupport::getFillColor(const GuiControlState state)
+{
+	if (state != mCurrentState)
+	{
+		//We have just switched states!
+		mPreviousState = mCurrentState;
+		mCurrentState = state;
+		if (mCurrentState == GuiControlState::DisabledState || mPreviousState == GuiControlState::DisabledState)
+		{
+			mFluidFillColor.stopFluidAnimation();
+			mFluidFillColor.set(mProfile->getFillColor(state));
+		}
+		else if (mCurrentState == GuiControlState::SelectedState || mPreviousState == GuiControlState::SelectedState)
+		{
+			mFluidFillColor.setEasingFunction(mEaseFillColorSL);
+			mFluidFillColor.setAnimationLength(mEaseTimeFillColorSL);
+			mFluidFillColor.startFluidAnimation(mProfile->getFillColor(state));
+		}
+		else if (mCurrentState == GuiControlState::HighlightState || mPreviousState == GuiControlState::HighlightState)
+		{
+			mFluidFillColor.setEasingFunction(mEaseFillColorHL);
+			mFluidFillColor.setAnimationLength(mEaseTimeFillColorHL);
+			mFluidFillColor.startFluidAnimation(mProfile->getFillColor(state));
+		}
+		else
+		{
+			//we should never get here...
+			mFluidFillColor.stopFluidAnimation();
+			mFluidFillColor.set(mProfile->getFillColor(state));
+		}
+	}
+
+	if (mFluidFillColor.isAnimating() && !isProcessingTicks())
+	{
+		setProcessTicks(true);
+	}
+
+	if (!mFluidFillColor.isAnimating())
+	{
+		mFluidFillColor.set(mProfile->getFillColor(state));
+	}
+
+	return mFluidFillColor;
+}
+
+void GuiEasingSupport::processTick()
+{
+	bool shouldWeContinue = false;
+
+	shouldWeContinue |= mFluidFillColor.processTick();
+
+	if (!shouldWeContinue)
+	{
+		setProcessTicks(false);
+	}
+}
+
+void GuiEasingSupport::setControlProfile(GuiControlProfile* prof)
+{
+	Parent::setControlProfile(prof);
+	mCurrentState = mCurrentState == DisabledState ? NormalState : DisabledState;
 }

+ 38 - 1
engine/source/gui/guiControl.h

@@ -686,7 +686,8 @@ public:
     virtual void setFirstResponder();
 
     /// Clears the first responder for this chain
-    void clearFirstResponder();
+	void clearFirstResponder();
+	void clearFirstResponder(GuiControl* target);
 
     /// Returns the first responder for this chain
     GuiControl *getFirstResponder() { return mFirstResponder; }
@@ -814,6 +815,42 @@ protected:
     VertAlignmentType getVertAlignmentType(GuiControlProfile* profile);
     const ColorI& getFontColor(GuiControlProfile* profile, const GuiControlState state = GuiControlState::NormalState);
 };
+
 /// @}
+/// <summary>
+/// Can be inherited from to add the members and methods needed to easily add smooth 
+/// transitions between fill colors in default rendering. To use this:
+/// 1. First, inherit from this class instead of GuiControl.
+/// 2. In the render function, when it calls renderUniversalRect(), pass in getFillColor() and true to 
+///    override the existing fill color. It should look like:
+///	   renderUniversalRect(ctrlRect, mProfile, currentState, getFillColor(currentState), true);
+/// 
+/// Note that this will only change the fill color used during default rendering.
+/// </summary>
+class GuiEasingSupport : public GuiControl
+{
+private:
+	typedef GuiControl Parent;
+
+protected:
+	FluidColorI mFluidFillColor; //The actual fill color as it moves fluidly from one color to another.
+	GuiControlState mPreviousState;
+	GuiControlState mCurrentState;
+
+public:
+	GuiEasingSupport();
+	static void initPersistFields();
+
+	EasingFunction mEaseFillColorHL; //Transitioning to or from HL (if SL is not involved)
+	EasingFunction mEaseFillColorSL; //Transitioning to or from SL (over HL)
+
+	S32 mEaseTimeFillColorHL;
+	S32 mEaseTimeFillColorSL;
+
+	virtual void setControlProfile(GuiControlProfile* prof);
+	const ColorI& getFillColor(const GuiControlState state); //Returns the fill color based on the state.
+	virtual void processTick();
+	inline const char* getEasingFunctionDescription(const EasingFunction ease) { return mFluidFillColor.getEasingFunctionDescription(ease); };
+};
 
 #endif

+ 1 - 1
engine/source/gui/guiTypes.cc

@@ -929,4 +929,4 @@ ConsoleGetType( TypeGuiProfile )
    GuiControlProfile **obj = (GuiControlProfile**)dptr;
    dSprintf(returnBuffer, sizeof(returnBuffer), "%s", *obj ? (*obj)->getName() ? (*obj)->getName() : (*obj)->getIdString() : "");
    return returnBuffer;
-}
+}