using iNKORE.UI.WPF.Modern.Controls; using Microsoft.Office.Interop.PowerPoint; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing.Drawing2D; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Media.Media3D; using System.Windows.Resources; using Ink_Canvas.Popups; using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel; using Application = System.Windows.Application; using Matrix = System.Windows.Media.Matrix; using Point = System.Windows.Point; using Ink_Canvas.Helpers; namespace Ink_Canvas { public partial class MainWindow : PerformanceTransparentWin { #region Floating Control private object lastBorderMouseDownObject; private void Border_MouseDown(object sender, MouseButtonEventArgs e) { lastBorderMouseDownObject = sender; } private bool isStrokeSelectionCloneOn = false; #region StrokesSelectionToolbarButtons private Border BorderStrokeSelectionToolButtonMouseDown = null; private void BorderStrokeSelectionToolButton_MouseDown(object sender, MouseButtonEventArgs e) { var bd = (Border)sender; BorderStrokeSelectionToolButtonMouseDown = bd; var innerBd = (Border)bd.Child; if (bd.Name != "BorderStrokeSelectionDelete") innerBd.Background = new SolidColorBrush(Color.FromArgb(24, 9, 9, 11)); else innerBd.Background = new SolidColorBrush(Color.FromRgb(254, 202, 202)); } private void BorderStrokeSelectionToolButton_MouseLeave(object sender, MouseEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown == null) return; var innerBd = (Border)BorderStrokeSelectionToolButtonMouseDown.Child; innerBd.Background = new SolidColorBrush(Colors.Transparent); BorderStrokeSelectionToolButtonMouseDown = null; } private void UpdateStrokesSelectionCloneToolButtonLimitStatus() { UpdateSelectionToolbarOtherIconLockedStatus(isStrokeSelectionCloneOn); BorderStrokeSelectionLock.IsHitTestVisible = !isStrokeSelectionCloneOn; BorderStrokeSelectionLock.Opacity = isStrokeSelectionCloneOn ? 0.5 : 1; BorderStrokeSelectionLock.IsEnabled = !isStrokeSelectionCloneOn; BorderStrokeSelectionClone.IsHitTestVisible = true; BorderStrokeSelectionClone.Opacity = 1; BorderStrokeSelectionClone.IsEnabled = true; } /// /// 墨迹克隆按钮 /// /// /// private void BorderStrokeSelectionClone_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; var bd = (Border)sender; var innerBd = (Border)bd.Child; // toolbutton BorderStrokeSelectionToolButton_MouseLeave(sender, e); if (isStrokeSelectionCloneOn) { isStrokeSelectionCloneOn = false; innerBd.Background = new SolidColorBrush(Colors.Transparent); BorderStrokeSelectionCloneGeometryIcon.Brush = new SolidColorBrush(Color.FromRgb(24, 24, 27)); } else { isStrokeSelectionCloneOn = true; innerBd.Background = new SolidColorBrush(Color.FromRgb(187, 247, 208)); BorderStrokeSelectionCloneGeometryIcon.Brush = new SolidColorBrush(Color.FromRgb(21, 128, 61)); } UpdateStrokesSelectionCloneToolButtonLimitStatus(); } /// /// 复制到白板或复制到新页面 /// /// /// private void BorderStrokeSelectionCloneToNewBoard_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; // toolbutton BorderStrokeSelectionToolButton_MouseLeave(sender, e); var strokes = inkCanvas.GetSelectedStrokes(); inkCanvas.Select(new StrokeCollection()); CancelCurrentStrokesSelection(); strokes = strokes.Clone(); if (currentMode == 0) ImageBlackboard_MouseUp(null, null); else BtnWhiteBoardAdd_Click(null, null); inkCanvas.Strokes.Add(strokes); } /// /// 删除墨迹 /// /// /// private void BorderStrokeSelectionDelete_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; SymbolIconDelete_MouseUp(sender, e); // toolbutton BorderStrokeSelectionToolButton_MouseLeave(sender, e); } /// /// 墨迹的 更多 菜单 /// /// /// private void BorderStrokeMoreMenuButton_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; var cm = this.FindResource("StrokesSelectionMoreMenuButtonContextMenu") as ContextMenu; cm.IsOpen = true; // toolbutton BorderStrokeSelectionToolButton_MouseLeave(sender, e); } /// /// 通用的墨迹矩阵操作 /// /// private void MatrixStrokes(Func func) { var m = new Matrix(); Point center = new Point(inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Width / 2, inkCanvas.GetSelectionBounds().Top + inkCanvas.GetSelectionBounds().Height / 2); center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点 m = func(m,center); var targetStrokes = inkCanvas.GetSelectedStrokes(); foreach (var stroke in targetStrokes) stroke.Transform(m, false); if (DrawingAttributesHistory.Count > 0) { timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } private void __tb_preview__(object sender, MouseButtonEventArgs e) { // toolbutton BorderStrokeSelectionToolButton_MouseLeave(sender, e); // update preview and border InkSelectionStrokesOverlay.DrawStrokes(inkCanvas.GetSelectedStrokes(), new Matrix()); UpdateStrokeSelectionBorder(true, inkCanvas.GetSelectionBounds()); updateBorderStrokeSelectionControlLocation(); } /// /// 旋转墨迹按钮 /// /// /// private void ImageRotate_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; MatrixStrokes((m, cent) => { m.RotateAt((((Border)sender).Name=="BorderImageRotate45"?45:90) * (StrokesRotateClockwise == 0 ? 1 : -1), cent.X, cent.Y); return m; }); __tb_preview__(sender, e); } /// /// 翻转墨迹按钮 /// /// /// private void ImageFlip_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; MatrixStrokes((m, cent) => { m.ScaleAt((((Border)sender).Name=="BorderImageFlipHorizontal"?-1:1),(((Border)sender).Name=="BorderImageFlipVertical"?-1:1),cent.X,cent.Y); return m; }); __tb_preview__(sender, e); } #endregion private void GridPenWidthDecrease_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; ChangeStrokeThickness(0.8); } private void GridPenWidthIncrease_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; ChangeStrokeThickness(1.25); } private void ChangeStrokeThickness(double multipler) { foreach (var stroke in inkCanvas.GetSelectedStrokes()) { var newWidth = stroke.DrawingAttributes.Width * multipler; var newHeight = stroke.DrawingAttributes.Height * multipler; if (!(newWidth >= DrawingAttributes.MinWidth) || !(newWidth <= DrawingAttributes.MaxWidth) || !(newHeight >= DrawingAttributes.MinHeight) || !(newHeight <= DrawingAttributes.MaxHeight)) continue; stroke.DrawingAttributes.Width = newWidth; stroke.DrawingAttributes.Height = newHeight; } if (DrawingAttributesHistory.Count > 0) { timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } private void GridPenWidthRestore_MouseUp(object sender, MouseButtonEventArgs e) { if (lastBorderMouseDownObject != sender) return; foreach (var stroke in inkCanvas.GetSelectedStrokes()) { stroke.DrawingAttributes.Width = inkCanvas.DefaultDrawingAttributes.Width; stroke.DrawingAttributes.Height = inkCanvas.DefaultDrawingAttributes.Height; } } private enum StrokesRotateClockwiseEnum { RotateClockwise, RotateCounterClockwise, } private StrokesRotateClockwiseEnum StrokesRotateClockwise = StrokesRotateClockwiseEnum.RotateClockwise; private void ImageRotateClockwise_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; StrokesRotateClockwise = (StrokesRotateClockwiseEnum)(StrokesRotateClockwise == 0 ? 1 : 0); BorderImageRotateClockwise.RenderTransform = new ScaleTransform(StrokesRotateClockwise == 0 ? 1 : -1, 1); // toolbutton BorderStrokeSelectionToolButton_MouseLeave(sender, e); } #endregion private bool isGridInkCanvasSelectionCoverMouseDown = false; private StrokeCollection StrokesSelectionClone = new StrokeCollection(); private bool isRectangleSelectionMouseDown = false; // 矩形框选 private Point rectangleSelection_FirstPoint = new Point(0, 0); private Point rectangleSelection_LastPoint = new Point(0, 0); // 套索框选 private PointCollection rectangleSelection_LassoPoints = new PointCollection(); private void GridInkCanvasSelectionCover_MouseDown(object sender, MouseButtonEventArgs e) { isGridInkCanvasSelectionCoverMouseDown = true; } private void GridInkCanvasSelectionCover_MouseUp(object sender, MouseButtonEventArgs e) { isGridInkCanvasSelectionCoverMouseDown = false; CancelCurrentStrokesSelection(); } private void InkCanvasDeleteCommandFiredEvent(object sender, RoutedEventArgs e) { SymbolIconDelete_MouseUp(null, null); } private void RectangleSelectionHitTestBorder_MouseDown(object sender, MouseButtonEventArgs e) { var pt = e.GetPosition(Main_Grid); var nt = inkCanvas.Strokes.HitTest(pt, 8); if (nt.Count > 0 && !(e.RightButton == MouseButtonState.Pressed)) { if (nt.Count > 1) { var nodia = nt.HitTest(pt); if (nodia.Count > 0) { if (nodia[nodia.Count - 1].ContainsPropertyData(IsLockGuid) && Settings.Canvas.AllowClickToSelectLockedStroke) return; inkCanvas.Select(new StrokeCollection() {nodia[nodia.Count-1]}); } else { if (nt[0].ContainsPropertyData(IsLockGuid) && Settings.Canvas.AllowClickToSelectLockedStroke) return; inkCanvas.Select(new StrokeCollection() { nt[0] }); } } else if (nt.Count == 1) { if (nt[0].ContainsPropertyData(IsLockGuid) && Settings.Canvas.AllowClickToSelectLockedStroke) return; inkCanvas.Select(nt); } } else { RectangleSelectionHitTestBorder.CaptureMouse(); isRectangleSelectionMouseDown = true; if (Settings.Canvas.SelectionMethod == 1) { rectangleSelection_FirstPoint = pt; } else { rectangleSelection_LassoPoints.Clear(); rectangleSelection_LassoPoints.Add(pt); } } } private void RectangleSelectionHitTestBorder_MouseMove(object sender, MouseEventArgs e) { var pt = e.GetPosition(Main_Grid); if (!isRectangleSelectionMouseDown) return; if (Settings.Canvas.SelectionMethod == 1) { rectangleSelection_LastPoint = pt; RectangleSelection.DrawSelectionBox(new Rect(rectangleSelection_FirstPoint, rectangleSelection_LastPoint)); } else { rectangleSelection_LassoPoints.Add(pt); RectangleSelection.DrawLassoLine(rectangleSelection_LassoPoints); } } private void RectangleSelectionHitTestBorder_MouseUp(object sender, MouseButtonEventArgs e) { if (!isRectangleSelectionMouseDown) return; RectangleSelectionHitTestBorder.ReleaseMouseCapture(); isRectangleSelectionMouseDown = false; var pt = e.GetPosition(Main_Grid); var ilh = inkCanvas.Strokes.GetIncrementalLassoHitTester(Settings.Canvas.OnlyHitTestFullyContainedStrokes ? 100 : 1); void func(object s, LassoSelectionChangedEventArgs _e) { var _ilh = s as IncrementalLassoHitTester; if (_e.SelectedStrokes.Count==0) UpdateStrokeSelectionBorder(false, null); inkCanvas.Select(_e.SelectedStrokes); _ilh.EndHitTesting(); } ilh.SelectionChanged += func; if (Settings.Canvas.SelectionMethod == 1) { rectangleSelection_LastPoint = pt; var rct = new Rect(rectangleSelection_FirstPoint, rectangleSelection_LastPoint); ilh.AddPoints(new Point[] { rct.TopLeft, rct.TopRight, rct.BottomRight, rct.BottomLeft, rct.TopLeft }); } else { rectangleSelection_LassoPoints.Add(pt); ilh.AddPoints(rectangleSelection_LassoPoints); } RectangleSelection.ClearDrawing(); } private Border[] StrokeSelectionBorderHandles = new Border[] { }; #region StrokeSelectionBorder private bool isLockedStrokeSelectionHandle = false; private Border lockedStrokeSelectionHandle = null; private StrokeSelectionBorderHandlesEnum? lockedStrokeSelectionBorderHandleType = null; private Rect? originalSelectionBounds = null; private Rect? resizedSelectionBounds = null; private Point? resizingFirstPoint = null; private Point? resizingLastPoint = null; private void StrokeSelectionBorderHandle_MouseDown(object sender, MouseButtonEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (isLockedStrokeSelectionHandle || isLockedStrokeSelectionMove || isLockedStrokeSelectionRotate) return; // lock isLockedStrokeSelectionHandle = true; var bd = (Border)sender; lockedStrokeSelectionHandle = bd; if (bd.Name != "StrokeSelectionRotateHandle") { var index = StrokeSelectionBorderGrid.Children.IndexOf((Border)sender) - 3; lockedStrokeSelectionBorderHandleType = (StrokeSelectionBorderHandlesEnum)index; } else { lockedStrokeSelectionBorderHandleType = StrokeSelectionBorderHandlesEnum.Rotate; } // capture // TODO 这里还需要对触摸屏幕的单个TouchDevice进行Capture bd.CaptureMouse(); // hide selectionToolBar BorderStrokeSelectionControl.Visibility = Visibility.Collapsed; // resize toast var sb = inkCanvas.GetSelectionBounds(); if (!(sb.Width < 96 && sb.Height < 64)) { StrokeSelectionSizeToast.Visibility = Visibility.Visible; ((TextBlock)StrokeSelectionSizeToastInner.Children[0]).Text = Math.Round(sb.Width, 2).ToString(); ((TextBlock)StrokeSelectionSizeToastInner.Children[2]).Text = Math.Round(sb.Height, 2).ToString(); var lti = (int)lockedStrokeSelectionBorderHandleType; System.Windows.Controls.Canvas.SetLeft(StrokeSelectionSizeToast, double.NaN); System.Windows.Controls.Canvas.SetRight(StrokeSelectionSizeToast, double.NaN); System.Windows.Controls.Canvas.SetTop(StrokeSelectionSizeToast, double.NaN); System.Windows.Controls.Canvas.SetBottom(StrokeSelectionSizeToast, double.NaN); if (lti == 0 || lti == 2 || lti == 4 || lti == 7) System.Windows.Controls.Canvas.SetLeft(StrokeSelectionSizeToast,1); else System.Windows.Controls.Canvas.SetRight(StrokeSelectionSizeToast, 1); if (lti == 0 || lti == 1 || lti == 4 || lti == 6) System.Windows.Controls.Canvas.SetTop(StrokeSelectionSizeToast, 1); else System.Windows.Controls.Canvas.SetBottom(StrokeSelectionSizeToast, 1); } else { StrokeSelectionSizeToast.Visibility = Visibility.Collapsed; } // record first pt var nowWindowPosition = e.GetPosition(Main_Grid); resizingFirstPoint = nowWindowPosition; // record original bounds var transform = StrokeSelectionBorder.TransformToVisual(Main_Grid); var ori_lt = transform.Transform(new Point(0, 0)); var ori_rb = transform.Transform(new Point(StrokeSelectionBorder.Width, StrokeSelectionBorder.Height)); originalSelectionBounds = new Rect(ori_lt, ori_rb); } private void StrokeSelectionBorderHandle_MouseUp(object sender, MouseButtonEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (!isLockedStrokeSelectionHandle || isLockedStrokeSelectionMove || isLockedStrokeSelectionRotate) return; // resize strokes and preview strokes SelectedStrokesResize(inkCanvas.GetSelectedStrokes(), (StrokeSelectionBorderHandlesEnum)lockedStrokeSelectionBorderHandleType, (Rect)originalSelectionBounds, (Rect)resizedSelectionBounds); InkSelectionStrokesOverlay.DrawStrokes(inkCanvas.GetSelectedStrokes(), new Matrix()); // display selectionToolBar var bd = new Binding("Visibility"); bd.Source = GridInkCanvasSelectionCover; BorderStrokeSelectionControl.SetBinding(Border.VisibilityProperty, bd); // unlock isLockedStrokeSelectionHandle = false; lockedStrokeSelectionBorderHandleType = null; // release capture // TODO 这里还需要对触摸屏幕的单个TouchDevice进行ReleaseCapture lockedStrokeSelectionHandle.ReleaseMouseCapture(); // unlock lockedStrokeSelectionHandle = null; // resize toast StrokeSelectionSizeToast.Visibility = Visibility.Collapsed; // update selection border size UpdateStrokeSelectionBorder(true, inkCanvas.GetSelectionBounds()); updateBorderStrokeSelectionControlLocation(); } private void StrokeSelectionBorderHandle_MouseMove(object sender, MouseEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (!isLockedStrokeSelectionHandle || isLockedStrokeSelectionMove || isLockedStrokeSelectionRotate) return; var bd = (Border)sender; var nowWindowPosition = e.GetPosition(Main_Grid); // record last pt var rlp = nowWindowPosition; // resize preview border var isReverseWidth = false; // 是否逆向 resize Width,true 為調小,false為調大 var isReverseHeight = false; // 是否逆向 resize Height,true 為調小,false為調大 if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.LeftTop) { isReverseWidth = ((Point)rlp).X > ((Point)resizingFirstPoint).X; isReverseHeight = ((Point)rlp).Y > ((Point)resizingFirstPoint).Y; } else if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.RightTop) { isReverseWidth = ((Point)rlp).X < ((Point)resizingFirstPoint).X; isReverseHeight = ((Point)rlp).Y > ((Point)resizingFirstPoint).Y; } else if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.LeftBottom) { isReverseWidth = ((Point)rlp).X > ((Point)resizingFirstPoint).X; isReverseHeight = ((Point)rlp).Y < ((Point)resizingFirstPoint).Y; } else if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.RightBottom) { isReverseWidth = ((Point)rlp).X < ((Point)resizingFirstPoint).X; isReverseHeight = ((Point)rlp).Y < ((Point)resizingFirstPoint).Y; } else if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.Top) { isReverseWidth = false; isReverseHeight = ((Point)rlp).Y > ((Point)resizingFirstPoint).Y; } else if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.Bottom) { isReverseWidth = false; isReverseHeight = ((Point)rlp).Y < ((Point)resizingFirstPoint).Y; } else if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.Left) { isReverseWidth = ((Point)rlp).X > ((Point)resizingFirstPoint).X; isReverseHeight = false; } else if (lockedStrokeSelectionBorderHandleType == StrokeSelectionBorderHandlesEnum.Right) { isReverseWidth = ((Point)rlp).X < ((Point)resizingFirstPoint).X; isReverseHeight = false; } // caculate height width left top var l = Math.Round( ((Rect)originalSelectionBounds).Left + ((int)lockedStrokeSelectionBorderHandleType == 0 || (int)lockedStrokeSelectionBorderHandleType == 2 || (int)lockedStrokeSelectionBorderHandleType == 4 ? new Rect(((Point)resizingFirstPoint), ((Point)rlp)).Width * (isReverseWidth ? 1 : -1) : 0), 0); var t = Math.Round( ((Rect)originalSelectionBounds).Top + ((int)lockedStrokeSelectionBorderHandleType == 0 || (int)lockedStrokeSelectionBorderHandleType == 1 || (int)lockedStrokeSelectionBorderHandleType == 6 ? new Rect(((Point)resizingFirstPoint), ((Point)rlp)).Height * (isReverseHeight ? 1 : -1) : 0), 0); var w = (int)lockedStrokeSelectionBorderHandleType != 6 && (int)lockedStrokeSelectionBorderHandleType != 7 ? Math.Round(((Rect)originalSelectionBounds).Width + new Rect(((Point)resizingFirstPoint), ((Point)rlp)).Width * (isReverseWidth ? -1 : 1), 0) : ((Rect)originalSelectionBounds).Width; var h = (int)lockedStrokeSelectionBorderHandleType != 4 && (int)lockedStrokeSelectionBorderHandleType != 5 ? Math.Round(((Rect)originalSelectionBounds).Height + new Rect(((Point)resizingFirstPoint), ((Point)rlp)).Height * (isReverseHeight ? -1 : 1), 0) : ((Rect)originalSelectionBounds).Height; var final_w = Math.Round(w,0); var final_h = Math.Round(h,0); if (isShiftKeyDown) { var scaleW = w / ((Rect)originalSelectionBounds).Width; var scaleH = h / ((Rect)originalSelectionBounds).Height; final_w = Math.Round(((Rect)originalSelectionBounds).Width * Math.Max(scaleW, scaleH),0); final_h = Math.Round(((Rect)originalSelectionBounds).Height * Math.Max(scaleW, scaleH),0); } if (final_w >= 1 && final_h >= 1) { // TODO 此處還需要修改行為,讓其能夠縮放到1x1; StrokeSelectionBorder.Width = final_w >=1 ? final_w : 1; StrokeSelectionBorder.Height = final_h >=1 ? final_h : 1; System.Windows.Controls.Canvas.SetLeft(StrokeSelectionBorder, l); System.Windows.Controls.Canvas.SetTop(StrokeSelectionBorder, t); resizingLastPoint = rlp; if (!(final_w < 96 && final_h < 64)) StrokeSelectionSizeToast.Visibility = Visibility.Visible; else StrokeSelectionSizeToast.Visibility = Visibility.Collapsed; // resize toast text ((TextBlock)StrokeSelectionSizeToastInner.Children[0]).Text = final_w.ToString(); ((TextBlock)StrokeSelectionSizeToastInner.Children[2]).Text = final_h.ToString(); // record resized bounds var transform = StrokeSelectionBorder.TransformToVisual(Main_Grid); var ori_lt = transform.Transform(new Point(0, 0)); var ori_rb = transform.Transform(new Point(StrokeSelectionBorder.Width, StrokeSelectionBorder.Height)); resizedSelectionBounds = new Rect(ori_lt, ori_rb); // preview resize SelectedStrokesResize(inkCanvas.GetSelectedStrokes(), (StrokeSelectionBorderHandlesEnum)lockedStrokeSelectionBorderHandleType, (Rect)originalSelectionBounds, (Rect)resizedSelectionBounds, true); } } private Point? movingFirstPoint = null; private Point? movingLastPoint = null; private double movingFirstBorderLeft = 0; private double movingFirstBorderTop = 0; private bool isLockedStrokeSelectionMove = false; private bool isProgramChangeStrokesSelection = false; private void StrokeSelectionBorder_MouseUp(object sender, MouseButtonEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (isLockedStrokeSelectionHandle || isLockedStrokeSelectionRotate || !isLockedStrokeSelectionMove) return; // release capture var bd = (Border)sender; bd.ReleaseMouseCapture(); // record last move point var pt = e.GetPosition(Main_Grid); movingLastPoint = pt; // caculate offset var offX = ((Point)movingLastPoint).X - ((Point)movingFirstPoint).X; var offY = ((Point)movingLastPoint).Y - ((Point)movingFirstPoint).Y; // unlock isLockedStrokeSelectionMove = false; if (isStrokeSelectionCloneOn) { // transform strokes var matrix = new Matrix(); matrix.Translate(((Point)movingLastPoint).X-((Point)movingFirstPoint).X, ((Point)movingLastPoint).Y - ((Point)movingFirstPoint).Y); isProgramChangeStrokesSelection = true; var ori = inkCanvas.GetSelectedStrokes(); inkCanvas.Select(new StrokeCollection()); clonedStrokes = ori.Clone(); inkCanvas.Select(ori); isProgramChangeStrokesSelection = false; clonedStrokes.Transform(matrix,false); // add to inkcanvas inkCanvas.Strokes.Add(clonedStrokes); InkSelectionStrokesOverlay.DrawStrokes(inkCanvas.GetSelectedStrokes(), new Matrix()); InkSelectionStrokesBackgroundInkCanvas.Strokes.Add(clonedStrokes); } else { if (isStrokesSelectionBorderMovingUseRenderTransform) { StrokeSelectionBorderTranslateTransform.X = 0; StrokeSelectionBorderTranslateTransform.Y = 0; System.Windows.Controls.Canvas.SetLeft(StrokeSelectionBorder, movingFirstBorderLeft + offX); System.Windows.Controls.Canvas.SetTop(StrokeSelectionBorder, movingFirstBorderTop + offY); } // preview SelectedStrokesMove(inkCanvas.GetSelectedStrokes(), (Point)movingFirstPoint, (Point)movingLastPoint, true); // transform strokes SelectedStrokesMove(inkCanvas.GetSelectedStrokes(), (Point)movingFirstPoint, (Point)movingLastPoint); // display selectionToolBar var _bd = new Binding("Visibility"); _bd.Source = GridInkCanvasSelectionCover; BorderStrokeSelectionControl.SetBinding(Border.VisibilityProperty, _bd); updateBorderStrokeSelectionControlLocation(); // hide move toast StrokeSelectionMoveToast.Visibility = Visibility.Collapsed; } } private void StrokeSelectionBorder_MouseDown(object sender, MouseButtonEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (isLockedStrokeSelectionHandle || isLockedStrokeSelectionRotate || isLockedStrokeSelectionMove) return; // record first move point var pt = e.GetPosition(Main_Grid); movingFirstPoint = pt; // capture mouse var bd = (Border)sender; bd.CaptureMouse(); // lock isLockedStrokeSelectionMove = true; // record first border position movingFirstBorderLeft = System.Windows.Controls.Canvas.GetLeft(StrokeSelectionBorder); movingFirstBorderTop = System.Windows.Controls.Canvas.GetTop(StrokeSelectionBorder); } private bool isStrokesSelectionBorderMovingUseRenderTransform = true; private StrokeCollection clonedStrokes; private void StrokeSelectionBorder_MouseMove(object sender, MouseEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (isLockedStrokeSelectionHandle || isLockedStrokeSelectionRotate || !isLockedStrokeSelectionMove) return; // record last move point var pt = e.GetPosition(Main_Grid); movingLastPoint = pt; // caculate offset var offX = ((Point)movingLastPoint).X - ((Point)movingFirstPoint).X; var offY = ((Point)movingLastPoint).Y - ((Point)movingFirstPoint).Y; if (isStrokeSelectionCloneOn) { var matrix = new Matrix(); matrix.Translate(((Point)movingLastPoint).X-((Point)movingFirstPoint).X, ((Point)movingLastPoint).Y - ((Point)movingFirstPoint).Y); InkSelectionStrokesOverlay.Open(); InkSelectionStrokesOverlay.DrawStrokes(inkCanvas.GetSelectedStrokes(), new Matrix(), false); InkSelectionStrokesOverlay.DrawStrokes(inkCanvas.GetSelectedStrokes(), matrix, false); InkSelectionStrokesOverlay.Close(); } else { // preview SelectedStrokesMove(inkCanvas.GetSelectedStrokes(), (Point)movingFirstPoint, (Point)movingLastPoint, true); // relocate border position if (isStrokesSelectionBorderMovingUseRenderTransform) { StrokeSelectionBorderTranslateTransform.X = offX; StrokeSelectionBorderTranslateTransform.Y = offY; } else { System.Windows.Controls.Canvas.SetLeft(StrokeSelectionBorder, movingFirstBorderLeft + offX); System.Windows.Controls.Canvas.SetTop(StrokeSelectionBorder, movingFirstBorderTop + offY); } // display move toast if (StrokeSelectionMoveToast.Visibility == Visibility.Collapsed) StrokeSelectionMoveToast.Visibility = Visibility.Visible; // move toast text ((TextBlock)((StackPanel)StrokeSelectionMoveToastInner.Children[0]).Children[0]).Text = $"X: {Math.Round(System.Windows.Controls.Canvas.GetLeft(StrokeSelectionBorder), 2)}"; ((TextBlock)((StackPanel)StrokeSelectionMoveToastInner.Children[0]).Children[1]).Text = $"Y: {Math.Round(System.Windows.Controls.Canvas.GetTop(StrokeSelectionBorder),2)}"; ((TextBlock)((StackPanel)StrokeSelectionMoveToastInner.Children[1]).Children[1]).Text = $"Δx: {Math.Round(offX,2)}"; ((TextBlock)((StackPanel)StrokeSelectionMoveToastInner.Children[1]).Children[2]).Text = $"Δy: {Math.Round(offY,2)}"; // hide selectionToolBar if (BorderStrokeSelectionControl.Visibility != Visibility.Collapsed) BorderStrokeSelectionControl.Visibility = Visibility.Collapsed; } } private Point? StrokesRotateSelectionBoundsCenterPoint = null; private Point? rotatingLastPoint = null; private bool isLockedStrokeSelectionRotate = false; private void StrokeSelectionRotateHandle_MouseUp(object sender, MouseButtonEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (isLockedStrokeSelectionHandle || isLockedStrokeSelectionMove || !isLockedStrokeSelectionRotate) return; // unlock isLockedStrokeSelectionRotate = false; // record last point var pt = e.GetPosition(Main_Grid); rotatingLastPoint = pt; // caculate rotate angle var vec1 = new double[] { ((Point)rotatingLastPoint).X - ((Point)StrokesRotateSelectionBoundsCenterPoint).X , ((Point)rotatingLastPoint).Y - ((Point)StrokesRotateSelectionBoundsCenterPoint).Y }; var vec_base = new double[] { 0, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y }; var cosine = (vec_base[0] * vec1[0] + vec_base[1] * vec1[1]) / (Math.Sqrt(Math.Pow(vec_base[0], 2) + Math.Pow(vec_base[1], 2)) * Math.Sqrt(Math.Pow(vec1[0], 2) + Math.Pow(vec1[1], 2))); var angle = Math.Acos(cosine); // 判斷在第幾象限 var isIn2And3Quadrant = ((Point)rotatingLastPoint).X <= ((Point)StrokesRotateSelectionBoundsCenterPoint).X; // final angle var rotateAngle = Math.Round(180 + 180 * (angle / Math.PI) * (isIn2And3Quadrant ? 1 : -1), 0); // release capture var bd = (Border)sender; bd.ReleaseMouseCapture(); // hide rotate toast StrokeSelectionRotateToast.Visibility = Visibility.Collapsed; // hide guideline overlay InkSelectionRotateGuidelineOverlay.Visibility = Visibility.Collapsed; // border rotate reset StrokeSelectionBorderRotateTransform.Angle = 0; StrokeSelectionBorderRotateTransform.CenterX = 0; StrokeSelectionBorderRotateTransform.CenterY = 0; // rotate stroke SelectedStrokesRotate(inkCanvas.GetSelectedStrokes(), rotateAngle, (Point)StrokesRotateSelectionBoundsCenterPoint); // border re-caculate strokes bounds UpdateStrokeSelectionBorder(true, inkCanvas.GetSelectionBounds()); // display selectionToolBar var _bd = new Binding("Visibility"); _bd.Source = GridInkCanvasSelectionCover; BorderStrokeSelectionControl.SetBinding(Border.VisibilityProperty, _bd); updateBorderStrokeSelectionControlLocation(); } private void StrokeSelectionRotateHandle_MouseDown(object sender, MouseButtonEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (isLockedStrokeSelectionHandle || isLockedStrokeSelectionMove || isLockedStrokeSelectionRotate) return; // lock isLockedStrokeSelectionRotate = true; // record center point var bounds = inkCanvas.GetSelectionBounds(); StrokesRotateSelectionBoundsCenterPoint = new Point(bounds.Left + bounds.Width / 2, bounds.Top + bounds.Height / 2); // capture mouse var bd = (Border)sender; bd.CaptureMouse(); } private void StrokeSelectionRotateHandle_MouseMove(object sender, MouseEventArgs e) { if (isStrokeSelectionBorderLocked) return; if (isLockedStrokeSelectionHandle || isLockedStrokeSelectionMove || !isLockedStrokeSelectionRotate) return; // record last point var pt = e.GetPosition(Main_Grid); rotatingLastPoint = pt; // caculate rotate angle var vec1 = new double[] { ((Point)rotatingLastPoint).X - ((Point)StrokesRotateSelectionBoundsCenterPoint).X , ((Point)rotatingLastPoint).Y - ((Point)StrokesRotateSelectionBoundsCenterPoint).Y }; var vec_base = new double[] { 0, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y }; var cosine = (vec_base[0] * vec1[0] + vec_base[1] * vec1[1]) / (Math.Sqrt(Math.Pow(vec_base[0],2) + Math.Pow(vec_base[1],2)) * Math.Sqrt(Math.Pow(vec1[0],2) + Math.Pow(vec1[1],2))); var angle = Math.Acos(cosine); // 判斷在第幾象限 var isIn2And3Quadrant = ((Point)rotatingLastPoint).X <= ((Point)StrokesRotateSelectionBoundsCenterPoint).X; // final angle var rotateAngle = Math.Round(180 + 180 * (angle / Math.PI) * (isIn2And3Quadrant ? 1 : -1), 0); // display guideline overlay if (InkSelectionRotateGuidelineOverlay.Visibility == Visibility.Collapsed) InkSelectionRotateGuidelineOverlay.Visibility = Visibility.Visible; var guidelinePen1 = new Pen(); // guideline1 thickness1 dash yellow opacity255 guidelinePen1.Brush = new SolidColorBrush(Color.FromRgb(245, 158, 11)); guidelinePen1.DashStyle = DashStyles.Dash; guidelinePen1.Thickness = 1; var guidelinePen2 = new Pen(); // guideline2 thickness2 dash yellow opacity255 guidelinePen2.Brush = new SolidColorBrush(Color.FromRgb(245, 158, 11)); guidelinePen2.DashStyle = DashStyles.Dash; guidelinePen2.Thickness = 2.5; var guidelinePen3 = new Pen(); // guideline3 thickness1 dash yellow opacity72 guidelinePen3.Brush = new SolidColorBrush(Color.FromArgb(72, 245, 158, 11)); guidelinePen3.DashStyle = DashStyles.Dash; guidelinePen3.Thickness = 1; var guidelinePen4 = new Pen(); // guideline4 thickness1 dot green opacity255 guidelinePen4.Brush = new SolidColorBrush(Color.FromRgb(34, 197, 94)); guidelinePen4.DashStyle = DashStyles.Dot; guidelinePen4.Thickness = 1; var guidelineSnapAngle = 0; // draw guideline var dv = InkSelectionRotateGuidelineOverlay.DrawingVisual; using (DrawingContext dc = dv.RenderOpen()) { dc.DrawLine(guidelinePen1, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X,0)); dc.DrawLine(guidelinePen3, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X,Main_Grid.ActualHeight)); dc.DrawLine(guidelinePen2, (Point)StrokesRotateSelectionBoundsCenterPoint, (Point)rotatingLastPoint); var QuadrantOneLongest = Math.Max( Main_Grid.ActualWidth - ((Point)StrokesRotateSelectionBoundsCenterPoint).X, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y); //30 angle if (rotateAngle >= 26 && rotateAngle <= 34) { dc.DrawLine(guidelinePen4, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X + QuadrantOneLongest, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y - QuadrantOneLongest * Math.Sqrt(3))); guidelineSnapAngle = 30; } //45 angle if (rotateAngle >= 41 && rotateAngle <= 49) { dc.DrawLine(guidelinePen4, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X+QuadrantOneLongest, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y - QuadrantOneLongest)); guidelineSnapAngle = 45; } //60 angle if (rotateAngle >= 56 && rotateAngle <= 64) { dc.DrawLine(guidelinePen4, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X + QuadrantOneLongest *Math.Sqrt(3), ((Point)StrokesRotateSelectionBoundsCenterPoint).Y - QuadrantOneLongest)); guidelineSnapAngle = 60; } //90 angle if (rotateAngle >= 86 && rotateAngle <= 94) { dc.DrawLine(guidelinePen4, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X + QuadrantOneLongest, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y)); guidelineSnapAngle = 90; } var QuadrantFourLongest = Math.Max( Main_Grid.ActualWidth - ((Point)StrokesRotateSelectionBoundsCenterPoint).X, Main_Grid.ActualHeight - ((Point)StrokesRotateSelectionBoundsCenterPoint).Y); //120 angle if (rotateAngle >= 116 && rotateAngle <= 124) { dc.DrawLine(guidelinePen4, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X + QuadrantFourLongest * Math.Sqrt(3), ((Point)StrokesRotateSelectionBoundsCenterPoint).Y + QuadrantFourLongest)); guidelineSnapAngle = 120; } //150 angle if (rotateAngle >= 146 && rotateAngle <= 154) { dc.DrawLine(guidelinePen4, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X + QuadrantFourLongest, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y + QuadrantFourLongest * Math.Sqrt(3))); guidelineSnapAngle = 150; } //180 angle if (rotateAngle >= 176 && rotateAngle <= 184) { dc.DrawLine(guidelinePen4, (Point)StrokesRotateSelectionBoundsCenterPoint, new Point(((Point)StrokesRotateSelectionBoundsCenterPoint).X, ((Point)StrokesRotateSelectionBoundsCenterPoint).Y + QuadrantFourLongest)); guidelineSnapAngle = 180; } } // preview SelectedStrokesRotate(inkCanvas.GetSelectedStrokes(), guidelineSnapAngle!=0?guidelineSnapAngle:rotateAngle, (Point)StrokesRotateSelectionBoundsCenterPoint, true); // border rotate StrokeSelectionBorderRotateTransform.Angle = guidelineSnapAngle != 0 ? guidelineSnapAngle : rotateAngle; StrokeSelectionBorderRotateTransform.CenterX = StrokeSelectionBorder.Width / 2; StrokeSelectionBorderRotateTransform.CenterY = StrokeSelectionBorder.Height / 2; // display rotate toast if (StrokeSelectionRotateToast.Visibility == Visibility.Collapsed) StrokeSelectionRotateToast.Visibility = Visibility.Visible; // update rotate toast ((TextBlock)StrokeSelectionRotateToast.Child).Text = (guidelineSnapAngle != 0 ? guidelineSnapAngle : rotateAngle) == 360 ? "360° = 0°" : $"{(guidelineSnapAngle != 0 ? guidelineSnapAngle : rotateAngle)}°"; // hide selectionToolBar if (BorderStrokeSelectionControl.Visibility != Visibility.Collapsed) BorderStrokeSelectionControl.Visibility = Visibility.Collapsed; } private void StrokeSelectionBorder_MouseEnter(object sender, MouseEventArgs e) { if (isStrokeSelectionBorderLocked) { StrokeSelectionCursorArea.Background = new SolidColorBrush(Color.FromArgb(5, 96, 165, 250)); return; }; StrokeSelectionCursorArea.Background = new SolidColorBrush(Color.FromArgb(17, 96, 165, 250)); } private void StrokeSelectionBorder_MouseLeave(object sender, MouseEventArgs e) { if (isStrokeSelectionBorderLocked) { StrokeSelectionCursorArea.Background = new SolidColorBrush(Color.FromArgb(5, 96, 165, 250)); return; }; StrokeSelectionCursorArea.Background = new SolidColorBrush(Color.FromArgb(5, 96, 165, 250)); } private void ApplyCursorToStrokeSelectionBorder() { StrokeSelectionCursorArea.ForceCursor = true; StreamResourceInfo sri_move = Application.GetResourceStream( new Uri("Resources/Cursors/cursor-move.cur", UriKind.Relative)); StrokeSelectionCursorArea.Cursor = new Cursor(sri_move.Stream); StreamResourceInfo sri_lr = Application.GetResourceStream( new Uri("Resources/Cursors/cursor-resize-lr.cur", UriKind.Relative)); StreamResourceInfo sri_tb = Application.GetResourceStream( new Uri("Resources/Cursors/cursor-resize-tb.cur", UriKind.Relative)); StreamResourceInfo sri_lt_rb = Application.GetResourceStream( new Uri("Resources/Cursors/cursor-resize-lt-rb.cur", UriKind.Relative)); StreamResourceInfo sri_rt_lb = Application.GetResourceStream( new Uri("Resources/Cursors/cursor-resize-rt-lb.cur", UriKind.Relative)); foreach (var bd in StrokeSelectionBorderHandles) { bd.ForceCursor = true; } StrokeSelectionBorderHandles[0].Cursor = StrokeSelectionBorderHandles[3].Cursor = new Cursor(sri_lt_rb.Stream); StrokeSelectionBorderHandles[1].Cursor = StrokeSelectionBorderHandles[2].Cursor = new Cursor(sri_rt_lb.Stream); StrokeSelectionBorderHandles[4].Cursor = StrokeSelectionBorderHandles[5].Cursor = new Cursor(sri_lr.Stream); StrokeSelectionBorderHandles[6].Cursor = StrokeSelectionBorderHandles[7].Cursor = new Cursor(sri_tb.Stream); StreamResourceInfo sri_open_hand = Application.GetResourceStream( new Uri("Resources/Cursors/open-hand-cursor.cur", UriKind.Relative)); StrokeSelectionBorderHandles[8].Cursor = new Cursor(sri_open_hand.Stream); } public enum StrokeSelectionBorderHandlesEnum { LeftTop, RightTop, LeftBottom, RightBottom, Left, Right, Top, Bottom, Rotate } private void AddStrokeSelectionHandlesToArr() { StrokeSelectionBorderHandles = new Border[] { StrokeSelectionLTHandle, StrokeSelectionRTHandle, StrokeSelectionLBHandle, StrokeSelectionRBHandle, StrokeSelectionLHandle, StrokeSelectionRHandle, StrokeSelectionTHandle, StrokeSelectionBHandle, StrokeSelectionRotateHandle }; foreach (var hd in StrokeSelectionBorderHandles) { if (hd.Name== "StrokeSelectionRotateHandle") continue; hd.MouseUp += StrokeSelectionBorderHandle_MouseUp; hd.MouseDown += StrokeSelectionBorderHandle_MouseDown; hd.MouseMove += StrokeSelectionBorderHandle_MouseMove; } StrokeSelectionRotateHandle.MouseUp += StrokeSelectionRotateHandle_MouseUp; StrokeSelectionRotateHandle.MouseDown += StrokeSelectionRotateHandle_MouseDown; StrokeSelectionRotateHandle.MouseMove += StrokeSelectionRotateHandle_MouseMove; ApplyCursorToStrokeSelectionBorder(); } private void SelectedStrokesRotate(StrokeCollection strokes, double angle, Point? centerPoint, bool isPreview = false) { if (centerPoint == null) return; var matrix = new Matrix(); matrix.RotateAt(angle, ((Point)centerPoint).X, ((Point)centerPoint).Y); if (isPreview) { InkSelectionStrokesOverlay.DrawStrokes(strokes, matrix); } else { strokes.Transform(matrix, Settings.Canvas.ApplyScaleToStylusTip); } } private void SelectedStrokesMove(StrokeCollection strokes, Point firstPoint, Point lastPoint, bool isPreview = false) { var matrix = new Matrix(); matrix.Translate(lastPoint.X-firstPoint.X, lastPoint.Y - firstPoint.Y); if (isPreview) { InkSelectionStrokesOverlay.DrawStrokes(strokes, matrix); } else { strokes.Transform(matrix, Settings.Canvas.ApplyScaleToStylusTip); } } /// /// 調整選中墨跡的大小 /// /// /// /// /// /// /// private Rect? SelectedStrokesResize(StrokeCollection strokes, StrokeSelectionBorderHandlesEnum handleType, Rect originalRect, Rect resizedRect, bool isPreview = false) { if ((strokes?.Count ?? 0) == 0 || handleType == null || originalRect.Width == 0 || originalRect.Height == 0 || resizedRect.Width == 0 || resizedRect.Height == 0) return null; Matrix matrix = new Matrix(); if ((int)handleType <= 3) { var asidePt = (int)handleType == 0 ? originalRect.BottomRight : (int)handleType == 1 ? originalRect.BottomLeft : (int)handleType == 2 ? originalRect.TopRight : originalRect.TopLeft; matrix.ScaleAt(Math.Round(resizedRect.Width / originalRect.Width, 2), Math.Round(resizedRect.Height / originalRect.Height, 2), asidePt.X, asidePt.Y); } else if ((int)handleType == 4 || (int)handleType == 5) { var asideX = (int)handleType == 5 ? originalRect.Left : originalRect.Right; matrix.ScaleAt(Math.Round(resizedRect.Width / originalRect.Width, 2),1D,asideX, 0); } else if ((int)handleType == 6 || (int)handleType == 7) { var asideY = (int)handleType == 7 ? originalRect.Top : originalRect.Bottom; matrix.ScaleAt(1D, Math.Round(resizedRect.Height / originalRect.Height, 2), 0, asideY); } if (isPreview) { InkSelectionStrokesOverlay.DrawStrokes(strokes, matrix); } else { strokes.Transform(matrix, Settings.Canvas.ApplyScaleToStylusTip); } return strokes.GetBounds(); } private void UpdateStrokeSelectionBorder(bool isDisplay, Rect? rect) { if (isDisplay) { if (rect == null) return; var r = (Rect)rect; if (r.Height == 0 || r.Width == 0) return; InkCanvasSelectFakeAdornerCanvas.Visibility = Visibility.Visible; System.Windows.Controls.Canvas.SetLeft(StrokeSelectionBorder,r.Left); System.Windows.Controls.Canvas.SetTop(StrokeSelectionBorder,r.Top); StrokeSelectionBorder.Width = r.Width; StrokeSelectionBorder.Height = r.Height; if (StrokeSelectionBorderHandles.Length == 0) AddStrokeSelectionHandlesToArr(); StrokeSelectionBorder.Visibility = Visibility.Visible; } else { InkCanvasSelectFakeAdornerCanvas.Visibility = Visibility.Collapsed; StrokeSelectionBorder.Visibility = Visibility.Collapsed; } } /// /// 取消选中本次选中的墨迹或对象,准备继续进行矩形选框操作。 /// private void CancelCurrentStrokesSelection() { GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; inkCanvas.Select(new StrokeCollection()); UpdateStrokeSelectionBorder(false, null); RectangleSelectionHitTestBorder.Visibility = Visibility.Visible; var cm = this.FindResource("StrokesSelectionMoreMenuButtonContextMenu") as ContextMenu; cm.IsOpen = false; } #endregion private void inkCanvas_SelectionChanged(object sender, EventArgs e) { if (isProgramChangeStrokesSelection) return; isStrokeSelectionCloneOn = false; if (inkCanvas.GetSelectedStrokes().Count == 0) { GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; UpdateStrokeSelectionBorder(false,null); inkCanvas.Opacity = 1; InkSelectionStrokesOverlay.Visibility = Visibility.Collapsed; InkSelectionStrokesBackgroundInkCanvas.Visibility = Visibility.Collapsed; InkSelectionStrokesOverlay.DrawStrokes(new StrokeCollection(), new Matrix()); } else { var isAllStrokesLocked = false; if (inkCanvas.GetSelectedStrokes().Count > 1) { List lockedStrokes = new List(); var stks = inkCanvas.GetSelectedStrokes(); foreach (var stk in stks) { if (stk.ContainsPropertyData(IsLockGuid)) lockedStrokes.Add(stk); } if (lockedStrokes.Count != stks.Count && lockedStrokes.Count!=0) { stks.Remove(new StrokeCollection(lockedStrokes)); inkCanvas.Select(stks); return; } else if (lockedStrokes.Count == stks.Count) { isAllStrokesLocked = true; } } else { isAllStrokesLocked = inkCanvas.GetSelectedStrokes().Single().ContainsPropertyData(IsLockGuid); } GridInkCanvasSelectionCover.Visibility = Visibility.Visible; BorderStrokeSelectionClone.Background = Brushes.Transparent; RectangleSelectionHitTestBorder.Visibility = Visibility.Collapsed; UpdateStrokeSelectionBorder(true, inkCanvas.GetSelectionBounds()); inkCanvas.Opacity = 0; InkSelectionStrokesOverlay.Visibility = Visibility.Visible; InkSelectionStrokesBackgroundInkCanvas.Visibility = Visibility.Visible; InkSelectionStrokesBackgroundInkCanvas.Strokes.Clear(); InkSelectionStrokesBackgroundInkCanvas.Strokes.Add(inkCanvas.Strokes); InkSelectionStrokesBackgroundInkCanvas.Strokes.Remove(inkCanvas.GetSelectedStrokes()); InkSelectionStrokesOverlay.DrawStrokes(inkCanvas.GetSelectedStrokes(), new Matrix()); UpdateStrokesSelectionCloneToolButtonLimitStatus(); updateBorderStrokeSelectionControlLocation(); UpdateSelectionToolbarOtherIconLockedStatus(isAllStrokesLocked); UpdateSelectionToolBarLockIcon(isAllStrokesLocked); UpdateSelectionBorderHandlesLockStatus(isAllStrokesLocked); } } private void updateBorderStrokeSelectionControlLocation() { if (currentMode == 0) BorderStrokeSelectionCloneToNewBoardTextBlock.Text = "克隆到白板"; else BorderStrokeSelectionCloneToNewBoardTextBlock.Text = "克隆到新页"; var _w = 660; BorderStrokeSelectionControl.Width = _w; var borderLeft = (inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Right - _w) / 2; var borderTop = inkCanvas.GetSelectionBounds().Bottom + 24; if (borderLeft < 0) borderLeft = 0; if (borderTop < 0) borderTop = 0; if (Width - borderLeft < _w || double.IsNaN(borderLeft)) borderLeft = Width - _w; if (Height - borderTop < 48 || double.IsNaN(borderTop)) borderTop = Height - 48; //if (borderTop > 60) borderTop -= 60; BorderStrokeSelectionControl.Margin = new Thickness(borderLeft, borderTop, 0, 0); } #region Strokes Lock public Guid IsLockGuid = new Guid("b701bb3f-16bf-43ce-a88f-30eab85cd77b"); private void UpdateSelectionToolBarLockIcon(bool isLocked) { BorderStrokeSelectionLock_LockClose.Visibility = !isLocked ? Visibility.Visible : Visibility.Collapsed; BorderStrokeSelectionLock_LockOpen.Visibility = isLocked ? Visibility.Visible : Visibility.Collapsed; } private void UpdateSelectionToolbarOtherIconLockedStatus(bool isLocked) { var toolbtns = new Border[] { BorderStrokeSelectionClone, BorderStrokeSelectionCloneToNewBoard, BorderImageRotate45, BorderImageRotate90, BorderImageFlipHorizontal, BorderImageFlipVertical, BorderStrokePaletteButton, BorderStrokeSelectionDelete, BorderImageRotateClockwise }; foreach (var tb in toolbtns) { tb.IsHitTestVisible = !isLocked; tb.Opacity = isLocked ? 0.5 : 1; tb.IsEnabled = !isLocked; } } private bool isStrokeSelectionBorderLocked = false; private void UpdateSelectionBorderHandlesLockStatus(bool isLocked) { var _v = !isLocked ? Visibility.Visible : Visibility.Collapsed; foreach (var hd in StrokeSelectionBorderHandles) hd.Visibility = _v; StrokeSelectionRotateHandleConnectLine.Visibility = _v; isStrokeSelectionBorderLocked = isLocked; if (isLocked) { StrokeSelectionCursorArea.ForceCursor = false; StrokeSelectionCursorArea.Cursor = Cursors.Arrow; } else { StrokeSelectionCursorArea.ForceCursor = true; StreamResourceInfo sri_move = Application.GetResourceStream( new Uri("Resources/Cursors/cursor-move.cur", UriKind.Relative)); StrokeSelectionCursorArea.Cursor = new Cursor(sri_move.Stream); } } private void BorderStrokeSelectionLock_MouseUp(object sender, MouseButtonEventArgs e) { if (BorderStrokeSelectionToolButtonMouseDown != (Border)sender) return; var isAllStrokesLocked = BorderStrokeSelectionLock_LockOpen.Visibility == Visibility.Visible; var stks = inkCanvas.GetSelectedStrokes(); if (isAllStrokesLocked) { foreach (var stk in stks) { stk.RemovePropertyData(IsLockGuid); } } else { foreach (var stk in stks) { stk.AddPropertyData(IsLockGuid, "Locked"); } } UpdateSelectionToolBarLockIcon(!isAllStrokesLocked); UpdateSelectionToolbarOtherIconLockedStatus(!isAllStrokesLocked); UpdateSelectionBorderHandlesLockStatus(!isAllStrokesLocked); // toolbutton BorderStrokeSelectionToolButton_MouseLeave(sender, e); } #endregion private void GridInkCanvasSelectionCover_ManipulationStarting(object sender, ManipulationStartingEventArgs e) { e.Mode = ManipulationModes.All; } private void GridInkCanvasSelectionCover_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { if (StrokeManipulationHistory?.Count > 0) { timeMachine.CommitStrokeManipulationHistory(StrokeManipulationHistory); foreach (var item in StrokeManipulationHistory) { StrokeInitialHistory[item.Key] = item.Value.Item2; } StrokeManipulationHistory = null; } if (DrawingAttributesHistory.Count > 0) { timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory); DrawingAttributesHistory = new Dictionary>(); foreach (var item in DrawingAttributesHistoryFlag) { item.Value.Clear(); } } } private void GridInkCanvasSelectionCover_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { try { if (dec.Count >= 1) { var md = e.DeltaManipulation; var trans = md.Translation; // 获得位移矢量 var rotate = md.Rotation; // 获得旋转角度 var scale = md.Scale; // 获得缩放倍数 var m = new Matrix(); // Find center of element and then transform to get current location of center var fe = e.Source as FrameworkElement; var center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2); center = new Point(inkCanvas.GetSelectionBounds().Left + inkCanvas.GetSelectionBounds().Width / 2, inkCanvas.GetSelectionBounds().Top + inkCanvas.GetSelectionBounds().Height / 2); center = m.Transform(center); // 转换为矩阵缩放和旋转的中心点 // Update matrix to reflect translation/rotation m.Translate(trans.X, trans.Y); // 移动 m.ScaleAt(scale.X, scale.Y, center.X, center.Y); // 缩放 var strokes = inkCanvas.GetSelectedStrokes(); if (StrokesSelectionClone.Count != 0) strokes = StrokesSelectionClone; else if (Settings.Gesture.IsEnableTwoFingerRotationOnSelection) m.RotateAt(rotate, center.X, center.Y); // 旋转 foreach (var stroke in strokes) { stroke.Transform(m, false); try { stroke.DrawingAttributes.Width *= md.Scale.X; stroke.DrawingAttributes.Height *= md.Scale.Y; } catch { } } updateBorderStrokeSelectionControlLocation(); } } catch { } } #region SelectionV2 Popup private void SelectionV2Init() { FloatingToolBarV2.SelectionV2_SelectAllEvent += (sender, args) => { inkCanvas.Select(inkCanvas.Strokes); FloatingToolBarV2.SelectionPopupV2.IsOpen = false; }; FloatingToolBarV2.SelectionV2_UnSelectEvent += (sender, args) => { CancelCurrentStrokesSelection(); FloatingToolBarV2.SelectionPopupV2.IsOpen = false; }; FloatingToolBarV2.SelectionV2_ReverseSelectEvent += (sender, args) => { var strokes = new StrokeCollection(inkCanvas.Strokes.Where(stroke => !inkCanvas.GetSelectedStrokes().Contains(stroke)).Where(stroke=>!stroke.ContainsPropertyData(IsLockGuid))); if (strokes.Any()) { inkCanvas.Select(strokes); } else { CancelCurrentStrokesSelection(); } FloatingToolBarV2.SelectionPopupV2.IsOpen = false; }; FloatingToolBarV2.SelectionV2.ApplyScaleToStylusTip = Settings.Canvas.ApplyScaleToStylusTip; FloatingToolBarV2.SelectionV2.OnlyHitTestFullyContainedStrokes = Settings.Canvas.OnlyHitTestFullyContainedStrokes; FloatingToolBarV2.SelectionV2.AllowClickToSelectLockedStroke = Settings.Canvas.AllowClickToSelectLockedStroke; FloatingToolBarV2.SelectionV2.SelectionModeSelected = (SelectionPopup.SelectionMode)Settings.Canvas.SelectionMethod; FloatingToolBarV2.SelectionV2_ApplyScaleToStylusTipChanged += (sender, args) => { if (!isLoaded) return; Settings.Canvas.ApplyScaleToStylusTip = FloatingToolBarV2.SelectionV2.ApplyScaleToStylusTip; ToggleSwitchApplyScaleToStylusTip.IsOn = FloatingToolBarV2.SelectionV2.ApplyScaleToStylusTip; SaveSettingsToFile(); }; FloatingToolBarV2.SelectionV2_OnlyHitTestFullyContainedStrokesChanged += (sender, args) => { if (!isLoaded) return; Settings.Canvas.OnlyHitTestFullyContainedStrokes = FloatingToolBarV2.SelectionV2.OnlyHitTestFullyContainedStrokes; ToggleSwitchOnlyHitTestFullyContainedStrokes.IsOn = FloatingToolBarV2.SelectionV2.OnlyHitTestFullyContainedStrokes; SaveSettingsToFile(); }; FloatingToolBarV2.SelectionV2_AllowClickToSelectLockedStrokeChanged += (sender, args) => { if (!isLoaded) return; Settings.Canvas.AllowClickToSelectLockedStroke = FloatingToolBarV2.SelectionV2.AllowClickToSelectLockedStroke; ToggleSwitchAllowClickToSelectLockedStroke.IsOn = FloatingToolBarV2.SelectionV2.AllowClickToSelectLockedStroke; SaveSettingsToFile(); }; FloatingToolBarV2.SelectionV2_SelectionModeChanged += (sender, args) => { if (!isLoaded) return; Settings.Canvas.SelectionMethod = (int)args.NowMode; ComboBoxSelectionMethod.SelectedIndex = (int)args.NowMode; SaveSettingsToFile(); }; } #endregion } }