Compare commits

...

10 Commits

Author SHA1 Message Date
df68c79f28 doc: add deprecation note
- add deprecation note in README.
- archive this repository.
2025-03-03 12:35:05 +08:00
96f61362d3 fix: fix issue that fail to open legal TAS file.
- switch zlib library from zlib.net to DotNetZip because old one can not handle zlib-compressed file properly.
- bump version to 1.2
2024-07-26 23:20:11 +08:00
e8fc96e3ac feat: add version feature
- add version info for Assembly.
- update version to 1.1
- add info textblock in main window for more convenient debug because some users only send a screenshot without reference any version info.
- fix typo
- fix menu dynamic resource link error
2022-05-11 15:08:04 +08:00
5971069e02 Do some release work
* Add Crtl + V for paste after
* Finish english readme
* Add icon designed plaer_2
* Update I18N
2021-09-25 15:18:28 +08:00
b506dafcbd Minor fix
* Fix selection error when use delete after to delete the last tas unit
* Fix sel_rect display error
* Add some documents
* Fix layout becoming confusing when secondly open file.
2021-09-22 13:33:57 +08:00
0534b22501 finish zh document and vertical layout design 2021-09-21 20:17:03 +08:00
352c133620 finish basic i18n and cfg loader 2021-09-20 15:59:54 +08:00
9d8f4b1727 Add more features
* Support full shortcut.
* Fix the crash when open invalid TAS documents.
* Support frag file to open.
2021-09-20 15:01:34 +08:00
1c57481f84 remove TASFlow and re-define it as a assist class 2021-09-20 13:58:10 +08:00
1def4c47f6 init i18n, config. add Cut oper 2021-08-08 12:04:17 +08:00
25 changed files with 1478 additions and 409 deletions

View File

@ -4,6 +4,11 @@
xmlns:local="clr-namespace:BallanceTASEditor"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Language/DefaultLanguage.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
@ -25,6 +26,24 @@ namespace BallanceTASEditor {
}
};
#endif
// init configure manager
GlobalVariable.configManager = new Core.ConfigManager("ballance-tas-editor.cfg", new Dictionary<string, string>() {
{Core.ConfigManager.CfgNode_Language, CultureInfo.CurrentCulture.ThreeLetterWindowsLanguageName},
{Core.ConfigManager.CfgNode_ItemCount, "15"},
{Core.ConfigManager.CfgNode_IsHorizonLayout, "True"},
{Core.ConfigManager.CfgNode_IsOverwrittenPaste, "False"}
});
// init i18n
Core.I18NProcessor.ChangeLanguage(GlobalVariable.configManager.Configuration[Core.ConfigManager.CfgNode_Language]);
}
protected override void OnExit(ExitEventArgs e) {
base.OnExit(e);
GlobalVariable.configManager.Save();
}
private void UncatchedErrorHandle(string message, string stackTrace) {
@ -50,7 +69,10 @@ namespace BallanceTASEditor {
;//skip
}
MessageBox.Show("A fatal error occurs. The application should exit. Please send the error log, reproduce step and corresponding TAS file(is possible) to use to help fixing this problem.", "Ballance TAS Editor", MessageBoxButton.OK, MessageBoxImage.Error);
MessageBox.Show("A fatal error occurs. The application should exit. Please send the error log, reproduce step and corresponding TAS file(if possible) to developer to help fixing this problem.",
"Ballance TAS Editor",
MessageBoxButton.OK, MessageBoxImage.Error
);
App.Current.Shutdown();
}

View File

@ -36,8 +36,17 @@
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Ionic.Zip, Version=1.9.1.8, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c, processorArchitecture=MSIL">
<HintPath>..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
@ -51,9 +60,6 @@
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="zlib.net, Version=1.0.3.0, Culture=neutral, PublicKeyToken=47d7877cb3620160">
<HintPath>..\packages\zlib.net.1.0.4.0\lib\zlib.net.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
@ -61,20 +67,30 @@
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Core\ClipboardUtil.cs" />
<Compile Include="Core\ConfigManager.cs" />
<Compile Include="Core\FileOperation.cs" />
<Compile Include="Core\I18NProcessor.cs" />
<Compile Include="Core\KeyboardState.cs" />
<Compile Include="Core\LimitedStack.cs" />
<Compile Include="GlobalVariable.cs" />
<Compile Include="UI\AddItem.xaml.cs">
<DependentUpon>AddItem.xaml</DependentUpon>
</Compile>
<Compile Include="UI\OperationEnum.cs" />
<Compile Include="UI\SelectionHelp.cs" />
<Compile Include="UI\StyleConverter.cs" />
<Compile Include="UI\TASFlow.xaml.cs">
<DependentUpon>TASFlow.xaml</DependentUpon>
</Compile>
<Compile Include="UI\TASFlow.cs" />
<Compile Include="UI\TASSlider.cs" />
<Compile Include="UI\TASViewer.cs" />
<Compile Include="UI\Util.cs" />
<Page Include="Language\CHS.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Language\DefaultLanguage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -96,10 +112,6 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UI\TASFlow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
@ -126,5 +138,8 @@
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Resource Include="icon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
namespace BallanceTASEditor.Core {
public class ConfigManager {
public ConfigManager(string fileName, Dictionary<string, string> defaultValue) {
_fileName = fileName;
_defaultValue = defaultValue;
Configuration = Read();
}
string _fileName;
Dictionary<string, string> _defaultValue;
public Dictionary<string, string> Configuration;
public static readonly string CfgNode_Language = "Language";
public static readonly string CfgNode_ItemCount = "ItemCount";
public static readonly string CfgNode_IsHorizonLayout = "IsHorizonLayout";
public static readonly string CfgNode_IsOverwrittenPaste = "IsOverwrittenPaste";
Dictionary<string, string> Read() {
if (!File.Exists(Path.Combine(Environment.CurrentDirectory, _fileName)))
Init();
Dictionary<string, string> data;
using (StreamReader fs = new StreamReader(Path.Combine(Environment.CurrentDirectory, _fileName), Encoding.UTF8)) {
data = JsonConvert.DeserializeObject<Dictionary<string, string>>(fs.ReadToEnd());
fs.Close();
}
// check field to make sure each field is existed
// because version update it might be changed
foreach(var pair in _defaultValue) {
if (!data.ContainsKey(pair.Key)) {
data.Add(pair.Key, pair.Value);
}
}
return data;
}
void Init() {
using (StreamWriter fs = new StreamWriter(Path.Combine(Environment.CurrentDirectory, _fileName), false, Encoding.UTF8)) {
fs.Write(JsonConvert.SerializeObject(_defaultValue));
fs.Close();
}
}
public void Save() {
using (StreamWriter fs = new StreamWriter(Path.Combine(Environment.CurrentDirectory, _fileName), false, Encoding.UTF8)) {
fs.Write(JsonConvert.SerializeObject(this.Configuration));
fs.Close();
}
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace BallanceTASEditor.Core {
public static class I18NProcessor {
public static string GetI18N(string key, params string[] parameters) {
try {
var cache = (string)(App.Current.Resources[key]);
return string.Format(cache, parameters);
} catch (Exception) {
return "";
}
}
public static void ChangeLanguage(string target) {
ResourceDictionary langRd = null;
try {
langRd =
Application.LoadComponent(
new Uri(@"Language/" + target + ".xaml", UriKind.Relative))
as ResourceDictionary;
} catch {
;
}
if (langRd != null) {
if (App.Current.Resources.MergedDictionaries.Count > 0) {
App.Current.Resources.MergedDictionaries.Clear();
}
App.Current.Resources.MergedDictionaries.Add(langRd);
}
}
}
}

View File

@ -38,7 +38,8 @@ namespace BallanceTASEditor.Core {
}
public long GetPointerIndex() {
if (mPointer == null) throw new Exception("Data is not ready");
// return invalid data to prevent error
if (mPointer == null) return -1;//throw new Exception("Data is not ready");
return mPointerIndex;
}

View File

@ -1,4 +1,4 @@
using BallanceTASEditor.Core.TASStruct;
using BallanceTASEditor.Core.TASStruct;
using System;
using System.Collections.Generic;
using System.IO;
@ -12,34 +12,63 @@ namespace BallanceTASEditor.Core {
public static void CompressTAS(LinkedList<FrameData> mem, FileStream file) {
file.Write(BitConverter.GetBytes(mem.Count * ConstValue.FRAMEDATA_SIZE), 0, 4);
var zo = new zlib.ZOutputStream(file, 9);
using (var zo = new Ionic.Zlib.ZlibStream(file, Ionic.Zlib.CompressionMode.Compress, Ionic.Zlib.CompressionLevel.Level9, true)) {
var node = mem.First;
while (node != null) {
zo.Write(BitConverter.GetBytes(node.Value.deltaTime), 0, 4);
zo.Write(BitConverter.GetBytes(node.Value.keystates), 0, 4);
node = node.Next;
}
zo.finish();
zo.Close();
}
//var zo = new zlib.ZOutputStream(file, 9);
//var node = mem.First;
//while (node != null) {
// zo.Write(BitConverter.GetBytes(node.Value.deltaTime), 0, 4);
// zo.Write(BitConverter.GetBytes(node.Value.keystates), 0, 4);
// node = node.Next;
//}
//zo.finish();
//zo.Close();
}
public static void DecompressTAS(LinkedList<FrameData> ls, FileStream file) {
var lengthTemp = new byte[4];
var mem = new MemoryStream();
file.Read(lengthTemp, 0, 4);
Int32 expectedLength = BitConverter.ToInt32(lengthTemp, 0);
long expectedCount = expectedLength / ConstValue.FRAMEDATA_SIZE;
var zo = new zlib.ZOutputStream(mem);
using (var mem = new MemoryStream()) {
using (var zo = new Ionic.Zlib.ZlibStream(mem, Ionic.Zlib.CompressionMode.Decompress, true)) {
CopyStream(file, zo);
zo.finish();
zo.Close();
}
mem.Seek(0, SeekOrigin.Begin);
for(long i = 0; i < expectedCount; i++) {
for (long i = 0; i < expectedCount; i++) {
ls.AddLast(new FrameData(mem));
}
mem.Close();
zo.Close();
}
//mem.Seek(0, SeekOrigin.Begin);
//for (long i = 0; i < expectedCount; i++) {
// ls.AddLast(new FrameData(mem));
//}
//mem.Close();
//zo.Close();
//var zo = new zlib.ZOutputStream(mem);
//CopyStream(file, zo);
//zo.finish();
//mem.Seek(0, SeekOrigin.Begin);
//for (long i = 0; i < expectedCount; i++) {
// ls.AddLast(new FrameData(mem));
//}
//mem.Close();
//zo.Close();
}
public static void CopyStream(Stream origin, Stream target) {

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTASEditor {
public class GlobalVariable {
public static Core.ConfigManager configManager;
}
}

View File

@ -0,0 +1,77 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BallanceTASEditor.Language"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<!-- ui data-->
<sys:String x:Key="ui_MainWindow_Title">Ballance TAS 编辑器</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File">文件</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_Open">打开</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_Save">保存</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_SaveAs">另存为...</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_Close">关闭</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display">编辑</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_Undo">撤销</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_Redo">重做</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_ItemCount">项个数</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_OverwrittenPaste">覆盖式粘贴</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_HorizontalLayout">横向布局</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Help">帮助</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Help_ReportBugs">汇报漏洞</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Help_About">关于</sys:String>
<sys:String x:Key="ui_MainWindow_EditorNote">打开或拖入一个 TAS 文件以编辑</sys:String>
<sys:String x:Key="ui_MainWindow_Tools_Cursor">选择模式</sys:String>
<sys:String x:Key="ui_MainWindow_Tools_Fill">填充模式</sys:String>
<sys:String x:Key="ui_MainWindow_Tools_Overwritten">画笔模式</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_Mode_Cursor">选择模式</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_Mode_Fill">填充模式</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_Mode_Overwritten">画笔模式</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_OverwrittenPaste">覆盖式粘贴</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_Selected">已选择:</sys:String>
<sys:String x:Key="ui_TASFlow_Sheet_Frame">帧</sys:String>
<sys:String x:Key="ui_TASFlow_Sheet_DeltaTime">间隔时间</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Set">设置</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Unset">不设置</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Cut">剪切</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Copy">复制</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_PasteAfter">粘贴于后方</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_PasteBefore">粘贴于前方</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Delete">删除</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_DeleteAfter">向后删除</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_DeleteBefore">向前删除</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_AddAfter">在后方添加新项</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_AddBefore">在前方添加新项</sys:String>
<sys:String x:Key="ui_AddItem_Title">添加项</sys:String>
<sys:String x:Key="ui_AddItem_Count">个数</sys:String>
<sys:String x:Key="ui_AddItem_FPS">帧率</sys:String>
<sys:String x:Key="ui_AddItem_DeltaTime">间隔时间</sys:String>
<sys:String x:Key="ui_AddItem_OK">好</sys:String>
<sys:String x:Key="ui_AddItem_Cancel">取消</sys:String>
<!-- code data-->
<sys:String x:Key="code_Shared_ProgramName">Ballance TAS 编辑器</sys:String>
<sys:String x:Key="code_MainWindow_Menu_Help_About" xml:space="preserve">基于 MIT 开源许可证发布
版本1.2 stable
程序yyc12345.
图标设计plAer_2</sys:String>
<sys:String x:Key="code_MainWindow_Closing">文件未关闭。您想直接退出吗?所有修改不会被保存。</sys:String>
<sys:String x:Key="code_MainWindow_Menu_File_Close">您想要关闭这个TAS文件吗</sys:String>
<sys:String x:Key="code_MainWindow_Menu_File_Open_Fail">无法打开文件文件可能不是合法的TAS文件。</sys:String>
<sys:String x:Key="code_MainWindow_Menu_Display_ItemCount">输入新的数量(&lt;=5 &amp;&amp; &gt;=30</sys:String>
<sys:String x:Key="code_DialogUtil_Warning">警告</sys:String>
<sys:String x:Key="code_DialogUtil_FileFilter">TAS 文件(*.tas)|*.tas|所有文件(*.*)|*.*</sys:String>
<sys:String x:Key="code_DialogUtil_InputNumber_Title">输入数字</sys:String>
<sys:String x:Key="code_DialogUtil_InputNumber_Wrong">无效的数字,请再试一次。</sys:String>
</ResourceDictionary>

View File

@ -0,0 +1,77 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BallanceTASEditor.Language"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<!-- ui data-->
<sys:String x:Key="ui_MainWindow_Title">Ballance TAS Editor</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File">File</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_Open">Open</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_Save">Save</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_SaveAs">Save As ...</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_File_Close">Close</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display">Edit</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_Undo">Undo</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_Redo">Redo</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_ItemCount">Item Count</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_OverwrittenPaste">Overwritten Paste</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Display_HorizontalLayout">Horizontal Layout</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Help">Help</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Help_ReportBugs">Report Bugs</sys:String>
<sys:String x:Key="ui_MainWindow_Menu_Help_About">About</sys:String>
<sys:String x:Key="ui_MainWindow_EditorNote">Open or drop a TAS file for editing</sys:String>
<sys:String x:Key="ui_MainWindow_Tools_Cursor">Select mode</sys:String>
<sys:String x:Key="ui_MainWindow_Tools_Fill">Fill mode</sys:String>
<sys:String x:Key="ui_MainWindow_Tools_Overwritten">Draw mode</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_Mode_Cursor">Select mode</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_Mode_Fill">Fill mode</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_Mode_Overwritten">Draw mode</sys:String>
<sys:String x:Key="ui_MainWindow_StatusBar_OverwrittenPaste">Overwritten paste</sys:String>
<sys:String xml:space="preserve" x:Key="ui_MainWindow_StatusBar_Selected">Selected: </sys:String>
<sys:String x:Key="ui_TASFlow_Sheet_Frame">Frame</sys:String>
<sys:String x:Key="ui_TASFlow_Sheet_DeltaTime">Delta Time</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Set">Set</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Unset">Unset</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Cut">Cut</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Copy">Copy</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_PasteAfter">Paste after this</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_PasteBefore">Paste before this</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_Delete">Delete</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_DeleteAfter">Delete this frame</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_DeleteBefore">Delete last frame</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_AddAfter">Add blank item after this</sys:String>
<sys:String x:Key="ui_TASFlow_Menu_AddBefore">Add blank item before this</sys:String>
<sys:String x:Key="ui_AddItem_Title">Add Item</sys:String>
<sys:String x:Key="ui_AddItem_Count">Count</sys:String>
<sys:String x:Key="ui_AddItem_FPS">FPS</sys:String>
<sys:String x:Key="ui_AddItem_DeltaTime">Delta Time</sys:String>
<sys:String x:Key="ui_AddItem_OK">OK</sys:String>
<sys:String x:Key="ui_AddItem_Cancel">Cancel</sys:String>
<!-- code data-->
<sys:String x:Key="code_Shared_ProgramName">Ballance TAS Editor</sys:String>
<sys:String x:Key="code_MainWindow_Menu_Help_About" xml:space="preserve">Under MIT License
Version: 1.2 stable
Program: yyc12345.
Icon design: plAer_2</sys:String>
<sys:String x:Key="code_MainWindow_Closing">File is not closed. Do you want to just quit? All changes will not be saved.</sys:String>
<sys:String x:Key="code_MainWindow_Menu_File_Close">Do you want to close this TAS file?</sys:String>
<sys:String x:Key="code_MainWindow_Menu_File_Open_Fail">Fail to open file. This file might not a legal TAS file.</sys:String>
<sys:String x:Key="code_MainWindow_Menu_Display_ItemCount">Input new count (&lt;=5 &amp;&amp; &gt;=30)</sys:String>
<sys:String x:Key="code_DialogUtil_Warning">Warning</sys:String>
<sys:String x:Key="code_DialogUtil_FileFilter">TAS file(*.tas)|*.tas|All file(*.*)|*.*</sys:String>
<sys:String x:Key="code_DialogUtil_InputNumber_Title">Input number</sys:String>
<sys:String x:Key="code_DialogUtil_InputNumber_Wrong">Invalid number. Please input again.</sys:String>
</ResourceDictionary>

View File

@ -1,4 +1,4 @@
<Window x:Class="BallanceTASEditor.MainWindow"
<Window x:Class="BallanceTASEditor.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"
@ -7,9 +7,47 @@
xmlns:controls="clr-namespace:BallanceTASEditor.UI"
xmlns:input="clr-namespace:System.Windows.Input;assembly=PresentationCore"
mc:Ignorable="d"
Title="Ballance TAS Editor" Height="500" Width="800" KeyUp="funcWindow_KeyUp"
input:InputMethod.IsInputMethodEnabled="False">
Title="{DynamicResource ui_MainWindow_Title}" Height="500" Width="800" KeyUp="funcWindow_KeyUp"
input:InputMethod.IsInputMethodEnabled="False" MouseWheel="funcWindow_MouseWheel" Icon="icon.ico" Closing="funcWindow_Closing">
<!-- shortcut defination-->
<Window.Resources>
<RoutedUICommand x:Key="commandMenu_File_Open"/>
<RoutedUICommand x:Key="commandMenu_File_Save"/>
<RoutedUICommand x:Key="commandMenu_Display_Undo"/>
<RoutedUICommand x:Key="commandMenu_Display_Redo"/>
<RoutedUICommand x:Key="commandDataMenu_Cut"/>
<RoutedUICommand x:Key="commandDataMenu_Copy"/>
<RoutedUICommand x:Key="commandDataMenu_PasteAfter"/>
<RoutedUICommand x:Key="commandDataMenu_DeleteAfter"/>
<RoutedUICommand x:Key="commandDataMenu_DeleteBefore"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Gesture="Ctrl+O" Command="{StaticResource commandMenu_File_Open}"/>
<KeyBinding Gesture="Ctrl+S" Command="{StaticResource commandMenu_File_Save}"/>
<KeyBinding Gesture="Ctrl+Z" Command="{StaticResource commandMenu_Display_Undo}"/>
<KeyBinding Gesture="Ctrl+Y" Command="{StaticResource commandMenu_Display_Redo}"/>
<KeyBinding Gesture="Ctrl+X" Command="{StaticResource commandDataMenu_Cut}"/>
<KeyBinding Gesture="Ctrl+C" Command="{StaticResource commandDataMenu_Copy}"/>
<KeyBinding Gesture="Ctrl+V" Command="{StaticResource commandDataMenu_PasteAfter}"/>
<KeyBinding Gesture="Delete" Command="{StaticResource commandDataMenu_DeleteAfter}"/>
<KeyBinding Gesture="Back" Command="{StaticResource commandDataMenu_DeleteBefore}"/>
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource commandMenu_File_Open}" Executed="funcCommand_Menu_File_Open" CanExecute="funcCanExeCmd_Menu_File_Open"/>
<CommandBinding Command="{StaticResource commandMenu_File_Save}" Executed="funcCommand_Menu_File_Save" CanExecute="funcCanExeCmd_Menu_File_Save"/>
<CommandBinding Command="{StaticResource commandMenu_Display_Undo}" Executed="funcCommand_Menu_Display_Undo" CanExecute="funcCanExeCmd_Menu_Display_Undo"/>
<CommandBinding Command="{StaticResource commandMenu_Display_Redo}" Executed="funcCommand_Menu_Display_Redo" CanExecute="funcCanExeCmd_Menu_Display_Redo"/>
<CommandBinding Command="{StaticResource commandDataMenu_Cut}" Executed="funcCommand_DataMenu_Cut" CanExecute="funcCanExeCmd_DataMenu_Cut"/>
<CommandBinding Command="{StaticResource commandDataMenu_Copy}" Executed="funcCommand_DataMenu_Copy" CanExecute="funcCanExeCmd_DataMenu_Copy"/>
<CommandBinding Command="{StaticResource commandDataMenu_PasteAfter}" Executed="funcCommand_DataMenu_PasteAfter" CanExecute="funcCanExeCmd_DataMenu_PasteAfter"/>
<CommandBinding Command="{StaticResource commandDataMenu_DeleteAfter}" Executed="funcCommand_DataMenu_DeleteAfter" CanExecute="funcCanExeCmd_DataMenu_DeleteAfter"/>
<CommandBinding Command="{StaticResource commandDataMenu_DeleteBefore}" Executed="funcCommand_DataMenu_DeleteBefore" CanExecute="funcCanExeCmd_DataMenu_DeleteBefore"/>
</Window.CommandBindings>
<!-- real ui elements-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
@ -18,31 +56,44 @@
</Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Header="File">
<MenuItem x:Name="uiMenu_File_Open" Header="Open" Click="funcMenu_File_Open"/>
<MenuItem x:Name="uiMenu_File_Save" Header="Save" Click="funcMenu_File_Save"/>
<MenuItem x:Name="uiMenu_File_SaveAs" Header="Save As..." Click="funcMenu_File_SaveAs"/>
<MenuItem x:Name="uiMenu_File_Close" Header="Close" Click="funcMenu_File_Close"/>
<MenuItem Header="{DynamicResource ui_MainWindow_Menu_File}">
<MenuItem x:Name="uiMenu_File_Open" Header="{DynamicResource ui_MainWindow_Menu_File_Open}" Click="funcMenu_File_Open" InputGestureText="Ctrl + O"/>
<MenuItem x:Name="uiMenu_File_Save" Header="{DynamicResource ui_MainWindow_Menu_File_Save}" Click="funcMenu_File_Save" InputGestureText="Ctrl + S"/>
<MenuItem x:Name="uiMenu_File_SaveAs" Header="{DynamicResource ui_MainWindow_Menu_File_SaveAs}" Click="funcMenu_File_SaveAs"/>
<MenuItem x:Name="uiMenu_File_Close" Header="{DynamicResource ui_MainWindow_Menu_File_Close}" Click="funcMenu_File_Close"/>
</MenuItem>
<MenuItem Header="Display">
<MenuItem x:Name="uiMenu_Display_Undo" Header="Undo" Click="funcMenu_Display_Undo"/>
<MenuItem x:Name="uiMenu_Display_Redo" Header="Redo" Click="funcMenu_Display_Redo"/>
<MenuItem x:Name="uiMenu_Display_ItemCount" Header="Item Count" Click="funcMenu_Display_ItemCount"/>
<MenuItem x:Name="uiMenu_Display_OverwrittenPaste" Header="Overwritten Paste" IsCheckable="True" Checked="funcMenu_Display_OverwrittenPaste" Unchecked="funcMenu_Display_OverwrittenPaste"/>
<MenuItem Header="{DynamicResource ui_MainWindow_Menu_Display}">
<MenuItem x:Name="uiMenu_Display_Undo" Header="{DynamicResource ui_MainWindow_Menu_Display_Undo}" Click="funcMenu_Display_Undo" InputGestureText="Ctrl + Z"/>
<MenuItem x:Name="uiMenu_Display_Redo" Header="{DynamicResource ui_MainWindow_Menu_Display_Redo}" Click="funcMenu_Display_Redo" InputGestureText="Ctrl + Y"/>
<Separator/>
<MenuItem x:Name="uiMenu_Display_ItemCount" Header="{DynamicResource ui_MainWindow_Menu_Display_ItemCount}" Click="funcMenu_Display_ItemCount"/>
<Separator/>
<MenuItem x:Name="uiMenu_Display_OverwrittenPaste" Header="{DynamicResource ui_MainWindow_Menu_Display_OverwrittenPaste}" Click="funcMenu_Display_OverwrittenPaste"/>
<MenuItem x:Name="uiMenu_Display_HorizontalLayout" Header="{DynamicResource ui_MainWindow_Menu_Display_HorizontalLayout}" Click="funcMenu_Display_HorizontalLayout"/>
</MenuItem>
<MenuItem Header="Help">
<MenuItem x:Name="uiMenu_Help_ReportBugs" Header="Report bugs" Click="funcMenu_Help_ReportBugs"/>
<MenuItem x:Name="uiMenu_Help_About" Header="About" Click="funcMenu_Help_About"/>
<MenuItem Header="{DynamicResource ui_MainWindow_Menu_Help}">
<MenuItem x:Name="uiMenu_Help_ReportBugs" Header="{DynamicResource ui_MainWindow_Menu_Help_ReportBugs}" Click="funcMenu_Help_ReportBugs"/>
<MenuItem x:Name="uiMenu_Help_About" Header="{DynamicResource ui_MainWindow_Menu_Help_About}" Click="funcMenu_Help_About"/>
</MenuItem>
</Menu>
<TextBlock x:Name="uiEditorNote" Grid.Row="1" Text="Open a TAS file for editing" Opacity="0.5" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16"/>
<Grid x:Name="uiEditorNote" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" AllowDrop="True" Drop="funcDrop_Drop" DragEnter="funcDrop_DragEnter" Visibility="Collapsed">
<Rectangle StrokeThickness="4" Stroke="Gray" StrokeDashArray="4 4" Fill="#ffffff"/>
<StackPanel Orientation="Horizontal">
<Viewbox Width="24" Height="24" Margin="20, 20, 0, 20" VerticalAlignment="Center">
<Canvas Width="24" Height="24">
<Path Fill="Gray" Data="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
</Canvas>
</Viewbox>
<TextBlock Margin="20" Text="{DynamicResource ui_MainWindow_EditorNote}" Foreground="Gray" FontSize="16" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
<Grid x:Name="uiEditorPanel" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.ColumnSpan="2">
@ -53,7 +104,7 @@
<Path Fill="Black" Data="M10.07,14.27C10.57,14.03 11.16,14.25 11.4,14.75L13.7,19.74L15.5,18.89L13.19,13.91C12.95,13.41 13.17,12.81 13.67,12.58L13.95,12.5L16.25,12.05L8,5.12V15.9L9.82,14.43L10.07,14.27M13.64,21.97C13.14,22.21 12.54,22 12.31,21.5L10.13,16.76L7.62,18.78C7.45,18.92 7.24,19 7,19A1,1 0 0,1 6,18V3A1,1 0 0,1 7,2C7.24,2 7.47,2.09 7.64,2.23L7.65,2.22L19.14,11.86C19.57,12.22 19.62,12.85 19.27,13.27C19.12,13.45 18.91,13.57 18.7,13.61L15.54,14.23L17.74,18.96C18,19.46 17.76,20.05 17.26,20.28L13.64,21.97Z" />
</Canvas>
</Viewbox>
<TextBlock Text="Select mode" VerticalAlignment="Center"/>
<TextBlock Text="{DynamicResource ui_MainWindow_Tools_Cursor}" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button x:Name="uiBtn_Fill" Margin="5" Padding="5" Click="funcBtn_Fill">
@ -63,7 +114,7 @@
<Path Fill="Black" Data="M19,11.5C19,11.5 17,13.67 17,15A2,2 0 0,0 19,17A2,2 0 0,0 21,15C21,13.67 19,11.5 19,11.5M5.21,10L10,5.21L14.79,10M16.56,8.94L7.62,0L6.21,1.41L8.59,3.79L3.44,8.94C2.85,9.5 2.85,10.47 3.44,11.06L8.94,16.56C9.23,16.85 9.62,17 10,17C10.38,17 10.77,16.85 11.06,16.56L16.56,11.06C17.15,10.47 17.15,9.5 16.56,8.94Z" />
</Canvas>
</Viewbox>
<TextBlock Text="Fill mode" VerticalAlignment="Center"/>
<TextBlock Text="{DynamicResource ui_MainWindow_Tools_Fill}" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<Button x:Name="uiBtn_Overwrite" Margin="5" Padding="5" Click="funcBtn_Overwrite">
@ -73,15 +124,72 @@
<Path Fill="Black" Data="M18.62,1.5C18.11,1.5 17.6,1.69 17.21,2.09L10.75,8.55L14.95,12.74L21.41,6.29C22.2,5.5 22.2,4.24 21.41,3.46L20.04,2.09C19.65,1.69 19.14,1.5 18.62,1.5M9.8,9.5L3.23,16.07L3.93,16.77C3.4,17.24 2.89,17.78 2.38,18.29C1.6,19.08 1.6,20.34 2.38,21.12C3.16,21.9 4.42,21.9 5.21,21.12C5.72,20.63 6.25,20.08 6.73,19.58L7.43,20.27L14,13.7" />
</Canvas>
</Viewbox>
<TextBlock Text="Draw mode" VerticalAlignment="Center"/>
<TextBlock Text="{DynamicResource ui_MainWindow_Tools_Overwritten}" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</StackPanel>
<controls:TASFlow x:Name="uiTASData" Grid.Row="1" Margin="5" BorderThickness="1" BorderBrush="Gray">
</controls:TASFlow>
<Grid x:Name="uiLayoutContainer" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="2" Margin="5">
<Border x:Name="uiTASFlowContainer" BorderThickness="1" BorderBrush="Gray" Grid.Row="0" Margin="5" Background="#ffffff">
<Grid x:Name="uiTASData">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem x:Name="uiDataMenu_Set" Header="{DynamicResource ui_TASFlow_Menu_Set}" Click="funcDataMenu_Set"/>
<MenuItem x:Name="uiDataMenu_Unset" Header="{DynamicResource ui_TASFlow_Menu_Unset}" Click="funcDataMenu_Unset"/>
<Separator/>
<MenuItem x:Name="uiDataMenu_Cut" Header="{DynamicResource ui_TASFlow_Menu_Cut}" Click="funcDataMenu_Cut" InputGestureText="Ctrl + X"/>
<MenuItem x:Name="uiDataMenu_Copy" Header="{DynamicResource ui_TASFlow_Menu_Copy}" Click="funcDataMenu_Copy" InputGestureText="Ctrl + C"/>
<MenuItem x:Name="uiDataMenu_PasteAfter" Header="{DynamicResource ui_TASFlow_Menu_PasteAfter}" Click="funcDataMenu_PasteAfter" InputGestureText="Ctrl + V"/>
<MenuItem x:Name="uiDataMenu_PasteBefore" Header="{DynamicResource ui_TASFlow_Menu_PasteBefore}" Click="funcDataMenu_PasteBefore"/>
<Separator/>
<MenuItem x:Name="uiDataMenu_Delete" Header="{DynamicResource ui_TASFlow_Menu_Delete}" Click="funcDataMenu_Delete"/>
<MenuItem x:Name="uiDataMenu_DeleteAfter" Header="{DynamicResource ui_TASFlow_Menu_DeleteAfter}" Click="funcDataMenu_DeleteAfter" InputGestureText="Del"/>
<MenuItem x:Name="uiDataMenu_DeleteBefore" Header="{DynamicResource ui_TASFlow_Menu_DeleteBefore}" Click="funcDataMenu_DeleteBefore" InputGestureText="Backspace"/>
<Separator/>
<MenuItem x:Name="uiDataMenu_AddAfter" Header="{DynamicResource ui_TASFlow_Menu_AddAfter}" Click="funcDataMenu_AddAfter"/>
<MenuItem x:Name="uiDataMenu_AddBefore" Header="{DynamicResource ui_TASFlow_Menu_AddBefore}" Click="funcDataMenu_AddBefore"/>
</ContextMenu>
</Grid.ContextMenu>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="uiFlowHeader_Frame" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="1" Text="{DynamicResource ui_TASFlow_Sheet_Frame}"/>
<TextBlock x:Name="uiFlowHeader_DeltaTime" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="2" Text="{DynamicResource ui_TASFlow_Sheet_DeltaTime}"/>
<TextBlock x:Name="uiFlowHeader_Up" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="3" Text="^"/>
<TextBlock x:Name="uiFlowHeader_Down" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="4" Text="v"/>
<TextBlock x:Name="uiFlowHeader_Left" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="5" Text="&lt;"/>
<TextBlock x:Name="uiFlowHeader_Right" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="6" Text="&gt;"/>
<TextBlock x:Name="uiFlowHeader_Shift" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="7" Text="shift"/>
<TextBlock x:Name="uiFlowHeader_Space" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="8" Text="space"/>
<TextBlock x:Name="uiFlowHeader_Q" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="9" Text="q"/>
<TextBlock x:Name="uiFlowHeader_Esc" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="10" Text="esc"/>
<TextBlock x:Name="uiFlowHeader_Enter" Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="11" Text="enter"/>
</Grid>
</Border>
<Grid x:Name="uiTASSliderContainer" Grid.Row="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
@ -90,14 +198,14 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="uiBtn_FastMovePrev" Grid.Column="0" Margin="2" HorizontalAlignment="Center" Click="funcBtn_FastMovePrev">
<Button x:Name="uiBtn_FastMovePrev" Grid.Column="0" Margin="2" HorizontalAlignment="Center" Click="funcBtn_FastMovePrev" RenderTransformOrigin="0.5, 0.5">
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
<Path Fill="Black" Data="M11.5,12L20,18V6M11,18V6L2.5,12L11,18Z" />
</Canvas>
</Viewbox>
</Button>
<Button x:Name="uiBtn_MovePrev" Grid.Column="1" Margin="2" HorizontalAlignment="Center" Click="funcBtn_MovePrev">
<Button x:Name="uiBtn_MovePrev" Grid.Column="1" Margin="2" HorizontalAlignment="Center" Click="funcBtn_MovePrev" RenderTransformOrigin="0.5, 0.5">
<Viewbox Width="16" Height="16" RenderTransformOrigin="0.5,0.5">
<Viewbox.RenderTransform>
<RotateTransform Angle="-90"/>
@ -107,7 +215,7 @@
</Canvas>
</Viewbox>
</Button>
<Button x:Name="uiBtn_MoveNext" Grid.Column="2" Margin="2" HorizontalAlignment="Center" Click="funcBtn_MoveNext">
<Button x:Name="uiBtn_MoveNext" Grid.Column="2" Margin="2" HorizontalAlignment="Center" Click="funcBtn_MoveNext" RenderTransformOrigin="0.5, 0.5">
<Viewbox Width="16" Height="16" RenderTransformOrigin="0.5,0.5">
<Viewbox.RenderTransform>
<RotateTransform Angle="-90"/>
@ -117,28 +225,45 @@
</Canvas>
</Viewbox>
</Button>
<Button x:Name="uiBtn_FastMoveNext" Grid.Column="3" Margin="2" HorizontalAlignment="Center" Click="funcBtn_FastMoveNext">
<Button x:Name="uiBtn_FastMoveNext" Grid.Column="3" Margin="2" HorizontalAlignment="Center" Click="funcBtn_FastMoveNext" RenderTransformOrigin="0.5, 0.5">
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
<Path Fill="Black" Data="M13,6V18L21.5,12M4,18L12.5,12L4,6V18Z" />
</Canvas>
</Viewbox>
</Button>
<Slider x:Name="uiTASSlider" Margin="5,0,0,0" Grid.Column="4" SmallChange="1" LargeChange="10" Maximum="1" VerticalAlignment="Center">
<Slider x:Name="uiTASSlider" Margin="2" Grid.Column="4" SmallChange="1" LargeChange="10" Maximum="1" VerticalAlignment="Center" RenderTransformOrigin="0.5, 0.5">
</Slider>
</Grid>
</Grid>
</Grid>
<StatusBar x:Name="uiStatusbar" Grid.Row="2">
<TextBlock x:Name="uiStatusbar_Mode_Cursor" Text="Select mode"/>
<TextBlock x:Name="uiStatusbar_Mode_Fill" Text="Fill mode"/>
<TextBlock x:Name="uiStatusbar_Mode_Overwrite" Text="Draw mode"/>
<Separator/>
<TextBlock Text="Selected: "/>
<StatusBarItem x:Name="uiStatusbar_Runtime_Mode">
<Grid>
<TextBlock x:Name="uiStatusbar_Mode_Cursor" Text="{DynamicResource ui_MainWindow_StatusBar_Mode_Cursor}"/>
<TextBlock x:Name="uiStatusbar_Mode_Fill" Text="{DynamicResource ui_MainWindow_StatusBar_Mode_Fill}"/>
<TextBlock x:Name="uiStatusbar_Mode_Overwrite" Text="{DynamicResource ui_MainWindow_StatusBar_Mode_Overwritten}"/>
</Grid>
</StatusBarItem>
<Separator x:Name="uiStatusbar_Runtime_Separator1"/>
<StatusBarItem x:Name="uiStatusbar_Runtime_PasteMode">
<TextBlock x:Name="uiStatusbar_OverwrittenPaste" Text="{DynamicResource ui_MainWindow_StatusBar_OverwrittenPaste}"/>
</StatusBarItem>
<Separator x:Name="uiStatusbar_Runtime_Separator2"/>
<StatusBarItem x:Name="uiStatusbar_Runtime_Selected">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{DynamicResource ui_MainWindow_StatusBar_Selected}"/>
<TextBlock x:Name="uiStatusbar_Selected" Text="-"/>
</StackPanel>
</StatusBarItem>
<StatusBarItem DockPanel.Dock="Right" HorizontalAlignment="Right">
<TextBlock Text="v1.2 stable" Foreground="Gray" FontStyle="Italic"/>
</StatusBarItem>
</StatusBar>
</Grid>

View File

@ -1,4 +1,4 @@
using BallanceTASEditor.Core;
using BallanceTASEditor.Core;
using BallanceTASEditor.Core.TASStruct;
using BallanceTASEditor.UI;
using System;
@ -22,31 +22,83 @@ namespace BallanceTASEditor {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
mIsHorizontalLayout = true;
// init layout controller
var headers = new List<TextBlock>();
headers.Add(uiFlowHeader_Frame);
headers.Add(uiFlowHeader_DeltaTime);
headers.Add(uiFlowHeader_Up);
headers.Add(uiFlowHeader_Down);
headers.Add(uiFlowHeader_Left);
headers.Add(uiFlowHeader_Right);
headers.Add(uiFlowHeader_Shift);
headers.Add(uiFlowHeader_Space);
headers.Add(uiFlowHeader_Q);
headers.Add(uiFlowHeader_Esc);
headers.Add(uiFlowHeader_Enter);
mFlow = new TASFlow(uiTASData, headers.ToArray());
var components = new TASSliderComponents();
components.container = uiTASSliderContainer;
components.btnFastPrev = uiBtn_FastMovePrev;
components.btnPrev = uiBtn_MovePrev;
components.btnNext = uiBtn_MoveNext;
components.btnFastNext = uiBtn_FastMoveNext;
components.mSlider = uiTASSlider;
mSlider = new TASSlider(components); ;
// refresh ui and load cfg
RefreshUI(false);
ApplyConfigureManager();
}
TASFile mFile;
TASViewer mViewer;
TASFlow mFlow;
TASSlider mSlider;
bool mIsHorizontalLayout;
#region ui func
// shortcut
private void funcCommand_Menu_File_Open(object sender, ExecutedRoutedEventArgs e) => funcMenu_File_Open(sender, e);
private void funcCommand_Menu_File_Save(object sender, ExecutedRoutedEventArgs e) => funcMenu_File_Save(sender, e);
private void funcCommand_Menu_Display_Undo(object sender, ExecutedRoutedEventArgs e) => funcMenu_Display_Undo(sender, e);
private void funcCommand_Menu_Display_Redo(object sender, ExecutedRoutedEventArgs e) => funcMenu_Display_Redo(sender, e);
private void funcCommand_DataMenu_Cut(object sender, ExecutedRoutedEventArgs e) => funcDataMenu_Cut(sender, e);
private void funcCommand_DataMenu_Copy(object sender, ExecutedRoutedEventArgs e) => funcDataMenu_Copy(sender, e);
private void funcCommand_DataMenu_PasteAfter(object sender, ExecutedRoutedEventArgs e) => funcDataMenu_PasteAfter(sender, e);
private void funcCommand_DataMenu_DeleteAfter(object sender, ExecutedRoutedEventArgs e) => funcDataMenu_DeleteAfter(sender, e);
private void funcCommand_DataMenu_DeleteBefore(object sender, ExecutedRoutedEventArgs e) => funcDataMenu_DeleteBefore(sender, e);
private void funcCanExeCmd_Menu_File_Open(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = uiMenu_File_Open.IsEnabled;
private void funcCanExeCmd_Menu_File_Save(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = uiMenu_File_Save.IsEnabled;
private void funcCanExeCmd_Menu_Display_Undo(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = uiMenu_Display_Undo.IsEnabled;
private void funcCanExeCmd_Menu_Display_Redo(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = uiMenu_Display_Redo.IsEnabled;
private void funcCanExeCmd_DataMenu_Cut(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = mViewer != null && uiDataMenu_Cut.IsEnabled;
private void funcCanExeCmd_DataMenu_Copy(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = mViewer != null && uiDataMenu_Copy.IsEnabled;
private void funcCanExeCmd_DataMenu_PasteAfter(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = mViewer != null && uiDataMenu_PasteAfter.IsEnabled;
private void funcCanExeCmd_DataMenu_DeleteAfter(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = mViewer != null && uiDataMenu_DeleteAfter.IsEnabled;
private void funcCanExeCmd_DataMenu_DeleteBefore(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = mViewer != null && uiDataMenu_DeleteBefore.IsEnabled;
// =========================== menu
#region window menu
private void funcMenu_Help_ReportBugs(object sender, RoutedEventArgs e) {
System.Diagnostics.Process.Start("https://github.com/yyc12345/BallanceTASEditor/issues");
}
private void funcMenu_Help_About(object sender, RoutedEventArgs e) {
MessageBox.Show("Under MIT License\nVersion: 1.0 alpha\nyyc12345.", "Ballance TAS Editor");
MessageBox.Show(I18NProcessor.GetI18N("code_MainWindow_Menu_Help_About"),
I18NProcessor.GetI18N("code_Shared_ProgramName"),
MessageBoxButton.OK, MessageBoxImage.Information);
}
private void funcMenu_File_Open(object sender, RoutedEventArgs e) {
var file = DialogUtil.OpenFileDialog();
if (file == "") return;
mFile = new TASFile(file);
mViewer = new TASViewer(mFile, uiTASSlider, uiTASData, uiStatusbar_Selected);
RefreshUI(true);
ChangeToolMode(ToolMode.Cursor);
mViewer.ChangeOverwrittenMode(uiMenu_Display_OverwrittenPaste.IsChecked);
OpenFile(file);
}
private void funcMenu_File_Save(object sender, RoutedEventArgs e) {
@ -60,26 +112,48 @@ namespace BallanceTASEditor {
}
private void funcMenu_File_Close(object sender, RoutedEventArgs e) {
if (!DialogUtil.ConfirmDialog("Do you want to close this TAS file?")) return;
if (!DialogUtil.ConfirmDialog(I18NProcessor.GetI18N("code_MainWindow_Menu_File_Close"))) return;
mViewer.UpdateDataUI -= RefreshDataUI;
mViewer.UpdateSelection -= RefreshSelection;
mViewer.UpdateToolMode -= RefreshToolMode;
mViewer.Dispose();
mFile = null;
mViewer = null;
RefreshUI(false);
}
private void funcWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (!(mFile is null)) {
if (!DialogUtil.ConfirmDialog(I18NProcessor.GetI18N("code_MainWindow_Closing"))) {
e.Cancel = true;
}
}
}
private void funcMenu_Display_ItemCount(object sender, RoutedEventArgs e) {
int newvalue = 0;
if (DialogUtil.InputNumber("Input new count (>=5 && <=30)", 5, 30, ref newvalue)) {
mViewer.ChangeListLength(newvalue);
if (DialogUtil.InputNumber(I18NProcessor.GetI18N("code_MainWindow_Menu_Display_ItemCount"), 5, 30, ref newvalue)) {
ChangeItemCount(newvalue);
}
}
private void funcMenu_Display_OverwrittenPaste(object sender, RoutedEventArgs e) {
//uiMenu_Display_OverwrittenPaste.IsChecked = !uiMenu_Display_OverwrittenPaste.IsChecked;
uiMenu_Display_OverwrittenPaste.IsChecked = !uiMenu_Display_OverwrittenPaste.IsChecked;
uiStatusbar_OverwrittenPaste.Visibility = uiMenu_Display_OverwrittenPaste.IsChecked ? Visibility.Visible : Visibility.Hidden;
GlobalVariable.configManager.Configuration[ConfigManager.CfgNode_IsOverwrittenPaste] = uiMenu_Display_OverwrittenPaste.IsChecked.ToString();
if (mViewer != null)
mViewer.ChangeOverwrittenMode(uiMenu_Display_OverwrittenPaste.IsChecked);
}
private void funcMenu_Display_HorizontalLayout(object sender, RoutedEventArgs e) {
uiMenu_Display_HorizontalLayout.IsChecked = !uiMenu_Display_HorizontalLayout.IsChecked;
GlobalVariable.configManager.Configuration[ConfigManager.CfgNode_IsHorizonLayout] = uiMenu_Display_HorizontalLayout.IsChecked.ToString();
ChangeLayout(uiMenu_Display_HorizontalLayout.IsChecked);
}
private void funcMenu_Display_Redo(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.Redo);
}
@ -88,35 +162,85 @@ namespace BallanceTASEditor {
mViewer.ProcessOperation(OperationEnum.Undo);
}
#endregion
#region datamenu operation
private void funcDataMenu_Set(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.Set);
}
private void funcDataMenu_Unset(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.Unset);
}
private void funcDataMenu_Cut(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.Cut);
}
private void funcDataMenu_Copy(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.Copy);
}
private void funcDataMenu_PasteAfter(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.PasteAfter);
}
private void funcDataMenu_PasteBefore(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.PasteBefore);
}
private void funcDataMenu_Delete(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.Delete);
}
private void funcDataMenu_DeleteAfter(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.DeleteAfter);
}
private void funcDataMenu_DeleteBefore(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.DeleteBefore);
}
private void funcDataMenu_AddAfter(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.AddAfter);
}
private void funcDataMenu_AddBefore(object sender, RoutedEventArgs e) {
mViewer.ProcessOperation(OperationEnum.AddBefore);
}
#endregion
// =========================== btn
private void funcBtn_Cursor(object sender, RoutedEventArgs e) {
ChangeToolMode(ToolMode.Cursor);
mViewer.ChangeToolMode(ToolMode.Cursor);
}
private void funcBtn_Fill(object sender, RoutedEventArgs e) {
ChangeToolMode(ToolMode.Fill);
mViewer.ChangeToolMode(ToolMode.Fill);
}
private void funcBtn_Overwrite(object sender, RoutedEventArgs e) {
ChangeToolMode(ToolMode.Overwrite);
mViewer.ChangeToolMode(ToolMode.Overwrite);
}
// move btn
private void funcBtn_FastMovePrev(object sender, RoutedEventArgs e) {
MoveSliderManually(true, true);
mSlider.MoveSliderManually(true, true, mViewer.GetItemCountInPage());
}
private void funcBtn_MovePrev(object sender, RoutedEventArgs e) {
MoveSliderManually(true, false);
mSlider.MoveSliderManually(true, false, mViewer.GetItemCountInPage());
}
private void funcBtn_MoveNext(object sender, RoutedEventArgs e) {
MoveSliderManually(false, false);
mSlider.MoveSliderManually(false, false, mViewer.GetItemCountInPage());
}
private void funcBtn_FastMoveNext(object sender, RoutedEventArgs e) {
MoveSliderManually(false, true);
mSlider.MoveSliderManually(false, true, mViewer.GetItemCountInPage());
}
// move keyboard
@ -124,26 +248,117 @@ namespace BallanceTASEditor {
private void funcWindow_KeyUp(object sender, KeyEventArgs e) {
if (mFile == null || mViewer == null) return;
switch(e.Key) {
switch (e.Key) {
case Key.A:
MoveSliderManually(true, true);
mSlider.MoveSliderManually(true, true, mViewer.GetItemCountInPage());
break;
case Key.S:
MoveSliderManually(true, false);
mSlider.MoveSliderManually(true, false, mViewer.GetItemCountInPage());
break;
case Key.D:
MoveSliderManually(false, false);
mSlider.MoveSliderManually(false, false, mViewer.GetItemCountInPage());
break;
case Key.F:
MoveSliderManually(false, true);
mSlider.MoveSliderManually(false, true, mViewer.GetItemCountInPage());
break;
}
}
// move mouse
private void funcWindow_MouseWheel(object sender, MouseWheelEventArgs e) {
if (e.Delta > 0) {
// wheel up
if (KeyboardState.IsKeyPressed(KeyboardState.VirtualKeyStates.VK_SHIFT)) {
// move quickly
mSlider.MoveSliderManually(true, true, mViewer.GetItemCountInPage());
} else if (KeyboardState.IsKeyPressed(KeyboardState.VirtualKeyStates.VK_CONTROL)) {
// decrease item count
var newvalue = mViewer.GetItemCountInPage();
ChangeItemCount(newvalue - 1);
} else {
// normally move
mSlider.MoveSliderManually(true, false, mViewer.GetItemCountInPage());
}
} else if (e.Delta < 0) {
// wheel down
if (KeyboardState.IsKeyPressed(KeyboardState.VirtualKeyStates.VK_SHIFT)) {
// move quickly
mSlider.MoveSliderManually(false, true, mViewer.GetItemCountInPage());
} else if (KeyboardState.IsKeyPressed(KeyboardState.VirtualKeyStates.VK_CONTROL)) {
// increase item count
var newvalue = mViewer.GetItemCountInPage();
ChangeItemCount(newvalue + 1);
} else {
// normally move
mSlider.MoveSliderManually(false, false, mViewer.GetItemCountInPage());
}
}
}
// drop file to open
private void funcDrop_Drop(object sender, DragEventArgs e) {
string fileName = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
OpenFile(fileName);
}
private void funcDrop_DragEnter(object sender, DragEventArgs e) {
// only accept one file
if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
var arr = (System.Array)e.Data.GetData(DataFormats.FileDrop);
if (arr.Length != 1) e.Effects = DragDropEffects.None;
else e.Effects = DragDropEffects.Link;
} else e.Effects = DragDropEffects.None;
}
#endregion
private void ApplyConfigureManager() {
var isOverwrittenPaste = bool.Parse(GlobalVariable.configManager.Configuration[ConfigManager.CfgNode_IsOverwrittenPaste]);
var isHorizontalLayout = bool.Parse(GlobalVariable.configManager.Configuration[ConfigManager.CfgNode_IsHorizonLayout]);
var itemCount = int.Parse(GlobalVariable.configManager.Configuration[ConfigManager.CfgNode_ItemCount]);
private void ChangeToolMode(ToolMode mode) {
uiMenu_Display_OverwrittenPaste.IsChecked = isOverwrittenPaste;
uiMenu_Display_HorizontalLayout.IsChecked = isHorizontalLayout;
uiStatusbar_OverwrittenPaste.Visibility = isOverwrittenPaste ? Visibility.Visible : Visibility.Hidden;
if (mViewer != null) {
mViewer.ChangeOverwrittenMode(isOverwrittenPaste);
mViewer.ChangeListLength(itemCount);
ChangeLayout(isHorizontalLayout);
}
}
private void OpenFile(string file) {
#if DEBUG
#else
try {
#endif
mFile = new TASFile(file);
#if DEBUG
#else
} catch {
MessageBox.Show(I18NProcessor.GetI18N("code_MainWindow_Menu_File_Open_Fail"),
I18NProcessor.GetI18N("code_Shared_ProgramName"),
MessageBoxButton.OK, MessageBoxImage.Error);
mFile = null;
return;
}
#endif
mViewer = new TASViewer(mFile, mSlider, mFlow);
mViewer.UpdateDataUI += RefreshDataUI;
mViewer.UpdateSelection += RefreshSelection;
mViewer.UpdateToolMode += RefreshToolMode;
RefreshUI(true);
ApplyConfigureManager(); // apply item count and overwritten item
mViewer.ChangeToolMode(ToolMode.Cursor);
}
private void RefreshToolMode(ToolMode mode) {
switch (mode) {
case ToolMode.Cursor:
uiBtn_Cursor.IsEnabled = false;
@ -173,8 +388,6 @@ namespace BallanceTASEditor {
uiStatusbar_Mode_Overwrite.Visibility = Visibility.Visible;
break;
}
mViewer.ChangeToolMode(mode);
}
private void RefreshUI(bool isFileOpened) {
@ -189,10 +402,15 @@ namespace BallanceTASEditor {
uiMenu_Display_ItemCount.IsEnabled = true;
uiMenu_Display_OverwrittenPaste.IsEnabled = true;
uiMenu_Display_HorizontalLayout.IsEnabled = true;
uiMenu_Display_Undo.IsEnabled = true;
uiMenu_Display_Redo.IsEnabled = true;
uiStatusbar.Visibility = Visibility.Visible;
uiStatusbar_Runtime_Mode.Visibility = Visibility.Visible;
uiStatusbar_Runtime_PasteMode.Visibility = Visibility.Visible;
uiStatusbar_Runtime_Selected.Visibility = Visibility.Visible;
uiStatusbar_Runtime_Separator1.Visibility = Visibility.Visible;
uiStatusbar_Runtime_Separator2.Visibility = Visibility.Visible;
} else {
uiEditorPanel.Visibility = Visibility.Collapsed;
uiEditorNote.Visibility = Visibility.Visible;
@ -204,16 +422,89 @@ namespace BallanceTASEditor {
uiMenu_Display_ItemCount.IsEnabled = false;
uiMenu_Display_OverwrittenPaste.IsEnabled = false;
uiMenu_Display_HorizontalLayout.IsEnabled = false;
uiMenu_Display_Undo.IsEnabled = false;
uiMenu_Display_Redo.IsEnabled = false;
uiStatusbar.Visibility = Visibility.Collapsed;
uiStatusbar_Runtime_Mode.Visibility = Visibility.Collapsed;
uiStatusbar_Runtime_PasteMode.Visibility = Visibility.Collapsed;
uiStatusbar_Runtime_Selected.Visibility = Visibility.Collapsed;
uiStatusbar_Runtime_Separator1.Visibility = Visibility.Collapsed;
uiStatusbar_Runtime_Separator2.Visibility = Visibility.Collapsed;
}
}
private void MoveSliderManually(bool isPrev, bool isFast) {
var step = isFast ? mViewer.GetItemCountInPage() : 1;
uiTASSlider.Value = Util.Clamp(uiTASSlider.Value.ToInt32() + (isPrev ? -1 : 1) * step, uiTASSlider.Minimum.ToInt32(), uiTASSlider.Maximum.ToInt32());
private void RefreshDataUI(bool showCursorPasteAddDeleteOne, bool showFill, bool showCursorCopyDelete) {
uiDataMenu_Set.IsEnabled = showFill;
uiDataMenu_Unset.IsEnabled = showFill;
uiDataMenu_Cut.IsEnabled = showCursorCopyDelete;
uiDataMenu_Copy.IsEnabled = showCursorCopyDelete;
uiDataMenu_Delete.IsEnabled = showCursorCopyDelete;
uiDataMenu_DeleteAfter.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_DeleteBefore.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_PasteAfter.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_PasteBefore.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_AddAfter.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_AddBefore.IsEnabled = showCursorPasteAddDeleteOne;
}
private void RefreshSelection(SelectionHelp mSelectionHelp) {
var mode = mSelectionHelp.GetToolMode();
switch (mode) {
case ToolMode.Cursor:
if (mSelectionHelp.IsDataReady()) {
var data = mSelectionHelp.GetRange();
uiStatusbar_Selected.Text = $"{data.start} - {data.end}";
} else if (mSelectionHelp.IsDataPartialReady()) {
var data2 = mSelectionHelp.GetPoint();
uiStatusbar_Selected.Text = data2.ToString();
} else uiStatusbar_Selected.Text = "-";
break;
case ToolMode.Fill:
if (mSelectionHelp.IsDataReady()) {
var data3 = mSelectionHelp.GetRange();
uiStatusbar_Selected.Text = $"{data3.start} - {data3.end}";
} else uiStatusbar_Selected.Text = "-";
break;
case ToolMode.Overwrite:
if (mSelectionHelp.IsDataReady()) {
var data4 = mSelectionHelp.GetPoint();
uiStatusbar_Selected.Text = data4.ToString();
} else uiStatusbar_Selected.Text = "-";
break;
}
}
private void ChangeItemCount(int count) {
GlobalVariable.configManager.Configuration[ConfigManager.CfgNode_ItemCount] = count.ToString();
mViewer.ChangeListLength(count);
}
private void ChangeLayout(bool isHorizontal) {
if (isHorizontal == mIsHorizontalLayout) return;
mIsHorizontalLayout = isHorizontal;
// swap window size
var swap = this.Width;
this.Width = this.Height;
this.Height = swap;
// change self layout first
uiLayoutContainer.RowDefinitions.Clear();
uiLayoutContainer.ColumnDefinitions.Clear();
if (isHorizontal) {
UI.Util.GridRowAdder(uiLayoutContainer, new GridLength(1, GridUnitType.Star));
UI.Util.GridRowAdder(uiLayoutContainer, GridLength.Auto);
} else {
UI.Util.GridColumnAdder(uiLayoutContainer, new GridLength(1, GridUnitType.Star));
UI.Util.GridColumnAdder(uiLayoutContainer, GridLength.Auto);
}
UI.Util.SwapGridItemRC(uiTASFlowContainer);
UI.Util.SwapGridItemRC(uiTASSliderContainer);
// change sub layout
mFlow.ChangeLayout(isHorizontal);
mSlider.ChangeLayout(isHorizontal);
}
}

View File

@ -7,12 +7,12 @@ using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("BallanceTASEditor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyTitle("Ballance TAS Editor")]
[assembly: AssemblyDescription("Ballance TAS Editor")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BallanceTASEditor")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyCompany("Ballance Community")]
[assembly: AssemblyProduct("Ballance TAS Editor")]
[assembly: AssemblyCopyright("Copyright © Ballance Community 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -51,5 +51,5 @@ using System.Windows;
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]

View File

@ -6,7 +6,7 @@
xmlns:local="clr-namespace:BallanceTASEditor.UI"
xmlns:converter="clr-namespace:BallanceTASEditor.UI"
mc:Ignorable="d"
Title="Add Item" Height="200" Width="400" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner">
Title="{DynamicResource ui_AddItem_Title}" Height="200" Width="400" WindowStyle="ToolWindow" WindowStartupLocation="CenterOwner" Icon="/BallanceTASEditor;component/icon.ico">
<Window.Resources>
<converter:AddItemConverter x:Key="conv_addItem"/>
<converter:FPS2DeltaTimeConverter x:Key="conv_fps2DeltaTime"/>
@ -25,9 +25,9 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="5" Grid.Column="0" Grid.Row="0" Text="Count" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="0" Grid.Row="1" Text="FPS" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="0" Grid.Row="2" Text="Delta Time" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="0" Grid.Row="0" Text="{DynamicResource ui_AddItem_Count}" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="0" Grid.Row="1" Text="{DynamicResource ui_AddItem_FPS}" VerticalAlignment="Center"/>
<TextBlock Margin="5" Grid.Column="0" Grid.Row="2" Text="{DynamicResource ui_AddItem_DeltaTime}" VerticalAlignment="Center"/>
<TextBox x:Name="uiTextbox_Count" Margin="5" Padding="5" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center"/>
<TextBox x:Name="uiTextbox_FPS" Margin="5" Padding="5" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center"/>
@ -35,7 +35,7 @@
Text="{Binding Converter={StaticResource conv_fps2DeltaTime}, Mode=OneWay, ElementName=uiTextbox_FPS, Path=Text}"/>
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" Grid.ColumnSpan="2" Grid.Row="4">
<Button x:Name="uiBtn_OK" Margin="5" Padding="5" Content="OK" MinWidth="50" Click="funcBtn_OK">
<Button x:Name="uiBtn_OK" Margin="5" Padding="5" Content="{DynamicResource ui_AddItem_OK}" MinWidth="50" Click="funcBtn_OK">
<Button.IsEnabled>
<MultiBinding Converter="{StaticResource conv_addItem}" Mode="OneWay">
<Binding ElementName="uiTextbox_Count" Path="Text"/>
@ -43,7 +43,7 @@
</MultiBinding>
</Button.IsEnabled>
</Button>
<Button x:Name="uiBtn_Cancel" Margin="5" Padding="5" Content="Cancel" MinWidth="50" Click="funcBtn_Cancel"/>
<Button x:Name="uiBtn_Cancel" Margin="5" Padding="5" Content="{DynamicResource ui_AddItem_Cancel}" MinWidth="50" Click="funcBtn_Cancel"/>
</StackPanel>
</Grid>
</Window>

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using BallanceTASEditor.Core;
namespace BallanceTASEditor.UI {
public class DialogUtil {
@ -12,7 +13,7 @@ namespace BallanceTASEditor.UI {
Microsoft.Win32.OpenFileDialog op = new Microsoft.Win32.OpenFileDialog();
op.RestoreDirectory = true;
op.Multiselect = false;
op.Filter = "TAS file(*.tas)|*.tas|All file(*.*)|*.*";
op.Filter = I18NProcessor.GetI18N("code_DialogUtil_FileFilter");
if (!(bool)op.ShowDialog()) return "";
return op.FileName;
}
@ -20,24 +21,26 @@ namespace BallanceTASEditor.UI {
public static string SaveFileDialog() {
Microsoft.Win32.SaveFileDialog op = new Microsoft.Win32.SaveFileDialog();
op.RestoreDirectory = true;
op.Filter = "TAS file(*.tas)|*.tas|All file(*.*)|*.*";
op.Filter = I18NProcessor.GetI18N("code_DialogUtil_FileFilter");
if (!(bool)op.ShowDialog()) return "";
return op.FileName;
}
public static bool ConfirmDialog(string str) {
var result = MessageBox.Show(str, "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);
var result = MessageBox.Show(str, I18NProcessor.GetI18N("code_DialogUtil_Warning"), MessageBoxButton.YesNo, MessageBoxImage.Warning);
return (result == MessageBoxResult.Yes);
}
public static bool InputNumber(string title, int min, int max, ref int result) {
while (true) {
var dialog = Interaction.InputBox(title, "Input number", "");
var dialog = Interaction.InputBox(title, I18NProcessor.GetI18N("code_DialogUtil_InputNumber_Title"), "");
if (dialog == "") return false;
if (int.TryParse(dialog, out result)) {
if (result <= max && result >= min) break;
}
MessageBox.Show("Invalid number. Please input again", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
MessageBox.Show(I18NProcessor.GetI18N("code_DialogUtil_InputNumber_Wrong"),
I18NProcessor.GetI18N("code_DialogUtil_Warning"),
MessageBoxButton.OK, MessageBoxImage.Warning);
}
return true;
}

View File

@ -7,6 +7,7 @@ namespace BallanceTASEditor.UI {
public enum OperationEnum {
Set,
Unset,
Cut,
Copy,
PasteAfter,
PasteBefore,

View File

@ -15,27 +15,98 @@ using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BallanceTASEditor.UI {
/// <summary>
/// TASFlow.xaml 的交互逻辑
/// </summary>
public partial class TASFlow : UserControl {
public TASFlow() {
InitializeComponent();
public partial class TASFlow {
public TASFlow(Grid coreWindow, TextBlock[] headers) {
uiCoreWindow = coreWindow;
mHeaders = headers;
mItemList = new List<TASFlowUIItem>();
mRectMap = new Dictionary<Rectangle, CellPosition>();
mItemCount = 0;
mIsHorizontalLayout = true;
SetItemCount(1);
}
public event Action Click;
public event Action<OperationEnum> NewOperation;
private readonly TextBlock[] mHeaders;
private bool mIsHorizontalLayout;
private Grid uiCoreWindow;
private int mItemCount;
private List<TASFlowUIItem> mItemList;
private Dictionary<Rectangle, CellPosition> mRectMap;
public SelectionHelp SelectionHelp { get; set; }
public List<FrameDataDisplay> DataSources { get; set; }
public void ChangeLayout(bool isHorizontal) {
if (isHorizontal == mIsHorizontalLayout) return;
// the layout changed, re-construct elements
mIsHorizontalLayout = isHorizontal;
// delete original grid layout
var lenHeader = mHeaders.Length;
uiCoreWindow.ColumnDefinitions.Clear();
uiCoreWindow.RowDefinitions.Clear();
// add header layout
if (mIsHorizontalLayout) {
// horizontal layout
// row is header count
// column is tas unit
// header
layout_row_adder(GridLength.Auto);
for (int r = 0; r < lenHeader; r++) {
layout_row_adder(GridLength.Auto);
}
layout_row_adder(new GridLength(1, GridUnitType.Star));
// tas unit
layout_column_adder(GridLength.Auto);
for (int c = 0; c < mItemCount; c++) {
layout_column_adder(new GridLength(1, GridUnitType.Star));
}
} else {
// vertical layout
// row is tas unit
// column is header count (use start to split, not auto)
// header
layout_column_adder(GridLength.Auto);
for (int r = 0; r < lenHeader; r++) {
if (r == 0 || r == 1)
layout_column_adder(GridLength.Auto);
else
layout_column_adder(new GridLength(1, GridUnitType.Star));
}
//layout_column_adder(new GridLength(1, GridUnitType.Star));
// tas unit
layout_row_adder(GridLength.Auto);
for (int c = 0; c < mItemCount; c++) {
layout_row_adder(new GridLength(1, GridUnitType.Star));
}
}
// now, change items attach prop
// just swap Grid.Row and Grid.Column's number
foreach (var item in mHeaders) {
var swap = Grid.GetColumn(item);
Grid.SetColumn(item, Grid.GetRow(item));
Grid.SetRow(item, swap);
}
foreach (var item in mItemList) {
item.FlipUnit();
}
}
private void layout_row_adder(GridLength size) {
UI.Util.GridRowAdder(uiCoreWindow, size);
}
private void layout_column_adder(GridLength size) {
UI.Util.GridColumnAdder(uiCoreWindow, size);
}
public void RefreshDataSources() {
if (DataSources == null) return;
@ -51,24 +122,30 @@ namespace BallanceTASEditor.UI {
// change column defination first
if (offset > 0) {
for(int i = 0; i < abs; i++) {
var item = new ColumnDefinition();
item.Width = GridLength.Auto;
uiCoreWindow.ColumnDefinitions.Add(item);
for (int i = 0; i < abs; i++) {
if (mIsHorizontalLayout) {
layout_column_adder(new GridLength(1, GridUnitType.Star));
} else {
layout_row_adder(new GridLength(1, GridUnitType.Star));
}
}
} else {
if (mIsHorizontalLayout) {
uiCoreWindow.ColumnDefinitions.RemoveRange(newCount + 1, abs); // the first col is sheet header, so add 1 additionally
} else {
uiCoreWindow.RowDefinitions.RemoveRange(newCount + 1, abs); // the first col is sheet header, so add 1 additionally
}
}
// add / remove item
if (offset > 0) {
for (int i = 0; i < abs; i++) {
var newItem = new TASFlowUIItem(mItemCount + 1 + i); // the first col is sheet header, so add 1 additionally
var newItem = new TASFlowUIItem(mItemCount + 1 + i, mIsHorizontalLayout); // the first col is sheet header, so add 1 additionally
newItem.Add(uiCoreWindow, mRectMap, Rectangle_MouseDown);
mItemList.Add(newItem);
}
} else {
for(int i = 0; i < abs; i++) {
for (int i = 0; i < abs; i++) {
mItemList[newCount + i].Remove(uiCoreWindow, mRectMap, Rectangle_MouseDown);
}
mItemList.RemoveRange(newCount, abs);
@ -78,26 +155,6 @@ namespace BallanceTASEditor.UI {
mItemCount = newCount;
}
public void RefreshDataMenu() {
if (SelectionHelp == null) return;
ToolMode mode = SelectionHelp.GetToolMode();
bool showCursorPasteAddDeleteOne = mode == ToolMode.Cursor && SelectionHelp.IsDataPartialReady();
bool showFill = mode == ToolMode.Fill && SelectionHelp.IsDataReady();
bool showCursorCopyDelete = mode == ToolMode.Cursor && SelectionHelp.IsDataReady();
uiDataMenu_Set.IsEnabled = showFill;
uiDataMenu_Unset.IsEnabled = showFill;
uiDataMenu_Copy.IsEnabled = showCursorCopyDelete;
uiDataMenu_Delete.IsEnabled = showCursorCopyDelete;
uiDataMenu_DeleteAfter.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_DeleteBefore.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_PasteAfter.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_PasteBefore.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_AddAfter.IsEnabled = showCursorPasteAddDeleteOne;
uiDataMenu_AddBefore.IsEnabled = showCursorPasteAddDeleteOne;
}
public void RefreshSelectionHighlight() {
ToolMode mode = SelectionHelp.GetToolMode();
@ -158,6 +215,14 @@ namespace BallanceTASEditor.UI {
} else {
SelectionHelp.FirstClick(mItemList[pos.column - 1].rawFrame, pos.field);
}
} else if (e.MouseDevice.RightButton == MouseButtonState.Pressed) {
// if we click right button and there are no full selection in cursor mode
// try to add a first selection
if (SelectionHelp.GetToolMode() == ToolMode.Cursor) {
if (!SelectionHelp.IsDataReady()) {
SelectionHelp.FirstClick(mItemList[pos.column - 1].rawFrame, pos.field);
}
}
}
// note main window to process it.
@ -170,50 +235,6 @@ namespace BallanceTASEditor.UI {
Click?.Invoke();
}
#region menu operation
private void uiDataMenu_Set_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.Set);
}
private void uiDataMenu_Unset_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.Unset);
}
private void uiDataMenu_Copy_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.Copy);
}
private void uiDataMenu_PasteAfter_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.PasteAfter);
}
private void uiDataMenu_PasteBefore_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.PasteBefore);
}
private void uiDataMenu_Delete_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.Delete);
}
private void uiDataMenu_DeleteAfter_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.DeleteAfter);
}
private void uiDataMenu_DeleteBefore_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.DeleteBefore);
}
private void uiDataMenu_AddAfter_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.AddAfter);
}
private void uiDataMenu_AddBefore_Click(object sender, RoutedEventArgs e) {
NewOperation?.Invoke(OperationEnum.AddBefore);
}
#endregion
}
public class TASFlowUIItem {
@ -226,21 +247,30 @@ namespace BallanceTASEditor.UI {
private static readonly Color COLOR_UNSET = Color.FromArgb(0, 255, 255, 255);
private static readonly Color COLOR_SELECTED = Colors.OrangeRed;
private static readonly Color COLOR_UNSELECTED = Colors.LightGray;
private const int KEY_COUNT = 9;
private const double SELECTION_HEADER_HEIGHT = 10.0f;
private const int KEY_COUNT = 9;
public TASFlowUIItem(int column) {
public TASFlowUIItem(int column, bool useHorizontalLayout) {
// basic item
sel_rect = new Rectangle();
frame = new TextBlock();
deltaTime = new TextBlock();
if (useHorizontalLayout) {
Grid.SetRow(sel_rect, 0);
Grid.SetRow(frame, 1);
Grid.SetRow(deltaTime, 2);
Grid.SetColumn(sel_rect, column);
Grid.SetColumn(frame, column);
Grid.SetColumn(deltaTime, column);
} else {
Grid.SetColumn(sel_rect, 0);
Grid.SetColumn(frame, 1);
Grid.SetColumn(deltaTime, 2);
Grid.SetRow(sel_rect, column);
Grid.SetRow(frame, column);
Grid.SetRow(deltaTime, column);
}
sel_rect.Margin = RECT_MARGIN;
frame.Margin = DEFAULT_MARGIN;
@ -248,7 +278,10 @@ namespace BallanceTASEditor.UI {
sel_rect.StrokeThickness = 2;
sel_rect.Stroke = SEL_RECT_STROKE;
if (useHorizontalLayout)
sel_rect.Height = SELECTION_HEADER_HEIGHT;
else
sel_rect.Width = SELECTION_HEADER_HEIGHT;
// keystates item
keystates = new Rectangle[KEY_COUNT];
@ -258,8 +291,13 @@ namespace BallanceTASEditor.UI {
keystates[i] = new Rectangle();
keystatesFill[i] = new SolidColorBrush(COLOR_UNSET);
keystatesStroke[i] = new SolidColorBrush(COLOR_UNSELECTED);
if (useHorizontalLayout) {
Grid.SetRow(keystates[i], 3 + i);
Grid.SetColumn(keystates[i], column);
} else {
Grid.SetColumn(keystates[i], 3 + i);
Grid.SetRow(keystates[i], column);
}
keystates[i].Margin = RECT_MARGIN;
keystates[i].StrokeThickness = 3;
keystates[i].Stroke = keystatesStroke[i];
@ -271,6 +309,20 @@ namespace BallanceTASEditor.UI {
rawIsEnable = false;
}
public void FlipUnit() {
UI.Util.SwapGridItemRC(sel_rect);
UI.Util.SwapGridItemRC(frame);
UI.Util.SwapGridItemRC(deltaTime);
for (int i = 0; i < KEY_COUNT; i++) {
UI.Util.SwapGridItemRC(keystates[i]);
}
// swap sel_rect height and width
var swap = sel_rect.Height;
sel_rect.Height = sel_rect.Width;
sel_rect.Width = swap;
}
public void Add(Grid target, Dictionary<Rectangle, CellPosition> map, MouseButtonEventHandler func) {
target.Children.Add(sel_rect);
target.Children.Add(frame);

View File

@ -1,59 +0,0 @@
<UserControl x:Class="BallanceTASEditor.UI.TASFlow"
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.UI"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="800">
<Grid x:Name="uiCoreWindow" Background="#ffffff">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem x:Name="uiDataMenu_Set" Header="Set" Click="uiDataMenu_Set_Click"/>
<MenuItem x:Name="uiDataMenu_Unset" Header="Unset" Click="uiDataMenu_Unset_Click"/>
<Separator/>
<MenuItem x:Name="uiDataMenu_Copy" Header="Copy" Click="uiDataMenu_Copy_Click"/>
<MenuItem x:Name="uiDataMenu_PasteAfter" Header="Paste after this" Click="uiDataMenu_PasteAfter_Click"/>
<MenuItem x:Name="uiDataMenu_PasteBefore" Header="Paste before this" Click="uiDataMenu_PasteBefore_Click"/>
<Separator/>
<MenuItem x:Name="uiDataMenu_Delete" Header="Delete" Click="uiDataMenu_Delete_Click"/>
<MenuItem x:Name="uiDataMenu_DeleteAfter" Header="Delete next frame" Click="uiDataMenu_DeleteAfter_Click"/>
<MenuItem x:Name="uiDataMenu_DeleteBefore" Header="Delete last frame" Click="uiDataMenu_DeleteBefore_Click"/>
<Separator/>
<MenuItem x:Name="uiDataMenu_AddAfter" Header="Add blank item after this" Click="uiDataMenu_AddAfter_Click"/>
<MenuItem x:Name="uiDataMenu_AddBefore" Header="Add blank item before this" Click="uiDataMenu_AddBefore_Click"/>
</ContextMenu>
</Grid.ContextMenu>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="1" Text="Frame"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="2" Text="Delta Time"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="3" Text="^"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="4" Text="v"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="5" Text="&lt;"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="6" Text="&gt;"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="7" Text="shift"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="8" Text="space"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="9" Text="q"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="10" Text="esc"/>
<TextBlock Padding="2" Background="#afafaf" Grid.Column="0" Grid.Row="11" Text="enter"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,117 @@
using BallanceTASEditor.Core;
using BallanceTASEditor.Core.TASStruct;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BallanceTASEditor.UI {
public class TASSlider {
public TASSlider(TASSliderComponents _components) {
components = _components;
components.mSlider.Minimum = 0;
mIsHorizontalLayout = true;
components.mSlider.ValueChanged += func_SliderValueChanged;
}
public event Action<long> ValueChanged;
TASSliderComponents components;
bool mIsHorizontalLayout;
public void MoveSliderManually(bool isPrev, bool isFast, int fastCount) {
var step = isFast ? fastCount : 1;
components.mSlider.Value = Core.Util.Clamp(components.mSlider.Value.ToInt32() + (isPrev ? -1 : 1) * step, components.mSlider.Minimum.ToInt32(), components.mSlider.Maximum.ToInt32());
}
public void UpdateRange(TASFile mFile) {
components.mSlider.Maximum = mFile.mFrameCount == 0 ? 0 : mFile.mFrameCount - 1;
var index = mFile.GetPointerIndex();
if (index >= 0) {
components.mSlider.Value = mFile.GetPointerIndex();
components.mSlider.IsEnabled = true;
} else {
// invalid index, mean slider is useless, disable it
components.mSlider.Value = components.mSlider.Maximum;
components.mSlider.IsEnabled = false;
}
}
private void func_SliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
ValueChanged?.Invoke(e.NewValue.ToInt64());
}
public void ChangeLayout(bool isHorizontal) {
if (isHorizontal == mIsHorizontalLayout) return;
// the layout changed, re-construct elements
mIsHorizontalLayout = isHorizontal;
// change container
components.container.RowDefinitions.Clear();
components.container.ColumnDefinitions.Clear();
if (mIsHorizontalLayout) {
for(int i = 0; i < 4; i++) {
UI.Util.GridColumnAdder(components.container, GridLength.Auto);
}
UI.Util.GridColumnAdder(components.container, new GridLength(1, GridUnitType.Star));
} else {
for (int i = 0; i < 4; i++) {
UI.Util.GridRowAdder(components.container, GridLength.Auto);
}
UI.Util.GridRowAdder(components.container, new GridLength(1, GridUnitType.Star));
}
// flip elements
UI.Util.SwapGridItemRC(components.btnFastPrev);
UI.Util.SwapGridItemRC(components.btnPrev);
UI.Util.SwapGridItemRC(components.btnNext);
UI.Util.SwapGridItemRC(components.btnFastNext);
UI.Util.SwapGridItemRC(components.mSlider);
// change transform
if (mIsHorizontalLayout) {
// clear all btn's transform and set slider as horizontal style
components.btnFastPrev.RenderTransform = Transform.Identity;
components.btnPrev.RenderTransform = Transform.Identity;
components.btnNext.RenderTransform = Transform.Identity;
components.btnFastNext.RenderTransform = Transform.Identity;
components.mSlider.RenderTransform = Transform.Identity;
components.mSlider.Orientation = Orientation.Horizontal;
components.mSlider.VerticalAlignment = VerticalAlignment.Center;
components.mSlider.HorizontalAlignment = HorizontalAlignment.Stretch;
} else {
components.btnFastPrev.RenderTransform = new RotateTransform(90);
components.btnPrev.RenderTransform = new RotateTransform(90);
components.btnNext.RenderTransform = new RotateTransform(90);
components.btnFastNext.RenderTransform = new RotateTransform(90);
components.mSlider.RenderTransform = new RotateTransform(180);
components.mSlider.Orientation = Orientation.Vertical;
components.mSlider.VerticalAlignment = VerticalAlignment.Stretch;
components.mSlider.HorizontalAlignment = HorizontalAlignment.Center;
}
}
}
public class TASSliderComponents {
public Grid container;
public Button btnFastPrev;
public Button btnPrev;
public Button btnNext;
public Button btnFastNext;
public Slider mSlider;
}
}

View File

@ -10,15 +10,13 @@ using System.Windows;
namespace BallanceTASEditor.UI {
public class TASViewer : IDisposable {
public TASViewer(TASFile file, Slider slider, TASFlow datagrid, TextBlock statusbar) {
public TASViewer(TASFile file, TASSlider slider, TASFlow datagrid) {
mFile = file;
mSlider = slider;
mDataGrid = datagrid;
mStatusbar = statusbar;
// restore slider
mSlider.Minimum = 0;
updateSliderRange();
mSlider.UpdateRange(mFile);
// init selection
mSelectionHelp = new SelectionHelp();
@ -35,86 +33,78 @@ namespace BallanceTASEditor.UI {
mDataGrid.SelectionHelp = mSelectionHelp;
mDataGrid.Click += funcDataMenu_Click;
mDataGrid.NewOperation += funcDataMenu_NewOperation;
mSlider.ValueChanged += sliderValueChanged;
// display data
ChangeListLength(DATA_LIST_LENGTH);
mSlider.ValueChanged += funcSlider_ValueChanged;
}
public void Dispose() {
mDataGrid.DataSources = null;
mDataGrid.Click -= funcDataMenu_Click;
mDataGrid.NewOperation -= funcDataMenu_NewOperation;
mSlider.ValueChanged -= sliderValueChanged;
mSlider.ValueChanged -= funcSlider_ValueChanged;
}
const int DATA_LIST_LENGTH = 15;
//const int DATA_LIST_LENGTH = 15;
FrameData INVALID_FRAME_DATA;
TASFile mFile;
Slider mSlider;
TextBlock mStatusbar;
TASSlider mSlider;
TASFlow mDataGrid;
SelectionHelp mSelectionHelp;
int mListLength;
List<FrameDataDisplay> mDataSource;
bool mOverwrittenPaste;
private void sliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
long pos = e.NewValue.ToInt64();
mFile.Shift(pos);
#region self event
public event Action<bool, bool, bool> UpdateDataUI;
public event Action<ToolMode> UpdateToolMode;
public event Action<SelectionHelp> UpdateSelection;
private void OnUpdateDataUI() {
ToolMode mode = mSelectionHelp.GetToolMode();
bool showCursorPasteAddDeleteOne = mode == ToolMode.Cursor && mSelectionHelp.IsDataPartialReady();
bool showFill = mode == ToolMode.Fill && mSelectionHelp.IsDataReady();
bool showCursorCopyDelete = mode == ToolMode.Cursor && mSelectionHelp.IsDataReady();
UpdateDataUI?.Invoke(showCursorPasteAddDeleteOne, showFill, showCursorCopyDelete);
}
private void OnUpdateToolMode() {
UpdateToolMode?.Invoke(mSelectionHelp.GetToolMode());
}
private void OnUpdateSelection() {
UpdateSelection?.Invoke(mSelectionHelp);
}
#endregion
#region process event
private void funcSlider_ValueChanged(long pos) {
mFile.Shift(pos);
RefreshDisplay();
}
private void updateSliderRange() {
mSlider.Maximum = mFile.mFrameCount - 1;
mSlider.Value = mFile.GetPointerIndex();
}
private void funcSelectionHelp_SelectionChanged() {
mDataGrid.RefreshDataMenu();
mDataGrid.RefreshSelectionHighlight();
OnStatusbarSelectionChanged();
OnUpdateDataUI();
OnUpdateSelection();
}
private void OnStatusbarSelectionChanged() {
var mode = mSelectionHelp.GetToolMode();
private void funcDataMenu_Click() {
var data = mSelectionHelp.GetPoint();
var field = (int)mSelectionHelp.GetPointField();
mFile.Set(new SelectionRange(field, field), new SelectionRange(data, data), null);
RefreshDisplay();
}
switch (mode) {
case ToolMode.Cursor:
if (mSelectionHelp.IsDataReady()) {
var data = mSelectionHelp.GetRange();
mStatusbar.Text = $"{data.start} - {data.end}";
} else if (mSelectionHelp.IsDataPartialReady()) {
var data2 = mSelectionHelp.GetPoint();
mStatusbar.Text = data2.ToString();
} else mStatusbar.Text = "-";
break;
case ToolMode.Fill:
if (mSelectionHelp.IsDataReady()) {
var data3 = mSelectionHelp.GetRange();
mStatusbar.Text = $"{data3.start} - {data3.end}";
} else mStatusbar.Text = "-";
break;
case ToolMode.Overwrite:
if (mSelectionHelp.IsDataReady()) {
var data4 = mSelectionHelp.GetPoint();
mStatusbar.Text = data4.ToString();
} else mStatusbar.Text = "-";
break;
}
}
#endregion
public void ChangeOverwrittenMode(bool isOverwritten) {
mOverwrittenPaste = isOverwritten;
}
public void ChangeListLength(int newLen) {
if (newLen < 5 || newLen > 30) return;
if (newLen < 5 || newLen > 200) return;
int offset = newLen - mListLength;
int abs = Math.Abs(offset);
if (offset == 0) return;
@ -147,6 +137,7 @@ namespace BallanceTASEditor.UI {
public void ChangeToolMode(ToolMode mode) {
mSelectionHelp.SetMode(mode);
OnUpdateToolMode();
}
public int GetItemCountInPage() {
@ -161,6 +152,25 @@ namespace BallanceTASEditor.UI {
RefreshDisplay();
}
break;
case OperationEnum.Cut: {
// cut is a hybrid operation, first, do copy
// then delete selected item
// due to copy is not affect TASFile and only delete oper affect it
// so this is a revocable oper
var data = new LinkedList<FrameData>();
mFile.Copy(mSelectionHelp.GetRange(), data);
if (!ClipboardUtil.SetFrameData(data)) {
MessageBox.Show("Fail to cut due to unknow reason!");
break; // if fail to cut, do not delete selected items.
}
// do delete
mFile.Remove(mSelectionHelp.GetRange());
mSelectionHelp.Reset();
mSlider.UpdateRange(mFile);
RefreshDisplay();
}
break;
case OperationEnum.Copy: {
var data = new LinkedList<FrameData>();
mFile.Copy(mSelectionHelp.GetRange(), data);
@ -174,7 +184,7 @@ namespace BallanceTASEditor.UI {
if (ClipboardUtil.GetFrameData(data)) {
mFile.Insert(mSelectionHelp.GetPoint(), data, oper == OperationEnum.PasteBefore, mOverwrittenPaste);
mSelectionHelp.Reset();
updateSliderRange();
mSlider.UpdateRange(mFile);
RefreshDisplay();
} else MessageBox.Show("Fail to paste due to unknow reason or blank clipboard!");
}
@ -182,7 +192,7 @@ namespace BallanceTASEditor.UI {
case OperationEnum.Delete: {
mFile.Remove(mSelectionHelp.GetRange());
mSelectionHelp.Reset();
updateSliderRange();
mSlider.UpdateRange(mFile);
RefreshDisplay();
}
break;
@ -192,12 +202,12 @@ namespace BallanceTASEditor.UI {
if (oper == OperationEnum.DeleteBefore) pos -= 1; // delete after mean delete current selected item
if (pos < 0 || pos >= mFile.mFrameCount) return;
// only delete before need shift selection
// delete before need shift selection
// delete before couldn't cause empty list, so we just need to directly shift
if (oper == OperationEnum.DeleteBefore)
mSelectionHelp.ShiftTo(false);
// also, if we use delete after and delete the tail of item list, we also need to shift pos(use `else if` to prevent double shift)
else if (oper == OperationEnum.DeleteAfter && pos == mFile.mFrameCount) {
else if (oper == OperationEnum.DeleteAfter && pos == mFile.mFrameCount - 1) {
// but delete after may cause empty list error(delete the item within only 1 item list)
// so we need prevent this situation
if (mFile.mFrameCount == 1) mSelectionHelp.Reset(); //yes, reset selection to prevent error
@ -207,7 +217,7 @@ namespace BallanceTASEditor.UI {
// do real operation
mFile.Remove(new SelectionRange(pos, pos));
updateSliderRange();
mSlider.UpdateRange(mFile);
RefreshDisplay();
}
break;
@ -218,40 +228,26 @@ namespace BallanceTASEditor.UI {
var pos = mSelectionHelp.GetPoint();
mFile.Add(pos, count, deltaTime, oper == OperationEnum.AddBefore);
mSelectionHelp.Reset();
updateSliderRange();
mSlider.UpdateRange(mFile);
RefreshDisplay();
}
break;
case OperationEnum.Undo: {
mFile.Undo();
mSelectionHelp.Reset();
updateSliderRange();
mSlider.UpdateRange(mFile);
RefreshDisplay();
}
break;
case OperationEnum.Redo: {
mFile.Redo();
mSelectionHelp.Reset();
updateSliderRange();
mSlider.UpdateRange(mFile);
RefreshDisplay();
}
break;
}
}
#region data menu
private void funcDataMenu_Click() {
var data = mSelectionHelp.GetPoint();
var field = (int)mSelectionHelp.GetPointField();
mFile.Set(new SelectionRange(field, field), new SelectionRange(data, data), null);
RefreshDisplay();
}
private void funcDataMenu_NewOperation(OperationEnum obj) {
ProcessOperation(obj);
}
#endregion
}
}

View File

@ -3,9 +3,37 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BallanceTASEditor.UI {
public static class Util {
public static void GridRowAdder(Grid container, GridLength size) {
var item = new RowDefinition();
item.Height = size;
container.RowDefinitions.Add(item);
}
public static void GridColumnAdder(Grid container, GridLength size) {
var item = new ColumnDefinition();
item.Width = size;
container.ColumnDefinitions.Add(item);
}
public static void SwapGridItemRC(UIElement item) {
var swap = Grid.GetColumn(item);
Grid.SetColumn(item, Grid.GetRow(item));
Grid.SetRow(item, swap);
}
}
public enum ToolMode {
Cursor,

BIN
BallanceTASEditor/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="zlib.net" version="1.0.4.0" targetFramework="net40" />
<package id="DotNetZip" version="1.9.1.8" targetFramework="net45" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
</packages>

119
README.md
View File

@ -1 +1,120 @@
# Ballance TAS Editor
A editor written for TAS file editing.
## Deprecation Note
I have lost all my interests on this game, Ballance, since 1 year ago. However, recently, I noticed that there were some improvements and modifications in Ballance TAS. What I want to clarified is that I have no intention on updating this project to adapt the new format or feature of Ballance TAS. So I archive this repository to explicitly indicate this standpoint.
However, this status is not prepetual. I will reopen this repository until I have learned enough text editor skills, such as Gap Buffer, to improve this application, or, restored the interests on this game (barely but still possible). You can freely fork this repository, or create new Ballance TAS editor on your own. That's your right granted by open source license.
## Basic Interface
### Program Menu
* File
* Open: Open a TAS file.
* Save: Save modifications to opended TAS file.
* Save as...: Save the modifications as a new TAS file and switch to the new file.
* Close: Close current file.
* Edit
* Undo: Undo a operation.
* Redo: Redo previous undo operation.
* Item Count: Set the count of items showed in editor panel.
* Overwritten Paste: Switch paste mode between Overwritten Mode and Insert Mode.
* Horizontal Layout: Switch editor layout between horzontal layout and vertical layout.
* Help
* Report Bugs: Open a web page to report bugs about this program.
* About: Open a dialog to show some infomations about this prrogram.
### TAS Unit Menu
Right click TAS unit will open a menu. The enable statue of each is decided by your current tools and selected items.
|Operation|Needed mode|Needed selection|What the operation do|
|:---|:---|:---|:---|
|Set|Fill mode|Multiple selection|Set selected units as *set* status|
|Unset|Fill mode|Multiple selection|Set selected units as *unset* status|
|Cut|Cursor mode|Multiple selection|Cut selected units|
|Copy|Cursor mode|Multiple selection|Copy selected units|
|Paste after this|Cursor mode|Single selection|Pasted copied units after selected unit|
|Paste before this|Cursor mode|Single selection|Pasted copied units before selected unit|
|Delete|Cursor mode|Single selection|Delete selected units|
|Delete this frame|Cursor mode|Single selection|Delete selected unit, and move selection to the next unit|
|Delete last frame|Cursor mode|Single selection|Delete one unit aheading of selected unit|
|Add blank item after this|Cursor mode|Single selection|Add blank unit after selected unit|
|Add blank item before this|Cursor mode|Single selection|Add blank unit before selected unit|
### Status Bar
A status bar will be shown at the bottom of window if you opended a TAS file. The item located in status bar from left to right is: Current tools mode, Overwritten paste status, Current selected region.
## View
### Basic Operation
Once a file opened, a slider will be placed at the bottom of window. You can drag it and go to any position of this file which you want to browse.
4 buttons were located at the left of slider, from left to right is: fast rewind, rewind, forward, fast forward. Fase backward and fast forward will rewind or forward the number of units by one page at a time.
If you want to change the count of shown units in display panel, please use Edit - Item Count to change it.
### Quick Operation
The key ASDF of keyboard are corresponding with the functions of 4 buttons.
A mouse wheel will scroll 1 TAS unit.
Or, if you press Shift at the same time, it will scroll 1 page TAS units.
Or, if you press Crtl at the same time, it will increase or decrease the count of shown item in display panel.
### Horzontal Layout and Vertical Layout
After opening a file, you can use Edit - Horizontal Layout to switch layout between horzontal layout and vertical layout. Different layouts suit for different people. Please choose your favorite layout freely.
## Mode Introduction
### Basic Mode
Once ths file opened, 3 tools buttons were placed at the top of window. They are:
* Cursor mode: Allow you pick TAS unit by column. In this mode, you can copy, paste, insert, delete and etc...
* Fill mode: Allow you select TAS unit by cell. And you can set or unset the status of cells.
* Draw mode: See cursor as brush. Flip the set status of clicked cell.
### Multiple and Single Selection
Cursor mode allow single selection. Click a TAS unit directly, you can select it.
Cursor mode and Fill mode allow multiple selection. For a multiple selection, you should click a cell as the start point, then hold on shift to click another cell, as the end of selection.
Click a cell again will start a new selection, hold shift click a cell to finish the new multiple selection.
If you want to multiple select a single cell(if operation require), you can hold on shift to click the start of selection to finish a single cell's multiple selection.
### Highlight of Selection
In cursor mode, selected TAS unit will show a orange rectangle at the top of them.
In fill mode, selected cell will show with orange border.
## Special Operation
### Overwritten Paste and Insert Paste
Paste have 2 mode, overwritten paste and insert paste.
Insert will insert copied content before or after selected unit.
However, overwrite pasting will use the currently selected cell as the beginning or end of the paste, and write the contents of the clipboard forward or backward directly. The original data of affected unit will be wipe out. If the length of data is not enough, additional items will be added.
### Shortcut
* `Ctrl + O`Open file
* `Ctrl + S`Save file
* `Ctrl + Z`Undo
* `Ctrl + Y`Redo
* `Ctrl + X`Cut
* `Ctrl + C`Copy
* `Ctrl + V`Paste After
* `Delete`Delete this frame
* `Backspace`Delete last frame
## Something Went Wrong
If program crashed, program will output a error log in the folder called `logs`. Please send error log to developer to help us to locate and reproduce error.

View File

@ -2,40 +2,83 @@
一款专门用于编辑Ballance TAS文件的编辑器
## 基本操作
## 弃用说明
### 菜单
一年前,我对 Ballance 这款游戏失去了兴趣。然而,最近,我注意到 Ballance TAS 有一些改进和修改。我想澄清的是,我无意更新这个项目以适应 Ballance TAS 的新格式或功能。所以我存档了这个存储库以明确表明这一立场。
* File
* Open打开一个TAS文件
* Save保存当前改动到TAS文件
* Save As...:将当前改动保存到另一个新地方,保存完毕后文件自动切换成新目标,之后的保存操作将针对新文件来保存
* Close关闭当前文件
* Display
* Item Count设置一行显示多少个TAS操作单元最少5个最多30个
* Help
* Report bugs打开一个网页来汇报这个程序的Bug
* About关于此程序
但是,这种状态不是永久的。我将重新打开这个存储库,直到我学会了足够的文本编辑器技能(例如 Gap Buffer来改进这个应用程序或者恢复对这个游戏的兴趣勉强但仍然可能。您可以自由地分叉这个存储库或者自己创建新的 Ballance TAS 编辑器。这是开源许可证授予您的权利。
## 基本界面
### 程序菜单
* 文件
* 打开打开一个TAS文件
* 保存保存当前改动到TAS文件
* 另存为...:将当前改动保存到另一个新地方,保存完毕后文件自动切换成新目标,之后的保存操作将针对新文件来保存
* 关闭:关闭当前文件
* 编辑
* 撤销:撤销上一步操作
* 重做:重做上个撤销的操作
* 项个数设置一个页面显示多少个TAS操作单元。
* 覆盖式粘贴:决定粘贴时的操作是插入式还是覆盖式,详情请查看“覆盖式粘贴”章节
* 横向布局决定TAS单元的显示方向选中为横向布局不选中则为纵向布局
* 帮助
* 汇报漏洞打开一个网页来汇报这个程序的Bug
* 关于:关于此程序
### TAS单元菜单
在TAS单元页面右键将打开一个操作菜单其会根据您当前选择的工具和选中的项目决定可以做的操作
|操作|需要的模式|需要的选中|作用|
|:---|:---|:---|:---|
|设置|填充模式|多选|设置选中单元为设置状态|
|不设置|填充模式|多选|设置选中单元为不设置状态|
|剪切|选择模式|多选|剪切选中单元|
|复制|选择模式|多选|复制选中单元|
|粘贴于后方|选择模式|单选|在其后方粘贴剪贴板中已复制的单元|
|粘贴于前方|选择模式|单选|在其前方粘贴剪贴板中已复制的单元|
|删除|选择模式|单选|删除选中单元|
|向后删除|选择模式|单选|删除当前选中单元,将光标移至后一项|
|向前删除|选择模式|单选|删除当前选中单元之前的一项|
|在后方添加新项|选择模式|单选|在其后方添加空白单元|
|在前方添加新项|选择模式|单选|在其前方添加空白单元|
### 状态栏
在打开文件后,底部状态栏将显示当前的状态:当前工具模式 和 当前选定的区域
在打开文件后,底部状态栏将显示当前的状态,从左至右分别是:当前工具模式,覆盖式粘贴状态 和 当前选定的区域
## 移动视图
## 视图
### 基本操作
打开文件后,靠近底部有一个滑条,可以快速滑动到希望浏览的位置。
滑条左侧有4个按钮分别是快退退一个单元进一个单元快进。快进和快退将一次性前进或后退一个页面的单元数量。
如果要控制界面中显示的TAS单元数量可以选择编辑 - 项个数进行设置
### 快速操作
键盘上的ASDF四个键从左至右也分别对应滑条左侧的四个按钮的功能。
鼠标滚轮一次可以一次向前或向后滚动一个TAS单元。
如果按住Shift滚动鼠标滚轮可以一次向前或向后滚动一整页。
如果按住Ctrl滚动鼠标滚轮可以增加或减少当前页面显示的TAS单元的个数。
### 横向布局与纵向布局
打开文档后,通过编辑 - 横向布局的勾选来确定启用的布局,不同的布局适用于不同的人,请根据自身喜好选择。
## 模式介绍
### 基本模式
打开文件后上部3个带有图标的按钮是TAS编辑器的三种模式它们分别是
* Select mode选择模式允许成列的选择TAS操作单元。在此模式下可以进行复制粘贴插入和删除操作。
* Fill mode填充模式:像编辑表格那样,允许多列选择和跨行选择(但必须连续),并且支持在选择区域内批量设置或不设置项目
* Draw mode画模式:将鼠标视为画笔,反转点击的单元格的设置状态。
* 选择模式允许成列的选择TAS操作单元。在此模式下可以进行复制粘贴插入和删除操作。
* 填充模式:像编辑表格那样,允许多列选择和跨行选择(但必须连续),并且支持在选择区域内批量设置或不设置项目
*模式:将鼠标视为画笔,反转点击的单元格的设置状态。
### 如何单选与多选
@ -43,6 +86,7 @@
选择模式和填充模式允许多选具体操作是点击某个单元格作为起始位置然后按住Shift点击第二个单元格作为终止位置。完成多选。
再次左键单击重新开始一次选择。再次按住Shift左键单击重新选定终止位置。
如果您仅仅只需要选中一个单元格但是要求的操作需要多选可以再按Shift点击当前单元完成多选。
### 选择的标识
@ -50,18 +94,25 @@
填充模式下,选中的单元格的外边框将从灰色变为橘色。
### 模式中的菜单
## 特殊操作
在表格页面右键将打开一个操作菜单,如下:
### 覆盖式粘贴与插入式粘贴
* Set位于填充模式且已多选时设置选中单元为设置状态
* Unset位于填充模式且已多选时设置选中单元为不设置状态
* Copy位于选择模式且已多选时复制选中单元
* Delete位于选择模式且已多选时删除选中单元
* Paste after this位于选择模式且已单选时在其前方粘贴剪贴板中已复制的单元
* Paste before this位于选择模式且已单选时在其后方粘贴剪贴板中已复制的单元
* Add blank item after this位于选择模式且已单选时在其前方添加空白单元
* Add blank item before this位于选择模式且已单选时在其后方添加空白单元
覆盖式粘贴与插入式粘贴为粘贴的两种模式。
插入式粘贴为在选中项前或后方插入剪贴板内的内容。
覆盖式粘贴则将会以当前选中单元为粘贴的头或尾,向前或向后覆盖式写入剪贴板内的内容,如果后方或前方数据长度不够,则会添加额外的项目补充。
### 快捷键
* `Ctrl + O`:打开文件
* `Ctrl + S`:保存文件
* `Ctrl + Z`:撤销
* `Ctrl + Y`:重做
* `Ctrl + X`:剪切
* `Ctrl + C`:复制
* `Ctrl + V`:向后粘贴
* `Delete`:向后删除
* `Backspace`:向前删除
## 出错啦