Files
MRCC/launcher/PCL-CE/Plain Craft Launcher 2/Controls/MyLoading.xaml.cs
T

398 lines
12 KiB
C#
Raw Normal View History

using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using static PCL.MyLoading;
using PCL.Core.App.Localization;
namespace PCL;
public partial class MyLoading
{
public delegate void ClickEventHandler(object sender, MouseButtonEventArgs e);
public delegate void IsErrorChangedEventHandler(object sender, bool isError);
public delegate void StateChangedEventHandler(object sender, MyLoadingState newState, MyLoadingState oldState);
private readonly int uuid = ModBase.GetUuid();
public bool AutoRun { get; set; } = true;
public event IsErrorChangedEventHandler? IsErrorChanged;
public event StateChangedEventHandler? StateChanged;
public event ClickEventHandler? Click;
#region 颜色
public SolidColorBrush Foreground
{
get => (SolidColorBrush)GetValue(ForegroundProperty);
set => SetValue(ForegroundProperty, value);
}
public static readonly DependencyProperty ForegroundProperty =
DependencyProperty.Register("Foreground", typeof(SolidColorBrush), typeof(MyLoading));
public MyLoading()
{
InitializeComponent();
SetCurrentValue(TextProperty, Lang.Text("Application.Control.Loading.Default"));
SetCurrentValue(TextErrorProperty, Lang.Text("Application.Control.Loading.Failed"));
SetResourceReference(ForegroundProperty, "ColorBrush3");
IsErrorChanged += (_, _) => RefreshText();
Loaded += (_, _) => RefreshText();
Loaded += (_, _) => InitState();
Loaded += (_, _) => RefreshState();
Unloaded += (_, _) => RefreshState();
MouseLeftButtonUp += Button_MouseUp;
MouseLeftButtonDown += Button_MouseDown;
MouseLeave += Button_MouseLeave;
MouseLeftButtonUp += Button_MouseLeave;
}
#endregion
#region 文本
private bool _ShowProgress { get; set; }
public bool ShowProgress
{
get => _ShowProgress;
set
{
if (_ShowProgress == value)
return;
_ShowProgress = value;
RefreshText();
}
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyLoading),
new PropertyMetadata("", (d, e) => ((MyLoading)d).RefreshText()));
public string TextError
{
get => (string)GetValue(TextErrorProperty);
set => SetValue(TextErrorProperty, value);
}
public static readonly DependencyProperty TextErrorProperty =
DependencyProperty.Register("TextError", typeof(string), typeof(MyLoading),
new PropertyMetadata("", (d, e) => ((MyLoading)d).RefreshText()));
/// <summary>
/// 是否在使用 Loader 时使用 Loader 的错误输出来替换默认的错误文本显示。
/// </summary>
public bool TextErrorInherit { get; set; } = true;
private void RefreshText()
{
ModBase.RunInUi(() =>
{
if (InnerState == MyLoadingState.Error)
{
if (TextErrorInherit && State.IsLoader)
{
var ex = State.Error;
if (ex is null)
{
LabText.Text = Lang.Text("Application.Control.Loading.UnknownError");
}
else
{
while (ex.InnerException is not null) ex = ex.InnerException;
LabText.Text = ModBase.StrTrim(ex.Message).ToString();
if (new[]
{
"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝", "操作已超时", "操作超时", "服务器超时", "连接超时"
}.Any(s => LabText.Text.Contains(s)))
LabText.Text = Lang.Text("Application.Control.Loading.NetworkUnstable");
}
}
else
{
LabText.Text = TextError;
}
}
else if (ShowProgress && State.IsLoader)
{
LabText.Text = Text + " - " + Lang.Number(State.Progress, "P0");
}
else
{
LabText.Text = Text;
}
});
}
#endregion
#region 状态改变
// 状态枚举
public enum MyLoadingState
{
Unloaded = -1,
Run = 0,
Stop = 1,
Error = 2
}
// 用于外部改变的公开状态
private ILoadingTrigger _State
{
[MethodImpl(MethodImplOptions.Synchronized)]
get => field;
[MethodImpl(MethodImplOptions.Synchronized)]
set
{
if (field is not null)
{
field.ProgressChanged -= (_, _) => RefreshText();
field.LoadingStateChanged -= (_, _) => RefreshState();
}
field = value;
if (field is not null)
{
field.ProgressChanged += (_, _) => RefreshText();
field.LoadingStateChanged += (_, _) => RefreshState();
}
}
}
public ILoadingTrigger State
{
get
{
InitState();
return _State;
}
set
{
_State = value;
RefreshState();
}
}
private void InitState()
{
if (_State is null)
{
_State = new MyLoadingStateSimulator();
if (AutoRun)
_State.LoadingState = MyLoadingState.Run;
}
}
private void RefreshState()
{
if (_State.LoadingState == MyLoadingState.Run && !IsLoaded)
InnerState = MyLoadingState.Stop;
InnerState = _State.LoadingState;
OuterState = _State.LoadingState;
AniLoop();
}
// 用于引发外部事件的状态
private MyLoadingState _OuterState { get; set; } = MyLoadingState.Unloaded;
private MyLoadingState OuterState
{
get => _OuterState;
set
{
if (_OuterState == value)
return;
var oldValue = _OuterState;
_OuterState = value;
// 引发事件
StateChanged?.Invoke(this, value, oldValue);
if (oldValue == MyLoadingState.Error != (value == MyLoadingState.Error))
IsErrorChanged?.Invoke(this, value == MyLoadingState.Error);
}
}
// 用于引发内部动画事件的状态
private MyLoadingState _InnerState { get; set; } = MyLoadingState.Unloaded;
private MyLoadingState InnerState
{
get => _InnerState;
set
{
if (_InnerState == value)
return;
var oldValue = _InnerState;
_InnerState = value;
// 引发事件
AniLoop();
if (oldValue == MyLoadingState.Error != (value == MyLoadingState.Error))
ErrorAnimation(this, value == MyLoadingState.Error);
}
}
#endregion
#region 动画
/// <summary>
/// 是否需要动画。
/// </summary>
public bool HasAnimation { get; set; } = true;
/// <summary>
/// 主动画循环是否正在运行中。
/// </summary>
private bool isLooping;
private void AniLoop()
{
// 这坨循环代码也是老屎坑了,救救.jpg
if (!HasAnimation || isLooping || !(InnerState == MyLoadingState.Run) || ModAnimation.aniSpeed > 10d ||
!IsLoaded)
return;
isLooping = true;
errorAnimationWaiting = true;
ModAnimation.AniStart(new[]
{
ModAnimation.AaRotateTransform(PathPickaxe, -20 - ((RotateTransform)PathPickaxe.RenderTransform).Angle, 350,
250, new ModAnimation.AniEaseInBack(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaRotateTransform(PathPickaxe, 50d, 900, ease: new ModAnimation.AniEaseOutFluent(),
after: true),
ModAnimation.AaRotateTransform(PathPickaxe, 25d, 900,
ease: new ModAnimation.AniEaseOutElastic(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaCode(() =>
{
PathLeft.Opacity = 1d;
PathLeft.Margin = new Thickness(7d, 41d, 0d, 0d);
PathRight.Opacity = 1d;
PathRight.Margin = new Thickness(14d, 41d, 0d, 0d);
errorAnimationWaiting = false;
}),
ModAnimation.AaOpacity(PathLeft, -1, 100, 50),
ModAnimation.AaX(PathLeft, -5, 180, ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaY(PathLeft, -6, 180, ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaOpacity(PathRight, -1, 100, 50),
ModAnimation.AaX(PathRight, 5d, 180, ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaY(PathRight, -6, 180, ease: new ModAnimation.AniEaseOutFluent()),
ModAnimation.AaCode(() =>
{
isLooping = false;
AniLoop();
}, after: true)
}, "MyLoader Loop " + uuid + "/" + ModBase.GetUuid());
}
/// <summary>
/// 镐子是否还没挥下去,要求错误动画等待。
/// </summary>
private bool errorAnimationWaiting;
private void ErrorAnimation(object sender, bool isError)
{
if (isError)
{
// 非错误变为错误
var wait = errorAnimationWaiting ? 400 : 0;
ModAnimation.AniStart(
new[]
{
ModAnimation.AaColor(PanBack, ForegroundProperty, "ColorBrushRedLight", 300),
ModAnimation.AaOpacity(PathError, 1d - PathError.Opacity, 100, 300 + wait),
ModAnimation.AaScaleTransform(PathError, 1d - ((ScaleTransform)PathError.RenderTransform).ScaleX,
400, 300 + wait, new ModAnimation.AniEaseOutBack())
}, "MyLoader Error " + uuid);
}
else
{
// 错误变为非错误
ModAnimation.AniStart(
new[]
{
ModAnimation.AaOpacity(PathError, -PathError.Opacity, 100),
ModAnimation.AaScaleTransform(PathError, 0.5d - ((ScaleTransform)PathError.RenderTransform).ScaleX,
200),
ModAnimation.AaColor(PanBack, ForegroundProperty, "ColorBrush3", 300)
}, "MyLoader Error " + uuid);
}
}
#endregion
#region 点击事件
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
{
Click?.Invoke(sender, e);
}
private bool isMouseDown;
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
// 鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
isMouseDown = true;
}
private void Button_MouseLeave(object sender, object e)
{
isMouseDown = false;
}
#endregion
}
public interface ILoadingTrigger
{
delegate void LoadingStateChangedEventHandler(MyLoadingState newState, MyLoadingState oldState);
delegate void ProgressChangedEventHandler(double newProgress, double oldProgress);
bool IsLoader { get; }
double Progress { get; }
Exception? Error { get; }
MyLoadingState LoadingState { get; set; }
event LoadingStateChangedEventHandler? LoadingStateChanged;
event ProgressChangedEventHandler? ProgressChanged;
}
public class MyLoadingStateSimulator : ILoadingTrigger
{
private MyLoadingState _LoadingState { get; set; } = MyLoadingState.Unloaded;
public MyLoadingState LoadingState
{
get => _LoadingState;
set
{
if (_LoadingState == value)
return;
var oldState = _LoadingState;
_LoadingState = value;
LoadingStateChanged?.Invoke(value, oldState);
}
}
public bool IsLoader { get; } = false;
public double Progress => 0;
public Exception? Error => null;
public event ILoadingTrigger.LoadingStateChangedEventHandler? LoadingStateChanged;
public event ILoadingTrigger.ProgressChangedEventHandler? ProgressChanged;
}