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 compItemList = new(); private List selectedItemList = new(); public PageDownloadCompFavorites() { loader = new ModLoader.LoaderTask, List>("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> 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 LoaderInput() { List targetList = null; try { targetList = CurrentFavTarget.Favs.Distinct().ToList(); } catch (Exception ex) { ModBase.Log(ex, "[Favorites] 加载收藏夹列表时出错"); } return (List)targetList.Clone(); // 复制而不是直接引用! } private void CompFavoritesGet(ModLoader.LoaderTask, List> 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 itemList = new(); /// /// 刷新收藏夹列表 /// 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; // 默认选择第一个 } /// /// 返回适合当前工程项目的卡片记录 /// /// 工程项目类型 /// 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.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(); 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(); 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(); getInfoAndDownloadLoader.Add(new ModLoader.LoaderTask, List>( Lang.Text("Download.Comp.Favorites.LoaderName.QueryInfo"), ts => { List> allFiles = []; List suitVersion = []; var versionFirstSet = true; // 工程支持的全部版本获取 Func>, List> getAllVersionList = ls => { var allVersionList = new List(); 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 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 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>( 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 searchResult = new(); public void SearchRun(object sender, EventArgs e) { if (!allowSearch) return; if (IsSearching) { // 构造请求 var queryList = new List>(); foreach (var Item in compItemList) { if (Item.Tag is not ModComp.CompProject) continue; var entry = (ModComp.CompProject)Item.Tag; var searchSource = new List(); 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 { item = Item, searchSource = searchSource }); } // 进行搜索 searchResult = ModBase.Search(queryList, PanSearchBox.Text, ModBase.MaxLocalSearchDepth, 0.35d).Select(r => r.item).ToList(); } RefreshContent(); RefreshCardTitle(); } #endregion }