Files
MRCC/launcher/PCL-CE/Plain Craft Launcher 2/Pages/PageDownload/Comp/MyCompItem.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

468 lines
16 KiB
C#

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace PCL;
public partial class MyCompItem
{
private string stateLast;
/// <summary>
/// 是否允许交互。目前仅用于 PageDownloadCompDetail 的顶部栏展示:若关闭碰撞检测,则无法展开 Tooltip。
/// </summary>
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<ModAnimation.AniData>();
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<string> 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();
}
}
/// <summary>右侧是否存在任意可见的操作按钮(收藏 / 下载),用于决定悬停时是否淡入按钮区。</summary>
private bool _HasActionButtons => ShowFavoriteBtn || ShowDownloadBtn;
private void _UpdatePanButtons()
{
if (PanButtons is null) return;
PanButtons.Visibility = _HasActionButtons ? Visibility.Visible : Visibility.Collapsed;
}
/// <summary>
/// 刷新收藏状态(仅更新图标,不影响 hover 展示逻辑)
/// </summary>
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<string>();
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<string>(), 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;
// <!--<corelocal:BlurBorder x:Name = "RectBack" CornerRadius="3" RenderTransformOrigin="0.5,0.5" SnapsToDevicePixels="True"
// IsHitTestVisible = "False" Opacity="0" BorderThickness="1"
// Grid.ColumnSpan = "4" Background="{DynamicResource ColorBrush7}" BorderBrush="{DynamicResource ColorBrush6}"/>-->
}
return _RectBack;
}
}
#endregion
}