2 // ToolStripSplitStackLayout.cs
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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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.
23 // Copyright (c) 2006 Jonathan Pobst
26 // Jonathan Pobst (monkey@jpobst.com)
31 using System.Windows.Forms.Layout;
33 namespace System.Windows.Forms
35 class ToolStripSplitStackLayout : LayoutEngine
37 public override bool Layout (object container, LayoutEventArgs args)
39 if (container is ToolStrip)
41 ToolStrip ts = (ToolStrip)container;
46 Rectangle ts_rect = ts.DisplayRectangle;
48 // Mono hasn't yet implemented ToolStripLayoutStyle.Table, so it comes here
49 // for layout. The default (minimal) Table layout is 1 column, any number of rows,
50 // which translates effectively to Vertical orientation.
51 if (ts.Orientation == Orientation.Horizontal && ts.LayoutStyle != ToolStripLayoutStyle.Table)
52 LayoutHorizontalToolStrip (ts, ts_rect);
54 LayoutVerticalToolStrip (ts, ts_rect);
58 ToolStripContentPanel ts = (ToolStripContentPanel)container;
59 int x = ts.DisplayRectangle.Left;
60 int y = ts.DisplayRectangle.Top;
62 foreach (ToolStrip tsi in ts.Controls) {
63 Rectangle new_bounds = new Rectangle ();
67 new_bounds.Location = new Point (x, y + tsi.Margin.Top);
68 new_bounds.Height = ts.DisplayRectangle.Height - tsi.Margin.Vertical;
69 new_bounds.Width = tsi.GetToolStripPreferredSize (new Size (0, new_bounds.Height)).Width;
71 tsi.Width = new_bounds.Width + 12;
73 x += new_bounds.Width + tsi.Margin.Right;
80 private void LayoutHorizontalToolStrip (ToolStrip ts, Rectangle bounds)
82 //if (!ts.Visible) return;
84 ToolStripItemOverflow[] overflow = new ToolStripItemOverflow[ts.Items.Count];
85 ToolStripItemPlacement[] placement = new ToolStripItemPlacement[ts.Items.Count];
86 Size proposedSize = new Size (0, bounds.Height);
87 int[] widths = new int[ts.Items.Count];
89 int toolstrip_width = bounds.Width;
91 bool can_overflow = ts.CanOverflow & !(ts is MenuStrip) & !(ts is StatusStrip);
92 bool need_overflow = false;
94 foreach (ToolStripItem tsi in ts.Items) {
95 overflow[i] = tsi.Overflow;
96 placement[i] = tsi.Overflow == ToolStripItemOverflow.Always ? ToolStripItemPlacement.Overflow : ToolStripItemPlacement.Main;
97 widths[i] = tsi.GetPreferredSize (proposedSize).Width + tsi.Margin.Horizontal;
99 placement[i] = ToolStripItemPlacement.None;
100 total_width += placement[i] == ToolStripItemPlacement.Main ? widths[i] : 0;
102 if (placement[i] == ToolStripItemPlacement.Overflow)
103 need_overflow = true;
107 // This is needed for a button set to Overflow = Always
109 ts.OverflowButton.Visible = true;
110 ts.OverflowButton.SetBounds (new Rectangle (ts.Width - 16, 0, 16, ts.Height));
111 toolstrip_width -= ts.OverflowButton.Width;
113 ts.OverflowButton.Visible = false;
115 while (total_width > toolstrip_width) {
116 // If we can overflow, get our overflow button setup, and subtract it's width
117 // from our available width
118 if (can_overflow && !ts.OverflowButton.Visible) {
119 ts.OverflowButton.Visible = true;
120 ts.OverflowButton.SetBounds (new Rectangle (ts.Width - 16, 0, 16, ts.Height));
121 toolstrip_width -= ts.OverflowButton.Width;
124 bool removed_one = false;
126 // Start at the right, removing Overflow.AsNeeded first
127 for (int j = widths.Length - 1; j >= 0; j--)
128 if (overflow[j] == ToolStripItemOverflow.AsNeeded && placement[j] == ToolStripItemPlacement.Main) {
129 placement[j] = ToolStripItemPlacement.Overflow;
130 total_width -= widths[j];
135 // If we didn't remove any AsNeeded ones, we have to start removing Never ones
136 // These are not put on the Overflow, they are simply not shown
138 for (int j = widths.Length - 1; j >= 0; j--)
139 if (overflow[j] == ToolStripItemOverflow.Never && placement[j] == ToolStripItemPlacement.Main) {
140 placement[j] = ToolStripItemPlacement.None;
141 total_width -= widths[j];
146 // There's nothing left to remove, break or we will loop forever
152 Point start_layout_pointer = new Point (ts.DisplayRectangle.Left, ts.DisplayRectangle.Top);
153 Point end_layout_pointer = new Point (ts.DisplayRectangle.Right, ts.DisplayRectangle.Top);
154 int button_height = ts.DisplayRectangle.Height;
156 // Now we should know where everything goes, so lay everything out
157 foreach (ToolStripItem tsi in ts.Items) {
158 tsi.SetPlacement (placement[i]);
160 if (placement[i] == ToolStripItemPlacement.Main) {
161 if (tsi.Alignment == ToolStripItemAlignment.Left) {
162 tsi.SetBounds (new Rectangle (start_layout_pointer.X + tsi.Margin.Left, start_layout_pointer.Y + tsi.Margin.Top, widths[i] - tsi.Margin.Horizontal, button_height - tsi.Margin.Vertical));
163 start_layout_pointer.X += widths[i];
165 tsi.SetBounds (new Rectangle (end_layout_pointer.X - tsi.Margin.Right - tsi.Width, end_layout_pointer.Y + tsi.Margin.Top, widths[i] - tsi.Margin.Horizontal, button_height - tsi.Margin.Vertical));
166 end_layout_pointer.X -= widths[i];
174 private void LayoutVerticalToolStrip (ToolStrip ts, Rectangle bounds)
176 if (!ts.Visible) return;
178 ToolStripItemOverflow[] overflow = new ToolStripItemOverflow[ts.Items.Count];
179 ToolStripItemPlacement[] placement = new ToolStripItemPlacement[ts.Items.Count];
180 Size proposedSize = new Size (bounds.Width, 0);
181 int[] heights = new int[ts.Items.Count];
182 int[] widths = new int[ts.Items.Count]; // needed if ts.LayoutStyle == ToolStripLayoutStyle.Table
183 int total_height = 0;
184 int toolstrip_height = bounds.Height;
186 bool can_overflow = ts.CanOverflow & !(ts is MenuStrip) & !(ts is StatusStrip);
188 foreach (ToolStripItem tsi in ts.Items) {
189 overflow[i] = tsi.Overflow;
190 placement[i] = tsi.Overflow == ToolStripItemOverflow.Always ? ToolStripItemPlacement.Overflow : ToolStripItemPlacement.Main;
191 var size = tsi.GetPreferredSize (proposedSize);
192 heights[i] = size.Height + tsi.Margin.Vertical;
193 widths[i] = size.Width + tsi.Margin.Horizontal;
195 placement[i] = ToolStripItemPlacement.None;
196 total_height += placement[i] == ToolStripItemPlacement.Main ? heights[i] : 0;
200 ts.OverflowButton.Visible = false;
202 while (total_height > toolstrip_height) {
203 // If we can overflow, get our overflow button setup, and subtract it's width
204 // from our available width
205 if (can_overflow && !ts.OverflowButton.Visible) {
206 ts.OverflowButton.Visible = true;
207 ts.OverflowButton.SetBounds (new Rectangle (0, ts.Height - 16, ts.Width, 16));
208 toolstrip_height -= ts.OverflowButton.Height;
211 bool removed_one = false;
213 // Start at the right, removing Overflow.AsNeeded first
214 for (int j = heights.Length - 1; j >= 0; j--)
215 if (overflow[j] == ToolStripItemOverflow.AsNeeded && placement[j] == ToolStripItemPlacement.Main) {
216 placement[j] = ToolStripItemPlacement.Overflow;
217 total_height -= heights[j];
222 // If we didn't remove any AsNeeded ones, we have to start removing Never ones
223 // These are not put on the Overflow, they are simply not shown
225 for (int j = heights.Length - 1; j >= 0; j--)
226 if (overflow[j] == ToolStripItemOverflow.Never && placement[j] == ToolStripItemPlacement.Main) {
227 placement[j] = ToolStripItemPlacement.None;
228 total_height -= heights[j];
233 // There's nothing left to remove, break or we will loop forever
239 Point start_layout_pointer = new Point (ts.DisplayRectangle.Left, ts.DisplayRectangle.Top);
240 Point end_layout_pointer = new Point (ts.DisplayRectangle.Left, ts.DisplayRectangle.Bottom);
241 int button_width = ts.DisplayRectangle.Width;
243 // Now we should know where everything goes, so lay everything out
244 foreach (ToolStripItem tsi in ts.Items) {
245 tsi.SetPlacement (placement[i]);
246 // Table layout is defined to lay out items flush left.
247 if (ts.LayoutStyle == ToolStripLayoutStyle.Table)
248 button_width = widths[i];
250 if (placement[i] == ToolStripItemPlacement.Main) {
251 if (tsi.Alignment == ToolStripItemAlignment.Left) {
252 tsi.SetBounds (new Rectangle (start_layout_pointer.X + tsi.Margin.Left, start_layout_pointer.Y + tsi.Margin.Top, button_width - tsi.Margin.Horizontal, heights[i] - tsi.Margin.Vertical));
253 start_layout_pointer.Y += heights[i];
255 tsi.SetBounds (new Rectangle (end_layout_pointer.X + tsi.Margin.Left, end_layout_pointer.Y - tsi.Margin.Bottom - tsi.Height, button_width - tsi.Margin.Horizontal, heights[i] - tsi.Margin.Vertical));
256 start_layout_pointer.Y += heights[i];