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