9 changed files with 1123 additions and 43 deletions
@ -0,0 +1,190 @@
@@ -0,0 +1,190 @@
|
||||
/* |
||||
* Created by SharpDevelop. |
||||
* User: trubra |
||||
* Date: 2014-12-22 |
||||
* Time: 11:00 |
||||
* |
||||
* To change this template use Tools | Options | Coding | Edit Standard Headers. |
||||
*/ |
||||
using System; |
||||
using ICSharpCode.WpfDesign.Designer.Controls; |
||||
using ICSharpCode.WpfDesign.Designer.Extensions; |
||||
using System.Windows.Shapes; |
||||
|
||||
using ICSharpCode.WpfDesign; |
||||
using ICSharpCode.WpfDesign.Adorners; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System; |
||||
using System.Collections; |
||||
using System.ComponentModel; |
||||
|
||||
namespace ICSharpCode.WpfDesign.Designer.Extensions |
||||
{ |
||||
/// <summary>
|
||||
/// Description of LineExtensionBase.
|
||||
/// </summary>
|
||||
class Bounds |
||||
{ |
||||
public double X, Y, Left, Top; |
||||
} |
||||
/// <summary>
|
||||
/// base class for the Line, Polyline and Polygon extension classes
|
||||
/// </summary>
|
||||
public class LineExtensionBase : SelectionAdornerProvider |
||||
{ |
||||
|
||||
|
||||
internal AdornerPanel adornerPanel; |
||||
internal IEnumerable resizeThumbs; |
||||
|
||||
/// <summary>An array containing this.ExtendedItem as only element</summary>
|
||||
internal readonly DesignItem[] extendedItemArray = new DesignItem[1]; |
||||
|
||||
internal IPlacementBehavior resizeBehavior; |
||||
internal PlacementOperation operation; |
||||
internal ChangeGroup changeGroup; |
||||
private Canvas _surface; |
||||
internal bool _isResizing; |
||||
private TextBlock _text; |
||||
//private DesignPanel designPanel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this extension is resizing any element.
|
||||
/// </summary>
|
||||
public bool IsResizing |
||||
{ |
||||
get { return _isResizing; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// on creation add adornerlayer
|
||||
/// </summary>
|
||||
public LineExtensionBase() |
||||
{ |
||||
_surface = new Canvas(); |
||||
adornerPanel = new AdornerPanel(); |
||||
adornerPanel.Order = AdornerOrder.Foreground; |
||||
adornerPanel.Children.Add(_surface); |
||||
Adorners.Add(adornerPanel); |
||||
} |
||||
|
||||
#region eventhandlers
|
||||
|
||||
|
||||
protected void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) |
||||
{ |
||||
UpdateAdornerVisibility(); |
||||
} |
||||
|
||||
protected override void OnRemove() |
||||
{ |
||||
this.ExtendedItem.PropertyChanged -= OnPropertyChanged; |
||||
this.Services.Selection.PrimarySelectionChanged -= OnPrimarySelectionChanged; |
||||
DependencyPropertyDescriptor.FromProperty(FrameworkElement.HeightProperty, typeof (Shape)) |
||||
.RemoveValueChanged(ExtendedItem.View, (s, ev) => ResetWidthHeightProperties()); |
||||
base.OnRemove(); |
||||
} |
||||
|
||||
protected void OnPrimarySelectionChanged(object sender, EventArgs e) |
||||
{ |
||||
bool isPrimarySelection = this.Services.Selection.PrimarySelection == this.ExtendedItem; |
||||
if (isPrimarySelection) |
||||
DependencyPropertyDescriptor.FromProperty(FrameworkElement.HeightProperty, typeof (Shape)) |
||||
.AddValueChanged(ExtendedItem.View, (s, ev) => ResetWidthHeightProperties()); |
||||
//foreach (ResizeThumb g in adornerPanel.Children)
|
||||
//{
|
||||
// g.IsPrimarySelection = isPrimarySelection;
|
||||
//}
|
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
protected void UpdateAdornerVisibility() |
||||
{ |
||||
FrameworkElement fe = this.ExtendedItem.View as FrameworkElement; |
||||
foreach (ResizeThumb r in resizeThumbs) |
||||
{ |
||||
bool isVisible = resizeBehavior != null && |
||||
resizeBehavior.CanPlace(extendedItemArray, PlacementType.Resize, r.Alignment); |
||||
r.Visibility = isVisible ? Visibility.Visible : Visibility.Hidden; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Places resize thumbs at their respective positions
|
||||
/// and streches out thumbs which are at the center of outline to extend resizability across the whole outline
|
||||
/// </summary>
|
||||
/// <param name="resizeThumb"></param>
|
||||
/// <param name="alignment"></param>
|
||||
/// <param name="index">if using a polygon or multipoint adorner this is the index of the point in the Points array</param>
|
||||
/// <returns></returns>
|
||||
protected PointTrackerPlacementSupport Place(ref ResizeThumb resizeThumb, PlacementAlignment alignment, int index = -1) |
||||
{ |
||||
PointTrackerPlacementSupport placement = new PointTrackerPlacementSupport(ExtendedItem.View as Shape, alignment, index); |
||||
return placement; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// forces redraw of shape
|
||||
/// </summary>
|
||||
protected void Invalidate() |
||||
{ |
||||
Shape s = ExtendedItem.View as Shape; |
||||
if (s != null) |
||||
{ |
||||
s.InvalidateVisual(); |
||||
s.BringIntoView(); |
||||
} |
||||
} |
||||
|
||||
protected void SetSurfaceInfo(int x, int y, string s) |
||||
{ |
||||
if (_text == null) |
||||
{ |
||||
_text = new TextBlock(); |
||||
_surface.Children.Add(_text); |
||||
} |
||||
AdornerPanel ap = _surface.Parent as AdornerPanel; |
||||
|
||||
_surface.Width = ap.Width; |
||||
_surface.Height = ap.Height; |
||||
|
||||
_text.Text = s; |
||||
Canvas.SetLeft(_text, x); |
||||
Canvas.SetTop(_text, y); |
||||
|
||||
} |
||||
|
||||
protected void HideSizeAndShowHandles() |
||||
{ |
||||
SizeDisplayExtension sizeDisplay = null; |
||||
MarginHandleExtension marginDisplay = null; |
||||
foreach (var extension in ExtendedItem.Extensions) |
||||
{ |
||||
if (extension is SizeDisplayExtension) |
||||
sizeDisplay = extension as SizeDisplayExtension; |
||||
if (extension is MarginHandleExtension) |
||||
marginDisplay = extension as MarginHandleExtension; |
||||
} |
||||
|
||||
if (sizeDisplay != null) |
||||
{ |
||||
sizeDisplay.HeightDisplay.Visibility = Visibility.Hidden; |
||||
sizeDisplay.WidthDisplay.Visibility = Visibility.Hidden; |
||||
} |
||||
if (marginDisplay != null) |
||||
{ |
||||
marginDisplay.ShowHandles(); |
||||
} |
||||
} |
||||
|
||||
protected void ResetWidthHeightProperties() |
||||
{ |
||||
ExtendedItem.Properties.GetProperty(FrameworkElement.HeightProperty).Reset(); |
||||
ExtendedItem.Properties.GetProperty(FrameworkElement.WidthProperty).Reset(); |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
@ -0,0 +1,248 @@
@@ -0,0 +1,248 @@
|
||||
/* |
||||
* Created by SharpDevelop. |
||||
* User: trubra |
||||
* Date: 2014-12-22 |
||||
* Time: 10:34 |
||||
* |
||||
* To change this template use Tools | Options | Coding | Edit Standard Headers. |
||||
*/ |
||||
using System; |
||||
using ICSharpCode.WpfDesign.Extensions; |
||||
using ICSharpCode.WpfDesign; |
||||
using ICSharpCode.WpfDesign.Adorners; |
||||
using ICSharpCode.WpfDesign.Designer; |
||||
using ICSharpCode.WpfDesign.Designer.Controls; |
||||
using System.Windows.Input; |
||||
using System.Windows.Media; |
||||
using System.Windows.Shapes; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
namespace ICSharpCode.WpfDesign.Designer.Extensions |
||||
{ |
||||
/// <summary>
|
||||
/// Description of LineHandlerExtension.
|
||||
/// </summary>
|
||||
[ExtensionFor(typeof(Line), OverrideExtensions = new Type[] { typeof(ResizeThumbExtension), typeof(SelectedElementRectangleExtension), typeof(CanvasPositionExtension), typeof(QuickOperationMenuExtension), typeof(RotateThumbExtension), typeof(RenderTransformOriginExtension), typeof(InPlaceEditorExtension) })] |
||||
internal class LineHandlerExtension : LineExtensionBase |
||||
{ |
||||
//
|
||||
private double CurrentX2; |
||||
private double CurrentY2; |
||||
private double CurrentLeft; |
||||
private double CurrentTop; |
||||
|
||||
//Size oldSize;
|
||||
ZoomControl zoom; |
||||
|
||||
protected ResizeThumb CreateThumb(PlacementAlignment alignment, Cursor cursor) |
||||
{ |
||||
ResizeThumb resizeThumb = new ResizeThumb { Alignment = alignment, Cursor = cursor, IsPrimarySelection = true}; |
||||
AdornerPanel.SetPlacement(resizeThumb, Place(ref resizeThumb, alignment)); |
||||
|
||||
adornerPanel.Children.Add(resizeThumb); |
||||
|
||||
DragListener drag = new DragListener(resizeThumb); |
||||
drag.Started += drag_Started; |
||||
drag.Changed += drag_Changed; |
||||
drag.Completed += drag_Completed; |
||||
return resizeThumb; |
||||
} |
||||
|
||||
protected Bounds CalculateDrawing(double x, double y, double left, double top, double xleft, double xtop) |
||||
{ |
||||
|
||||
Double theta = (180 / Math.PI) * Math.Atan2(y, x); |
||||
double verticaloffset = Math.Abs(90 - Math.Abs(theta)); |
||||
if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) |
||||
{ |
||||
if (Math.Abs(theta) < 45 || Math.Abs(theta) > 135) |
||||
{ |
||||
y = 0; |
||||
top = xtop; |
||||
} |
||||
else if (verticaloffset < 45) |
||||
{ |
||||
x = 0; |
||||
left = xleft; |
||||
} |
||||
} |
||||
else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) |
||||
{ |
||||
if (verticaloffset < 10) |
||||
{ |
||||
x = 0; |
||||
left = xleft; |
||||
} |
||||
else if (Math.Abs(theta) < 10 || Math.Abs(theta) > 170) |
||||
{ |
||||
y = 0; |
||||
top = xtop; |
||||
} |
||||
} |
||||
|
||||
SetSurfaceInfo(0, 0, Math.Round((180 / Math.PI) * Math.Atan2(y, x), 0).ToString()); |
||||
return new Bounds { X = Math.Round(x, 1), Y = Math.Round(y, 1), Left = Math.Round(left, 1), Top = Math.Round(top, 1) }; |
||||
} |
||||
|
||||
#region eventhandlers
|
||||
|
||||
|
||||
// TODO : Remove all hide/show extensions from here.
|
||||
protected virtual void drag_Started(DragListener drag) |
||||
{ |
||||
Line al = ExtendedItem.View as Line; |
||||
CurrentX2 = al.X2; |
||||
CurrentY2 = al.Y2; |
||||
CurrentLeft = (double)al.GetValue(Canvas.LeftProperty); |
||||
CurrentTop = (double)al.GetValue(Canvas.TopProperty); |
||||
|
||||
var designPanel = ExtendedItem.Services.DesignPanel as DesignPanel; |
||||
if (designPanel != null) |
||||
{ |
||||
var p = VisualTreeHelper.GetParent(designPanel); |
||||
while (p != null && !(p is ZoomControl)) |
||||
{ |
||||
p = VisualTreeHelper.GetParent(p); |
||||
} |
||||
zoom = p as ZoomControl; |
||||
} |
||||
|
||||
if (resizeBehavior != null) |
||||
operation = PlacementOperation.Start(extendedItemArray, PlacementType.Resize); |
||||
else |
||||
{ |
||||
changeGroup = this.ExtendedItem.Context.OpenGroup("Resize", extendedItemArray); |
||||
} |
||||
_isResizing = true; |
||||
|
||||
(drag.Target as ResizeThumb).IsPrimarySelection = false; |
||||
} |
||||
|
||||
protected virtual void drag_Changed(DragListener drag) |
||||
{ |
||||
Line al = ExtendedItem.View as Line; |
||||
|
||||
var alignment = (drag.Target as ResizeThumb).Alignment; |
||||
var info = operation.PlacedItems[0]; |
||||
double dx = 0; |
||||
double dy = 0; |
||||
|
||||
if (zoom != null) |
||||
{ |
||||
dx = drag.Delta.X * (1 / zoom.CurrentZoom); |
||||
dy = drag.Delta.Y * (1 / zoom.CurrentZoom); |
||||
} |
||||
double top, left, x, y, xtop, xleft; |
||||
|
||||
|
||||
if (alignment == PlacementAlignment.TopLeft) |
||||
{ |
||||
//normal values
|
||||
x = CurrentX2 - dx; |
||||
y = CurrentY2 - dy; |
||||
top = CurrentTop + dy; |
||||
left = CurrentLeft + dx; |
||||
|
||||
//values to use when keys are pressed
|
||||
xtop = CurrentTop + CurrentY2; |
||||
xleft = CurrentLeft + CurrentX2; |
||||
|
||||
} |
||||
else |
||||
{ |
||||
x = CurrentX2 + dx; |
||||
y = CurrentY2 + dy; |
||||
top = xtop = CurrentTop; |
||||
left = xleft = CurrentLeft; |
||||
} |
||||
Bounds position = CalculateDrawing(x, y, left, top, xleft, xtop); |
||||
|
||||
ExtendedItem.Properties.GetProperty(Line.X1Property).SetValue(0); |
||||
ExtendedItem.Properties.GetProperty(Line.Y1Property).SetValue(0); |
||||
ExtendedItem.Properties.GetProperty(Line.X2Property).SetValue(position.X); |
||||
ExtendedItem.Properties.GetProperty(Line.Y2Property).SetValue(position.Y); |
||||
|
||||
if (operation != null) |
||||
{ |
||||
var result = info.OriginalBounds; |
||||
result.X = position.Left; |
||||
result.Y = position.Top; |
||||
result.Width = Math.Abs(position.X); |
||||
result.Height = Math.Abs(position.Y); |
||||
|
||||
info.Bounds = result.Round(); |
||||
operation.CurrentContainerBehavior.BeforeSetPosition(operation); |
||||
operation.CurrentContainerBehavior.SetPosition(info); |
||||
} |
||||
(drag.Target as ResizeThumb).InvalidateArrange(); |
||||
ResetWidthHeightProperties(); |
||||
} |
||||
|
||||
protected virtual void drag_Completed(DragListener drag) |
||||
{ |
||||
if (operation != null) |
||||
{ |
||||
if (drag.IsCanceled) operation.Abort(); |
||||
else |
||||
{ |
||||
ResetWidthHeightProperties(); |
||||
|
||||
operation.Commit(); |
||||
} |
||||
operation = null; |
||||
} |
||||
else |
||||
{ |
||||
if (drag.IsCanceled) changeGroup.Abort(); |
||||
else changeGroup.Commit(); |
||||
changeGroup = null; |
||||
} |
||||
|
||||
_isResizing = false; |
||||
(drag.Target as ResizeThumb).IsPrimarySelection = true; |
||||
HideSizeAndShowHandles(); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// is invoked whenever a line is selected on the canvas, remember that the adorners are created for each line object and never destroyed
|
||||
/// </summary>
|
||||
protected override void OnInitialized() |
||||
{ |
||||
base.OnInitialized(); |
||||
if (ExtendedItem.Properties.GetProperty(Line.StrokeProperty).ValueOnInstance == null) |
||||
{ |
||||
ExtendedItem.Properties[Shape.StrokeProperty].SetValue(Colors.Black); |
||||
ExtendedItem.Properties[Shape.StrokeThicknessProperty].SetValue(2d); |
||||
ExtendedItem.Properties[Shape.StretchProperty].SetValue(Stretch.None); |
||||
ExtendedItem.Properties.GetProperty(Line.X1Property).SetValue(0); |
||||
ExtendedItem.Properties.GetProperty(Line.Y1Property).SetValue(0); |
||||
ExtendedItem.Properties.GetProperty(Line.X2Property).SetValue(20); |
||||
ExtendedItem.Properties.GetProperty(Line.Y2Property).SetValue(20); |
||||
(ExtendedItem.View as Line).BringIntoView(); |
||||
} |
||||
|
||||
resizeThumbs = new ResizeThumb[] |
||||
{ |
||||
CreateThumb(PlacementAlignment.TopLeft, Cursors.SizeNWSE), |
||||
CreateThumb(PlacementAlignment.BottomRight, Cursors.SizeNWSE) |
||||
}; |
||||
|
||||
extendedItemArray[0] = this.ExtendedItem; |
||||
|
||||
Invalidate(); |
||||
//ResetWidthHeightProperties();
|
||||
|
||||
this.ExtendedItem.PropertyChanged += OnPropertyChanged; |
||||
this.Services.Selection.PrimarySelectionChanged += OnPrimarySelectionChanged; |
||||
resizeBehavior = PlacementOperation.GetPlacementBehavior(extendedItemArray); |
||||
UpdateAdornerVisibility(); |
||||
OnPrimarySelectionChanged(null, null); |
||||
} |
||||
|
||||
#endregion
|
||||
} |
||||
} |
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* Created by SharpDevelop. |
||||
* User: trubra |
||||
* Date: 2014-12-22 |
||||
* Time: 11:06 |
||||
* |
||||
* To change this template use Tools | Options | Coding | Edit Standard Headers. |
||||
*/ |
||||
using System; |
||||
using System.Windows; |
||||
using ICSharpCode.WpfDesign.Adorners; |
||||
using ICSharpCode.WpfDesign.Designer.Controls; |
||||
|
||||
namespace ICSharpCode.WpfDesign.Designer.Extensions |
||||
{ |
||||
/// <summary>
|
||||
/// Description of MultiPointResizeThumb.
|
||||
/// </summary>
|
||||
sealed class MultiPointResizeThumb: ResizeThumb |
||||
{ |
||||
private int _index; |
||||
public int Index |
||||
{ |
||||
get { return _index; } |
||||
set |
||||
{ |
||||
_index = value; |
||||
PointTrackerPlacementSupport p = AdornerPlacement as PointTrackerPlacementSupport; |
||||
if (p != null) |
||||
p.Index = value; |
||||
} |
||||
} |
||||
|
||||
public void Invalidate() |
||||
{ |
||||
PointTrackerPlacementSupport p = AdornerPlacement as PointTrackerPlacementSupport; |
||||
|
||||
} |
||||
|
||||
private AdornerPlacement _adornerPlacement; |
||||
|
||||
public AdornerPlacement AdornerPlacement |
||||
{ |
||||
get { return _adornerPlacement; } |
||||
set { _adornerPlacement = value; } |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
/* |
||||
* Created by SharpDevelop. |
||||
* User: trubra |
||||
* Date: 2014-12-22 |
||||
* Time: 11:05 |
||||
* |
||||
* To change this template use Tools | Options | Coding | Edit Standard Headers. |
||||
*/ |
||||
using System; |
||||
using System.Windows; |
||||
using System.Windows.Shapes; |
||||
using ICSharpCode.WpfDesign; |
||||
using ICSharpCode.WpfDesign.Adorners; |
||||
|
||||
namespace ICSharpCode.WpfDesign.Designer.Extensions |
||||
{ |
||||
public class PointTrackerPlacementSupport : AdornerPlacement |
||||
{ |
||||
private Shape shape; |
||||
private PlacementAlignment alignment; |
||||
|
||||
public int Index |
||||
{ |
||||
get; set; |
||||
} |
||||
public PointTrackerPlacementSupport(Shape s, PlacementAlignment align, int index) |
||||
{ |
||||
shape = s; |
||||
alignment = align; |
||||
Index = index; |
||||
} |
||||
|
||||
|
||||
|
||||
public PointTrackerPlacementSupport(Shape s, PlacementAlignment align) |
||||
{ |
||||
shape = s; |
||||
alignment = align; |
||||
Index = -1; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Arranges the adorner element on the specified adorner panel.
|
||||
/// </summary>
|
||||
public override void Arrange(AdornerPanel panel, UIElement adorner, Size adornedElementSize) |
||||
{ |
||||
Point p = new Point(0, 0); |
||||
double thumbsize = 7; |
||||
double distance = thumbsize / 2; |
||||
if (shape as Line != null) |
||||
{ |
||||
Line s = shape as Line; |
||||
double x, y; |
||||
//will give you the angle of the line if more than 180 degrees it becomes negative from
|
||||
Double theta = (180 / Math.PI) * Math.Atan2(s.Y2 - s.Y1, s.X2 - s.X1); |
||||
|
||||
//this will give you the x offset from the line x point in parts of half the size of the thumb
|
||||
double dx = Math.Cos(theta * (Math.PI / 180)) * distance; |
||||
|
||||
//this will give you the y offset from the line y point in parts of half the size of the thumb
|
||||
double dy = Math.Sin(theta * (Math.PI / 180)) * distance; |
||||
|
||||
if (alignment == PlacementAlignment.BottomRight) |
||||
{ |
||||
//x offset is linear
|
||||
x = s.X2 - Math.Abs(theta) / (180 / thumbsize) + dx; |
||||
//y offset is angular
|
||||
y = s.Y2 - ((.5 - Math.Sin(theta * (Math.PI / 180)) * .5) * thumbsize) + dy; |
||||
} |
||||
else |
||||
{ |
||||
x = s.X1 - ((180 - Math.Abs(theta)) / (180 / thumbsize)) - dx; |
||||
y = s.Y1 - ((.5 - Math.Sin(theta * (Math.PI / 180) + Math.PI) * .5) * thumbsize) - dy; |
||||
} |
||||
p = new Point(x, y); |
||||
|
||||
} |
||||
Polygon pg = shape as Polygon; |
||||
Polyline pl = shape as Polyline; |
||||
if (pg != null || pl != null) |
||||
{ |
||||
if (Index > 0) |
||||
{ |
||||
p = pl != null ? pl.Points[Index] : pg.Points[Index]; |
||||
|
||||
} |
||||
} |
||||
adorner.Arrange(new Rect(p, new Size(thumbsize, thumbsize))); |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
@ -0,0 +1,450 @@
@@ -0,0 +1,450 @@
|
||||
/* |
||||
* Created by SharpDevelop. |
||||
* User: trubra |
||||
* Date: 2014-12-22 |
||||
* Time: 10:34 |
||||
* |
||||
* To change this template use Tools | Options | Coding | Edit Standard Headers. |
||||
*/ |
||||
using System; |
||||
using ICSharpCode.WpfDesign.Extensions; |
||||
using ICSharpCode.WpfDesign; |
||||
using ICSharpCode.WpfDesign.Adorners; |
||||
using ICSharpCode.WpfDesign.Designer; |
||||
using ICSharpCode.WpfDesign.Designer.Controls; |
||||
using System.Windows.Input; |
||||
using System.Windows.Media; |
||||
using System.Windows.Shapes; |
||||
using System.Windows; |
||||
using System.Windows.Controls; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
namespace ICSharpCode.WpfDesign.Designer.Extensions |
||||
{ |
||||
/// <summary>
|
||||
/// Description of PolyLineHandlerExtension.
|
||||
/// </summary>
|
||||
[ExtensionFor(typeof(Polyline), OverrideExtensions = new Type[] { typeof(ResizeThumbExtension), typeof(SelectedElementRectangleExtension), typeof(CanvasPositionExtension), typeof(QuickOperationMenuExtension), typeof(RotateThumbExtension), typeof(RenderTransformOriginExtension), typeof(InPlaceEditorExtension), typeof(SizeDisplayExtension) })] |
||||
internal class PolyLineHandlerExtension : LineExtensionBase, IKeyDown, IKeyUp |
||||
{ |
||||
private readonly Dictionary<int, Bounds> _selectedThumbs = new Dictionary<int, Bounds>(); |
||||
private bool _isDragging; |
||||
ZoomControl _zoom; |
||||
|
||||
#region thumb methods
|
||||
protected ResizeThumb CreateThumb(PlacementAlignment alignment, Cursor cursor, int index) |
||||
{ |
||||
ResizeThumb resizeThumb = new MultiPointResizeThumb { Index = index, Alignment = alignment, Cursor = cursor, IsPrimarySelection = true }; |
||||
AdornerPlacement ap = Place(ref resizeThumb, alignment, index); |
||||
(resizeThumb as MultiPointResizeThumb).AdornerPlacement = ap; |
||||
|
||||
AdornerPanel.SetPlacement(resizeThumb, ap); |
||||
adornerPanel.Children.Add(resizeThumb); |
||||
|
||||
DragListener drag = new DragListener(resizeThumb); |
||||
|
||||
WeakEventManager<ResizeThumb, MouseButtonEventArgs>.AddHandler(resizeThumb, "PreviewMouseLeftButtonDown", ResizeThumbOnMouseLeftButtonUp); |
||||
|
||||
drag.Started += drag_Started; |
||||
drag.Changed += drag_Changed; |
||||
drag.Completed += drag_Completed; |
||||
return resizeThumb; |
||||
} |
||||
|
||||
private void ResetThumbs() |
||||
{ |
||||
foreach (FrameworkElement rt in adornerPanel.Children) |
||||
{ |
||||
if (rt is ResizeThumb) |
||||
(rt as ResizeThumb).IsPrimarySelection = true; |
||||
} |
||||
_selectedThumbs.Clear(); |
||||
} |
||||
|
||||
private void SelectThumb(MultiPointResizeThumb mprt) |
||||
{ |
||||
PointCollection points = GetPointCollection(); |
||||
Point p = points[mprt.Index]; |
||||
_selectedThumbs.Add(mprt.Index, new Bounds { X = p.X, Y = p.Y }); |
||||
|
||||
mprt.IsPrimarySelection = false; |
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
#region eventhandlers
|
||||
|
||||
private void ResizeThumbOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs) |
||||
{ |
||||
//get current thumb
|
||||
MultiPointResizeThumb mprt = sender as MultiPointResizeThumb; |
||||
if (mprt != null) |
||||
{ |
||||
//shift+ctrl will remove selected point
|
||||
if ((Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) && |
||||
(Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))) |
||||
{ |
||||
//unselect all points
|
||||
ResetThumbs(); |
||||
PointCollection points = GetPointCollection(); |
||||
|
||||
//iterate thumbs to lower index of remaining thumbs
|
||||
foreach (MultiPointResizeThumb m in adornerPanel.Children) |
||||
{ |
||||
if (m.Index > mprt.Index) |
||||
m.Index--; |
||||
} |
||||
|
||||
//remove point and thumb
|
||||
points.RemoveAt(mprt.Index); |
||||
adornerPanel.Children.Remove(mprt); |
||||
|
||||
Invalidate(); |
||||
} |
||||
else |
||||
{ |
||||
//if not keyboard ctrl is pressed and selected point is not previously selected, clear selection
|
||||
if (!_selectedThumbs.ContainsKey(mprt.Index) & !Keyboard.IsKeyDown(Key.LeftCtrl) & |
||||
!Keyboard.IsKeyDown(Key.RightCtrl)) |
||||
{ |
||||
ResetThumbs(); |
||||
} |
||||
//add selected thumb, if ctrl pressed this could be all points in poly
|
||||
if (!_selectedThumbs.ContainsKey(mprt.Index)) |
||||
SelectThumb(mprt); |
||||
_isDragging = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// TODO : Remove all hide/show extensions from here.
|
||||
protected void drag_Started(DragListener drag) |
||||
{ |
||||
//get current thumb
|
||||
MultiPointResizeThumb mprt = (drag.Target as MultiPointResizeThumb); |
||||
if (mprt != null) |
||||
{ |
||||
SetOperation(); |
||||
} |
||||
} |
||||
|
||||
void SetOperation() |
||||
{ |
||||
var designPanel = ExtendedItem.Services.DesignPanel as DesignPanel; |
||||
if (designPanel != null) |
||||
{ |
||||
var p = VisualTreeHelper.GetParent(designPanel); |
||||
while (p != null && !(p is ZoomControl)) |
||||
{ |
||||
p = VisualTreeHelper.GetParent(p); |
||||
} |
||||
_zoom = p as ZoomControl; |
||||
} |
||||
|
||||
if (resizeBehavior != null) |
||||
operation = PlacementOperation.Start(extendedItemArray, PlacementType.Resize); |
||||
else |
||||
{ |
||||
changeGroup = ExtendedItem.Context.OpenGroup("Resize", extendedItemArray); |
||||
} |
||||
_isResizing = true; |
||||
} |
||||
|
||||
void ChangeOperation(PointCollection points) |
||||
{ |
||||
//this is for SharpDevelop built in undo functionality
|
||||
if (operation != null) |
||||
{ |
||||
var info = operation.PlacedItems[0]; |
||||
var result = info.OriginalBounds; |
||||
|
||||
IEnumerable<double> xs = points.Select(x => x.X); |
||||
IEnumerable<double> ys = points.Select(y => y.Y); |
||||
result.X = (double)(info.Item.Properties.GetAttachedProperty(Canvas.LeftProperty).ValueOnInstance); |
||||
result.Y = (double)(info.Item.Properties.GetAttachedProperty(Canvas.TopProperty).ValueOnInstance); |
||||
result.Width = xs.Max() - xs.Min(); |
||||
result.Height = ys.Max() - ys.Min(); |
||||
|
||||
info.Bounds = result.Round(); |
||||
operation.CurrentContainerBehavior.BeforeSetPosition(operation); |
||||
operation.CurrentContainerBehavior.SetPosition(info); |
||||
} |
||||
|
||||
ResetWidthHeightProperties(); |
||||
|
||||
} |
||||
|
||||
void CommitOperation() |
||||
{ |
||||
if (operation != null) |
||||
{ |
||||
PointCollection points; |
||||
Polygon pg = ExtendedItem.View as Polygon; |
||||
Polyline pl = ExtendedItem.View as Polyline; |
||||
if (pl == null) |
||||
{ |
||||
points = pg.Points; |
||||
|
||||
} |
||||
else |
||||
{ |
||||
points = pl.Points; |
||||
} |
||||
|
||||
foreach (int i in _selectedThumbs.Keys) |
||||
{ |
||||
_selectedThumbs[i].X = points[i].X; |
||||
_selectedThumbs[i].Y = points[i].Y; |
||||
} |
||||
ExtendedItem.Properties.GetProperty(pl != null ? Polyline.PointsProperty : Polygon.PointsProperty).SetValue(points); |
||||
ResetWidthHeightProperties(); |
||||
operation.Commit(); |
||||
|
||||
operation = null; |
||||
} |
||||
else |
||||
{ |
||||
changeGroup.Commit(); |
||||
changeGroup = null; |
||||
} |
||||
_isResizing = false; |
||||
|
||||
ResetWidthHeightProperties(); |
||||
Invalidate(); |
||||
} |
||||
|
||||
protected void drag_Changed(DragListener drag) |
||||
{ |
||||
PointCollection points = GetPointCollection(); |
||||
|
||||
MultiPointResizeThumb mprt = drag.Target as MultiPointResizeThumb; |
||||
if (mprt != null) |
||||
{ |
||||
double dx = 0; |
||||
double dy = 0; |
||||
//if has zoomed
|
||||
if (_zoom != null) |
||||
{ |
||||
dx = drag.Delta.X * (1 / _zoom.CurrentZoom); |
||||
dy = drag.Delta.Y * (1 / _zoom.CurrentZoom); |
||||
} |
||||
|
||||
Double theta; |
||||
//if one point selected snapping angle is calculated in relation to previous point
|
||||
if (_selectedThumbs.Count == 1) |
||||
{ |
||||
theta = (180 / Math.PI) * Math.Atan2(_selectedThumbs[mprt.Index].Y + dy - points[mprt.Index - 1].Y, _selectedThumbs[mprt.Index].X + dx - points[mprt.Index - 1].X); |
||||
} |
||||
else//if multiple points snapping angle is calculated in relation to mouse dragging angle
|
||||
{ |
||||
theta = (180 / Math.PI) * Math.Atan2(dy, dx); |
||||
} |
||||
|
||||
//snappingAngle is used for snapping function to horizontal or vertical plane in line drawing, and is activated by pressing ctrl or shift button
|
||||
int? snapAngle = null; |
||||
|
||||
//shift+alt gives a new point
|
||||
if ((Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) && (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))) |
||||
{ |
||||
//if dragging occurs on a point and that point is the only selected, a new node will be added.
|
||||
//_isCtrlDragging is needed since this method is called for every x pixel that the mouse moves
|
||||
//so it could be many thousands of times during a single dragging
|
||||
if (!_isDragging && _selectedThumbs.Count == 1 && (Math.Abs(dx) > 0 || Math.Abs(dy) > 0)) |
||||
{ |
||||
|
||||
//duplicate point that is selected
|
||||
Point p = points[mprt.Index]; |
||||
|
||||
//insert duplicate
|
||||
points.Insert(mprt.Index, p); |
||||
|
||||
//create adorner marker
|
||||
CreateThumb(PlacementAlignment.BottomRight, Cursors.SizeNWSE, mprt.Index); |
||||
|
||||
//set index of all points that had a higher index than selected to +1
|
||||
foreach (FrameworkElement rt in adornerPanel.Children) |
||||
{ |
||||
if (rt is MultiPointResizeThumb) |
||||
{ |
||||
MultiPointResizeThumb t = rt as MultiPointResizeThumb; |
||||
if (t.Index > mprt.Index) |
||||
t.Index++; |
||||
} |
||||
} |
||||
|
||||
//set index of new point to old point index + 1
|
||||
mprt.Index = mprt.Index + 1; |
||||
ResetThumbs(); |
||||
SelectThumb(mprt); |
||||
|
||||
} |
||||
snapAngle = 10; |
||||
} |
||||
|
||||
//snapping occurs when mouse is within 10 degrees from horizontal or vertical plane if shift is pressed
|
||||
else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) |
||||
{ |
||||
snapAngle = 10; |
||||
} |
||||
//snapping occurs within 45 degree intervals that is line will always be horizontal or vertical if alt is pressed
|
||||
else if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) |
||||
{ |
||||
snapAngle = 45; |
||||
} |
||||
_isDragging = true; |
||||
points = MovePoints(points, dx, dy, theta, snapAngle); |
||||
|
||||
} |
||||
ChangeOperation(points); |
||||
(drag.Target as ResizeThumb).InvalidateArrange(); |
||||
|
||||
|
||||
} |
||||
|
||||
protected void drag_Completed(DragListener drag) |
||||
{ |
||||
MultiPointResizeThumb mprt = drag.Target as MultiPointResizeThumb; |
||||
if (mprt != null) |
||||
{ |
||||
if (operation != null && drag.IsCanceled) |
||||
{ |
||||
operation.Abort(); |
||||
} |
||||
else if (drag.IsCanceled) |
||||
{ |
||||
changeGroup.Abort(); |
||||
} |
||||
CommitOperation(); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
protected override void OnInitialized() |
||||
{ |
||||
base.OnInitialized(); |
||||
|
||||
PointCollection points = GetPointCollection(); |
||||
|
||||
if (ExtendedItem.Properties[Shape.StrokeProperty].ValueOnInstance == null) |
||||
{ |
||||
ExtendedItem.Properties[Shape.StrokeProperty].SetValue(Colors.Black); |
||||
ExtendedItem.Properties[Shape.StrokeThicknessProperty].SetValue(2d); |
||||
ExtendedItem.Properties[Shape.StretchProperty].SetValue(Stretch.None); |
||||
points.AddRange(new List<Point> { new Point(0, 0), new Point(20, 20) }); |
||||
ExtendedItem.Properties.GetProperty(ExtendedItem.View as Polyline != null ? Polyline.PointsProperty : Polygon.PointsProperty).SetValue(points); |
||||
} |
||||
|
||||
resizeThumbs = new List<ResizeThumb>(); |
||||
for (int i = 1; i < points.Count; i++) |
||||
{ |
||||
CreateThumb(PlacementAlignment.BottomRight, Cursors.SizeNWSE, i); |
||||
} |
||||
|
||||
Invalidate(); |
||||
|
||||
ResetWidthHeightProperties(); |
||||
|
||||
ResetThumbs(); |
||||
_isDragging = false; |
||||
|
||||
extendedItemArray[0] = ExtendedItem; |
||||
ExtendedItem.PropertyChanged += OnPropertyChanged; |
||||
Services.Selection.PrimarySelectionChanged += OnPrimarySelectionChanged; |
||||
resizeBehavior = PlacementOperation.GetPlacementBehavior(extendedItemArray); |
||||
UpdateAdornerVisibility(); |
||||
OnPrimarySelectionChanged(null, null); |
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
PointCollection GetPointCollection() |
||||
{ |
||||
Polygon pg = ExtendedItem.View as Polygon; |
||||
Polyline pl = ExtendedItem.View as Polyline; |
||||
|
||||
return pl == null ? pg.Points : pl.Points; |
||||
} |
||||
|
||||
PointCollection MovePoints(PointCollection pc, double displacementX, double displacementY, double theta, int? snapangle) |
||||
{ |
||||
//iterate all selected points
|
||||
foreach (int i in _selectedThumbs.Keys) |
||||
{ |
||||
Point p = pc[i]; |
||||
|
||||
//x and y is calculated from the currentl point
|
||||
double x = _selectedThumbs[i].X + displacementX; |
||||
double y = _selectedThumbs[i].Y + displacementY; |
||||
|
||||
//if snap is applied
|
||||
if (snapangle != null) |
||||
{ |
||||
if (_selectedThumbs.Count > 0) |
||||
{ |
||||
//horizontal snap
|
||||
if (Math.Abs(theta) < snapangle || 180 - Math.Abs(theta) < snapangle) |
||||
{ |
||||
//if one point selected use point before as snap point, else snap to movement
|
||||
y = _selectedThumbs.Count == 1 ? pc[i - 1].Y : y - displacementY; |
||||
} |
||||
else if (Math.Abs(90 - Math.Abs(theta)) < snapangle)//vertical snap
|
||||
{ |
||||
//if one point selected use point before as snap point, else snap to movement
|
||||
x = _selectedThumbs.Count == 1 ? pc[i - 1].X : x - displacementX; |
||||
} |
||||
} |
||||
} |
||||
|
||||
p.X = x; |
||||
p.Y = y; |
||||
pc[i] = p; |
||||
} |
||||
return pc; |
||||
} |
||||
|
||||
#region IKeyDown
|
||||
|
||||
public bool InvokeDefaultAction |
||||
{ |
||||
get { return _selectedThumbs.Count == 0 || _selectedThumbs.Count == GetPointCollection().Count - 1; } |
||||
} |
||||
|
||||
int _movingDistance; |
||||
public void KeyDownAction(object sender, KeyEventArgs e) |
||||
{ |
||||
Debug.WriteLine("KeyDown"); |
||||
if (IsArrowKey(e.Key)) |
||||
if (operation == null) |
||||
{ |
||||
SetOperation(); |
||||
_movingDistance = 0; |
||||
} |
||||
|
||||
|
||||
var dx1 = (e.Key == Key.Left) ? Keyboard.IsKeyDown(Key.LeftShift) ? _movingDistance - 10 : _movingDistance - 1 : 0; |
||||
var dy1 = (e.Key == Key.Up) ? Keyboard.IsKeyDown(Key.LeftShift) ? _movingDistance - 10 : _movingDistance - 1 : 0; |
||||
var dx2 = (e.Key == Key.Right) ? Keyboard.IsKeyDown(Key.LeftShift) ? _movingDistance + 10 : _movingDistance + 1 : 0; |
||||
var dy2 = (e.Key == Key.Down) ? Keyboard.IsKeyDown(Key.LeftShift) ? _movingDistance + 10 : _movingDistance + 1 : 0; |
||||
|
||||
ChangeOperation(MovePoints(GetPointCollection(), dx1 + dx2, dy1 + dy2, 0, null)); |
||||
_movingDistance = (dx1 + dx2 + dy1 + dy2); |
||||
} |
||||
|
||||
public void KeyUpAction(object sender, KeyEventArgs e) |
||||
{ |
||||
Debug.WriteLine("Keyup"); |
||||
if (IsArrowKey(e.Key)) |
||||
CommitOperation(); |
||||
} |
||||
|
||||
bool IsArrowKey(Key key) |
||||
{ |
||||
return (key == Key.Left || key == Key.Right || key == Key.Up || key == Key.Down); |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
Loading…
Reference in new issue