comparison SilverlightGlimpse/SilverFlow.Controls/Controllers/ResizeController.cs @ 63:536498832a79

Latest version before changing bindings to Listbox
author Steven Hollidge <stevenhollidge@hotmail.com>
date Sun, 22 Apr 2012 13:33:42 +0100
parents
children
comparison
equal deleted inserted replaced
62:810116cd6b8e 63:536498832a79
1 using System;
2 using System.Windows;
3 using System.Windows.Input;
4 using SilverFlow.Controls.Enums;
5 using SilverFlow.Controls.Extensions;
6
7 namespace SilverFlow.Controls.Controllers
8 {
9 /// <summary>
10 /// Calculates resizing element position and size.
11 /// </summary>
12 public class ResizeController
13 {
14 /// <summary>
15 /// Thickness of resizing area.
16 /// </summary>
17 private const double ResizingAreaDefaultValue = 6;
18
19 /// <summary>
20 /// Minimal width of the resized window if MinWidth is set to 0.
21 /// </summary>
22 private const double MinResizedWidth = 20;
23
24 /// <summary>
25 /// Resizing bounds, i.e. an area within which the resizing cursor can move.
26 /// </summary>
27 private Rect bounds;
28
29 /// <summary>
30 /// Initial element size when resizing started.
31 /// </summary>
32 private Size initialSize;
33
34 /// <summary>
35 /// Initial position of the element when resizing started.
36 /// </summary>
37 private Point initialPosition;
38
39 /// <summary>
40 /// A corner or edge of the element used for resizing.
41 /// </summary>
42 private ResizeAnchor anchor;
43
44 /// <summary>
45 /// An element implementing IResizableElement interface.
46 /// </summary>
47 private IResizableElement element;
48
49 /// <summary>
50 /// Gets a framework element hosting resizing element.
51 /// </summary>
52 /// <value>The host.</value>
53 private FrameworkElement Host
54 {
55 get
56 {
57 return element.Parent as FrameworkElement;
58 }
59 }
60
61 /// <summary>
62 /// Gets a value indicating whether the resizing can be started.
63 /// </summary>
64 /// <value>
65 /// <c>true</c> if resizing can be started; otherwise, <c>false</c>.
66 /// </value>
67 public bool CanResize
68 {
69 get
70 {
71 return anchor != ResizeAnchor.None;
72 }
73 }
74
75 /// <summary>
76 /// Gets or sets the width of the resizing area.
77 /// </summary>
78 /// <value>The width of the resizing area. Default value is 6.</value>
79 public double ResizingArea { get; set; }
80
81 /// <summary>
82 /// Gets a value indicating whether this instance can be resized horizontally.
83 /// </summary>
84 /// <value>
85 /// <c>true</c> if this instance can be resized horizontally; otherwise, <c>false</c>.
86 /// </value>
87 private bool CanResizeHorizontally
88 {
89 get
90 {
91 return !(element.MinWidth == element.MaxWidth && element.MinWidth != 0);
92 }
93 }
94
95 /// <summary>
96 /// Gets a value indicating whether this instance can be resized vertically.
97 /// </summary>
98 /// <value>
99 /// <c>true</c> if this instance can be resized vertically; otherwise, <c>false</c>.
100 /// </value>
101 private bool CanResizeVertically
102 {
103 get
104 {
105 return !(element.MinHeight == element.MaxHeight && element.MinHeight != 0);
106 }
107 }
108
109 /// <summary>
110 /// Initializes a new instance of the <see cref="ResizeController"/> class.
111 /// </summary>
112 /// <param name="resizableElement">The resizable element.</param>
113 public ResizeController(IResizableElement resizableElement)
114 {
115 if (resizableElement == null)
116 throw new ArgumentNullException("resizableElement");
117
118 element = resizableElement;
119 anchor = ResizeAnchor.None;
120 ResizingArea = ResizingAreaDefaultValue;
121 }
122
123 /// <summary>
124 /// Starts resizing of the element.
125 /// </summary>
126 public void StartResizing()
127 {
128 initialSize = new Size(element.ActualWidth, element.ActualHeight);
129 initialPosition = element.Position;
130
131 CalculateBoundsForResizing();
132 }
133
134 /// <summary>
135 /// Resizes the element.
136 /// </summary>
137 /// <param name="dx">Displacement by X-coordinate.</param>
138 /// <param name="dy">Displacement by Y-coordinate.</param>
139 public void Resize(double dx, double dy)
140 {
141 switch (anchor)
142 {
143 case ResizeAnchor.BottomRight:
144 ResizeBottomRight(dx, dy);
145 break;
146
147 case ResizeAnchor.Right:
148 ResizeBottomRight(dx, 0);
149 break;
150
151 case ResizeAnchor.Bottom:
152 ResizeBottomRight(0, dy);
153 break;
154
155 case ResizeAnchor.TopLeft:
156 ResizeTopLeft(dx, dy);
157 break;
158
159 case ResizeAnchor.Top:
160 ResizeTopLeft(0, dy);
161 break;
162
163 case ResizeAnchor.Left:
164 ResizeTopLeft(dx, 0);
165 break;
166
167 case ResizeAnchor.BottomLeft:
168 ResizeBottomLeft(dx, dy);
169 break;
170
171 case ResizeAnchor.TopRight:
172 ResizeTopRight(dx, dy);
173 break;
174 }
175 }
176
177 /// <summary>
178 /// Determines resizing anchor and sets appropriate Cursor.
179 /// </summary>
180 /// <param name="p">Coordinates of the mouse pointer.</param>
181 public void SetCursor(Point p)
182 {
183 if (p.Y < ResizingArea && p.X < ResizingArea)
184 {
185 anchor = ResizeAnchor.TopLeft;
186 element.Cursor = Cursors.SizeNWSE;
187 }
188 else if (p.Y < ResizingArea && p.X >= (element.ActualWidth - ResizingArea))
189 {
190 anchor = ResizeAnchor.TopRight;
191 element.Cursor = Cursors.SizeNESW;
192 }
193 else if (p.Y < ResizingArea)
194 {
195 if (CanResizeVertically)
196 {
197 anchor = ResizeAnchor.Top;
198 element.Cursor = Cursors.SizeNS;
199 }
200 }
201 else if (p.X < ResizingArea && p.Y >= (element.ActualHeight - ResizingArea))
202 {
203 anchor = ResizeAnchor.BottomLeft;
204 element.Cursor = Cursors.SizeNESW;
205 }
206 else if (p.X < ResizingArea)
207 {
208 if (CanResizeHorizontally)
209 {
210 anchor = ResizeAnchor.Left;
211 element.Cursor = Cursors.SizeWE;
212 }
213 }
214 else if (p.X >= (element.ActualWidth - ResizingArea) && p.Y >= (element.ActualHeight - ResizingArea))
215 {
216 anchor = ResizeAnchor.BottomRight;
217 element.Cursor = Cursors.SizeNWSE;
218 }
219 else if (p.X >= (element.ActualWidth - ResizingArea))
220 {
221 if (CanResizeHorizontally)
222 {
223 anchor = ResizeAnchor.Right;
224 element.Cursor = Cursors.SizeWE;
225 }
226 }
227 else if (p.Y >= (element.ActualHeight - ResizingArea))
228 {
229 if (CanResizeVertically)
230 {
231 anchor = ResizeAnchor.Bottom;
232 element.Cursor = Cursors.SizeNS;
233 }
234 }
235 else
236 {
237 anchor = ResizeAnchor.None;
238 element.Cursor = null;
239 }
240 }
241
242 /// <summary>
243 /// Calculates bounds for resizing.
244 /// </summary>
245 private void CalculateBoundsForResizing()
246 {
247 switch (anchor)
248 {
249 case ResizeAnchor.BottomRight:
250 case ResizeAnchor.Right:
251 case ResizeAnchor.Bottom:
252 bounds = GetBoundsForLowerRightCorner();
253 break;
254
255 case ResizeAnchor.TopLeft:
256 case ResizeAnchor.Top:
257 case ResizeAnchor.Left:
258 bounds = GetBoundsForUpperLeftCorner();
259 break;
260
261 case ResizeAnchor.BottomLeft:
262 bounds = GetBoundsForLowerLeftCorner();
263 break;
264
265 case ResizeAnchor.TopRight:
266 bounds = GetBoundsForUpperRightCorner();
267 break;
268 }
269 }
270
271 /// <summary>
272 /// Calculates bounds for resizing by the lower right corner.
273 /// </summary>
274 /// <returns>Bounding rectangle.</returns>
275 private Rect GetBoundsForLowerRightCorner()
276 {
277 double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth;
278 double minHeight = element.MinHeight;
279
280 double maxWidth = Host.ActualWidth - initialPosition.X;
281
282 if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0)
283 maxWidth = Math.Min(maxWidth, element.MaxWidth);
284
285 double maxHeight = Host.ActualHeight - initialPosition.Y;
286 if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0)
287 maxHeight = Math.Min(maxHeight, element.MaxHeight);
288
289 Point p1 = initialPosition.Add(minWidth, minHeight);
290 Point p2 = initialPosition.Add(maxWidth, maxHeight);
291
292 return new Rect(p1, p2);
293 }
294
295 /// <summary>
296 /// Calculates bounds for resizing by the upper left corner.
297 /// </summary>
298 /// <returns>Bounding rectangle.</returns>
299 private Rect GetBoundsForUpperLeftCorner()
300 {
301 double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth;
302 double minHeight = element.MinHeight;
303
304 Point point = initialPosition.Add(initialSize);
305 double maxWidth = point.X;
306 double maxHeight = point.Y;
307
308 if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0)
309 maxWidth = Math.Min(maxWidth, element.MaxWidth);
310
311 if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0)
312 maxHeight = Math.Min(maxHeight, element.MaxHeight);
313
314 Point p1 = point.Add(-maxWidth, -maxHeight);
315 Point p2 = point.Add(-minWidth, -minHeight);
316
317 return new Rect(p1, p2);
318 }
319
320 /// <summary>
321 /// Calculates bounds for resizing by the lower left corner.
322 /// </summary>
323 /// <returns>Bounding rectangle.</returns>
324 private Rect GetBoundsForLowerLeftCorner()
325 {
326 double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth;
327 double minHeight = element.MinHeight;
328
329 Point point = initialPosition.Add(initialSize.Width, 0);
330 double maxWidth = point.X;
331
332 if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0)
333 maxWidth = Math.Min(maxWidth, element.MaxWidth);
334
335 double maxHeight = Host.ActualHeight - initialPosition.Y;
336 if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0)
337 maxHeight = Math.Min(maxHeight, element.MaxHeight);
338
339 Point p1 = point.Add(-maxWidth, minHeight);
340 Point p2 = point.Add(-minWidth, maxHeight);
341
342 return new Rect(p1, p2);
343 }
344
345 /// <summary>
346 /// Calculates bounds for resizing by the upper right corner.
347 /// </summary>
348 /// <returns>Bounding rectangle.</returns>
349 private Rect GetBoundsForUpperRightCorner()
350 {
351 double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth;
352 double minHeight = element.MinHeight;
353
354 Point point = initialPosition.Add(0, initialSize.Height);
355 double maxWidth = Host.ActualWidth - initialPosition.X;
356
357 if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0)
358 maxWidth = Math.Min(maxWidth, element.MaxWidth);
359
360 double maxHeight = point.Y;
361 if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0)
362 maxHeight = Math.Min(maxHeight, element.MaxHeight);
363
364 Point p1 = point.Add(minWidth, -maxHeight);
365 Point p2 = point.Add(maxWidth, -minHeight);
366
367 return new Rect(p1, p2);
368 }
369
370 /// <summary>
371 /// Resizes the window by the bottom right corner of the window.
372 /// </summary>
373 /// <param name="dx">Increment by X-coordinate.</param>
374 /// <param name="dy">Increment by Y-coordinate.</param>
375 private void ResizeBottomRight(double dx, double dy)
376 {
377 Rect newBounds = new Rect(initialPosition, initialSize.Add(dx, dy));
378 Point point = element.SnapinController.SnapBottomRightCorner(newBounds);
379
380 // If only one coordinate was changed - restore the other after snap in
381 if (dx == 0)
382 point.X = newBounds.BottomRight().X;
383
384 if (dy == 0)
385 point.Y = newBounds.BottomRight().Y;
386
387 point = point.EnsureInBounds(bounds);
388
389 element.Width = point.X - initialPosition.X;
390 element.Height = point.Y - initialPosition.Y;
391 }
392
393 /// <summary>
394 /// Resizes the window by the top left corner of the window.
395 /// </summary>
396 /// <param name="dx">Increment by X-coordinate.</param>
397 /// <param name="dy">Increment by Y-coordinate.</param>
398 private void ResizeTopLeft(double dx, double dy)
399 {
400 Rect newBounds = new Rect(initialPosition.Add(dx, dy), initialSize);
401 Point point = element.SnapinController.SnapTopLeftCorner(newBounds).EnsureInBounds(bounds);
402
403 // If only one coordinate was changed - restore the other after snap in
404 if (dx == 0)
405 point.X = newBounds.Position().X;
406
407 if (dy == 0)
408 point.Y = newBounds.Position().Y;
409
410 element.Position = point;
411
412 Point lowerRight = initialPosition.Add(initialSize);
413 element.Width = lowerRight.X - point.X;
414 element.Height = lowerRight.Y - point.Y;
415 }
416
417 /// <summary>
418 /// Resizes the window by the lower left corner of the window.
419 /// </summary>
420 /// <param name="dx">Increment by X-coordinate.</param>
421 /// <param name="dy">Increment by Y-coordinate.</param>
422 private void ResizeBottomLeft(double dx, double dy)
423 {
424 Rect newBounds = new Rect(initialPosition.Add(dx, 0), initialSize.Add(-dx, dy));
425
426 Point lowerLeft = element.SnapinController.SnapBottomLeftCorner(newBounds).EnsureInBounds(bounds);
427 Point topLeft = new Point(lowerLeft.X, initialPosition.Y);
428
429 element.Position = topLeft;
430
431 element.Width = initialSize.Width + initialPosition.X - topLeft.X;
432 element.Height = lowerLeft.Y - initialPosition.Y;
433 }
434
435 /// <summary>
436 /// Resizes the window by the top right corner of the window.
437 /// </summary>
438 /// <param name="dx">Increment by X-coordinate.</param>
439 /// <param name="dy">Increment by Y-coordinate.</param>
440 private void ResizeTopRight(double dx, double dy)
441 {
442 Rect newBounds = new Rect(initialPosition.Add(0, dy), initialSize.Add(dx, -dy));
443
444 Point topRight = element.SnapinController.SnapTopRightCorner(newBounds).EnsureInBounds(bounds);
445 Point topLeft = new Point(initialPosition.X, topRight.Y);
446
447 element.Position = topLeft;
448
449 element.Width = topRight.X - initialPosition.X;
450 element.Height = initialSize.Height + initialPosition.Y - topRight.Y;
451 }
452 }
453 }