CI / Go Backend (push) Canceled after 0s
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2246 lines
84 KiB
C#
2246 lines
84 KiB
C#
using System.ComponentModel;
|
||
using System.IO;
|
||
using System.Net;
|
||
using System.Runtime.InteropServices;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Input;
|
||
using System.Windows.Interop;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Effects;
|
||
using PCL.Core.App;
|
||
using PCL.Core.App.IoC;
|
||
using PCL.Core.App.Localization;
|
||
using PCL.Core.Logging;
|
||
using PCL.Core.Minecraft;
|
||
using PCL.Core.UI;
|
||
using PCL.Core.UI.Theme;
|
||
using PCL.Core.Utils;
|
||
using PCL.Core.Utils.OS;
|
||
using PCL.Core.Utils.Validate;
|
||
using PCL.Network;
|
||
|
||
namespace PCL;
|
||
|
||
public partial class FormMain
|
||
{
|
||
// 愚人节鼠标位置
|
||
public MouseEventArgs lastMouseArg;
|
||
|
||
private void FormMain_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
lastMouseArg = e;
|
||
}
|
||
|
||
#region 基础
|
||
|
||
// 更新日志
|
||
private void ShowUpdateLog()
|
||
{
|
||
ModBase.RunInNewThread(() =>
|
||
{
|
||
var changelogFile = $"{ModBase.pathTemp}CEUpdateLog.md";
|
||
string changelog;
|
||
if (File.Exists(changelogFile))
|
||
changelog = ModBase.ReadFile(changelogFile);
|
||
else
|
||
changelog = Lang.Text("Main.UpdateLog.Empty");
|
||
if (ModMain.MyMsgBoxMarkdown(changelog,
|
||
Lang.Text("Main.UpdateLog.Title", ModBase.versionBranchName, ModBase.versionBaseName), Lang.Text("Common.Action.Confirm"), Lang.Text("Main.UpdateLog.FullChangelog")) ==
|
||
2) ModBase.OpenWebsite("https://github.com/PCL-Community/PCL2-CE/releases");
|
||
}, "UpdateLog Output");
|
||
}
|
||
|
||
// 窗口加载
|
||
private bool isWindowLoadFinished;
|
||
private readonly DragHelper _helper = new();
|
||
|
||
public FormMain()
|
||
{
|
||
ModBase.applicationStartTick = TimeUtils.GetTimeTick();
|
||
// 刷新主题
|
||
// ThemeCheckAll(False)
|
||
// ThemeRefreshColor()
|
||
ThemeService.ColorModeChanged += (_, _) => ThemeManager.ThemeRefresh();
|
||
ThemeService.ColorThemeChanged += theme => ThemeManager.ThemeRefresh((int)theme);
|
||
// 窗体参数初始化
|
||
ModMain.frmMain = this;
|
||
ModMain.frmLaunchLeft = new PageLaunchLeft();
|
||
ModMain.frmLaunchRight = new PageLaunchRight();
|
||
// 版本号改变
|
||
var lastVersion = States.System.LastVersion;
|
||
if (lastVersion < ModBase.versionCode)
|
||
{
|
||
// 重新询问是否启用遥测数据收集
|
||
if (lastVersion <= 511)
|
||
{
|
||
if (!Config.System.TelemetryConfig.IsDefault() && Config.System.Telemetry)
|
||
{
|
||
Config.System.TelemetryConfig.Reset();
|
||
ModBase.Log("[Start] 遥测策略变更:由旧版本升级到含新版遥测的版本,已重置遥测设置");
|
||
}
|
||
}
|
||
// 触发升级
|
||
UpgradeSub(lastVersion);
|
||
}
|
||
else if (lastVersion > ModBase.versionCode)
|
||
// 触发降级
|
||
DowngradeSub(lastVersion);
|
||
|
||
_ = Config.Preference.Theme.ThemeSelected;
|
||
// 注册拖拽事件(不能直接加 Handles,否则没用;#6340)
|
||
AddHandler(DragDrop.DragEnterEvent, new DragEventHandler(HandleDrag), true);
|
||
AddHandler(DragDrop.DragOverEvent, new DragEventHandler(HandleDrag), true);
|
||
// 注册 MsgBox 事件
|
||
MsgBoxWrapper.OnShow += ModMain.MsgBoxWrapper_OnShow;
|
||
// 注册 Hint 事件
|
||
HintWrapper.OnShow += HintService.HintWrapper_OnShow;
|
||
// 加载 UI
|
||
InitializeComponent();
|
||
Opacity = 0d;
|
||
try
|
||
{
|
||
Height = States.UI.WindowHeight;
|
||
Width = States.UI.WindowWidth;
|
||
}
|
||
catch (Exception ex) // 修复 #2019
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"读取窗口默认大小失败",
|
||
ModBase.LogLevel.Hint,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
Height = MinHeight + 100d;
|
||
Width = MinWidth + 100d;
|
||
}
|
||
|
||
// 管理员权限下文件拖拽
|
||
if (ProcessInterop.IsAdmin())
|
||
{
|
||
ModBase.Log("[Start] PCL 当前正以管理员权限运行");
|
||
SourceInitialized += (_, _) =>
|
||
{
|
||
var windowInterop = new WindowInteropHelper(this);
|
||
_helper.HwndSource = HwndSource.FromHwnd(windowInterop.Handle);
|
||
_helper.AddHook();
|
||
};
|
||
Closing += (_, _) => _helper.RemoveHook();
|
||
_helper.DragDrop += (_, _) => FileDrag(_helper.DropFilePaths);
|
||
}
|
||
|
||
if (ModMain.frmLaunchLeft.Parent is not null)
|
||
ModMain.frmLaunchLeft.SetValue(ContentPresenter.ContentProperty, null);
|
||
if (ModMain.frmLaunchRight.Parent is not null)
|
||
ModMain.frmLaunchRight.SetValue(ContentPresenter.ContentProperty, null);
|
||
PanMainLeft.Child = ModMain.frmLaunchLeft;
|
||
pageLeft = ModMain.frmLaunchLeft;
|
||
PanMainRight.Child = ModMain.frmLaunchRight;
|
||
pageRight = ModMain.frmLaunchRight;
|
||
ModMain.frmLaunchRight.PageState = MyPageRight.PageStates.ContentStay;
|
||
// 调试模式提醒
|
||
if (ModBase.modeDebug)
|
||
HintService.Hint(Lang.Text("Main.DebugMode.Hint"));
|
||
// 尽早执行的加载池
|
||
ModFolder.mcFolderListLoader
|
||
.Start(0); // 为了让下载已存在文件检测可以正常运行,必须跑一次;为了让启动按钮尽快可用,需要尽早执行;为了与 PageLaunchLeft 联动,需要为 0 而不是 GetUuid
|
||
|
||
ModBase.Log("[Start] 第二阶段加载用时:" + (TimeUtils.GetTimeTick() - ModBase.applicationStartTick) + " ms");
|
||
// 注册生命周期状态事件
|
||
Lifecycle.When(LifecycleState.WindowCreated, FormMain_Loaded);
|
||
}
|
||
|
||
private void FormMain_Loaded() // (sender As Object, e As RoutedEventArgs) Handles Me.Loaded
|
||
{
|
||
FormMain_SizeChanged();
|
||
ModBase.applicationStartTick = TimeUtils.GetTimeTick();
|
||
ModBase.frmHandle = new WindowInteropHelper(this).Handle;
|
||
// 读取设置
|
||
PageSetupUI.BackgroundRefresh(false, true);
|
||
ModMusic.MusicRefreshPlay(false, true);
|
||
// 扩展按钮
|
||
BtnExtraUpdateRestart.showCheck = BtnExtraUpdateRestart_ShowCheck;
|
||
BtnExtraDownload.showCheck = BtnExtraDownload_ShowCheck;
|
||
BtnExtraBack.showCheck = BtnExtraBack_ShowCheck;
|
||
BtnExtraApril.showCheck = BtnExtraApril_ShowCheck;
|
||
BtnExtraShutdown.showCheck = BtnExtraShutdown_ShowCheck;
|
||
BtnExtraLog.showCheck = BtnExtraLog_ShowCheck;
|
||
BtnExtraApril.ShowRefresh();
|
||
// 初始化尺寸改变
|
||
if (!Config.Preference.LockWindowSize)
|
||
AddResizer();
|
||
else
|
||
RemoveResizer();
|
||
// PLC 彩蛋
|
||
if (RandomUtils.NextInt(1, 1000) == 233)
|
||
ShapeTitleLogo.Data = (Geometry)new GeometryConverter().ConvertFromString(
|
||
"M26,29 v-25 h6 a7,7 180 0 1 0,14 h-6 M83,6.5 a10,11.5 180 1 0 0,18 M48,2.5 v24.5 h13.5");
|
||
// 加载窗口
|
||
|
||
ThemeManager.ThemeRefresh();
|
||
ModSetup.ApplyAll();
|
||
Lifecycle.CurrentApplication.Resources["BlurSamplingRate"] = Config.Preference.Blur.SamplingRate * 0.01d;
|
||
Lifecycle.CurrentApplication.Resources["BlurType"] = Config.Preference.Blur.KernelType;
|
||
if (Config.Preference.Blur.IsEnabled)
|
||
Lifecycle.CurrentApplication.Resources["BlurRadius"] = Config.Preference.Blur.Radius * 1.0d;
|
||
else
|
||
Lifecycle.CurrentApplication.Resources["BlurRadius"] = 0.0d;
|
||
|
||
// #If DEBUG Then
|
||
// MinHeight = 50
|
||
// MinWidth = 50
|
||
// #End If
|
||
Topmost = false;
|
||
if (ModMain.frmStart is not null)
|
||
ModMain.frmStart.Close(new TimeSpan(0, 0, 0, 0, (int)Math.Round(400d / ModAnimation.aniSpeed)));
|
||
// 更改窗口
|
||
// Top = (GetWPFSize(My.Computer.Screen.WorkingArea.Height) - Height) / 2
|
||
// Left = (GetWPFSize(My.Computer.Screen.WorkingArea.Width) - Width) / 2
|
||
isSizeSaveable = true;
|
||
ShowWindowToTop();
|
||
var hwndSource = (HwndSource)PresentationSource.FromVisual(this);
|
||
hwndSource.AddHook(WndProc);
|
||
ModAnimation.AniStart(new[]
|
||
{
|
||
ModAnimation.AaCode(() => ModAnimation.AniControlEnabled -= 1, 50),
|
||
ModAnimation.AaOpacity(this, Config.Preference.Theme.WindowOpacity / 1000d + 0.4d, 250, 100),
|
||
ModAnimation.AaDouble(i => TransformPos.Y += (double)i, -TransformPos.Y, 600,
|
||
100, new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)),
|
||
ModAnimation.AaDouble(i => TransformRotate.Angle += (double)i,
|
||
-TransformRotate.Angle, 500, 100, new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)),
|
||
ModAnimation.AaCode(() =>
|
||
{
|
||
RenderTransform = null;
|
||
isWindowLoadFinished = true;
|
||
ModBase.Log(
|
||
$"[System] DPI:{ModBase.dpi},系统版本:{Environment.OSVersion.VersionString},PCL 位置:{Basics.ExecutablePath}");
|
||
}, after: true)
|
||
}, "Form Show");
|
||
// Timer 启动
|
||
ModAnimation.AniStart();
|
||
ModMain.TimerMainStart();
|
||
// 特殊版本提示
|
||
ModBase.RunInNewThread(() =>
|
||
{
|
||
// 特殊版本提示
|
||
try
|
||
{
|
||
|
||
|
||
#if DEBUG || DEBUGCI
|
||
|
||
if (Environment.GetEnvironmentVariable("PCL_DISABLE_DEBUG_HINT") is null)
|
||
{
|
||
|
||
#if DEBUG
|
||
var hint = Lang.Text("Main.SpecialVersion.DebugHint");
|
||
#else
|
||
var hint = Lang.Text("Main.SpecialVersion.CiHint");
|
||
#endif
|
||
|
||
ModMain.MyMsgBox(
|
||
$"{hint}{"\r\n"}{"\r\n"}{Lang.Text("Main.SpecialVersion.HideHintNotice")}",
|
||
Lang.Text("Main.SpecialVersion.Title"), Lang.Text("Main.SpecialVersion.IUnderstand"), Lang.Text("Main.SpecialVersion.OpenDownloadPageAndExit"), isWarn: true, button2Action: () =>
|
||
{
|
||
ModBase.OpenWebsite("https://github.com/PCL-Community/PCL2-CE/releases/latest");
|
||
EndProgram(false);
|
||
});
|
||
}
|
||
|
||
|
||
#endif
|
||
// EULA 提示
|
||
if (!States.System.LauncherEula)
|
||
switch (ModMain.MyMsgBox(Lang.Text("Main.Eula.Message"), Lang.Text("Main.Eula.Title"), Lang.Text("Common.Action.Agree"), Lang.Text("Common.Action.Decline"), Lang.Text("Main.Eula.View"),
|
||
button3Action: () => ModBase.OpenWebsite("https://shimo.im/docs/rGrd8pY8xWkt6ryW")))
|
||
{
|
||
case 1:
|
||
{
|
||
States.System.LauncherEula = true;
|
||
break;
|
||
}
|
||
case 2:
|
||
{
|
||
EndProgram(false);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 遥测提示
|
||
if (Config.System.TelemetryConfig.IsDefault())
|
||
{
|
||
var selection = ModMain.MyMsgBox(
|
||
Lang.Text("Main.Telemetry.Message"),
|
||
Lang.Text("Main.Telemetry.Title"), Lang.Text("Common.Action.Agree"), Lang.Text("Common.Action.Decline"));
|
||
Config.System.TelemetryConfig.SetValue(selection == 1, forceNewValue: true);
|
||
}
|
||
// 启动加载器池
|
||
try
|
||
{
|
||
ModDownload.dlClientListMojangLoader.Start(1); // PCL 会同时根据这里的加载结果决定是否使用官方源进行下载
|
||
RunCountSub();
|
||
UpdateManager.serverLoader.Start(1);
|
||
ModBase.RunInNewThread(ModMain.TryClearTaskTemp, "TryClearTaskTemp", ThreadPriority.BelowNormal);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"初始化加载池运行失败",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
|
||
HardwareInfo.GetHardwareInfo();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"初始弹窗提示运行失败",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
}, "Start Loader", ThreadPriority.BelowNormal);
|
||
|
||
ModBase.Log($"[Start] 第三阶段加载用时:{TimeUtils.GetTimeTick() - ModBase.applicationStartTick} ms");
|
||
}
|
||
|
||
// 根据打开次数触发的事件
|
||
private void RunCountSub()
|
||
{
|
||
States.System.StartupCount += 1;
|
||
}
|
||
|
||
// 升级与降级事件
|
||
private void UpgradeSub(int lastVersionCode)
|
||
{
|
||
ModBase.Log("[Start] 版本号从 " + lastVersionCode + " 升高到 " + ModBase.versionCode);
|
||
States.System.LastVersion = ModBase.versionCode;
|
||
// 检查有记录的最高版本号
|
||
int lowerVersionCode;
|
||
#if BETA
|
||
lowerVersionCode = States.System.LastBetaVersion;
|
||
if (lowerVersionCode < ModBase.versionCode)
|
||
{
|
||
States.System.LastBetaVersion = ModBase.versionCode;
|
||
ModBase.Log($"[Start] 最高版本号从 {lowerVersionCode} 升高到 {ModBase.versionCode}");
|
||
}
|
||
#else
|
||
lowerVersionCode = States.System.LastAlphaVersion;
|
||
if (lowerVersionCode < ModBase.versionCode)
|
||
{
|
||
States.System.LastAlphaVersion = ModBase.versionCode;
|
||
ModBase.Log($"[Start] 最高版本号从 {lowerVersionCode} 升高到 {ModBase.versionCode}");
|
||
}
|
||
#endif
|
||
// 被移除的窗口设置选项 (Commit 3161488 2026/1/23)
|
||
if ((int)Config.Launch.GameWindowMode == 5)
|
||
Config.Launch.GameWindowMode = GameWindowSizeMode.Default;
|
||
|
||
// 更新后展示社区版提示
|
||
UpdateManager.ShowCEAnnounce();
|
||
// 输出更新日志
|
||
if (lastVersionCode <= 0)
|
||
return;
|
||
if (lowerVersionCode >= ModBase.versionCode)
|
||
return;
|
||
ShowUpdateLog();
|
||
|
||
// 重置自定义主页配置
|
||
if (lastVersionCode < 521 && Config.Preference.Homepage.SelectedPreset >= 3)
|
||
{
|
||
Config.Preference.Homepage.SelectedPreset = 0;
|
||
ModMain.MyMsgBox(Lang.Text("Main.HomepageReset.Content"), Lang.Text("Main.HomepageReset.Title"));
|
||
}
|
||
}
|
||
|
||
private void DowngradeSub(int lastVersionCode)
|
||
{
|
||
ModBase.Log("[Start] 版本号从 " + lastVersionCode + " 降低到 " + ModBase.versionCode);
|
||
States.System.LastVersion = ModBase.versionCode;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 自定义窗口
|
||
|
||
private bool canResize = true;
|
||
|
||
// 重写窗口边缘判定以使 DWM 自带的 resizer 行为看起来比较正常
|
||
private nint _SizeWndProc(nint hWnd, int msg, nint wParam, nint lParam, ref bool handled)
|
||
{
|
||
// 窗口活动常量
|
||
const int WM_NCHITTEST = 0x84;
|
||
const int HTCLIENT = 1;
|
||
const int HTLEFT = 10;
|
||
const int HTRIGHT = 11;
|
||
const int HTTOP = 12;
|
||
const int HTTOPLEFT = 13;
|
||
const int HTTOPRIGHT = 14;
|
||
const int HTBOTTOM = 15;
|
||
const int HTBOTTOMLEFT = 16;
|
||
const int HTBOTTOMRIGHT = 17;
|
||
|
||
// WPF 尺寸的 offset
|
||
const int offsetWpf = 6;
|
||
const int hitWidthWpf = 5;
|
||
|
||
// 过滤非 WM_NCHITTEST 事件
|
||
if (msg != WM_NCHITTEST)
|
||
return nint.Zero;
|
||
|
||
// 提取鼠标坐标
|
||
// 没妈的 VB 强转还得检查一下幻想的妈是不是还活着
|
||
var mouseBytes = BitConverter.GetBytes(lParam.ToInt64());
|
||
var xMouse = BitConverter.ToInt16(mouseBytes, 0);
|
||
var yMouse = BitConverter.ToInt16(mouseBytes, 2);
|
||
|
||
// 获取窗口参数
|
||
var windowRect = WindowInterop.GetWindowRectangle(hWnd);
|
||
var windowBounds = windowRect.ToWindowBounds();
|
||
|
||
// 判断鼠标是否在窗口范围内
|
||
var isInWindow = xMouse >= windowRect.Left && xMouse <= windowRect.Right && yMouse >= windowRect.Top &&
|
||
yMouse <= windowRect.Bottom;
|
||
|
||
// 过滤不在窗口内的请求
|
||
if (!isInWindow)
|
||
return nint.Zero;
|
||
|
||
// 如果 CanResize 为 False,直接返回 HTCLIENT
|
||
if (!canResize)
|
||
return new nint(HTCLIENT);
|
||
|
||
// 真实像素尺寸的 offset
|
||
var dpi = VisualTreeHelper.GetDpi(this);
|
||
var offsetPxX = offsetWpf * dpi.DpiScaleX;
|
||
var offsetPxY = offsetWpf * dpi.DpiScaleY;
|
||
var hitWidthPxX = hitWidthWpf * dpi.DpiScaleX;
|
||
var hitWidthPxY = hitWidthWpf * dpi.DpiScaleY;
|
||
|
||
// 计算鼠标相对于窗口左上角的物理像素位置
|
||
var relX = xMouse - windowRect.Left;
|
||
var relY = yMouse - windowRect.Top;
|
||
var w = windowBounds.Width;
|
||
var h = windowBounds.Height;
|
||
|
||
// 判定是否命中偏移后的热区
|
||
var inLeft = relX >= offsetPxX && relX <= offsetPxX + hitWidthPxX;
|
||
var inRight = relX <= w - offsetPxX && relX >= w - offsetPxX - hitWidthPxX;
|
||
var inTop = relY >= offsetPxY && relY <= offsetPxY + hitWidthPxY;
|
||
var inBottom = relY <= h - offsetPxY && relY >= h - offsetPxY - hitWidthPxY;
|
||
|
||
handled = true; // 接管该区域的消息
|
||
|
||
// 返回结果
|
||
if (inTop && inLeft)
|
||
return new nint(HTTOPLEFT);
|
||
if (inTop && inRight)
|
||
return new nint(HTTOPRIGHT);
|
||
if (inBottom && inLeft)
|
||
return new nint(HTBOTTOMLEFT);
|
||
if (inBottom && inRight)
|
||
return new nint(HTBOTTOMRIGHT);
|
||
if (inLeft)
|
||
return new nint(HTLEFT);
|
||
if (inRight)
|
||
return new nint(HTRIGHT);
|
||
if (inTop)
|
||
return new nint(HTTOP);
|
||
if (inBottom)
|
||
return new nint(HTBOTTOM);
|
||
|
||
// 如果在 0-offset 范围内,返回 HTCLIENT 杀掉默认缩放
|
||
return new nint(HTCLIENT);
|
||
}
|
||
|
||
protected override void OnSourceInitialized(EventArgs e)
|
||
{
|
||
// 硬件加速
|
||
if (Config.System.DisableHardwareAcceleration)
|
||
{
|
||
var hwndSource = PresentationSource.FromVisual(this) as HwndSource;
|
||
if (hwndSource is not null) hwndSource.CompositionTarget.RenderMode = RenderMode.SoftwareOnly;
|
||
}
|
||
|
||
base.OnSourceInitialized(e);
|
||
|
||
// 获取当前窗口句柄
|
||
var hwnd = new WindowInteropHelper(this).Handle;
|
||
var source = HwndSource.FromHwnd(hwnd);
|
||
if (source is not null)
|
||
{
|
||
// 渲染层允许 Alpha 通道通过
|
||
source.CompositionTarget.BackgroundColor = Colors.Transparent;
|
||
// 魔改窗口边缘判定
|
||
source.AddHook(_SizeWndProc);
|
||
}
|
||
|
||
// 设置 DWM 窗口框架
|
||
try
|
||
{
|
||
WindowInterop.ExtendFrameIntoClientArea(hwnd, -1);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogWrapper.Error("DWM 窗口框架应用失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
// 关闭
|
||
private void FormMain_Closing(object sender, CancelEventArgs e)
|
||
{
|
||
EndProgram(true);
|
||
e.Cancel = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 正常关闭程序。程序将在执行此方法后约 0.3s 退出。
|
||
/// </summary>
|
||
/// <param name="sendWarning">是否在还有下载任务未完成时发出警告。</param>
|
||
/// <param name="isUpdating">是否正在更新重启</param>
|
||
public void EndProgram(bool sendWarning, bool isUpdating = false)
|
||
{
|
||
// 发出警告
|
||
if (sendWarning && ModNet.HasDownloadingTask())
|
||
{
|
||
if (ModMain.MyMsgBox(Lang.Text("Main.Exit.HasDownloadingTask"), Lang.Text("Common.Dialog.Title"), Lang.Text("Common.Action.Confirm"), Lang.Text("Common.Action.Cancel")) == 1)
|
||
// 强行结束下载任务
|
||
ModBase.RunInNewThread(() =>
|
||
{
|
||
ModBase.Log("[System] 正在强行停止任务");
|
||
foreach (var Task in ModLoader.loaderTaskbar.ToList())
|
||
Task.Abort();
|
||
}, "强行停止下载任务");
|
||
else
|
||
return;
|
||
}
|
||
|
||
// 关闭联机大厅
|
||
// Await LobbyController.CloseAsync().ConfigureAwait(False)
|
||
// 存储上次使用的档案编号
|
||
ModProfile.SaveProfile();
|
||
// 关闭
|
||
ModBase.RunInUiWait(() =>
|
||
{
|
||
// 清理视频背景
|
||
VideoBack.Stop();
|
||
VideoBack.Source = null;
|
||
VideoBack.Close();
|
||
IsHitTestVisible = false;
|
||
if (RenderTransform is null)
|
||
{
|
||
var transformPos = new TranslateTransform(0d, 0d);
|
||
var transformRotate = new RotateTransform(0d);
|
||
var transformScale = new ScaleTransform(1d, 1d);
|
||
transformScale.CenterX = Width / 2d;
|
||
transformScale.CenterY = Height / 2d;
|
||
RenderTransform = new TransformGroup
|
||
{ Children = new TransformCollection([transformRotate, transformPos, transformScale]) };
|
||
ModAnimation.AniStart(new[]
|
||
{
|
||
ModAnimation.AaOpacity(this, -Opacity, 140, 40,
|
||
new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak)),
|
||
ModAnimation.AaDouble(i =>
|
||
{
|
||
transformScale.ScaleX += (double)i;
|
||
transformScale.ScaleY += (double)i;
|
||
}, 0.88d - transformScale.ScaleX, 180),
|
||
ModAnimation.AaDouble(i => transformPos.Y += (double)i,
|
||
20d - transformPos.Y, 180, 0,
|
||
new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak)),
|
||
ModAnimation.AaDouble(i => transformRotate.Angle += (double)i,
|
||
0.6d - transformRotate.Angle, 180, 0,
|
||
new ModAnimation.AniEaseInoutFluent(ModAnimation.AniEasePower.Weak)),
|
||
ModAnimation.AaCode(() =>
|
||
{
|
||
IsHitTestVisible = false;
|
||
Visibility = Visibility.Collapsed;
|
||
ShowInTaskbar = false;
|
||
}, 210),
|
||
ModAnimation.AaCode(() => EndProgramForce(force: false, isUpdating: isUpdating), 230)
|
||
}, "Form Close");
|
||
}
|
||
else
|
||
{
|
||
EndProgramForce(force: false, isUpdating: isUpdating);
|
||
}
|
||
|
||
ModBase.Log("[System] 收到关闭指令");
|
||
});
|
||
}
|
||
|
||
private static bool isLogShown;
|
||
|
||
public static void EndProgramForce(ModBase.ProcessReturnValues returnCode = ModBase.ProcessReturnValues.Success,
|
||
bool force = true, bool isUpdating = false)
|
||
{
|
||
// On Error Resume Next
|
||
// 关闭联机大厅
|
||
// Await LobbyController.CloseAsync().ConfigureAwait(False)
|
||
ModBase.isProgramEnded = true;
|
||
ModAnimation.AniControlEnabled += 1;
|
||
if (UpdateManager.isUpdateWaitingRestart && !isUpdating)
|
||
UpdateManager.UpdateRestart(false, false);
|
||
if (returnCode == ModBase.ProcessReturnValues.Exception)
|
||
{
|
||
if (!isLogShown)
|
||
{
|
||
ModBase.FeedbackInfo();
|
||
ModBase.Log("请在 https://github.com/PCL-Community/PCL2-CE/issues 提交错误报告,以便于社区解决此问题!(这也有可能是原版 PCL 的问题)");
|
||
isLogShown = true;
|
||
ModBase.ShellOnly(LogWrapper.CurrentLogger.CurrentLogFiles.Last());
|
||
}
|
||
|
||
Thread.Sleep(500); // 防止 PCL 在记事本打开前就被掐掉
|
||
}
|
||
|
||
ModBase.Log("[System] 程序已退出,返回值:" + ModBase.GetStringFromEnum(returnCode));
|
||
// If ReturnCode <> ProcessReturnValues.Success Then Environment.Exit(ReturnCode)
|
||
// Process.GetCurrentProcess.Kill()
|
||
Lifecycle.Shutdown((int)returnCode, force);
|
||
}
|
||
|
||
private void BtnTitleClose_Click(object sender, EventArgs e)
|
||
{
|
||
EndProgram(true);
|
||
}
|
||
|
||
// 移动
|
||
private void FormDragMove(object sender, MouseButtonEventArgs e)
|
||
{
|
||
// On Error Resume Next
|
||
if (((Grid)sender).IsMouseDirectlyOver)
|
||
DragMove();
|
||
}
|
||
|
||
// 改变大小
|
||
/// <summary>
|
||
/// 是否可以向注册表储存尺寸改变信息。以此避免初始化时误储存。
|
||
/// </summary>
|
||
public bool isSizeSaveable;
|
||
|
||
private void FormMain_SizeChanged(object? sender = null, EventArgs? e = null)
|
||
{
|
||
if (isSizeSaveable)
|
||
{
|
||
States.UI.WindowHeight = Height;
|
||
States.UI.WindowWidth = Width;
|
||
}
|
||
|
||
if (PanBack is not null)
|
||
{
|
||
RectForm.Rect = new Rect(0d, 0d, PanBack.ActualWidth, PanBack.ActualHeight);
|
||
|
||
var formWidth = PanBack.ActualWidth + 0.001d;
|
||
var formHeight = PanBack.ActualHeight + 0.001d;
|
||
|
||
PanForm.Width = formWidth;
|
||
PanForm.Height = formHeight;
|
||
PanMain.Width = formWidth;
|
||
|
||
if (PanTitle is not null)
|
||
PanMain.Height = Math.Max(0d, formHeight - PanTitle.ActualHeight);
|
||
else
|
||
PanMain.Height = formHeight;
|
||
|
||
VideoBack.Width = formWidth;
|
||
VideoBack.Height = formHeight;
|
||
}
|
||
|
||
if (WindowState == WindowState.Maximized)
|
||
WindowState = WindowState.Normal; // 修复 #1938
|
||
}
|
||
|
||
// 标题栏改变大小
|
||
private void PanTitle_SizeChanged(object sender, EventArgs e)
|
||
{
|
||
if (PanTitleMain.ColumnDefinitions[0].ActualWidth - 30 <= 0)
|
||
PanTitleLeft.ColumnDefinitions[0].MaxWidth = 0;
|
||
else
|
||
PanTitleLeft.ColumnDefinitions[0].MaxWidth = PanTitleMain.ColumnDefinitions[0].ActualWidth - 30;
|
||
}
|
||
|
||
// 最小化
|
||
private void BtnTitleMin_Click(object sender, EventArgs e)
|
||
{
|
||
WindowState = WindowState.Minimized;
|
||
}
|
||
|
||
private void BtnTitleHelp_Click(object sender, EventArgs e)
|
||
{
|
||
ModBase.OpenWebsite("https://www.bilibili.com/video/BV1uT4y1P7CX");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 窗体事件
|
||
|
||
public void AddResizer()
|
||
{
|
||
canResize = true;
|
||
}
|
||
|
||
public void RemoveResizer()
|
||
{
|
||
canResize = false;
|
||
}
|
||
|
||
// 按键事件
|
||
private void FormMain_KeyDown(object sender, KeyEventArgs e)
|
||
{
|
||
if (e.IsRepeat)
|
||
return;
|
||
// 调用弹窗:回车选择第一个,Esc 选择最后一个
|
||
if (PanMsg.Children.Count > 0)
|
||
{
|
||
if (e.Key == Key.Enter)
|
||
{
|
||
var msg = PanMsg.Children[0];
|
||
Action? enterAction = msg switch
|
||
{
|
||
MyMsgInput input => () => input.Btn1_Click(sender, null),
|
||
MyMsgSelect select => () => select.Btn1_Click(sender, null),
|
||
MyMsgText text => () => text.Btn1_Click(sender, null),
|
||
MyMsgMarkdown markdown => () => markdown.Btn1_Click(sender, null),
|
||
MyMsgLogin login => () => login.Btn1_Click(sender, null),
|
||
_ => null
|
||
};
|
||
enterAction?.Invoke();
|
||
return;
|
||
}
|
||
|
||
if (e.Key == Key.Escape)
|
||
{
|
||
var msg = PanMsg.Children[0];
|
||
Action? escapeAction = msg switch
|
||
{
|
||
MyMsgInput input => input.Btn2.Visibility == Visibility.Visible
|
||
? () => input.Btn2_Click(sender, null)
|
||
: () => input.Btn1_Click(sender, null),
|
||
MyMsgSelect select => select.Btn2.Visibility == Visibility.Visible
|
||
? () => select.Btn2_Click(sender, null)
|
||
: () => select.Btn1_Click(sender, null),
|
||
MyMsgText text => text.Btn3.Visibility == Visibility.Visible
|
||
? () => text.Btn3_Click(sender, null)
|
||
: text.Btn2.Visibility == Visibility.Visible
|
||
? () => text.Btn2_Click(sender, null)
|
||
: () => text.Btn1_Click(sender, null),
|
||
MyMsgMarkdown markdown => markdown.Btn3.Visibility == Visibility.Visible
|
||
? () => markdown.Btn3_Click(sender, null)
|
||
: markdown.Btn2.Visibility == Visibility.Visible
|
||
? () => markdown.Btn2_Click(sender, null)
|
||
: () => markdown.Btn1_Click(sender, null),
|
||
MyMsgLogin login => login.Btn3.Visibility == Visibility.Visible
|
||
? () => login.Btn3_Click(sender, null)
|
||
: () => login.Btn1_Click(sender, null),
|
||
_ => null
|
||
};
|
||
escapeAction?.Invoke();
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 按 ESC 返回上一级
|
||
if (e.Key == Key.Escape)
|
||
TriggerPageBack();
|
||
// 更改隐藏实例可见性
|
||
if (e.Key == Key.F11 && pageCurrent == PageType.InstanceSelect)
|
||
{
|
||
ModMain.frmSelectRight.showHidden = !ModMain.frmSelectRight.showHidden;
|
||
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
|
||
ModLoader.LoaderFolderRunType.ForceRun, 1, @"versions\");
|
||
return;
|
||
}
|
||
|
||
// 更改功能隐藏可见性
|
||
if (e.Key == Key.F12)
|
||
{
|
||
PageSetupUI.HiddenForceShow = !PageSetupUI.HiddenForceShow;
|
||
if (PageSetupUI.HiddenForceShow)
|
||
HintService.Hint(Lang.Text("Main.HiddenFeature.Disabled"), HintType.Success);
|
||
else
|
||
HintService.Hint(Lang.Text("Main.HiddenFeature.Enabled"), HintType.Success);
|
||
PageSetupUI.HiddenRefresh();
|
||
return;
|
||
}
|
||
|
||
// 按 F5 刷新页面
|
||
if (e.Key == Key.F5)
|
||
{
|
||
if (pageLeft is IRefreshable)
|
||
((IRefreshable)pageLeft).Refresh();
|
||
if (pageRight is IRefreshable)
|
||
((IRefreshable)pageRight).Refresh();
|
||
return;
|
||
}
|
||
|
||
// 调用启动游戏
|
||
if (e.Key == Key.Enter && pageCurrent == PageType.Launch)
|
||
{
|
||
if (ModMain.isAprilEnabled && !ModMain.isAprilGiveup)
|
||
HintService.Hint(Lang.Text("Main.April.Nope"));
|
||
else
|
||
ModMain.frmLaunchLeft.LaunchButtonClick();
|
||
}
|
||
|
||
// 修复按下 Alt 后误认为弹出系统菜单导致的冻结
|
||
if (e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt)
|
||
e.Handled = true;
|
||
}
|
||
|
||
private void FormMain_MouseDown(object sender, MouseButtonEventArgs e)
|
||
{
|
||
// 鼠标侧键返回上一级
|
||
if (ModMain.frmMain!.PanMsg.Children.Count > 0 || ModMain.WaitingMyMsgBox.Any())
|
||
return; // 弹窗中(#5513)
|
||
if (e.ChangedButton == MouseButton.XButton1 || e.ChangedButton == MouseButton.XButton2)
|
||
TriggerPageBack();
|
||
}
|
||
|
||
private void TriggerPageBack()
|
||
{
|
||
if (pageCurrent == PageType.Download && PageCurrentSub == PageSubType.DownloadInstall &&
|
||
ModMain.frmDownloadInstall.isInSelectPage)
|
||
ModMain.frmDownloadInstall.ExitSelectPage();
|
||
else if (pageCurrent == PageType.InstanceSetup && PageCurrentSub == PageSubType.VersionInstall &&
|
||
ModMain.frmInstanceInstall.isInSelectPage)
|
||
ModMain.frmInstanceInstall.ExitSelectPage();
|
||
else
|
||
PageBack();
|
||
}
|
||
|
||
// 切回窗口
|
||
private void FormMain_Activated(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (Config.Download.Comp.ReadClipboard)
|
||
ModComp.CompClipboard.GetClipboardResource();
|
||
if (pageCurrent == PageType.InstanceSetup && PageCurrentSub == PageSubType.VersionMod)
|
||
{
|
||
// Mod 管理自动刷新
|
||
ModMain.frmInstanceMod.ReloadCompFileList();
|
||
}
|
||
else if (pageCurrent == PageType.InstanceSetup && PageCurrentSub == PageSubType.VersionResourcePack)
|
||
{
|
||
// 资源包管理自动刷新
|
||
if (ModMain.frmInstanceResourcePack is not null)
|
||
ModMain.frmInstanceResourcePack.ReloadCompFileList();
|
||
}
|
||
else if (pageCurrent == PageType.InstanceSetup && PageCurrentSub == PageSubType.VersionShader)
|
||
{
|
||
// 光影包管理自动刷新
|
||
if (ModMain.frmInstanceShader is not null)
|
||
ModMain.frmInstanceShader.ReloadCompFileList();
|
||
}
|
||
else if (pageCurrent == PageType.InstanceSetup && PageCurrentSub == PageSubType.VersionSchematic)
|
||
{
|
||
// 投影原理图管理自动刷新
|
||
if (ModMain.frmInstanceSchematic is not null)
|
||
ModMain.frmInstanceSchematic.ReloadCompFileList();
|
||
}
|
||
else if (pageCurrent == PageType.InstanceSelect)
|
||
{
|
||
// 实例选择自动刷新
|
||
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
|
||
ModLoader.LoaderFolderRunType.RunOnUpdated, 1, @"versions\");
|
||
}
|
||
else if (ModMain.frmMain.pageRight is PageInstanceSavesDatapack &&
|
||
ModMain.frmInstanceSavesDatapack is not null)
|
||
{
|
||
// 数据包管理自动刷新
|
||
ModMain.frmInstanceSavesDatapack.ReloadDatapackFileList();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"切回窗口时出错",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
}
|
||
|
||
private IDataObject _HandleDrag_PrevData;
|
||
private DragDropEffects _HandleDrag_PrevEffects;
|
||
|
||
// 文件拖放
|
||
private void HandleDrag(object sender, DragEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (e.Handled && e.Effects != DragDropEffects.None)
|
||
return;
|
||
// 缓存
|
||
e.Handled = true;
|
||
if (ReferenceEquals(e.Data, _HandleDrag_PrevData))
|
||
{
|
||
e.Effects = _HandleDrag_PrevEffects;
|
||
return;
|
||
}
|
||
|
||
// 确定拖放效果
|
||
e.Effects = DragDropEffects.None;
|
||
if (e.Data.GetDataPresent(DataFormats.Text))
|
||
{
|
||
var str = (string)e.Data.GetData(DataFormats.Text);
|
||
if (str.StartsWithF("authlib-injector:yggdrasil-server:"))
|
||
e.Effects = DragDropEffects.Copy;
|
||
else if (str.StartsWithF("file:///")) e.Effects = DragDropEffects.Copy;
|
||
}
|
||
else if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||
{
|
||
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||
if (files is not null && files.Length > 0) e.Effects = DragDropEffects.Link;
|
||
}
|
||
|
||
_HandleDrag_PrevData = e.Data;
|
||
_HandleDrag_PrevEffects = e.Effects;
|
||
ModBase.Log("[System] 设置拖放类型:" + ModBase.GetStringFromEnum(e.Effects));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"处理拖放时出错",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
}
|
||
|
||
private void FrmMain_Drop(object sender, DragEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (e.Data.GetDataPresent(DataFormats.Text))
|
||
{
|
||
// 获取文本
|
||
try
|
||
{
|
||
var str = (string)e.Data.GetData(DataFormats.Text);
|
||
ModBase.Log("[System] 接受文本拖拽:" + str);
|
||
if (str.StartsWithF("authlib-injector:yggdrasil-server:"))
|
||
{
|
||
// Authlib 拖拽
|
||
e.Handled = true;
|
||
e.Effects = DragDropEffects.Copy;
|
||
var authlibServer =
|
||
WebUtility.UrlDecode(str.Substring("authlib-injector:yggdrasil-server:".Length));
|
||
ModBase.Log("[System] Authlib 拖拽:" + authlibServer);
|
||
if (!new HttpValidator().Validate(authlibServer).IsValid)
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.AuthlibInvalid", authlibServer), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
if (ModMain.MyMsgBox(Lang.Text("Main.FileDrag.CreateAuthlibProfile", authlibServer), Lang.Text("Main.FileDrag.CreateAuthlibProfileTitle"),
|
||
Lang.Text("Common.Action.Confirm"), Lang.Text("Common.Action.Cancel")) == 2)
|
||
return;
|
||
ModProfile.selectedProfile = null;
|
||
ModBase.RunInUi(() =>
|
||
{
|
||
PageLoginAuth.draggedAuthServer = authlibServer;
|
||
ModMain.frmLaunchLeft.RefreshPage(true, ModLaunch.McLoginType.Auth);
|
||
});
|
||
if (pageCurrent == PageType.InstanceSetup && PageCurrentSub == PageSubType.VersionSetup)
|
||
// 正在服务器选项页,需要刷新设置项显示
|
||
ModMain.frmInstanceSetup.Reload();
|
||
}
|
||
else if (str.StartsWithF("file:///"))
|
||
{
|
||
// 文件拖拽(例如从浏览器下载窗口拖入)
|
||
var filePath = WebUtility.UrlDecode(str).Substring("file:///".Length).Replace("/", @"\");
|
||
e.Handled = true;
|
||
e.Effects = DragDropEffects.Copy;
|
||
FileDrag(new List<string> { filePath });
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(ex, "无法接取文本拖拽事件", ModBase.LogLevel.Developer);
|
||
}
|
||
}
|
||
else if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||
{
|
||
// 获取文件并检查
|
||
var filePathRaw = e.Data.GetData(DataFormats.FileDrop);
|
||
if (filePathRaw is null) // #2690
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.ExtractFirst"), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
e.Handled = true;
|
||
e.Effects = DragDropEffects.Link;
|
||
FileDrag((IEnumerable<string>)filePathRaw);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"接取拖拽事件失败",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
}
|
||
|
||
private void FileDrag(IEnumerable<string> filePathList)
|
||
{
|
||
ModBase.RunInNewThread(() =>
|
||
{
|
||
var filePath = filePathList.First();
|
||
ModBase.Log("[System] 接受文件拖拽:" + filePath + (filePathList.Any() ? $" 等 {filePathList.Count()} 个文件" : ""),
|
||
ModBase.LogLevel.Developer);
|
||
// 基础检查
|
||
if (Directory.Exists(filePathList.First()) && !File.Exists(filePathList.First()))
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.FileOnly"), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
if (!File.Exists(filePathList.First()))
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.FileNotFound", filePathList.First()), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
// 多文件拖拽
|
||
if (filePathList.Count() > 1)
|
||
{
|
||
// 检查是否为同类型文件
|
||
var firstExtension = filePathList.First().AfterLast(".").ToLower();
|
||
var allSameType = filePathList.All(f => (f.AfterLast(".").ToLower() ?? "") == (firstExtension ?? ""));
|
||
|
||
if (allSameType &&
|
||
new[] { "jar", "litemod", "disabled", "old", "litematic", "nbt", "schematic", "schem" }.Contains(
|
||
firstExtension))
|
||
{
|
||
}
|
||
// 允许同类型的 Mod 文件或投影文件批量拖拽
|
||
else
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.SameTypeOnly"), HintType.Error);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 主页
|
||
var extension = filePath.AfterLast(".").ToLower();
|
||
if (extension == "xaml")
|
||
{
|
||
ModBase.Log("[System] 文件后缀为 XAML,作为主页加载");
|
||
if (File.Exists(ModBase.exePath + @"PCL\Custom.xaml"))
|
||
if (ModMain.MyMsgBox(Lang.Text("Main.FileDrag.HomepageExists"), Lang.Text("Main.FileDrag.OverwriteTitle"), Lang.Text("Common.Action.Overwrite"), Lang.Text("Common.Action.Cancel")) == 2)
|
||
return;
|
||
|
||
ModBase.CopyFile(filePath, ModBase.exePath + @"PCL\Custom.xaml");
|
||
ModBase.RunInUi(() =>
|
||
{
|
||
Config.Preference.Homepage.Type = 1;
|
||
ModMain.frmLaunchRight.ForceRefresh();
|
||
HintService.Hint(Lang.Text("Main.FileDrag.HomepageLoaded"), HintType.Success);
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 安装 Mod
|
||
if (PageInstanceCompResource.InstallMods(filePathList))
|
||
return;
|
||
// 安装投影文件
|
||
if (new[] { "litematic", "nbt", "schematic", "schem" }.Contains(extension))
|
||
{
|
||
ModBase.Log($"[System] 文件为 {extension} 格式,尝试作为原理图安装");
|
||
// 获取当前文件夹路径(如果在资源管理页面)
|
||
string targetFolderPath = null;
|
||
if (pageCurrent == PageType.InstanceSetup && PageCurrentSub == PageSubType.VersionSchematic &&
|
||
ModMain.frmInstanceSchematic is not null &&
|
||
ModMain.frmInstanceSchematic is PageInstanceCompResource)
|
||
targetFolderPath = ModMain.frmInstanceSchematic.CurrentFolderPath;
|
||
PageInstanceCompResource.InstallCompFiles(filePathList, ModComp.CompType.Schematic, targetFolderPath);
|
||
return;
|
||
}
|
||
|
||
// 处理资源安装
|
||
if (pageCurrent == PageType.InstanceSetup && new[] { "zip" }.Any(i => (i ?? "") == (extension ?? "")))
|
||
switch (PageCurrentSub)
|
||
{
|
||
case PageSubType.VersionWorld:
|
||
{
|
||
var destFolder = PageInstanceLeft.McInstance.PathIndie + @"saves\" +
|
||
ModBase.GetFileNameWithoutExtentionFromPath(filePath);
|
||
var destLevelDat = Path.Combine(destFolder, "level.dat");
|
||
if (Directory.Exists(destFolder))
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.SameFolderExists", destFolder), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
var extractFolder = Path.Combine(ModBase.pathTemp, "Cache", "WorldImport", ModBase.GetUuid().ToString());
|
||
try
|
||
{
|
||
ModBase.ExtractFile(filePath, extractFolder);
|
||
var saveRoot = SaveImportHelper.GetSaveRootDirectory(extractFolder);
|
||
if (saveRoot is null)
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.SaveNotFound"), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
ModBase.CopyDirectory(saveRoot, destFolder);
|
||
if (!File.Exists(destLevelDat))
|
||
{
|
||
if (Directory.Exists(destFolder))
|
||
ModBase.DeleteDirectory(destFolder, true);
|
||
HintService.Hint(Lang.Text("Main.FileDrag.SaveInvalid"), HintType.Error);
|
||
return;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
if (Directory.Exists(destFolder))
|
||
ModBase.DeleteDirectory(destFolder, true);
|
||
ModBase.Log(
|
||
ex,
|
||
Lang.Text("Main.FileDrag.SaveImportFailed"),
|
||
ModBase.LogLevel.Hint,
|
||
userSummary: Lang.Text("Main.FileDrag.SaveImportFailed"));
|
||
return;
|
||
}
|
||
finally
|
||
{
|
||
if (Directory.Exists(extractFolder))
|
||
ModBase.DeleteDirectory(extractFolder, true);
|
||
}
|
||
|
||
HintService.Hint(Lang.Text("Main.FileDrag.Imported", ModBase.GetFileNameWithoutExtentionFromPath(filePath)),
|
||
HintType.Success);
|
||
if (ModMain.frmInstanceSaves is not null)
|
||
ModBase.RunInUi(() => ModMain.frmInstanceSaves.Reload());
|
||
return;
|
||
}
|
||
case PageSubType.VersionResourcePack:
|
||
{
|
||
var destFile = PageInstanceLeft.McInstance.PathIndie + @"resourcepacks\" +
|
||
ModBase.GetFileNameFromPath(filePath);
|
||
if (File.Exists(destFile))
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.SameFileExists", destFile), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
ModBase.CopyFile(filePath, destFile);
|
||
HintService.Hint(Lang.Text("Main.FileDrag.Imported", ModBase.GetFileNameFromPath(filePath)), HintType.Success);
|
||
if (ModMain.frmInstanceResourcePack is not null)
|
||
ModBase.RunInUi(() => ModMain.frmInstanceResourcePack.ReloadCompFileList());
|
||
return;
|
||
}
|
||
case PageSubType.VersionShader:
|
||
{
|
||
var destFile = PageInstanceLeft.McInstance.PathIndie + @"shaderpacks\" +
|
||
ModBase.GetFileNameFromPath(filePath);
|
||
if (File.Exists(destFile))
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.SameFileExists", destFile), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
ModBase.CopyFile(filePath, destFile);
|
||
HintService.Hint(Lang.Text("Main.FileDrag.Imported", ModBase.GetFileNameFromPath(filePath)), HintType.Success);
|
||
if (ModMain.frmInstanceShader is not null)
|
||
ModBase.RunInUi(() => ModMain.frmInstanceShader.ReloadCompFileList());
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 处理投影文件
|
||
if (pageCurrent == PageType.InstanceSetup &&
|
||
new[] { "litematic", "nbt", "schematic", "schem" }.Contains(extension) &&
|
||
PageCurrentSub == PageSubType.VersionSchematic)
|
||
{
|
||
var destFile = PageInstanceLeft.McInstance.PathIndie + @"schematics\" +
|
||
ModBase.GetFileNameFromPath(filePath);
|
||
if (File.Exists(destFile))
|
||
{
|
||
HintService.Hint(Lang.Text("Main.FileDrag.SameFileExists", destFile), HintType.Error);
|
||
return;
|
||
}
|
||
|
||
Directory.CreateDirectory(PageInstanceLeft.McInstance.PathIndie + @"schematics\");
|
||
ModBase.CopyFile(filePath, destFile);
|
||
HintService.Hint(Lang.Text("Main.FileDrag.Imported", ModBase.GetFileNameFromPath(filePath)), HintType.Success);
|
||
if (ModMain.frmInstanceSchematic is not null)
|
||
ModBase.RunInUi(() => ModMain.frmInstanceSchematic.ReloadCompFileList());
|
||
return;
|
||
}
|
||
|
||
// 安装整合包
|
||
if (new[] { "zip", "rar", "mrpack" }.Any(t =>
|
||
(t ?? "") == (extension ?? ""))) // 部分压缩包是 zip 格式但后缀为 rar,总之试一试
|
||
{
|
||
ModBase.Log("[System] 文件为压缩包,尝试作为整合包安装");
|
||
try
|
||
{
|
||
ModModpack.ModpackInstall(filePath);
|
||
return;
|
||
}
|
||
catch (ModBase.CancelledException ex)
|
||
{
|
||
return; // 用户主动取消
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 安装失败,继续往后尝试
|
||
}
|
||
}
|
||
|
||
// 错误报告分析
|
||
do
|
||
{
|
||
try
|
||
{
|
||
ModBase.Log("[System] 尝试进行错误报告分析");
|
||
var analyzer = new CrashAnalyzer(ModBase.GetUuid());
|
||
analyzer.Import(filePath);
|
||
if (!analyzer.Prepare())
|
||
break;
|
||
analyzer.Analyze();
|
||
analyzer.Output(true, new List<string>());
|
||
return;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"自主错误报告分析失败",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
} while (false);
|
||
|
||
// 未知操作
|
||
HintService.Hint(Lang.Text("Main.FileDrag.UnknownOperation"));
|
||
}, "文件拖拽");
|
||
}
|
||
|
||
// 接受到 Windows 窗体事件
|
||
public bool isSystemTimeChanged;
|
||
|
||
private nint WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
|
||
{
|
||
if (msg == 30)
|
||
{
|
||
var nowDate = DateTime.Now;
|
||
if (nowDate.Date == ModBase.applicationOpenTime.Date)
|
||
{
|
||
ModBase.Log("[System] 系统时间微调为:" + nowDate.ToLongDateString() + " " + nowDate.ToLongTimeString());
|
||
isSystemTimeChanged = false;
|
||
}
|
||
else
|
||
{
|
||
ModBase.Log("[System] 系统时间修改为:" + nowDate.ToLongDateString() + " " + nowDate.ToLongTimeString());
|
||
isSystemTimeChanged = true;
|
||
}
|
||
}
|
||
else if (msg == 400 * 16 + 2)
|
||
{
|
||
ModBase.Log("[System] 收到置顶信息:" + hwnd.ToInt64());
|
||
if (!isWindowLoadFinished)
|
||
{
|
||
ModBase.Log("[System] 窗口尚未加载完成,忽略置顶请求");
|
||
return nint.Zero;
|
||
}
|
||
|
||
ShowWindowToTop();
|
||
handled = true;
|
||
}
|
||
else if (msg == 26) // WM_SETTINGCHANGE
|
||
{
|
||
if (Marshal.PtrToStringAuto(lParam) == "ImmersiveColorSet")
|
||
{
|
||
ModBase.Log($"[System] 系统主题更改,深色模式:{SystemTheme.IsSystemInDarkMode()}");
|
||
if (Config.Preference.Theme.ColorMode == ColorMode.System &&
|
||
(ThemeManager.IsDarkMode != SystemTheme.IsSystemInDarkMode())) ThemeService.RefreshColorMode();
|
||
}
|
||
}
|
||
|
||
return nint.Zero;
|
||
}
|
||
|
||
// 窗口隐藏与置顶
|
||
public bool Hidden
|
||
{
|
||
get => field;
|
||
set
|
||
{
|
||
if (field == value)
|
||
return;
|
||
field = value;
|
||
if (value)
|
||
{
|
||
// 隐藏
|
||
Left -= 10000d;
|
||
ShowInTaskbar = false;
|
||
Visibility = Visibility.Hidden;
|
||
ModBase.Log("[System] 窗口已隐藏,位置:(" + Left + "," + Top + ")");
|
||
}
|
||
else
|
||
{
|
||
// 取消隐藏
|
||
if (Left < -2000)
|
||
Left += 10000d;
|
||
ShowWindowToTop();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 解决龙猫的非通用实现史山
|
||
protected override void OnActivated(EventArgs e)
|
||
{
|
||
base.OnActivated(e);
|
||
if (Hidden)
|
||
Hidden = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 把当前窗口拖到最前面。
|
||
/// </summary>
|
||
public void ShowWindowToTop()
|
||
{
|
||
ModBase.RunInUi(() =>
|
||
{
|
||
// 这一坨乱七八糟的,别改,改了指不定就炸了,自己电脑还复现不出来
|
||
Visibility = Visibility.Visible;
|
||
ShowInTaskbar = true;
|
||
WindowState = WindowState.Normal;
|
||
Hidden = false;
|
||
Topmost = true; // 偶尔 SetForegroundWindow 失效
|
||
Topmost = false;
|
||
ModMain.SetForegroundWindow(ModBase.frmHandle);
|
||
Focus();
|
||
ModBase.Log($"[System] 窗口已置顶,位置:({Left}, {Top}), {Width} x {Height}");
|
||
});
|
||
}
|
||
|
||
// 背景视频循环播放
|
||
private void VideoEnded(object sender, RoutedEventArgs e)
|
||
{
|
||
VideoBack.Position = TimeSpan.Zero;
|
||
VideoBack.Play();
|
||
}
|
||
|
||
// 最小化时暂停背景视频
|
||
private void WindowStateChanged(object sender, EventArgs e)
|
||
{
|
||
switch (WindowState)
|
||
{
|
||
case WindowState.Minimized:
|
||
{
|
||
ModVideoBack.isMinimized = true;
|
||
ModVideoBack.VideoPause();
|
||
break;
|
||
}
|
||
case WindowState.Normal:
|
||
{
|
||
ModVideoBack.isMinimized = false;
|
||
ModVideoBack.VideoPlay();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 切换页面
|
||
|
||
// 页面种类与属性
|
||
// 注意,这一枚举在 “切换页面” EventType 中调用,应视作公开 API 的一部分
|
||
/// <summary>
|
||
/// 页面种类。
|
||
/// </summary>
|
||
public enum PageType
|
||
{
|
||
/// <summary>
|
||
/// 启动。
|
||
/// </summary>
|
||
Launch = 0,
|
||
|
||
/// <summary>
|
||
/// 下载。
|
||
/// </summary>
|
||
Download = 1,
|
||
|
||
/// <summary>
|
||
/// 联机。
|
||
/// </summary>
|
||
Tools = 3,
|
||
|
||
/// <summary>
|
||
/// 设置。
|
||
/// </summary>
|
||
Setup = 2,
|
||
|
||
/// <summary>
|
||
/// 实例选择。这是一个副页面。
|
||
/// </summary>
|
||
InstanceSelect = 5,
|
||
|
||
/// <summary>
|
||
/// 任务管理。这是一个副页面。
|
||
/// </summary>
|
||
TaskManager = 6,
|
||
|
||
/// <summary>
|
||
/// 实例设置。这是一个副页面。
|
||
/// </summary>
|
||
InstanceSetup = 7,
|
||
|
||
/// <summary>
|
||
/// 资源工程详情。这是一个副页面。
|
||
/// </summary>
|
||
CompDetail = 8,
|
||
|
||
/// <summary>
|
||
/// 游戏实时日志。这是一个副页面。
|
||
/// </summary>
|
||
GameLog = 10,
|
||
|
||
/// <summary>
|
||
/// 存档详细管理,这是一个副页面。
|
||
/// </summary>
|
||
VersionSaves = 12,
|
||
}
|
||
|
||
/// <summary>
|
||
/// 次要页面种类。其数值必须与 StackPanel 中的下标一致。
|
||
/// </summary>
|
||
public enum PageSubType
|
||
{
|
||
Default = 0,
|
||
DownloadInstall = 1,
|
||
DownloadMod = 2,
|
||
DownloadPack = 3,
|
||
DownloadDataPack = 4,
|
||
DownloadResourcePack = 5,
|
||
DownloadShader = 6,
|
||
DownloadWorld = 7,
|
||
DownloadCompFavorites = 8,
|
||
DownloadClient = 9,
|
||
DownloadOptiFine = 10,
|
||
DownloadForge = 11,
|
||
DownloadNeoForge = 12,
|
||
DownloadCleanroom = 13,
|
||
DownloadFabric = 14,
|
||
DownloadLiteLoader = 16,
|
||
DownloadLabyMod = 17,
|
||
DownloadLegacyFabric = 18,
|
||
|
||
SetupLaunch = 0,
|
||
SetupUI = 1,
|
||
SetupGameManage = 2,
|
||
SetupLink = 3,
|
||
SetupAbout = 4,
|
||
SetupLog = 5,
|
||
SetupFeedback = 6,
|
||
SetupGameLink = 7,
|
||
SetupUpdate = 8,
|
||
SetupJava = 9,
|
||
SetupLauncherMisc = 10,
|
||
SetupLauncherLanguage = 11,
|
||
|
||
ToolsGameLink = 1,
|
||
ToolsTest = 3,
|
||
|
||
VersionOverall = 0,
|
||
VersionSetup = 1,
|
||
VersionExport = 2,
|
||
VersionWorld = 3,
|
||
VersionScreenshot = 4,
|
||
VersionMod = 5,
|
||
VersionModDisabled = 6,
|
||
VersionResourcePack = 7,
|
||
VersionShader = 8,
|
||
VersionSchematic = 9,
|
||
VersionInstall = 10,
|
||
VersionServer = 11,
|
||
VersionSavesInfo = 0,
|
||
VersionSavesDatapack = 1
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取次级页面的名称。若并非次级页面则返回空字符串,故可以以此判断是否为次级页面。
|
||
/// </summary>
|
||
private string PageNameGet(PageStackData stack)
|
||
{
|
||
switch (stack.page)
|
||
{
|
||
case PageType.InstanceSelect:
|
||
{
|
||
return Lang.Text("Main.Title.InstanceSelect");
|
||
}
|
||
case PageType.TaskManager:
|
||
{
|
||
return Lang.Text("Main.Title.TaskManager");
|
||
}
|
||
case PageType.GameLog:
|
||
{
|
||
return Lang.Text("Main.Title.GameLog");
|
||
}
|
||
case PageType.InstanceSetup:
|
||
{
|
||
return Lang.Text("Main.Title.InstanceSetup", PageInstanceLeft.McInstance is null ? Lang.Text("Common.State.Unknown") : PageInstanceLeft.McInstance.Name);
|
||
}
|
||
case PageType.CompDetail:
|
||
{
|
||
return Lang.Text("Main.Title.ResourceDownload", stack.additional.Value.CompProject.TranslatedName);
|
||
}
|
||
case PageType.VersionSaves:
|
||
{
|
||
return Lang.Text("Main.Title.SaveManagement", ModBase.GetFolderNameFromPath(stack.additional.Value.SavePath));
|
||
}
|
||
|
||
default:
|
||
{
|
||
return "";
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新次级页面的名称。
|
||
/// </summary>
|
||
public void PageNameRefresh(PageStackData type)
|
||
{
|
||
LabTitleInner.Text = PageNameGet(type);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新次级页面的名称。
|
||
/// </summary>
|
||
public void PageNameRefresh()
|
||
{
|
||
PageNameRefresh(pageCurrent);
|
||
}
|
||
|
||
// 页面状态存储
|
||
/// <summary>
|
||
/// 当前的主页面。
|
||
/// </summary>
|
||
public PageStackData pageCurrent = PageType.Launch;
|
||
|
||
/// <summary>
|
||
/// 上一个主页面。
|
||
/// </summary>
|
||
public PageStackData pageLast = PageType.Launch;
|
||
|
||
/// <summary>
|
||
/// 当前的子页面。
|
||
/// </summary>
|
||
public PageSubType PageCurrentSub
|
||
{
|
||
get
|
||
{
|
||
switch (pageCurrent.page)
|
||
{
|
||
case PageType.Download:
|
||
{
|
||
if (ModMain.frmDownloadLeft is null)
|
||
ModMain.frmDownloadLeft = new PageDownloadLeft();
|
||
return ModMain.frmDownloadLeft.pageID;
|
||
}
|
||
|
||
case PageType.Setup:
|
||
{
|
||
if (ModMain.frmSetupLeft is null)
|
||
ModMain.frmSetupLeft = new PageSetupLeft();
|
||
return ModMain.frmSetupLeft.pageID;
|
||
}
|
||
|
||
case PageType.InstanceSetup:
|
||
{
|
||
if (ModMain.frmInstanceLeft is null)
|
||
ModMain.frmInstanceLeft = new PageInstanceLeft();
|
||
return ModMain.frmInstanceLeft.pageID;
|
||
}
|
||
|
||
default:
|
||
{
|
||
return 0; // 没有子页面
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上层页面的编号堆栈,用于返回。
|
||
/// </summary>
|
||
public List<PageStackData> pageStack = new();
|
||
|
||
public class PageStackData
|
||
{
|
||
/// <summary>
|
||
/// <list type="bullet">
|
||
/// <item><description>CompDetail: (CompProject, ExpandedTitles, TargetVersion, TargetLoader, ResourceType)</description></item>
|
||
/// <item><description>VersionSaves: SavePath</description></item>
|
||
/// </list>
|
||
/// </summary>
|
||
public (
|
||
ModComp.CompProject CompProject,
|
||
List<string> ExpandedTitles,
|
||
string TargetVersion,
|
||
ModComp.CompLoaderType TargetLoader,
|
||
ModComp.CompType ResourceType,
|
||
string SavePath
|
||
)? additional;
|
||
|
||
public PageType page;
|
||
|
||
public override bool Equals(object other)
|
||
{
|
||
if (other is null)
|
||
return false;
|
||
if (other is PageStackData)
|
||
{
|
||
var pageOther = (PageStackData)other;
|
||
if (page != pageOther.page)
|
||
return false;
|
||
if (additional is null) return pageOther.additional is null;
|
||
|
||
return pageOther.additional is not null && additional.Equals(pageOther.additional);
|
||
}
|
||
|
||
if (other is int o)
|
||
{
|
||
if ((int)page == o)
|
||
return false;
|
||
return additional is null;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static bool operator ==(PageStackData left, PageStackData right)
|
||
{
|
||
return EqualityComparer<PageStackData>.Default.Equals(left, right);
|
||
}
|
||
|
||
public static bool operator !=(PageStackData left, PageStackData right)
|
||
{
|
||
return !(left == right);
|
||
}
|
||
|
||
public static implicit operator PageStackData(PageType value)
|
||
{
|
||
return new PageStackData { page = value };
|
||
}
|
||
|
||
public static implicit operator PageType(PageStackData value)
|
||
{
|
||
return value.page;
|
||
}
|
||
}
|
||
|
||
public MyPageLeft pageLeft;
|
||
public MyPageRight pageRight;
|
||
|
||
// 引发实际页面切换的入口
|
||
private bool isChangingPage;
|
||
|
||
/// <summary>
|
||
/// 切换页面,并引起对应选择 UI 的改变。
|
||
/// </summary>
|
||
public void PageChange(PageStackData stack, PageSubType subType = PageSubType.Default)
|
||
{
|
||
if (string.IsNullOrEmpty(PageNameGet(stack)))
|
||
{
|
||
// 切换到主页面
|
||
PageChangeExit();
|
||
isChangingPage = true; // 防止下面的勾选直接触发了 PageChangeActual
|
||
((MyRadioButton)PanTitleSelect.Children[(int)stack.page]).SetChecked(true, true,
|
||
string.IsNullOrEmpty(PageNameGet(pageCurrent)));
|
||
isChangingPage = false;
|
||
switch (stack.page)
|
||
{
|
||
case PageType.Download:
|
||
{
|
||
if (ModMain.frmDownloadLeft is null)
|
||
ModMain.frmDownloadLeft = new PageDownloadLeft();
|
||
foreach (var item in ModMain.frmDownloadLeft.PanItem.Children)
|
||
if (item is MyListItem listItem &&
|
||
ModBase.Val(listItem.Tag) == (double)subType)
|
||
{
|
||
listItem.SetChecked(true, true, stack == pageCurrent);
|
||
break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
case PageType.Setup:
|
||
{
|
||
if (ModMain.frmSetupLeft is null)
|
||
ModMain.frmSetupLeft = new PageSetupLeft();
|
||
foreach (var item in ModMain.frmSetupLeft.PanItem.Children)
|
||
if (item is MyListItem listItem &&
|
||
ModBase.Val(listItem.Tag) == (double)subType)
|
||
{
|
||
listItem.SetChecked(true, true, stack == pageCurrent);
|
||
break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
PageChangeActual(stack, subType);
|
||
}
|
||
else
|
||
{
|
||
// 切换到次页面
|
||
switch (stack.page)
|
||
{
|
||
case PageType.InstanceSetup:
|
||
{
|
||
if (ModMain.frmInstanceLeft is null)
|
||
ModMain.frmInstanceLeft = new PageInstanceLeft();
|
||
foreach (var item in ModMain.frmInstanceLeft.PanItem.Children)
|
||
if (item is MyListItem listItem &&
|
||
ModBase.Val(listItem.Tag) == (double)subType)
|
||
{
|
||
listItem.SetChecked(true, true, stack == pageCurrent);
|
||
break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
case PageType.VersionSaves:
|
||
{
|
||
if (ModMain.frmInstanceSavesLeft is null)
|
||
ModMain.frmInstanceSavesLeft = new PageInstanceSavesLeft();
|
||
foreach (var item in ModMain.frmInstanceSavesLeft.PanItem.Children)
|
||
if (item is MyListItem listItem &&
|
||
ModBase.Val(listItem.Tag) == (double)subType)
|
||
{
|
||
listItem.SetChecked(true, true, stack == pageCurrent);
|
||
break;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
PageChangeActual(stack, subType);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过点击导航栏改变页面。
|
||
/// </summary>
|
||
private void BtnTitleSelect_Click(MyRadioButton sender, bool raiseByMouse)
|
||
{
|
||
if (isChangingPage)
|
||
return;
|
||
var pageType = (PageType)int.Parse(sender.Tag.ToString());
|
||
PageChangeActual(pageType, PageSubType.Default);
|
||
}
|
||
|
||
private void BtnTitleInner_Click(object sender, EventArgs e)
|
||
{
|
||
PageBack();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过点击返回按钮或手动触发返回来改变页面。
|
||
/// </summary>
|
||
public void PageBack()
|
||
{
|
||
if (pageStack.Any())
|
||
PageChangeActual(pageStack[0], PageSubType.Default);
|
||
else
|
||
PageChange(PageType.Launch);
|
||
}
|
||
|
||
// 实际处理页面切换
|
||
/// <summary>
|
||
/// 切换现有页面的实际方法。
|
||
/// </summary>
|
||
private void PageChangeActual(PageStackData stack, PageSubType subType)
|
||
{
|
||
if (pageCurrent == stack && (PageCurrentSub == subType || (int)subType == -1))
|
||
return;
|
||
ModAnimation.AniControlEnabled += 1;
|
||
try
|
||
{
|
||
#region 子页面处理
|
||
|
||
var pageName = PageNameGet(stack);
|
||
if (string.IsNullOrEmpty(pageName))
|
||
{
|
||
// 即将切换到一个顶级页面
|
||
PageChangeExit();
|
||
}
|
||
// 即将切换到一个子页面
|
||
else if (pageStack.Any())
|
||
{
|
||
// 子页面 → 另一个子页面,更新
|
||
ModAnimation.AniStart(
|
||
new[]
|
||
{
|
||
ModAnimation.AaOpacity(LabTitleInner, -LabTitleInner.Opacity, 130),
|
||
ModAnimation.AaCode(() => LabTitleInner.Text = pageName, after: true),
|
||
ModAnimation.AaOpacity(LabTitleInner, 1d, 150, 30)
|
||
}, "FrmMain Titlebar SubLayer");
|
||
if (pageStack.Contains(stack))
|
||
// 返回到更上层的子页面
|
||
while (pageStack.Contains(stack))
|
||
pageStack.RemoveAt(0);
|
||
else
|
||
// 进入更深层的子页面
|
||
pageStack.Insert(0, pageCurrent);
|
||
}
|
||
else
|
||
{
|
||
// 主页面 → 子页面,进入
|
||
PanTitleInner.Visibility = Visibility.Visible;
|
||
PanTitleMain.IsHitTestVisible = false;
|
||
PanTitleInner.IsHitTestVisible = true;
|
||
PageNameRefresh(stack);
|
||
ModAnimation.AniStart(
|
||
new[]
|
||
{
|
||
ModAnimation.AaOpacity(PanTitleMain, -PanTitleMain.Opacity, 150),
|
||
ModAnimation.AaX(PanTitleMain, 12d - PanTitleMain.Margin.Left, 150,
|
||
ease: new ModAnimation.AniEaseInFluent(ModAnimation.AniEasePower.Weak)),
|
||
ModAnimation.AaOpacity(PanTitleInner, 1d - PanTitleInner.Opacity, 150, 200),
|
||
ModAnimation.AaX(PanTitleInner, -PanTitleInner.Margin.Left, 350, 200,
|
||
new ModAnimation.AniEaseOutBack()),
|
||
ModAnimation.AaCode(() => PanTitleMain.Visibility = Visibility.Collapsed, after: true)
|
||
}, "FrmMain Titlebar FirstLayer");
|
||
pageStack.Insert(0, pageCurrent);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 实际更改页面框架 UI
|
||
|
||
pageLast = pageCurrent;
|
||
pageCurrent = stack;
|
||
switch (stack.page)
|
||
{
|
||
case PageType.Launch: // 启动
|
||
{
|
||
PageChangeAnim(ModMain.frmLaunchLeft, ModMain.frmLaunchRight);
|
||
break;
|
||
}
|
||
case PageType.Download: // 下载
|
||
{
|
||
ModMain.frmDownloadLeft ??= new PageDownloadLeft();
|
||
if (subType != PageSubType.Default)
|
||
ModMain.frmDownloadLeft.pageID = subType;
|
||
else
|
||
subType = ModMain.frmDownloadLeft.pageID;
|
||
// PageGet 方法会在未设置 SubType 时指定默认值,并建立相关页面的实例
|
||
PageChangeAnim(ModMain.frmDownloadLeft, (FrameworkElement)ModMain.frmDownloadLeft.PageGet(subType));
|
||
break;
|
||
}
|
||
case PageType.Tools: // 联机
|
||
{
|
||
ModMain.frmToolsLeft ??= new PageToolsLeft();
|
||
subType = ModMain.frmToolsLeft.pageID;
|
||
PageChangeAnim(ModMain.frmToolsLeft, (FrameworkElement)ModMain.frmToolsLeft.PageGet(subType));
|
||
break;
|
||
}
|
||
case PageType.Setup: // 设置
|
||
{
|
||
ModMain.frmSetupLeft ??= new PageSetupLeft();
|
||
subType = ModMain.frmSetupLeft.pageID;
|
||
PageChangeAnim(ModMain.frmSetupLeft, (FrameworkElement)ModMain.frmSetupLeft.PageGet(subType));
|
||
break;
|
||
}
|
||
case PageType.GameLog: // 实时日志
|
||
{
|
||
if (ModMain.frmLogLeft is null)
|
||
ModMain.frmLogLeft = new PageLogLeft();
|
||
if (ModMain.frmLogLeft is null)
|
||
ModMain.frmLogRight = new PageLogRight();
|
||
PageChangeAnim(ModMain.frmLogLeft, ModMain.frmLogRight);
|
||
break;
|
||
}
|
||
case PageType.InstanceSelect: // 实例选择
|
||
{
|
||
if (ModMain.frmSelectLeft is null)
|
||
ModMain.frmSelectLeft = new PageSelectLeft();
|
||
if (ModMain.frmSelectRight is null)
|
||
ModMain.frmSelectRight = new PageSelectRight();
|
||
PageChangeAnim(ModMain.frmSelectLeft, ModMain.frmSelectRight);
|
||
break;
|
||
}
|
||
case PageType.TaskManager: // 任务管理
|
||
{
|
||
if (ModMain.frmSpeedLeft is null)
|
||
ModMain.frmSpeedLeft = new PageSpeedLeft();
|
||
if (ModMain.frmSpeedRight is null)
|
||
ModMain.frmSpeedRight = new PageSpeedRight();
|
||
PageChangeAnim(ModMain.frmSpeedLeft, ModMain.frmSpeedRight);
|
||
break;
|
||
}
|
||
case PageType.InstanceSetup: // 实例设置
|
||
{
|
||
ModMain.frmInstanceLeft ??= new PageInstanceLeft();
|
||
subType = ModMain.frmInstanceLeft.pageID;
|
||
PageChangeAnim(ModMain.frmInstanceLeft, (FrameworkElement)ModMain.frmInstanceLeft.PageGet(subType));
|
||
break;
|
||
}
|
||
case PageType.CompDetail: // Mod 信息
|
||
{
|
||
if (ModMain.frmDownloadCompDetail is null)
|
||
ModMain.frmDownloadCompDetail = new PageDownloadCompDetail();
|
||
PageChangeAnim(new MyPageLeft(), ModMain.frmDownloadCompDetail);
|
||
break;
|
||
}
|
||
case PageType.VersionSaves: // 存档管理
|
||
{
|
||
if (ModMain.frmInstanceSavesLeft is null)
|
||
ModMain.frmInstanceSavesLeft = new PageInstanceSavesLeft();
|
||
PageInstanceSavesLeft.currentSave = stack.additional.Value.SavePath;
|
||
subType = ModMain.frmInstanceSavesLeft.pageID;
|
||
PageChangeAnim(ModMain.frmInstanceSavesLeft,
|
||
(FrameworkElement)ModMain.frmInstanceSavesLeft.PageGet(subType));
|
||
break;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 设置为最新状态
|
||
|
||
BtnExtraDownload.ShowRefresh();
|
||
BtnExtraApril.ShowRefresh();
|
||
|
||
#endregion
|
||
|
||
ModBase.Log("[Control] 切换主要页面:" + ModBase.GetStringFromEnum(stack) + ", " + (int)subType);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"切换主要页面失败(ID " + (int)pageCurrent.page + ")",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
finally
|
||
{
|
||
ModAnimation.AniControlEnabled -= 1;
|
||
}
|
||
}
|
||
|
||
private void PageChangeAnim(FrameworkElement targetLeft, FrameworkElement targetRight)
|
||
{
|
||
ModAnimation.AniStop("FrmMain LeftChange");
|
||
ModAnimation.AniStop("PageLeft PageChange"); // 停止左边栏变更导致的右页面切换动画,防止它与本动画一起触发多次 PageOnEnter
|
||
ModAnimation.AniControlEnabled += 1;
|
||
// 清除新页面关联性
|
||
if (targetLeft.Parent is not null)
|
||
targetLeft.SetValue(ContentPresenter.ContentProperty, null);
|
||
if (targetRight is not null && targetRight.Parent is not null)
|
||
targetRight.SetValue(ContentPresenter.ContentProperty, null);
|
||
pageLeft = (MyPageLeft)targetLeft;
|
||
pageRight = (MyPageRight)targetRight;
|
||
// 触发页面通用动画
|
||
((MyPageLeft)PanMainLeft.Child).TriggerHideAnimation();
|
||
((MyPageRight)PanMainRight.Child).PageOnExit();
|
||
ModAnimation.AniControlEnabled -= 1;
|
||
// 执行动画
|
||
ModAnimation.AniStart(new[]
|
||
{
|
||
ModAnimation.AaCode(() =>
|
||
{
|
||
ModAnimation.AniControlEnabled += 1;
|
||
// 把新页面添加进容器
|
||
PanMainLeft.Child = pageLeft;
|
||
pageLeft.Opacity = 0d;
|
||
PanMainLeft.Background = null;
|
||
ModAnimation.AniControlEnabled -= 1;
|
||
ModBase.RunInUi(() => PanMainLeft_Resize(PanMainLeft.ActualWidth), true);
|
||
}, 110),
|
||
ModAnimation.AaCode(() =>
|
||
{
|
||
// 延迟触发页面通用动画,以使得在 Loaded 事件中加载的控件得以处理
|
||
pageLeft.Opacity = 1d;
|
||
pageLeft.TriggerShowAnimation();
|
||
}, 30, true)
|
||
}, "FrmMain PageChangeLeft");
|
||
ModAnimation.AniStart(new[]
|
||
{
|
||
ModAnimation.AaCode(() =>
|
||
{
|
||
ModAnimation.AniControlEnabled += 1;
|
||
((MyPageRight)PanMainRight.Child).PageOnForceExit();
|
||
// 把新页面添加进容器
|
||
PanMainRight.Child = pageRight;
|
||
pageRight.Opacity = 0d;
|
||
PanMainRight.Background = null;
|
||
ModAnimation.AniControlEnabled -= 1;
|
||
ModBase.RunInUi(() => BtnExtraBack.ShowRefresh(), true);
|
||
}, 110),
|
||
ModAnimation.AaCode(() =>
|
||
{
|
||
// 延迟触发页面通用动画,以使得在 Loaded 事件中加载的控件得以处理
|
||
pageRight.Opacity = 1d;
|
||
pageRight.PageOnEnter();
|
||
}, 30, true)
|
||
}, "FrmMain PageChangeRight");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 退出子界面。
|
||
/// </summary>
|
||
private void PageChangeExit()
|
||
{
|
||
if (pageStack.Any())
|
||
{
|
||
// 子页面 → 主页面,退出
|
||
PanTitleMain.Visibility = Visibility.Visible;
|
||
PanTitleMain.IsHitTestVisible = true;
|
||
PanTitleInner.IsHitTestVisible = false;
|
||
ModAnimation.AniStart(
|
||
new[]
|
||
{
|
||
ModAnimation.AaOpacity(PanTitleInner, -PanTitleInner.Opacity, 150),
|
||
ModAnimation.AaX(PanTitleInner, -18 - PanTitleInner.Margin.Left, 150,
|
||
ease: new ModAnimation.AniEaseInFluent()),
|
||
ModAnimation.AaOpacity(PanTitleMain, 1d - PanTitleMain.Opacity, 150, 200),
|
||
ModAnimation.AaX(PanTitleMain, -PanTitleMain.Margin.Left, 350, 200,
|
||
new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)),
|
||
ModAnimation.AaCode(() => PanTitleInner.Visibility = Visibility.Collapsed, after: true)
|
||
}, "FrmMain Titlebar FirstLayer");
|
||
pageStack.Clear();
|
||
}
|
||
// 主页面 → 主页面,无事发生
|
||
}
|
||
|
||
// 左边栏改变
|
||
private void PanMainLeft_SizeChanged(object sender, SizeChangedEventArgs e)
|
||
{
|
||
if (!e.WidthChanged)
|
||
return;
|
||
PanMainLeft_Resize(e.NewSize.Width);
|
||
}
|
||
|
||
private void PanMainLeft_Resize(double newWidth)
|
||
{
|
||
var delta = newWidth - RectLeftBackground.Width;
|
||
if (Math.Abs(delta) > 0.1d && ModAnimation.AniControlEnabled == 0)
|
||
{
|
||
if (PanMain.Opacity < 0.1d)
|
||
PanMainLeft.IsHitTestVisible = false; // 避免左边栏指向背景未能完美覆盖左边栏
|
||
if (newWidth > 0d)
|
||
// 宽度足够,显示
|
||
ModAnimation.AniStart(
|
||
new[]
|
||
{
|
||
ModAnimation.AaWidth(RectLeftBackground, newWidth - RectLeftBackground.Width, 180,
|
||
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.ExtraStrong)),
|
||
ModAnimation.AaOpacity(RectLeftShadow, 1d - RectLeftShadow.Opacity, 180),
|
||
ModAnimation.AaCode(() => PanMainLeft.IsHitTestVisible = true, 150)
|
||
}, "FrmMain LeftChange", true);
|
||
else
|
||
// 宽度不足,隐藏
|
||
ModAnimation.AniStart(
|
||
new[]
|
||
{
|
||
ModAnimation.AaWidth(RectLeftBackground, -RectLeftBackground.Width, 180,
|
||
ease: new ModAnimation.AniEaseOutFluent()),
|
||
ModAnimation.AaOpacity(RectLeftShadow, -RectLeftShadow.Opacity, 180),
|
||
ModAnimation.AaCode(() => PanMainLeft.IsHitTestVisible = true, 150)
|
||
}, "FrmMain LeftChange", true);
|
||
}
|
||
else
|
||
{
|
||
RectLeftBackground.Width = newWidth;
|
||
PanMainLeft.IsHitTestVisible = true;
|
||
ModAnimation.AniStop("FrmMain LeftChange");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 控件拖动
|
||
|
||
// 在时钟中调用,使得即使鼠标在窗口外松开,也可以释放控件
|
||
public void DragTick()
|
||
{
|
||
if (ModMain.dragControl is null)
|
||
return;
|
||
if (!(Mouse.LeftButton == MouseButtonState.Pressed)) DragStop();
|
||
}
|
||
|
||
// 在鼠标移动时调用,以改变 Slider 位置
|
||
public void DragDoing()
|
||
{
|
||
if (ModMain.dragControl is null)
|
||
return;
|
||
if (Mouse.LeftButton == MouseButtonState.Pressed)
|
||
{
|
||
ModMain.dragControl.DragDoing();
|
||
}
|
||
else
|
||
DragStop();
|
||
}
|
||
|
||
private void PanBack_MouseMove(object sender, EventArgs e)
|
||
{
|
||
DragDoing();
|
||
}
|
||
|
||
public void DragStop()
|
||
{
|
||
// 存在其他线程调用的可能性,因此需要确保在 UI 线程运行
|
||
ModBase.RunInUi(() =>
|
||
{
|
||
if (ModMain.dragControl is null)
|
||
return;
|
||
var control = ModMain.dragControl;
|
||
ModMain.dragControl = null;
|
||
control.DragStop(); // 控件会在该事件中判断 DragControl,所以得放在后面
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 附加按钮
|
||
|
||
// 更新重启
|
||
private void BtnExtraUpdateRestart_Click(object sender, MouseButtonEventArgs e)
|
||
{
|
||
UpdateManager.UpdateRestart(true);
|
||
}
|
||
|
||
private bool BtnExtraUpdateRestart_ShowCheck()
|
||
{
|
||
return UpdateManager.isUpdateWaitingRestart;
|
||
}
|
||
|
||
// 音乐
|
||
private void BtnExtraMusic_Click(object sender, MouseButtonEventArgs e)
|
||
{
|
||
ModMusic.MusicControlPause();
|
||
}
|
||
|
||
private void BtnExtraMusic_RightClick(object sender, MouseButtonEventArgs e)
|
||
{
|
||
ModMusic.MusicControlNext();
|
||
}
|
||
|
||
// 任务管理
|
||
private void BtnExtraDownload_Click(object sender, MouseButtonEventArgs e)
|
||
{
|
||
PageChange(PageType.TaskManager);
|
||
}
|
||
|
||
private bool BtnExtraDownload_ShowCheck()
|
||
{
|
||
return ModNet.HasDownloadingTask() && !(pageCurrent == PageType.TaskManager);
|
||
}
|
||
|
||
// 投降
|
||
public void AprilGiveup()
|
||
{
|
||
if (ModMain.isAprilEnabled && !ModMain.isAprilGiveup)
|
||
{
|
||
HintService.Hint("=D", HintType.Success);
|
||
ModMain.isAprilGiveup = true;
|
||
ModMain.frmLaunchLeft.AprilScaleTrans.ScaleX = 1d;
|
||
ModMain.frmLaunchLeft.AprilScaleTrans.ScaleY = 1d;
|
||
BtnExtraApril.ShowRefresh();
|
||
}
|
||
}
|
||
|
||
private void BtnExtraApril_Click(object sender, MouseButtonEventArgs e)
|
||
{
|
||
AprilGiveup();
|
||
}
|
||
|
||
public bool BtnExtraApril_ShowCheck()
|
||
{
|
||
return ModMain.isAprilEnabled && !ModMain.isAprilGiveup && pageCurrent == PageType.Launch;
|
||
}
|
||
|
||
// 关闭 Minecraft
|
||
private void BtnExtraShutdown_Click(object sender, MouseButtonEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (ModLaunch.mcLaunchLoaderReal is not null)
|
||
ModLaunch.mcLaunchLoaderReal.Abort();
|
||
foreach (var Watcher in ModWatcher.mcWatcherList)
|
||
Watcher.Kill();
|
||
HintService.Hint(Lang.Text("Main.ShutdownMinecraft.Success"), HintType.Success);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ModBase.Log(
|
||
ex,
|
||
"强制关闭所有 Minecraft 失败",
|
||
ModBase.LogLevel.Feedback,
|
||
userSummary: Lang.Text("Main.Error.OperationFailed"));
|
||
}
|
||
}
|
||
|
||
public bool BtnExtraShutdown_ShowCheck()
|
||
{
|
||
return ModWatcher.hasRunningMinecraft;
|
||
}
|
||
|
||
// 游戏日志
|
||
private void BtnExtraLog_Click(object sender, MouseButtonEventArgs e)
|
||
{
|
||
PageChange(PageType.GameLog);
|
||
}
|
||
|
||
public bool BtnExtraLog_ShowCheck()
|
||
{
|
||
if (ModMain.frmLogLeft is null || ModMain.frmLogRight is null || pageCurrent == PageType.GameLog)
|
||
return false;
|
||
return ModMain.frmLogLeft.shownLogs.Count > 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 返回顶部。
|
||
/// </summary>
|
||
public void BackToTop()
|
||
{
|
||
var realScroll = BtnExtraBack_GetRealChild();
|
||
if (realScroll is not null)
|
||
realScroll.PerformVerticalOffsetDelta(-realScroll.VerticalOffset);
|
||
else
|
||
ModBase.Log(
|
||
"[UI] 无法返回顶部,未找到合适的 RealScroll",
|
||
ModBase.LogLevel.Hint,
|
||
userSummary: Lang.Text("Main.Error.ScrollToTopFailed"));
|
||
}
|
||
|
||
private void BtnExtraBack_Click(object sender, MouseButtonEventArgs e)
|
||
{
|
||
BackToTop();
|
||
}
|
||
|
||
private bool BtnExtraBack_ShowCheck()
|
||
{
|
||
var realScroll = BtnExtraBack_GetRealChild();
|
||
return realScroll is not null && realScroll.Visibility == Visibility.Visible &&
|
||
realScroll.VerticalOffset > Height + (BtnExtraBack.Show ? 0 : 700);
|
||
}
|
||
|
||
private MyScrollViewer? BtnExtraBack_GetRealChild()
|
||
{
|
||
if (PanMainRight.Child is null || !(PanMainRight.Child is MyPageRight))
|
||
return null;
|
||
return ((MyPageRight)PanMainRight.Child).PanScroll;
|
||
}
|
||
|
||
#endregion
|
||
}
|