Files
MRCC/launcher/PCL-CE/Plain Craft Launcher 2/Pages/PageDownload/PageDownloadCompFavorites.xaml.cs
T
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

916 lines
36 KiB
C#

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using PCL.Core.UI;
using PCL.Network;
using PCL.Network.Loaders;
using PCL.Core.App.Localization;
namespace PCL;
public partial class PageDownloadCompFavorites
{
private readonly List<MyListItem> compItemList = new();
private List<MyListItem> selectedItemList = new();
public PageDownloadCompFavorites()
{
loader = new ModLoader.LoaderTask<List<string>, List<ModComp.CompProject>>("CompProject Favorites",
CompFavoritesGet, LoaderInput);
Initialized += PageDownloadCompFavorites_Inited;
Loaded += PageDownloadCompFavorites_Loaded;
KeyDown += Page_KeyDown;
InitializeComponent();
{
// 这是选择收藏夹旁边那个图标按钮
// 实在不想把布局写动态代码里,但是奈何龙猫的石山没办法在 XAML 里定义 Logo 属性为已有常量值
// 还有一个很扯淡的点,同样自定义的 MyButton 能在 XAML 直接设置 Click 事件
// 到 MyIconButton 就不行了,死活跑不了,也不知道是不是漏了什么依赖属性没写
Btn_ManageTargetFav.SvgIcon = "lucide/settings";
Btn_ManageTargetFav.Click += Manage_Click;
}
// Handles
Load.StateChanged += Load_State;
Btn_FavoritesCancel.Click += Btn_FavoritesCancel_Clicked;
Btn_SelectCancel.Click += Btn_SelectCancel_Clicked;
Btn_FavoritesShare.Click += Btn_FavoritesShare_Clicked;
Btn_FavoritesDownload.Click += Btn_FavoritesDownload_Clicked;
ComboTargetFav.SelectionChanged += ComboTargetFav_Selected;
HintGetFail.MouseLeftButtonDown += HintGetFail_MouseLeftButtonDown;
PanSearchBox.TextChanged += SearchRun;
WeakLanguageChanged.Add(this, OnLanguageChanged);
}
private static void OnLanguageChanged(PageDownloadCompFavorites page) => ModBase.RunInUi(page._RefreshCategoryTitles);
private ModComp.CompFavorites.FavData CurrentFavTarget
{
get
{
var selectedItem = (MyComboBoxItem)ComboTargetFav.SelectedItem;
if (selectedItem is null)
{
ModBase.Log("[Favorites] 异常:未选择收藏夹");
selectedItem = (MyComboBoxItem)ComboTargetFav.Items.GetItemAt(0);
}
return ModComp.CompFavorites.FavoritesList
.First(e => string.Equals(e.Id, selectedItem.Tag?.ToString(), StringComparison.OrdinalIgnoreCase));
}
}
#region 加载器信息
// 加载器信息
public ModLoader.LoaderTask<List<string>, List<ModComp.CompProject>> loader;
private void PageDownloadCompFavorites_Inited(object sender, EventArgs e)
{
RefreshFavTargets();
PageLoaderInit(Load, PanLoad, PanContent, null, loader, _ => Load_OnFinish(), LoaderInput);
}
private void PageDownloadCompFavorites_Loaded(object sender, EventArgs e)
{
Items_SetSelectAll(false);
RefreshBar();
if (loader.input is not null && !loader.input.Count.Equals(CurrentFavTarget.Favs.Count)) RefreshFavTargets();
}
private List<string> LoaderInput()
{
List<string> targetList = null;
try
{
targetList = CurrentFavTarget.Favs.Distinct().ToList();
}
catch (Exception ex)
{
ModBase.Log(ex, "[Favorites] 加载收藏夹列表时出错");
}
return (List<string>)targetList.Clone(); // 复制而不是直接引用!
}
private void CompFavoritesGet(ModLoader.LoaderTask<List<string>, List<ModComp.CompProject>> task)
{
task.output = ModComp.CompRequest.GetCompProjectsByIds(task.input);
}
#endregion
#region UI - 自适应卡片
public class CompListItemContainer // 用来存储自动依据类型生成的卡片及其相关信息
{
public MyCard Card { get; set; }
public StackPanel ContentList { get; set; }
public string Title { get; set; }
public int CompType { get; set; }
}
private readonly List<CompListItemContainer> itemList = new();
/// <summary>
/// 刷新收藏夹列表
/// </summary>
private void RefreshFavTargets()
{
ComboTargetFav.Items.Clear();
foreach (var Target in ModComp.CompFavorites.FavoritesList)
{
var item = new MyComboBoxItem
{
Content = Target.Name,
Tag = Target.Id
};
ComboTargetFav.Items.Add(item);
}
if (ComboTargetFav.SelectedIndex == -1) ComboTargetFav.SelectedIndex = 0; // 默认选择第一个
}
/// <summary>
/// 返回适合当前工程项目的卡片记录
/// </summary>
/// <param name="type">工程项目类型</param>
/// <returns></returns>
private CompListItemContainer GetSuitListContainer(int type)
{
if (itemList.Any(e => e.CompType.Equals(type))) return itemList.First(e => e.CompType.Equals(type));
var newItem = new CompListItemContainer
{
Card = new MyCard
{
CanSwap = true,
Margin = new Thickness(0d, 0d, 0d, 15d)
},
ContentList = new StackPanel
{
Orientation = Orientation.Vertical,
Margin = new Thickness(12d, 38d, 12d, 12d)
},
CompType = type
};
newItem.Title = _GetCategoryTitleFormat(type);
newItem.Card.Title = string.Format(newItem.Title, 0);
newItem.Card.Children.Add(newItem.ContentList);
itemList.Add(newItem);
return newItem;
}
private void RefreshContent()
{
foreach (var item in itemList) // 清除逻辑父子关系
item.ContentList.Children.Clear();
PanContentList.Children.Clear();
var dataSource = IsSearching ? searchResult : compItemList;
foreach (var item in dataSource)
GetSuitListContainer(IsSearching ? -1 : (int)((ModComp.CompProject)item.Tag).Type).ContentList.Children
.Add(item);
foreach (var item in itemList)
{
if (item.ContentList.Children.Count == 0)
continue;
PanContentList.Children.Add(item.Card);
}
}
private static string _GetCategoryTitleFormat(int type) => type switch
{
-1 => Lang.Text("Download.Comp.Favorites.SearchResults.Title"),
(int)ModComp.CompType.Mod => Lang.Text("Download.Comp.Favorites.Category.Mod"),
(int)ModComp.CompType.ModPack => $"{Lang.Text("Download.Comp.Type.Modpack")} ({{0}})",
(int)ModComp.CompType.ResourcePack => $"{Lang.Text("Download.Comp.Type.ResourcePack")} ({{0}})",
(int)ModComp.CompType.Shader => $"{Lang.Text("Download.Comp.Type.Shader")} ({{0}})",
(int)ModComp.CompType.DataPack => $"{Lang.Text("Download.Comp.Type.DataPack")} ({{0}})",
(int)ModComp.CompType.Plugin => $"{Lang.Text("Download.Comp.Type.Plugin")} ({{0}})",
(int)ModComp.CompType.World => $"{Lang.Text("Download.Comp.Type.World")} ({{0}})",
_ => $"{Lang.Text("Download.Comp.Favorites.UnknownType")} ({{0}})"
};
private void _RefreshCategoryTitles()
{
foreach (var item in itemList)
item.Title = _GetCategoryTitleFormat(item.CompType);
RefreshCardTitle();
}
private void RefreshCardTitle()
{
foreach (var item in itemList)
item.Card.Title = string.Format(item.Title,
compItemList.Where(e => (int)((ModComp.CompProject)e.Tag).Type == item.CompType).Count());
if (!itemList.Any(e => e.CompType.Equals(-1)))
return;
var searchItem = itemList.First(e => e.CompType.Equals(-1));
if (searchItem is not null) searchItem.Card.Title = string.Format(searchItem.Title, searchResult.Count);
}
#endregion
#region UI - 加载主逻辑
// 结果 UI 化
private void Load_OnFinish()
{
itemList.Clear();
try
{
allowSearch = false;
PanSearchBox.Text = string.Empty;
allowSearch = true;
compItemList.Clear();
var someGetFail = loader.input.Count != loader.output.Count;
HintGetFail.Visibility = someGetFail ? Visibility.Visible : Visibility.Collapsed;
foreach (var item in loader.output)
{
var compItem = item.ToListItem();
ListItemBuild(compItem);
compItemList.Add(compItem);
}
if (compItemList.Any()) // 有收藏
{
if (!IsSearching)
{
PanSearchBox.Visibility = Visibility.Visible;
PanContentList.Visibility = Visibility.Visible;
CardNoContent.Visibility = Visibility.Collapsed;
}
}
else // 没有收藏
{
PanSearchBox.Visibility = Visibility.Collapsed;
PanContentList.Visibility = Visibility.Collapsed;
CardNoContent.Visibility = Visibility.Visible;
}
RefreshContent();
RefreshCardTitle();
}
catch (Exception ex)
{
ModBase.Log(
ex,
"可视化收藏夹列表出错",
ModBase.LogLevel.Feedback,
userSummary: Lang.Text("Download.Comp.Favorites.Error.OperationFailed"));
}
}
private void ListItemBuild(MyListItem compItem)
{
compItem.Type = MyListItem.CheckType.CheckBox;
var compId = ((ModComp.CompProject)compItem.Tag).Id;
// ----备注----
var notes = "";
CurrentFavTarget.Notes.TryGetValue(compId, out notes);
var noteItem = new Run { Foreground = new SolidColorBrush(Color.FromRgb(0, 184, 148)) };
if (!string.IsNullOrWhiteSpace(notes)) noteItem.Text = $" ({notes})";
compItem.LabTitle.Inlines.Add(noteItem);
// ----添加按钮----
// 修改备注按钮
var btn_EditNote = new MyIconButton();
btn_EditNote.SvgIcon = "lucide/pencil";
btn_EditNote.ToolTip = Lang.Text("Download.Comp.Favorites.EditNote");
ToolTipService.SetPlacement(btn_EditNote, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btn_EditNote, 30d);
ToolTipService.SetHorizontalOffset(btn_EditNote, 2d);
btn_EditNote.Click += (sender, e) =>
{
CurrentFavTarget.Notes.TryGetValue(compId, out notes);
var desiredNote = ModMain.MyMsgBoxInput(Lang.Text("Download.Comp.Favorites.EditNote"), defaultInput: notes);
// 只有在用户确认时才更新备注,避免取消时清空原有备注
if (desiredNote is not null)
{
CurrentFavTarget.Notes[compId] = desiredNote;
noteItem.Text = string.IsNullOrWhiteSpace(desiredNote) ? "" : $" ({desiredNote})";
ModComp.CompFavorites.Save();
}
};
// 删除按钮
var btn_Delete = new MyIconButton
{
SvgIcon = "lucide/heart-filled",
ToolTip = Lang.Text("Download.Comp.Favorites.Action.Unfavorite")
};
ToolTipService.SetPlacement(btn_Delete, PlacementMode.Center);
ToolTipService.SetVerticalOffset(btn_Delete, 30d);
ToolTipService.SetHorizontalOffset(btn_Delete, 2d);
btn_Delete.Click += (sender, e) =>
{
Items_CancelFavorites(compItem);
RefreshContent();
RefreshCardTitle();
RefreshBar();
};
compItem.Buttons = [btn_EditNote, btn_Delete];
// ---操作逻辑---
// 右键查看详细信息界面
if (compItem.Tag is ModComp.CompProject)
compItem.MouseRightButtonUp += (_, _) => ModMain.frmMain.PageChange(
new FormMain.PageStackData
{
page = FormMain.PageType.CompDetail,
additional = ((ModComp.CompProject)compItem.Tag, new List<string>(), string.Empty, ModComp.CompLoaderType.Any,
((ModComp.CompProject)compItem.Tag).Type, null)
});
// ---其它事件---
compItem.Changed += ItemCheckStatusChanged;
}
#endregion
#region UI - 选择操作
private int bottomBarShownCount;
private void RefreshBar()
{
var newCount = selectedItemList.Count;
var selected = newCount > 0;
if (selected)
LabSelect.Text = Lang.Text("Download.Comp.Favorites.Hint.SelectedCount", newCount); // 取消所有选择时不更新数字
// 更新显示状态
if (ModAnimation.AniControlEnabled == 0)
{
PanContentList.Margin = new Thickness(0d, 0d, 0d, selected ? 80 : 0);
if (selected)
{
// 仅在数量增加时播放出现/跳跃动画
if (bottomBarShownCount >= newCount)
{
bottomBarShownCount = newCount;
return;
}
bottomBarShownCount = newCount;
// 出现/跳跃动画
CardSelect.Visibility = Visibility.Visible;
ModAnimation.AniStart(
new[]
{
ModAnimation.AaOpacity(CardSelect, 1d - CardSelect.Opacity, 60),
ModAnimation.AaTranslateY(CardSelect, -27 - TransSelect.Y, 120,
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaTranslateY(CardSelect, 3d, 150, 120,
new ModAnimation.AniEaseInoutFluent(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaTranslateY(CardSelect, -1, 90, 270,
new ModAnimation.AniEaseInoutFluent(ModAnimation.AniEasePower.Weak))
}, "CompFavorites Sidebar");
}
else
{
// 不重复播放隐藏动画
if (bottomBarShownCount == 0)
return;
bottomBarShownCount = 0;
// 隐藏动画
ModAnimation.AniStart(
new[]
{
ModAnimation.AaOpacity(CardSelect, -CardSelect.Opacity, 90),
ModAnimation.AaTranslateY(CardSelect, -10 - TransSelect.Y, 90,
ease: new ModAnimation.AniEaseInFluent(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaCode(() => CardSelect.Visibility = Visibility.Collapsed, after: true)
}, "CompFavorites Sidebar");
}
}
else
{
ModAnimation.AniStop("CompFavorites Sidebar");
bottomBarShownCount = newCount;
if (selected)
{
CardSelect.Visibility = Visibility.Visible;
CardSelect.Opacity = 1d;
TransSelect.Y = -25;
}
else
{
CardSelect.Visibility = Visibility.Collapsed;
CardSelect.Opacity = 0d;
TransSelect.Y = -10;
}
}
}
#endregion
#region 事件
// 选中状态改变
private void ItemCheckStatusChanged(object sender, ModBase.RouteEventArgs e)
{
var senderItem = (MyListItem)sender;
if (selectedItemList.Contains(senderItem))
selectedItemList.Remove(senderItem);
if (senderItem.Checked)
selectedItemList.Add(senderItem);
RefreshBar();
}
// 自动重试
private void Load_State(object sender, MyLoading.MyLoadingState state, MyLoading.MyLoadingState oldState)
{
switch (loader.State)
{
case ModBase.LoadState.Failed:
{
var errorMessage = "";
if (loader.Error is not null)
errorMessage = loader.Error.Message;
if (errorMessage.Contains(Lang.Text("Common.Error.InvalidJson")))
{
ModBase.Log("[Download] 下载的工程列表 JSON 文件损坏,已自动重试", ModBase.LogLevel.Debug);
PageLoaderRestart();
}
break;
}
}
}
private void Btn_FavoritesCancel_Clicked(object sender, ModBase.RouteEventArgs e)
{
foreach (var Items in selectedItemList.Clone())
Items_CancelFavorites(Items);
if (compItemList.Any())
{
RefreshContent();
RefreshCardTitle();
}
else
{
loader.Start();
}
RefreshBar();
}
private void Btn_SelectCancel_Clicked(object sender, ModBase.RouteEventArgs e)
{
Items_SetSelectAll(false);
}
private void Btn_FavoritesShare_Clicked(object sender, ModBase.RouteEventArgs e)
{
try
{
ModBase.ClipboardSet(
ModComp.CompFavorites.GetShareCode(selectedItemList.Select(i => ((ModComp.CompProject)i.Tag).Id)
.ToHashSet()));
Items_SetSelectAll(false);
}
catch (Exception ex)
{
ModBase.Log(
ex,
"[CompFavourites] 分享收藏时发生错误",
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Download.Comp.Favorites.Error.OperationFailed"));
}
}
private void Btn_FavoritesDownload_Clicked(object sender, ModBase.RouteEventArgs e)
{
try
{
if (1 != ModMain.MyMsgBox(
Lang.Text("Download.Comp.Favorites.Dialog.BulkDownload.Content"),
Lang.Text("Download.Comp.Favorites.Dialog.BulkDownload.Title"),
Lang.Text("Common.Action.Continue"),
Lang.Text("Common.Action.Cancel"), isWarn: true
)) return;
var supportedModLoader = new List<ModComp.CompLoaderType>();
var loaderFirstSet = true;
var hasMod = false;
foreach (var Item in selectedItemList) // 获取共同支持的 ModLoader
{
var proj = (ModComp.CompProject)Item.Tag;
if (proj.Type == ModComp.CompType.Mod)
{
hasMod = true;
if (loaderFirstSet)
{
loaderFirstSet = false;
supportedModLoader = proj.ModLoaders;
}
else
{
supportedModLoader = supportedModLoader.Intersect(proj.ModLoaders).ToList();
}
}
}
// 检查是否有共同支持的 ModLoader
if (hasMod && supportedModLoader.Count == 0)
{
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.SelectLoader"), HintType.Error);
return;
}
// 要求选择版本
var desiredModLoader = ModComp.CompLoaderType.Any;
if (hasMod && supportedModLoader.Count > 0)
if (supportedModLoader.Count > 0)
{
var mSelection = new List<IMyRadio>();
foreach (var i in supportedModLoader)
mSelection.Add(new MyRadioBox { Text = i.ToString() });
var selectedModLoaderStr = ModMain.MyMsgBoxSelect(mSelection, Lang.Text("Download.Comp.Favorites.Dialog.SelectLoader.Title"), button2: Lang.Text("Common.Action.Cancel"));
if (selectedModLoaderStr is null)
return;
desiredModLoader = supportedModLoader[(int)selectedModLoaderStr];
}
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.LoadingVersions"));
// 输入 Ids,输出合适版本
var getInfoAndDownloadLoader = new List<ModLoader.LoaderBase>();
getInfoAndDownloadLoader.Add(new ModLoader.LoaderTask<List<string>, List<DownloadFile>>(
Lang.Text("Download.Comp.Favorites.LoaderName.QueryInfo"), ts =>
{
List<List<ModComp.CompFile>> allFiles = [];
List<string> suitVersion = [];
var versionFirstSet = true;
// 工程支持的全部版本获取
Func<List<List<string>>, List<string>> getAllVersionList = ls =>
{
var allVersionList = new List<string>();
foreach (var i in ls) allVersionList.AddRange(i);
return allVersionList.Distinct().ToList();
};
// 获取多个工程之间支持的版本的交集
var finishedTasks = 0;
foreach (var Item in ts.input)
ModBase.RunInNewThread(() =>
{
try
{
allFiles.Add(ModComp.CompFilesGet(Item, ModComp.CompRequest.IsFromCurseForge(Item))
.Where(i => i.Type != ModComp.CompType.Mod || i.ModLoaders.Contains(desiredModLoader))
.ToList());
}
catch (Exception ex)
{
ModBase.Log(
ex,
$"获取 {Item} 的下载信息失败",
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Download.Comp.Favorites.Error.OperationFailed"));
}
finally
{
finishedTasks += 1;
}
});
while (finishedTasks != ts.input.Count)
Thread.Sleep(200);
// 求取共同的版本
foreach (var Item in allFiles)
{
var current = getAllVersionList(Item.Select(i => i.GameVersions).ToList());
if (versionFirstSet)
{
versionFirstSet = false;
suitVersion = current;
}
else
{
suitVersion = suitVersion.Intersect(current).ToList();
}
// Log(SuitVersion.Join(","))
if (suitVersion.Count == 0)
{
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.NoResource"), HintType.Error);
ts.Abort();
return;
}
// 要求用户选择希望下载的版本
}
int? selectedVersion = 0;
ModBase.RunInUiWait(() =>
{
List<IMyRadio> selection = [];
foreach (var i in suitVersion)
selection.Add(new MyRadioBox { Text = i });
selectedVersion = ModMain.MyMsgBoxSelect(selection, Lang.Text("Download.Comp.Favorites.Dialog.SelectVersion.Title"), button2: Lang.Text("Common.Action.Cancel"));
if (selectedVersion is null) ts.Abort();
});
string selectedVersionStr = suitVersion[(int)selectedVersion];
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.SelectSaveLocation", selectedVersionStr));
var saveFolder = SystemDialogs.SelectFolder();
if (string.IsNullOrWhiteSpace(saveFolder))
{
ts.Abort();
return;
}
;
// 获取有期望版本号的文件
List<DownloadFile> res = [];
foreach (var Target in allFiles)
{
// 按照发布日期排序
var finalChoices = Target.Where(i => i.GameVersions.Contains(selectedVersionStr)).ToList();
finalChoices.Sort((a, b) => a.ReleaseDate > b.ReleaseDate);
// 获取文件名
var targetProject = ModComp.compProjectCache[finalChoices.First().ProjectId];
var fileName = ModComp.CompFileNameGet(targetProject, finalChoices.First());
// 选择最新版本进行下载
res.Add(finalChoices.First().ToNetFile(System.IO.Path.Combine(saveFolder, fileName)));
}
ts.output = res;
})
{
ProgressWeight = 2d
});
getInfoAndDownloadLoader.Add(
new LoaderDownload(
Lang.Text("Download.Comp.Favorites.LoaderName.BatchDownloadSuitable"),
[]
)
{
ProgressWeight = 8d
}
);
var checkLoader = new ModLoader.LoaderCombo<List<string>>(
Lang.Text("Download.Comp.Favorites.LoaderName.BatchDownload", ModBase.GetUuid()),
getInfoAndDownloadLoader
)
{
OnStateChanged = ModDownloadLib.LoaderStateChangedHintOnly
};
checkLoader.Start(selectedItemList.Select(i => ((ModComp.CompProject)i.Tag).Id).ToList());
ModLoader.LoaderTaskbarAdd(checkLoader);
ModMain.frmMain.BtnExtraDownload.ShowRefresh();
ModMain.frmMain.BtnExtraDownload.Ribble();
Items_SetSelectAll(false);
}
catch (Exception ex)
{
ModBase.Log(
ex,
"批量下载收藏时发生错误",
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Download.Comp.Favorites.Error.OperationFailed"));
}
}
private void Items_SetSelectAll(bool targetStatus)
{
if (IsSearching)
foreach (var Item in searchResult)
Item.Checked = targetStatus;
else
foreach (var Item in compItemList)
Item.Checked = targetStatus;
selectedItemList = compItemList.Where(e => e.Checked).ToList();
}
private void Items_CancelFavorites(MyListItem item)
{
try
{
compItemList.Remove(item);
if (selectedItemList.Contains(item))
selectedItemList.Remove(item);
if (searchResult.Contains(item))
searchResult.Remove(item);
CurrentFavTarget.Favs.Remove(((ModComp.CompProject)item.Tag).Id);
ModComp.CompFavorites.Save();
if (!compItemList.Any())
ModMain.frmDownloadCompFavorites.PageLoaderRestart();
}
catch (Exception ex)
{
ModBase.Log(ex, "[CompFavourites] 移除收藏时发生错误");
}
}
private void Page_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.A && (e.KeyboardDevice.IsKeyDown(Key.LeftCtrl) || e.KeyboardDevice.IsKeyDown(Key.RightCtrl)))
Items_SetSelectAll(true);
}
private void Manage_Click(object sender, EventArgs _)
{
var body = new ContextMenu();
var newItem = new MyMenuItem
{
Header = Lang.Text("Download.Comp.Favorites.Menu.Share"),
SvgIcon = "lucide/share-2"
};
newItem.Click += (_, _) =>
{
try
{
if (CurrentFavTarget.Favs.Count == 0)
{
HintWrapper.Show(Lang.Text("Download.Comp.Favorites.Hint.NothingShared"));
return;
}
ModBase.ClipboardSet(ModComp.CompFavorites.GetShareCode(CurrentFavTarget.Favs));
}
catch (Exception ex)
{
ModBase.Log(
ex,
"[Favourites] 分享收藏时发生错误",
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Download.Comp.Favorites.Error.OperationFailed"));
}
};
body.Items.Add(newItem);
newItem = new MyMenuItem
{
Header = Lang.Text("Download.Comp.Favorites.Menu.Import"),
SvgIcon = "lucide/circle-plus"
};
newItem.Click += (_, _) =>
{
try
{
var clipData = ModMain.MyMsgBoxInput(Lang.Text("Download.Comp.Favorites.Dialog.Import.Input"), hintText: Lang.Text("Download.Comp.Favorites.Dialog.Import.Hint"));
if (string.IsNullOrWhiteSpace(clipData)) return;
var newFavs = ModComp.CompFavorites.GetIdsByShareCode(clipData);
if (newFavs.Count == 0)
{
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.NothingShared"));
return;
}
var userWant = ModMain.MyMsgBox(Lang.Text("Download.Comp.Favorites.Dialog.Import.Type"), button1: Lang.Text("Download.Comp.Favorites.Dialog.Import.NewFolder"), button2: Lang.Text("Download.Comp.Favorites.Dialog.Import.CurrentFolder"));
switch (userWant)
{
case 1:
{
var newFavName = ModMain.MyMsgBoxInput(Lang.Text("Download.Comp.Favorites.Dialog.Import.New"), Lang.Text("Download.Comp.Favorites.Dialog.NewPrompt"));
if (string.IsNullOrWhiteSpace(newFavName)) return;
ModComp.CompFavorites.FavoritesList.Add(ModComp.CompFavorites.GetNewFav(newFavName, newFavs));
ModComp.CompFavorites.Save();
RefreshFavTargets();
ComboTargetFav.SelectedIndex = ComboTargetFav.Items.Count - 1;
break;
}
case 2:
{
newFavs.ToList().ForEach(x => CurrentFavTarget.Favs.Add(x));
ModComp.CompFavorites.Save();
loader.Start(isForceRestart: true);
break;
}
}
}
catch (Exception ex)
{
ModBase.Log(
ex,
"解析分享数据失败",
ModBase.LogLevel.Hint,
userSummary: Lang.Text("Download.Comp.Favorites.Error.OperationFailed"));
}
};
body.Items.Add(newItem);
newItem = new MyMenuItem
{
Header = Lang.Text("Download.Comp.Favorites.Menu.New"),
SvgIcon = "lucide/folder-plus"
};
newItem.Click += (_, _) =>
{
var newFavName = ModMain.MyMsgBoxInput(Lang.Text("Download.Comp.Favorites.Menu.New"), Lang.Text("Download.Comp.Favorites.Dialog.NewPrompt"));
if (string.IsNullOrWhiteSpace(newFavName))
return;
ModComp.CompFavorites.FavoritesList.Add(ModComp.CompFavorites.GetNewFav(newFavName, null));
ModComp.CompFavorites.Save();
RefreshFavTargets();
ComboTargetFav.SelectedIndex = ComboTargetFav.Items.Count - 1;
};
body.Items.Add(newItem);
newItem = new MyMenuItem
{
Header = Lang.Text("Download.Comp.Favorites.Menu.Rename"),
SvgIcon = "lucide/pencil"
};
newItem.Click += (_, _) =>
{
var newName = ModMain.MyMsgBoxInput(Lang.Text("Download.Comp.Favorites.Dialog.Rename.Title"), defaultInput: CurrentFavTarget.Name);
if (string.IsNullOrWhiteSpace(newName) || (CurrentFavTarget.Name ?? "") == (newName ?? ""))
return;
CurrentFavTarget.Name = newName;
ModComp.CompFavorites.Save();
RefreshFavTargets();
};
body.Items.Add(newItem);
newItem = new MyMenuItem
{
Header = Lang.Text("Download.Comp.Favorites.Menu.Delete"),
SvgIcon = "lucide/trash-2"
};
newItem.Click += (_, _) =>
{
if (ModComp.CompFavorites.FavoritesList.Count == 1)
{
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.LastCollection"));
return;
}
var content = Lang.Text("Download.Comp.Favorites.Dialog.Delete.Confirm", CurrentFavTarget.Name, CurrentFavTarget.Favs.Count, CurrentFavTarget.Id);
var res = ModMain.MyMsgBox(content, Lang.Text("Download.Comp.Favorites.Dialog.Delete.Title"), isWarn: true, button1: Lang.Text("Common.Option.No"), button2: Lang.Text("Common.Option.Yes"), button3: Lang.Text("Common.Option.No"));
if (res == 2)
{
ModComp.CompFavorites.FavoritesList.Remove(CurrentFavTarget);
ModComp.CompFavorites.Save();
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.Deleted"), HintType.Success);
RefreshFavTargets();
ComboTargetFav.SelectedIndex = 0;
}
};
body.Items.Add(newItem);
body.PlacementTarget = (UIElement)sender;
body.Placement = PlacementMode.Bottom;
body.IsOpen = true;
}
private void ComboTargetFav_Selected(object sender, RoutedEventArgs e)
{
if (ComboTargetFav.SelectedItem is null)
return;
Items_SetSelectAll(false);
loader.Start(isForceRestart: true);
}
private void HintGetFail_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var content = Lang.Text("Download.Comp.Favorites.Dialog.GetFailed.Content") + "\r\n" + "\r\n";
var failIds = loader.input.Except(loader.output.Select(i => i.Id).ToList()).ToList();
foreach (var Id in failIds)
content += $" - {Id}" + "\r\n";
ModMain.MyMsgBox(content, Lang.Text("Download.Comp.Favorites.Dialog.GetFailed.Title"), button2: Lang.Text("Download.Comp.Favorites.Dialog.GetFailed.CopyIds"), button3: Lang.Text("Download.Comp.Favorites.Dialog.GetFailed.Remove"),
button2Action: () => ModBase.ClipboardSet(failIds.Join("\r\n")), button3Action: () =>
{
foreach (var Id in failIds)
CurrentFavTarget.Favs.Remove(Id);
ModComp.CompFavorites.Save();
HintService.Hint(Lang.Text("Download.Comp.Favorites.Hint.Removed"), HintType.Success);
});
}
#endregion
#region 搜索
private bool IsSearching => !string.IsNullOrWhiteSpace(PanSearchBox.Text);
private bool allowSearch = true;
private List<MyListItem> searchResult = new();
public void SearchRun(object sender, EventArgs e)
{
if (!allowSearch)
return;
if (IsSearching)
{
// 构造请求
var queryList = new List<ModBase.SearchEntry<MyListItem>>();
foreach (var Item in compItemList)
{
if (Item.Tag is not ModComp.CompProject)
continue;
var entry = (ModComp.CompProject)Item.Tag;
var searchSource = new List<ModBase.SearchSource>();
searchSource.Add(new ModBase.SearchSource(entry.RawName, 1d));
if (entry.Description is not null && !string.IsNullOrEmpty(entry.Description))
searchSource.Add(new ModBase.SearchSource(entry.Description, 0.4d));
if ((entry.TranslatedName ?? "") != (entry.RawName ?? ""))
searchSource.Add(new ModBase.SearchSource(entry.TranslatedName, 1d));
searchSource.Add(new ModBase.SearchSource(string.Join("", entry.Tags), 0.2d));
queryList.Add(new ModBase.SearchEntry<MyListItem> { item = Item, searchSource = searchSource });
}
// 进行搜索
searchResult = ModBase.Search(queryList, PanSearchBox.Text, ModBase.MaxLocalSearchDepth, 0.35d).Select(r => r.item).ToList();
}
RefreshContent();
RefreshCardTitle();
}
#endregion
}