[corlib] Avoid unnecessary ephemeron array resizes
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Annotations / AnnotationAdorner.cs
1 //----------------------------------------------------------------
2 // <copyright company="Microsoft Corporation">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //----------------------------------------------------------------
6
7 namespace System.Activities.Presentation.Annotations
8 {
9     using System.Activities.Presentation.View;
10     using System.Runtime;
11     using System.Windows;
12     using System.Windows.Controls;
13     using System.Windows.Documents;
14     using System.Windows.Media;
15
16     internal class AnnotationAdorner : Adorner
17     {
18         public static readonly DependencyProperty AnchorProperty = DependencyProperty.RegisterAttached("Anchor", typeof(AdornerLocation), typeof(AnnotationAdorner), new FrameworkPropertyMetadata(AdornerLocation.None));
19         private UIElement content;
20
21         public AnnotationAdorner(UIElement adornedElement)
22             : base(adornedElement)
23         {
24         }
25
26         internal ScrollViewer ScrollViewer
27         {
28             get;
29             set;
30         }
31
32         internal UIElement Content
33         {
34             get
35             {
36                 return this.content;
37             }
38
39             set
40             {
41                 if (this.content != value)
42                 {
43                     if (this.content != null)
44                     {
45                         this.RemoveVisualChild(this.content);
46                     }
47
48                     this.content = value;
49                     if (this.content != null)
50                     {
51                         this.AddVisualChild(this.content);
52                     }
53                 }
54             }
55         }
56
57         protected override int VisualChildrenCount
58         {
59             get
60             {
61                 return 1;
62             }
63         }
64
65         public static AdornerLocation GetAnchor(DependencyObject obj)
66         {
67             return (AdornerLocation)obj.GetValue(AnnotationAdorner.AnchorProperty);
68         }
69
70         public static void SetAnchor(DependencyObject obj, AdornerLocation anchor)
71         {
72             obj.SetValue(AnnotationAdorner.AnchorProperty, anchor);
73         }
74
75         // (3)   |   (2)
76         // ______|______
77         // (4)   |   (1)
78         //       |
79         // canvas is divided into four areas by anchorPoint
80         // try to figure out which area could fit desiredSize from area1 to area4, if there's no fit, default to area4.
81         internal static AdornerLocation FindAnchor(Point anchorPoint, Size desiredSize, Rect canvas)
82         {
83             Fx.Assert(anchorPoint.X >= canvas.X && anchorPoint.X <= canvas.X + canvas.Width, "X axis of anchorPoint not within canvas");
84             Fx.Assert(anchorPoint.Y >= canvas.Y && anchorPoint.Y <= canvas.Y + canvas.Height, "Y axis of anchorPoint not within canvas");
85
86             AdornerLocation anchor = AdornerLocation.None;
87
88             // try area 1 and 2
89             if (anchorPoint.X + desiredSize.Width <= canvas.X + canvas.Width)
90             {
91                 // area 1
92                 if (anchorPoint.Y + desiredSize.Height <= canvas.Y + canvas.Height)
93                 {
94                     anchor = AdornerLocation.BottomRight;
95                 }
96
97                 // area 2
98                 if (anchor == AdornerLocation.None && anchorPoint.Y - desiredSize.Height >= canvas.Y)
99                 {
100                     anchor = AdornerLocation.TopRight;
101                 }
102             }
103
104             // area 3
105             if (anchor == AdornerLocation.None && anchorPoint.X - desiredSize.Width >= canvas.X && anchorPoint.Y - desiredSize.Height >= canvas.Y)
106             {
107                 anchor = AdornerLocation.TopLeft;
108             }
109
110             // default to area 4
111             if (anchor == AdornerLocation.None)
112             {
113                 anchor = AdornerLocation.BottomLeft;
114             }
115
116             return anchor;
117         }
118
119         protected override Visual GetVisualChild(int index)
120         {
121             return this.Content;
122         }
123
124         protected override Size MeasureOverride(Size constraint)
125         {
126             this.content.Measure(constraint);
127             return this.content.DesiredSize;
128         }
129
130         protected override Size ArrangeOverride(Size finalSize)
131         {
132             Point anchorPoint = this.AdornedElement.TranslatePoint(new Point(this.AdornedElement.RenderSize.Width, 0), this.ScrollViewer);
133
134             AdornerLocation anchor = AnnotationAdorner.GetAnchor(this);
135             if (anchor == AdornerLocation.None)
136             {
137                 // Calculate based on the real size of the adorner, depending on current zoom level
138                 DesignerView designerView = ((WorkflowViewElement)AdornedElement).Context.Services.GetService<DesignerView>();
139                 double zoomLevel = designerView.ZoomFactor;
140                 Size adornerSize = new Size(this.content.DesiredSize.Width * zoomLevel, this.content.DesiredSize.Height * zoomLevel);
141                 anchor = FindAnchor(anchorPoint, adornerSize, new Rect(0, 0, this.ScrollViewer.ViewportWidth, this.ScrollViewer.ViewportHeight));
142                 AnnotationAdorner.SetAnchor(this, anchor);
143             }
144
145             Point location = CalculateLocation(anchor, this.AdornedElement.RenderSize, this.content.DesiredSize);
146
147             this.content.Arrange(new Rect(location, finalSize));
148
149             return finalSize;
150         }
151
152         private static Point CalculateLocation(AdornerLocation anchor, Size adorneeSize, Size adornerSize)
153         {
154             Point location = new Point();
155
156             switch (anchor)
157             {
158                 case AdornerLocation.BottomRight:
159                     location = new Point(adorneeSize.Width, 0);
160                     break;
161                 case AdornerLocation.TopRight:
162                     location = new Point(adorneeSize.Width, -adornerSize.Height);
163                     break;
164                 case AdornerLocation.TopLeft:
165                     location = new Point(adorneeSize.Width - adornerSize.Width, -adornerSize.Height);
166                     break;
167                 case AdornerLocation.BottomLeft:
168                     location = new Point(adorneeSize.Width - adornerSize.Width, 20);
169                     break;
170             }
171
172             return location;
173         }
174     }
175 }