LayoutEngine.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using System.Collections.Generic;
  2. using System.Drawing;
  3. namespace System.Windows.Forms
  4. {
  5. internal class LayoutEngine
  6. {
  7. private Control control;
  8. private List<LayoutTransaction> transactions = new();
  9. private bool suspended;
  10. private bool updatingLayout;
  11. public void SuspendLayout()
  12. {
  13. suspended = true;
  14. }
  15. public void AddTransaction(int deltaX, int deltaY)
  16. {
  17. if (updatingLayout)
  18. return;
  19. int width = control.DisplayRectangle.Width;
  20. int height = control.DisplayRectangle.Height;
  21. if (suspended)
  22. transactions.Add(new LayoutTransaction(deltaX, deltaY, width, height));
  23. else
  24. UpdateLayout(deltaX, deltaY, width, height);
  25. }
  26. public void AddTransaction() => AddTransaction(0, 0);
  27. public void ResumeLayout(bool resume)
  28. {
  29. if (resume)
  30. {
  31. foreach (LayoutTransaction tr in transactions)
  32. {
  33. UpdateLayout(tr.DeltaX, tr.DeltaY, tr.Width, tr.Height);
  34. }
  35. }
  36. transactions.Clear();
  37. suspended = false;
  38. }
  39. private Rectangle Bounds(int left, int top, int width, int height)
  40. {
  41. if (left < 0)
  42. left = 0;
  43. if (top < 0)
  44. top = 0;
  45. if (width < 0)
  46. width = 0;
  47. if (height < 0)
  48. height = 0;
  49. return new Rectangle(left, top, width, height);
  50. }
  51. private void UpdateLayout(int dx, int dy, int width, int height)
  52. {
  53. if (updatingLayout)
  54. return;
  55. updatingLayout = true;
  56. try
  57. {
  58. Rectangle remainingBounds = new Rectangle(control.Padding.Left + control.DefaultPadding.Left,
  59. control.Padding.Top + control.DefaultPadding.Top,
  60. width - control.Padding.Horizontal - control.DefaultPadding.Horizontal,
  61. height - control.Padding.Vertical - control.DefaultPadding.Vertical);
  62. remainingBounds.Width += dx;
  63. remainingBounds.Height += dy;
  64. // phase 1: everything except Dock = Fill
  65. for (int i = control.Controls.Count - 1; i >= 0; i--)
  66. {
  67. var c = control.Controls[i];
  68. if ((c.Anchor & AnchorStyles.Right) != 0)
  69. {
  70. if ((c.Anchor & AnchorStyles.Left) != 0)
  71. c.Width += dx;
  72. else
  73. c.Left += dx;
  74. }
  75. else if ((c.Anchor & AnchorStyles.Left) == 0)
  76. {
  77. c.Left += dx / 2;
  78. }
  79. if ((c.Anchor & AnchorStyles.Bottom) != 0)
  80. {
  81. if ((c.Anchor & AnchorStyles.Top) != 0)
  82. c.Height += dy;
  83. else
  84. c.Top += dy;
  85. }
  86. else if ((c.Anchor & AnchorStyles.Top) == 0)
  87. {
  88. c.Top += dy / 2;
  89. }
  90. if (!c.Visible)
  91. continue;
  92. switch (c.Dock)
  93. {
  94. case DockStyle.Left:
  95. c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Top, c.Width, remainingBounds.Height);
  96. remainingBounds.X += c.Width;
  97. remainingBounds.Width -= c.Width;
  98. break;
  99. case DockStyle.Top:
  100. c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Top, remainingBounds.Width, c.Height);
  101. remainingBounds.Y += c.Height;
  102. remainingBounds.Height -= c.Height;
  103. break;
  104. case DockStyle.Right:
  105. c.Bounds = Bounds(remainingBounds.Right - c.Width, remainingBounds.Top, c.Width, remainingBounds.Height);
  106. remainingBounds.Width -= c.Width;
  107. break;
  108. case DockStyle.Bottom:
  109. c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Bottom - c.Height, remainingBounds.Width, c.Height);
  110. remainingBounds.Height -= c.Height;
  111. break;
  112. }
  113. }
  114. // phase 2: Dock = Fill
  115. for (int i = control.Controls.Count - 1; i >= 0; i--)
  116. {
  117. var c = control.Controls[i];
  118. if (!c.Visible)
  119. continue;
  120. switch (c.Dock)
  121. {
  122. case DockStyle.Fill:
  123. c.Bounds = Bounds(remainingBounds.Left, remainingBounds.Top, remainingBounds.Width, remainingBounds.Height);
  124. break;
  125. }
  126. }
  127. }
  128. finally
  129. {
  130. updatingLayout = false;
  131. control.OnLayout();
  132. }
  133. }
  134. public LayoutEngine(Control control) => this.control = control;
  135. private struct LayoutTransaction
  136. {
  137. public int DeltaX { get; }
  138. public int DeltaY { get; }
  139. public int Width { get; }
  140. public int Height { get; }
  141. public LayoutTransaction(int deltaX, int deltaY, int width, int height)
  142. {
  143. DeltaX = deltaX;
  144. DeltaY = deltaY;
  145. Width = width;
  146. Height = height;
  147. }
  148. }
  149. }
  150. }