diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..17d9055
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..f6b3a3d
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace RDTKEditor
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..df9dfed
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..7158024
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,257 @@
+using Microsoft.Win32;
+using RDTKEditor.Models;
+using RhythmBase.Events;
+using SkiaSharp;
+using System.ComponentModel;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Shapes;
+
+namespace RDTKEditor
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ private readonly RDTKView viewModel = new();
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ private void OpenFile(object sender, RoutedEventArgs e)
+ {
+ OpenFileDialog openFileDialog = new()
+ {
+ Filter = "关卡文件|*.rdlevel|关卡压缩包 (*.rdzip)|*.rdzip"
+ };
+ if (openFileDialog.ShowDialog() == true)
+ {
+ string filename = openFileDialog.FileName;
+ viewModel.UpdateLevelByPath(filename);
+ }
+ }
+ private void SaveFile(object sender, RoutedEventArgs e)
+ {
+ OpenFileDialog openFileDialog = new()
+ {
+ Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
+ };
+ if (openFileDialog.ShowDialog() == true)
+ {
+ string filename = openFileDialog.FileName;
+ viewModel.SaveLevelByPath(filename);
+ }
+ }
+ private void Exit(object sender, RoutedEventArgs e)
+ {
+ Application.Current.Shutdown();
+ }
+ private void About(object sender, RoutedEventArgs e)
+ {
+ OnUpdateEventProperties(GetProperties(new MoveRow()));
+
+ //OnUpdateEventProperties([
+ // new(){
+ // Verifier = new RDTKPropertyVerifierDefault(),
+ // Name = "Name",
+ // Value = "RDTK Editor",
+ // Type = RDPropertyType.String,
+ // },
+ // new(){
+ // Verifier = new RDTKPropertyVerifierDefault(),
+ // Name = "Version",
+ // Value = "1.0.0",
+ // Type = RDPropertyType.String,
+ // },
+ // new(){
+ // Verifier = new RDTKPropertyVerifierEnum(typeof(EventType)),
+ // Name = "Description",
+ // Value = "RDTK Editor 是一个简单的节奏医生关卡编辑器。",
+ // Type = RDPropertyType.Enum,
+ // },
+ // new(){
+ // Verifier = new RDTKPropertyVerifierBool(),
+ // Name = "测试值",
+ // Value = "true",
+ // Type = RDPropertyType.Bool,
+ // }
+ // ]);
+ //MessageBox.Show("RDTK Editor 是一个简单的节奏医生关卡编辑器。", "关于 RDTK Editor");
+ }
+ private RDTKProperty[] GetProperties(BaseEvent e)
+ {
+ return [
+ new RDTKProperty()
+ {
+ Verifier = new RDTKPropertyVerifierDefault(),
+ Name = "类型",
+ Value = e.Type.ToString(),
+ Type = RDPropertyType.String,
+ },
+ ..e.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => GetProperty(p,e)).ToArray()
+ ];
+ }
+ private RDTKProperty GetProperty(PropertyInfo property, object obj)
+ {
+ switch (property.PropertyType.Name)
+ {
+ case "Boolean":
+ return new()
+ {
+ Verifier = new RDTKPropertyVerifierBool(),
+ Name = property.Name,
+ Value = property.GetValue(obj)?.ToString() ?? "false",
+ Type = RDPropertyType.Bool,
+ };
+ case "Enum":
+ return new()
+ {
+ Verifier = new RDTKPropertyVerifierEnum(property.PropertyType),
+ Name = property.Name,
+ Value = property.GetValue(obj)?.ToString() ?? "null",
+ Type = RDPropertyType.Enum,
+ };
+ case "String":
+ return new()
+ {
+ Verifier = new RDTKPropertyVerifierDefault(),
+ Name = property.Name,
+ Value = property.GetValue(obj)?.ToString() ?? "null",
+ Type = RDPropertyType.String,
+ };
+ }
+ return new()
+ {
+ Verifier = new RDTKPropertyVerifierDefault(),
+ Name = property.Name,
+ Value = property.GetValue(obj)?.ToString() ?? "null",
+ Type = RDPropertyType.String,
+ };
+ }
+ internal void OnUpdateEventProperties(RDTKProperty[] properties)
+ {
+ ItemProperties.Items.Clear();
+ foreach (var property in properties)
+ {
+ StackPanel stackPanel;
+ switch (property.Type)
+ {
+ case RDPropertyType.Bool:
+ stackPanel = new()
+ {
+ Orientation = Orientation.Horizontal,
+ FlowDirection = FlowDirection.LeftToRight,
+ Children =
+ {
+ new Label(){Content = property.Name},
+ new CheckBox() { IsChecked = bool.Parse( property.Value), }
+ },
+ };
+ break;
+ case RDPropertyType.Enum:
+ stackPanel = new()
+ {
+ AllowDrop = true,
+ Orientation = Orientation.Horizontal,
+ FlowDirection = FlowDirection.LeftToRight,
+ Children =
+ {
+ new Label(){Content = property.Name},
+ new ComboBox()
+ {
+ ItemsSource = Enum.GetValues(((RDTKPropertyVerifierEnum)property.Verifier).EnumType),
+ SelectedValue = property.Value,
+ }
+ },
+ };
+ break;
+ case RDPropertyType.String:
+ stackPanel = new()
+ {
+ AllowDrop = true,
+ Orientation = Orientation.Horizontal,
+ FlowDirection = FlowDirection.LeftToRight,
+ Children =
+ {
+ new Label(){Content = property.Name},
+ new TextBox() { Text = property.Value, }
+ },
+ };
+ break;
+ default:
+ stackPanel = new()
+ {
+ Orientation = Orientation.Horizontal,
+ Children =
+ {
+ new Label(){Content = property.Name},
+ new Label(){Content = property.Value}
+ }
+ };
+ break;
+ }
+ foreach (var child in stackPanel.Children)
+ {
+ switch (child)
+ {
+ case CheckBox checkBox:
+ checkBox.Checked += (sender, e) =>
+ {
+ property.OnChanged("true");
+ };
+ checkBox.Unchecked += (sender, e) =>
+ {
+ property.OnChanged("false");
+ };
+ break;
+ case TextBox textBox:
+ textBox.TextChanged += (sender, e) =>
+ {
+ property.OnChanged(textBox.Text);
+ };
+ break;
+ case ComboBox comboBox:
+ comboBox.SelectionChanged += (sender, e) =>
+ {
+ property.OnChanged(comboBox.SelectedValue);
+ };
+ break;
+ default:
+ break;
+ }
+ }
+ ItemProperties.Items.Add(stackPanel);
+ property.PropertyChanged += (e, s) => UpdateEvent();
+ }
+ }
+ private void UpdateEvent()
+ {
+ MessageBox.Show("Update Event");
+ }
+ private void UpdateTimeLine()
+ {
+ Rectangle rectangle = new()
+ {
+ Width = 100,
+ Height = 100,
+ };
+ timelineCanvas.Children.Add(rectangle);
+ }
+ private void mainUI_PaintSurface(object sender, SkiaSharp.Views.Desktop.SKPaintSurfaceEventArgs e)
+ {
+ var sfc = e.Surface;
+ var cvs = sfc.Canvas;
+ cvs.Clear(SKColors.Black);
+ cvs.DrawLine(0, 0, 1600, 900, new()
+ {
+ Color = SKColors.Red,
+ });
+ }
+ }
+ public class MainViewModel : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+ }
+}
\ No newline at end of file
diff --git a/Models/RDTKView.cs b/Models/RDTKView.cs
new file mode 100644
index 0000000..5db3917
--- /dev/null
+++ b/Models/RDTKView.cs
@@ -0,0 +1,147 @@
+using RhythmBase.Components;
+using System.Diagnostics.CodeAnalysis;
+
+namespace RDTKEditor.Models
+{
+ internal enum RDPropertyType
+ {
+ Bool,
+ Integer,
+ Float,
+ Expression,
+ IntegerPair,
+ FloatPair,
+ ExpressionPair,
+ String,
+ Color,
+ Enum,
+ }
+ internal interface IRDTKPropertyVerifier
+ {
+ RDPropertyType Type { get; }
+ bool TryParse(string value, out object result);
+ string ToString(object value);
+ }
+ internal class RDTKPropertyVerifierEnum(Type enumType) : IRDTKPropertyVerifier
+ {
+ public RDPropertyType Type => RDPropertyType.Enum;
+ public Type EnumType { get; set; } = enumType;
+ public bool TryParse(string value, out object result)
+ {
+ if (Enum.TryParse(EnumType, value, out object v))
+ {
+ result = v;
+ return true;
+ }
+ result = null;
+ return false;
+ }
+ public string ToString(object value) => value?.ToString() ?? "null";
+ }
+ internal class RDTKPropertyVerifierBool : IRDTKPropertyVerifier
+ {
+ public RDPropertyType Type => RDPropertyType.Bool;
+ public bool TryParse(string value, out object result)
+ {
+ var t = bool.TryParse(value, out bool v);
+ result = v;
+ return t;
+ }
+ public string ToString(object value) => (bool)value ? "true" : "false";
+ }
+ internal class RDTKPropertyVerifierDefault : IRDTKPropertyVerifier
+ {
+ public RDPropertyType Type { get; set; }
+ public bool TryParse(string value, out object result)
+ {
+ result = value;
+ return true;
+ }
+ public string ToString(object value) => value?.ToString() ?? "null";
+ }
+ internal struct RDTKProperty(object value)
+ {
+ public event EventHandler