Fix XMM scanning on Mac x86.
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / PropertyEditing / PropertyPanel.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing 
5 {
6     using System;
7     using System.Windows;
8     using System.Windows.Controls;
9     using System.Windows.Media;
10
11     // <summary>
12     // PropertyPanel is a simplified version of a horizontal StackPanel that we use for PropertyContainer
13     // visuals in lieu of Grid, which was too heavy-weight and bogging down perf.  It exposes a property,
14     // LastChildWidth, that specifies the forced width of the last child in the panel.  All other
15     // children are stacked on the left and eat up the remainder of the space left on the panel.
16     //
17     // The panel also deals with drawing compartments for itself and the last child and it deals with
18     // visually nesting sub-properties based on their depth (Level).
19     // </summary>
20     internal class PropertyPanel : Panel 
21     {
22
23         // LastChildWidth DP
24
25         public static readonly DependencyProperty OutlineBrushProperty =
26             DependencyProperty.Register("OutlineBrush",
27             typeof(Brush),
28             typeof(PropertyPanel),
29             new FrameworkPropertyMetadata((Brush)null,
30             FrameworkPropertyMetadataOptions.AffectsRender |
31             FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
32
33         public static readonly DependencyProperty SelectionBrushProperty =
34             DependencyProperty.Register("SelectionBrush",
35             typeof(Brush),
36             typeof(PropertyPanel),
37             new FrameworkPropertyMetadata((Brush)null,
38             FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
39
40         public static readonly DependencyProperty OutlineThicknessProperty =
41             DependencyProperty.Register("OutlineThickness",
42             typeof(double),
43             typeof(PropertyPanel),
44             new FrameworkPropertyMetadata((double)1,
45             FrameworkPropertyMetadataOptions.AffectsRender));
46
47         public static readonly DependencyProperty IgnoreFirstChildBackgroundProperty =
48             DependencyProperty.Register("IgnoreFirstChildBackground",
49             typeof(bool),
50             typeof(PropertyPanel),
51             new FrameworkPropertyMetadata(
52             false,
53             FrameworkPropertyMetadataOptions.AffectsRender));
54
55         public static DependencyProperty LastChildWidthProperty = DependencyProperty.Register(
56             "LastChildWidth",
57             typeof(double),
58             typeof(PropertyPanel),
59             new FrameworkPropertyMetadata(
60             (double)0,
61             FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender));
62
63         public static DependencyProperty LevelProperty = DependencyProperty.Register(
64             "Level",
65             typeof(int),
66             typeof(PropertyPanel),
67             new FrameworkPropertyMetadata(
68             (int)0,
69             FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
70
71         public static DependencyProperty LevelIndentProperty = DependencyProperty.Register(
72             "LevelIndent",
73             typeof(double),
74             typeof(PropertyPanel),
75             new FrameworkPropertyMetadata(
76             (double)13,
77             FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
78
79         // <summary>
80         // Gets or sets the pixel width of the last child added into this panel.
81         // </summary>
82         public double LastChildWidth 
83         {
84             get { return (double)this.GetValue(LastChildWidthProperty); }
85             set { this.SetValue(LastChildWidthProperty, value); }
86         }
87
88
89
90         // Level DP
91
92         // <summary>
93         // Gets or sets the indentation level for the first child in this panel.  Levels are
94         // measured in ints, with 0 = no indentation, 1 = 1st sub-property, ...
95         // The actual amount of space taken up by each level is controled by LevelIndent property
96         // </summary>
97         public int Level 
98         {
99             get { return (int)this.GetValue(LevelProperty); }
100             set { this.SetValue(LevelProperty, value); }
101         }
102
103
104
105         // LevelIndent DP
106
107         // <summary>
108         // Gets or sets the pixel width that the first child is indented for each level that
109         // it belongs to
110         // </summary>
111         public double LevelIndent 
112         {
113             get { return (double)this.GetValue(LevelIndentProperty); }
114             set { this.SetValue(LevelIndentProperty, value); }
115         }
116
117
118
119         // OutlineBrush DP
120
121         // <summary>
122         // Gets or sets the line brush to use for the panel compartments
123         // </summary>
124         public Brush OutlineBrush 
125         {
126             get { return (Brush)GetValue(OutlineBrushProperty); }
127             set { SetValue(OutlineBrushProperty, value); }
128         }
129
130
131
132         // SelectionBrush DP
133
134         // <summary>
135         // Gets or sets the brush to be used as the background for everything but the last
136         // element in the panel
137         // </summary>
138         public Brush SelectionBrush 
139         {
140             get { return (Brush)GetValue(SelectionBrushProperty); }
141             set { SetValue(SelectionBrushProperty, value); }
142         }
143
144
145
146         // OutlineThickness DP
147
148         // <summary>
149         // Gets or sets the line thickness for the panel compartments (not as Thickness, but
150         // instead as a simple double)
151         // </summary>
152         public double OutlineThickness 
153         {
154             get { return (double)GetValue(OutlineThicknessProperty); }
155             set { SetValue(OutlineThicknessProperty, value); }
156         }
157
158
159
160         // IgnoreFirstChildBackground DP
161
162         // <summary>
163         // Gets or sets a flag indicating whether the SelectionBrush background should
164         // or should not be applied to the first child of the panel
165         // </summary>
166         public bool IgnoreFirstChildBackground 
167         {
168             get { return (bool)GetValue(IgnoreFirstChildBackgroundProperty); }
169             set { SetValue(IgnoreFirstChildBackgroundProperty, value); }
170         }
171
172
173
174         // Stacks the children to the left, leaving LastChildWidth amount of space for the last child
175         protected override Size MeasureOverride(Size availableSize) 
176         {
177
178             double lastChildWidth = Math.Max(0, this.LastChildWidth);
179             double indent = this.LevelIndent * this.Level;
180             double availableWidth = Math.Max(0, availableSize.Width - lastChildWidth - indent);
181             int childrenCount = InternalChildren.Count;
182             int lastIndex = childrenCount - 1;
183             Size actualSize = new Size();
184
185             for (int i = 0; i < childrenCount; i++) 
186             {
187                 UIElement child = InternalChildren[i];
188
189                 if (i == lastIndex) 
190                 {
191                     InternalChildren[i].Measure(new Size(lastChildWidth, availableSize.Height));
192                 }
193                 else 
194                 {
195                     InternalChildren[i].Measure(new Size(availableWidth, availableSize.Height));
196                 }
197
198                 availableWidth -= child.DesiredSize.Width;
199                 //Compute the actual size for the propertypanel
200                 actualSize.Height = Math.Max(actualSize.Height, child.DesiredSize.Height);
201                 actualSize.Width += child.DesiredSize.Width;
202             }
203
204             return actualSize;
205         }
206
207         // Stacks the children to the left, leaving LastChildWidth amount of space for the last child
208         protected override Size ArrangeOverride(Size finalSize) 
209         {
210
211             double lastChildWidth = Math.Max(0, this.LastChildWidth);
212             double indent = this.LevelIndent * this.Level;
213             double availableWidth = Math.Max(0, finalSize.Width - lastChildWidth - indent);
214             double left = indent;
215             int childrenCount = InternalChildren.Count;
216             int lastIndex = childrenCount - 1;
217
218             for (int i = 0; i < childrenCount; i++) 
219             {
220
221                 UIElement child = InternalChildren[i];
222                 double desiredWidth = child.DesiredSize.Width;
223                 if (i == lastIndex) 
224                 {
225                     child.Arrange(new Rect(Math.Max(0, finalSize.Width - lastChildWidth), 0, lastChildWidth, finalSize.Height));
226                 }
227                 else 
228                 {
229                     child.Arrange(new Rect(left, 0, Math.Min(desiredWidth, availableWidth), finalSize.Height));
230                 }
231
232                 left += desiredWidth;
233                 availableWidth -= desiredWidth;
234                 availableWidth = Math.Max(0, availableWidth);
235             }
236
237             return finalSize;
238         }
239
240         // Custom renders compartments and dividers
241         protected override void OnRender(DrawingContext dc) 
242         {
243
244             Size renderSize = this.RenderSize;
245             Brush outlineBrush = this.OutlineBrush;
246             double outlineThickness = this.OutlineThickness;
247             double halfThickness = outlineThickness / 2.0;
248             double dividerRight = Math.Max(0, this.LastChildWidth);
249             double dividerLeft = renderSize.Width - dividerRight - outlineThickness;
250
251             Brush selectionBrush = this.SelectionBrush;
252
253             if (selectionBrush != null) 
254             {
255                 bool ignoreFirstChildBackground = this.IgnoreFirstChildBackground;
256                 double firstChildWidth = 0;
257
258                 if (ignoreFirstChildBackground && this.Children.Count > 0) 
259                 {
260                     firstChildWidth = this.Children[0].RenderSize.Width;
261                 }
262
263                 dc.DrawRectangle(selectionBrush, null, new Rect(
264                     firstChildWidth,
265                     0,
266                     Math.Max(dividerLeft - firstChildWidth, 0),
267                     renderSize.Height));
268             }
269
270             base.OnRender(dc);
271
272             // Use Guidelines to avoid anti-aliasing (fuzzy border lines)
273             dc.PushGuidelineSet(new GuidelineSet(
274                 // X coordinates for guidelines (vertical lines)
275                 new double[] { 0, dividerLeft, dividerLeft + outlineThickness, renderSize.Width - outlineThickness, renderSize.Width },
276                 // Y coordinates for guidelines (horizontal lines)
277                 new double[] { 0, renderSize.Height - outlineThickness, renderSize.Height }));
278
279             Pen outlinePen = new Pen(outlineBrush, outlineThickness);
280
281             // Bottom edge
282             dc.DrawLine(
283                 outlinePen,
284                 new Point(0, renderSize.Height - halfThickness),
285                 new Point(renderSize.Width, renderSize.Height - halfThickness));
286
287             // Top edge
288             dc.DrawLine(
289                 outlinePen,
290                 new Point(0, 0 - halfThickness),
291                 new Point(renderSize.Width, 0 - halfThickness));
292
293             // Right edge
294             dc.DrawLine(
295                 outlinePen,
296                 new Point(renderSize.Width - halfThickness, 0),
297                 new Point(renderSize.Width - halfThickness, renderSize.Height));
298
299             // Divider
300             dc.DrawLine(
301                 outlinePen,
302                 new Point(dividerLeft + halfThickness, 0),
303                 new Point(dividerLeft + halfThickness, renderSize.Height));
304
305             dc.Pop();
306         }
307     }
308 }