Mercurial > silverbladetech
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 } |