Files
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

455 lines
15 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace PCL;
public partial class MyIconButton
{
public delegate void ClickEventHandler(object sender, EventArgs e);
public enum Themes
{
Color,
White,
Black,
Red,
Custom
}
// 务必放在 IsMouseDown 更新之后
private const int animationColorIn = 120;
private const int animationColorOut = 150;
//鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
private bool isMouseDown;
// 自定义属性
public int Uuid = ModBase.GetUuid();
protected override Size MeasureOverride(Size constraint)
{
var measured = base.MeasureOverride(constraint);
if (double.IsNaN(Width) && !double.IsNaN(Height) && Height > 0D && !double.IsInfinity(Height))
return new Size(Height, Height);
return measured;
}
public MyIconButton()
{
InitializeComponent();
MouseLeftButtonUp += Button_MouseUp;
MouseLeftButtonDown += Button_MouseDown;
MouseLeftButtonUp += (_, _) => Button_MouseUp();
MouseLeave += (_, _) => Button_MouseLeave();
MouseEnter += (_, _) => RefreshAnim();
MouseLeave += (_, _) => RefreshAnim();
Loaded += (_, _) => RefreshAnim();
}
public string Logo
{
get => Path.Data?.ToString() ?? string.Empty;
set
{
if (Path is null) return;
Path.Data = (Geometry)new GeometryConverter().ConvertFromString(value);
SvgIconControlHelper.ApplyVisibility(Path, ShapeSvgIcon, IsUsingSvgIcon);
}
}
public string SvgIcon
{
get;
set
{
value ??= string.Empty;
if (value == field)
return;
field = value;
if (Path is null || ShapeSvgIcon is null)
return;
SvgIconControlHelper.ApplyIcon(Path, ShapeSvgIcon, field);
ApplyLogoScale();
RefreshAnim();
}
} = string.Empty;
private bool IsUsingSvgIcon => SvgIconControlHelper.HasSvgIcon(SvgIcon);
private double EffectiveLogoScale => IsUsingSvgIcon ? 1D : LogoScale;
private void ApplyLogoScale()
{
IconHost?.RenderTransform = new ScaleTransform { ScaleX = EffectiveLogoScale, ScaleY = EffectiveLogoScale };
}
public double LogoScale
{
get;
set
{
field = value;
ApplyLogoScale();
}
} = 1d;
public Themes Theme { get; set; } = Themes.Color;
public SolidColorBrush Foreground
{
get;
set
{
field = value;
ModAnimation.AniControlEnabled += 1;
RefreshAnim();
ModAnimation.AniControlEnabled -= 1;
}
} = new(Color.FromRgb(128, 128, 128));
private string ColorAnimationKey => "MyIconButton Color " + Uuid;
// 自定义事件
public event ClickEventHandler? Click;
private static ModBase.MyColor GetTransparentBackground()
{
return new ModBase.MyColor(0d, 255d, 255d, 255d);
}
private ModBase.MyColor? GetBaseFillColor()
{
return Theme switch
{
Themes.Red => new ModBase.MyColor(160d, 255d, 76d, 76d),
Themes.Black => ThemeManager.IsDarkMode
? new ModBase.MyColor(160d, 255d, 255d, 255d)
: new ModBase.MyColor(160d, 0d, 0d, 0d),
Themes.Custom => new ModBase.MyColor(160d, Foreground),
_ => null
};
}
private void EnsureBaseBrushes()
{
PanBack.Background ??= GetTransparentBackground();
var baseFill = GetBaseFillColor();
if (baseFill is not null && !IsUsingSvgIcon)
Path.Fill ??= baseFill;
}
private void AnimateActiveSvgIconBrush(string resourceKey, int duration)
{
if (IsUsingSvgIcon)
SvgIconControlHelper.AnimateSvgIconBrushTo(ShapeSvgIcon, resourceKey, duration, ColorAnimationKey);
}
private void AnimateActiveSvgIconBrush(ModBase.MyColor color, int duration)
{
if (IsUsingSvgIcon)
SvgIconControlHelper.AnimateSvgIconBrushTo(ShapeSvgIcon, color, duration, ColorAnimationKey);
}
private void SetActiveIconResource(string resourceKey)
{
SvgIconControlHelper.SetIconResource(Path, ShapeSvgIcon, IsUsingSvgIcon, resourceKey);
}
private void SetActiveIconBrush(Brush brush)
{
SvgIconControlHelper.SetIconBrush(Path, ShapeSvgIcon, IsUsingSvgIcon, brush);
}
private List<ModAnimation.AniData> GetHoverAnimations()
{
var animations = new List<ModAnimation.AniData>();
switch (Theme)
{
case Themes.Color:
{
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush("ColorBrush2", animationColorIn);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
"ColorBrush2",
animationColorIn));
break;
}
case Themes.White:
{
animations.Add(ModAnimation.AaColor(
PanBack,
BackgroundProperty,
new ModBase.MyColor(50d, 255d, 255d, 255d) - PanBack.Background,
animationColorIn));
break;
}
case Themes.Red:
{
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush(
new ModBase.MyColor(255d, 76d, 76d),
animationColorIn);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
new ModBase.MyColor(255d, 76d, 76d) - Path.Fill,
animationColorIn));
break;
}
case Themes.Black:
{
var blackHoverColor = ThemeManager.IsDarkMode
? new ModBase.MyColor(230d, 255d, 255d, 255d)
: new ModBase.MyColor(230d, 0d, 0d, 0d);
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush(blackHoverColor, animationColorIn);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
blackHoverColor - Path.Fill,
animationColorIn));
break;
}
case Themes.Custom:
{
var customHoverColor = new ModBase.MyColor(255d, Foreground);
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush(customHoverColor, animationColorIn);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
customHoverColor - Path.Fill,
animationColorIn));
break;
}
}
return animations;
}
private List<ModAnimation.AniData> GetNormalAnimations()
{
var animations = new List<ModAnimation.AniData>();
switch (Theme)
{
case Themes.Color:
{
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush("ColorBrush4", animationColorOut);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
"ColorBrush4",
animationColorOut));
PanBack.Background = GetTransparentBackground();
break;
}
case Themes.White:
{
var whiteNormalColor = new ModBase.MyColor(234d, 242d, 254d);
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush(whiteNormalColor, animationColorOut);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
whiteNormalColor,
animationColorOut));
animations.Add(ModAnimation.AaColor(
PanBack,
BackgroundProperty,
GetTransparentBackground() - PanBack.Background,
animationColorOut));
break;
}
case Themes.Red:
{
var redNormalColor = new ModBase.MyColor(160d, 255d, 76d, 76d);
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush(redNormalColor, animationColorOut);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
redNormalColor - Path.Fill,
animationColorOut));
PanBack.Background = GetTransparentBackground();
break;
}
case Themes.Black:
{
var blackNormalColor = ThemeManager.IsDarkMode
? new ModBase.MyColor(160d, 255d, 255d, 255d)
: new ModBase.MyColor(160d, 0d, 0d, 0d);
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush(blackNormalColor, animationColorOut);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
blackNormalColor - Path.Fill,
animationColorOut));
PanBack.Background = GetTransparentBackground();
break;
}
case Themes.Custom:
{
var customNormalColor = new ModBase.MyColor(160d, Foreground);
if (IsUsingSvgIcon)
AnimateActiveSvgIconBrush(customNormalColor, animationColorOut);
else
animations.Add(ModAnimation.AaColor(
Path,
Shape.FillProperty,
customNormalColor - Path.Fill,
animationColorOut));
PanBack.Background = GetTransparentBackground();
break;
}
}
return animations;
}
private void ApplyNonAnimatedTheme()
{
switch (Theme)
{
case Themes.Color:
SetActiveIconResource("ColorBrush4");
break;
case Themes.White:
SetActiveIconBrush(new ModBase.MyColor(234d, 242d, 254d));
break;
case Themes.Red:
SetActiveIconBrush(new ModBase.MyColor(160d, 255d, 76d, 76d));
break;
case Themes.Black:
SetActiveIconBrush(ThemeManager.IsDarkMode
? new ModBase.MyColor(160d, 255d, 255d, 255d)
: new ModBase.MyColor(160d, 0d, 0d, 0d));
break;
case Themes.Custom:
SetActiveIconBrush(new ModBase.MyColor(160d, Foreground));
break;
}
PanBack.Background = GetTransparentBackground();
}
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
{
if (!isMouseDown)
return;
ModBase.Log("[Control] 按下图标按钮" + (string.IsNullOrEmpty(Name) ? "" : "" + Name));
Click?.Invoke(sender, e);
e.Handled = true;
Button_MouseUp();
ModMain.RaiseCustomEvent(this);
}
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
isMouseDown = true;
Focus();
// 指向
ModAnimation.AniStart(
ModAnimation.AaScaleTransform(PanBack, 0.8d - ((ScaleTransform)PanBack.RenderTransform).ScaleX,
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Strong)),
"MyIconButton Scale " + Uuid);
}
private void Button_MouseUp()
{
if (isMouseDown)
{
isMouseDown = false;
ModAnimation.AniStart(
new[]
{
ModAnimation.AaScaleTransform(PanBack, 1.05d - ((ScaleTransform)PanBack.RenderTransform).ScaleX,
250, ease: new ModAnimation.AniEaseOutBack(ModAnimation.AniEasePower.Weak)),
ModAnimation.AaScaleTransform(PanBack, -0.05d, 250,
ease: new ModAnimation.AniEaseOutFluent(ModAnimation.AniEasePower.Strong))
}, "MyIconButton Scale " + Uuid);
}
RefreshAnim(); // 直接刷新颜色以判断是否已触发 MouseLeave
}
private void Button_MouseLeave()
{
isMouseDown = false;
ModAnimation.AniStart(
new[]
{
ModAnimation.AaScaleTransform(PanBack, 1d - ((ScaleTransform)PanBack.RenderTransform).ScaleX, 250,
ease: new ModAnimation.AniEaseOutFluent())
}, "MyIconButton Scale " + Uuid);
RefreshAnim(); // 直接刷新颜色以判断是否已触发 MouseLeave
}
public void RefreshAnim()
{
try
{
if (ControlVisualHelpers.ShouldAnimate(this)) // 防止默认属性变更触发动画
{
EnsureBaseBrushes();
ModAnimation.AniStart(IsMouseOver ? GetHoverAnimations() : GetNormalAnimations(), ColorAnimationKey);
}
else
{
ModAnimation.AniStop(ColorAnimationKey);
ApplyNonAnimatedTheme();
}
}
catch (Exception ex)
{
ModBase.Log(ex, "刷新图标按钮动画状态出错");
}
}
}
public static partial class ModAnimation
{
public static void AniDispose(MyIconButton control, bool removeFromChildren,
ParameterizedThreadStart callBack = null)
{
if (!control.IsHitTestVisible)
return;
control.IsHitTestVisible = false;
AniStart(new[]
{
AaScaleTransform(control, -1.5d, 200, ease: new AniEaseInFluent()),
AaCode(() =>
{
if (removeFromChildren)
((Panel)control.Parent).Children.Remove(control);
else
control.Visibility = Visibility.Collapsed;
if (callBack is not null)
callBack(control);
}, after: true)
}, "MyIconButton Dispose " + control.Uuid);
}
}