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? PropertyChanged = null; + private object _value = value; + public string Name { get; set; } = ""; + public string Description { get; set; } = ""; + public RDPropertyType Type { get; set; } + public IRDTKPropertyVerifier Verifier { get; set; } = new RDTKPropertyVerifierDefault(); + public string Value + { + readonly get => Verifier.ToString(_value); + set + { + if (Verifier.TryParse(value, out object result)) + { + _value = result; + } + } + } + public void OnChanged(object value) + { + if (_value == value) return; + if (Verifier.TryParse(value?.ToString()??"", out object result)) + { + _value = result; + PropertyChanged?.Invoke(this, value); + } + } + } + internal class RDTKView + { + + private RDLevel? Level { get; set; } + public RDTKView() + { + } + public RDTKViewMessage UpdateLevelByPath(string path) + { + try + { + Level = RDLevel.Read(path); + return new() + { + Message = "关卡已加载。", + Level = RDTKViewMessageLevel.Info, + Details = $"关卡路径:{path}", + }; + } + catch (Exception ex) + { + return new() + { + Message = ex.Message, + Level = RDTKViewMessageLevel.Error, + Details = ex.StackTrace ?? "", + }; + } + } + public RDTKViewMessage SaveLevelByPath(string path) + { + try + { + Level?.Write(path); + + return new() + { + Message = "关卡已加载。", + Level = RDTKViewMessageLevel.Info, + Details = $"关卡路径:{path}", + }; + + + + } + catch (Exception ex) + { + return new() + { + Message = ex.Message, + Level = RDTKViewMessageLevel.Error, + Details = ex.StackTrace ?? "", + }; + } + } + } +} diff --git a/Models/RDTKViewMessage.cs b/Models/RDTKViewMessage.cs new file mode 100644 index 0000000..634fe27 --- /dev/null +++ b/Models/RDTKViewMessage.cs @@ -0,0 +1,10 @@ +namespace RDTKEditor.Models +{ + internal struct RDTKViewMessage() + { + public string Message { get; set; } = ""; + public RDTKViewMessageLevel Level { get; set; } + public string Details { get; set; } = ""; + public DateTime TimeStamp { get; } = DateTime.Now; + } +} diff --git a/Models/RDTKViewMessageLevel.cs b/Models/RDTKViewMessageLevel.cs new file mode 100644 index 0000000..a09e5bc --- /dev/null +++ b/Models/RDTKViewMessageLevel.cs @@ -0,0 +1,10 @@ +namespace RDTKEditor.Models +{ + internal enum RDTKViewMessageLevel + { + None = 0, + Info = 1, + Warning = 2, + Error = 3, + } +} diff --git a/RDTKEditor.csproj b/RDTKEditor.csproj new file mode 100644 index 0000000..7d41591 --- /dev/null +++ b/RDTKEditor.csproj @@ -0,0 +1,16 @@ + + + + WinExe + net8.0-windows + enable + enable + true + + + + + + + + diff --git a/RDTKEditor.sln b/RDTKEditor.sln new file mode 100644 index 0000000..cb722b7 --- /dev/null +++ b/RDTKEditor.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RDTKEditor", "RDTKEditor.csproj", "{6372AB7C-8E8F-4803-8930-B74A51CE334D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6372AB7C-8E8F-4803-8930-B74A51CE334D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6372AB7C-8E8F-4803-8930-B74A51CE334D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6372AB7C-8E8F-4803-8930-B74A51CE334D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6372AB7C-8E8F-4803-8930-B74A51CE334D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/UML.drawio b/UML.drawio new file mode 100644 index 0000000..e69de29