using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace PCL; public partial class MyCompItem { private string stateLast; /// /// 是否允许交互。目前仅用于 PageDownloadCompDetail 的顶部栏展示:若关闭碰撞检测,则无法展开 Tooltip。 /// public bool CanInteraction { get; set; } = true; public void RefreshColor(object sender, EventArgs e) { if (!CanInteraction) return; // 判断当前颜色 string stateNew; int time; if (IsMouseOver) { if (isMouseDown) { stateNew = "MouseDown"; time = 120; } else { stateNew = "MouseOver"; time = 120; } } else { stateNew = "Idle"; time = 180; } if ((stateLast ?? "") == (stateNew ?? "")) return; stateLast = stateNew; // 触发颜色动画 if (IsLoaded && ModAnimation.AniControlEnabled == 0) // 防止默认属性变更触发动画 { // 有动画 var ani = new List(); if (IsMouseOver) { if (PanButtons is not null && _HasActionButtons) ani.Add(ModAnimation.AaOpacity(PanButtons, 1d - PanButtons.Opacity, (int)Math.Round(time * 0.35d), (int)Math.Round(time * 0.15d))); ani.AddRange(new[] { ModAnimation.AaColor(RectBack, Border.BackgroundProperty, isMouseDown ? "ColorBrush6" : "ColorBrushBg1", time), ModAnimation.AaOpacity(RectBack, 1d - RectBack.Opacity, time, ease: new ModAnimation.AniEaseOutFluent()) }); if (isMouseDown) ani.Add(ModAnimation.AaScaleTransform(RectBack, 0.996d - ((ScaleTransform)RectBack.RenderTransform).ScaleX, (int)Math.Round(time * 1.2d), ease: new ModAnimation.AniEaseOutFluent())); else ani.Add(ModAnimation.AaScaleTransform(RectBack, 1d - ((ScaleTransform)RectBack.RenderTransform).ScaleX, (int)Math.Round(time * 1.2d), ease: new ModAnimation.AniEaseOutFluent())); } else { if (PanButtons is not null && _HasActionButtons) ani.Add(ModAnimation.AaOpacity(PanButtons, -PanButtons.Opacity, (int)Math.Round(time * 0.4d))); ani.AddRange(new[] { ModAnimation.AaOpacity(RectBack, -RectBack.Opacity, time), ModAnimation.AaColor(RectBack, Border.BackgroundProperty, isMouseDown ? "ColorBrush6" : "ColorBrush7", time), ModAnimation.AaScaleTransform(RectBack, 0.996d - ((ScaleTransform)RectBack.RenderTransform).ScaleX, time, ease: new ModAnimation.AniEaseOutFluent()), ModAnimation.AaScaleTransform(RectBack, -0.196d, 1, after: true) }); } ModAnimation.AniStart(ani, "CompItem Color " + Uuid); } else { // 无动画 ModAnimation.AniStop("CompItem Color " + Uuid); if (_RectBack is not null) RectBack.Opacity = 0d; if (PanButtons is not null) PanButtons.Opacity = 0d; } } #region 基础属性 public int Uuid = ModBase.GetUuid(); // Logo public string Logo { get => PathLogo.Source; set => PathLogo.Source = value; } // 标题 public string Title { get => LabTitle.Text; set { if ((LabTitle.Text ?? "") == (value ?? "")) return; LabTitle.Text = value; } } // 副标题 public string SubTitle { get => LabTitleRaw?.Text ?? ""; set { if ((LabTitleRaw.Text ?? "") == (value ?? "")) return; LabTitleRaw.Text = value; LabTitleRaw.Visibility = string.IsNullOrEmpty(value) ? Visibility.Collapsed : Visibility.Visible; } } // 描述 public string Description { get => LabInfo.Text; set { if ((LabInfo.Text ?? "") == (value ?? "")) return; LabInfo.Text = value; } } public MyCompItem() { InitializeComponent(); Click += (sender, e) => MyCompItem_Click((MyCompItem)sender, e); PreviewMouseLeftButtonUp += Button_MouseUp; PreviewMouseLeftButtonDown += Button_MouseDown; MouseLeave += Button_MouseLeave; PreviewMouseLeftButtonUp += Button_MouseLeave; MouseEnter += RefreshColor; MouseLeave += RefreshColor; MouseLeftButtonDown += RefreshColor; MouseLeftButtonUp += RefreshColor; // Handles LabInfo.MouseEnter += LabInfo_MouseEnter; BtnDelete.Click += BtnDelete_Click; BtnDownload.Click += _BtnDownload_Click; } // 指向时扩展描述 private void LabInfo_MouseEnter(object sender, MouseEventArgs e) { if (IsTextTrimmed(LabInfo)) { ToolTipInfo.Content = LabInfo.Text; ToolTipInfo.Width = LabInfo.ActualWidth + 25d; LabInfo.ToolTip = ToolTipInfo; } else { LabInfo.ToolTip = null; } } private bool IsTextTrimmed(TextBlock textBlock) { var typeface = new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch); var formattedText = new FormattedText(textBlock.Text, Thread.CurrentThread.CurrentCulture, textBlock.FlowDirection, typeface, textBlock.FontSize, textBlock.Foreground, ModBase.dpi); return formattedText.Width > textBlock.ActualWidth; } // Tag public List Tags { set { PanTags.Children.Clear(); PanTags.Visibility = value.Any() ? Visibility.Visible : Visibility.Collapsed; foreach (var tagText in value) { var newTag = new Border { Background = new SolidColorBrush(Color.FromArgb(17, 0, 0, 0)), Padding = new Thickness(3d, 1d, 3d, 1d), CornerRadius = new CornerRadius(3d), Margin = new Thickness(0d, 0d, 3d, 0d), SnapsToDevicePixels = true, UseLayoutRounding = false }; var tagTextBlock = new TextBlock { Text = tagText, Foreground = new SolidColorBrush(Color.FromRgb(134, 134, 134)), FontSize = 11d }; newTag.Child = tagTextBlock; PanTags.Children.Add(newTag); } } } // ‘收藏按钮 public bool ShowFavoriteBtn { get => BtnDelete.Visibility == Visibility.Visible; set { BtnDelete.Visibility = value ? Visibility.Visible : Visibility.Collapsed; _UpdatePanButtons(); } } // 快速下载按钮 public bool ShowDownloadBtn { get => BtnDownload.Visibility == Visibility.Visible; set { BtnDownload.Visibility = value ? Visibility.Visible : Visibility.Collapsed; _UpdatePanButtons(); } } /// 右侧是否存在任意可见的操作按钮(收藏 / 下载),用于决定悬停时是否淡入按钮区。 private bool _HasActionButtons => ShowFavoriteBtn || ShowDownloadBtn; private void _UpdatePanButtons() { if (PanButtons is null) return; PanButtons.Visibility = _HasActionButtons ? Visibility.Visible : Visibility.Collapsed; } /// /// 刷新收藏状态(仅更新图标,不影响 hover 展示逻辑) /// public void RefreshFavoriteStatus() { if (Tag is not ModComp.CompProject project) return; var isFavourite = ModComp.CompFavorites.IsFavourite(project.Id); BtnDelete.SvgIcon = isFavourite ? "lucide/heart-filled" : "lucide/heart"; } #endregion #region 点击 // 触发点击事件 public event ClickEventHandler? Click; public delegate void ClickEventHandler(object sender, MouseButtonEventArgs e); private void BtnDelete_Click(object sender, EventArgs e) { if (PanButtons.Opacity > 0d && Tag is ModComp.CompProject) { var project = (ModComp.CompProject)Tag; ModComp.CompFavorites.ShowMenu(project, (UIElement)sender, () => RefreshFavoriteStatus()); } } private void _BtnDownload_Click(object sender, EventArgs e) { if (PanButtons.Opacity > 0d && Tag is ModComp.CompProject project) ModComp.QuickDownload(project); } private void MyCompItem_Click(MyCompItem sender, EventArgs e) { // 记录当前展开的卡片标题(#2712) var titles = new List(); if (ModMain.frmMain.pageCurrent.page == FormMain.PageType.CompDetail) { foreach (MyCard Card in ModMain.frmDownloadCompDetail.PanResults.Children) if (!string.IsNullOrEmpty(Card.Title) && !Card.IsSwapped) titles.Add(Card.Title); ModBase.Log("[Comp] 记录当前已展开的卡片:" + string.Join("、", titles)); var additional = ModMain.frmMain.pageCurrent.additional.Value; ModMain.frmMain.pageCurrent.additional = additional with { ExpandedTitles = titles }; } // 打开详情页 var targetType = default(ModComp.CompType); string targetVersion = null; var targetLoader = ModComp.CompLoaderType.Any; if (ModMain.frmMain.pageCurrent.page == FormMain.PageType.Download) { if (ModMain.frmMain.PageCurrentSub == FormMain.PageSubType.DownloadCompFavorites) { targetVersion = ""; targetLoader = ModComp.CompLoaderType.Any; } else { // 从下载页进入 switch (ModMain.frmMain.PageCurrentSub) { case FormMain.PageSubType.DownloadMod: { targetType = ModComp.CompType.Mod; targetVersion = ModMain.frmDownloadMod.Content.loader.input.gameVersion; targetLoader = ModMain.frmDownloadMod.Content.loader.input.modLoader; break; } case FormMain.PageSubType.DownloadPack: { targetType = ModComp.CompType.ModPack; targetVersion = ModMain.frmDownloadPack.Content.loader.input.gameVersion; break; } case FormMain.PageSubType.DownloadDataPack: { targetType = ModComp.CompType.DataPack; targetVersion = ModMain.frmDownloadDataPack.Content.loader.input.gameVersion; break; } case FormMain.PageSubType.DownloadResourcePack: { targetType = ModComp.CompType.ResourcePack; targetVersion = ModMain.frmDownloadResourcePack.Content.loader.input.gameVersion; break; } case FormMain.PageSubType.DownloadShader: { targetType = ModComp.CompType.Shader; targetVersion = ModMain.frmDownloadShader.Content.loader.input.gameVersion; break; } case FormMain.PageSubType.DownloadWorld: { targetType = ModComp.CompType.World; targetVersion = ModMain.frmDownloadWorld.Content.loader.input.gameVersion; break; } } } } else if (ModMain.frmMain.pageCurrent.page == FormMain.PageType.InstanceSetup) { // 从实例设置页进入(查看整合包信息) targetType = ModComp.CompType.ModPack; } else { // 从详情页进入(查看前置) targetType = ModComp.CompType.Any; // 允许任意类别 var additional = ModMain.frmMain.pageCurrent.additional.Value; targetVersion = additional.TargetVersion; targetLoader = additional.TargetLoader; } ModMain.frmMain.PageChange(new FormMain.PageStackData { page = FormMain.PageType.CompDetail, additional = ((ModComp.CompProject)sender.Tag, new List(), targetVersion, targetLoader, targetType, null) }); } // 鼠标点击判定 private bool isMouseDown; // 触发点击事件 private void Button_MouseUp(object sender, MouseButtonEventArgs e) { if (!isMouseDown) return; Click?.Invoke(sender, e); } private void Button_MouseDown(object sender, MouseButtonEventArgs e) { if (!CanInteraction) return; // 检查点击位置是否在按钮区域内 var clickPosition = e.GetPosition(this); var isClickOnButton = false; if (PanButtons.Visibility == Visibility.Visible) isClickOnButton = _IsClickOnActionButton(BtnDelete, clickPosition) || _IsClickOnActionButton(BtnDownload, clickPosition); // 如果点击在按钮上,不处理主项目点击事件 if (isClickOnButton) return; // 如果点击在其他区域,按原逻辑处理 // 也要检查是否点击在LabInfo区域(支持ToolTip点击) var isClickOnLabInfo = false; if (LabInfo.Visibility == Visibility.Visible) { var labInfoBounds = new Rect(LabInfo.TranslatePoint(new Point(0d, 0d), this), LabInfo.RenderSize); isClickOnLabInfo = labInfoBounds.Contains(clickPosition); } if (IsMouseDirectlyOver || isClickOnLabInfo) isMouseDown = true; } private void Button_MouseLeave(object sender, object e) { isMouseDown = false; } // 判断点击是否落在某个操作按钮(收藏 / 下载)上 private bool _IsClickOnActionButton(FrameworkElement button, Point clickPosition) { if (button is null || button.Visibility != Visibility.Visible) return false; var bounds = new Rect(button.TranslatePoint(new Point(0d, 0d), this), button.RenderSize); return bounds.Contains(clickPosition); } #endregion #region 后加载指向背景 private Border _RectBack; public Border RectBack { get { if (_RectBack is null) { var rect = new Border { Name = "RectBack", CornerRadius = new CornerRadius(3d), RenderTransform = new ScaleTransform(0.8d, 0.8d), RenderTransformOrigin = new Point(0.5d, 0.5d), BorderThickness = new Thickness(ModBase.GetWPFSize(1d)), SnapsToDevicePixels = true, IsHitTestVisible = false, Opacity = 0d }; rect.SetResourceReference(Border.BackgroundProperty, "ColorBrush7"); rect.SetResourceReference(Border.BorderBrushProperty, "ColorBrush6"); SetColumnSpan(rect, 999); SetRowSpan(rect, 999); Children.Insert(0, rect); _RectBack = rect; // } return _RectBack; } } #endregion }