Guest User

Untitled

a guest
Nov 24th, 2017
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.06 KB | None | 0 0
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3.  
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using Avalonia.Logging;
  8. using Avalonia.Threading;
  9.  
  10. namespace Avalonia.Layout
  11. {
  12. /// <summary>
  13. /// Manages measuring and arranging of controls.
  14. /// </summary>
  15. public class LayoutManager : ILayoutManager
  16. {
  17. private Queue<ILayoutable> _toMeasure = new Queue<ILayoutable>();
  18. private Queue<ILayoutable> _toArrange = new Queue<ILayoutable>();
  19. private bool _queued;
  20. private bool _running;
  21.  
  22. /// <summary>
  23. /// Gets the layout manager.
  24. /// </summary>
  25. public static ILayoutManager Instance => AvaloniaLocator.Current.GetService<ILayoutManager>();
  26.  
  27. /// <inheritdoc/>
  28. public void InvalidateMeasure(ILayoutable control)
  29. {
  30. Contract.Requires<ArgumentNullException>(control != null);
  31. Dispatcher.UIThread.VerifyAccess();
  32.  
  33. if (!control.IsAttachedToVisualTree)
  34. {
  35. #if DEBUG
  36. throw new AvaloniaInternalException(
  37. "LayoutManager.InvalidateMeasure called on a control that is detached from the visual tree.");
  38. #else
  39. return;
  40. #endif
  41. }
  42.  
  43. _toMeasure.Enqueue(control);
  44. _toArrange.Enqueue(control);
  45. QueueLayoutPass();
  46. }
  47.  
  48. /// <inheritdoc/>
  49. public void InvalidateArrange(ILayoutable control)
  50. {
  51. Contract.Requires<ArgumentNullException>(control != null);
  52. Dispatcher.UIThread.VerifyAccess();
  53.  
  54. if (!control.IsAttachedToVisualTree)
  55. {
  56. #if DEBUG
  57. throw new AvaloniaInternalException(
  58. "LayoutManager.InvalidateArrange called on a control that is detached from the visual tree.");
  59. #else
  60. return;
  61. #endif
  62. }
  63.  
  64. _toArrange.Enqueue(control);
  65. QueueLayoutPass();
  66. }
  67.  
  68. /// <inheritdoc/>
  69. public void ExecuteLayoutPass()
  70. {
  71. const int MaxPasses = 3;
  72.  
  73. Dispatcher.UIThread.VerifyAccess();
  74.  
  75. if (!_running)
  76. {
  77. _running = true;
  78.  
  79. Logger.Information(
  80. LogArea.Layout,
  81. this,
  82. "Started layout pass. To measure: {Measure} To arrange: {Arrange}",
  83. _toMeasure.Count,
  84. _toArrange.Count);
  85.  
  86. var stopwatch = new System.Diagnostics.Stopwatch();
  87. stopwatch.Start();
  88.  
  89. try
  90. {
  91. for (var pass = 0; pass < MaxPasses; ++pass)
  92. {
  93. var toMeasure = _toMeasure;
  94. var toArrange = _toArrange;
  95.  
  96. _toMeasure = new Queue<ILayoutable>();
  97. _toArrange = new Queue<ILayoutable>();
  98.  
  99. ExecuteMeasurePass(toMeasure);
  100. ExecuteArrangePass(toArrange);
  101.  
  102. if (_toMeasure.Count == 0)
  103. {
  104. break;
  105. }
  106. }
  107. }
  108. finally
  109. {
  110. _running = false;
  111. }
  112.  
  113. stopwatch.Stop();
  114. Logger.Information(LogArea.Layout, this, "Layout pass finised in {Time}", stopwatch.Elapsed);
  115. }
  116.  
  117. _queued = false;
  118. }
  119.  
  120. /// <inheritdoc/>
  121. public void ExecuteInitialLayoutPass(ILayoutRoot root)
  122. {
  123. Measure(root);
  124. Arrange(root);
  125.  
  126. // Running the initial layout pass may have caused some control to be invalidated
  127. // so run a full layout pass now (this usually due to scrollbars; its not known
  128. // whether they will need to be shown until the layout pass has run and if the
  129. // first guess was incorrect the layout will need to be updated).
  130. ExecuteLayoutPass();
  131. }
  132.  
  133. private void ExecuteMeasurePass(Queue<ILayoutable> toMeasure)
  134. {
  135. while (toMeasure.Count > 0)
  136. {
  137. var control = toMeasure.Dequeue();
  138.  
  139. if (!control.IsMeasureValid && control.IsAttachedToVisualTree)
  140. {
  141. Measure(control);
  142. }
  143. }
  144. }
  145.  
  146. private void ExecuteArrangePass(Queue<ILayoutable> toArrange)
  147. {
  148. while (toArrange.Count > 0)
  149. {
  150. var control = toArrange.Dequeue();
  151.  
  152. if (!control.IsArrangeValid && control.IsAttachedToVisualTree)
  153. {
  154. Arrange(control);
  155. }
  156. }
  157. }
  158.  
  159. private void Measure(ILayoutable control)
  160. {
  161. // Controls closest to the visual root need to be arranged first. We don't try to store
  162. // ordered invalidation lists, instead we traverse the tree upwards, measuring the
  163. // controls closest to the root first. This has been shown by benchmarks to be the
  164. // fastest and most memory-efficent algorithm.
  165. if (control.VisualParent is ILayoutable parent)
  166. {
  167. Measure(parent);
  168. }
  169.  
  170. // If the control being measured has IsMeasureValid == true here then its measure was
  171. // handed by an ancestor and can be ignored. The measure may have also caused the
  172. // control to be removed.
  173. if (!control.IsMeasureValid && control.IsAttachedToVisualTree)
  174. {
  175. if (control is ILayoutRoot root)
  176. {
  177. root.Measure(Size.Infinity);
  178. }
  179. else
  180. {
  181. control.Measure(control.PreviousMeasure.Value);
  182. }
  183. }
  184. }
  185.  
  186. private void Arrange(ILayoutable control)
  187. {
  188. if (control.VisualParent is ILayoutable parent)
  189. {
  190. Arrange(parent);
  191. }
  192.  
  193. if (!control.IsArrangeValid && control.IsAttachedToVisualTree)
  194. {
  195. if (control is IEmbeddedLayoutRoot embeddedRoot)
  196. control.Arrange(new Rect(embeddedRoot.AllocatedSize));
  197. else if (control is ILayoutRoot root)
  198. control.Arrange(new Rect(root.DesiredSize));
  199. else if (control.PreviousArrange != null)
  200. {
  201. // Has been observed that PreviousArrange sometimes is null, probably a bug somewhere else.
  202. // Condition observed: control.VisualParent is Scrollbar, control is Border.
  203. control.Arrange(control.PreviousArrange.Value);
  204. }
  205. }
  206. }
  207.  
  208. private void QueueLayoutPass()
  209. {
  210. if (!_queued && !_running)
  211. {
  212. Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Layout);
  213. _queued = true;
  214. }
  215. }
  216. }
  217. }
Add Comment
Please, Sign In to add comment