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