summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Studio/Controls/FlowLayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Studio/Controls/FlowLayout.cpp')
-rw-r--r--src/Authoring/Studio/Controls/FlowLayout.cpp1009
1 files changed, 1009 insertions, 0 deletions
diff --git a/src/Authoring/Studio/Controls/FlowLayout.cpp b/src/Authoring/Studio/Controls/FlowLayout.cpp
new file mode 100644
index 00000000..9864321d
--- /dev/null
+++ b/src/Authoring/Studio/Controls/FlowLayout.cpp
@@ -0,0 +1,1009 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "FlowLayout.h"
+#include "Renderer.h"
+#include "MasterP.h"
+#include "ControlData.h"
+#include "CoreUtils.h"
+#include "UICMath.h"
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+using namespace Q3DStudio::Control;
+
+//=============================================================================
+/**
+ * Checks to see if this has hit it's max X
+ */
+bool CanBeExpandedX(CControl *inControl)
+{
+ bool theRetVal = false;
+ CPt theSize = inControl->GetSize();
+ CPt theMaxSize = inControl->GetMaximumSize();
+ if (theSize.x < theMaxSize.x) {
+ theRetVal = true;
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Checks to see if this has hit it's max Y
+ */
+bool CanBeExpandedY(CControl *inControl)
+{
+ bool theRetVal = false;
+ CPt theSize = inControl->GetSize();
+ CPt theMaxSize = inControl->GetMaximumSize();
+ if (theSize.y < theMaxSize.y) {
+ theRetVal = true;
+ }
+ return theRetVal;
+}
+
+IMPLEMENT_OBJECT_COUNTER(CFlowLayout)
+
+//=============================================================================
+/**
+ * Constructs a new FlowLayout flowing vertically (down).
+ */
+CFlowLayout::CFlowLayout(CControl *inControl, bool inUseControl)
+ : m_FlowDirection(FLOW_VERTICAL)
+ , m_VerticalAlignment(ALIGN_TOP)
+ , m_HorizontalAlignment(ALIGN_LEFT)
+ , m_ResizingChildren(false)
+ , m_LeftMargin(0)
+ , m_RightMargin(0)
+ , m_TopMargin(0)
+ , m_BottomMargin(0)
+ , m_ChildGap(0)
+ , m_DebugFlag(false)
+ , m_AutoMin(true)
+ , m_SuspendRecalcLayout(false)
+{
+ ADDTO_OBJECT_COUNTER(CFlowLayout)
+
+ m_BlankDisable = false;
+ m_HasUnusedSpace = false;
+
+ if (inControl != nullptr) {
+ m_BlankControl = inControl;
+ } else if (inUseControl) {
+ m_BlankControl = new CBlankControl();
+ } else {
+ m_BlankControl = nullptr;
+ }
+
+ SetMaximumSize(CPt(LONG_MAX, LONG_MAX));
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CFlowLayout::~CFlowLayout()
+{
+ delete m_BlankControl;
+
+ REMOVEFROM_OBJECT_COUNTER(CFlowLayout)
+}
+
+//=============================================================================
+/**
+ * The left margin is the gap on the left side of the flow layout where no
+ * controls are drawn. The default left margin of a flow layout is zero. If
+ * you have a horizontal flow layout with left alignment, you can use the margin
+ * to push all the child controls over by a fixed amount.
+ * @param inMargin Width in pixels of the left margin
+ * @return the previous width of the left margin
+ */
+long CFlowLayout::SetLeftMargin(long inMargin)
+{
+ long theOldMargin = m_LeftMargin;
+ m_LeftMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * The left margin is the gap on the left side of the flow layout where no
+ * controls are drawn. The default left margin of a flow layout is zero. Only
+ * valid with a horizontal flow layout with left alignment.
+ * @return the gap on the left side of this flow, in pixels
+ */
+long CFlowLayout::GetLeftMargin() const
+{
+ return m_LeftMargin;
+}
+
+long CFlowLayout::GetRightMargin() const
+{
+ return m_RightMargin;
+}
+
+long CFlowLayout::GetTopMargin() const
+{
+ return m_TopMargin;
+}
+
+long CFlowLayout::GetBottomMargin() const
+{
+ return m_BottomMargin;
+}
+
+//=============================================================================
+/**
+ * Disables the drawing of blank rectangles in blank areas on the screen
+ * @param NONE
+ * @return NONE
+ */
+void CFlowLayout::DisableBlank()
+{
+ m_BlankDisable = true;
+}
+
+//=============================================================================
+/**
+ * The right margin is the gap on the rgiht side of the flow layout where no
+ * controls are drawn. The default right margin of a flow layout is zero. If
+ * you have a horizontal flow layout with right alignment, you can use the margin
+ * to push all the child controls over by a fixed amount.
+ * @param inMargin Width in pixels of the right margin
+ * @return the previous width of the right margin
+ */
+long CFlowLayout::SetRightMargin(long inMargin)
+{
+ long theOldMargin = m_RightMargin;
+ m_RightMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * The top margin is the gap on the top of the flow layout where no controls
+ * are drawn. The default top margin of a flow layout is zero. If you have
+ * a vertical flow layout with top alignment, you can use the margin to push
+ * all the child controls down by a fixed amount.
+ * @param inMargin Height in pixels of the top margin
+ * @return the previous height of the top margin
+ */
+long CFlowLayout::SetTopMargin(long inMargin)
+{
+ long theOldMargin = m_TopMargin;
+ m_TopMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * The bottom margin is the gap on the bottom of the flow layout where no
+ * controls are drawn. The default bottom margin of a flow layout is zero. If
+ * you have a vertical flow layout with bottom alignment, you can use the margin
+ * to push all the child controls up by a fixed amount.
+ * @param inMargin Height in pixels of the bottom margin
+ * @return the previous height of the bottom margin
+ */
+long CFlowLayout::SetBottomMargin(long inMargin)
+{
+ long theOldMargin = m_BottomMargin;
+ m_BottomMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * This functions sets a fixed gap between children of the flow layout.
+ * @param inGap pixel gap between each child of the flow layout
+ * @return the previous pixel gap between children
+ */
+long CFlowLayout::SetGapBetweenChildren(long inGap)
+{
+ long theOldGap = m_ChildGap;
+ m_ChildGap = inGap;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldGap;
+}
+
+//=============================================================================
+/**
+ * Get the preferred size of this control.
+ * The preferred size is the sum of all the preferred sizes of all the children.
+ * @return the preferred size of this control.
+ */
+// CPt CFlowLayout::GetPreferredSize( )
+//{
+// return m_PreferredSize;
+//}
+
+//=============================================================================
+/**
+ * Get the minimum size of this control.
+ * The minimum size is the sum of all the minimum sizes of all the children in
+ * the flow direction, and the largest minimum size in the non-flow direction.
+ * @return the minimum size of this control.
+ */
+// CPt CFlowLayout::GetMinimumSize( )
+//{
+// return m_MinimumSize;
+//}
+
+//=============================================================================
+/**
+ * Get the maximum size of this control.
+ * The maximum size is the sum of all the maximum sizes of all the children in
+ * the flow direction, and the smallest maximum size in the non-flow direction.
+ * @return the maximum size of this control.
+ */
+// CPt CFlowLayout::GetMaximumSize( )
+//{
+// return m_MaximumSize;
+//}
+
+//=============================================================================
+/**
+ * Set the direction the components are flowing.
+ * Vertical means the components will be positioned one on top of the other,
+ * starting at the top and flowing down.
+ * Horizontal means the components will be positions side by side, starting on
+ * the left and flowing right.
+ * @param inFlowDirection the new flow direction.
+ */
+void CFlowLayout::SetFlowDirection(EFlowDirection inFlowDirection)
+{
+ if (inFlowDirection != m_FlowDirection) {
+ m_FlowDirection = inFlowDirection;
+
+ RecalcLayout();
+ }
+}
+
+//=============================================================================
+/**
+ * Add a child control to this control.
+ * This adds the child control to this and includes it in the layout.
+ * @param inControl the control to be added.
+ * @param inInsertBefore the location to insert the control, nullptr = end.
+ */
+void CFlowLayout::AddChild(CControl *inControl, CControl *inInsertBefore /*= nullptr*/)
+{
+ CControl::AddChild(inControl, inInsertBefore);
+
+ if (GetParent() != nullptr) {
+ ResetMinMaxPref();
+ GetParent()->OnChildSizeChanged(this);
+ }
+
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Remove a child from this control.
+ * @param inControl the control to be removed.
+ */
+void CFlowLayout::RemoveChild(CControl *inControl)
+{
+ CControl::RemoveChild(inControl);
+
+ if (GetParent() != nullptr) {
+ ResetMinMaxPref();
+ GetParent()->OnChildSizeChanged(this);
+ }
+
+ RecalcLayout();
+}
+
+/**
+ * Sets the flag on whether to suspend the recalculation of the layout.
+ * Recalc is expensive, especially when there are many children.
+ * if you need to add lots of children items to this control, do this:
+ * SuspendRecalcLayout( true ); AddLotsOFItems( ); SuspendRecalcLayout( false );
+ * DoSomethingThatWillTriggerRecalcLayout( );
+ * @param inSuspendFlag true would suspend recalc
+ * @see RecalcLayout
+ * @see CListBoxControl
+*/
+void CFlowLayout::SuspendRecalcLayout(bool inSuspendFlag)
+{
+ m_SuspendRecalcLayout = inSuspendFlag;
+}
+
+//=============================================================================
+/**
+ * Recalculate the positions of all the child controls of this control.
+ * This does the work of all the layout of the children and figures out where
+ * everything belongs.
+ */
+void CFlowLayout::RecalcLayout()
+{
+ UICPROFILE(RecalcLayout);
+
+ // if you need to add lots of children items to this control, do this:
+ // SuspendRecalcLayout( true ); AddLotsOFItems( ); SuspendRecalcLayout( false );
+ // DoSomethingThatWillTriggerRecalcLayout( );
+ if (m_SuspendRecalcLayout)
+ return;
+
+ m_ResizingChildren = true;
+
+ CPt thePosition(0, 0);
+ CPt theCurSize = GetSize();
+ long theNumExpandableY = 0;
+ long theNumExpandableX = 0;
+ m_HasUnusedSpace = false;
+
+ if ((m_FlowDirection == FLOW_HORIZONTAL && m_HorizontalAlignment != ALIGN_RIGHT)
+ || (m_FlowDirection == FLOW_VERTICAL && m_VerticalAlignment != ALIGN_BOTTOM)) {
+ thePosition.x = m_LeftMargin;
+ thePosition.y = m_TopMargin;
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = *thePos;
+ RecalcChild(theChild->GetControl(), thePosition, theNumExpandableX, theNumExpandableY);
+
+ if (m_FlowDirection == FLOW_HORIZONTAL)
+ thePosition += CPt(m_ChildGap, 0);
+ else
+ thePosition += CPt(0, m_ChildGap);
+ }
+ } else {
+ // thePosition.x = m_RightMargin;
+ // thePosition.y = m_BottomMargin;
+ ControlGraph::SReverseIterator thePos = GetReverseChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = *thePos;
+ RecalcChild(theChild->GetControl(), thePosition, theNumExpandableX, theNumExpandableY);
+
+ if (m_FlowDirection == FLOW_HORIZONTAL)
+ thePosition -= CPt(m_ChildGap, 0);
+ else
+ thePosition -= CPt(0, m_ChildGap);
+ }
+ }
+
+ // If there is extra space and there are items able to expand still
+ if (thePosition.y < theCurSize.y && m_FlowDirection == FLOW_VERTICAL) {
+ if (theNumExpandableY > 0)
+ ResizeExpandableControlsY((theCurSize.y - thePosition.y), theNumExpandableY);
+ else
+ m_HasUnusedSpace = true;
+
+ // SDJ: See below
+ // ResetChildPositions( );
+ } else if (thePosition.x < theCurSize.x && m_FlowDirection == FLOW_HORIZONTAL) {
+ if (theNumExpandableX > 0)
+ ResizeExpandableControlsX((theCurSize.x - thePosition.x), theNumExpandableX);
+ else
+ m_HasUnusedSpace = true;
+
+ // SDJ: See below
+ // ResetChildPositions( );
+ }
+
+ // SDJ: There is a case where the top margin is not properly calculated when
+ // neither of the above conditions is met. Calling ResetChildPositions fixes
+ // this, but might be ineffiecient. If you attempt to fix this, replace the
+ // commented out calls to ResetChildPositions above.
+ ResetChildPositions();
+
+ m_ResizingChildren = false;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * XXX
+ */
+void CFlowLayout::RecalcChild(CControl *inChild, CPt &ioPosition, long &ioNumExpandableX,
+ long &ioNumExpandableY)
+{
+ CPt theTotalPrefSizes = GetTotalPreferredSizes();
+ CPt theCurSize = GetSize();
+ CPt theChildSize = theCurSize;
+
+ theCurSize.x -= m_RightMargin;
+ theCurSize.y -= m_BottomMargin;
+
+ if (inChild->IsVisible()) {
+ inChild->ResetMinMaxPref();
+ CPt thePrefSize = inChild->GetPreferredSize();
+ CPt theMinSize = inChild->GetMinimumSize();
+ CPt theMaxSize = inChild->GetMaximumSize();
+
+ if (theMaxSize.x > theCurSize.x - ioPosition.x)
+ theMaxSize.x = theCurSize.x - ioPosition.x;
+ if (theMaxSize.x < 0)
+ theMaxSize.x = 0;
+ if (theMaxSize.y > theCurSize.y - ioPosition.y)
+ theMaxSize.y = theCurSize.y - ioPosition.y;
+ if (theMaxSize.y < 0)
+ theMaxSize.y = 0;
+
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ CPt theChildPosition;
+ theChildPosition.y = ioPosition.y;
+ theChildPosition.x = 0;
+ theChildSize.x = theCurSize.x;
+ ///*
+ // SDJ; Why is this commented out?
+ switch (m_HorizontalAlignment) {
+ case ALIGN_LEFT:
+ theChildPosition.x = 0;
+ break;
+
+ case ALIGN_RIGHT:
+ theChildPosition.x =
+ (GetSize().x - (m_LeftMargin + m_RightMargin)) - inChild->GetSize().x;
+ break;
+
+ case ALIGN_MIDDLE:
+ theChildPosition.x = (GetSize().x / 2) - (inChild->GetSize().x / 2);
+ break;
+
+ case ALIGN_HORIZ_NEITHER:
+ // NO BREAK
+ default:
+ theChildPosition.x = inChild->GetPosition().x;
+ break;
+ }
+ //*/
+
+ inChild->SetPosition(theChildPosition);
+
+ if (theTotalPrefSizes.y) {
+ float theModifier = ((float)thePrefSize.y) / ((float)theTotalPrefSizes.y);
+ // Check to see if they have hit their min or max
+ if (theMinSize.y > ::dtol(theCurSize.y * theModifier)) {
+ theChildSize.y = theMinSize.y;
+ ++ioNumExpandableY;
+
+ } else if (theMaxSize.y < ::dtol(theCurSize.y * theModifier)) {
+ theChildSize.y = theMaxSize.y;
+ } else {
+ // This item can be expanded
+ ++ioNumExpandableY;
+ theChildSize.y = inChild->GetSize().y;
+ }
+ }
+
+ // If the child cannot be expanded, maintain its current width
+ // if ( !CanBeExpandedX( inChild ) )
+ // theChildSize.x = inChild->GetSize( ).x;
+
+ theChildSize.x = Q3DStudio::MAX(theChildSize.x, theMinSize.x);
+ theChildSize.y = Q3DStudio::MAX(theChildSize.y, theMinSize.y);
+
+ theChildSize.x = Q3DStudio::MIN(theChildSize.x, theMaxSize.x);
+ theChildSize.y = Q3DStudio::MIN(theChildSize.y, theMaxSize.y);
+
+ inChild->SetSize(theChildSize);
+ ioPosition.y += theChildSize.y;
+ } else {
+ CPt theChildPosition;
+ theChildPosition.x = ioPosition.x;
+
+ switch (m_VerticalAlignment) {
+ case ALIGN_TOP:
+ theChildPosition.y = m_TopMargin;
+ break;
+
+ case ALIGN_BOTTOM:
+ theChildPosition.y = GetSize().y - inChild->GetSize().y;
+ break;
+
+ case ALIGN_CENTER:
+ theChildPosition.y = (GetSize().y / 2) - (inChild->GetSize().y / 2);
+ break;
+
+ case ALIGN_VERT_NEITHER:
+ // NO BREAK
+ default:
+ theChildPosition.y = inChild->GetPosition().y;
+ break;
+ }
+
+ inChild->SetPosition(theChildPosition);
+
+ if (theTotalPrefSizes.x) {
+ float theModifier = ((float)thePrefSize.x) / ((float)theTotalPrefSizes.x);
+ // Check to see if they have hit their min or max
+ if (theMinSize.x > ::dtol(theCurSize.x * theModifier)) {
+ theChildSize.x = theMinSize.x;
+ ++ioNumExpandableX;
+
+ } else if (theMaxSize.x < ::dtol(theCurSize.x * theModifier)) {
+ theChildSize.x = theMaxSize.x;
+ } else {
+ // This item can be expanded
+ theChildSize.x = ::dtol((theCurSize.x * theModifier));
+ ++ioNumExpandableX;
+ }
+ }
+
+ // If the child cannot by expanded, maintain the same height
+ if (!CanBeExpandedY(inChild))
+ theChildSize.y = inChild->GetSize().y;
+
+ theChildSize.x = Q3DStudio::MAX(theChildSize.x, theMinSize.x);
+ theChildSize.y = Q3DStudio::MAX(theChildSize.y, theMinSize.y);
+
+ theChildSize.x = Q3DStudio::MIN(theChildSize.x, theMaxSize.x);
+ theChildSize.y = Q3DStudio::MIN(theChildSize.y, theMaxSize.y);
+
+ inChild->SetSize(theChildSize);
+ ioPosition.x += theChildSize.x;
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Resets all child positions accd to their size
+ */
+void CFlowLayout::ResetChildPositions()
+{
+ if ((m_FlowDirection == FLOW_HORIZONTAL && m_HorizontalAlignment != ALIGN_RIGHT)
+ || (m_FlowDirection == FLOW_VERTICAL && m_VerticalAlignment != ALIGN_BOTTOM)) {
+ CPt thePosition(m_LeftMargin, m_TopMargin);
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ thePosition.x = theChild->GetPosition().x + m_LeftMargin;
+ theChild->SetPosition(thePosition);
+ thePosition.y = thePosition.y + theChildSize.y + m_ChildGap;
+ } else {
+ thePosition.y = theChild->GetPosition().y + m_TopMargin;
+ theChild->SetPosition(thePosition);
+ thePosition.x = thePosition.x + theChildSize.x + m_ChildGap;
+ }
+ }
+ }
+
+ if (m_HasUnusedSpace && m_BlankControl) {
+ CPt theBlankPosition;
+ if (m_FlowDirection == FLOW_HORIZONTAL) {
+ theBlankPosition.x = thePosition.x;
+ theBlankPosition.y = 0;
+ } else {
+ theBlankPosition.x = 0;
+ theBlankPosition.y = thePosition.y;
+ }
+ m_BlankControl->SetPosition(theBlankPosition);
+ m_BlankControl->SetSize(GetSize() - theBlankPosition);
+ }
+ } else {
+ CPt thePosition(GetSize() - CPt(m_RightMargin, m_BottomMargin));
+ ControlGraph::SReverseIterator thePos = GetReverseChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ thePosition.x = theChild->GetPosition().x - m_RightMargin;
+ thePosition.y = thePosition.y - theChildSize.y - m_ChildGap;
+ theChild->SetPosition(thePosition);
+ } else {
+ thePosition.y = theChild->GetPosition().y - m_BottomMargin;
+ thePosition.x = thePosition.x - theChildSize.x - m_ChildGap;
+ theChild->SetPosition(thePosition);
+ }
+ }
+ }
+
+ if (m_HasUnusedSpace && m_BlankControl) {
+ CPt theBlankSize;
+ if (m_FlowDirection == FLOW_HORIZONTAL) {
+ theBlankSize.x = thePosition.x;
+ theBlankSize.y = GetSize().y;
+ } else {
+ theBlankSize.x = GetSize().x;
+ theBlankSize.y = thePosition.y;
+ }
+ m_BlankControl->SetPosition(CPt(0, 0));
+ m_BlankControl->SetSize(theBlankSize);
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Resizes any expandable Controls in the Y Dir
+ *
+ * @param inExtraSpace the Amount left
+ * @param inNumExpandable used to calc percentages
+ */
+void CFlowLayout::ResizeExpandableControlsY(long inExtraSpace, long inNumExpandable)
+{
+ m_HasUnusedSpace = false;
+ long theSpaceDistributed = 0;
+ if (inExtraSpace / inNumExpandable < 1) {
+ this->DistributeRemaining(inExtraSpace);
+ } else {
+ ControlGraph::SIterator thePos = GetChildren();
+ long theNumExpandable = 0;
+
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedY(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theMaxSize = theChild->GetMaximumSize();
+ CPt theChildSize = theChild->GetSize();
+ float theModifier = (float)1 / inNumExpandable;
+ if (theMaxSize.y < ::dtol((inExtraSpace * theModifier) + theChildSize.y)) {
+ theSpaceDistributed += theMaxSize.y - theChildSize.y;
+ theChildSize.y = theMaxSize.y;
+ } else {
+ theSpaceDistributed += ::dtol((inExtraSpace * theModifier));
+ theChildSize.y += ::dtol((inExtraSpace * theModifier));
+ ++theNumExpandable;
+ }
+
+ theChild->SetSize(theChildSize);
+ }
+ }
+
+ // If there is extra space and there are items able to expand still
+ if ((inExtraSpace - theSpaceDistributed) > 0) {
+ if (theNumExpandable > 0) {
+ this->ResizeExpandableControlsY((inExtraSpace - theSpaceDistributed),
+ theNumExpandable);
+ } else {
+ m_HasUnusedSpace = true;
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Resizes any expandable Controls in the X Dir
+ *
+ * @param inExtraSpace the Amount left
+ * @param inNumExpandable used to calc percentages
+ */
+void CFlowLayout::ResizeExpandableControlsX(long inExtraSpace, long inNumExpandable)
+{
+ m_HasUnusedSpace = false;
+ long theSpaceDistributed = 0;
+ if (inExtraSpace / inNumExpandable < 1) {
+ this->DistributeRemaining(inExtraSpace);
+ } else {
+ long theNumExpandable = 0;
+ ControlGraph::SIterator thePos = GetChildren();
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedX(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theMaxSize = theChild->GetMaximumSize();
+ CPt theChildSize = theChild->GetSize();
+ float theModifier = (float)1 / inNumExpandable;
+ if (theMaxSize.x < ::dtol((inExtraSpace * theModifier) + theChildSize.x)) {
+ theSpaceDistributed += theMaxSize.x - theChildSize.x;
+ theChildSize.x = theMaxSize.x;
+ } else {
+ theSpaceDistributed += ::dtol((inExtraSpace * theModifier));
+ theChildSize.x += ::dtol((inExtraSpace * theModifier));
+ ++theNumExpandable;
+ }
+
+ theChild->SetSize(theChildSize);
+ }
+ }
+
+ // If there is extra space and there are items able to expand still
+ if ((inExtraSpace - theSpaceDistributed) > 0) {
+ if (theNumExpandable > 0) {
+ this->ResizeExpandableControlsX((inExtraSpace - theSpaceDistributed),
+ theNumExpandable);
+ } else {
+ m_HasUnusedSpace = true;
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Distributes remaining size to items that can be expanded.
+ * this should only be called when theNumExpandable items > the extra space
+ */
+void CFlowLayout::DistributeRemaining(long inExtraSpace)
+{
+ long theNumToDistribute = inExtraSpace;
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ ControlGraph::SIterator thePos = GetChildren();
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext() && theNumToDistribute > 0; ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedY(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ theChildSize.y++;
+ theChild->SetSize(theChildSize.x, theChildSize.y);
+ theNumToDistribute--;
+ }
+ }
+ } else {
+ ControlGraph::SIterator thePos = GetChildren();
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext() && theNumToDistribute > 0; ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedX(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ theChildSize.x++;
+ theChild->SetSize(theChildSize.x, theChildSize.y);
+ theNumToDistribute--;
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Gets the total prefered sizes of the children
+ */
+CPt CFlowLayout::GetTotalPreferredSizes()
+{
+ CPt theTotalPrefSizes(0, 0);
+
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ theTotalPrefSizes += theChild->GetPreferredSize();
+ }
+ }
+
+ return theTotalPrefSizes;
+}
+
+//==============================================================================
+/**
+ * sets the size and does a recalc
+ */
+void CFlowLayout::SetSize(CPt inSize)
+{
+ if (inSize != GetSize()) {
+ CControl::SetSize(inSize);
+
+ RecalcLayout();
+ }
+}
+
+void CFlowLayout::SetLayout(CPt inSize, CPt inPosition)
+{
+ CControl::SetLayout(inSize, inPosition);
+
+ RecalcLayout();
+}
+//==============================================================================
+/**
+ * override to force a recalc when the parent is changed
+ */
+void CFlowLayout::OnParentChanged(CControl *inParent)
+{
+ CControl::OnParentChanged(inParent);
+ RecalcLayout();
+}
+
+//==============================================================================
+/**
+ * Sets this object's alignment
+ */
+void CFlowLayout::SetAlignment(EVerticalAlignment inVertical, EHorizontalAlignment inHorizontal)
+{
+ m_VerticalAlignment = inVertical;
+ m_HorizontalAlignment = inHorizontal;
+
+ RecalcLayout();
+}
+
+//==============================================================================
+/**
+ * Overloaded draw function. Draws the extra control if it is needed
+ */
+void CFlowLayout::Draw(CRenderer *inRenderer)
+{
+ // If there is unused space in this control, then use the blank control
+ if (m_HasUnusedSpace && m_BlankControl) {
+ if (m_BlankControl->IsVisible()) {
+ inRenderer->PushTranslation(m_BlankControl->GetPosition());
+ if (!m_BlankDisable)
+ m_BlankControl->Draw(inRenderer);
+ inRenderer->PopTranslation();
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * called when a child's size changes
+ */
+void CFlowLayout::OnChildSizeChanged(CControl *inChild)
+{
+ Q_UNUSED(inChild);
+
+ if (!m_ResizingChildren) {
+ ResetMinMaxPref();
+
+ if (GetParent() != nullptr)
+ GetParent()->OnChildSizeChanged(this);
+
+ RecalcLayout();
+ }
+}
+
+//==============================================================================
+/**
+ * Called to calc the min/max/and pre size of this layout. NOTE: this function
+ * no longer adjusts the max size of the flow layout. The max size is not dictated
+ * by the size of the children, so it can be set independently with a call to
+ * SetMaximumSize().
+ */
+void CFlowLayout::ResetMinMaxPref()
+{
+ if (m_AutoMin) {
+ CPt thePreferredSize(0, 0);
+ CPt theMinimumSize(0, 0);
+
+ thePreferredSize = theMinimumSize;
+
+ // Keep track of the largest, minimum size of all the children for use later
+ CPt theBiggestChildMinSize(0, 0);
+
+ // Go through all the children and find their maximum sizes.
+ ControlGraph::SIterator thePos = GetChildren();
+
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ theChild->ResetMinMaxPref();
+ CPt theChildPrefSize = theChild->GetPreferredSize();
+ CPt theChildMinSize = theChild->GetMinimumSize();
+
+ // use the sum of the children in the flow direction and the smallest size
+ // in the non-flow direction.
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ thePreferredSize.y += theChildPrefSize.y;
+
+ theMinimumSize.y += theChildMinSize.y;
+
+ if (theChildMinSize.x > theBiggestChildMinSize.x)
+ theBiggestChildMinSize = theChildMinSize;
+ } else {
+ thePreferredSize.x += theChildPrefSize.x;
+
+ theMinimumSize.x += theChildMinSize.x;
+
+ if (theChildMinSize.y > theBiggestChildMinSize.y)
+ theBiggestChildMinSize = theChildMinSize;
+ }
+ }
+ }
+
+ // If this is a vertical flow
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ // The width is determined by minimum size of the largest child
+ theMinimumSize.x = theBiggestChildMinSize.x;
+
+ // If the flow is aligned to top, the top margin must be added to the minimum height
+ if (m_VerticalAlignment == ALIGN_TOP)
+ theMinimumSize.y += m_TopMargin;
+ // If the flow is aligned to bottom, the bottom margin must be added to the minimum
+ // height
+ else if (m_VerticalAlignment == ALIGN_BOTTOM)
+ theMinimumSize.y += m_BottomMargin;
+
+ theMinimumSize.y += (m_ChildGap * GetChildCount());
+ }
+ // If this is a horzontal flow
+ else {
+ // The height is determined by minimum size of the largest child
+ theMinimumSize.y = theBiggestChildMinSize.y;
+
+ // If the flow is left aligned, the left margin must be added to the minimum width
+ if (m_HorizontalAlignment == ALIGN_LEFT)
+ theMinimumSize.x += m_LeftMargin;
+ // If the flow is right aligned, the right margin must be added to the minimum width
+ else if (m_HorizontalAlignment == ALIGN_RIGHT)
+ theMinimumSize.x += m_RightMargin;
+
+ theMinimumSize.x += (m_ChildGap * GetChildCount());
+ }
+
+ thePreferredSize.x = max(thePreferredSize.x, theMinimumSize.x);
+ thePreferredSize.y = max(thePreferredSize.y, theMinimumSize.y);
+
+ SetMinimumSize(theMinimumSize);
+ SetPreferredSize(thePreferredSize);
+ }
+}
+
+//=============================================================================
+/**
+ * Override the default in case the blank control was hit
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * @return true if the mouse event is processed.
+ */
+bool CFlowLayout::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = CControl::OnMouseDown(inPoint, inFlags);
+ if (!theRetVal) {
+ if (m_BlankControl && m_BlankControl->HitTest(inPoint))
+ GrabFocus(nullptr);
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Override the default in case the blank control was hit
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * @return true if the mouse event is processed.
+ */
+bool CFlowLayout::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = CControl::OnMouseRDown(inPoint, inFlags);
+ if (!theRetVal) {
+ if (m_BlankControl && m_BlankControl->HitTest(inPoint))
+ GrabFocus(nullptr);
+ }
+ return theRetVal;
+}