MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
layoutstrategy.cpp
1 //© Copyright 2010 - 2013 BlackTopp Studios Inc.
2 /* This file is part of The Mezzanine Engine.
3 
4  The Mezzanine Engine is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  The Mezzanine Engine is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with The Mezzanine Engine. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /* The original authors have included a copy of the license specified above in the
18  'Docs' folder. See 'gpl.txt'
19 */
20 /* We welcome the use of the Mezzanine engine to anyone, including companies who wish to
21  Build professional software and charge for their product.
22 
23  However there are some practical restrictions, so if your project involves
24  any of the following you should contact us and we will try to work something
25  out:
26  - DRM or Copy Protection of any kind(except Copyrights)
27  - Software Patents You Do Not Wish to Freely License
28  - Any Kind of Linking to Non-GPL licensed Works
29  - Are Currently In Violation of Another Copyright Holder's GPL License
30  - If You want to change our code and not add a few hundred MB of stuff to
31  your distribution
32 
33  These and other limitations could cause serious legal problems if you ignore
34  them, so it is best to simply contact us or the Free Software Foundation, if
35  you have any questions.
36 
37  Joseph Toppi - toppij@gmail.com
38  John Blackwood - makoenergy02@gmail.com
39 */
40 #ifndef _uilayoutstrategy_cpp
41 #define _uilayoutstrategy_cpp
42 
43 #include "UI/layoutstrategy.h"
44 #include "UI/widget.h"
45 #include "mathtool.h"
46 #include "exception.h"
47 
48 #include <algorithm>
49 
50 namespace Mezzanine
51 {
52  namespace UI
53  {
54  ///////////////////////////////////////////////////////////////////////////////
55  // Construction and Destruction
56 
58  { }
59 
61  { }
62 
63  ///////////////////////////////////////////////////////////////////////////////
64  // Primary Methods
65 
66  void LayoutStrategy::Layout(const Rect& OldSelfRect, const Rect& NewSelfRect, const ChildContainer& ChildQuads)
67  {
68  Boolean QuadPositionUpdated = (OldSelfRect.Position != NewSelfRect.Position);
69  Boolean QuadSizeUpdated = (OldSelfRect.Size != NewSelfRect.Size);
70  for( ConstChildIterator ChildIt = ChildQuads.begin() ; ChildIt != ChildQuads.end() ; ++ChildIt )
71  {
72  QuadRenderable* Child = (*ChildIt);
73 
74  if( Child->GetManualTransformUpdates() )
75  continue;
76 
77  const Rect OldChildRect = Child->GetRect();
78  Rect NewChildRect = OldChildRect;
79  //NewChildRect.SetIdentity();
80 
81  if( QuadSizeUpdated ) {
82  NewChildRect.Size = this->HandleChildSizing(OldSelfRect,NewSelfRect,Child);
83  }
84 
85  if( QuadSizeUpdated || QuadPositionUpdated ) {
86  NewChildRect.Position = this->HandleChildPositioning(OldSelfRect,NewSelfRect,NewChildRect.Size,Child);
87  }
88 
89  Child->UpdateDimensions(OldChildRect,NewChildRect);
90  }// for each child
91  }
92 
93  ///////////////////////////////////////////////////////////////////////////////
94  // Sub-task Methods
95 
96  Vector2 LayoutStrategy::HandleChildPositioning(const Rect& OldSelfRect, const Rect& NewSelfRect, const Vector2& NewChildSize, QuadRenderable* Child)
97  {
98  Vector2 NewPosition;
99 
100  // Resolve our position
101  NewPosition.X = this->HandleChildHorizontalPositioning(OldSelfRect,NewSelfRect,NewChildSize,Child);
102  NewPosition.Y = this->HandleChildVerticalPositioning(OldSelfRect,NewSelfRect,NewChildSize,Child);
103 
104  return NewPosition;
105  }
106 
107  Real LayoutStrategy::HandleChildHorizontalPositioning(const Rect& OldSelfRect, const Rect& NewSelfRect, const Vector2& NewChildSize, QuadRenderable* Child)
108  {
109  const Real OldXPos = Child->GetActualPosition().X;
110  const Real OldXSize = Child->GetActualSize().X;
111  const PositioningInfo& ChildPositioning = Child->GetPositioningPolicy();
112  // Rather than have a bunch of complicated checks to see what needs to be filled in, blindly assign Unified position
113  // to the new rect, and allow simpler targetted checks fill in the exceptions
114  Real Ret = NewSelfRect.Position.X + ChildPositioning.UPosition.X.CalculateActualDimension(NewSelfRect.Size.X);
115  // Do our checks
116  if( ChildPositioning.PositionRules & UI::PF_HorizontalCenter ) { // Check if we're centered
117  // Get the center point in the parent space on this axis, and align the childs center on the same axis to that point
118  Ret = ( NewSelfRect.Position.X + ( NewSelfRect.Size.X * 0.5 ) ) - ( NewChildSize.X * 0.5 );
119  }else if( ChildPositioning.PositionRules & UI::PF_Left ) { // Check if we're anchored to the left
120  // Get the difference there used to be prior to this update between the left edge of this quad and it's parent.
121  // Then add it to the parents updated rect to get our new position. This preserves absolute distance.
122  Ret = NewSelfRect.Position.X + ( OldSelfRect.Position.X - OldXPos );
123  }else if( ChildPositioning.PositionRules & UI::PF_Right ) { // Check if we're anchored to the right
124  // Get the right edge of the parent's old transform and get the distance from that point to the left edge of this quad.
125  // Then subtract apply that distance (via subtraction) to the updated transform. This preserves absolute distance.
126  Ret = ( NewSelfRect.Position.X + NewSelfRect.Size.X ) - ( ( OldSelfRect.Position.X + OldSelfRect.Size.X ) - ( OldXPos + OldXSize ) );
127  }
128  return Ret;
129  }
130 
131  Real LayoutStrategy::HandleChildVerticalPositioning(const Rect& OldSelfRect, const Rect& NewSelfRect, const Vector2& NewChildSize, QuadRenderable* Child)
132  {
133  const Real OldYPos = Child->GetActualPosition().Y;
134  const Real OldYSize = Child->GetActualSize().Y;
135  const PositioningInfo& ChildPositioning = Child->GetPositioningPolicy();
136  // Rather than have a bunch of complicated checks to see what needs to be filled in, blindly assign Unified position
137  // to the new rect, and allow simpler targetted checks fill in the exceptions
138  Real Ret = NewSelfRect.Position.Y + ChildPositioning.UPosition.Y.CalculateActualDimension(NewSelfRect.Size.Y);
139  // Do our checks
140  if( ChildPositioning.PositionRules & UI::PF_VerticalCenter ) { // Check if we're centered
141  // Get the center point in the parent space on this axis, and align the childs center on the same axis to that point
142  Ret = ( NewSelfRect.Position.Y + ( NewSelfRect.Size.Y * 0.5 ) ) - ( NewChildSize.Y * 0.5 );
143  }else if( ChildPositioning.PositionRules & UI::PF_Top ) { // Check if we're anchored to the top
144  // Get the difference there used to be prior to this update between the top edge of this quad and it's parent.
145  // Then add it to the parents updated rect to get our new position. This preserves absolute distance.
146  Ret = NewSelfRect.Position.Y + ( OldSelfRect.Position.Y - NewSelfRect.Position.Y );
147  }else if( ChildPositioning.PositionRules & UI::PF_Bottom ) { // Check if we're anchored to the bottom
148  // Get the bottom edge of the parent's old transform and get the distance from that point to the top edge of this quad.
149  // Then subtract apply that distance (via subtraction) to the updated transform. This preserves absolute distance.
150  Ret = ( NewSelfRect.Position.Y + NewSelfRect.Size.Y ) - ( ( OldSelfRect.Position.Y + OldSelfRect.Size.Y ) - ( OldYPos + OldYSize ) );
151  }
152  return Ret;
153  }
154 
155  Vector2 LayoutStrategy::HandleChildSizing(const Rect& OldSelfRect, const Rect& NewSelfRect, QuadRenderable* Child)
156  {
157  Vector2 NewSize;
158  Vector2 OldSize = Child->GetActualSize();
159  const SizingInfo& ChildSizing = Child->GetSizingPolicy();
160 
161  // Quick check for a configuration that is just not doable
163  { MEZZ_EXCEPTION(Exception::INVALID_STATE_EXCEPTION,"Both axes of a SizingPolicy cannot attempt to match the other axis."); }
164 
165  // In general, process the vertical dimension first. This is because vertical resizing is likely to be less extreme compared to horizontal
166  // in the case of transitioning from widescreen to standard viewing formats. In general, horizontal is more likely to be the dependent axis.
167  // That is of course unless Vertical is explicitly declared otherwise. So check for that.
168  if( ChildSizing.VerticalRules != UI::SR_Match_Other_Axis ) {
169  // Vertical first
170  NewSize.Y = this->HandleChildVerticalSizing(OldSelfRect,NewSelfRect,0,Child);
171  // Horizontal second
172  NewSize.X = this->HandleChildHorizontalSizing(OldSelfRect,NewSelfRect,NewSize.Y,Child);
173  }else{
174  // Horizontal first
175  NewSize.X = this->HandleChildHorizontalSizing(OldSelfRect,NewSelfRect,0,Child);
176  // Vertical second
177  NewSize.Y = this->HandleChildVerticalSizing(OldSelfRect,NewSelfRect,NewSize.X,Child);
178  }
179 
180  // Preserve the aspect ratio if we need to
181  this->CheckChildAspectRatio(OldSize,NewSize,Child);
182 
183  // Clamp to our min and max sizes
184  this->ClampChildToMinSize(NewSelfRect,NewSize,Child);
185  this->ClampChildToMaxSize(NewSelfRect,NewSize,Child);
186 
187  return NewSize;
188  }
189 
190  Real LayoutStrategy::HandleChildHorizontalSizing(const Rect& OldSelfRect, const Rect& NewSelfRect, const Real PrevAxisResult, QuadRenderable* Child)
191  {
192  const SizingInfo& ChildSizing = Child->GetSizingPolicy();
193  Vector2 OldSize = Child->GetActualSize();
194  // Horizontal second
195  switch(ChildSizing.HorizontalRules)
196  {
197  case UI::SR_Unified_Dims: return ChildSizing.USize.X.CalculateActualDimension(NewSelfRect.Size.X); break;
198  case UI::SR_Match_Other_Axis: return PrevAxisResult; break;
199  case UI::SR_Fixed_Size:
200  default: return OldSize.X; break;
201  }
202  }
203 
204  Real LayoutStrategy::HandleChildVerticalSizing(const Rect& OldSelfRect, const Rect& NewSelfRect, const Real PrevAxisResult, QuadRenderable* Child)
205  {
206  const SizingInfo& ChildSizing = Child->GetSizingPolicy();
207  Vector2 OldSize = Child->GetActualSize();
208  // Vertical first
209  switch(ChildSizing.VerticalRules)
210  {
211  case UI::SR_Unified_Dims: return ChildSizing.USize.Y.CalculateActualDimension(NewSelfRect.Size.Y); break;
212  case UI::SR_Match_Other_Axis: return PrevAxisResult; break;
213  case UI::SR_Fixed_Size:
214  default: return OldSize.Y; break;
215  }
216  }
217 
218  void LayoutStrategy::CheckChildAspectRatio(const Vector2& OldChildSize, Vector2& NewChildSize, QuadRenderable* Child)
219  {
220  const SizingInfo& ChildSizing = Child->GetSizingPolicy();
221  // Do we care about aspect ratio?
222  // And did the aspect ratio change?
223  if( ChildSizing.RatioLock != UI::ARL_Ratio_Unlocked && !MathTools::WithinTolerance( NewChildSize.X / NewChildSize.Y, OldChildSize.X / OldChildSize.Y, 0.01 ) )
224  {
225  Real XChange = NewChildSize.X / OldChildSize.X;
226  Real YChange = NewChildSize.Y / OldChildSize.Y;
227 
228  if( XChange > YChange ) {
229  if( ChildSizing.RatioLock == UI::ARL_Ratio_Locked_Expanding )
230  NewChildSize.Y = OldChildSize.Y * XChange;
231  else if( ChildSizing.RatioLock == UI::ARL_Ratio_Locked_Shrinking )
232  NewChildSize.X = OldChildSize.X * YChange;
233  }else if( YChange > XChange ) {
234  if( ChildSizing.RatioLock == UI::ARL_Ratio_Locked_Expanding )
235  NewChildSize.X = OldChildSize.X * YChange;
236  else if( ChildSizing.RatioLock == UI::ARL_Ratio_Locked_Shrinking )
237  NewChildSize.Y = OldChildSize.Y * XChange;
238  }
239  }
240  }
241 
242  void LayoutStrategy::ClampChildToMinSize(const Rect& NewSelfRect, Vector2& NewChildSize, QuadRenderable* Child)
243  {
244  const SizingInfo& ChildSizing = Child->GetSizingPolicy();
245  if( ChildSizing.MinSize.X.Rel != 0 && ChildSizing.MinSize.X.Abs != 0 &&
246  ChildSizing.MinSize.Y.Rel != 0 && ChildSizing.MinSize.Y.Abs != 0 )
247  {
248  Vector2 TempVec = ChildSizing.MinSize.CalculateActualDimensions(NewSelfRect.Size);
249  NewChildSize.X = std::max(NewChildSize.X,TempVec.X);
250  NewChildSize.Y = std::max(NewChildSize.Y,TempVec.Y);
251  }
252  }
253 
254  void LayoutStrategy::ClampChildToMaxSize(const Rect& NewSelfRect, Vector2& NewChildSize, QuadRenderable* Child)
255  {
256  const SizingInfo& ChildSizing = Child->GetSizingPolicy();
257  if( ChildSizing.MaxSize.X.Rel != 0 && ChildSizing.MaxSize.X.Abs != 0 &&
258  ChildSizing.MaxSize.Y.Rel != 0 && ChildSizing.MaxSize.Y.Abs != 0 )
259  {
260  Vector2 TempVec = ChildSizing.MaxSize.CalculateActualDimensions(NewSelfRect.Size);
261  NewChildSize.X = std::min(NewChildSize.X,TempVec.X);
262  NewChildSize.Y = std::min(NewChildSize.Y,TempVec.Y);
263  }
264  }
265  }//UI
266 }//Mezzanine
267 
268 #endif