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