Merge pull request #62 from WXRIW/feat/TimeMachine
[Feature] 新增多步撤回功能 (Project Name: TimeMachine)
This commit is contained in:
commit
8a312dc3c9
@ -19,6 +19,14 @@ namespace Ink_Canvas
|
||||
public App()
|
||||
{
|
||||
this.Startup += new StartupEventHandler(App_Startup);
|
||||
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
|
||||
}
|
||||
|
||||
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
Ink_Canvas.MainWindow.ShowNewMessage("抱歉,出现未预期的异常,可能导致 Ink Canvas 画板运行不稳定。\n建议保存墨迹后重启应用。", true);
|
||||
LogHelper.NewLog(e.Exception.ToString());
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
void App_Startup(object sender, StartupEventArgs e)
|
||||
|
@ -352,7 +352,7 @@ namespace Ink_Canvas
|
||||
Height = 700;
|
||||
BigViewController.Visibility = Visibility.Visible;
|
||||
TbCurrentTime.Visibility = Visibility.Collapsed;
|
||||
|
||||
|
||||
// Set to center
|
||||
double screenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
|
||||
double screenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;
|
||||
|
@ -32,4 +32,21 @@ namespace Ink_Canvas.Converter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IsEnabledToOpacityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
bool isChecked = (bool)value;
|
||||
if (isChecked == true)
|
||||
{
|
||||
return 1d;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.35;
|
||||
}
|
||||
}
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); }
|
||||
}
|
||||
}
|
148
Ink Canvas/Helpers/TimeMachine.cs
Normal file
148
Ink Canvas/Helpers/TimeMachine.cs
Normal file
@ -0,0 +1,148 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Ink;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
public class TimeMachine
|
||||
{
|
||||
private readonly List<TimeMachineHistory> _currentStrokeHistory = new List<TimeMachineHistory>();
|
||||
|
||||
private int _currentIndex = -1;
|
||||
|
||||
public delegate void OnUndoStateChange(bool status);
|
||||
|
||||
public event OnUndoStateChange OnUndoStateChanged;
|
||||
|
||||
public delegate void OnRedoStateChange(bool status);
|
||||
|
||||
public event OnRedoStateChange OnRedoStateChanged;
|
||||
|
||||
public void CommitStrokeUserInputHistory(StrokeCollection stroke)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(stroke, TimeMachineHistoryType.UserInput, false));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void CommitStrokeShapeHistory(StrokeCollection strokeToBeReplaced, StrokeCollection generatedStroke)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(generatedStroke,
|
||||
TimeMachineHistoryType.ShapeRecognition,
|
||||
false,
|
||||
strokeToBeReplaced));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void CommitStrokeRotateHistory(StrokeCollection strokeToBeReplaced, StrokeCollection generatedStroke)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(generatedStroke,
|
||||
TimeMachineHistoryType.Rotate,
|
||||
false,
|
||||
strokeToBeReplaced));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void CommitStrokeEraseHistory(StrokeCollection stroke, StrokeCollection sourceStroke = null)
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
_currentStrokeHistory.Add(new TimeMachineHistory(stroke, TimeMachineHistoryType.Clear, true, sourceStroke));
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public void ClearStrokeHistory()
|
||||
{
|
||||
_currentStrokeHistory.Clear();
|
||||
_currentIndex = -1;
|
||||
NotifyUndoRedoState();
|
||||
}
|
||||
|
||||
public TimeMachineHistory Undo()
|
||||
{
|
||||
var item = _currentStrokeHistory[_currentIndex];
|
||||
item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared;
|
||||
_currentIndex--;
|
||||
OnUndoStateChanged?.Invoke(_currentIndex > -1);
|
||||
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0);
|
||||
return item;
|
||||
}
|
||||
|
||||
public TimeMachineHistory Redo()
|
||||
{
|
||||
var item = _currentStrokeHistory[++_currentIndex];
|
||||
item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared;
|
||||
NotifyUndoRedoState();
|
||||
return item;
|
||||
}
|
||||
|
||||
public TimeMachineHistory[] ExportTimeMachineHistory()
|
||||
{
|
||||
if (_currentIndex + 1 < _currentStrokeHistory.Count)
|
||||
{
|
||||
_currentStrokeHistory.RemoveRange(_currentIndex + 1, (_currentStrokeHistory.Count - 1) - _currentIndex);
|
||||
}
|
||||
return _currentStrokeHistory.ToArray();
|
||||
}
|
||||
|
||||
public bool ImportTimeMachineHistory(TimeMachineHistory[] sourceHistory)
|
||||
{
|
||||
_currentStrokeHistory.Clear();
|
||||
_currentStrokeHistory.AddRange(sourceHistory);
|
||||
_currentIndex = _currentStrokeHistory.Count - 1;
|
||||
NotifyUndoRedoState();
|
||||
return true;
|
||||
}
|
||||
private void NotifyUndoRedoState()
|
||||
{
|
||||
OnUndoStateChanged?.Invoke(_currentIndex > -1);
|
||||
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0);
|
||||
}
|
||||
}
|
||||
|
||||
public class TimeMachineHistory
|
||||
{
|
||||
public TimeMachineHistoryType CommitType;
|
||||
public bool StrokeHasBeenCleared;
|
||||
public StrokeCollection CurrentStroke;
|
||||
public StrokeCollection ReplacedStroke;
|
||||
public TimeMachineHistory(StrokeCollection currentStroke, TimeMachineHistoryType commitType, bool strokeHasBeenCleared)
|
||||
{
|
||||
CommitType = commitType;
|
||||
CurrentStroke = currentStroke;
|
||||
StrokeHasBeenCleared = strokeHasBeenCleared;
|
||||
ReplacedStroke = null;
|
||||
}
|
||||
public TimeMachineHistory(StrokeCollection currentStroke, TimeMachineHistoryType commitType, bool strokeHasBeenCleared, StrokeCollection replacedStroke)
|
||||
{
|
||||
CommitType = commitType;
|
||||
CurrentStroke = currentStroke;
|
||||
StrokeHasBeenCleared = strokeHasBeenCleared;
|
||||
ReplacedStroke = replacedStroke;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TimeMachineHistoryType
|
||||
{
|
||||
UserInput,
|
||||
ShapeRecognition,
|
||||
Clear,
|
||||
Rotate
|
||||
}
|
||||
}
|
@ -166,7 +166,8 @@
|
||||
<Compile Include="Helpers\InkRecognizeHelper.cs" />
|
||||
<Compile Include="Helpers\LogHelper.cs" />
|
||||
<Compile Include="Helpers\MultiTouchInput.cs" />
|
||||
<Compile Include="Helpers\VisibilityConverter.cs" />
|
||||
<Compile Include="Helpers\TimeMachine.cs" />
|
||||
<Compile Include="Helpers\Converters.cs" />
|
||||
<Compile Include="NamesInputWindow.xaml.cs">
|
||||
<DependentUpon>NamesInputWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<Window x:Class="Ink_Canvas.MainWindow"
|
||||
<Window x:Class="Ink_Canvas.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@ -20,7 +20,7 @@
|
||||
Closing="Window_Closing"
|
||||
Closed="Window_Closed"
|
||||
PreviewKeyDown="Main_Grid_PreviewKeyDown"
|
||||
Height="10000" Width="1000"
|
||||
Height="1000" Width="1000"
|
||||
FontFamily="Microsoft YaHei UI"
|
||||
MouseWheel="Window_MouseWheel"
|
||||
Foreground="Black"
|
||||
@ -28,6 +28,7 @@
|
||||
<!--资源中添加命令-->
|
||||
<Window.Resources>
|
||||
<c:VisibilityConverter x:Key="VisibilityConverter"/>
|
||||
<c:IsEnabledToOpacityConverter x:Key="IsEnabledToOpacityConverter"/>
|
||||
<RoutedUICommand x:Key="KeyExit" Text=" "/>
|
||||
<RoutedUICommand x:Key="back_HotKey_Command" Text=" "/>
|
||||
<RoutedUICommand x:Key="HotKey_Capture" Text=" "/>
|
||||
@ -97,13 +98,17 @@
|
||||
ManipulationDelta="Main_Grid_ManipulationDelta"
|
||||
ManipulationCompleted="Main_Grid_ManipulationCompleted"
|
||||
ManipulationInertiaStarting="inkCanvas_ManipulationInertiaStarting"
|
||||
IsManipulationEnabled="True" EditingModeChanged="inkCanvas_EditingModeChanged"
|
||||
IsManipulationEnabled="True"
|
||||
EditingModeChanged="inkCanvas_EditingModeChanged"
|
||||
PreviewTouchDown="inkCanvas_PreviewTouchDown"
|
||||
PreviewTouchUp="inkCanvas_PreviewTouchUp"
|
||||
MouseDown="inkCanvas_MouseDown" MouseMove="inkCanvas_MouseMove" MouseUp="inkCanvas_MouseUp"
|
||||
MouseDown="inkCanvas_MouseDown"
|
||||
MouseMove="inkCanvas_MouseMove"
|
||||
MouseUp="inkCanvas_MouseUp"
|
||||
ManipulationStarting="inkCanvas_ManipulationStarting"
|
||||
SelectionChanged="inkCanvas_SelectionChanged"
|
||||
StrokeCollected="inkCanvas_StrokeCollected">
|
||||
StrokeCollected="inkCanvas_StrokeCollected"
|
||||
>
|
||||
<!--<InkCanvas.DefaultDrawingAttributes>
|
||||
<DrawingAttributes StylusTip="Ellipse" Height="8" Width="4" IgnorePressure="False" FitToCurve="True" >
|
||||
<DrawingAttributes.StylusTipTransform>
|
||||
@ -849,7 +854,7 @@
|
||||
Margin="0,10,0,0" Width="{Binding ElementName=StackPanelMain, Path=ActualWidth}"
|
||||
Click="BtnUndo_Click" Foreground="{Binding ElementName=BtnExit, Path=Foreground}"
|
||||
Background="{Binding ElementName=BtnExit, Path=Background}"
|
||||
IsEnabled="False" IsEnabledChanged="Btn_IsEnabledChanged">
|
||||
IsEnabled="False" Visibility="Collapsed" IsEnabledChanged="Btn_IsEnabledChanged">
|
||||
<StackPanel Opacity="0.2">
|
||||
<ui:SymbolIcon Symbol="Undo"/>
|
||||
<TextBlock Text="撤销" Margin="0,4,0,0"/>
|
||||
@ -1129,18 +1134,14 @@
|
||||
</Viewbox>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Viewbox Margin="0,2">
|
||||
<Grid>
|
||||
<ui:SymbolIcon Symbol="Undo" Foreground="#666666"
|
||||
Visibility="{Binding ElementName=BtnUndo, Path=Visibility}"
|
||||
IsEnabled="{Binding ElementName=BtnUndo, Path=IsEnabled}"
|
||||
MouseUp="SymbolIconUndo_MouseUp" MouseDown="Border_MouseDown"/>
|
||||
<ui:SymbolIcon Symbol="Redo" Foreground="#666666"
|
||||
Visibility="{Binding ElementName=BtnRedo, Path=Visibility}"
|
||||
IsEnabled="{Binding ElementName=BtnRedo, Path=IsEnabled}"
|
||||
MouseUp="SymbolIconRedo_MouseUp" MouseDown="Border_MouseDown"/>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
<ui:SymbolIcon Symbol="Undo" Foreground="#666666" Margin="0,0,-2,0"
|
||||
Opacity="{Binding ElementName=BtnUndo, Path=IsEnabled, Converter={StaticResource IsEnabledToOpacityConverter}}"
|
||||
IsEnabled="{Binding ElementName=BtnUndo, Path=IsEnabled}"
|
||||
MouseUp="SymbolIconUndo_MouseUp" MouseDown="Border_MouseDown"/>
|
||||
<ui:SymbolIcon Symbol="Redo" Foreground="#666666" Margin="-2,0,0,0"
|
||||
Opacity="{Binding ElementName=BtnRedo, Path=IsEnabled, Converter={StaticResource IsEnabledToOpacityConverter}}"
|
||||
IsEnabled="{Binding ElementName=BtnRedo, Path=IsEnabled}"
|
||||
MouseUp="SymbolIconRedo_MouseUp" MouseDown="Border_MouseDown"/>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="{Binding ElementName=StackPanelFloatingBar, Path=Orientation}">
|
||||
<Grid Width="20" Height="24" Margin="2,0,12,1">
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@ namespace Ink_Canvas
|
||||
[JsonProperty("startup")]
|
||||
public Startup Startup { get; set; } = new Startup();
|
||||
}
|
||||
|
||||
|
||||
public class Canvas
|
||||
{
|
||||
[JsonProperty("inkWidth")]
|
||||
@ -48,7 +48,7 @@ namespace Ink_Canvas
|
||||
public bool IsEnableTwoFingerRotation { get; set; } = false;
|
||||
[JsonProperty("isEnableTwoFingerRotationOnSelection")]
|
||||
public bool IsEnableTwoFingerRotationOnSelection { get; set; } = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class Startup
|
||||
@ -101,7 +101,7 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isEnableFingerGestureSlideShowControl")]
|
||||
public bool IsEnableFingerGestureSlideShowControl { get; set; } = true;
|
||||
}
|
||||
|
||||
|
||||
public class Automation
|
||||
{
|
||||
[JsonProperty("isAutoKillPptService")]
|
||||
@ -112,7 +112,7 @@ namespace Ink_Canvas
|
||||
public bool IsAutoSaveStrokesAtScreenshot { get; set; } = false;
|
||||
[JsonProperty("isAutoSaveStrokesAtClear")]
|
||||
public bool IsAutoSaveStrokesAtClear { get; set; } = false;
|
||||
|
||||
|
||||
[JsonProperty("isAutoClearWhenExitingWritingMode")]
|
||||
public bool IsAutoClearWhenExitingWritingMode { get; set; } = false;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user