Files

901 lines
36 KiB
C#
Raw Permalink Normal View History

using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using PCL.Core.App;
using PCL.Core.App.Localization;
using PCL.Core.Logging;
using PCL.Core.UI;
using PCL.Core.Utils.Validate;
using PCL.Network;
namespace PCL;
public partial class PageSelectLeft : IRefreshable
{
private bool isFirstLoad = true;
private List<ModFolder.McFolder> mcFolderListLast;
public PageSelectLeft()
{
Initialized += PageSelectLeft_Initialized;
Loaded += PageSelectLeft_Loaded;
InitializeComponent();
}
void IRefreshable.Refresh()
{
RefreshCurrent();
}
private void PageSelectLeft_Initialized(object sender, EventArgs e)
{
ModFolder.mcFolderListLoader.PreviewFinish += _ =>
{
if (ModMain.frmSelectLeft is not null) ModBase.RunInUiWait(McFolderListUI);
};
}
private void PageSelectLeft_Loaded(object sender, RoutedEventArgs e)
{
if (isFirstLoad)
McFolderListUI(); // 若已经执行完成,触发首次加载
isFirstLoad = false;
}
private void McFolderListUI()
{
try
{
// 确认数据有变化
if (mcFolderListLast is not null && mcFolderListLast.SequenceEqual(ModFolder.mcFolderList))
return;
mcFolderListLast = new List<ModFolder.McFolder>(ModFolder.mcFolderList);
// 创建 UI
ModMain.frmSelectLeft.PanList.Children.Clear();
// 文件夹列表标题
ModMain.frmSelectLeft.PanList.Children.Add(new TextBlock
{
Text = Lang.Text("Select.Folder.ListTitle"),
Margin = new Thickness(13, 18, 5, 4),
Opacity = 0.6,
FontSize = 12
});
for (var i = 0; i < ModFolder.mcFolderList.Count; i++)
{
var folder = ModFolder.mcFolderList[i];
// 创建 ContextMenu
var contMenu = new ContextMenu();
// 添加菜单项
void AddMenuItem(string name, string header, string svgIcon = null, Thickness? padding = null,
RoutedEventHandler clickHandler = null)
{
var item = new MyMenuItem
{
Name = name,
Header = header,
SvgIcon = svgIcon,
Padding = padding ?? new Thickness(0)
};
if (clickHandler is not null)
item.Click += clickHandler;
contMenu.Items.Add(item);
}
const string iconRestore = "lucide/rotate-ccw";
const string iconRename = "lucide/pencil";
const string iconMoveup = "lucide/move-up";
const string iconMovedown = "lucide/move-down";
const string iconOpen = "lucide/folder-open";
const string iconRefresh = "lucide/refresh-cw";
const string iconDelete = "lucide/trash-2";
const string iconRemove = "lucide/list-x";
switch (folder.type)
{
case ModFolder.McFolder.Types.Original:
AddMenuItem("Rename", Lang.Text("Select.Folder.Rename"), iconRename, new Thickness(0, 2, 0, 0),
ModMain.frmSelectLeft.Rename_Click);
AddMenuItem("MoveUp", Lang.Text("Select.Folder.MoveUp"), iconMoveup, null,
ModMain.frmSelectLeft.MoveUp_Click);
AddMenuItem("MoveDown", Lang.Text("Select.Folder.MoveDown"), iconMovedown, null,
ModMain.frmSelectLeft.MoveDown_Click);
AddMenuItem("Open", Lang.Text("Common.Action.Open"), iconOpen, null,
ModMain.frmSelectLeft.Open_Click);
AddMenuItem("Refresh", Lang.Text("Common.Action.Refresh"), iconRefresh, null,
ModMain.frmSelectLeft.Refresh_Click);
AddMenuItem("Delete",
ModFolder.mcFolderList.Count == 1 &&
folder.Location == Path.Combine(ModBase.exePath, ".minecraft") + @"\"
? Lang.Text("Select.Folder.Clear")
: Lang.Text("Common.Action.Delete"), iconDelete, new Thickness(0, 0, 0, 2),
ModMain.frmSelectLeft.Delete_Click);
break;
case ModFolder.McFolder.Types.RenamedOriginal:
AddMenuItem("Restore", Lang.Text("Select.Folder.RestoreName"), iconRestore,
new Thickness(0, 2, 0, 0),
ModMain.frmSelectLeft.Restore_Click);
AddMenuItem("Rename", Lang.Text("Select.Folder.Rename"), iconRename, null,
ModMain.frmSelectLeft.Rename_Click);
AddMenuItem("MoveUp", Lang.Text("Select.Folder.MoveUp"), iconMoveup, null,
ModMain.frmSelectLeft.MoveUp_Click);
AddMenuItem("MoveDown", Lang.Text("Select.Folder.MoveDown"), iconMovedown, null,
ModMain.frmSelectLeft.MoveDown_Click);
AddMenuItem("Open", Lang.Text("Common.Action.Open"), iconOpen, null,
ModMain.frmSelectLeft.Open_Click);
AddMenuItem("Refresh", Lang.Text("Common.Action.Refresh"), iconRefresh, null,
ModMain.frmSelectLeft.Refresh_Click);
AddMenuItem("Delete", Lang.Text("Common.Action.Delete"), iconDelete, new Thickness(0, 0, 0, 2),
ModMain.frmSelectLeft.Delete_Click);
break;
case ModFolder.McFolder.Types.Custom:
AddMenuItem("Rename", Lang.Text("Select.Folder.Rename"), iconRename, new Thickness(0, 2, 0, 0),
ModMain.frmSelectLeft.Rename_Click);
AddMenuItem("MoveUp", Lang.Text("Select.Folder.MoveUp"), iconMoveup, null,
ModMain.frmSelectLeft.MoveUp_Click);
AddMenuItem("MoveDown", Lang.Text("Select.Folder.MoveDown"), iconMovedown, null,
ModMain.frmSelectLeft.MoveDown_Click);
AddMenuItem("Open", Lang.Text("Common.Action.Open"), iconOpen, null,
ModMain.frmSelectLeft.Open_Click);
AddMenuItem("Refresh", Lang.Text("Common.Action.Refresh"), iconRefresh, null,
ModMain.frmSelectLeft.Refresh_Click);
AddMenuItem("Remove", Lang.Text("Select.Folder.RemoveFromList"), iconRemove,
null, ModMain.frmSelectLeft.Remove_Click);
AddMenuItem("Delete", Lang.Text("Common.Action.Delete"), iconDelete, new Thickness(0, 0, 0, 2),
ModMain.frmSelectLeft.Delete_Click);
break;
}
// 控制上移下移显示
var moveUpItem = contMenu.Items.OfType<MyMenuItem>().FirstOrDefault(x => x.Name == "MoveUp");
var moveDownItem = contMenu.Items.OfType<MyMenuItem>().FirstOrDefault(x => x.Name == "MoveDown");
// 如果是第一个项目,隐藏上移按钮
if (i == 0) moveUpItem.Visibility = Visibility.Collapsed;
// 如果是最后一个项目,隐藏下移按钮
if (i == ModFolder.mcFolderList.Count - 1) moveDownItem.Visibility = Visibility.Collapsed;
// 构建列表项
var newItem = new MyListItem
{
IsScaleAnimationEnabled = false,
Type = MyListItem.CheckType.RadioBox,
MinPaddingRight = 30,
Title = folder.Name,
Info = folder.Location,
Height = 40,
ContextMenu = contMenu,
Tag = folder
};
newItem.Changed += (a, b) => ModMain.frmSelectLeft.Folder_Change((MyListItem)a, b);
// 拖拽
newItem.AllowDrop = true;
newItem.MouseMove += ModMain.frmSelectLeft.Item_MouseMove;
newItem.DragEnter += ModMain.frmSelectLeft.Item_DragEnter;
newItem.DragOver += ModMain.frmSelectLeft.Item_DragOver;
newItem.DragLeave += ModMain.frmSelectLeft.Item_DragLeave;
newItem.Drop += ModMain.frmSelectLeft.Item_Drop;
// 图标按钮
var newIconButton = new MyIconButton
{
SvgIcon = "lucide/settings",
LogoScale = 1.1
};
newIconButton.Click += (_, _) =>
{
contMenu.PlacementTarget = newItem;
contMenu.IsOpen = true;
};
newItem.Buttons = [newIconButton];
ModMain.frmSelectLeft.PanList.Children.Add(newItem);
LogWrapper.Info($"[Minecraft] 有效的 Minecraft 文件夹:{folder.Name} > {folder.Location}");
}
// 标题文本
ModMain.frmSelectLeft.PanList.Children.Add(new TextBlock
{
Text = Lang.Text("Select.Folder.AddOrImport"),
Margin = new Thickness(13, 18, 5, 4),
Opacity = 0.6,
FontSize = 12
});
// 创建新文件夹按钮
if (!Directory.Exists(Path.Combine(ModBase.exePath, ".minecraft")))
{
var itemCreate = new MyListItem
{
IsScaleAnimationEnabled = false,
Type = MyListItem.CheckType.Clickable,
Title = Lang.Text("Select.Folder.CreateNew.Title"),
Height = 34,
ToolTip = Lang.Text("Select.Folder.CreateNew.ToolTip"),
LogoScale = 0.9,
SvgIcon = "lucide/folder-plus"
};
ToolTipService.SetPlacement(itemCreate, PlacementMode.Right);
itemCreate.Click += (_, _) => ModMain.frmSelectLeft.Create_Click();
ModMain.frmSelectLeft.PanList.Children.Add(itemCreate);
}
// 添加按钮
var itemAdd = new MyListItem
{
IsScaleAnimationEnabled = false,
Type = MyListItem.CheckType.Clickable,
Title = Lang.Text("Select.Folder.AddExisting.Title"),
Height = 34,
ToolTip = Lang.Text("Select.Folder.AddExisting.ToolTip"),
SvgIcon = "lucide/folder-input"
};
ToolTipService.SetPlacement(itemAdd, PlacementMode.Right);
itemAdd.Click += (_, _) => ModMain.frmSelectLeft.Add_Click();
ModMain.frmSelectLeft.PanList.Children.Add(itemAdd);
// 导入整合包
var itemInstall = new MyListItem
{
IsScaleAnimationEnabled = false,
Type = MyListItem.CheckType.Clickable,
Title = Lang.Text("Select.Folder.ImportModpack.Title"),
Height = 34,
ToolTip = Lang.Text("Select.Folder.ImportModpack.ToolTip"),
SvgIcon = "lucide/package-plus"
};
ToolTipService.SetPlacement(itemInstall, PlacementMode.Right);
itemInstall.Click += (_, _) => ModModpack.ModpackInstall();
ModMain.frmSelectLeft.PanList.Children.Add(itemInstall);
// 边距
ModMain.frmSelectLeft.PanList.Children.Add(new FrameworkElement { Height = 10, IsHitTestVisible = false });
// 确认勾选状态
for (var i = 0; i < ModFolder.mcFolderList.Count; i++)
if (ModFolder.mcFolderList[i].Location == ModFolder.mcFolderSelected)
{
((MyListItem)ModMain.frmSelectLeft.PanList.Children[i + 1]).Checked = true; //去掉第一个标题
return;
}
if (ModFolder.mcFolderList.Count == 0)
throw new ArgumentNullException("没有可用的 Minecraft 文件夹");
States.Game.SelectedFolder = ModFolder.mcFolderList[0].Location.Replace(ModBase.exePath, "$");
((MyListItem)ModMain.frmSelectLeft.PanList.Children[1]).Checked = true;
}
catch (Exception ex)
{
LogWrapper.Error(ex, "构建 Minecraft 文件夹列表 UI 出错");
}
finally
{
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader,
ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.RunOnUpdated,
1,
"versions\\");
}
}
private void MoveUp_Click(object sender, RoutedEventArgs e)
{
var folder =
(ModFolder.McFolder)((MyListItem)((Popup)((ContextMenu)((MyMenuItem)sender).Parent).Parent)
.PlacementTarget)
.Tag;
var index = ModFolder.mcFolderList.IndexOf(folder);
if (index <= 0) return;
ModFolder.mcFolderList.RemoveAt(index);
ModFolder.mcFolderList.Insert(index - 1, folder);
UpdateFolderOrder();
}
private void MoveDown_Click(object sender, RoutedEventArgs e)
{
var folder =
(ModFolder.McFolder)((MyListItem)((Popup)((ContextMenu)((MyMenuItem)sender).Parent).Parent)
.PlacementTarget)
.Tag;
var index = ModFolder.mcFolderList.IndexOf(folder);
if (index >= ModFolder.mcFolderList.Count - 1) return;
ModFolder.mcFolderList.RemoveAt(index);
ModFolder.mcFolderList.Insert(index + 1, folder);
UpdateFolderOrder();
}
private void UpdateFolderOrder()
{
States.Game.Folders = ModFolder.mcFolderList
.Select(folder => $"{folder.Name}>{folder.Location}")
.ToArray()
.Join("|");
McFolderListUI();
}
private void Restore_Click(object sender, RoutedEventArgs e)
{
var folder =
(ModFolder.McFolder)((MyListItem)((Popup)((ContextMenu)((MyMenuItem)sender).Parent).Parent)
.PlacementTarget)
.Tag;
var index = ModFolder.mcFolderList.IndexOf(folder);
ModFolder.mcFolderList[index].type = ModFolder.McFolder.Types.Original;
ModFolder.mcFolderList[index].Name = Lang.Text("Select.Folder.OfficialLauncherFolder");
UpdateFolderOrder();
}
// 添加文件夹
private void Add_Click()
{
var newFolder = "";
// 检查是否有下载任务
if (ModNet.HasDownloadingTask())
{
HintService.Hint(Lang.Text("Select.Folder.CannotAddWhileDownloading"), HintType.Error);
return;
}
try
{
// 获取输入
newFolder = SystemDialogs.SelectFolder();
if (string.IsNullOrEmpty(newFolder))
return;
if (newFolder.Contains('!') || newFolder.Contains(';'))
{
HintService.Hint(Lang.Text("Select.Folder.InvalidPathChars"), HintType.Error);
return;
}
// 要求输入显示名称
var splitedNames = newFolder.TrimEnd('\\').Split(@"\");
var defaultName = splitedNames.Last() == ".minecraft"
? splitedNames.Length >= 3 ? splitedNames[^2] : ""
: splitedNames.Last();
if (defaultName.Length > 40)
defaultName = defaultName[..39];
var newName = ModMain.MyMsgBoxInput(Lang.Text("Select.Folder.InputDisplayName.Title"),
Lang.Text("Select.Folder.InputDisplayName.Message"), defaultName,
[new NullOrWhiteSpaceValidator(), new StringLengthValidator(), new BlacklistValidator([">", "|"])]);
if (string.IsNullOrWhiteSpace(newName))
return;
// 添加文件夹
AddFolder(newFolder, newName, true);
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Folder.Error.Add", newFolder),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Select.Folder.Error.Add", newFolder));
}
}
/// <summary>
/// 将指定文件夹添加到 Minecraft 文件夹列表,并选中它。
/// </summary>
public static void AddFolder(string folderPath, string displayName, bool showHint)
{
// 检查文件夹权限
// 检查实际的 Minecraft 文件夹位置(没有问题,或是在子文件夹中)
// 判断是否已经添加过,若添加过则直接修改自定义名
// 如果没有添加过,则添加进去
// 保存
// 切换选择并更新列表
// 提示
// 检查是否为根目录整合包,自动关闭版本隔离
// 1. 根目录中存在数个 Mod
// 2. 实例数较少,可能为整合包
// 3. 能够找到可安装 Mod 的实例
// 4. 该实例的隔离文件夹下不存在 mods
// 满足以上全部条件则视为根目录整合包
ModBase.RunInThread(() =>
{
try
{
if (!folderPath.EndsWith(@"\")) folderPath += @"\";
if (!ModBase.CheckPermission(folderPath))
{
if (!showHint) throw new Exception("PCL 没有访问文件夹的权限:" + folderPath);
HintService.Hint(Lang.Text("Select.Folder.AccessDenied"), HintType.Error);
return;
}
if (!ModBase.CheckPermission(folderPath + @"versions\"))
foreach (var Folder in new DirectoryInfo(folderPath).GetDirectories())
if (ModBase.CheckPermission(Path.Combine(Folder.FullName, "versions")))
{
folderPath = Folder.FullName + @"\";
break;
}
var folders = new List<string>(States.Game.Folders.Split("|"));
var isAdded = false;
var isReplace = false;
for (int i = 0, loopTo = folders.Count - 1; i <= loopTo; i++)
{
var folder = folders[i];
if (string.IsNullOrEmpty(folder)) continue;
if (folder.Split(">")[1] != (folderPath ?? "")) continue;
isAdded = true;
if (folder.Split(">")[0] == displayName)
{
if (showHint) HintService.Hint(Lang.Text("Select.Folder.AlreadyInList"));
return;
}
folders[i] = $"{displayName}>{folderPath}";
isReplace = true;
if (showHint)
HintService.Hint(Lang.Text("Select.Folder.NameUpdated", displayName), HintType.Success);
break;
}
if (!isAdded) folders.Add($"{displayName}>{folderPath}");
States.Game.Folders = folders.ToArray().Join("|");
States.Game.SelectedFolder = folderPath.Replace(ModBase.exePath, "$");
ModFolder.mcFolderListLoader.Start(isForceRestart: true);
if (isReplace) return;
if (showHint) HintService.Hint(Lang.Text("Select.Folder.Added", displayName), HintType.Success);
var modFolder = new DirectoryInfo(folderPath + @"mods\");
if (!(modFolder.Exists && modFolder.EnumerateFiles().Count() >= 3)) return;
var versionFolder = new DirectoryInfo(folderPath + @"versions\");
if (!(versionFolder.Exists && versionFolder.EnumerateDirectories().Count() <= 3)) return;
foreach (var VersionPath in versionFolder.EnumerateDirectories())
{
var version = new McInstance(VersionPath.FullName);
version.Load();
if (!version.Modable) continue;
var modIndieFolder = new DirectoryInfo(version.PathInstance + @"mods\");
if (modIndieFolder.Exists && modIndieFolder.EnumerateFiles().Any()) return;
Config.Instance.IndieV1[version.PathInstance] = 2;
Config.Instance.IndieV2[version.PathInstance] = false;
ModBase.Log("[Setup] 已自动关闭单版本隔离:" + version.Name, ModBase.LogLevel.Debug);
}
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Folder.Error.AddNew"),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Select.Folder.Error.AddNew"));
}
}); // 加上斜杠……
}
// 创建文件夹
public void Create_Click()
{
// 检查是否有下载任务
if (ModNet.HasDownloadingTask())
{
HintService.Hint(Lang.Text("Select.Folder.CannotCreateWhileDownloading"), HintType.Error);
return;
}
if (!Directory.Exists(ModBase.exePath + @".minecraft\"))
{
Directory.CreateDirectory(ModBase.exePath + @".minecraft\");
Directory.CreateDirectory(ModBase.exePath + @".minecraft\versions\");
States.Game.SelectedFolder = @"$.minecraft\";
ModFolder.McFolderLauncherProfilesJsonCreate(ModBase.exePath + @".minecraft\");
HintService.Hint(Lang.Text("Select.Folder.CreateSuccess"), HintType.Success);
}
ModFolder.mcFolderListLoader.Start(isForceRestart: true);
}
// 右键菜单
public void Remove_Click(object sender, RoutedEventArgs e)
{
try
{
var folder =
(ModFolder.McFolder)((MyListItem)((Popup)((ContextMenu)((MyMenuItem)sender).Parent).Parent)
.PlacementTarget).Tag;
switch (ModMain.MyMsgBox(
Lang.Text("Select.Folder.Cleanup.Message"),
Lang.Text("Select.Folder.Cleanup.Title"), Lang.Text("Common.Action.Delete"),
Lang.Text("Select.Folder.Cleanup.Keep"), Lang.Text("Common.Action.Cancel")))
{
case 1:
{
// 删除配置文件
if (File.Exists(folder.Location + "PCL.ini"))
File.Delete(folder.Location + "PCL.ini");
if (Directory.Exists(folder.Location + @"versions\"))
foreach (var Version in new DirectoryInfo(folder.Location + @"versions\")
.EnumerateDirectories())
if (Directory.Exists(Path.Combine(Version.FullName, "PCL")))
Directory.Delete(Path.Combine(Version.FullName, "PCL"), true);
break;
}
case 2:
{
break;
}
// 不删除
case 3:
{
// 取消
return;
}
}
// 若修改了本部分代码,应对应修改 Delete_Click 中的代码
// 获取并删除列表项
var folders = new List<string>(States.Game.Folders.Split("|"));
var name = "";
for (int i = 0, loopTo = folders.Count - 1; i <= loopTo; i++)
{
if (string.IsNullOrEmpty(folders[i]))
break;
if (!folders[i].EndsWith(folder.Location)) continue;
name = folders[i].BeforeFirst(">");
folders.RemoveAt(i);
break;
}
// 保存
States.Game.Folders = folders.Count == 0 ? "" : folders.ToArray().Join("|");
HintService.Hint(
folder.type == ModFolder.McFolder.Types.Custom
? Lang.Text("Select.Folder.RemoveSuccess", name)
: Lang.Text("Select.Folder.RestoreSuccess"),
HintType.Success);
ModFolder.mcFolderListLoader.Start(isForceRestart: true);
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Folder.Error.Remove"),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Select.Folder.Error.Remove"));
}
}
public void Delete_Click(object sender, RoutedEventArgs e)
{
var menuItem = (MyMenuItem)sender;
var contextMenu = (ContextMenu)menuItem.Parent;
var popup = (Popup)contextMenu.Parent;
var listItem = (MyListItem)popup.PlacementTarget;
var folder = (ModFolder.McFolder)listItem.Tag;
var isClearing =
folder.type is ModFolder.McFolder.Types.Original or ModFolder.McFolder.Types.RenamedOriginal
&& folder.Location == ModBase.exePath + @".minecraft\"
&& ModFolder.mcFolderList.Count == 1;
var deleteText = Lang.Text(isClearing ? "Select.Folder.Clear" : "Common.Action.Delete");
var firstWarning =
Lang.Text(isClearing ? "Select.Folder.Clear.FirstWarning" : "Select.Folder.Delete.FirstWarning",
folder.Location);
var finalWarning =
Lang.Text(isClearing ? "Select.Folder.Clear.FinalWarning" : "Select.Folder.Delete.FinalWarning",
folder.Location);
var confirmTitle = Lang.Text(isClearing ? "Select.Folder.Clear.Confirm" : "Select.Folder.Delete.Confirm");
var inProgress = Lang.Text(isClearing ? "Select.Folder.Clear.InProgress" : "Select.Folder.Delete.InProgress",
folder.Name);
var success = Lang.Text(isClearing ? "Select.Folder.Clear.Success" : "Select.Folder.Delete.Success",
folder.Name);
if (ModMain.MyMsgBox(firstWarning, Lang.Text("Select.Folder.Delete.WarningTitle"),
Lang.Text("Common.Action.Cancel"), Lang.Text("Common.Action.Confirm"),
Lang.Text("Common.Action.Cancel")) != 2)
return;
if (ModMain.MyMsgBox(finalWarning, Lang.Text("Select.Folder.Delete.WarningTitle"),
confirmTitle, Lang.Text("Common.Action.Cancel"),
isWarn: true) != 1)
return;
var folders = States.Game.Folders.Split('|', StringSplitOptions.RemoveEmptyEntries).ToList();
var index = folders.FindIndex(f => f.EndsWith(folder.Location));
if (index >= 0)
folders.RemoveAt(index);
States.Game.Folders = string.Join("|", folders);
ModBase.RunInNewThread(() =>
{
try
{
HintService.Hint(inProgress);
ModBase.DeleteDirectory(folder.Location);
if (isClearing)
Directory.CreateDirectory(folder.Location);
HintService.Hint(success, HintType.Success);
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Folder.Error.Operate", deleteText, folder.Name),
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Select.Folder.Error.Operate", deleteText, folder.Name));
}
finally
{
ModFolder.mcFolderListLoader.Start(isForceRestart: true);
}
}, "Folder Delete " + ModBase.GetUuid(), ThreadPriority.BelowNormal);
}
public void Open_Click(object sender, RoutedEventArgs e)
{
ModBase.OpenExplorer(((MyListItem)((Popup)((ContextMenu)((MyMenuItem)sender).Parent).Parent).PlacementTarget)
.Info);
}
public void Refresh_Click(object sender, RoutedEventArgs e)
{
var data = (ModFolder.McFolder)((MyListItem)((Popup)((ContextMenu)((MyMenuItem)sender).Parent).Parent)
.PlacementTarget).Tag;
RefreshCurrent(data.Location);
}
public void RefreshCurrent()
{
RefreshCurrent(ModFolder.mcFolderSelected);
}
public static void RefreshCurrent(string folder)
{
ModBase.WriteIni(Path.Combine(folder, "PCL.ini"), "InstanceCache", "");
if (folder == ModFolder.mcFolderSelected)
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.ForceRun, 1, @"versions\");
}
public void Rename_Click(object sender, RoutedEventArgs e)
{
var folder =
(ModFolder.McFolder)((MyListItem)((Popup)((ContextMenu)((MyMenuItem)sender).Parent).Parent)
.PlacementTarget)
.Tag;
try
{
// 获取输入
var newName = ModMain.MyMsgBoxInput(Lang.Text("Select.Folder.Rename.Title"), "", folder.Name,
[
new NullOrWhiteSpaceValidator(), new StringLengthValidator(1, 30),
new BlacklistValidator([">", "|"])
]);
if (string.IsNullOrWhiteSpace(newName))
return;
// 修改自定义名
var folders = new List<string>(States.Game.Folders.Split("|"));
var isAdded = false;
for (int i = 0, loopTo = folders.Count - 1; i <= loopTo; i++)
{
var folderCurrent = folders[i];
if (string.IsNullOrEmpty(folderCurrent))
continue;
if (folderCurrent.Split(">")[1] != (folder.Location ?? "")) continue;
isAdded = true;
if (folderCurrent.Split(">")[0] == newName)
// 名称未修改
return;
folders[i] = $"{newName}>{folder.Location}";
break;
}
// 如果没有添加过,则添加进去(因为修改了默认项的名称)
if (!isAdded)
folders.Add($"{newName}>{folder.Location}");
HintService.Hint(Lang.Text("Select.Folder.NameUpdated", newName), HintType.Success);
// 保存
States.Game.Folders = folders.ToArray().Join("|");
ModFolder.mcFolderListLoader.Start(isForceRestart: true);
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Folder.Error.Rename"),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Select.Folder.Error.Rename"));
}
}
// 点击选项
public void Folder_Change(MyListItem sender, ModBase.RouteEventArgs e)
{
if (!e.raiseByMouse || !sender.Checked)
return;
// 检查是否有下载任务
if (ModNet.HasDownloadingTask(true))
{
HintService.Hint(Lang.Text("Select.Folder.SwitchBlockedByDownload"), HintType.Error);
e.handled = true;
return;
}
// 更换
States.Game.SelectedFolder = ((ModFolder.McFolder)sender.Tag).Location.Replace(ModBase.exePath, "$");
ModFolder.mcFolderListLoader.Start(isForceRestart: true);
ModLoader.LoaderFolderRun(ModInstanceList.mcInstanceListLoader, ModFolder.mcFolderSelected,
ModLoader.LoaderFolderRunType.RunOnUpdated, 1, @"versions\"); // 刷新实例列表
}
#region 拖拽排序功能
// 拖拽开始时的鼠标移动处理
private void Item_MouseMove(object sender, MouseEventArgs e)
{
var item = (MyListItem)sender;
// 当按住鼠标左键时开始拖拽操作
if (e.LeftButton != MouseButtonState.Pressed) return;
try
{
DragDrop.DoDragDrop(item, item.Tag, DragDropEffects.Move);
}
catch (Exception ex)
{
ModBase.Log(ex, "开始拖拽操作失败");
}
}
// 拖拽进入时的处理
private void Item_DragEnter(object sender, DragEventArgs e)
{
try
{
if (e.Data.GetDataPresent(typeof(ModFolder.McFolder)))
{
e.Effects = DragDropEffects.Move;
// 添加视觉反馈
var item = (MyListItem)sender;
item.Opacity = 0.7d;
}
else
{
e.Effects = DragDropEffects.None;
}
}
catch (Exception)
{
e.Effects = DragDropEffects.None;
}
e.Handled = true;
}
// 拖拽悬停时的处理
private void Item_DragOver(object sender, DragEventArgs e)
{
try
{
e.Effects = e.Data.GetDataPresent(typeof(ModFolder.McFolder))
? DragDropEffects.Move
: DragDropEffects.None;
}
catch (Exception)
{
e.Effects = DragDropEffects.None;
}
e.Handled = true;
}
// 拖拽离开时的处理
private void Item_DragLeave(object sender, DragEventArgs e)
{
try
{
// 恢复视觉状态
var item = (MyListItem)sender;
item.Opacity = 1.0d;
}
catch (Exception ex)
{
ModBase.Log(ex, "拖拽离开处理失败");
}
e.Handled = true;
}
// 拖拽放下时的处理
private void Item_Drop(object sender, DragEventArgs e)
{
try
{
var targetItem = (MyListItem)sender;
var targetFolder = (ModFolder.McFolder)targetItem.Tag;
// 恢复视觉状态
targetItem.Opacity = 1.0d;
// 检查数据有效性
if (!e.Data.GetDataPresent(typeof(ModFolder.McFolder)))
{
e.Handled = true;
return;
}
var sourceFolder = (ModFolder.McFolder)e.Data.GetData(typeof(ModFolder.McFolder));
// 检查是否为有效的拖拽操作
if (ReferenceEquals(sourceFolder, targetFolder))
{
e.Handled = true;
return;
}
// 检查文件夹是否在列表中
if (!ModFolder.mcFolderList.Contains(sourceFolder) || !ModFolder.mcFolderList.Contains(targetFolder))
{
e.Handled = true;
return;
}
// 获取源文件夹和目标文件夹的索引
var sourceIndex = ModFolder.mcFolderList.IndexOf(sourceFolder);
var targetIndex = ModFolder.mcFolderList.IndexOf(targetFolder);
// 执行移动操作
if (sourceIndex == targetIndex) return;
// 先移除源文件夹
ModFolder.mcFolderList.RemoveAt(sourceIndex);
// 计算新的插入位置
int newTargetIndex;
// 向下拖拽:插入到目标项目的后面
// 由于移除了源项目,目标索引已经自动减1,所以直接使用TargetIndex就是插入到目标后面
// 向上拖拽:插入到目标项目的前面
newTargetIndex = targetIndex;
// 确保插入位置不超出列表范围
if (newTargetIndex > ModFolder.mcFolderList.Count)
newTargetIndex = ModFolder.mcFolderList.Count;
else if (newTargetIndex < 0) newTargetIndex = 0;
// 插入到新位置
ModFolder.mcFolderList.Insert(newTargetIndex, sourceFolder);
// 更新文件夹顺序并刷新UI
UpdateFolderOrder();
var direction = sourceIndex < targetIndex ? "后面" : "前面";
ModBase.Log(
$"[Control] 文件夹拖拽排序:{sourceFolder.Name} -> 位置 {newTargetIndex} (在 {targetFolder.Name} {direction})",
ModBase.LogLevel.Debug);
}
catch (Exception ex)
{
ModBase.Log(
ex,
Lang.Text("Select.Folder.Error.DragDrop"),
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Select.Folder.Error.DragDrop"));
}
finally
{
e.Handled = true;
}
}
#endregion
}