diff --git a/PCL.Core/Utils/Exts/EnumerableExtensions.cs b/PCL.Core/Utils/Exts/EnumerableExtensions.cs new file mode 100644 index 000000000..e20ddb3d6 --- /dev/null +++ b/PCL.Core/Utils/Exts/EnumerableExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace PCL.Core.Utils.Exts; + +public static class EnumerableExtensions +{ + /// + /// 对目标集合进行枚举,并传递该元素在集合中的位置 + /// + /// 要枚举的集合 + /// 处理函数 + /// 类型 + public static void ForEachIndexed(this IEnumerable collection, Action handle) + { + var i = 0; + foreach (var element in collection) + { + handle.Invoke(element ,i); + i++; + } + } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgBoxCommand.cs b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgBoxCommand.cs new file mode 100644 index 000000000..feaab219d --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgBoxCommand.cs @@ -0,0 +1,39 @@ +using System.Windows.Input; +using PCL.Controls.MyMsg.Models; +using PCL.Core.Utils.Exts; + +namespace PCL.Controls.MyMsg.Commands; + +public class MyMsgBoxCommand: ICommand +{ + public MyMsgBoxCommand(ButtonContext[] contexts) + { + contexts.ForEachIndexed((value, index) => OperateMap[index] = value); + } + + public event Action? Exited; + + public event Action? Clicked; + + public event EventHandler? CanExecuteChanged; + + private Dictionary OperateMap { get; set; } = []; + + public string GetButtonTextById(int id) => + OperateMap.FirstOrDefault(key => key.Key == id).Value.ButtonName; + + public bool CanExecute(object? parameter) => + parameter is not null && OperateMap.ContainsKey((int)parameter); + + public void Execute(object? parameter) + { + + if(parameter is null + || !OperateMap.TryGetValue((int)parameter, out var op)) return; + + Clicked?.Invoke((int)parameter); + + op.Operation.Invoke(parameter); + if(op.ExitWhenClick) Exited?.Invoke(); + } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputButtonCommand.cs b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputButtonCommand.cs new file mode 100644 index 000000000..494731b17 --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputButtonCommand.cs @@ -0,0 +1,14 @@ +using System.Windows.Input; + +namespace PCL.Controls.MyMsg.Commands; + +public class MyMsgInputButtonCommand: ICommand +{ + public event Action? Exited; + + public event EventHandler? CanExecuteChanged; + + public bool CanExecute(object? parameter) => true; + + public void Execute(object? parameter) => Exited?.Invoke(); +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputCommand.cs b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputCommand.cs new file mode 100644 index 000000000..c5fb4803c --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/Commands/MyMsgInputCommand.cs @@ -0,0 +1,30 @@ +using System.Windows.Input; +using FluentValidation; + +namespace PCL.Controls.MyMsg.Commands; + +public class MyMsgInputCommand: ICommand +{ + public List> ValidateRules { get; } = []; + public event Action? Error; + + public event Action? Success; + + public event EventHandler? CanExecuteChanged; + + public void Execute(object? parameter) + { + var userInput = parameter?.ToString()!; + foreach (var rule in ValidateRules) + { + var result = rule.Validate(userInput); + if(result.IsValid) continue; + Error?.Invoke(string.Join(Environment.NewLine, result.Errors.Select(e => e.ErrorMessage))); + return; + } + Success?.Invoke(); + } + + public bool CanExecute(object? parameter) => true; + +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Models/ButtonContext.cs b/Plain Craft Launcher 2/Controls/MyMsg/Models/ButtonContext.cs new file mode 100644 index 000000000..741120bd1 --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/Models/ButtonContext.cs @@ -0,0 +1,8 @@ +namespace PCL.Controls.MyMsg.Models; + +public class ButtonContext +{ + public required string ButtonName { get; set; } + public bool ExitWhenClick { get; set; } + public Action Operation { get; set; } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Models/MyMsgInputData.cs b/Plain Craft Launcher 2/Controls/MyMsg/Models/MyMsgInputData.cs new file mode 100644 index 000000000..13a9efef2 --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/Models/MyMsgInputData.cs @@ -0,0 +1,45 @@ +using System.Windows.Input; +using FluentValidation; +using PCL.Controls.MyMsg.Commands; + +namespace PCL.Controls.MyMsg.Models; + +public class MyMsgInputData +{ + + public string? UserInput { get; set; } + + public string? Description { get; set; } + + public bool NoError { get; set; } + + public ICommand Command; + + public MyMsgInputData(string description) + { + Description = description; + var command = new MyMsgInputCommand(); + command.Error += _ => NoError = false; + command.Success += () => NoError = true; + Command = command; + } + + public MyMsgInputData(List> rules) + { + var command = new MyMsgInputCommand(); + command.Error += _ => NoError = false; + command.Success += () => NoError = true; + rules.ForEach(v => command.ValidateRules.Add(v)); + Command = command; + } + + public MyMsgInputData(string description, List> rules) + { + Description = description; + var command = new MyMsgInputCommand(); + command.Error += _ => NoError = false; + command.Success += () => NoError = true; + rules.ForEach(v => command.ValidateRules.Add(v)); + Command = command; + } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/Models/MyMsgTextData.cs b/Plain Craft Launcher 2/Controls/MyMsg/Models/MyMsgTextData.cs new file mode 100644 index 000000000..2725b1346 --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/Models/MyMsgTextData.cs @@ -0,0 +1,50 @@ +using System.Windows.Input; +using PCL.Controls.MyMsg.Commands; + +// 非常感谢龙猫送来的工作量,非常感谢!!! + +namespace PCL.Controls.MyMsg.Models; + +public class MyMsgTextData +{ + /// + /// 退出事件 + /// + public event Action? Exited; + /// + /// 窗口消息 + /// + public string Message { get; set; } + + /// + /// 标题 + /// + public string Title { get; set; } = "提示"; + + public MyMsgTextData(string message) : this(message, + new ButtonContext + { + ButtonName = "确定", ExitWhenClick = true, Operation = static _ => { } + }, + new ButtonContext + { + ButtonName = "取消", ExitWhenClick = true, Operation = static _ => { } + } + ){} + + public MyMsgTextData(string message, params ButtonContext[] buttons) + { + Message = message; + var cmd =new MyMsgBoxCommand(buttons); + cmd.Exited += () => Exited?.Invoke(); + cmd.Clicked += value => Result = value; + Command = cmd; + } + + /// + /// 执行器 + /// + public ICommand Command { get; set; } + + public int Result { get; set; } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgInput.xaml.cs b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgInput.xaml.cs index b8dc7c782..1f020d79e 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgInput.xaml.cs +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgInput.xaml.cs @@ -1,7 +1,11 @@ +using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; +using PCL.Controls.MyMsg.Commands; +using PCL.Controls.MyMsg.Models; +using PCL.Controls.MyMsg.ViewModels; using PCL.Core.UI.Controls; namespace PCL; @@ -10,7 +14,16 @@ public partial class MyMsgInput { private readonly ModMain.MyMsgBoxConverter myConverter; private readonly int uuid = ModBase.GetUuid(); - + + public MyMsgInput(MyMsgInputData data) + { + var command = new MyMsgInputButtonCommand(); + command.Exited += Close; + DataContext = new MyMsgInputViewModel(data, command); + var buttonCommand = new MyMsgInputButtonCommand(); + + } + public MyMsgInput(ModMain.MyMsgBoxConverter converter) { try diff --git a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml index 724dfd415..96f70c087 100644 --- a/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml +++ b/Plain Craft Launcher 2/Controls/MyMsg/MyMsgText.xaml @@ -24,7 +24,7 @@ ErrorDescription = value; + ButtonCommand = buttonCommand; + ValidateCommand = data.Command; + } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/ViewModels/MyMsgTextViewModel.cs b/Plain Craft Launcher 2/Controls/MyMsg/ViewModels/MyMsgTextViewModel.cs new file mode 100644 index 000000000..d15644844 --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/ViewModels/MyMsgTextViewModel.cs @@ -0,0 +1,111 @@ +using System.Windows; +using System.Windows.Input; +using PCL.Controls.MyMsg.Commands; +using PCL.Controls.MyMsg.Models; + +namespace PCL.Controls.MyMsg.ViewModels; + +public class MyMsgTextViewModel: ViewModelBase +{ + public string Title + { + get; + set + { + if(field == value) return; + SetProperty(value, ref field); + } + } + + public string Message + { + get; + set + { + if(field == value) return; + SetProperty(value, ref field); + } + } + + public Visibility Button1Visibility + { + get; + set + { + if (field == value) return; + SetProperty(value, ref field); + } + } + + + public Visibility Button2Visibility + { + get; + set + { + if (field == value) return; + SetProperty(value, ref field); + } + } + + + public Visibility Button3Visibility + { + get; + set + { + if (field == value) return; + SetProperty(value, ref field); + } + } + + public string Button1Text + { + get; + set + { + if(value == field) return; + SetProperty(value, ref field); + } + } + + public string Button2Text + { + get; + set + { + if(value == field) return; + SetProperty(value, ref field); + } + } + + public string Button3Text + { + get; + set + { + if(value == field) return; + SetProperty(value, ref field); + } + } + + public ICommand Context { get; set; } + + public MyMsgTextViewModel(MyMsgTextData data) + { + var cmd = (MyMsgBoxCommand)data.Command; + Context = data.Command; + Title = data.Title; + Message = data.Message; + var hasButton1 = Context.CanExecute(0); + var hasButton2 = Context.CanExecute(1); + var hasButton3 = Context.CanExecute(2); + if (!hasButton1) throw new InvalidOperationException("必须提供至少一个按钮"); + Button1Visibility = hasButton1 ? Visibility.Visible : Visibility.Collapsed; + Button2Visibility = hasButton2 ? Visibility.Visible : Visibility.Collapsed; + Button3Visibility = hasButton3 ? Visibility.Visible : Visibility.Collapsed; + Button1Text = cmd.GetButtonTextById(0); + Button2Text = cmd.GetButtonTextById(1); + Button3Text = cmd.GetButtonTextById(2); + } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Controls/MyMsg/ViewModels/ViewModelBase.cs b/Plain Craft Launcher 2/Controls/MyMsg/ViewModels/ViewModelBase.cs new file mode 100644 index 000000000..874971ddc --- /dev/null +++ b/Plain Craft Launcher 2/Controls/MyMsg/ViewModels/ViewModelBase.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace PCL.Controls.MyMsg.ViewModels; + +public class ViewModelBase: INotifyPropertyChanged +{ + public event PropertyChangedEventHandler? PropertyChanged; + + protected virtual void SetProperty(T newValue, ref T value, [CallerMemberName] string propertyName = "") + { + value = newValue; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} \ No newline at end of file diff --git a/Plain Craft Launcher 2/Modules/ModMain.cs b/Plain Craft Launcher 2/Modules/ModMain.cs index 1a653f3e3..3998308af 100644 --- a/Plain Craft Launcher 2/Modules/ModMain.cs +++ b/Plain Craft Launcher 2/Modules/ModMain.cs @@ -11,6 +11,7 @@ using FluentValidation; using Microsoft.VisualBasic; using Microsoft.Win32; +using PCL.Controls.MyMsg.Models; using PCL.Core.App; using PCL.Core.App.Configuration; using PCL.Core.App.Localization; @@ -96,10 +97,15 @@ public static class ModMain private static int timer150Count; /// - /// 等待显示的弹窗。 + /// 等待显示的弹窗。 /// public static List WaitingMyMsgBox { get; } = []; + /// + /// 等待显示的 MyMsgBox + /// + public static List WaitMyMsgData { get; } = []; + private static void TimerMain() { try @@ -126,6 +132,7 @@ private static void TimerMain() try { #region 每 250ms 执行一次的代码 + } #endregion @@ -393,6 +400,11 @@ public static int MyMsgBox(string caption, string? title = null, string? button1 return 1; } + public static int MyMsgBox(MyMsgTextData data) + { + + } + /// /// 显示弹窗,返回点击按钮的编号(从 1 开始)。 /// @@ -405,7 +417,8 @@ public static int MyMsgBox(string caption, string? title = null, string? button1 /// 点击第二个按钮将执行该方法,不关闭弹窗。 /// 点击第三个按钮将执行该方法,不关闭弹窗。 /// 是否为警告弹窗,若为 True,弹窗配色和背景会变为红色。 - public static int MyMsgBoxMarkdown(string caption, string? title = null, string? button1 = null, string? button2 = "", + public static int MyMsgBoxMarkdown(string caption, string? title = null, string? button1 = null, + string? button2 = "", string? button3 = "", bool isWarn = false, bool highLight = true, bool forceWait = false, Action button1Action = null, Action button2Action = null, Action button3Action = null) { @@ -566,7 +579,36 @@ public static string MyMsgBoxInput(string title, string text = "", string defaul return (int?)converter.Result; } - + public static void MyMsgBoxShow() + { + try + { + if (frmMain is null || frmMain.PanMsg is null || frmMain.WindowState == WindowState.Minimized) + return; + if (frmMain.PanMsg.Children.Count > 0) + { + // 弹窗中 + frmMain.PanMsgBackground.Visibility = Visibility.Visible; + } + else if (WaitingMyMsgBox.Count != 0) + { + // 没有弹窗,显示一个等待的弹窗 + frmMain.PanMsgBackground.Visibility = Visibility.Visible; + frmMain.PanMsg.Children.Add(new MyMsgText(WaitMyMsgData[0])); + WaitMyMsgData.RemoveAt(0); + } + // 没有弹窗,没有等待的弹窗 + else if (!(frmMain.PanMsgBackground.Visibility == Visibility.Collapsed)) + { + frmMain.PanMsgBackground.Visibility = Visibility.Collapsed; + } + } + catch (Exception ex) + { + ModBase.Log(ex, "处理等待中的弹窗失败", ModBase.LogLevel.Feedback); + } + } + public static void MyMsgBoxTick() { try @@ -578,7 +620,7 @@ public static void MyMsgBoxTick() // 弹窗中 frmMain.PanMsgBackground.Visibility = Visibility.Visible; } - else if (WaitingMyMsgBox.Any()) + else if (WaitingMyMsgBox.Count != 0) { // 没有弹窗,显示一个等待的弹窗 frmMain.PanMsgBackground.Visibility = Visibility.Visible;