feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
CI / Go Backend (push) Canceled after 0s

初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

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

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

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
xyou
2026-08-08 14:07:56 +08:00
parent 9500c4c80a
commit f70b061d1a
1972 changed files with 159760 additions and 6 deletions
@@ -0,0 +1,200 @@
/* This file is generated by DeepSeek */
using System;
using System.Windows;
using System.Windows.Controls;
namespace PCL.Core.UI.Controls;
/// <summary>
/// 瀑布流布局模式
/// </summary>
public enum WaterfallLayoutMode
{
/// <summary>
/// 固定列数模式,使用 ColumnCount 属性指定列数
/// </summary>
FixedColumns,
/// <summary>
/// 自适应模式,根据可用宽度和 MaxItemWidth 自动计算列数
/// </summary>
AutoFit
}
public class WaterfallPanel : Panel
{
// 私有字段,存储 Measure 阶段计算出的实际列数(用于 Arrange 阶段)
private int _computedColumnCount;
#region
/// <summary>
/// 布局模式(固定列数 / 自适应)
/// </summary>
public WaterfallLayoutMode LayoutMode
{
get { return (WaterfallLayoutMode)GetValue(LayoutModeProperty); }
set { SetValue(LayoutModeProperty, value); }
}
public static readonly DependencyProperty LayoutModeProperty =
DependencyProperty.Register(nameof(LayoutMode), typeof(WaterfallLayoutMode), typeof(WaterfallPanel),
new FrameworkPropertyMetadata(WaterfallLayoutMode.FixedColumns, FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 固定列数(仅在 LayoutMode = FixedColumns 时生效)
/// </summary>
public int ColumnCount
{
get { return (int)GetValue(ColumnCountProperty); }
set { SetValue(ColumnCountProperty, value); }
}
public static readonly DependencyProperty ColumnCountProperty =
DependencyProperty.Register(nameof(ColumnCount), typeof(int), typeof(WaterfallPanel),
new FrameworkPropertyMetadata(2, FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 列间距
/// </summary>
public double ColumnSpacing
{
get { return (double)GetValue(ColumnSpacingProperty); }
set { SetValue(ColumnSpacingProperty, value); }
}
public static readonly DependencyProperty ColumnSpacingProperty =
DependencyProperty.Register(nameof(ColumnSpacing), typeof(double), typeof(WaterfallPanel),
new FrameworkPropertyMetadata(5.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 行间距
/// </summary>
public double RowSpacing
{
get { return (double)GetValue(RowSpacingProperty); }
set { SetValue(RowSpacingProperty, value); }
}
public static readonly DependencyProperty RowSpacingProperty =
DependencyProperty.Register(nameof(RowSpacing), typeof(double), typeof(WaterfallPanel),
new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
/// <summary>
/// 项的最大宽度(仅在 LayoutMode = AutoFit 时生效)
/// </summary>
public double MaxItemWidth
{
get { return (double)GetValue(MaxItemWidthProperty); }
set { SetValue(MaxItemWidthProperty, value); }
}
public static readonly DependencyProperty MaxItemWidthProperty =
DependencyProperty.Register(nameof(MaxItemWidth), typeof(double), typeof(WaterfallPanel),
new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
#endregion
protected override Size MeasureOverride(Size availableSize)
{
// 1. 确定列数
int columnCount;
if (LayoutMode == WaterfallLayoutMode.FixedColumns)
{
columnCount = ColumnCount;
}
else // AutoFit
{
if (double.IsInfinity(availableSize.Width) || double.IsNaN(availableSize.Width))
{
// 宽度无限时,回退到固定列数模式(使用 ColumnCount 作为后备)
columnCount = ColumnCount;
}
else
{
double effectiveMaxWidth = MaxItemWidth > 0 ? MaxItemWidth : 100; // 防御性处理
double slotWidth = effectiveMaxWidth + ColumnSpacing;
columnCount = Math.Max(1, (int)Math.Floor((availableSize.Width + ColumnSpacing) / slotWidth));
}
}
_computedColumnCount = columnCount;
// 2. 计算每列的实际宽度
double colWidth = (availableSize.Width - (columnCount - 1) * ColumnSpacing) / columnCount;
colWidth = Math.Max(0, colWidth); // 避免负宽度
// 3. 记录每列的当前高度(用于测量时决定元素放入哪一列)
double[] colHeights = new double[columnCount];
// 4. 测量所有子元素
foreach (UIElement child in InternalChildren)
{
// 约束子元素宽度为列宽,高度不限
child.Measure(new Size(colWidth, double.PositiveInfinity));
Size desiredSize = child.DesiredSize;
// 找到当前高度最小的列
int minColIndex = _GetMinHeightColumnIndex(colHeights);
double y = colHeights[minColIndex]; // 当前列已占高度,即新元素的 Y 坐标
// 更新该列高度:加上元素高度和行间距(为下一个元素预留)
colHeights[minColIndex] += desiredSize.Height + RowSpacing;
}
// 5. 计算面板所需总高度(取最高列的实际内容高度,需减去最后一个多余的行间距)
double totalHeight = 0;
for (int i = 0; i < colHeights.Length; i++)
{
if (colHeights[i] > 0)
totalHeight = Math.Max(totalHeight, colHeights[i] - RowSpacing);
else
totalHeight = Math.Max(totalHeight, colHeights[i]);
}
return new Size(availableSize.Width, totalHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
if (_computedColumnCount <= 0) return finalSize;
// 使用 Measure 阶段确定的列数,但根据 finalSize 重新计算列宽(保证填满可用宽度)
double colWidth = (finalSize.Width - (_computedColumnCount - 1) * ColumnSpacing) / _computedColumnCount;
colWidth = Math.Max(0, colWidth);
double[] colHeights = new double[_computedColumnCount];
foreach (UIElement child in InternalChildren)
{
int colIndex = _GetMinHeightColumnIndex(colHeights);
double x = colIndex * (colWidth + ColumnSpacing);
double y = colHeights[colIndex];
child.Arrange(new Rect(new Point(x, y), new Size(colWidth, child.DesiredSize.Height)));
// 更新该列高度:加上元素高度和行间距(为下一个元素定位)
colHeights[colIndex] += child.DesiredSize.Height + RowSpacing;
}
return finalSize;
}
/// <summary>
/// 获取当前高度最小的列索引
/// </summary>
private int _GetMinHeightColumnIndex(double[] colHeights)
{
int minIndex = 0;
double minHeight = colHeights[0];
for (int i = 1; i < colHeights.Length; i++)
{
if (colHeights[i] < minHeight)
{
minHeight = colHeights[i];
minIndex = i;
}
}
return minIndex;
}
}