write shit

This commit is contained in:
2021-05-21 22:37:25 +08:00
parent 2adefe86f4
commit 4aacc76a49
9 changed files with 320 additions and 131 deletions

View File

@ -59,6 +59,9 @@
</ApplicationDefinition>
<Compile Include="Core\ClipboardUtil.cs" />
<Compile Include="Core\KeyboardState.cs" />
<Compile Include="UI\AddItem.xaml.cs">
<DependentUpon>AddItem.xaml</DependentUpon>
</Compile>
<Compile Include="UI\SelectionHelp.cs" />
<Compile Include="UI\StyleConverter.cs" />
<Compile Include="UI\TASFlow.xaml.cs">
@ -83,6 +86,10 @@
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="UI\AddItem.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UI\TASFlow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -17,29 +17,32 @@ namespace BallanceTASEditor.Core {
fs.Close();
fs.Dispose();
mPointer = mMem.First;
mPointerIndex = 0;
}
public string mFilename { get; private set; }
public long mFrameCount { get { return mMem.Count; } }
LinkedList<FrameData> mMem;
LinkedListNode<FrameData> mPointer;
long mPointerIndex;
public void Shift(long shiftNum) {
if (mPointer == null) return;
if (shiftNum == 0) return;
var absNum = Math.Abs(shiftNum);
if (shiftNum > 0) {
for(long num = 0; num < absNum && mPointer.Next != null; num++) {
mPointer = mPointer.Next;
}
} else {
for (long num = 0; num < absNum && mPointer.Previous != null; num++) {
mPointer = mPointer.Previous;
}
}
public bool IsEmpty() {
return (mPointer == null);
}
public void Get(List<FrameDataDisplay> container, long startIndex, int count) {
public long GetPointerIndex() {
if (mPointer == null) throw new Exception("Data is not ready");
return mPointerIndex;
}
public void Shift(long absoluteIndex) {
if (mPointer == null) return;
mPointer = mMem.FastGetNode(mPointer, mPointerIndex, absoluteIndex);
mPointerIndex = absoluteIndex;
}
public void Get(List<FrameDataDisplay> container, int count) {
// no item. clean container
if (mPointer == null) {
for(int j = 0; j < count; j++) {
@ -50,6 +53,7 @@ namespace BallanceTASEditor.Core {
// fill container
var cachePointer = mPointer;
var startIndex = mPointerIndex;
int i;
for(i = 0; i < count && cachePointer != null; i++, startIndex++) {
container[i].Reload(startIndex, cachePointer.Value);
@ -62,41 +66,102 @@ namespace BallanceTASEditor.Core {
}
// if isSet is null, mean flip state
public void Set(SelectionRange field, SelectionRange relativeRange, bool? isSet) {
public void Set(SelectionRange field, SelectionRange absoluteRange, bool? isSet) {
if (mPointer == null) return;
var cachePointer = mPointer;
uint offset = 0;
for(int i = (int)field.start; i <= (int)field.end; i++) {
offset |= ConstValue.Mapping[(FrameDataField)i];
}
foreach(var item in mMem.IterateWithSelectionRange(relativeRange, mPointer)) {
foreach(var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
if (isSet == null) item.Value.ReverseKeyStates(offset);
else if (isSet == true) item.Value.SetKeyStates(offset);
else if (isSet == false) item.Value.UnsetKeyStates(offset);
}
}
public void Remove(SelectionRange relativeRange) {
public void Remove(SelectionRange absoluteRange) {
if (mPointer == null) return;
mMem.RemoveWithSelectionRange(relativeRange, mPointer);
// todo: fix pointer point to invalid item
// remove
foreach(var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
mMem.Remove(item);
}
// correct index data
// if state is true, it mean the deleted content is placed before pointer previously.
// so we need shift the pointer to the head of selection range.
// and we should consider 2 situations, the full delete of LinkedList and delete from head
if (mPointerIndex <= absoluteRange.end) {
var newIndex = absoluteRange.start - 1;
if (newIndex < 0) {
// this contains 2 situation
// if full delete, mPointer is null and mPointerIndex is invalid(with wrong data: 0)
// if delete from head, mPointer and mPointerIndex all are valid.
mPointer = mMem.First;
mPointerIndex = 0;
} else {
mPointer = mMem.FastGetNode(mPointer, mPointerIndex, newIndex);
mPointerIndex = newIndex;
}
}
}
public void Add(long relativePos, bool isAddBefore) {
public void Add(long absolutePos, long count, float deltaTime, bool isAddBefore) {
if (count <= 0) return;
if (mPointer == null) {
// add into blank list, absolutePos and isAddBefore parameters are invalid
// specially process
for(long i = 0; i < count; i++) {
mMem.AddFirst(new FrameData(deltaTime, 0));
}
mPointer = mMem.First;
mPointerIndex = 0;
} else {
// normal add
// normal add doesn't affect pointer
LinkedListNode<FrameData> node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos);
if (isAddBefore) {
for (long i = 0; i < count; i++) {
mMem.AddBefore(node, new FrameData(deltaTime, 0));
}
} else {
for (long i = 0; i < count; i++) {
mMem.AddAfter(node, new FrameData(deltaTime, 0));
}
}
}
}
public void Insert(long relativePos, LinkedList<FrameData> data, bool isInsertBefore) {
public void Insert(long absolutePos, LinkedList<FrameData> data, bool isInsertBefore) {
if (data.Count == 0) return;
// the same process route with add function
if (mPointer == null) {
foreach (var item in data.IterateFull()) {
mMem.AddFirst(item.Value);
}
mPointer = mMem.First;
mPointerIndex = 0;
} else {
LinkedListNode<FrameData> node = mMem.FastGetNode(mPointer, mPointerIndex, absolutePos);
if (isInsertBefore) {
foreach (var item in data.IterateFull()) {
mMem.AddBefore(node, item.Value);
}
} else {
foreach (var item in data.IterateFull()) {
mMem.AddAfter(node, item.Value);
}
}
}
}
public void Copy(SelectionRange absoluteRange, LinkedList<FrameData> data) {
if (mPointer == null) return;
}
public void Copy(SelectionRange relativeRange, LinkedList<FrameData> data) {
if (mPointer == null) return;
foreach (var item in mMem.IterateWithSelectionRange(relativeRange, mPointer)) {
foreach (var item in mMem.IterateWithSelectionRange(absoluteRange, mPointer, mPointerIndex)) {
data.AddLast(item.Value);
}
}

View File

@ -28,52 +28,66 @@ namespace BallanceTASEditor.Core {
}
}
public static LinkedListNode<FrameData> FastGetNode(this LinkedList<FrameData> ls, LinkedListNode<FrameData> refNode, long refIndex, long targetIndex) {
long count = ls.Count;
if (targetIndex >= count || refIndex >= count) throw new Exception("Index is invalid!");
var span = new StupidSortStruct[3] {
new StupidSortStruct() { type = 1, data = targetIndex },
new StupidSortStruct() { type = 2, data = count - targetIndex },
new StupidSortStruct() { type = 3, data = targetIndex - refIndex }
};
public static IEnumerable<LinkedListNode<FrameData>> IterateWithSelectionRange(this LinkedList<FrameData> ls, SelectionRange relativeRange, LinkedListNode<FrameData> current) {
if (current == null) goto end;
// goto header first
long counter;
var cache = current.TryShiftTo(relativeRange.start, out counter);
while (counter <= relativeRange.end && cache != null) {
yield return cache;
cache = cache.Next;
counter++;
// sort to get the min value
StupidSortStruct tmp;
if (Math.Abs(span[0].data) < Math.Abs(span[1].data)) {
tmp = span[0];
span[0] = span[1];
span[1] = tmp;
}
if (Math.Abs(span[1].data) < Math.Abs(span[2].data)) {
tmp = span[1];
span[2] = span[1];
span[2] = tmp;
}
end:;
LinkedListNode<FrameData> iterateNode;
if (span[2].type == 1) iterateNode = ls.First;
else if (span[2].type == 2) iterateNode = ls.Last;
else if (span[2].type == 3) iterateNode = refNode;
else throw new Exception("Unknow node type");
return iterateNode.ShiftTo(span[2].data);
}
public static void RemoveWithSelectionRange(this LinkedList<FrameData> ls, SelectionRange relativeRange, LinkedListNode<FrameData> current) {
if (current == null) goto end;
// remove safety. because it store the next node.
public static IEnumerable<LinkedListNode<FrameData>> IterateWithSelectionRange(this LinkedList<FrameData> ls, SelectionRange absoluteRange, LinkedListNode<FrameData> refNode, long refIndex) {
// goto header first
long counter;
var cache = current.TryShiftTo(relativeRange.start, out counter);
var cache = ls.FastGetNode(refNode, refIndex, absoluteRange.start);
var counter = absoluteRange.start;
LinkedListNode<FrameData> cacheNextNode;
while (counter <= relativeRange.end && cache != null) {
while (counter <= absoluteRange.end) {
if (cache == null) throw new Exception("Unexpected head or tail of linked list!");
cacheNextNode = cache.Next;
ls.Remove(cache);
yield return cache;
cache = cacheNextNode;
counter++;
}
end:;
}
public static LinkedListNode<FrameData> TryShiftTo(this LinkedListNode<FrameData> node, long offset, out long realShifted) {
public static LinkedListNode<FrameData> ShiftTo(this LinkedListNode<FrameData> node, long offset) {
var cache = node;
realShifted = 0;
long realShifted = 0;
if (offset < 0) {
while (realShifted != offset && cache.Previous != null) {
while (realShifted != offset) {
if (cache.Previous == null) throw new Exception("Unexpected head or tail of linked list!");
cache = cache.Previous;
realShifted--;
}
} else if (offset > 0) {
while (realShifted != offset && cache.Next != null) {
while (realShifted != offset) {
if (cache.Next == null) throw new Exception("Unexpected head or tail of linked list!");
cache = cache.Next;
realShifted++;
}
@ -82,4 +96,37 @@ namespace BallanceTASEditor.Core {
return cache;
}
}
public struct SelectionRange {
public SelectionRange(long value1, long value2) {
if (value1 > value2) {
start = value2;
end = value1;
} else {
start = value1;
end = value2;
}
}
public long start;
public long end;
public SelectionRange GetRelative(long refer) {
var res = new SelectionRange();
res.start = start - refer;
res.end = end - refer;
return res;
}
public bool Within(long num) {
return (num >= start && num <= end);
}
public long GetCount() {
return end - start;
}
}
public struct StupidSortStruct {
public int type;
public long data;
}
}

49
UI/AddItem.xaml Normal file
View File

@ -0,0 +1,49 @@
<Window x:Class="BallanceTASEditor.UI.AddItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTASEditor.UI"
xmlns:converter="clr-namespace:BallanceTASEditor.UI"
mc:Ignorable="d"
Title="Add Item" Height="200" Width="400">
<Window.Resources>
<converter:AddItemConverter x:Key="conv_addItem"/>
<converter:FPS2DeltaTimeConverter x:Key="conv_fps2DeltaTime"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<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"/>
<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"/>
<TextBlock x:Name="uiText_DeltaTime" Margin="5" Padding="5" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center"
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.IsEnabled>
<MultiBinding Converter="{StaticResource conv_addItem}" Mode="OneWay">
<Binding ElementName="uiTextbox_Count" Path="Text"/>
<Binding ElementName="uiTextbox_FPS" Path="Text"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
<Button x:Name="uiBtn_Cancel" Margin="5" Padding="5" Content="Cancel" MinWidth="50" Click="funcBtn_Cancel"/>
</StackPanel>
</Grid>
</Window>

42
UI/AddItem.xaml.cs Normal file
View File

@ -0,0 +1,42 @@
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.Shapes;
namespace BallanceTASEditor.UI {
/// <summary>
/// AddItem.xaml 的交互逻辑
/// </summary>
public partial class AddItem : Window {
public AddItem() {
InitializeComponent();
}
public int Output_Count { get; private set; }
public float Output_DeltaTime { get; private set; }
private void funcBtn_OK(object sender, RoutedEventArgs e) {
int count;
float fps;
if (!int.TryParse(uiTextbox_Count.Text, out count)) return;
if (!float.TryParse(uiTextbox_FPS.Text, out fps)) return;
Output_Count = count;
Output_DeltaTime = 1000f / fps;
this.DialogResult = true;
}
private void funcBtn_Cancel(object sender, RoutedEventArgs e) {
this.DialogResult = false;
}
}
}

View File

@ -42,5 +42,18 @@ namespace BallanceTASEditor.UI {
return true;
}
public static bool AddItemDialog(out int count, out float deltaTime) {
var win = new AddItem();
if (!(bool)win.ShowDialog()) {
count = 0;
deltaTime = 0f;
return false;
}
count = win.Output_Count;
deltaTime = win.Output_DeltaTime;
return true;
}
}
}

View File

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BallanceTASEditor.Core;
namespace BallanceTASEditor.UI {
public class SelectionHelp {
@ -127,29 +128,4 @@ namespace BallanceTASEditor.UI {
}
public struct SelectionRange {
public SelectionRange(long value1, long value2) {
if (value1 > value2) {
start = value2;
end = value1;
} else {
start = value1;
end = value2;
}
}
public long start;
public long end;
public SelectionRange GetRelative(long refer) {
var res = new SelectionRange();
res.start = start - refer;
res.end = end - refer;
return res;
}
public bool Within(long num) {
return (num >= start && num <= end);
}
public long GetCount() {
return end - start;
}
}
}

View File

@ -3,55 +3,41 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace BallanceTASEditor.UI {
[ValueConversion(typeof(bool), typeof(Color))]
public class BackgroundConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
public class AddItemConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
try {
bool bl = System.Convert.ToBoolean(value);
if (bl) return Color.FromRgb(30, 144, 255);
else return Color.FromArgb(0, 255, 255, 255);
} catch {
return Color.FromArgb(0, 255, 255, 255);
var textCount = values[0] as string;
var textFps = values[1] as string;
var count = int.Parse(textCount);
var fps = float.Parse(textFps);
if (count <= 0 || fps <= 0) return false;
return true;
} catch {
return false;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
return null;
}
}
[ValueConversion(typeof(float), typeof(string))]
public class FloatConverter : IValueConverter {
public class FPS2DeltaTimeConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
try {
float bl = System.Convert.ToSingle(value);
if (bl < 0) return "";
else return bl.ToString();
} catch {
return "";
}
}
var text = value as string;
if (text == null) return "0";
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return null;
}
}
[ValueConversion(typeof(long), typeof(string))]
public class LongConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
try {
float bl = System.Convert.ToInt64(value);
if (bl < 0) return "";
else return bl.ToString();
} catch {
return "";
}
float data;
if (!float.TryParse(text, out data)) return "0";
return (1000f / data).ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {

View File

@ -25,7 +25,6 @@ namespace BallanceTASEditor.UI {
mSelectionHelp.SelectionChanged += funcSelectionHelp_SelectionChanged;
// init data
mPosition = 0;
INVALID_FRAME_DATA = new FrameData(-1f, 0);
mDataSource = new List<FrameDataDisplay>();
mListLength = 0;
@ -73,17 +72,14 @@ namespace BallanceTASEditor.UI {
TextBlock mStatusbar;
TASFlow mDataGrid;
SelectionHelp mSelectionHelp;
long mPosition;
int mListLength;
List<FrameDataDisplay> mDataSource;
private void sliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
long pos = Convert.ToInt64(Math.Floor(e.NewValue));
long offset = pos - mPosition;
mFile.Shift(offset);
mFile.Shift(pos);
RefreshDisplay(pos);
mPosition = pos;
RefreshDisplay();
}
private void updateSliderRange() {
@ -150,11 +146,11 @@ namespace BallanceTASEditor.UI {
mListLength = newLen;
// then refresh
RefreshDisplay(mPosition);
RefreshDisplay();
}
public void RefreshDisplay(long pos) {
mFile.Get(mDataSource, pos, mListLength);
public void RefreshDisplay() {
mFile.Get(mDataSource, mListLength);
mDataGrid.RefreshDataSources();
mDataGrid.RefreshSelectionHighlight();
}
@ -168,11 +164,19 @@ namespace BallanceTASEditor.UI {
#region data menu
private void funcDataMenu_AddBefore(object sender, RoutedEventArgs e) {
throw new NotImplementedException();
if (!DialogUtil.AddItemDialog(out int count, out float deltaTime)) return;
var pos = mSelectionHelp.GetPoint();
mFile.Add(pos, count, deltaTime, true);
RefreshDisplay();
}
private void funcDataMenu_AddAfter(object sender, RoutedEventArgs e) {
throw new NotImplementedException();
if (!DialogUtil.AddItemDialog(out int count, out float deltaTime)) return;
var pos = mSelectionHelp.GetPoint();
mFile.Add(pos, count, deltaTime, false);
RefreshDisplay();
}
private void funcDataMenu_PasteBefore(object sender, RoutedEventArgs e) {
@ -192,20 +196,20 @@ namespace BallanceTASEditor.UI {
}
private void funcDataMenu_Unset(object sender, RoutedEventArgs e) {
mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange().GetRelative(mPosition), false);
RefreshDisplay(mPosition);
mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange(), false);
RefreshDisplay();
}
private void funcDataMenu_Set(object sender, RoutedEventArgs e) {
mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange().GetRelative(mPosition), true);
RefreshDisplay(mPosition);
mFile.Set(mSelectionHelp.GetFieldRange(), mSelectionHelp.GetRange(), true);
RefreshDisplay();
}
private void funcDataMenu_Click() {
var data = mSelectionHelp.GetPoint() - mPosition;
var data = mSelectionHelp.GetPoint();
var field = (int)mSelectionHelp.GetPointField();
mFile.Set(new SelectionRange(field, field), new SelectionRange(data, data), null);
RefreshDisplay(mPosition);
RefreshDisplay();
}
#endregion