Files
community/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs
T

433 lines
18 KiB
C#
Raw Normal View History

2025-08-31 09:54:13 +08:00
using System;
2025-05-25 09:29:48 +08:00
using System.Collections.Generic;
2025-07-28 14:40:44 +08:00
using System.Diagnostics;
2025-05-25 09:29:48 +08:00
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
2025-08-31 09:54:13 +08:00
using Ink_Canvas.Helpers;
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private enum CommitReason
{
2025-05-25 09:29:48 +08:00
UserInput,
CodeInput,
ShapeDrawing,
ShapeRecognition,
ClearingCanvas,
Manipulation
}
private CommitReason _currentCommitType = CommitReason.UserInput;
private bool IsEraseByPoint => inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
private StrokeCollection ReplacedStroke;
private StrokeCollection AddedStroke;
private StrokeCollection CuboidStrokeCollection;
private Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>> StrokeManipulationHistory;
private Dictionary<Stroke, StylusPointCollection> StrokeInitialHistory =
new Dictionary<Stroke, StylusPointCollection>();
private Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> DrawingAttributesHistory =
new Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
2025-07-28 14:40:44 +08:00
private Dictionary<Guid, List<Stroke>> DrawingAttributesHistoryFlag = new Dictionary<Guid, List<Stroke>> {
2025-05-25 09:29:48 +08:00
{ DrawingAttributeIds.Color, new List<Stroke>() },
{ DrawingAttributeIds.DrawingFlags, new List<Stroke>() },
{ DrawingAttributeIds.IsHighlighter, new List<Stroke>() },
{ DrawingAttributeIds.StylusHeight, new List<Stroke>() },
{ DrawingAttributeIds.StylusTip, new List<Stroke>() },
{ DrawingAttributeIds.StylusTipTransform, new List<Stroke>() },
{ DrawingAttributeIds.StylusWidth, new List<Stroke>() }
};
private TimeMachine timeMachine = new TimeMachine();
2025-08-03 16:46:33 +08:00
private void ApplyHistoryToCanvas(TimeMachineHistory item, InkCanvas applyCanvas = null)
{
2025-05-25 09:29:48 +08:00
_currentCommitType = CommitReason.CodeInput;
var canvas = inkCanvas;
2025-08-03 16:46:33 +08:00
if (applyCanvas != null && applyCanvas is InkCanvas)
{
2025-05-25 09:29:48 +08:00
canvas = applyCanvas;
}
2025-08-03 16:46:33 +08:00
if (item.CommitType == TimeMachineHistoryType.UserInput)
{
if (!item.StrokeHasBeenCleared)
{
2025-05-25 09:29:48 +08:00
foreach (var strokes in item.CurrentStroke)
if (!canvas.Strokes.Contains(strokes))
canvas.Strokes.Add(strokes);
2025-08-03 16:46:33 +08:00
}
else
{
2025-05-25 09:29:48 +08:00
foreach (var strokes in item.CurrentStroke)
if (canvas.Strokes.Contains(strokes))
canvas.Strokes.Remove(strokes);
}
2025-08-03 16:46:33 +08:00
}
else if (item.CommitType == TimeMachineHistoryType.ShapeRecognition)
{
if (item.StrokeHasBeenCleared)
{
2025-05-25 09:29:48 +08:00
foreach (var strokes in item.CurrentStroke)
if (canvas.Strokes.Contains(strokes))
canvas.Strokes.Remove(strokes);
foreach (var strokes in item.ReplacedStroke)
if (!canvas.Strokes.Contains(strokes))
canvas.Strokes.Add(strokes);
2025-08-03 16:46:33 +08:00
}
else
{
2025-05-25 09:29:48 +08:00
foreach (var strokes in item.CurrentStroke)
if (!canvas.Strokes.Contains(strokes))
canvas.Strokes.Add(strokes);
foreach (var strokes in item.ReplacedStroke)
if (canvas.Strokes.Contains(strokes))
canvas.Strokes.Remove(strokes);
}
2025-08-03 16:46:33 +08:00
}
else if (item.CommitType == TimeMachineHistoryType.Manipulation)
{
if (!item.StrokeHasBeenCleared)
{
foreach (var currentStroke in item.StylusPointDictionary)
{
if (canvas.Strokes.Contains(currentStroke.Key))
{
2025-05-25 09:29:48 +08:00
currentStroke.Key.StylusPoints = currentStroke.Value.Item2;
}
}
2025-08-03 16:46:33 +08:00
}
else
{
foreach (var currentStroke in item.StylusPointDictionary)
{
if (canvas.Strokes.Contains(currentStroke.Key))
{
2025-05-25 09:29:48 +08:00
currentStroke.Key.StylusPoints = currentStroke.Value.Item1;
}
}
}
2025-08-03 16:46:33 +08:00
}
else if (item.CommitType == TimeMachineHistoryType.DrawingAttributes)
{
if (!item.StrokeHasBeenCleared)
{
foreach (var currentStroke in item.DrawingAttributes)
{
if (canvas.Strokes.Contains(currentStroke.Key))
{
2025-05-25 09:29:48 +08:00
currentStroke.Key.DrawingAttributes = currentStroke.Value.Item2;
}
}
2025-08-03 16:46:33 +08:00
}
else
{
foreach (var currentStroke in item.DrawingAttributes)
{
if (canvas.Strokes.Contains(currentStroke.Key))
{
2025-05-25 09:29:48 +08:00
currentStroke.Key.DrawingAttributes = currentStroke.Value.Item1;
}
}
}
2025-08-03 16:46:33 +08:00
}
else if (item.CommitType == TimeMachineHistoryType.Clear)
{
if (!item.StrokeHasBeenCleared)
{
2025-05-25 09:29:48 +08:00
if (item.CurrentStroke != null)
foreach (var currentStroke in item.CurrentStroke)
if (!canvas.Strokes.Contains(currentStroke))
canvas.Strokes.Add(currentStroke);
if (item.ReplacedStroke != null)
foreach (var replacedStroke in item.ReplacedStroke)
if (canvas.Strokes.Contains(replacedStroke))
canvas.Strokes.Remove(replacedStroke);
2025-08-03 16:46:33 +08:00
}
else
{
2025-05-25 09:29:48 +08:00
if (item.ReplacedStroke != null)
foreach (var replacedStroke in item.ReplacedStroke)
if (!canvas.Strokes.Contains(replacedStroke))
canvas.Strokes.Add(replacedStroke);
if (item.CurrentStroke != null)
foreach (var currentStroke in item.CurrentStroke)
if (canvas.Strokes.Contains(currentStroke))
canvas.Strokes.Remove(currentStroke);
}
2025-08-03 16:46:33 +08:00
}
else if (item.CommitType == TimeMachineHistoryType.ElementInsert)
{
2025-07-28 16:04:36 +08:00
// 使用传入的canvas参数,而不是总是使用inkCanvas
var targetCanvas = canvas ?? inkCanvas;
2025-08-03 16:46:33 +08:00
if (item.StrokeHasBeenCleared)
{
2025-07-21 12:44:05 +08:00
// Undo: 移除元素
2025-07-29 17:44:15 +08:00
if (item.InsertedElement != null && targetCanvas.Children.Contains(item.InsertedElement))
2025-07-28 16:04:36 +08:00
targetCanvas.Children.Remove(item.InsertedElement);
2025-08-03 16:46:33 +08:00
}
else
{
2025-07-21 12:44:05 +08:00
// Redo: 添加元素
2025-08-03 16:46:33 +08:00
if (item.InsertedElement != null && !targetCanvas.Children.Contains(item.InsertedElement))
{
2025-07-28 16:04:36 +08:00
targetCanvas.Children.Add(item.InsertedElement);
2025-07-29 17:38:58 +08:00
// 重新绑定事件处理器(仅对主画布)
2025-08-03 16:46:33 +08:00
if (targetCanvas == inkCanvas)
{
if (item.InsertedElement is Image img)
{
2025-08-30 23:14:00 +08:00
// 检查图片是否有位置信息,如果没有则应用居中
double left = InkCanvas.GetLeft(img);
double top = InkCanvas.GetTop(img);
if (double.IsNaN(left) || double.IsNaN(top))
{
// 图片没有位置信息,应用居中
CenterAndScaleElement(img);
}
// 重新绑定事件处理器
BindElementEvents(img);
2025-08-03 16:46:33 +08:00
}
else if (item.InsertedElement is MediaElement media)
{
2025-08-30 23:14:00 +08:00
// 检查媒体元素是否有位置信息,如果没有则应用居中
double left = InkCanvas.GetLeft(media);
double top = InkCanvas.GetTop(media);
if (double.IsNaN(left) || double.IsNaN(top))
{
// 媒体元素没有位置信息,应用居中
CenterAndScaleElement(media);
}
// 重新绑定事件处理器
BindElementEvents(media);
2025-07-29 17:38:58 +08:00
}
}
}
2025-07-21 12:44:05 +08:00
}
2025-05-25 09:29:48 +08:00
}
_currentCommitType = CommitReason.UserInput;
}
2025-08-03 16:46:33 +08:00
private StrokeCollection ApplyHistoriesToNewStrokeCollection(TimeMachineHistory[] items)
{
InkCanvas fakeInkCanv = new InkCanvas
{
2025-05-25 09:29:48 +08:00
Width = inkCanvas.ActualWidth,
Height = inkCanvas.ActualHeight,
EditingMode = InkCanvasEditingMode.None,
};
2025-08-03 16:46:33 +08:00
if (items != null && items.Length > 0)
{
foreach (var timeMachineHistory in items)
{
2025-07-28 16:04:36 +08:00
// 只处理笔画历史,不处理图片元素历史
// 因为页面预览只需要显示笔画,图片元素会影响主画布
2025-08-03 16:46:33 +08:00
if (timeMachineHistory.CommitType != TimeMachineHistoryType.ElementInsert)
{
2025-07-28 16:04:36 +08:00
ApplyHistoryToCanvas(timeMachineHistory, fakeInkCanv);
}
2025-05-25 09:29:48 +08:00
}
}
return fakeInkCanv.Strokes;
}
2025-07-28 15:47:18 +08:00
// 新增:获取页面的所有图片元素
2025-08-03 16:46:33 +08:00
private List<UIElement> GetPageImageElements(TimeMachineHistory[] items)
{
2025-07-28 15:47:18 +08:00
var imageElements = new List<UIElement>();
2025-08-03 16:46:33 +08:00
if (items != null && items.Length > 0)
{
foreach (var timeMachineHistory in items)
{
2025-07-28 15:47:18 +08:00
if (timeMachineHistory.CommitType == TimeMachineHistoryType.ElementInsert &&
timeMachineHistory.InsertedElement != null &&
2025-08-03 16:46:33 +08:00
!timeMachineHistory.StrokeHasBeenCleared)
{
2025-07-28 15:47:18 +08:00
imageElements.Add(timeMachineHistory.InsertedElement);
}
}
}
return imageElements;
}
2025-08-03 16:46:33 +08:00
private void TimeMachine_OnUndoStateChanged(bool status)
{
2025-05-25 09:29:48 +08:00
var result = status ? Visibility.Visible : Visibility.Collapsed;
BtnUndo.Visibility = result;
BtnUndo.IsEnabled = status;
}
2025-08-03 16:46:33 +08:00
private void TimeMachine_OnRedoStateChanged(bool status)
{
2025-05-25 09:29:48 +08:00
var result = status ? Visibility.Visible : Visibility.Collapsed;
BtnRedo.Visibility = result;
BtnRedo.IsEnabled = status;
}
2025-08-03 16:46:33 +08:00
private void StrokesOnStrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
{
if (!isHidingSubPanelsWhenInking)
{
2025-05-25 09:29:48 +08:00
isHidingSubPanelsWhenInking = true;
HideSubPanels(); // 书写时自动隐藏二级菜单
}
2025-08-03 16:46:33 +08:00
foreach (var stroke in e?.Removed)
{
2025-05-25 09:29:48 +08:00
stroke.StylusPointsChanged -= Stroke_StylusPointsChanged;
stroke.StylusPointsReplaced -= Stroke_StylusPointsReplaced;
stroke.DrawingAttributesChanged -= Stroke_DrawingAttributesChanged;
StrokeInitialHistory.Remove(stroke);
}
2025-08-03 16:46:33 +08:00
foreach (var stroke in e?.Added)
{
2025-05-25 09:29:48 +08:00
stroke.StylusPointsChanged += Stroke_StylusPointsChanged;
stroke.StylusPointsReplaced += Stroke_StylusPointsReplaced;
stroke.DrawingAttributesChanged += Stroke_DrawingAttributesChanged;
StrokeInitialHistory[stroke] = stroke.StylusPoints.Clone();
}
if (_currentCommitType == CommitReason.CodeInput || _currentCommitType == CommitReason.ShapeDrawing) return;
2025-08-03 16:46:33 +08:00
if ((e.Added.Count != 0 || e.Removed.Count != 0) && IsEraseByPoint)
{
2025-05-25 09:29:48 +08:00
if (AddedStroke == null) AddedStroke = new StrokeCollection();
if (ReplacedStroke == null) ReplacedStroke = new StrokeCollection();
AddedStroke.Add(e.Added);
ReplacedStroke.Add(e.Removed);
return;
}
2025-07-28 14:40:44 +08:00
if (e.Added.Count != 0)
{
2025-08-03 16:46:33 +08:00
if (_currentCommitType == CommitReason.ShapeRecognition)
{
2025-05-25 09:29:48 +08:00
timeMachine.CommitStrokeShapeHistory(ReplacedStroke, e.Added);
ReplacedStroke = null;
return;
}
2025-07-28 14:40:44 +08:00
timeMachine.CommitStrokeUserInputHistory(e.Added);
return;
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
if (e.Removed.Count != 0)
{
if (_currentCommitType == CommitReason.ShapeRecognition)
{
2025-05-25 09:29:48 +08:00
ReplacedStroke = e.Removed;
2025-08-03 16:46:33 +08:00
}
else if (!IsEraseByPoint || _currentCommitType == CommitReason.ClearingCanvas)
{
2025-05-25 09:29:48 +08:00
timeMachine.CommitStrokeEraseHistory(e.Removed);
}
}
}
2025-08-03 16:46:33 +08:00
private void Stroke_DrawingAttributesChanged(object sender, PropertyDataChangedEventArgs e)
{
2025-05-25 09:29:48 +08:00
var key = sender as Stroke;
var currentValue = key.DrawingAttributes.Clone();
DrawingAttributesHistory.TryGetValue(key, out var previousTuple);
var previousValue = previousTuple?.Item1 ?? currentValue.Clone();
var needUpdateValue = !DrawingAttributesHistoryFlag[e.PropertyGuid].Contains(key);
2025-08-03 16:46:33 +08:00
if (needUpdateValue)
{
2025-05-25 09:29:48 +08:00
DrawingAttributesHistoryFlag[e.PropertyGuid].Add(key);
Debug.Write(e.PreviousValue.ToString());
}
2025-08-03 16:46:33 +08:00
if (e.PropertyGuid == DrawingAttributeIds.Color && needUpdateValue)
{
2025-05-25 09:29:48 +08:00
previousValue.Color = (Color)e.PreviousValue;
}
2025-08-03 16:46:33 +08:00
if (e.PropertyGuid == DrawingAttributeIds.IsHighlighter && needUpdateValue)
{
2025-05-25 09:29:48 +08:00
previousValue.IsHighlighter = (bool)e.PreviousValue;
}
2025-08-03 16:46:33 +08:00
if (e.PropertyGuid == DrawingAttributeIds.StylusHeight && needUpdateValue)
{
2025-05-25 09:29:48 +08:00
previousValue.Height = (double)e.PreviousValue;
}
2025-08-03 16:46:33 +08:00
if (e.PropertyGuid == DrawingAttributeIds.StylusWidth && needUpdateValue)
{
2025-05-25 09:29:48 +08:00
previousValue.Width = (double)e.PreviousValue;
}
2025-08-03 16:46:33 +08:00
if (e.PropertyGuid == DrawingAttributeIds.StylusTip && needUpdateValue)
{
2025-05-25 09:29:48 +08:00
previousValue.StylusTip = (StylusTip)e.PreviousValue;
}
2025-08-03 16:46:33 +08:00
if (e.PropertyGuid == DrawingAttributeIds.StylusTipTransform && needUpdateValue)
{
2025-05-25 09:29:48 +08:00
previousValue.StylusTipTransform = (Matrix)e.PreviousValue;
}
2025-08-03 16:46:33 +08:00
if (e.PropertyGuid == DrawingAttributeIds.DrawingFlags && needUpdateValue)
{
2025-05-25 09:29:48 +08:00
previousValue.IgnorePressure = (bool)e.PreviousValue;
}
DrawingAttributesHistory[key] =
new Tuple<DrawingAttributes, DrawingAttributes>(previousValue, currentValue);
}
2025-08-03 16:46:33 +08:00
private void Stroke_StylusPointsReplaced(object sender, StylusPointsReplacedEventArgs e)
{
2025-05-25 09:29:48 +08:00
StrokeInitialHistory[sender as Stroke] = e.NewStylusPoints.Clone();
}
2025-08-03 16:46:33 +08:00
private void Stroke_StylusPointsChanged(object sender, EventArgs e)
{
2025-05-25 09:29:48 +08:00
var selectedStrokes = inkCanvas.GetSelectedStrokes();
var count = selectedStrokes.Count;
if (count == 0) count = inkCanvas.Strokes.Count;
2025-08-03 16:46:33 +08:00
if (StrokeManipulationHistory == null)
{
2025-05-25 09:29:48 +08:00
StrokeManipulationHistory =
new Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>>();
}
StrokeManipulationHistory[sender as Stroke] =
new Tuple<StylusPointCollection, StylusPointCollection>(StrokeInitialHistory[sender as Stroke],
(sender as Stroke).StylusPoints.Clone());
2025-08-03 16:46:33 +08:00
if ((StrokeManipulationHistory.Count == count || sender == null) && dec.Count == 0)
{
2025-05-25 09:29:48 +08:00
timeMachine.CommitStrokeManipulationHistory(StrokeManipulationHistory);
2025-08-03 16:46:33 +08:00
foreach (var item in StrokeManipulationHistory)
{
2025-05-25 09:29:48 +08:00
StrokeInitialHistory[item.Key] = item.Value.Item2;
}
StrokeManipulationHistory = null;
}
}
}
}