Files

632 lines
27 KiB
C#
Raw Permalink Normal View History

using System.Collections;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.FileIO;
using PCL.Core.App;
using PCL.Core.App.Configuration;
using PCL.Core.App.Configuration.Storage;
using FileSystem = Microsoft.VisualBasic.FileIO.FileSystem;
using PCL.Core.App.Localization;
using PCL.Core.UI;
namespace PCL;
public partial class PageSelectRight
{
private const int normalDelay = 75; // 正常输入延迟0.075秒
private const int quickDelay = 50; // 清空搜索框延迟0.05秒
private bool isRefreshing;
private DateTime lastInputTime = DateTime.MinValue;
private DispatcherTimer reloadTimer;
// 窗口属性
/// <summary>
/// 是否显示隐藏的 Minecraft 实例。
/// </summary>
public bool showHidden = false;
public PageSelectRight()
{
InitializeComponent();
PanVerSearchBox.HintText = Lang.Text("Select.Instance.Search.Hint");
Loaded += PageSelectRight_Loaded;
Unloaded += PageSelectRight_Unloaded;
LoaderInit();
}
// 窗口基础
private void PageSelectRight_Loaded(object sender, RoutedEventArgs e)
{
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.RunOnUpdated, 1, @"versions\");
PanBack.ScrollToHome();
PanVerSearchBox.TextChanged += (a, b) => PanVerSearchBox_TextChanged(a, (TextChangedEventArgs)b);
reloadTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(normalDelay) };
reloadTimer.Tick += ReloadTimer_Tick;
}
private void PanVerSearchBox_TextChanged(object sender, TextChangedEventArgs e)
{
// 记录最后一次输入时间
lastInputTime = DateTime.Now;
isRefreshing = false;
// 动态调整延迟时间
if (string.IsNullOrWhiteSpace(PanVerSearchBox.Text))
{
if (reloadTimer.Interval.TotalMilliseconds != quickDelay)
reloadTimer.Interval = TimeSpan.FromMilliseconds(quickDelay);
}
else if (reloadTimer.Interval.TotalMilliseconds != normalDelay)
{
reloadTimer.Interval = TimeSpan.FromMilliseconds(normalDelay);
}
if (!reloadTimer.IsEnabled) reloadTimer.Start();
}
private void ReloadTimer_Tick(object sender, EventArgs e)
{
// 检查是否超过当前设定的延迟时间没有新输入
var elapsed = (DateTime.Now - lastInputTime).TotalMilliseconds;
var currentDelay = reloadTimer.Interval.TotalMilliseconds;
if (elapsed >= currentDelay && ModInstanceList.mcInstanceListLoader.State == ModBase.LoadState.Finished &&
!isRefreshing)
{
isRefreshing = true;
// 确保在UI线程执行刷新
Dispatcher.BeginInvoke(new Action(() =>
{
McInstanceListUI(ModInstanceList.mcInstanceListLoader);
isRefreshing = false;
}));
reloadTimer.Stop();
}
}
private void PageSelectRight_Unloaded(object sender, RoutedEventArgs e)
{
// 清理计时器
if (reloadTimer is not null)
{
reloadTimer.Stop();
reloadTimer.Tick -= ReloadTimer_Tick;
reloadTimer = null;
}
}
private void LoaderInit()
{
PageLoaderInit(Load, PanLoad, PanAllBack, null, ModInstanceList.mcInstanceListLoader,
a => this.McInstanceListUI((ModLoader.LoaderTask<string, int>)a),
autoRun: false);
}
private void Load_Click(object sender, MouseButtonEventArgs e)
{
if (ModInstanceList.mcInstanceListLoader.State == ModBase.LoadState.Failed)
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.ForceRun, 1, @"versions\");
}
#region 结果 UI
private void McInstanceListUI(ModLoader.LoaderTask<string, int> loader)
{
try
{
var path = loader.input;
// 加载 UI
PanMain.Children.Clear();
var hasVisibleFolders = false;
var searchText = PanVerSearchBox.Text.Trim().ToLower(); // 获取搜索框文本
var hasAnyResults = false;
var originalHasInstances = ModInstanceList.mcInstanceList.ToArray().Any(c => c.Value.Count > 0);
// 搜索无结果时显示 PanEmptySearch
PanEmptySearch.Visibility = Visibility.Collapsed; // 默认隐藏
foreach (var Card in ModInstanceList.mcInstanceList.ToArray())
{
if ((Card.Key == McInstanceCardType.Hidden) ^ showHidden)
continue;
var filteredInstances = Card.Value.Where(v =>
{
if (string.IsNullOrEmpty(searchText))
return true;
return v.Name.ToLower().Contains(searchText) ||
(v.Desc is not null && v.Desc.ToLower().Contains(searchText)) || v.GetDefaultDescription()
.Replace(",", "").ToLower().Trim().Contains(searchText);
}).ToList();
if (filteredInstances.Count == 0)
continue;
hasVisibleFolders = true;
hasAnyResults = true;
if (filteredInstances.Count == 0)
continue;
hasVisibleFolders = true;
#region 确认卡片名称
var cardName = "";
switch (Card.Key)
{
case McInstanceCardType.OriginalLike:
{
cardName = Lang.Text("Select.Instance.Card.Regular");
break;
}
case McInstanceCardType.API:
{
var isForgeExists = false;
var isNeoForgeExists = false;
var isFabricExists = false;
var isQuiltExists = false;
var isLiteExists = false;
var isCleanroomExists = false;
var isLabyModExists = false;
foreach (var instance in Card.Value)
{
if (!instance.IsLoaded)
instance.Load();
if (instance.Info.HasFabric)
isFabricExists = true;
if (instance.Info.HasQuilt)
isQuiltExists = true;
if (instance.Info.HasLiteLoader)
isLiteExists = true;
if (instance.Info.HasForge)
isForgeExists = true;
if (instance.Info.HasNeoForge)
isNeoForgeExists = true;
if (instance.Info.HasCleanroom)
isCleanroomExists = true;
if (instance.Info.HasLabyMod)
isLabyModExists = true;
}
if ((isLiteExists ? 1 : 0) + (isForgeExists ? 1 : 0) + (isFabricExists ? 1 : 0) +
(isNeoForgeExists ? 1 : 0) + (isQuiltExists ? 1 : 0) + (isCleanroomExists ? 1 : 0) +
(isLabyModExists ? 1 : 0) > 1)
cardName = Lang.Text("Select.Instance.Card.Modable");
else if (isForgeExists)
cardName = Lang.Text("Select.Instance.Card.Forge");
else if (isNeoForgeExists)
cardName = Lang.Text("Select.Instance.Card.NeoForge");
else if (isCleanroomExists)
cardName = Lang.Text("Select.Instance.Card.Cleanroom");
else if (isLabyModExists)
cardName = Lang.Text("Select.Instance.Card.LabyMod");
else if (isLiteExists)
cardName = Lang.Text("Select.Instance.Card.LiteLoader");
else if (isQuiltExists)
cardName = Lang.Text("Select.Instance.Card.Quilt");
else
cardName = Lang.Text("Select.Instance.Card.Fabric");
break;
}
case McInstanceCardType.Error:
{
cardName = Lang.Text("Select.Instance.Card.Error");
break;
}
case McInstanceCardType.Hidden:
{
cardName = Lang.Text("Select.Instance.Card.Hidden");
break;
}
case McInstanceCardType.Rubbish:
{
cardName = Lang.Text("Select.Instance.Card.LessUsed");
break;
}
case McInstanceCardType.Star:
{
cardName = Lang.Text("Select.Instance.Card.Favorites");
break;
}
case McInstanceCardType.Fool:
{
cardName = Lang.Text("Select.Instance.Card.AprilFools");
break;
}
default:
{
throw new ArgumentException($"未知的卡片种类({(int)Card.Key}");
}
}
#endregion
// 建立控件
var cardTitle = $"{cardName}{(Card.Key == McInstanceCardType.Star ? "" : $" ({Lang.Number(filteredInstances.Count, "N0")})")}";
var newCard = new MyCard { Title = cardTitle, Margin = new Thickness(0d, 0d, 0d, 15d) };
var newStack = new StackPanel
{
Margin = new Thickness(20d, MyCard.SwapedHeight, 18d, 0d),
VerticalAlignment = VerticalAlignment.Top, RenderTransform = new TranslateTransform(0d, 0d),
Tag = filteredInstances
};
newCard.Children.Add(newStack);
newCard.SwapControl = newStack;
PanMain.Children.Add(newCard);
// 确定卡片是否展开
void PutMethod(StackPanel stack)
{
foreach (var item in (IEnumerable)stack.Tag)
stack.Children.Add(McVersionListItem((McInstance)item));
}
;
if (Card.Key == McInstanceCardType.Rubbish ||
Card.Key == McInstanceCardType.Error ||
Card.Key == McInstanceCardType.Fool)
{
newCard.IsSwapped = true;
newCard.InstallMethod = PutMethod;
}
else
{
MyCard.StackInstall(ref newStack, PutMethod);
}
}
// 若只有一个卡片,则强制展开
if (PanMain.Children.Count == 1 && ((MyCard)PanMain.Children[0]).IsSwapped)
((MyCard)PanMain.Children[0]).IsSwapped = false;
PanVerSearchBox.Visibility = hasVisibleFolders ? Visibility.Visible : Visibility.Collapsed;
// 判断应该显示哪一个页面
if (!hasAnyResults)
{
if (!originalHasInstances)
{
// 完全没有实例的情况
PanEmpty.Visibility = Visibility.Visible;
PanBack.Visibility = Visibility.Collapsed;
if (showHidden)
{
LabEmptyTitle.Text = Lang.Text("Select.Instance.Hidden.EmptyTitle");
LabEmptyContent.Text = Lang.Text("Select.Instance.Hidden.EmptyMessage");
BtnEmptyDownload.Visibility = Visibility.Collapsed;
}
else
{
LabEmptyTitle.Text = Lang.Text("Select.Instance.Empty.Title");
LabEmptyContent.Text = Lang.Text("Select.Instance.Empty.Message");
BtnEmptyDownload.Visibility =
Config.Preference.Hide.PageDownload && !PageSetupUI.HiddenForceShow
? Visibility.Collapsed
: Visibility.Visible;
}
}
// 有实例但搜索无结果的情况
else if (showHidden && ModInstanceList.mcInstanceList.ToArray().Any(c =>
c.Key == McInstanceCardType.Hidden && c.Value.Count > 0))
{
// 有隐藏实例但搜索无结果 - 显示搜索无结果提示
PanVerSearchBox.Visibility = Visibility.Visible;
PanEmpty.Visibility = Visibility.Collapsed;
PanBack.Visibility = Visibility.Visible;
PanEmptySearch.Visibility = Visibility.Visible;
LabEmptySearchTitle.Text = Lang.Text("Select.Instance.Hidden.EmptySearchTitle");
LabEmptySearchContent.Text = string.IsNullOrWhiteSpace(searchText)
? Lang.Text("Select.Instance.Search.EmptyInput")
: Lang.Text("Select.Instance.Search.NoHiddenResult", searchText);
}
else if (showHidden)
{
// 无隐藏实例 - 显示"无隐藏实例"提示
PanEmpty.Visibility = Visibility.Visible;
PanBack.Visibility = Visibility.Collapsed;
LabEmptyTitle.Text = Lang.Text("Select.Instance.Hidden.EmptyTitle");
LabEmptyContent.Text = Lang.Text("Select.Instance.Hidden.EmptyMessage");
BtnEmptyDownload.Visibility = Visibility.Collapsed;
PanVerSearchBox.Visibility = Visibility.Collapsed;
}
else
{
// 普通模式下的搜索无结果
PanVerSearchBox.Visibility = Visibility.Visible;
PanEmpty.Visibility = Visibility.Collapsed;
PanBack.Visibility = Visibility.Visible;
PanEmptySearch.Visibility = Visibility.Visible;
LabEmptySearchTitle.Text = Lang.Text("Select.Instance.EmptySearch.Title");
LabEmptySearchContent.Text = string.IsNullOrWhiteSpace(searchText)
? Lang.Text("Select.Instance.Search.EmptyInput")
: Lang.Text("Select.Instance.Search.NoResult", searchText);
}
}
else
{
PanBack.Visibility = Visibility.Visible;
PanEmpty.Visibility = Visibility.Collapsed;
PanEmptySearch.Visibility = Visibility.Collapsed;
} // 有结果时隐藏
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Instance.Error.UiUpdate"),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Select.Instance.Error.UiUpdate"));
}
}
public static MyListItem McVersionListItem(McInstance mcInstance)
{
var newItem = new MyListItem
{
Title = mcInstance.Name, Info = mcInstance.Desc, Height = 42d, Tag = mcInstance, SnapsToDevicePixels = true,
Type = MyListItem.CheckType.Clickable
};
var instanceInfo = mcInstance.Info;
var tags = new List<string>();
tags.Add(instanceInfo.VanillaName);
if (instanceInfo.HasForge)
tags.Add("Forge " + instanceInfo.Forge);
else if (instanceInfo.HasNeoForge)
tags.Add("NeoForge " + instanceInfo.NeoForge);
else if (instanceInfo.HasCleanroom)
tags.Add("Cleanroom " + instanceInfo.Cleanroom);
else if (instanceInfo.HasLabyMod)
tags.Add("LabyMod " + instanceInfo.LabyMod);
else if (instanceInfo.HasQuilt)
tags.Add("Quilt " + instanceInfo.Quilt);
else if (instanceInfo.HasFabric) tags.Add("Fabric " + instanceInfo.Fabric);
if (instanceInfo.HasLiteLoader)
tags.Add("LiteLoader");
if (instanceInfo.HasOptiFine)
tags.Add("OptiFine " + instanceInfo.OptiFine);
newItem.Tags = tags;
try
{
if (mcInstance.Logo.EndsWith(@"PCL\Logo.png"))
newItem.Logo = mcInstance.PathInstance + @"PCL\Logo.png"; // 修复老版本中,存储的自定义 Logo 使用完整路径,导致移动后无法加载的 Bug
else
newItem.Logo = mcInstance.Logo;
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Instance.Error.IconLoad"),
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Select.Instance.Error.IconLoad"));
newItem.Logo = "pack://application:,,,/images/Blocks/RedstoneBlock.png";
}
newItem.ContentHandler = McVersionListContent;
return newItem;
}
private static void McVersionListContent(MyListItem sender, EventArgs e)
{
var version = (McInstance)sender.Tag;
// 注册点击事件
sender.Click += (a, b) => Item_Click((MyListItem)a, b);
// 图标按钮
var btnStar = new MyIconButton();
if (version.IsStar)
{
btnStar.ToolTip = Lang.Text("Select.Instance.Unfavorite");
ToolTipService.SetPlacement(btnStar, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btnStar, 30d);
ToolTipService.SetHorizontalOffset(btnStar, 2d);
btnStar.LogoScale = 1.1d;
btnStar.SvgIcon = "lucide/heart-filled";
}
else
{
btnStar.ToolTip = Lang.Text("Select.Instance.Favorite");
ToolTipService.SetPlacement(btnStar, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btnStar, 30d);
ToolTipService.SetHorizontalOffset(btnStar, 2d);
btnStar.LogoScale = 1.1d;
btnStar.SvgIcon = "lucide/heart";
}
btnStar.Click += (_, _) =>
{
States.Instance.Starred[version.PathInstance] = !version.IsStar;
ModInstanceList.mcInstanceListForceRefresh = true;
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.ForceRun, 1, @"versions\");
};
var btnOpenFolder = new MyIconButton { LogoScale = 1.1d, SvgIcon = "lucide/folder-open" };
btnOpenFolder.ToolTip = Lang.Text("Select.Instance.OpenFolder");
ToolTipService.SetPlacement(btnOpenFolder, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btnOpenFolder, 30d);
ToolTipService.SetHorizontalOffset(btnOpenFolder, 2d);
btnOpenFolder.Click += (_, _) => PageInstanceOverall.OpenVersionFolder(version);
var btnDel = new MyIconButton { LogoScale = 1.1d, SvgIcon = "lucide/trash-2" };
btnDel.ToolTip = Lang.Text("Common.Action.Delete");
ToolTipService.SetPlacement(btnDel, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btnDel, 30d);
ToolTipService.SetHorizontalOffset(btnDel, 2d);
btnDel.Click += (_, _) => DeleteVersion(sender, version);
if (version.state != McInstanceState.Error)
{
var btnCont = new MyIconButton { LogoScale = 1.1d, SvgIcon = "lucide/settings" };
btnCont.ToolTip = Lang.Text("Select.Instance.Settings");
ToolTipService.SetPlacement(btnCont, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btnCont, 30d);
ToolTipService.SetHorizontalOffset(btnCont, 2d);
btnCont.Click += (_, _) =>
{
PageInstanceLeft.McInstance = version;
ModMain.frmMain.PageChange(FormMain.PageType.InstanceSetup);
};
sender.MouseRightButtonUp += (_, _) =>
{
PageInstanceLeft.McInstance = version;
ModMain.frmMain.PageChange(FormMain.PageType.InstanceSetup);
};
sender.Buttons = new[] { btnStar, btnOpenFolder, btnDel, btnCont };
}
else
{
var btnCont = new MyIconButton { LogoScale = 1.15d, SvgIcon = "lucide/folder-open" };
btnCont.ToolTip = Lang.Text("Common.Action.OpenFolder");
ToolTipService.SetPlacement(btnCont, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btnCont, 30d);
ToolTipService.SetHorizontalOffset(btnCont, 2d);
btnCont.Click += (_, _) => PageInstanceOverall.OpenVersionFolder(version);
sender.MouseRightButtonUp += (_, _) => PageInstanceOverall.OpenVersionFolder(version);
sender.Buttons = new[] { btnStar, btnOpenFolder, btnDel, btnCont };
}
}
#endregion
#region 页面事件
// 点击选项
public static void Item_Click(MyListItem sender, EventArgs e)
{
var instance = (McInstance)sender.Tag;
if (new McInstance(instance.PathInstance).Check())
{
// 正常实例
ModInstanceList.McMcInstanceSelected = instance;
States.Game.SelectedInstance = ModInstanceList.McMcInstanceSelected.Name;
ModMain.frmMain.PageBack();
}
else
{
// 错误实例
PageInstanceOverall.OpenVersionFolder(instance);
}
}
private void BtnDownload_Click(object sender, MouseButtonEventArgs e)
{
ModMain.frmMain.PageChange(FormMain.PageType.Download, FormMain.PageSubType.DownloadInstall);
}
// 修改此代码时,同时修改 PageInstanceOverall 中的代码
public static void DeleteVersion(MyListItem item, McInstance mcInstance)
{
try
{
var isShiftPressed = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
var isHintIndie = mcInstance.state != McInstanceState.Error &&
(mcInstance.PathIndie ?? "") != (ModFolder.mcFolderSelected ?? "");
var confirmMsg = isShiftPressed
? Lang.Text("Select.Instance.Delete.ConfirmPermanentMessage", mcInstance.Name)
: Lang.Text("Select.Instance.Delete.ConfirmMessage", mcInstance.Name);
var confirmFullMsg = confirmMsg +
(isHintIndie ? "\r\n" + Lang.Text("Select.Instance.Delete.IsolatedWarning") : "");
switch (ModMain.MyMsgBox(confirmFullMsg, Lang.Text("Select.Instance.Delete.ConfirmTitle"),
button2: Lang.Text("Common.Action.Cancel"), isWarn: true))
{
case 1:
{
ModBase.IniClearCache(Path.Combine(mcInstance.PathIndie, "options.txt"));
((DynamicCacheConfigStorage)ConfigService.GetProvider(ConfigSource.GameInstance)).InvalidateCache(
mcInstance.PathInstance);
if (isShiftPressed)
{
ModBase.DeleteDirectory(mcInstance.PathInstance);
HintService.Hint(Lang.Text("Select.Instance.Delete.PermanentSuccess", mcInstance.Name),
HintType.Success);
}
else
{
FileSystem.DeleteDirectory(mcInstance.PathInstance, UIOption.AllDialogs,
RecycleOption.SendToRecycleBin);
HintService.Hint(Lang.Text("Select.Instance.Delete.RecycleBinSuccess", mcInstance.Name),
HintType.Success);
}
break;
}
case 2:
{
return;
}
}
// 从 UI 中移除
if (mcInstance.displayType == McInstanceCardType.Hidden || !mcInstance.IsStar)
{
// 仅出现在当前卡片
var parent = (StackPanel)item.Parent;
if (parent.Children.Count > 2) // 当前的项目与一个占位符
{
// 删除后还有剩
var card = (MyCard)parent.Parent;
card.Title = card.Title.Replace(Lang.Number(parent.Children.Count - 1, "N0"),
Lang.Number(parent.Children.Count - 2, "N0")); // 有一个占位符
parent.Children.Remove(item);
if (ModInstanceList.McMcInstanceSelected is not null && (mcInstance.PathInstance ?? "") ==
(ModInstanceList.McMcInstanceSelected.PathInstance ?? ""))
// 删除当前实例就更改选择
ModInstanceList.McMcInstanceSelected = (McInstance)((MyListItem)parent.Children[0]).Tag;
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.UpdateOnly, 1, @"versions\");
}
else
{
// 删除后没剩了
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.ForceRun, 1, @"versions\");
}
}
else
{
// 同时出现在当前卡片与收藏夹
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.ForceRun, 1, @"versions\");
}
}
catch (OperationCanceledException ex)
{
ModBase.Log(ex, $"删除实例 {mcInstance.Name} 被主动取消");
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Instance.Error.Delete", mcInstance.Name),
ModBase.LogLevel.Msgbox,
userSummary: Lang.Text("Select.Instance.Error.Delete", mcInstance.Name));
}
}
public void BtnEmptyDownload_Loaded()
{
var newVisibility = (Config.Preference.Hide.PageDownload && !PageSetupUI.HiddenForceShow) || showHidden
? Visibility.Collapsed
: Visibility.Visible;
if (BtnEmptyDownload.Visibility != newVisibility)
{
BtnEmptyDownload.Visibility = newVisibility;
PanLoad.TriggerForceResize();
}
}
#endregion
}