Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Canvas.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Diagnostics;
5 
6 using SiliconStudio.Core;
7 using SiliconStudio.Core.Mathematics;
8 
9 namespace SiliconStudio.Paradox.UI.Panels
10 {
11  /// <summary>
12  /// Defines an area within which you can position and size child elements with respect to in the Canvas area size.
13  /// </summary>
14  [DebuggerDisplay("Canvas - Name={Name}")]
15  public class Canvas : Panel
16  {
17  /// <summary>
18  /// The key to the RelativeSize dependency property. RelativeSize indicate the ratio of the size of the <see cref="UIElement"/> with respect to the parent size.
19  /// </summary>
20  /// <remarks>Relative size must be strictly positive</remarks>
21  public readonly static PropertyKey<Vector3> RelativeSizePropertyKey = new PropertyKey<Vector3>("RelativeSizeKey", typeof(Canvas), DefaultValueMetadata.Static(new Vector3(float.NaN, float.NaN, float.NaN)), ValidateValueMetadata.New<Vector3>(ValidateRelativeSize), ObjectInvalidationMetadata.New<Vector3>(InvalidateCanvasMeasure));
22 
23  /// <summary>
24  /// The key to the RelativePosition dependency property. RelativePosition indicate where the <see cref="UIElement"/> is pinned in the canvas.
25  /// </summary>
26  public readonly static PropertyKey<Vector3> RelativePositionPropertyKey = new PropertyKey<Vector3>("RelativePositionKey", typeof(Canvas), DefaultValueMetadata.Static(Vector3.Zero), ObjectInvalidationMetadata.New<Vector3>(OnRelativePositionChanged));
27 
28  /// <summary>
29  /// The key to the AbsolutePosition dependency property. AbsolutePosition indicate where the <see cref="UIElement"/> is pinned in the canvas.
30  /// </summary>
31  public readonly static PropertyKey<Vector3> AbsolutePositionPropertyKey = new PropertyKey<Vector3>("AbsolutePositionKey", typeof(Canvas), DefaultValueMetadata.Static(Vector3.Zero), ObjectInvalidationMetadata.New<Vector3>(OnAbsolutePositionChanged));
32 
33  /// <summary>
34  /// The key to the useAbsolutionPosition dependency property. This indicates whether to use the AbsolutePosition or the RelativePosition to place to element.
35  /// </summary>
36  private readonly static PropertyKey<bool> useAbsolutionPositionPropertyKey = new PropertyKey<bool>("useAbsolutionPositionKey", typeof(Canvas), DefaultValueMetadata.Static(false));
37 
38  /// <summary>
39  /// The key to the PinOrigin dependency property. The PinOrigin indicate which point of the <see cref="UIElement"/> should be pinned to the canvas.
40  /// </summary>
41  /// <remarks>
42  /// Those values are normalized between 0 and 1. (0,0,0) represent the Left/Top/Back corner and (1,1,1) represent the Right/Bottom/Front corner.
43  /// <see cref="UIElement"/>'s margins are included in the normalization.
44  /// Values beyond [0,1] are clamped.</remarks>
45  public readonly static PropertyKey<Vector3> PinOriginPropertyKey = new PropertyKey<Vector3>("PinOriginKey", typeof(Canvas), DefaultValueMetadata.Static(Vector3.Zero), ValidateValueMetadata.New<Vector3>(PinOriginValueValidator), ObjectInvalidationMetadata.New<Vector3>(InvalidateCanvasMeasure));
46 
47  private static void OnRelativePositionChanged(object propertyOwner, PropertyKey<Vector3> propertyKey, Vector3 propertyOldValue)
48  {
49  var element = (UIElement)propertyOwner;
50  element.DependencyProperties.Set(useAbsolutionPositionPropertyKey, false);
51 
52  InvalidateCanvasMeasure(propertyOwner, propertyKey, propertyOldValue);
53  }
54 
55  private static void OnAbsolutePositionChanged(object propertyOwner, PropertyKey<Vector3> propertyKey, Vector3 propertyOldValue)
56  {
57  var element = (UIElement)propertyOwner;
58  element.DependencyProperties.Set(useAbsolutionPositionPropertyKey, true);
59 
60  InvalidateCanvasMeasure(propertyOwner, propertyKey, propertyOldValue);
61  }
62 
63  protected static void InvalidateCanvasMeasure(object propertyOwner, PropertyKey<Vector3> propertyKey, Vector3 propertyOldValue)
64  {
65  var element = (UIElement)propertyOwner;
66  var parentCanvas = element.Parent as Canvas;
67 
68  if (parentCanvas != null)
69  parentCanvas.InvalidateMeasure();
70  }
71 
72  private static void PinOriginValueValidator(ref Vector3 value)
73  {
74  value.X = Math.Min(1, Math.Max(0, value.X));
75  value.Y = Math.Min(1, Math.Max(0, value.Y));
76  value.Z = Math.Min(1, Math.Max(0, value.Z));
77  }
78 
79  private static void ValidateRelativeSize(ref Vector3 value)
80  {
81  if (value.X < 0 || value.Y < 0 || value.Z < 0)
82  throw new InvalidOperationException("All the components of the a relative size must be positive");
83  }
84 
85  protected override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
86  {
87  // Measure all the children
88  // Canvas does not take into account possible collisions between children
89  // The available size for a child is thus the size of the canvas available after or before the PinPosition depending on the element PinOrigin.
90  foreach (var child in VisualChildrenCollection)
91  {
92  // calculate the available space for the child
93  var childAvailableSizeWithMargin = ComputeAvailableSize(child, availableSizeWithoutMargins, false);
94 
95  // override the available space if the child size is relative to its parent's.
96  var childRelativeSize = child.DependencyProperties.Get(RelativeSizePropertyKey);
97  for (var i = 0; i < 3; i++)
98  {
99  if (float.IsNaN(childRelativeSize[i])) // relative size is not set
100  continue;
101 
102  childAvailableSizeWithMargin[i] = childRelativeSize[i] > 0? childRelativeSize[i] * availableSizeWithoutMargins[i]: 0f; // avoid NaN due to 0 x Infinity
103  }
104 
105  child.Measure(childAvailableSizeWithMargin);
106  }
107 
108  // Estimate the size needed so that the biggest child fits
109  var desiredSizeWithoutMargin = Vector3.Zero;
110  foreach (var child in VisualChildrenCollection)
111  {
112  if(child.IsCollapsed)
113  continue;
114 
115  // determine the position of the right/top/front corner of the child
116  var childExtremityCorner = Vector3.Zero;
117  var pinOrigin = child.DependencyProperties.Get(PinOriginPropertyKey);
118  var childRelativeSize = child.DependencyProperties.Get(RelativeSizePropertyKey);
119  var childUseAbsolutionPosition = child.DependencyProperties.Get(useAbsolutionPositionPropertyKey);
120  var childAbsolutePosition = child.DependencyProperties.Get(AbsolutePositionPropertyKey);
121  var childRelativePosition = child.DependencyProperties.Get(RelativePositionPropertyKey);
122  for (var i = 0; i < 3; i++)
123  {
124  if (!float.IsNaN(childRelativeSize[i])) // relative size is set
125  {
126  childExtremityCorner[i] = childRelativeSize[i] > 0? child.DesiredSizeWithMargins[i] / childRelativeSize[i]: 0f;
127  }
128  else if (childUseAbsolutionPosition && !float.IsNaN(childAbsolutePosition[i])) // prioritize absolute position and absolute position is set.
129  {
130  childExtremityCorner[i] = childAbsolutePosition[i] + (1f - pinOrigin[i]) * child.DesiredSizeWithMargins[i];
131  }
132  else if (!float.IsNaN(childRelativePosition[i]))
133  {
134  if (pinOrigin[i] > 0 && childRelativePosition[i] > 0)
135  childExtremityCorner[i] = pinOrigin[i] * child.DesiredSizeWithMargins[i] / childRelativePosition[i];
136  if (pinOrigin[i] < 1 && childRelativePosition[i] < 1)
137  childExtremityCorner[i] = Math.Max(childExtremityCorner[i], (1 - pinOrigin[i]) * child.DesiredSizeWithMargins[i] / (1 - childRelativePosition[i]));
138  }
139  else
140  {
141  childExtremityCorner[i] = 0;
142  }
143  }
144 
145  // increase the parent desired size if one of its children get out of it.
146  desiredSizeWithoutMargin = new Vector3(
147  Math.Max(desiredSizeWithoutMargin.X, childExtremityCorner.X),
148  Math.Max(desiredSizeWithoutMargin.Y, childExtremityCorner.Y),
149  Math.Max(desiredSizeWithoutMargin.Z, childExtremityCorner.Z));
150  }
151 
152  return desiredSizeWithoutMargin;
153  }
154 
155  protected override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins)
156  {
157  // Arrange all the children
158  foreach (var child in VisualChildrenCollection)
159  {
160  // calculate the size provided to the child
161  var availableSize = ComputeAvailableSize(child, finalSizeWithoutMargins, true); //should we force the element size when element relative size is set ???
162  var childProvidedSize = new Vector3(
163  Math.Min(availableSize.X, child.DesiredSizeWithMargins.X),
164  Math.Min(availableSize.Y, child.DesiredSizeWithMargins.Y),
165  Math.Min(availableSize.Z, child.DesiredSizeWithMargins.Z));
166 
167  // arrange the child
168  child.Arrange(childProvidedSize, IsCollapsed);
169 
170  // compute the child offsets wrt to parent (left,top,back) corner
171  var pinOrigin = child.DependencyProperties.Get(PinOriginPropertyKey);
172  var childOrigin = ComputeAbsolutePinPosition(child, ref finalSizeWithoutMargins) - Vector3.Modulate(pinOrigin, child.RenderSize);
173 
174  // compute the child offsets wrt to parent origin (0,0,0).
175  var childOriginParentCenter = childOrigin - finalSizeWithoutMargins / 2;
176 
177  // set the panel arrange matrix for the child
178  child.DependencyProperties.Set(PanelArrangeMatrixPropertyKey, Matrix.Translation(childOriginParentCenter));
179 
180  }
181 
182  return base.ArrangeOverride(finalSizeWithoutMargins);
183  }
184 
185  /// <summary>
186  /// Compute the child absolute position in the canvas according to parent size and the child layout properties.
187  /// </summary>
188  /// <param name="child">The child to place</param>
189  /// <param name="parentSize">The parent size</param>
190  /// <returns>The child absolute position offset</returns>
191  protected Vector3 ComputeAbsolutePinPosition(UIElement child, ref Vector3 parentSize)
192  {
193  var relativePosition = child.DependencyProperties.Get(RelativePositionPropertyKey);
194  var absolutePosition = child.DependencyProperties.Get(AbsolutePositionPropertyKey);
195  var useAbsolutionPosition = child.DependencyProperties.Get(useAbsolutionPositionPropertyKey);
196 
197  for (var dim = 0; dim < 3; ++dim)
198  {
199  if (float.IsNaN(absolutePosition[dim]) || !useAbsolutionPosition && !float.IsNaN(relativePosition[dim]))
200  absolutePosition[dim] = relativePosition[dim] == 0f ? 0f : relativePosition[dim] * parentSize[dim];
201  }
202 
203  return absolutePosition;
204  }
205 
206  /// <summary>
207  /// Compute the space available to the provided child based on size available to the canvas and the child layout properties.
208  /// </summary>
209  /// <param name="child">The child of the canvas to measure/arrange</param>
210  /// <param name="availableSize">The space available to the canvas</param>
211  /// <param name="ignoreRelativeSize">Indicate if the child RelativeSize property should be taken in account or nor</param>
212  /// <returns></returns>
213  protected Vector3 ComputeAvailableSize(UIElement child, Vector3 availableSize, bool ignoreRelativeSize)
214  {
215  // calculate the absolute position of the child
216  var pinPosition = ComputeAbsolutePinPosition(child, ref availableSize);
217  var pinOrigin = child.DependencyProperties.Get(PinOriginPropertyKey);
218  var relativeSize = child.DependencyProperties.Get(RelativeSizePropertyKey);
219  var childAvailableSize = Vector3.Zero;
220 
221  for (var dim = 0; dim < 3; dim++)
222  {
223  if (!ignoreRelativeSize && !float.IsNaN(relativeSize[dim]))
224  {
225  childAvailableSize[dim] = relativeSize[dim] > 0? relativeSize[dim] * availableSize[dim]: 0f;
226  }
227  else if (pinPosition[dim] < 0 || pinPosition[dim] > availableSize[dim])
228  {
229  childAvailableSize[dim] = 0;
230  }
231  else
232  {
233  var availableBeforeElement = float.PositiveInfinity;
234  if (pinPosition[dim] >= 0 && pinOrigin[dim] > 0)
235  availableBeforeElement = pinPosition[dim] / pinOrigin[dim];
236 
237  var availableAfterElement = float.PositiveInfinity;
238  if (pinPosition[dim] <= availableSize[dim] && !float.IsPositiveInfinity(pinPosition[dim]) && pinOrigin[dim] < 1)
239  availableAfterElement = (availableSize[dim] - pinPosition[dim]) / (1f - pinOrigin[dim]);
240 
241  childAvailableSize[dim] = Math.Min(availableBeforeElement, availableAfterElement);
242  }
243  }
244 
245  return childAvailableSize;
246  }
247  }
248 }
static void InvalidateCanvasMeasure(object propertyOwner, PropertyKey< Vector3 > propertyKey, Vector3 propertyOldValue)
Definition: Canvas.cs:63
Provides a base class for all the User Interface elements in Paradox applications.
Definition: UIElement.cs:21
override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
When overridden in a derived class, measures the size in layout required for possible child elements ...
Definition: Canvas.cs:85
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
Vector3 ComputeAbsolutePinPosition(UIElement child, ref Vector3 parentSize)
Compute the child absolute position in the canvas according to parent size and the child layout prope...
Definition: Canvas.cs:191
Defines an area within which you can position and size child elements with respect to in the Canvas a...
Definition: Canvas.cs:15
Vector3 ComputeAvailableSize(UIElement child, Vector3 availableSize, bool ignoreRelativeSize)
Compute the space available to the provided child based on size available to the canvas and the child...
Definition: Canvas.cs:213
override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins)
When overridden in a derived class, positions possible child elements and determines a size for a UIE...
Definition: Canvas.cs:155
SiliconStudio.Core.Mathematics.Vector3 Vector3
A class that represents a tag propety.
Definition: PropertyKey.cs:17
Provides a base class for all Panel elements. Use Panel elements to position and arrange child object...
Definition: Panel.cs:19