FlowLayout.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. //
  2. // FlowLayout.cs
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining
  5. // a copy of this software and associated documentation files (the
  6. // "Software"), to deal in the Software without restriction, including
  7. // without limitation the rights to use, copy, modify, merge, publish,
  8. // distribute, sublicense, and/or sell copies of the Software, and to
  9. // permit persons to whom the Software is furnished to do so, subject to
  10. // the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be
  13. // included in all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  19. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  20. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  21. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. //
  23. // Copyright (c) 2006 Jonathan Pobst
  24. //
  25. // Authors:
  26. // Jonathan Pobst ([email protected])
  27. //
  28. #if NET_2_0
  29. using System;
  30. using System.Collections.Generic;
  31. using System.Drawing;
  32. namespace System.Windows.Forms.Layout
  33. {
  34. class FlowLayout : LayoutEngine
  35. {
  36. private static FlowLayoutSettings default_settings = new FlowLayoutSettings ();
  37. public FlowLayout ()
  38. {
  39. }
  40. public override void InitLayout (object child, BoundsSpecified specified)
  41. {
  42. base.InitLayout (child, specified);
  43. }
  44. public override bool Layout (object container, LayoutEventArgs args)
  45. {
  46. if (container is ToolStripPanel)
  47. return false;
  48. Control parent = container as Control;
  49. FlowLayoutSettings settings;
  50. if (parent is FlowLayoutPanel)
  51. settings = (parent as FlowLayoutPanel).LayoutSettings;
  52. else
  53. settings = default_settings;
  54. // Nothing to layout, exit method
  55. if (parent.Controls.Count == 0) return false;
  56. // Use DisplayRectangle so that parent.Padding is honored.
  57. Rectangle parentDisplayRectangle = parent.DisplayRectangle;
  58. Point currentLocation;
  59. // Set our starting point based on flow direction
  60. switch (settings.FlowDirection) {
  61. case FlowDirection.BottomUp:
  62. currentLocation = new Point (parentDisplayRectangle.Left, parentDisplayRectangle.Bottom);
  63. break;
  64. case FlowDirection.LeftToRight:
  65. case FlowDirection.TopDown:
  66. default:
  67. currentLocation = parentDisplayRectangle.Location;
  68. break;
  69. case FlowDirection.RightToLeft:
  70. currentLocation = new Point (parentDisplayRectangle.Right, parentDisplayRectangle.Top);
  71. break;
  72. }
  73. bool forceFlowBreak = false;
  74. List<Control> rowControls = new List<Control> ();
  75. foreach (Control c in parent.Controls) {
  76. // Only apply layout to visible controls.
  77. if (!c.Visible) { continue; }
  78. // Resize any AutoSize controls to their preferred size
  79. if (c.AutoSize == true)
  80. c.Size = c.GetPreferredSize (c.Size);
  81. switch (settings.FlowDirection) {
  82. case FlowDirection.BottomUp:
  83. // Decide if it's time to start a new column
  84. // - Our settings must be WrapContents, and we ran out of room or the previous control's FlowBreak == true
  85. if (settings.WrapContents)
  86. if ((currentLocation.Y) < (c.Height + c.Margin.Top + c.Margin.Bottom) || forceFlowBreak) {
  87. currentLocation.X = FinishColumn (rowControls);
  88. currentLocation.Y = parentDisplayRectangle.Bottom;
  89. forceFlowBreak = false;
  90. rowControls.Clear ();
  91. }
  92. // Offset the right margin and set the control to our point
  93. currentLocation.Offset (0, c.Margin.Bottom * -1);
  94. c.Location = new Point (currentLocation.X + c.Margin.Left, currentLocation.Y - c.Height);
  95. // Update our location pointer
  96. currentLocation.Y -= (c.Height + c.Margin.Top);
  97. break;
  98. case FlowDirection.LeftToRight:
  99. default:
  100. // Decide if it's time to start a new row
  101. // - Our settings must be WrapContents, and we ran out of room or the previous control's FlowBreak == true
  102. if (settings.WrapContents && !(parent is ToolStripPanel))
  103. if ((parentDisplayRectangle.Width - currentLocation.X) < (c.Width + c.Margin.Left + c.Margin.Right) || forceFlowBreak) {
  104. currentLocation.Y = FinishRow (rowControls);
  105. currentLocation.X = parentDisplayRectangle.Left;
  106. forceFlowBreak = false;
  107. rowControls.Clear ();
  108. }
  109. // Offset the left margin and set the control to our point
  110. currentLocation.Offset (c.Margin.Left, 0);
  111. c.Location = new Point (currentLocation.X, currentLocation.Y + c.Margin.Top);
  112. // Update our location pointer
  113. currentLocation.X += c.Width + c.Margin.Right;
  114. break;
  115. case FlowDirection.RightToLeft:
  116. // Decide if it's time to start a new row
  117. // - Our settings must be WrapContents, and we ran out of room or the previous control's FlowBreak == true
  118. if (settings.WrapContents)
  119. if ((currentLocation.X) < (c.Width + c.Margin.Left + c.Margin.Right) || forceFlowBreak) {
  120. currentLocation.Y = FinishRow (rowControls);
  121. currentLocation.X = parentDisplayRectangle.Right;
  122. forceFlowBreak = false;
  123. rowControls.Clear ();
  124. }
  125. // Offset the right margin and set the control to our point
  126. currentLocation.Offset (c.Margin.Right * -1, 0);
  127. c.Location = new Point (currentLocation.X - c.Width, currentLocation.Y + c.Margin.Top);
  128. // Update our location pointer
  129. currentLocation.X -= (c.Width + c.Margin.Left);
  130. break;
  131. case FlowDirection.TopDown:
  132. // Decide if it's time to start a new column
  133. // - Our settings must be WrapContents, and we ran out of room or the previous control's FlowBreak == true
  134. if (settings.WrapContents)
  135. if ((parentDisplayRectangle.Height - currentLocation.Y) < (c.Height + c.Margin.Top + c.Margin.Bottom) || forceFlowBreak) {
  136. currentLocation.X = FinishColumn (rowControls);
  137. currentLocation.Y = parentDisplayRectangle.Top;
  138. forceFlowBreak = false;
  139. rowControls.Clear ();
  140. }
  141. // Offset the top margin and set the control to our point
  142. currentLocation.Offset (0, c.Margin.Top);
  143. c.Location = new Point (currentLocation.X + c.Margin.Left, currentLocation.Y);
  144. // Update our location pointer
  145. currentLocation.Y += c.Height + c.Margin.Bottom;
  146. break;
  147. }
  148. // Add it to our list of things to adjust the second dimension of
  149. rowControls.Add (c);
  150. // If user set a flowbreak on this control, it will be the last one in this row/column
  151. if (settings.GetFlowBreak (c))
  152. forceFlowBreak = true;
  153. }
  154. // Set the control heights/widths for the last row/column
  155. if (settings.FlowDirection == FlowDirection.LeftToRight || settings.FlowDirection == FlowDirection.RightToLeft)
  156. FinishRow (rowControls);
  157. else
  158. FinishColumn (rowControls);
  159. return false;
  160. }
  161. // Calculate the heights of the controls, returns the y coordinate of the greatest height it uses
  162. private int FinishRow (List<Control> row)
  163. {
  164. // Nothing to do
  165. if (row.Count == 0) return 0;
  166. int rowTop = int.MaxValue;
  167. int rowBottom = 0;
  168. bool allDockFill = true;
  169. bool noAuto = true;
  170. // Special semantics if all controls are Dock.Fill/Anchor:Top,Bottom or AutoSize = true
  171. foreach (Control c in row) {
  172. if (c.Dock != DockStyle.Fill && !((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top && (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom))
  173. allDockFill = false;
  174. if (c.AutoSize == true)
  175. noAuto = false;
  176. }
  177. // Find the tallest control with a concrete height
  178. foreach (Control c in row) {
  179. if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock != DockStyle.Fill) && ((c.Anchor & AnchorStyles.Top) != AnchorStyles.Top || (c.Anchor & AnchorStyles.Bottom) != AnchorStyles.Bottom || c.AutoSize == true))
  180. rowBottom = c.Bottom + c.Margin.Bottom;
  181. if (c.Top - c.Margin.Top < rowTop)
  182. rowTop = c.Top - c.Margin.Top;
  183. }
  184. // Find the tallest control that is AutoSize = true
  185. if (rowBottom == 0)
  186. foreach (Control c in row)
  187. if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock != DockStyle.Fill && c.AutoSize == true))
  188. rowBottom = c.Bottom + c.Margin.Bottom;
  189. // Find the tallest control that is Dock = Fill
  190. if (rowBottom == 0)
  191. foreach (Control c in row)
  192. if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock == DockStyle.Fill))
  193. rowBottom = c.Bottom + c.Margin.Bottom;
  194. // Set the new heights for each control
  195. foreach (Control c in row)
  196. if (allDockFill && noAuto)
  197. c.Height = 0;
  198. else if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top) && ((c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom))
  199. c.Height = rowBottom - c.Top - c.Margin.Bottom;
  200. else if (c.Dock == DockStyle.Bottom || ((c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom))
  201. c.Top = rowBottom - c.Margin.Bottom - c.Height;
  202. else if (c.Dock == DockStyle.Top || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top))
  203. continue;
  204. else
  205. c.Top = ((rowBottom - rowTop) / 2) - (c.Height / 2) + (int)Math.Floor (((c.Margin.Top - c.Margin.Bottom) / 2.0)) + rowTop;
  206. // Return bottom y of this row used
  207. if (rowBottom == 0)
  208. return rowTop;
  209. return rowBottom;
  210. }
  211. // Calculate the widths of the controls, returns the x coordinate of the greatest width it uses
  212. private int FinishColumn (List<Control> col)
  213. {
  214. // Nothing to do
  215. if (col.Count == 0) return 0;
  216. int rowLeft = int.MaxValue;
  217. int rowRight = 0;
  218. bool allDockFill = true;
  219. bool noAuto = true;
  220. // Special semantics if all controls are Dock.Fill/Anchor:Left,Right or AutoSize = true
  221. foreach (Control c in col) {
  222. if (c.Dock != DockStyle.Fill && !((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left && (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right))
  223. allDockFill = false;
  224. if (c.AutoSize == true)
  225. noAuto = false;
  226. }
  227. // Find the widest control with a concrete width
  228. foreach (Control c in col) {
  229. if (c.Right + c.Margin.Right > rowRight && (c.Dock != DockStyle.Fill) && ((c.Anchor & AnchorStyles.Left) != AnchorStyles.Left || (c.Anchor & AnchorStyles.Right) != AnchorStyles.Right || c.AutoSize == true))
  230. rowRight = c.Right + c.Margin.Right;
  231. if (c.Left - c.Margin.Left < rowLeft)
  232. rowLeft = c.Left - c.Margin.Left;
  233. }
  234. // Find the widest control that is AutoSize = true
  235. if (rowRight == 0)
  236. foreach (Control c in col)
  237. if (c.Right + c.Margin.Right > rowRight && (c.Dock != DockStyle.Fill && c.AutoSize == true))
  238. rowRight = c.Right + c.Margin.Right;
  239. // Find the widest control that is Dock = Fill
  240. if (rowRight == 0)
  241. foreach (Control c in col)
  242. if (c.Right + c.Margin.Right > rowRight && (c.Dock == DockStyle.Fill))
  243. rowRight = c.Right + c.Margin.Right;
  244. // Set the new widths for each control
  245. foreach (Control c in col)
  246. if (allDockFill && noAuto)
  247. c.Width = 0;
  248. else if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left) && ((c.Anchor & AnchorStyles.Right) == AnchorStyles.Right))
  249. c.Width = rowRight - c.Left - c.Margin.Right;
  250. else if (c.Dock == DockStyle.Right || ((c.Anchor & AnchorStyles.Right) == AnchorStyles.Right))
  251. c.Left = rowRight - c.Margin.Right - c.Width;
  252. else if (c.Dock == DockStyle.Left || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left))
  253. continue;
  254. else
  255. c.Left = ((rowRight - rowLeft) / 2) - (c.Width / 2) + (int)Math.Floor (((c.Margin.Left - c.Margin.Right) / 2.0)) + rowLeft;
  256. // Return rightmost x of this row used
  257. if (rowRight == 0)
  258. return rowLeft;
  259. return rowRight;
  260. }
  261. }
  262. }
  263. #endif