Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Grid.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.Collections.Generic;
5 using System.Collections.Specialized;
6 using System.Diagnostics;
7 
8 using SiliconStudio.Core.Collections;
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.Core.Mathematics;
11 
12 namespace SiliconStudio.Paradox.UI.Panels
13 {
14  /// <summary>
15  /// Represents a grid control with adjustable columns, rows and layers.
16  /// </summary>
17  [DebuggerDisplay("Grid - Name={Name}")]
18  public class Grid : GridBase
19  {
20  private readonly Logger logger = GlobalLogger.GetLogger("UI");
21 
22  private readonly StripDefinitionCollection[] stripDefinitions =
23  {
27  };
28 
29  /// <summary>
30  /// For each dimension and index of strip, return the list of UIElement that are contained only in auto-sized strips
31  /// </summary>
32  private readonly List<List<UIElement>>[] stripIndexToNoStarElements =
33  {
34  new List<List<UIElement>>(),
35  new List<List<UIElement>>(),
36  new List<List<UIElement>>()
37  };
38 
39  /// <summary>
40  /// For each dimension and UIelement, returns the list of all the strip definition it is contained in ordered by increasing strip index
41  /// </summary>
42  private readonly Dictionary<UIElement, List<StripDefinition>>[] elementToStripDefinitions =
43  {
44  new Dictionary<UIElement, List<StripDefinition>>(),
45  new Dictionary<UIElement, List<StripDefinition>>(),
46  new Dictionary<UIElement, List<StripDefinition>>()
47  };
48 
49  /// <summary>
50  /// For each dimension and UIelement that is partially contained in star-sized strip, returns the list of all the strip definition it is contained in
51  /// </summary>
52  private readonly Dictionary<UIElement, List<StripDefinition>>[] partialStarElementToStripDefinitions =
53  {
54  new Dictionary<UIElement, List<StripDefinition>>(),
55  new Dictionary<UIElement, List<StripDefinition>>(),
56  new Dictionary<UIElement, List<StripDefinition>>()
57  };
58 
59  /// <summary>
60  /// For each dimension and strip index, return the starting position of the strip.
61  /// </summary>
62  private readonly List<float>[] cachedStripIndexToStripPosition =
63  {
64  new List<float>(),
65  new List<float>(),
66  new List<float>()
67  };
68 
69  /// <summary>
70  /// For each dimension, the list of the star definitions for the current dimension iteration (Ox, Oy or Oz).
71  /// </summary>
72  /// <remarks> This variable is declared as a field to avoid reallocations at each frame</remarks>
73  private readonly List<StripDefinition>[] dimToStarDefinitions =
74  {
75  new List<StripDefinition>(),
76  new List<StripDefinition>(),
77  new List<StripDefinition>()
78  };
79 
80  /// <summary>
81  /// A list use to make a copy of the star definition and then make a modification on this list.
82  /// </summary>
83  private readonly List<StripDefinition> starDefinitionsCopy = new List<StripDefinition>();
84 
85  /// <summary>
86  /// The list of the star definitions of an element sorted by increasing minimum size wrt their star value
87  /// </summary>
88  /// <remarks> This variable is declared as a field to avoid reallocations at each frame</remarks>
89  private readonly List<StripDefinition> minSortedStarDefinitions = new List<StripDefinition>();
90 
91  /// <summary>
92  /// The list of the star definitions of an element sorted by increasing maximum size wrt their star value
93  /// </summary>
94  /// <remarks> This variable is declared as a field to avoid reallocations at each frame</remarks>
95  private readonly List<StripDefinition> maxSortedStarDefinitions = new List<StripDefinition>();
96 
97  /// <summary>
98  /// The list of the star definitions that were bounded by their maximum values in the current iteration step.
99  /// </summary>
100  /// <remarks> This variable is declared as a field to avoid reallocations at each frame</remarks>
101  private readonly List<StripDefinition> maxBoundedStarDefinitions = new List<StripDefinition>();
102 
103  /// <summary>
104  /// The list of the star definitions that were bounded by their minimum values in the current iteration step.
105  /// </summary>
106  /// <remarks> This variable is declared as a field to avoid reallocations at each frame</remarks>
107  private readonly List<StripDefinition> minBoundedStarDefinitions = new List<StripDefinition>();
108 
109  /// <summary>
110  /// The list of all elements contained in at least on auto-sized strip.
111  /// </summary>
112  private readonly HashSet<UIElement> autoDefinedElements = new HashSet<UIElement>();
113 
114  private readonly IComparer<StripDefinition> sortByIncreasingMaximumComparer = new StripDefinition.SortByIncreasingStarRelativeMaximumValues();
115  private readonly IComparer<StripDefinition> sortByIncreasingMinimumComparer = new StripDefinition.SortByIncreasingStarRelativeMinimumValues();
116 
117  public Grid()
118  {
119  foreach (var definitionCollection in stripDefinitions)
120  definitionCollection.CollectionChanged += DefinitionCollectionChanged;
121  }
122 
123  private void DefinitionCollectionChanged(object sender, TrackingCollectionChangedEventArgs trackingCollectionChangedEventArgs)
124  {
125  var modifiedElement = (StripDefinition)trackingCollectionChangedEventArgs.Item;
126  switch (trackingCollectionChangedEventArgs.Action)
127  {
128  case NotifyCollectionChangedAction.Add:
129  modifiedElement.DefinitionChanged += OnStripDefinitionChanged;
130  break;
131  case NotifyCollectionChangedAction.Remove:
132  modifiedElement.DefinitionChanged -= OnStripDefinitionChanged;
133  break;
134  default:
135  throw new NotImplementedException();
136  }
137  InvalidateMeasure();
138  }
139 
140  private void OnStripDefinitionChanged(object sender, EventArgs eventArgs)
141  {
142  InvalidateMeasure();
143  }
144 
145  /// <summary>
146  /// The definitions of the grid columns.
147  /// </summary>
148  /// <exception cref="ArgumentNullException">The provided value is null.</exception>
149  public StripDefinitionCollection ColumnDefinitions
150  {
151  get { return stripDefinitions[0]; }
152  }
153 
154  /// <summary>
155  /// The definitions of the grid rows.
156  /// </summary>
157  /// <exception cref="ArgumentNullException">The provided value is null.</exception>
158  public StripDefinitionCollection RowDefinitions
159  {
160  get { return stripDefinitions[1]; }
161  }
162 
163  /// <summary>
164  /// The definitions of the grid layers.
165  /// </summary>
166  /// <exception cref="ArgumentNullException">The provided value is null.</exception>
167  public StripDefinitionCollection LayerDefinitions
168  {
169  get { return stripDefinitions[2]; }
170  }
171 
172  protected override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
173  {
174  // This function is composed of 6 main parts:
175  // 1. Add default strip definition to ensure that all elements are in the grid
176  // 2. Rebuild the needed cache data to perform future calculations
177  // 3. Measure all children that are not contained in star-sized strips with the best estimation possible.
178  // 4. Determine the size of the auto-size strips.
179  // 5. Determine the size of 1-star strip.
180  // 6. Re-measure all children, this time with the exact strip size value.
181  // 7. Calculate size required by the grid to contain all its children.
182 
183  // 1. Ensure that all child UI element are completely inside the grid by adding strip definitions
184  CheckChildrenPositionsAndAdjustGridSize();
185 
186  // 2. Update the autoStripNumberToElements cache data structure for the next Measure and Arrange sequence
187  RebuildMeasureCacheData();
188 
189  // 3. Measure all children that are contained in a least one auto-strip with the best estimation possible of the strips final size.
190  // Note that only an estimation of the final strip size can be used at this point, since the final sizes can only be determined once all auto-children have been measured.
191  //
192  // We chose a kind of gross but simple/efficient algorithm. It works as follows:
193  // - Initialize the strip sizes with the strip minimum size for star/auto strips and exact final size for fixed strips.
194  // - Remove the minimal strip size as well as the fixed strip size.
195  // - Use this same remaining size to measure all the auto elements.
196  // This algorithm put all the auto elements on an equal footing, but propose more space to the children that the grid really have.
197  //
198  // For a better estimation of the size really available to the children, the following algorithm would be possible,
199  // but it is much more complex and heavy not only in term of CPU usage but also in memory (cached data):
200  // - Initialize the strip sizes with the strip minimum size for star/auto strips and exact final size for fixed strips.
201  // - Measure elements by iterating on columns (left to right) then rows (top to bottom) and finally layers (back to front)
202  // - Estimate the measure size by removing from the available size the size of all previous strips actual size (that is current size estimation).
203  // - When going to the next strip iteration, refine the previous strip estimated size (ActualSize) by taking the max sized needed among all element ending in this strip.
204 
205  // Initialize strip actual size with minimal values
206  foreach (var definitions in stripDefinitions)
207  InitializeStripDefinitionActualSize(definitions);
208 
209  // calculate size available for all auto elements.
210  var autoElementAvailableSize = availableSizeWithoutMargins;
211  for (var dim = 0; dim < 3; dim++)
212  {
213  foreach (var definition in stripDefinitions[dim])
214  {
215  autoElementAvailableSize[dim] -= definition.Type == StripType.Fixed ? definition.ActualSize : definition.MinimumSize;
216  }
217  }
218 
219  // measure all the children
220  foreach (var child in autoDefinedElements)
221  {
222  var childAvailableSize = Vector3.Zero;
223  for (var dim = 0; dim < 3; dim++)
224  {
225  var autoAvailableWithMin = autoElementAvailableSize[dim];
226  foreach (var definition in elementToStripDefinitions[dim][child])
227  {
228  autoAvailableWithMin += definition.Type == StripType.Fixed ? definition.ActualSize: definition.MinimumSize;
229  if (definition.Type == StripType.Fixed)
230  childAvailableSize[dim] += definition.ClampSizeByMinimumMaximum(definition.SizeValue);
231  else
232  childAvailableSize[dim] = Math.Min(autoAvailableWithMin, childAvailableSize[dim] + definition.MaximumSize);
233  }
234  }
235  child.Measure(childAvailableSize);
236  }
237 
238  // 4. Determine the size of the auto-sized strips
239  // -> Our algorithm here tries to minimize as long as possible the size of the Auto-strips during the iteration.
240  // By doing so, we postpone the increase in size of shared Auto-strips to the last strip shared.
241  // This method is way much easier than spreading space equally between all the shared auto-sized strips.
242  //
243  // |<- auto ->|<- auto ->|
244  // _________________________________
245  // |element1 |element2 | <-- spreads space equally between auto-sized strips -- very difficult (even WPF implementation is buggy and not optimal)
246  // |-----element3-with-span-of-2---|
247  //
248  // |<-auto->|<- auto ->|
249  // _________________________________
250  // |element1|element2 | <-- our algorithm always minimize auto-sized strips as long as possible -- simple and optimal
251  // |-----element3-with-span-of-2---|
252  //
253  // -> There is an issue with elements contained both in auto and star strip definitions.
254  // Intuitively, we expect that those elements receive enough space to layout and that this space is perfectly divided into the auto / star strips.
255  // The problem is that it is not possible to determine the size of star strips as long as all auto strip size as not been determined,
256  // and that it is not possible determine missing space to include into the auto-sized strips for those elements as long as we don't know the size of star-sized strips.
257  // We are in a dead-end. There is basically two solutions:
258  // 1. Include all the missing size for those element into the auto strips
259  // 2. Include none of the missing size into the auto strips and hope that the star strips will be big enough to contain those elements.
260  // Here we chose option (2), that is we ignore those elements during calculation of auto-sized strips.
261  // The reason between this choice is that (1) will tend to increase excessively the size of auto-sized strips (for nothing).
262  // Moreover, we consider most of the time elements included both auto and star-size strips are more elements that we want
263  // to be spread along several strips rather than elements that we want auto-sized.
264  for (var dim = 0; dim < 3; dim++)
265  {
266  var definitions = stripDefinitions[dim];
267 
268  // reset the estimated size of the auto-sized strip calculated before.
269  InitializeStripDefinitionActualSize(definitions);
270 
271  for (var index = 0; index < stripDefinitions[dim].Count; index++)
272  {
273  var currentDefinition = stripDefinitions[dim][index];
274  if (currentDefinition.Type != StripType.Auto) // we are interested only in auto-sized strip here
275  continue;
276 
277  // for each strip iterate all the elements (with no star definition) to determine the biggest space needed.
278  foreach (var element in stripIndexToNoStarElements[dim][index])
279  {
280  var currentDefinitionIndex = 0; // the index of 'currentDefinition' in 'elementToStripDefinitions[dim][element]'
281 
282  // first determine the total space still needed for the element
283  var spaceAvailable = 0f;
284  for (var i = 0; i < elementToStripDefinitions[dim][element].Count; i++)
285  {
286  spaceAvailable += elementToStripDefinitions[dim][element][i].ActualSize;
287 
288  if (elementToStripDefinitions[dim][element][i] == currentDefinition)
289  currentDefinitionIndex = i;
290  }
291  var spaceNeeded = Math.Max(0, element.DesiredSizeWithMargins[dim] - spaceAvailable);
292 
293  // if no space is needed, go check the next element
294  if (spaceNeeded <= 0)
295  continue;
296 
297  // look if the space needed can be found in next strip definitions
298  for (var i = currentDefinitionIndex + 1; i < elementToStripDefinitions[dim][element].Count; i++)
299  {
300  var def = elementToStripDefinitions[dim][element][i];
301 
302  if (def.Type == StripType.Auto)
303  spaceNeeded = Math.Max(0, spaceNeeded - (def.MaximumSize - def.ActualSize));
304 
305  if (spaceNeeded <= 0) // if no space is needed anymore, there is no need to continue the process
306  break;
307  }
308  // increase the strip size by the needed space
309  currentDefinition.ActualSize = currentDefinition.ClampSizeByMinimumMaximum(currentDefinition.ActualSize + spaceNeeded);
310  }
311  }
312  }
313 
314  // 5. Calculate the actual size of 1-star strip.
315  CalculateStarStripSize(availableSizeWithoutMargins);
316 
317  // 6. Re-measure all the children, this time with the exact available size.
318  foreach (var child in VisualChildrenCollection)
319  {
320  var availableToChildWithMargin = Vector3.Zero;
321  for (var dim = 0; dim < 3; dim++)
322  availableToChildWithMargin[dim] = SumStripCurrentSize(elementToStripDefinitions[dim][child]);
323 
324  child.Measure(availableToChildWithMargin);
325  }
326 
327  // 7. Calculate the size needed by the grid in order to be able to contain all its children.
328  // This consist at finding the size of 1-star strip so that elements enter into the star-size strips.
329  //
330  // For each grid dimension:
331  // -> calculate the size required by 1-star
332  // -> update the actual size of the star-sized elements
333  // -> calculate the size needed by the grid
334  //
335  var neededSize = Vector3.Zero;
336  for (var dim = 0; dim < 3; dim++)
337  {
338  var definitions = stripDefinitions[dim];
339 
340  // Determine the size needed by 1-star so that all the elements can enter the grid.
341  // The task is greatly complicated by the existence of minimum and maximum size for the strips.
342  var oneStarSize = 0f;
343  foreach (var element in partialStarElementToStripDefinitions[dim].Keys)
344  {
345  var elementDefinitions = partialStarElementToStripDefinitions[dim][element];
346 
347  // clear previous cached values
348  minSortedStarDefinitions.Clear();
349  maxSortedStarDefinitions.Clear();
350 
351  // calculate the space missing for the element
352  var availableSpace = 0f;
353  foreach (var def in elementDefinitions)
354  {
355  if (def.Type == StripType.Star)
356  {
357  def.ActualSize = def.MinimumSize;
358  minSortedStarDefinitions.Add(def);
359  }
360  availableSpace += def.ActualSize;
361  }
362  var currentNeededSpace = Math.Max(0, element.DesiredSizeWithMargins[dim] - availableSpace);
363 
364  // sort the star definition by increasing relative minimum and maximum values
365  minSortedStarDefinitions.Sort(sortByIncreasingMinimumComparer);
366 
367  // calculate the size needed for 1-star for this element
368  // -> starting with the element with the smallest relative minimum size,
369  // we progressively increase the star strip size until reaching the needed size
370  var neededOneStarSize = 0f;
371  for (var minIndex = 0; minIndex < minSortedStarDefinitions.Count && currentNeededSpace > 0; ++minIndex)
372  {
373  var minDefinition = minSortedStarDefinitions[minIndex];
374 
375  maxSortedStarDefinitions.Add(minDefinition); // add current definition to the list of definition that can have their size increased
376  maxSortedStarDefinitions.Sort(sortByIncreasingMaximumComparer);
377 
378  var nextDefinitionRelativeMinSize = (minIndex == minSortedStarDefinitions.Count - 1) ? float.PositiveInfinity : minSortedStarDefinitions[minIndex + 1].ValueRelativeMinimum();
379  var minNextRelativeStepSizeIncrease = Math.Min(currentNeededSpace / SumValues(maxSortedStarDefinitions), nextDefinitionRelativeMinSize - minDefinition.ValueRelativeMinimum());
380 
381  while (minNextRelativeStepSizeIncrease > 0 && maxSortedStarDefinitions.Count > 0)
382  {
383  var maxDefinition = maxSortedStarDefinitions[0];
384  var maxNextStepSizeIncrease = maxDefinition.SizeValue * minNextRelativeStepSizeIncrease;
385  var maxNextStepRelativeSizeIncreate = Math.Min(minNextRelativeStepSizeIncrease, (maxDefinition.MaximumSize - maxDefinition.ActualSize) / maxDefinition.SizeValue);
386 
387  // remove the size of the max increase from the min target increase
388  minNextRelativeStepSizeIncrease -= maxNextStepRelativeSizeIncreate;
389 
390  // determine if the current element has reached its maximum size
391  var maxDefinitionReachedItsMax = maxDefinition.ActualSize + maxNextStepSizeIncrease >= maxDefinition.MaximumSize;
392 
393  // update the actual size of all the max definitions and reduce the needed size accordingly
394  foreach (var definition in maxSortedStarDefinitions)
395  {
396  var absoluteIncrease = maxNextStepRelativeSizeIncreate * definition.SizeValue;
397  currentNeededSpace -= absoluteIncrease;
398  definition.ActualSize += absoluteIncrease;
399  }
400 
401  // if the element has reached its maximum -> we remove it from the list for the next iteration
402  if (maxDefinitionReachedItsMax)
403  {
404  var minNextStepSizeIncrease = minNextRelativeStepSizeIncrease * SumValues(maxSortedStarDefinitions);
405  maxSortedStarDefinitions.Remove(maxDefinition);
406  minNextRelativeStepSizeIncrease = minNextStepSizeIncrease / SumValues(maxSortedStarDefinitions);
407  }
408 
409  // update the size needed for one star
410  neededOneStarSize = Math.Max(neededOneStarSize, maxDefinition.ActualSize / maxDefinition.SizeValue);
411  }
412  }
413 
414  // update the grid dimension-global 1-star size
415  oneStarSize = Math.Max(oneStarSize, neededOneStarSize);
416  }
417 
418  // Update all the star strip size
419  foreach (var starDefinition in dimToStarDefinitions[dim])
420  starDefinition.ActualSize = starDefinition.ClampSizeByMinimumMaximum(oneStarSize * starDefinition.SizeValue);
421 
422  // determine to size needed by the grid
423  neededSize[dim] += SumStripCurrentSize(definitions);
424  }
425 
426  return neededSize;
427  }
428 
429  /// <summary>
430  /// Set the size of all the fix-sized strips, and initialize the size of auto/star-sized strips to their minimal size.
431  /// </summary>
432  /// <param name="definitions">The strip definitions</param>
433  private static void InitializeStripDefinitionActualSize(StripDefinitionCollection definitions)
434  {
435  foreach (var definition in definitions)
436  {
437  var stripSize = 0f;
438 
439  if (definition.Type == StripType.Fixed)
440  stripSize = definition.SizeValue;
441 
442  definition.ActualSize = definition.ClampSizeByMinimumMaximum(stripSize);
443  }
444  }
445 
446  protected override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins)
447  {
448  // determine the size of the star strips now that we have the final available size.
449  CalculateStarStripSize(finalSizeWithoutMargins);
450 
451  // Update strip starting position cache data
452  RebuildStripPositionCacheData();
453 
454  // calculate the final size of the grid.
455  var gridFinalSize = Vector3.Zero;
456  for (var dim = 0; dim < 3; dim++)
457  gridFinalSize[dim] = Math.Max(cachedStripIndexToStripPosition[dim][stripDefinitions[dim].Count], finalSizeWithoutMargins[dim]);
458 
459  // arrange the children
460  foreach (var child in VisualChildrenCollection)
461  {
462  // calculate child position
463  var gridPosition = GetElementGridPositions(child);
464  var position = new Vector3(
465  cachedStripIndexToStripPosition[0][gridPosition.X],
466  cachedStripIndexToStripPosition[1][gridPosition.Y],
467  cachedStripIndexToStripPosition[2][gridPosition.Z]);
468 
469  // set the arrange matrix values
470  child.DependencyProperties.Set(PanelArrangeMatrixPropertyKey, Matrix.Translation(position - gridFinalSize / 2));
471 
472  // calculate the size provided to the child
473  var providedSize = new Vector3(
474  SumStripCurrentSize(elementToStripDefinitions[0][child]),
475  SumStripCurrentSize(elementToStripDefinitions[1][child]),
476  SumStripCurrentSize(elementToStripDefinitions[2][child]));
477 
478  // arrange the child
479  child.Arrange(providedSize, IsCollapsed);
480  }
481 
482  return gridFinalSize;
483  }
484 
485  private void CalculateStarStripSize(Vector3 finalSizeWithoutMargins)
486  {
487  // calculate the ActualSize of the start-sized strips. Possible minimum and maximum values have to be taken in account for that calculation.
488  for (var dim = 0; dim < 3; dim++)
489  {
490  starDefinitionsCopy.Clear();
491  starDefinitionsCopy.AddRange(dimToStarDefinitions[dim]);
492 
493  // compute the size taken by fixed and auto strips
494  var spaceTakenByFixedAndAutoStrips = SumStripAutoAndFixedSize(stripDefinitions[dim]);
495 
496  // calculate the size remaining for the start-sized strips
497  var spaceRemainingForStarStrips = Math.Max(0f, finalSizeWithoutMargins[dim] - spaceTakenByFixedAndAutoStrips);
498 
499  // calculate the total value of the stars.
500  var starValuesSum = SumValues(starDefinitionsCopy);
501 
502  // calculate the space dedicated to one star
503  var oneStarSpace = spaceRemainingForStarStrips / starValuesSum;
504 
505  // At this point we have the size of a 1-star-sized strip if none of strips are saturated by their min or max size values.
506  // In following loop we progressively refine the value until reaching the final value for 1-star-sized strip.
507  // Our algorithm works as follow:
508  // 1. Finding saturated strips (by min or max)
509  // 2. Determine if size taken by star-sized strip will increase or decrease due to saturated strips.
510  // 3. Updating the total size dedicated of star-sized strips by removing the size taken by min (resp. max) saturated strips
511  // 4. Updating the total remaining star value by removing the star-values of min (resp. max) saturated strips
512  // 5. Updating size of 1-star-sized strip.
513  // 6. Removing from the star-sized strip list the min (resp. max) saturated strips.
514  // 7. As new strips can now reach min (resp. max) saturation with the decreased (resp. increase) of the 1-star-sized strip size,
515  // repeat the process until none of the remaining strips are saturated anymore.
516  //
517  // Note that termination is ensured by the fact the set of star-sized to measure strictly decrease at each iteration.
518  do
519  {
520  maxBoundedStarDefinitions.Clear();
521  minBoundedStarDefinitions.Clear();
522 
523  // find the min/max saturated strips.
524  foreach (var definition in starDefinitionsCopy)
525  {
526  definition.ActualSize = definition.SizeValue * oneStarSpace;
527  if (definition.ActualSize < definition.MinimumSize)
528  {
529  definition.ActualSize = definition.MinimumSize;
530  minBoundedStarDefinitions.Add(definition);
531  }
532  else if (definition.ActualSize > definition.MaximumSize)
533  {
534  definition.ActualSize = definition.MaximumSize;
535  maxBoundedStarDefinitions.Add(definition);
536  }
537  }
538 
539  // re-calculate the size taken by star-sized strips (taking into account saturated strips)
540  var resultingSize = SumStripCurrentSize(starDefinitionsCopy);
541 
542  // determine if we have to trim max or min saturated star strips
543  var strimList = resultingSize < spaceRemainingForStarStrips ? maxBoundedStarDefinitions : minBoundedStarDefinitions;
544 
545  // update the size of 1-star-strip
546  spaceRemainingForStarStrips -= SumStripCurrentSize(strimList);
547  starValuesSum -= SumValues(strimList);
548  oneStarSpace = spaceRemainingForStarStrips / starValuesSum;
549 
550  // remove definitions of star strip that will remain saturated until the end of the process from the to-measure list
551  foreach (var definition in strimList)
552  starDefinitionsCopy.Remove(definition);
553  }
554  // stops the process if either there is no saturated strip or no star-sized strip to measure anymore.
555  while ((maxBoundedStarDefinitions.Count != 0 || minBoundedStarDefinitions.Count != 0) && starDefinitionsCopy.Count != 0);
556  }
557  }
558 
559  protected override void OnLogicalChildRemoved(UIElement oldElement, int index)
560  {
561  base.OnLogicalChildRemoved(oldElement, index);
562 
563  for (var dim = 0; dim < 3; dim++)
564  {
565  // remove the strip definitions associated to the removed child
566  elementToStripDefinitions[dim].Remove(oldElement);
567 
568  // remove the strip definitions associated to the removed child
569  partialStarElementToStripDefinitions[dim].Remove(oldElement);
570 
571  autoDefinedElements.Remove(oldElement);
572  }
573  }
574 
575  protected override void OnLogicalChildAdded(UIElement newElement, int index)
576  {
577  base.OnLogicalChildAdded(newElement, index);
578 
579  for (var dim = 0; dim < 3; dim++)
580  {
581  // ensure that all children have a associate list strip definitions
582  elementToStripDefinitions[dim][newElement] = new List<StripDefinition>();
583 
584  // ensure that all children have a associate list strip definitions
585  partialStarElementToStripDefinitions[dim][newElement] = new List<StripDefinition>();
586  }
587  }
588 
589  private void RebuildMeasureCacheData()
590  {
591  // clear existing cache data
592  for (var dim = 0; dim < 3; dim++)
593  {
594  // the 'stripIndexToNoStarElements' entries
595  for (var index = 0; index < stripDefinitions[dim].Count; ++index)
596  {
597  if (stripIndexToNoStarElements[dim].Count <= index)
598  stripIndexToNoStarElements[dim].Add(new List<UIElement>());
599 
600  stripIndexToNoStarElements[dim][index].Clear();
601  }
602 
603  // the 'elementToStripDefinitions' entries
604  foreach (var list in elementToStripDefinitions[dim].Values)
605  list.Clear();
606 
607  // the 'partialStarElementToStripDefinitions' entries
608  foreach (var list in partialStarElementToStripDefinitions[dim].Values)
609  list.Clear();
610  }
611  autoDefinedElements.Clear();
612 
613  // build 'elementToStripDefinitions', stripIndexToNoStarElements', 'partialStarElementToStripDefinitions' and 'autoDefinedElements'
614  foreach (var child in VisualChildrenCollection)
615  {
616  var childPosition = GetElementGridPositions(child);
617  var childSpan = GetElementSpanValues(child);
618 
619  for (var dim = 0; dim < 3; ++dim)
620  {
621  var childHasNoStarDefinitions = true;
622 
623  // fill 'elementToStripDefinitions'
624  for (var i = childPosition[dim]; i < childPosition[dim] + childSpan[dim]; i++)
625  {
626  if (stripDefinitions[dim][i].Type == StripType.Star)
627  childHasNoStarDefinitions = false;
628  else if (stripDefinitions[dim][i].Type == StripType.Auto)
629  autoDefinedElements.Add(child);
630 
631  elementToStripDefinitions[dim][child].Add(stripDefinitions[dim][i]);
632  }
633 
634  // fill 'stripIndexToNoStarElements' and 'partialStarElementToStripDefinitions'
635  if (childHasNoStarDefinitions)
636  {
637  for (var i = childPosition[dim]; i < childPosition[dim] + childSpan[dim]; i++)
638  stripIndexToNoStarElements[dim][i].Add(child);
639  }
640  else
641  {
642  for (var i = childPosition[dim]; i < childPosition[dim] + childSpan[dim]; i++)
643  partialStarElementToStripDefinitions[dim][child].Add(stripDefinitions[dim][i]);
644  }
645  }
646  }
647 
648  // build the star definitions cache
649  for (var dim = 0; dim < 3; ++dim)
650  {
651  dimToStarDefinitions[dim].Clear();
652  foreach (var definition in stripDefinitions[dim])
653  if(definition.Type == StripType.Star)
654  dimToStarDefinitions[dim].Add(definition);
655  }
656  }
657 
658  private void CheckChildrenPositionsAndAdjustGridSize()
659  {
660  // add default strip definitions as long as one element is partially outside of the grid
661  foreach (var child in VisualChildrenCollection)
662  {
663  var childLastStripPlusOne = GetElementGridPositions(child) + GetElementSpanValues(child);
664  for (var dim = 0; dim < 3; dim++)
665  {
666  if (stripDefinitions[dim].Count < childLastStripPlusOne[dim])
667  logger.Warning("Element 'Name={0}' is outside of the grid 'Name={1}' definition for [{2}]. Auto strip definitions will be added to complete the grid definition.", child, Name, dim == 0 ? "Column" : dim == 1 ? "Row" : "Layer");
668 
669  while (stripDefinitions[dim].Count < childLastStripPlusOne[dim])
670  stripDefinitions[dim].Add(new StripDefinition(StripType.Auto));
671  }
672  }
673  }
674 
675  private void RebuildStripPositionCacheData()
676  {
677  // rebuild strip begin position cached data
678  for (var dim = 0; dim < 3; dim++)
679  {
680  //clear last cached data
681  cachedStripIndexToStripPosition[dim].Clear();
682 
683  // calculate the strip start position
684  var startPosition = 0f;
685  for (var index = 0; index < stripDefinitions[dim].Count; index++)
686  {
687  cachedStripIndexToStripPosition[dim].Add(startPosition);
688  startPosition += stripDefinitions[dim][index].ActualSize;
689  }
690  cachedStripIndexToStripPosition[dim].Add(startPosition);
691  }
692  }
693 
694  private float SumStripCurrentSize(StripDefinitionCollection definitions)
695  {
696  var sum = 0f;
697  foreach (var def in definitions) // do not use linq to avoid allocations
698  sum += def.ActualSize;
699 
700  return sum;
701  }
702 
703  private float SumStripCurrentSize(List<StripDefinition> definitions)
704  {
705  var sum = 0f;
706  foreach (var def in definitions) // do not use linq to avoid allocations
707  sum += def.ActualSize;
708 
709  return sum;
710  }
711 
712  private float SumStripAutoAndFixedSize(StripDefinitionCollection definitions)
713  {
714  var sum = 0f;
715  foreach (var def in definitions) // do not use linq to avoid allocations
716  if (def.Type != StripType.Star)
717  sum += def.ActualSize;
718 
719  return sum;
720  }
721 
722  private float SumValues(List<StripDefinition> definitions) // use List instead of IEnumerable in order to avoid boxing in "foreach"
723  {
724  var sum = 0f;
725  foreach (var def in definitions) // do not use linq to avoid allocations
726  sum += def.SizeValue;
727 
728  return sum;
729  }
730 
731  private void GetDistanceToSurroundingAnchors(List<float> stripPosition, float position, out Vector2 distances)
732  {
733  if (stripPosition.Count < 2)
734  {
735  distances = Vector2.Zero;
736  return;
737  }
738 
739  var validPosition = Math.Max(0, Math.Min(position, stripPosition[stripPosition.Count-1]));
740 
741  var index = 1;
742  while (index < stripPosition.Count-1 && stripPosition[index] <= validPosition)
743  ++index;
744 
745  distances = new Vector2(stripPosition[index - 1], stripPosition[index]);
746  distances -= new Vector2(validPosition, validPosition);
747  }
748 
749  public override Vector2 GetSurroudingAnchorDistances(Orientation direction, float position)
750  {
751  Vector2 distances;
752 
753  GetDistanceToSurroundingAnchors(cachedStripIndexToStripPosition[(int)direction], position, out distances);
754 
755  return distances;
756  }
757  }
758 }
StripType
The different types of strip possible of a grid.
Definition: StripType.cs:8
Provides a base class for all the User Interface elements in Paradox applications.
Definition: UIElement.cs:21
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
Represent the definition of a grid strip.
Represents a two dimensional mathematical vector.
Definition: Vector2.cs:42
Orientation
Defines the different orientations that a control or layout can have.
Definition: Orientation.cs:8
override Vector2 GetSurroudingAnchorDistances(Orientation direction, float position)
Get the distances to the previous and next anchors in the provided direction and from given position...
Definition: Grid.cs:749
SiliconStudio.Paradox.Input.Keys Keys
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins)
When overridden in a derived class, positions possible child elements and determines a size for a UIE...
Definition: Grid.cs:446
override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
When overridden in a derived class, measures the size in layout required for possible child elements ...
Definition: Grid.cs:172
Base implementation for ILogger.
Definition: Logger.cs:10
switch(inFormat)
override void OnLogicalChildRemoved(UIElement oldElement, int index)
Action to perform when a logical child is removed.
Definition: Grid.cs:559
override void OnLogicalChildAdded(UIElement newElement, int index)
Action to perform when a logical child is added.
Definition: Grid.cs:575
object Item
Gets the added or removed item (if dictionary, value only).
Represents the base primitive for all the grid-like controls
Definition: GridBase.cs:15
NotifyCollectionChangedAction Action
Gets the type of action performed. Allowed values are NotifyCollectionChangedAction.Add and NotifyCollectionChangedAction.Remove.
SiliconStudio.Core.Mathematics.Vector3 Vector3
Represents a grid control with adjustable columns, rows and layers.
Definition: Grid.cs:18