初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,397 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user