Ver Fonte

GuiFrameSet First Pass

This is the first pass at the frame set control. It basically is able to position its children in a tree. There's also a lot of bug fixes included as I'm now doing things outside of the original intended behavior of the editor. More to come.
Peter Robinson há 2 anos atrás
pai
commit
b3057d435e

+ 21 - 8
editor/EditorCore/Themes/BaseTheme/BaseTheme.cs

@@ -1765,20 +1765,20 @@ function BaseTheme::makeWindowProfile(%this)
 
 	%windowContentBorder = new GuiBorderProfile()
 	{
-		border = 3;
-		borderHL = 3;
-		borderSL = 3;
-		borderNA = 3;
+		border = 1;
+		borderHL = 1;
+		borderSL = 2;
+		borderNA = 1;
 
 		borderColor = %this.adjustValue(%this.color3, -50);
 		borderColorHL = %this.adjustValue(%this.color3, -60);
 		borderColorSL = %this.adjustValue(%this.color3, -60);
 		borderColorNA = %this.adjustValue(%this.color3, -50);
 
-		padding = 1;
-		paddingHL = 1;
-		paddingSL = 1;
-		paddingNA = 1;
+		padding = 3;
+		paddingHL = 3;
+		paddingSL = 2;
+		paddingNA = 3;
 	};
 
 	%windowContentTopBorder = new GuiBorderProfile()
@@ -2037,6 +2037,19 @@ function BaseTheme::makeGuiEditorProfile(%this)
 		align = "center";
 		vAlign = "middle";
 	};
+
+	%this.frameSetProfile = new GuiControlProfile()
+	{
+		fillColor = %this.adjustValue(%this.color1, 10); 
+		fillColorHL = %this.adjustValue(%this.color2, 20); 
+		fillColorSL = %this.color3; 
+		fillColorNA = %this.color2; 
+		
+		borderDefault = %this.emptyBorder;
+
+		tab = false;
+		canKeyFocus = true;
+	};
 }
 
 //Positive values are brighter, negative are darker

+ 65 - 39
editor/GuiEditor/GuiEditor.cs

@@ -34,44 +34,51 @@ function GuiEditor::create( %this )
 
 	%this.guiPage = EditorCore.RegisterEditor("Gui Editor", %this);
 
-    %this.content = new GuiSpriteCtrl() {
+    %this.content = new GuiFrameSetCtrl() {
 		HorizSizing = "width";
         VertSizing = "height";
         Position = "0 0";
         Extent = %this.guiPage.getExtent();
-		imageColor = "255 255 255 255";
-        bitmap = "./images/gridTiny2";
-		singleFrameBitmap = "1";
-		tileImage = "1";
-		positionOffset = "0 0";
-		imageSize = "128 128";
-		fullSize = "0";
-		constrainProportions = "1";
-        isContainer = true;
+        DividerThickness = 6;
 	};
-    ThemeManager.setProfile(%this.content, "emptyProfile");
+    ThemeManager.setProfile(%this.content, "frameSetProfile");
     %this.guiPage.add(%this.content);
 
+    %idList = %this.content.createHorizontalSplit(1);
+    %leftID = getWord(%idList, 0);
+    %rightID = getWord(%idList, 1);
+    %this.content.anchorFrame(%rightID);
+    %this.content.setFrameSize(%rightID, 300);
+    
+    %ids = %this.content.createHorizontalSplit(%leftID);
+    %inspectorFrameID = getWord(%ids, 0);
+    %centerFrameID = getWord(%ids, 1);
+    %this.content.setFrameSize(%inspectorFrameID, 360);
+    
+    %ids = %this.content.createVerticalSplit(%rightID);
+    %toolFrameID = getWord(%ids, 0);
+    %explorerFrameID = getWord(%ids, 1);
+    %this.content.setFrameSize(%toolFrameID, 380);
+
     %this.brain = new GuiEditCtrl()
     {
         Class = "GuiEditorBrain";
 		HorizSizing = "width";
         VertSizing = "height";
         Position = "0 0";
-        Extent = %this.guiPage.getExtent();
+        Extent = "100 100";
     };
     ThemeManager.setProfile(%this.brain, "guiEditorProfile");
-    %this.guiPage.add(%this.brain);
 
-    %this.ctrlListWindow = new GuiWindowCtrl()
+    %this.inspectorWindow = new GuiWindowCtrl()
     {
-        Class = "GuiEditorControlListWindow";
+        Class = "GuiEditorInspectorWindow";
         HorizSizing = "right";
         VertSizing = "bottom";
-        Position = "360 0";
-        Extent = "250 380";
+        Position = "0 0";
+        Extent = "360 380";
         MinExtent = "100 100";
-        text = "Control List";
+        text = "Gui Inspector";
         canMove = true;
         canClose = false;
         canMinimize = true;
@@ -79,22 +86,40 @@ function GuiEditor::create( %this )
         resizeWidth = false;
         resizeHeight = true;
     };
-    ThemeManager.setProfile(%this.ctrlListWindow, "windowProfile");
-    ThemeManager.setProfile(%this.ctrlListWindow, "windowContentProfile", "ContentProfile");
-    ThemeManager.setProfile(%this.ctrlListWindow, "windowButtonProfile", "CloseButtonProfile");
-    ThemeManager.setProfile(%this.ctrlListWindow, "windowButtonProfile", "MinButtonProfile");
-    ThemeManager.setProfile(%this.ctrlListWindow, "windowButtonProfile", "MaxButtonProfile");
-    %this.guiPage.add(%this.ctrlListWindow);
+    ThemeManager.setProfile(%this.inspectorWindow, "windowProfile");
+    ThemeManager.setProfile(%this.inspectorWindow, "windowContentProfile", "ContentProfile");
+    ThemeManager.setProfile(%this.inspectorWindow, "windowButtonProfile", "CloseButtonProfile");
+    ThemeManager.setProfile(%this.inspectorWindow, "windowButtonProfile", "MinButtonProfile");
+    ThemeManager.setProfile(%this.inspectorWindow, "windowButtonProfile", "MaxButtonProfile");
+    %this.content.add(%this.inspectorWindow);
+    %this.inspectorWindow.startListening(%this.brain);
 
-    %this.inspectorWindow = new GuiWindowCtrl()
+    %this.background = new GuiSpriteCtrl() {
+		HorizSizing = "right";
+        VertSizing = "bottom";
+        Position = "0 0";
+        Extent = "100 100";
+		imageColor = "255 255 255 255";
+        bitmap = "./images/gridTiny2";
+		singleFrameBitmap = "1";
+		tileImage = "1";
+		positionOffset = "0 0";
+		imageSize = "128 128";
+		fullSize = "0";
+		constrainProportions = "1";
+	};
+    ThemeManager.setProfile(%this.background, "emptyProfile");
+    %this.content.add(%this.background);
+
+    %this.ctrlListWindow = new GuiWindowCtrl()
     {
-        Class = "GuiEditorInspectorWindow";
+        Class = "GuiEditorControlListWindow";
         HorizSizing = "right";
         VertSizing = "bottom";
-        Position = "0 0";
-        Extent = "360 380";
+        Position = "360 0";
+        Extent = "250 380";
         MinExtent = "100 100";
-        text = "Gui Inspector";
+        text = "Control List";
         canMove = true;
         canClose = false;
         canMinimize = true;
@@ -102,13 +127,12 @@ function GuiEditor::create( %this )
         resizeWidth = false;
         resizeHeight = true;
     };
-    ThemeManager.setProfile(%this.inspectorWindow, "windowProfile");
-    ThemeManager.setProfile(%this.inspectorWindow, "windowContentProfile", "ContentProfile");
-    ThemeManager.setProfile(%this.inspectorWindow, "windowButtonProfile", "CloseButtonProfile");
-    ThemeManager.setProfile(%this.inspectorWindow, "windowButtonProfile", "MinButtonProfile");
-    ThemeManager.setProfile(%this.inspectorWindow, "windowButtonProfile", "MaxButtonProfile");
-    %this.guiPage.add(%this.inspectorWindow);
-    %this.inspectorWindow.startListening(%this.brain);
+    ThemeManager.setProfile(%this.ctrlListWindow, "windowProfile");
+    ThemeManager.setProfile(%this.ctrlListWindow, "windowContentProfile", "ContentProfile");
+    ThemeManager.setProfile(%this.ctrlListWindow, "windowButtonProfile", "CloseButtonProfile");
+    ThemeManager.setProfile(%this.ctrlListWindow, "windowButtonProfile", "MinButtonProfile");
+    ThemeManager.setProfile(%this.ctrlListWindow, "windowButtonProfile", "MaxButtonProfile");
+    %this.content.add(%this.ctrlListWindow);
 
     %this.explorerWindow = new GuiWindowCtrl()
     {
@@ -131,7 +155,7 @@ function GuiEditor::create( %this )
     ThemeManager.setProfile(%this.explorerWindow, "windowButtonProfile", "CloseButtonProfile");
     ThemeManager.setProfile(%this.explorerWindow, "windowButtonProfile", "MinButtonProfile");
     ThemeManager.setProfile(%this.explorerWindow, "windowButtonProfile", "MaxButtonProfile");
-    %this.guiPage.add(%this.explorerWindow);
+    %this.content.add(%this.explorerWindow);
     %this.explorerWindow.startListening(%this.brain);
 
     %this.rootGui = new GuiControl()
@@ -139,11 +163,13 @@ function GuiEditor::create( %this )
         HorizSizing = "width";
         VertSizing = "height";
         Position = "0 0";
-        Extent = %this.content.getExtent();
+        Extent = %this.background.getExtent();
         Profile = GuiDefaultProfile;
         class = "SimulatedCanvas";
     };
-    %this.content.add(%this.rootGui);
+    %this.background.add(%this.rootGui);
+    %this.brain.extent = %this.background.getExtent();
+    %this.background.add(%this.brain);
     %this.fileName = "";
     %this.filePath = "";
     %this.formatIndex = 0;

+ 1 - 8
editor/GuiEditor/scripts/GuiEditorBrain.cs

@@ -9,7 +9,7 @@ function GuiEditorBrain::onControlDragged(%this, %payload, %position)
 {
 	%x = getWord(%position, 0);
 	%y = getWord(%position, 1);
-	%target = GuiEditor.content.findHitControl(%x, %y);
+	%target = %this.root.findHitControl(%x, %y);
 
 	while(! %target.isContainer )
 	{
@@ -28,13 +28,6 @@ function GuiEditorBrain::onControlDropped(%this, %payload, %position)
    %x = getWord(%pos, 0);
    %y = getWord(%pos, 1);
 
-   if(%x < %this.root.position.x || %y < %this.root.poisition.y || 
-    %x > (%this.root.extent.x + %this.root.position.x) || %y > (%this.root.extent.y + %this.root.position.y))
-   {
-      messageBox("Error", "Cannot add a control outside the root gui element!");
-      return;
-   }
-
    %this.addNewCtrl(%payload);
    %payload.setPositionGlobal(%x, %y);
    %this.setFirstResponder();

+ 2 - 2
editor/GuiEditor/scripts/GuiEditorControlListBox.cs

@@ -1,7 +1,7 @@
 
 function GuiEditorControlListBox::onTouchDragged(%this, %index, %text)
 {
-	%position = GuiEditor.guiPage.getGlobalPosition();
+	%position = GuiEditor.brain.getGlobalPosition();
 	%cursorpos = Canvas.getCursorPos();
 
 	%class = %this.getItemText(%this.getSelectedItem());
@@ -32,7 +32,7 @@ function GuiEditorControlListBox::onTouchDragged(%this, %index, %text)
 	};
 
 	%dragCtrl.add(%payload);
-	GuiEditor.guiPage.add(%dragCtrl);
+	GuiEditor.brain.add(%dragCtrl);
 
 	%dragCtrl.startDragging(%xOffset, %yOffset);
 }

+ 3 - 2
engine/compilers/VisualStudio 2022/Torque 2D.vcxproj

@@ -624,7 +624,7 @@
     <ClCompile Include="..\..\source\gui\buttons\guiDropDownCtrl.cc" />
     <ClCompile Include="..\..\source\gui\containers\guiChainCtrl.cc" />
     <ClCompile Include="..\..\source\gui\containers\guiExpandCtrl.cc" />
-    <ClCompile Include="..\..\source\gui\containers\guiFrameCtrl.cc" />
+    <ClCompile Include="..\..\source\gui\containers\guiFrameSetCtrl.cc" />
     <ClCompile Include="..\..\source\gui\containers\guiGridCtrl.cc" />
     <ClCompile Include="..\..\source\gui\containers\guiSceneScrollCtrl.cc" />
     <ClCompile Include="..\..\source\gui\containers\guiTabPageCtrl.cc" />
@@ -1113,7 +1113,8 @@
     <ClInclude Include="..\..\source\gui\containers\guiDragAndDropCtrl_ScriptBinding.h" />
     <ClInclude Include="..\..\source\gui\containers\guiExpandCtrl.h" />
     <ClInclude Include="..\..\source\gui\containers\guiExpandCtrl_ScriptBinding.h" />
-    <ClInclude Include="..\..\source\gui\containers\guiFrameCtrl.h" />
+    <ClInclude Include="..\..\source\gui\containers\guiFrameSetCtrl.h" />
+    <ClInclude Include="..\..\source\gui\containers\guiFrameSetCtrl_ScriptBinding.h" />
     <ClInclude Include="..\..\source\gui\containers\guiGridCtrl.h" />
     <ClInclude Include="..\..\source\gui\containers\guiGridCtrl_ScriptBinding.h" />
     <ClInclude Include="..\..\source\gui\containers\guiPanelCtrl_ScriptBinding.h" />

+ 9 - 6
engine/compilers/VisualStudio 2022/Torque 2D.vcxproj.filters

@@ -1312,9 +1312,6 @@
     <ClCompile Include="..\..\source\2d\editorToy\EditorToyTool.cc">
       <Filter>2d\editorToy</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\source\gui\containers\guiFrameCtrl.cc">
-      <Filter>gui\containers</Filter>
-    </ClCompile>
     <ClCompile Include="..\..\source\gui\buttons\guiDropDownCtrl.cc">
       <Filter>gui\buttons</Filter>
     </ClCompile>
@@ -1342,6 +1339,9 @@
     <ClCompile Include="..\..\source\gui\guiTreeViewCtrl.cc">
       <Filter>gui</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\source\gui\containers\guiFrameSetCtrl.cc">
+      <Filter>gui\containers</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -3016,9 +3016,6 @@
     <ClInclude Include="..\..\source\2d\editorToy\EditorToyTool.h">
       <Filter>2d\editorToy</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\source\gui\containers\guiFrameCtrl.h">
-      <Filter>gui\containers</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\source\gui\guiListBoxCtrl_ScriptBinding.h">
       <Filter>gui</Filter>
     </ClInclude>
@@ -3076,6 +3073,12 @@
     <ClInclude Include="..\..\source\gui\guiTreeViewCtrl_ScriptBinding.h">
       <Filter>gui</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\gui\containers\guiFrameSetCtrl.h">
+      <Filter>gui\containers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\gui\containers\guiFrameSetCtrl_ScriptBinding.h">
+      <Filter>gui\containers</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Torque 2D.rc" />

+ 1 - 1
engine/source/2d/gui/guiSpriteCtrl.cc

@@ -652,7 +652,7 @@ void GuiSpriteCtrl::RenderTiledImage(RectI &bounds, Point2I &start, Point2I &siz
 	{
 		for (int x = 0; x < xdone; ++x)
 		{
-			dstRegion.set((size.x * x) + start.x, (size.y * y) + start.y, size.x, size.y);
+			dstRegion.set((size.x * x) + start.x + bounds.point.x, (size.y * y) + start.y + bounds.point.y, size.x, size.y);
 			dglDrawBitmapStretchSR(texture, dstRegion, srcRegion, false);
 		}
 	}

+ 358 - 0
engine/source/gui/containers/guiFrameSetCtrl.cc

@@ -0,0 +1,358 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "console/consoleTypes.h"
+#include "console/console.h"
+#include "gui/containers/guiFrameSetCtrl.h"
+#include "gui/guiDefaultControlRender.h"
+
+#include "guiFrameSetCtrl_ScriptBinding.h"
+
+void GuiFrameSetCtrl::Frame::resize(const Point2I& newPosition, const Point2I& newExtent)
+{
+	extent.set(newExtent.x, newExtent.y);
+	if (control)
+	{
+		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;
+
+		S32 x1 = newExtent.x;
+		S32 x2 = newExtent.x;
+		if(!isVertical)
+		{	
+			if (child1->isAnchored)
+			{
+				x1 = getMin(child1->extent.x, spaceX - minSize);
+				x2 = spaceX - x1;
+			}
+			else
+			{
+				x2 = getMin(child2->extent.x, spaceX - minSize);
+				x1 = spaceX - x2;
+			}
+		}
+
+		S32 y1 = newExtent.y;
+		S32 y2 = newExtent.y;
+		if (isVertical)
+		{
+			if (child1->isAnchored)
+			{
+				y1 = getMin(child1->extent.y, spaceY - minSize);
+				y2 = spaceY - y1;
+			}
+			else
+			{
+				y2 = getMin(child2->extent.y, spaceY - minSize);
+				y1 = spaceY - y2;
+			}
+		}
+
+		Point2I ext1 = Point2I(x1, y1);
+		Point2I ext2 = Point2I(x2, y2);
+		Point2I pos2 = isVertical ? Point2I(newPosition.x, y1 + owner->mDividerThickness) : Point2I(x1 + owner->mDividerThickness, newPosition.y);
+
+		child1->resize(newPosition, ext1);
+		child2->resize(pos2, ext2);
+	}
+}
+
+GuiFrameSetCtrl::Frame* GuiFrameSetCtrl::Frame::findFrame(const S32 frameID)
+{
+	if (id == frameID)
+	{
+		return this;
+	}
+
+	if (child1 && child2)
+	{
+		Frame* attempt = child1->findFrame(frameID);
+		return attempt ? attempt : child2->findFrame(frameID);
+	}
+	return nullptr;
+}
+
+GuiFrameSetCtrl::Frame* GuiFrameSetCtrl::Frame::findEmptyFrame()
+{
+	if (!control && !child1 && !child2)
+	{
+		return this;
+	}
+	else if (child1 && child2)
+	{
+		Frame* attempt = child1->findEmptyFrame();
+		return attempt ? attempt : child2->findEmptyFrame();
+	}
+	return nullptr;
+}
+
+GuiFrameSetCtrl::Frame* GuiFrameSetCtrl::Frame::twin()
+{
+	if (parent)
+	{
+		if (parent->child1 == this)
+		{
+			return parent->child2;
+		}
+		else
+		{
+			return parent->child1;
+		}
+	}
+	return this;
+}
+
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(GuiFrameSetCtrl);
+
+//------------------------------------------------------------------------------
+
+GuiFrameSetCtrl::GuiFrameSetCtrl()
+{
+	mIsContainer = true;
+	setField("profile", "GuiDefaultProfile");
+
+	mRootFrame = Frame(this, nullptr);
+	mDividerThickness = 8;
+	mNextFrameID = 1;
+	mResizeGuard = false;
+}
+
+//------------------------------------------------------------------------------
+
+void GuiFrameSetCtrl::initPersistFields()
+{
+	Parent::initPersistFields();
+
+	addField("DividerThickness", TypeS32, Offset(mDividerThickness, GuiFrameSetCtrl));
+}
+
+//------------------------------------------------------------------------------
+
+bool GuiFrameSetCtrl::onWake()
+{
+	if (!Parent::onWake())
+		return false;
+
+	return true;
+}
+
+//------------------------------------------------------------------------------
+
+void GuiFrameSetCtrl::onSleep()
+{
+	Parent::onSleep();
+}
+
+//------------------------------------------------------------------------------
+
+void GuiFrameSetCtrl::inspectPostApply()
+{
+	resize(getPosition(), getExtent());
+	Parent::inspectPostApply();
+}
+
+//------------------------------------------------------------------------------
+
+void GuiFrameSetCtrl::resize(const Point2I& newPosition, const Point2I& newExtent)
+{
+	if(!mResizeGuard)//Prevent circular resizing
+	{
+		mResizeGuard = true;
+		Point2I actualNewExtent = Point2I(getMax(mMinExtent.x, newExtent.x),
+			getMax(mMinExtent.y, newExtent.y));
+
+		//call set update both before and after
+		setUpdate();
+	
+		Point2I origin = Point2I::Zero;
+		mRootFrame.resize(origin, actualNewExtent);
+		mBounds.set(newPosition, actualNewExtent);
+
+		GuiControl* parent = getParent();
+		if (parent)
+			parent->childResized(this);
+		setUpdate();
+		mResizeGuard = false;
+	}
+}
+
+bool GuiFrameSetCtrl::onAdd()
+{
+	// Let Parent Do Work.
+	if (!Parent::onAdd())
+		return false;
+
+	// Always expand to fill the parent (we just ignore the points passed in).
+	parentResized(Point2I(), Point2I());
+
+	// Return Success.
+	return true;
+}
+
+void GuiFrameSetCtrl::parentResized(const Point2I& oldParentExtent, const Point2I& newParentExtent)
+{
+	//In the case of centering, we want to make doubly sure we are using the inner rect.
+	GuiControl* parent = getParent();
+	if(parent)
+	{
+		Point2I origin = Point2I(0, 0);
+		Point2I parentInnerExt = getInnerRect(origin, parent->mBounds.extent, NormalState, parent->mProfile).extent;
+
+		resize(origin, parentInnerExt);
+	}
+}
+
+void GuiFrameSetCtrl::onChildAdded(GuiControl* child)
+{
+	//Ensure the child isn't positioned to the center
+	if (child->getHorizSizing() == horizResizeCenter)
+	{
+		child->setHorizSizing(horizResizeLeft);
+	}
+	if (child->getVertSizing() == vertResizeCenter)
+	{
+		child->setVertSizing(vertResizeTop);
+	}
+	resize(getPosition(), getExtent());
+
+	Parent::onChildAdded(child);
+
+	Frame* frame = mRootFrame.findEmptyFrame();
+	if(frame)
+	{
+		frame->control = child;
+	}
+}
+
+void GuiFrameSetCtrl::onChildRemoved(SimObject* child)
+{
+	resize(getPosition(), getExtent());
+}
+
+void GuiFrameSetCtrl::childResized(GuiControl* child)
+{
+	Parent::childResized(child);
+	resize(getPosition(), getExtent());
+}
+
+void GuiFrameSetCtrl::childMoved(GuiControl* child)
+{
+	Parent::childMoved(child);
+	resize(getPosition(), getExtent());
+}
+
+void GuiFrameSetCtrl::childrenReordered()
+{
+	resize(getPosition(), getExtent());
+	Parent::childrenReordered();
+}
+
+Point2I GuiFrameSetCtrl::splitFrame(S32 frameID, bool isVertical)
+{
+	Frame* frame = mRootFrame.findFrame(frameID);
+
+	if(frame)
+	{
+		if (!frame->child1 && !frame->child2)
+		{
+			GuiControl* ctrl = frame->control;
+			frame->child1 = new GuiFrameSetCtrl::Frame(this, frame);
+			frame->child2 = new GuiFrameSetCtrl::Frame(this, frame);
+
+			frame->control = nullptr;
+			frame->child1->control = ctrl;
+			frame->child1->id = ++mNextFrameID;
+			frame->child2->id = ++mNextFrameID;
+			frame->child1->isAnchored = true;
+			frame->child2->isAnchored = false;
+		}
+	
+		frame->isVertical = isVertical;
+		return Point2I(static_cast<S32>(frame->child1->id), static_cast<S32>(frame->child2->id));
+	}
+	return Point2I::Zero;
+}
+
+void GuiFrameSetCtrl::anchorFrame(S32 frameID)
+{
+	Frame* frame = mRootFrame.findFrame(frameID);
+
+	if (frame && frame != &mRootFrame)//The root frame has no twin and can't be anchored.
+	{
+		frame->isAnchored = true;
+		frame->twin()->isAnchored = false;
+	}
+}
+
+void GuiFrameSetCtrl::setFrameSize(S32 frameID, S32 size)
+{
+	Frame* frame = mRootFrame.findFrame(frameID);
+
+	if (frame && frame != &mRootFrame)//The root frame must be the size of the control.
+	{
+		if (frame->parent->isVertical)
+		{
+			frame->extent.y = size;
+			frame->twin()->extent.y = getMax(frame->parent->extent.y - (size + mDividerThickness), minSize);
+		}
+		else
+		{
+			frame->extent.x = size;
+			frame->twin()->extent.x = getMax(frame->parent->extent.x - (size + mDividerThickness), minSize);
+		}
+		//Pass this down to the other frames and then to the child controls
+		resize(getPosition(), getExtent());
+	}
+}
+
+void GuiFrameSetCtrl::onRender(Point2I offset, const RectI& updateRect)
+{
+	RectI ctrlRect = applyMargins(offset, mBounds.extent, NormalState, mProfile);
+
+	if (!ctrlRect.isValidRect())
+	{
+		return;
+	}
+
+	renderUniversalRect(ctrlRect, mProfile, NormalState);
+
+	//Render Text
+	dglSetBitmapModulation(getFontColor(mProfile));
+	RectI fillRect = applyBorders(ctrlRect.point, ctrlRect.extent, NormalState, mProfile);
+	RectI contentRect = applyPadding(fillRect.point, fillRect.extent, NormalState, mProfile);
+
+	if (contentRect.isValidRect())
+	{
+		renderText(contentRect.point, contentRect.extent, mText, mProfile);
+
+		//Render the childen
+		renderChildControls(offset, contentRect, updateRect);
+	}
+}

+ 82 - 0
engine/source/gui/containers/guiFrameSetCtrl.h

@@ -0,0 +1,82 @@
+#ifndef _GUIFRAMESETCTRL_H_
+#define _GUIFRAMESETCTRL_H_
+
+#ifndef _GUICONTROL_H_
+#include "gui/guiControl.h"
+#endif
+
+#ifndef _VECTOR2_H_
+#include "2d/core/Vector2.h"
+#endif
+
+#include "graphics/dgl.h"
+#include "console/console.h"
+#include "console/consoleTypes.h"
+
+class GuiFrameSetCtrl : public GuiControl
+{
+private:
+	typedef GuiControl Parent;
+	U32 mNextFrameID;
+	bool mResizeGuard;
+
+public:
+	class Frame
+	{
+	public:
+		Frame() : owner(nullptr), parent(nullptr), child1(nullptr), child2(nullptr),
+			control(nullptr), isVertical(true), id(1), extent(Point2I(100, 100)) { }
+		Frame(GuiFrameSetCtrl* theOwner, Frame* theParent) : owner(theOwner), parent(theParent), child1(nullptr), child2(nullptr), 
+			control(nullptr), isVertical(true), id(1), extent(Point2I(100, 100)) { }
+		virtual ~Frame() { }
+
+		bool isVertical;
+		U32 id;
+		Point2I extent;
+		bool isAnchored;
+
+		GuiFrameSetCtrl* owner;
+		Frame* parent;
+		Frame* child1;
+		Frame* child2;
+
+		GuiControl* control;
+
+		void resize(const Point2I& newPosition, const Point2I& newExtent);
+		Frame* findFrame(const S32 frameID);
+		Frame* findEmptyFrame();
+		Frame* twin();
+	};
+	Frame mRootFrame;
+	U8 mDividerThickness; 
+	const S32 minSize = 20;
+
+
+private:
+
+public:
+	GuiFrameSetCtrl();
+	static void initPersistFields();
+	
+	bool onAdd();
+	virtual void parentResized(const Point2I& oldParentExtent, const Point2I& newParentExtent);
+	void resize(const Point2I& newPosition, const Point2I& newExtent);
+	//void childResized(GuiControl *child);
+	void inspectPostApply();
+	bool onWake();
+	void onSleep();
+	void onChildAdded(GuiControl* child);
+	void onChildRemoved(SimObject* child);
+	void childResized(GuiControl* child);
+	void childMoved(GuiControl* child);
+	void childrenReordered();
+
+	Point2I splitFrame(S32 frameID, bool isVertical);
+	void anchorFrame(S32 frameID);
+	void setFrameSize(S32 frameID, S32 size);
+	void onRender(Point2I offset, const RectI& updateRect);
+
+	DECLARE_CONOBJECT(GuiFrameSetCtrl);
+};
+
+#endif // _GUIFRAMESETCTRL_H_

+ 70 - 0
engine/source/gui/containers/guiFrameSetCtrl_ScriptBinding.h

@@ -0,0 +1,70 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethodGroupBeginWithDocs(GuiFrameSetCtrl, GuiControl)
+
+/*! Splits the frame with the frameID given into two frames with one on top and one on bottom. The existing control will move to the first frame.
+	@param frameID The frameID that should be split.
+	@return (int frameID1/int frameID2) The resulting two frameIDs seperated by a space.
+*/
+ConsoleMethodWithDocs(GuiFrameSetCtrl, createVerticalSplit, ConsoleString, 3, 3, (int frameID))
+{
+	Point2I point = object->splitFrame(dAtoi(argv[2]), true);
+	char* retBuffer = Con::getReturnBuffer(64);
+	dSprintf(retBuffer, 64, "%d %d", point.x, point.y);
+
+	return retBuffer;
+}
+
+/*! Splits the frame with the frameID given into two frames with one on the left and one on the right. The existing control will move to the first frame.
+	@param frameID The frameID that should be split.
+	@return (int frameID1/int frameID2) The resulting two frameIDs seperated by a space.
+*/
+ConsoleMethodWithDocs(GuiFrameSetCtrl, createHorizontalSplit, ConsoleString, 3, 3, (int frameID))
+{
+	Point2I point = object->splitFrame(dAtoi(argv[2]), false);
+	char* retBuffer = Con::getReturnBuffer(64);
+	dSprintf(retBuffer, 64, "%d %d", point.x, point.y);
+
+	return retBuffer;
+}
+
+/*! This anchors a frame and unachors it's twin. An anchored frame tries to stay the same size when its parent resizes. It can still be resized by the user.
+	@param frameID The frameID that should be anchored.
+	@return No return value.
+*/
+ConsoleMethodWithDocs(GuiFrameSetCtrl, anchorFrame, ConsoleVoid, 3, 3, (int frameID))
+{
+	object->anchorFrame(dAtoi(argv[2]));
+}
+
+/*! Sets the width or height of a frame depending on if its parent is split horizontally or vertically.
+	@param frameID The frameID that should be set.
+	@param size The size in pixels to set the width or height to.
+	@return No return value.
+*/
+ConsoleMethodWithDocs(GuiFrameSetCtrl, setFrameSize, ConsoleVoid, 4, 4, (int frameID, int size))
+{
+	object->setFrameSize(dAtoi(argv[2]), dAtoi(argv[3]));
+}
+
+ConsoleMethodGroupEndWithDocs(GuiFrameSetCtrl)

+ 5 - 0
engine/source/gui/containers/guiTabPageCtrl.cc

@@ -163,4 +163,9 @@ void GuiTabPageCtrl::onRender(Point2I offset, const RectI &updateRect)
 		//Render the childen
 		renderChildControls(offset, contentRect, updateRect);
 	}
+}
+
+void GuiTabPageCtrl::parentResized(const Point2I& oldParentExtent, const Point2I& newParentExtent)
+{
+	//Do nothing. If the parent of a page resized then it will tell us what size to be later.
 }

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

@@ -45,6 +45,7 @@ class GuiTabPageCtrl : public GuiControl
 
       virtual void setText(const char *txt = NULL); ///< Override setText function to signal parent we need to update.
 	  void onRender(Point2I offset, const RectI &updateRect);
+	  void parentResized(const Point2I& oldParentExtent, const Point2I& newParentExtent);
 };
 
 #endif //_GUITABPAGECTRL_H_

+ 2 - 1
engine/source/gui/editor/guiEditCtrl.cc

@@ -537,7 +537,8 @@ void GuiEditCtrl::onRender(Point2I offset, const RectI &updateRect)
       }
    }
 
-   renderChildControls(offset, mBounds, updateRect);
+   RectI contentRect = RectI(offset, mBounds.extent);
+   renderChildControls(offset, contentRect, updateRect);
 
    if(mActive && mCurrentAddSet && mUseGridSnap && 
        (mMouseDownMode == MovingSelection || mMouseDownMode == SizingSelection))

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

@@ -515,7 +515,7 @@ public:
 	virtual void onGroupRemove();
 
     /// Called when this object is added to the scene
-    bool onAdd();
+    virtual bool onAdd();
 
     /// Called when this object has a new child. Congratulations!
     virtual void onChildAdded( GuiControl *child );