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 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.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().FirstOrDefault(x => x.Name == "MoveUp"); var moveDownItem = contMenu.Items.OfType().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)); } } /// /// 将指定文件夹添加到 Minecraft 文件夹列表,并选中它。 /// 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(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(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(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 }