1
0

refactor: refactor project

This commit is contained in:
2026-03-25 10:33:05 +08:00
parent 6c07355601
commit ba53ad1da4
65 changed files with 547 additions and 403 deletions

View File

@@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BallanceTasEditor.Backend {
public class FileWatcher : IDisposable {
/// <summary>
/// Create a file watcher.
/// </summary>
/// <remarks>
/// This new created file watcher is not watching specified file
/// unless you explicitly call <see cref="Start"/> method.
/// </remarks>
/// <param name="filepath">The path to watching file.</param>
public FileWatcher(string filepath) {
m_FilePath = filepath;
m_IsWatching = false;
m_EventMutex = new Mutex();
m_IsEventProcessing = false;
// Get directory and file info
string directory = Path.GetDirectoryName(filepath) ?? throw new ArgumentException("Invalid file path", nameof(filepath));
string filename = Path.GetFileName(filepath);
// Create FileSystemWatcher
m_FileSystemWatcher = new FileSystemWatcher(directory, filename) {
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName,
EnableRaisingEvents = false
};
m_FileSystemWatcher.Changed += OnFileSystemChanged;
m_FileSystemWatcher.Deleted += OnFileSystemDeleted;
m_FileSystemWatcher.Renamed += OnFileSystemRenamed;
}
/// <summary>
/// The path to watching file.
/// </summary>
private string m_FilePath;
/// <summary>
/// Whether the file watcher is watching file.
/// </summary>
private bool m_IsWatching;
/// <summary>
/// The FileSystemWatcher instance.
/// </summary>
private readonly FileSystemWatcher m_FileSystemWatcher;
/// <summary>
/// Flag to indicate if disposed.
/// </summary>
private bool m_Disposed = false;
/// <summary>
/// Start watching file.
/// </summary>
public void Start() {
if (m_Disposed) throw new ObjectDisposedException(nameof(FileWatcher));
if (m_IsWatching) {
throw new InvalidOperationException("File watcher is already watching file.");
} else {
m_FileSystemWatcher.EnableRaisingEvents = true;
m_IsWatching = true;
}
}
/// <summary>
/// Stop watching file.
/// </summary>
public void Stop() {
if (m_Disposed) throw new ObjectDisposedException(nameof(FileWatcher));
if (m_IsWatching) {
m_FileSystemWatcher.EnableRaisingEvents = false;
m_IsWatching = false;
} else {
throw new InvalidOperationException("File watcher is not watching file.");
}
}
/// <summary>
/// Dispose the file watcher and release resources.
/// </summary>
public void Dispose() {
if (!m_Disposed) {
// Stop watching.
if (m_IsWatching) {
Stop();
}
// Dispose members
m_FileSystemWatcher.Dispose();
m_EventMutex.Dispose();
m_Disposed = true;
}
}
/// <summary>
/// The event handler when file is modified.
/// </summary>
public delegate void FileModifiedHandler();
/// <summary>
/// The event handler when file is deleted.
/// </summary>
public delegate void FileDeletedHandler();
/// <summary>
/// The event when file is modified.
/// </summary>
/// <remarks>
/// Before user process this event completely,
/// there is no any other event will be triggered.
/// </remarks>
public event FileModifiedHandler? FileModified;
/// <summary>
/// The event when file is deleted.
/// </summary>
/// <remarks>
/// Before user process this event completely,
/// there is no any other event will be triggered.
/// </remarks>
public event FileDeletedHandler? FileDeleted;
private Mutex m_EventMutex;
private bool m_IsEventProcessing;
private void OnFileModified() {
if (FileModified is not null) {
lock (m_EventMutex) {
if (m_IsEventProcessing) return;
else m_IsEventProcessing = true;
}
try {
FileModified.Invoke();
}
finally {
lock (m_EventMutex) {
m_IsEventProcessing = false;
}
}
}
}
private void OnFileDeleted() {
if (FileDeleted is not null) {
lock (m_EventMutex) {
if (m_IsEventProcessing) return;
else m_IsEventProcessing = true;
}
try {
FileDeleted.Invoke();
}
finally {
lock (m_EventMutex) {
m_IsEventProcessing = false;
}
}
}
}
/// <summary>
/// Handler for FileSystemWatcher Changed event.
/// </summary>
private void OnFileSystemChanged(object sender, FileSystemEventArgs e) {
// Filter out our own change notifications to avoid infinite loops
if (e.ChangeType == WatcherChangeTypes.Changed) {
OnFileModified();
}
}
/// <summary>
/// Handler for FileSystemWatcher Deleted event.
/// </summary>
private void OnFileSystemDeleted(object sender, FileSystemEventArgs e) {
OnFileDeleted();
}
/// <summary>
/// Handler for FileSystemWatcher Renamed event.
/// </summary>
private void OnFileSystemRenamed(object sender, RenamedEventArgs e) {
// Treat rename as a delete since the original file is gone
OnFileDeleted();
}
}
}

View File

@@ -25,7 +25,7 @@ namespace BallanceTasEditor.Backend {
/// <summary>
/// 描述TAS文件中一帧的结构。
/// </summary>
public class TasFrame {
public class TasFrame : IEquatable<TasFrame> {
/// <summary>
/// 以指定的FPS无任何按键初始化当前帧。
/// </summary>
@@ -43,6 +43,15 @@ namespace BallanceTasEditor.Backend {
m_KeyFlags = raw.KeyFlags;
}
/// <summary>
/// 将原始TAS数据覆写到自身
/// </summary>
/// <param name="raw">要写入的原始TAS数据</param>
public void FromRaw(RawTasFrame raw) {
m_TimeDelta = raw.TimeDelta;
m_KeyFlags = raw.KeyFlags;
}
/// <summary>
/// 转换为原始TAS数据。
/// </summary>
@@ -156,6 +165,38 @@ namespace BallanceTasEditor.Backend {
m_KeyFlags = 0;
}
/// <summary>
/// 指示当前对象是否等于另一个 TasFrame 对象。
/// </summary>
/// <param name="other">要比较的 TasFrame 对象。</param>
/// <returns>如果两个对象相等则为 true否则为 false。</returns>
public bool Equals(TasFrame? other) {
return other is not null &&
m_TimeDelta == other.m_TimeDelta &&
m_KeyFlags == other.m_KeyFlags;
}
/// <summary>
/// 指示当前对象是否等于另一个对象。
/// </summary>
/// <param name="obj">要比较的对象。</param>
/// <returns>如果两个对象相等则为 true否则为 false。</returns>
public override bool Equals(object? obj) {
if (obj is TasFrame other) {
return Equals(other);
} else {
return false;
}
}
/// <summary>
/// 返回此实例的哈希代码。
/// </summary>
/// <returns>32 位有符号整数哈希代码。</returns>
public override int GetHashCode() {
return HashCode.Combine(m_TimeDelta, m_KeyFlags);
}
}
/// <summary>

View File

@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<Resource Include="Assets\*.ico" />
<Resource Include="Frontend\Assets\*.ico" />
</ItemGroup>
<ItemGroup>
@@ -19,4 +19,8 @@
<PackageReference Include="DotNetZip" Version="1.9.1.8" />
</ItemGroup>
<ItemGroup>
<Folder Include="Frontend\Utils\" />
</ItemGroup>
</Project>

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace BallanceTasEditor.Converters {
namespace BallanceTasEditor.Frontend.Converters {
[ValueConversion(typeof(int), typeof(string))]
public class FpsConverter : IValueConverter {
public static FpsConverter Instance = new FpsConverter();
@@ -15,7 +15,7 @@ namespace BallanceTasEditor.Converters {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is int tv) {
if (tv <= 0) return DependencyProperty.UnsetValue;
else return Utils.FpsConverter.ToDelta(tv).ToString();
else return Backend.FpsConverter.ToDelta(tv).ToString();
} else {
return DependencyProperty.UnsetValue;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace BallanceTasEditor.Converters {
namespace BallanceTasEditor.Frontend.Converters {
[ValueConversion(typeof(int?), typeof(string))]
public class StringifyIntegerConverter : IValueConverter {

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -7,7 +7,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace BallanceTasEditor.Styles {
namespace BallanceTasEditor.Frontend.Styles {
public class AccessoryIcon {
public static ImageSource GetIcon(DependencyObject obj) {

View File

@@ -1,6 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BallanceTasEditor.Styles">
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Styles">
<DataTemplate x:Key="AccessoryIconDataTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=(local:AccessoryIcon.Icon), RelativeSource={RelativeSource AncestorType=Control, AncestorLevel=1}}"

View File

@@ -1,4 +1,4 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="OkButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Padding" Value="5"/>
@@ -8,7 +8,7 @@
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/Ok.ico" RenderOptions.BitmapScalingMode="HighQuality"
<Image Source="/Frontend/Assets/Ok.ico" RenderOptions.BitmapScalingMode="HighQuality"
Width="16" Height="16" VerticalAlignment="Center"/>
<ContentControl Content="{Binding}" Margin="5,0,0,0" VerticalAlignment="Center"/>
</StackPanel>
@@ -24,7 +24,7 @@
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="/Assets/Cancel.ico" RenderOptions.BitmapScalingMode="HighQuality"
<Image Source="/Frontend/Assets/Cancel.ico" RenderOptions.BitmapScalingMode="HighQuality"
Width="16" Height="16" VerticalAlignment="Center"/>
<ContentControl Content="{Binding}" Margin="5,0,0,0" VerticalAlignment="Center"/>
</StackPanel>

View File

@@ -1,4 +1,3 @@
using BallanceTasEditor.Utils;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
@@ -8,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.ViewModels {
namespace BallanceTasEditor.Frontend.ViewModels {
public struct NewFileDialogResult {
public int Count { get; set; }

View File

@@ -1,11 +1,11 @@
<Window x:Class="BallanceTasEditor.Views.AboutDialog"
<Window x:Class="BallanceTasEditor.Frontend.Views.AboutDialog"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="About Ballance TAS Editor" Width="340" Height="480" Icon="/Assets/About.ico">
Title="About Ballance TAS Editor" Width="340" Height="480" Icon="/Frontend/Assets/About.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
@@ -15,7 +15,7 @@
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<!-- 图标 -->
<Image Source="/Assets/App.ico" Grid.Row="0" Margin="10" HorizontalAlignment="Center"
<Image Source="/Frontend/Assets/App.ico" Grid.Row="0" Margin="10" HorizontalAlignment="Center"
Width="64" Height="64" RenderOptions.BitmapScalingMode="HighQuality"/>
<!-- 应用名称和简介 -->

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,7 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for AboutDialog.xaml
/// </summary>

View File

@@ -1,11 +1,11 @@
<Window x:Class="BallanceTasEditor.Views.AddFrameDialog"
<Window x:Class="BallanceTasEditor.Frontend.Views.AddFrameDialog"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Add Frame" Height="250" Width="400" Icon="/Assets/AddFrame.ico">
Title="Add Frame" Height="250" Width="400" Icon="/Frontend/Assets/AddFrame.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
@@ -34,9 +34,9 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="/Assets/Count.ico" RenderOptions.BitmapScalingMode="HighQuality"
<Image Source="/Frontend/Assets/Count.ico" RenderOptions.BitmapScalingMode="HighQuality"
Grid.Column="0" Grid.Row="0" Width="16" Height="16" VerticalAlignment="Center"/>
<Image Source="/Assets/Fps.ico" RenderOptions.BitmapScalingMode="HighQuality"
<Image Source="/Frontend/Assets/Fps.ico" RenderOptions.BitmapScalingMode="HighQuality"
Grid.Column="0" Grid.Row="1" Width="16" Height="16" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="1" Grid.Row="0" Text="Count" VerticalAlignment="Center"/>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,7 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for AddFrameDialog.xaml
/// </summary>

View File

@@ -1,11 +1,11 @@
<Window x:Class="BallanceTasEditor.Views.EditFpsDialog"
<Window x:Class="BallanceTasEditor.Frontend.Views.EditFpsDialog"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Edit FPS" Height="220" Width="400" Icon="/Assets/SetFps.ico">
Title="Edit FPS" Height="220" Width="400" Icon="/Frontend/Assets/SetFps.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
@@ -33,7 +33,7 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="/Assets/Fps.ico" RenderOptions.BitmapScalingMode="HighQuality"
<Image Source="/Frontend/Assets/Fps.ico" RenderOptions.BitmapScalingMode="HighQuality"
Grid.Column="0" Grid.Row="0" Width="16" Height="16" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="1" Grid.Row="0" Text="FPS" VerticalAlignment="Center"/>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,7 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for EditFpsDialog.xaml
/// </summary>

View File

@@ -1,11 +1,11 @@
<Window x:Class="BallanceTasEditor.Views.GotoDialog"
<Window x:Class="BallanceTasEditor.Frontend.Views.GotoDialog"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Goto..." Height="180" Width="400" Icon="/Assets/Goto.ico">
Title="Goto..." Height="180" Width="400" Icon="/Frontend/Assets/Goto.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,7 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for GotoDialog.xaml
/// </summary>

View File

@@ -1,12 +1,12 @@
<Window x:Class="BallanceTasEditor.Views.MainWindow"
<Window x:Class="BallanceTasEditor.Frontend.Views.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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:styles="clr-namespace:BallanceTasEditor.Styles"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:styles="clr-namespace:BallanceTasEditor.Frontend.Styles"
mc:Ignorable="d" WindowStartupLocation="CenterScreen"
Title="Ballance TAS Editor" Height="600" Width="800" Icon="/Assets/App.ico">
Title="Ballance TAS Editor" Height="600" Width="800" Icon="/Frontend/Assets/App.ico">
<Grid>
<Grid.RowDefinitions>
@@ -19,28 +19,28 @@
<Menu Grid.Row="0">
<Menu.Resources>
<!-- Menu Icons -->
<Image x:Key="IconNewFile" Source="/Assets/NewFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconOpenFile" Source="/Assets/OpenFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSaveFile" Source="/Assets/SaveFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSaveFileAs" Source="/Assets/SaveFileAs.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSaveFileThenRunGame" Source="/Assets/SaveFileThenRunGame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconCloseFile" Source="/Assets/CloseFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconExit" Source="/Assets/Exit.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconUndo" Source="/Assets/Undo.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconRedo" Source="/Assets/Redo.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconNextItem" Source="/Assets/NextItem.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconNextPage" Source="/Assets/NextPage.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPreviousItem" Source="/Assets/PreviousItem.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPreviousPage" Source="/Assets/PreviousPage.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconGoto" Source="/Assets/Goto.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconDrawMode" Source="/Assets/DrawMode.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconFillMode" Source="/Assets/FillMode.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSelectMode" Source="/Assets/SelectMode.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconClearKeys" Source="/Assets/ClearKeys.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconUniformFps" Source="/Assets/SetFps.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPreference" Source="/Assets/Preference.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconReportBug" Source="/Assets/ReportBug.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconAbout" Source="/Assets/About.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconNewFile" Source="/Frontend/Assets/NewFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconOpenFile" Source="/Frontend/Assets/OpenFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSaveFile" Source="/Frontend/Assets/SaveFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSaveFileAs" Source="/Frontend/Assets/SaveFileAs.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSaveFileThenRunGame" Source="/Frontend/Assets/SaveFileThenRunGame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconCloseFile" Source="/Frontend/Assets/CloseFile.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconExit" Source="/Frontend/Assets/Exit.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconUndo" Source="/Frontend/Assets/Undo.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconRedo" Source="/Frontend/Assets/Redo.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconNextItem" Source="/Frontend/Assets/NextItem.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconNextPage" Source="/Frontend/Assets/NextPage.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPreviousItem" Source="/Frontend/Assets/PreviousItem.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPreviousPage" Source="/Frontend/Assets/PreviousPage.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconGoto" Source="/Frontend/Assets/Goto.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconDrawMode" Source="/Frontend/Assets/DrawMode.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconFillMode" Source="/Frontend/Assets/FillMode.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSelectMode" Source="/Frontend/Assets/SelectMode.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconClearKeys" Source="/Frontend/Assets/ClearKeys.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconUniformFps" Source="/Frontend/Assets/SetFps.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPreference" Source="/Frontend/Assets/Preference.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconReportBug" Source="/Frontend/Assets/ReportBug.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconAbout" Source="/Frontend/Assets/About.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
</Menu.Resources>
<MenuItem Header="_File" Padding="5">
@@ -87,15 +87,15 @@
<StackPanel Orientation="Horizontal" Grid.Column="0">
<Button Content="Select Mode" Margin="5" Padding="5"
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Assets/SelectMode.ico"/>
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Frontend/Assets/SelectMode.ico"/>
<Button Content="Fill Mode" Margin="5" Padding="5"
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Assets/FillMode.ico"/>
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Frontend/Assets/FillMode.ico"/>
<Button Content="Draw Mode" Margin="5" Padding="5"
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Assets/DrawMode.ico"/>
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Frontend/Assets/DrawMode.ico"/>
</StackPanel>
<Button Content="Save File then Run Game" Grid.Column="1" Margin="5" Padding="5"
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Assets/SaveFileThenRunGame.ico"/>
Style="{StaticResource AccessoryIconButton}" styles:AccessoryIcon.Icon="/Frontend/Assets/SaveFileThenRunGame.ico"/>
</Grid>
@@ -103,7 +103,7 @@
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" AllowDrop="True">
<Rectangle StrokeThickness="4" Stroke="Gray" StrokeDashArray="4 4" Fill="Transparent"/>
<StackPanel Orientation="Horizontal" Margin="20">
<Image Source="/Assets/OpenFile.ico" Width="24" Height="24" Margin="5" VerticalAlignment="Center"/>
<Image Source="/Frontend/Assets/OpenFile.ico" Width="24" Height="24" Margin="5" VerticalAlignment="Center"/>
<TextBlock Margin="5" Text="Create, Open or Drop a TAS File in There for Editing" Foreground="Gray" FontSize="16" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
@@ -113,15 +113,15 @@
<ContextMenu>
<ContextMenu.Resources>
<!-- Context Menu Icons -->
<Image x:Key="IconSet" Source="/Assets/SetCell.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconUnset" Source="/Assets/UnsetCell.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconFlip" Source="/Assets/FlipCell.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconCut" Source="/Assets/CutFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconCopy" Source="/Assets/CopyFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPaste" Source="/Assets/PasteFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconDelete" Source="/Assets/DeleteFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconAdd" Source="/Assets/AddFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconFps" Source="/Assets/SetFps.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconSet" Source="/Frontend/Assets/SetCell.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconUnset" Source="/Frontend/Assets/UnsetCell.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconFlip" Source="/Frontend/Assets/FlipCell.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconCut" Source="/Frontend/Assets/CutFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconCopy" Source="/Frontend/Assets/CopyFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconPaste" Source="/Frontend/Assets/PasteFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconDelete" Source="/Frontend/Assets/DeleteFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconAdd" Source="/Frontend/Assets/AddFrame.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
<Image x:Key="IconFps" Source="/Frontend/Assets/SetFps.ico" RenderOptions.BitmapScalingMode="HighQuality"/>
</ContextMenu.Resources>
<MenuItem Header="Set" Icon="{StaticResource IconSet}"/>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -13,7 +13,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>

View File

@@ -1,14 +1,14 @@
<Window x:Class="BallanceTasEditor.Views.NewFileDialog"
<Window x:Class="BallanceTasEditor.Frontend.Views.NewFileDialog"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:vm="clr-namespace:BallanceTasEditor.ViewModels"
xmlns:conveter="clr-namespace:BallanceTasEditor.Converters"
xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels"
xmlns:conveter="clr-namespace:BallanceTasEditor.Frontend.Converters"
d:DataContext="{d:DesignInstance vm:NewFileDialog}"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="New File" Height="250" Width="400" Icon="/Assets/NewFile.ico">
Title="New File" Height="250" Width="400" Icon="/Frontend/Assets/NewFile.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
@@ -37,9 +37,9 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="/Assets/Count.ico" RenderOptions.BitmapScalingMode="HighQuality"
<Image Source="/Frontend/Assets/Count.ico" RenderOptions.BitmapScalingMode="HighQuality"
Grid.Column="0" Grid.Row="0" Width="16" Height="16" VerticalAlignment="Center"/>
<Image Source="/Assets/Fps.ico" RenderOptions.BitmapScalingMode="HighQuality"
<Image Source="/Frontend/Assets/Fps.ico" RenderOptions.BitmapScalingMode="HighQuality"
Grid.Column="0" Grid.Row="1" Width="16" Height="16" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="1" Grid.Row="0" Text="Count" VerticalAlignment="Center"/>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,7 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for NewFileDialog.xaml
/// </summary>

View File

@@ -1,12 +1,12 @@
<Window x:Class="BallanceTasEditor.Views.PreferenceDialog"
<Window x:Class="BallanceTasEditor.Frontend.Views.PreferenceDialog"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:styles="clr-namespace:BallanceTasEditor.Styles"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:styles="clr-namespace:BallanceTasEditor.Frontend.Styles"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Editor Preference" Height="450" Width="400" Icon="/Assets/Preference.ico">
Title="Editor Preference" Height="450" Width="400" Icon="/Frontend/Assets/Preference.ico">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
@@ -17,7 +17,7 @@
<StackPanel Orientation="Vertical">
<!-- TODO: This icon is wrong. -->
<GroupBox Header="Editor Layout" Margin="10" Padding="10"
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Assets/EditorLayout.ico">
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Frontend/Assets/EditorLayout.ico">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@@ -28,7 +28,7 @@
</Grid>
</GroupBox>
<GroupBox Header="Paste Behavior" Margin="10" Padding="10"
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Assets/PasteFrame.ico">
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Frontend/Assets/PasteFrame.ico">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@@ -39,11 +39,11 @@
</Grid>
</GroupBox>
<GroupBox Header="Frame Count" Margin="10" Padding="10"
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Assets/Count.ico">
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Frontend/Assets/Count.ico">
<TextBox Padding="3"/>
</GroupBox>
<GroupBox Header="Game Path" Margin="10" Padding="10"
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Assets/SaveFileThenRunGame.ico">
Style="{StaticResource AccessoryIconGroupBox}" styles:AccessoryIcon.Icon="/Frontend/Assets/SaveFileThenRunGame.ico">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -12,7 +12,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for PreferenceDialog.xaml
/// </summary>

View File

@@ -1,9 +1,9 @@
<UserControl x:Class="BallanceTasEditor.Views.TasViewer"
<UserControl x:Class="BallanceTasEditor.Frontend.Views.TasViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BallanceTasEditor.Views"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -13,7 +13,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BallanceTasEditor.Views {
namespace BallanceTasEditor.Frontend.Views {
/// <summary>
/// Interaction logic for TasViewer.xaml
/// </summary>

View File

@@ -1,97 +0,0 @@
using LanguageExt.Common;
using LanguageExt.TypeClasses;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Utils {
// YYC MARK:
// 这些验证器尽管服务于UI但是并不遵循WPF或者CommunityToolkit.Mvvm的Validator模型
// 所以我把他们放在这里。
/// <summary>
/// 验证器接口。
/// </summary>
/// <typeparam name="TIn">验证器接受的待验证数据的类型。</typeparam>
/// <typeparam name="TOut">验证器验证完毕后,会输出的类型。</typeparam>
public interface IValidator<TIn, TOut> {
/// <summary>
/// 验证给定数据是否正确。
/// </summary>
/// <param name="data">要验证的数据。</param>
/// <returns>数据正确,或对应的错误信息。</returns>
ValidationResult Validate(TIn data);
/// <summary>
/// 获取验证无误数据转换后的数据。
/// </summary>
/// <param name="data">验证无误,用于获取输出的数据。</param>
/// <returns>输出的数据。</returns>
/// <exception cref="ArgumentException">给定数据验证时出现错误。</exception>
TOut Fetch(TIn data);
}
/// <summary>
/// 以字符串呈现的数据的通用验证器
/// </summary>
public abstract class StringifiedValueValidator<T> : IValidator<string, T> where T : notnull {
/// <summary>
/// 用户需要实现的验证函数。
/// </summary>
/// <param name="stringifiedValue">要进行验证的数据。</param>
/// <returns>验证完毕用于输出的数值,或者验证失败时的错误消息。</returns>
protected abstract Either<T, string> ValidateValue(string stringifiedValue);
public ValidationResult Validate(string data) {
return ValidateValue(data).Match(
Left: (_) => ValidationResult.Success,
Right: (v) => new ValidationResult(v)
);
}
public T Fetch(string data) {
return ValidateValue(data).Match(
Left: (v) => v,
Right: (msg) => throw new ArgumentException($"Given value can not pass Validator due to {msg}.")
);
}
}
public abstract class IntegerValidator : StringifiedValueValidator<int> {
protected override Either<int, string> ValidateValue(string stringifiedValue) {
if (int.TryParse(stringifiedValue, out int val)) {
return Left(val);
} else {
return Right("Given string do not represent any valid number.");
}
}
}
public class FpsValidator : IntegerValidator {
public static FpsValidator Instance = new FpsValidator();
protected override Either<int, string> ValidateValue(string stringifiedValue) {
return base.ValidateValue(stringifiedValue).BindLeft<int>((v) => {
if (v <= 0) return Right("Fps must be greater than zero.");
else return Left(v);
});
}
}
public class CountValidator : IntegerValidator {
public static FpsValidator Instance = new FpsValidator();
protected override Either<int, string> ValidateValue(string stringifiedValue) {
return base.ValidateValue(stringifiedValue).BindLeft<int>((v) => {
if (v < 0) return Right("Count can not lower than zero.");
else return Left(v);
});
}
}
}

View File

@@ -0,0 +1,209 @@
using BallanceTasEditor.Backend;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditorTests.Backend {
[TestClass]
public class TasSequenceTests {
private static readonly TasFrame[] BLANK = { };
private static readonly TasFrame[] PROBE = {
new TasFrame(10),
new TasFrame(20),
new TasFrame(30),
new TasFrame(40),
new TasFrame(50),
};
private static CountableEnumerable<TasFrame> GetCountableProbe() {
return new CountableEnumerable<TasFrame>(PROBE);
}
private static CountableEnumerable<TasFrame> GetCountableBlank() {
return new CountableEnumerable<TasFrame>(BLANK);
}
private static IEnumerable<object[]> TasSequenceInstanceProvider {
get {
yield return new object[] { new ListTasSequence() };
yield return new object[] { new LegacyTasSequence() };
// TODO: Add GapBufferTasSequence once we finish it.
//yield return new object[] { new GapBufferTasSequence() };
}
}
/// <summary>
/// Visit函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void VisitTest(ITasSequence sequence) {
// 空时访问
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(-1));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(0));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(1));
// 设置数据
sequence.Insert(0, GetCountableProbe());
// 访问数据
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(-1));
for (int i = 0; i < PROBE.Length; i++) {
Assert.AreEqual(sequence.Visit(i), PROBE[i]);
}
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(PROBE.Length));
}
/// <summary>
/// Insert函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void InsertTest(ITasSequence sequence) {
// 需要在不同的存储器上,分别检测在空的时候插入,
// 和在非空时的头,中,尾分别插入的结果。
// 先检测空插入
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Insert(-1, GetCountableProbe()));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Insert(1, GetCountableProbe()));
sequence.Insert(0, GetCountableProbe());
for (int i = 0; i < PROBE.Length; i++) {
Assert.AreEqual(sequence.Visit(i), PROBE[i]);
}
// 再检测有数据的插入,分别在头尾和中间进行
var indices = new int[] { 0, PROBE.Length / 2, PROBE.Length - 1, PROBE.Length };
foreach (var index in indices) {
// 清空,一次插入,然后二次插入
sequence.Clear();
sequence.Insert(0, GetCountableProbe());
sequence.Insert(index, GetCountableProbe());
// 用List做正确模拟
var expected = new List<TasFrame>();
expected.AddRange(PROBE);
expected.InsertRange(index, PROBE);
// 检查结果
Assert.AreEqual(sequence.GetCount(), expected.Count);
for (int i = 0; i < expected.Count; i++) {
Assert.AreEqual(sequence.Visit(i), expected[i]);
}
}
}
/// <summary>
/// Remove函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void RemoveTest(ITasSequence sequence) {
// 在空的时候删除0项
sequence.Remove(0, 0);
// 插入项目后尝试在头中尾分别删除
var indices = new int[] { 0, PROBE.Length / 2, PROBE.Length - 1 };
foreach (var index in indices) {
// 清空,插入,删除
sequence.Clear();
sequence.Insert(0, GetCountableProbe());
sequence.Remove(index, 1);
// 用List做正确模拟
var expected = new List<TasFrame>();
expected.AddRange(PROBE);
expected.RemoveRange(index, 1);
// 检查结果
Assert.AreEqual(sequence.GetCount(), expected.Count);
for (int i = 0; i < expected.Count; i++) {
Assert.AreEqual(sequence.Visit(i), expected[i]);
}
}
}
/// <summary>
/// Clear函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void ClearTest(ITasSequence sequence) {
// 设置数据后清空
sequence.Insert(0, GetCountableProbe());
sequence.Clear();
// 检查是否为空
Assert.IsTrue(sequence.IsEmpty());
}
/// <summary>
/// IsEmpty函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void IsEmptyTest(ITasSequence sequence) {
// 检查是否为空
Assert.IsTrue(sequence.IsEmpty());
// 插入数据后再检查
sequence.Insert(0, GetCountableProbe());
Assert.IsFalse(sequence.IsEmpty());
}
/// <summary>
/// GetCount函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void GetCountTest(ITasSequence sequence) {
// 检查长度为0
Assert.AreEqual(sequence.GetCount(), 0);
// 插入数据后再检查
sequence.Insert(0, GetCountableProbe());
Assert.AreEqual(sequence.GetCount(), PROBE.Length);
}
/// <summary>
/// 混合检查VisitClearGetCountIsEmpty。
/// </summary>
/// <param name="sequence"></param>
[TestMethod]
[DynamicData(nameof(TasSequenceInstanceProvider))]
public void HybridTest(ITasSequence sequence) {
// 检查空和大小
Assert.IsTrue(sequence.IsEmpty());
Assert.AreEqual(sequence.GetCount(), 0);
// 设置内容
sequence.Insert(0, GetCountableProbe());
// 并再次检查大小
Assert.IsFalse(sequence.IsEmpty());
Assert.AreEqual(sequence.GetCount(), PROBE.Length);
// 访问数据
var len = PROBE.Length;
for (int i = 0; i < len; ++i) {
Assert.AreEqual(sequence.Visit(i), PROBE[i]);
}
// 清空数据
sequence.Clear();
// 再次检查数据
Assert.IsTrue(sequence.IsEmpty());
Assert.AreEqual(sequence.GetCount(), 0);
// 清空后插入0项然后确认
sequence.Clear();
sequence.Insert(0, GetCountableBlank());
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(-1));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(0));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => sequence.Visit(1));
}
}
}

View File

@@ -1,202 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BallanceTasEditor.Utils;
namespace BallanceTasEditorTests.Utils {
[TestClass]
public class TasStorageTests {
private static readonly int[] BLANK = { };
private static readonly int[] PROBE = { 10, 20, 30, 40, 50 };
private static CountableEnumerable<int> GetCountableProbe() {
return new CountableEnumerable<int>(PROBE);
}
private static CountableEnumerable<int> GetCountableBlank() {
return new CountableEnumerable<int>(BLANK);
}
private static IEnumerable<object[]> TasStorageInstanceProvider {
get {
yield return new object[] { new ListTasStorage<int>() };
yield return new object[] { new LegacyTasStorage<int>() };
// TODO: Add GapBufferTasStorage once we finish it.
//yield return new object[] { new GapBufferTasStorage<int>() };
}
}
/// <summary>
/// Visit函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasStorageInstanceProvider))]
public void VisitTest(ITasStorage<int> storage) {
// 空时访问
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(-1));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(0));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(1));
// 设置数据
storage.Insert(0, GetCountableProbe());
// 访问数据
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(-1));
for (int i = 0; i < PROBE.Length; i++) {
Assert.AreEqual(storage.Visit(i), PROBE[i]);
}
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(PROBE.Length));
}
/// <summary>
/// Insert函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasStorageInstanceProvider))]
public void InsertTest(ITasStorage<int> storage) {
// 需要在不同的存储器上,分别检测在空的时候插入,
// 和在非空时的头,中,尾分别插入的结果。
// 先检测空插入
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Insert(-1, GetCountableProbe()));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Insert(1, GetCountableProbe()));
storage.Insert(0, GetCountableProbe());
for (int i = 0; i < PROBE.Length; i++) {
Assert.AreEqual(storage.Visit(i), PROBE[i]);
}
// 再检测有数据的插入,分别在头尾和中间进行
var indices = new int[] { 0, PROBE.Length / 2, PROBE.Length - 1, PROBE.Length };
foreach (var index in indices) {
// 清空,一次插入,然后二次插入
storage.Clear();
storage.Insert(0, GetCountableProbe());
storage.Insert(index, GetCountableProbe());
// 用List做正确模拟
var expected = new List<int>();
expected.AddRange(PROBE);
expected.InsertRange(index, PROBE);
// 检查结果
Assert.AreEqual(storage.GetCount(), expected.Count);
for (int i = 0; i < expected.Count; i++) {
Assert.AreEqual(storage.Visit(i), expected[i]);
}
}
}
/// <summary>
/// Remove函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasStorageInstanceProvider))]
public void RemoveTest(ITasStorage<int> storage) {
// 在空的时候删除0项
storage.Remove(0, 0);
// 插入项目后尝试在头中尾分别删除
var indices = new int[] { 0, PROBE.Length / 2, PROBE.Length - 1 };
foreach (var index in indices) {
// 清空,插入,删除
storage.Clear();
storage.Insert(0, GetCountableProbe());
storage.Remove(index, 1);
// 用List做正确模拟
var expected = new List<int>();
expected.AddRange(PROBE);
expected.RemoveRange(index, 1);
// 检查结果
Assert.AreEqual(storage.GetCount(), expected.Count);
for (int i = 0; i < expected.Count; i++) {
Assert.AreEqual(storage.Visit(i), expected[i]);
}
}
}
/// <summary>
/// Clear函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasStorageInstanceProvider))]
public void ClearTest(ITasStorage<int> storage) {
// 设置数据后清空
storage.Insert(0, GetCountableProbe());
storage.Clear();
// 检查是否为空
Assert.IsTrue(storage.IsEmpty());
}
/// <summary>
/// IsEmpty函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasStorageInstanceProvider))]
public void IsEmptyTest(ITasStorage<int> storage) {
// 检查是否为空
Assert.IsTrue(storage.IsEmpty());
// 插入数据后再检查
storage.Insert(0, GetCountableProbe());
Assert.IsFalse(storage.IsEmpty());
}
/// <summary>
/// GetCount函数独立测试。
/// </summary>
[TestMethod]
[DynamicData(nameof(TasStorageInstanceProvider))]
public void GetCountTest(ITasStorage<int> storage) {
// 检查长度为0
Assert.AreEqual(storage.GetCount(), 0);
// 插入数据后再检查
storage.Insert(0, GetCountableProbe());
Assert.AreEqual(storage.GetCount(), PROBE.Length);
}
/// <summary>
/// 混合检查VisitClearGetCountIsEmpty。
/// </summary>
/// <param name="storage"></param>
[TestMethod]
[DynamicData(nameof(TasStorageInstanceProvider))]
public void HybridTest(ITasStorage<int> storage) {
// 检查空和大小
Assert.IsTrue(storage.IsEmpty());
Assert.AreEqual(storage.GetCount(), 0);
// 设置内容
storage.Insert(0, GetCountableProbe());
// 并再次检查大小
Assert.IsFalse(storage.IsEmpty());
Assert.AreEqual(storage.GetCount(), PROBE.Length);
// 访问数据
var len = PROBE.Length;
for (int i = 0; i < len; ++i) {
Assert.AreEqual(storage.Visit(i), PROBE[i]);
}
// 清空数据
storage.Clear();
// 再次检查数据
Assert.IsTrue(storage.IsEmpty());
Assert.AreEqual(storage.GetCount(), 0);
// 清空后插入0项然后确认
storage.Clear();
storage.Insert(0, GetCountableBlank());
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(-1));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(0));
AssertExtension.ThrowsDerivedException<ArgumentException>(() => storage.Visit(1));
}
}
}