diff --git a/PCL.Core.csproj b/PCL.Core.csproj index aefd73c0..58a86ce2 100644 --- a/PCL.Core.csproj +++ b/PCL.Core.csproj @@ -69,14 +69,11 @@ - + - + - + diff --git a/UI/Animation/AniData.cs b/UI/Animation/AniData.cs new file mode 100644 index 00000000..7d677d8a --- /dev/null +++ b/UI/Animation/AniData.cs @@ -0,0 +1,71 @@ +using System; + +namespace PCL.Core.UI.Animation; + +/// +/// 单个动画对象。 +/// +/// +public struct AniData +{ + + /// + /// 动画种类。 + /// + /// + public AniType TypeMain; + /// + /// 动画副种类。 + /// + /// + public AniTypeExt TypeSub; + + /// + /// 动画总长度。 + /// + /// + public int TimeTotal; + /// + /// 已经执行的动画长度。如果为负数则为延迟。 + /// + /// + public int TimeFinished; + /// + /// 已经完成的百分比。 + /// + /// + public double TimePercent; + + /// + /// 是否为“以后”。 + /// + /// + public bool IsAfter; + + /// + /// 插值器类型。 + /// + /// + public AniEase Ease; + /// + /// 动画对象。 + /// + /// + public object Obj; + /// + /// 动画值。 + /// + /// + public object Value; + /// + /// 上次执行时的动画值。 + /// + /// + public object ValueLast; + + public override readonly string ToString() + { + Enum enumData = (Enum)TypeMain; + return Enum.GetName(enumData.GetType(), enumData) + " | " + TimeFinished + "/" + TimeTotal + "(" + Math.Round(TimePercent * 100d) + "%)" + (Obj is null ? "" : " | " + Obj.ToString() + "(" + Obj.GetType().Name + ")"); + } +} \ No newline at end of file diff --git a/UI/Animation/AniEase.cs b/UI/Animation/AniEase.cs new file mode 100644 index 00000000..d0b93342 --- /dev/null +++ b/UI/Animation/AniEase.cs @@ -0,0 +1,195 @@ +using System; +using PCL.Core.Utils; + +namespace PCL.Core.UI.Animation; + +// 基类 +public enum AniEasePower : int +{ + Weak = 2, + Middle = 3, + Strong = 4, + ExtraStrong = 5 +} + +/// +/// 缓动函数基类。 +/// +public abstract class AniEase +{ + /// + /// 获取函数值。 + /// + /// 时间百分比。 + public abstract double GetValue(double t); + /// + /// 获取增量值。 + /// + /// 较大的 X。 + /// 较小的 X。 + public virtual double GetDelta(double t1, double t0) + => GetValue(t1) - GetValue(t0); +} + +/// +/// 渐入渐出组合。 +/// +public class AniEaseInout(AniEase easeIn, AniEase easeOut, double easeInPercent = 0.5d) : AniEase +{ + private readonly AniEase _easeIn = easeIn; + private readonly AniEase _easeOut = easeOut; + private readonly double _easeInPercent = easeInPercent; + + public override double GetValue(double t) + { + if (t < _easeInPercent) + return _easeInPercent * _easeIn.GetValue(t / _easeInPercent); + return (1d - _easeInPercent) * _easeOut.GetValue((t - _easeInPercent) / (1d - _easeInPercent)) + _easeInPercent; + } +} + +// Linear / 线性 +/// +/// 线性,无缓动。 +/// +public class AniEaseLinear : AniEase +{ + public override double GetValue(double t) + => MathUtils.Clamp(t, 0d, 1d); + public override double GetDelta(double t1, double t0) + => MathUtils.Clamp(t1, 0d, 1d) - MathUtils.Clamp(t0, 0d, 1d); +} + +// Fluent / 平滑 +/// +/// 平滑开始。 +/// +public class AniEaseInFluent(AniEasePower power = AniEasePower.Middle) : AniEase +{ + private readonly AniEasePower _p = power; + + public override double GetValue(double t) + => Math.Pow(MathUtils.Clamp(t, 0d, 1d), (double)_p); +} +/// +/// 平滑结束。 +/// +public class AniEaseOutFluent(AniEasePower power = AniEasePower.Middle) : AniEase +{ + private readonly AniEasePower _p = power; + + public override double GetValue(double t) + => 1d - Math.Pow(MathUtils.Clamp(1d - t, 0d, 1d), (double)_p); +} +/// +/// 平滑开始与结束。 +/// +public class AniEaseInoutFluent(AniEasePower power = AniEasePower.Middle, double middle = 0.5d) : AniEase +{ + private AniEaseInout _ease = new(new AniEaseInFluent(power), new AniEaseOutFluent(power), middle); + + public override double GetValue(double t) => _ease.GetValue(t); +} +/// +/// 以特定速度开始的平滑结束。 +/// +public class AniEaseOutFluentWithInitial : AniEase +{ + private readonly double _alpha; // (初速度 / 平均速度) – 1 + + /// 初速度,px/s + /// 总时长,s + /// 总路程,px + public AniEaseOutFluentWithInitial(double initialPixelPerSecond, double totalSecond, double totalDistance) + { + double v0_norm = initialPixelPerSecond * totalSecond / totalDistance; // 归一化初速度 + _alpha = v0_norm - 1.0d; + if (_alpha < 0d) + _alpha = 0d; // 初速度小于平均速度时,退化为线性 + } + public override double GetValue(double percent) + { + double p = MathUtils.Clamp(percent, 0d, 1d); + if (_alpha == 0d) + return p; // 退化到线性 + return (_alpha + 1d) * p / (1d + _alpha * p); + } +} + +// Back / 回弹 +/// +/// 回弹开始。有效时间为 1/3。 +/// +public class AniEaseInBack(AniEasePower power = AniEasePower.Middle) : AniEase +{ + private readonly double _p = 3d - (double)power * 0.5d; + + public override double GetValue(double t) + { + t = MathUtils.Clamp(t, 0d, 1d); + return Math.Pow(t, _p) * Math.Cos(1.5d * Math.PI * (1d - t)); + } +} +/// +/// 回弹结束。有效时间为 1/3。 +/// +public class AniEaseOutBack(AniEasePower power = AniEasePower.Middle) : AniEase +{ + private readonly double _p = 3d - (double)power * 0.5d; + + public override double GetValue(double t) + { + t = MathUtils.Clamp(t, 0d, 1d); + return 1d - Math.Pow(1d - t, _p) * Math.Cos(1.5d * Math.PI * t); + } +} + +// Car / 平滑-回弹 +/// +/// 回弹开始,短平滑结束。 +/// +public class AniEaseInCar(double middle = 0.7d, AniEasePower power = AniEasePower.Middle) : AniEase +{ + private AniEaseInout _ease = new(new AniEaseInBack(power), new AniEaseOutFluent(power), middle); + + public override double GetValue(double t) + => _ease.GetValue(t); +} +/// +/// 短平滑开始,回弹结束。 +/// +public class AniEaseOutCar(double middle = 0.3d, AniEasePower power = AniEasePower.Middle) : AniEase +{ + private AniEaseInout _ease = new AniEaseInout(new AniEaseInFluent(power), new AniEaseOutBack(power), middle); + + public override double GetValue(double t) + => _ease.GetValue(t); +} + +// Elastic / 弹簧 +/// +/// 弹簧开始。约在 60% 到达最小值。 +/// +public class AniEaseInElastic(AniEasePower power = AniEasePower.Middle) : AniEase +{ + private readonly int _p = (int)power + 4; // 6~9 + + public override double GetValue(double t) + { + t = MathUtils.Clamp(t, 0d, 1d); + return Math.Pow(t, (_p - 1) * 0.25d) * Math.Cos((_p - 3.5d) * Math.PI * Math.Pow(1d - t, 1.5d)); + } +} +/// +/// 弹簧结束。约在 40% 到达最大值。 +/// +public class AniEaseOutElastic(AniEasePower power = AniEasePower.Middle) : AniEase +{ + private readonly int _p = (int)power + 4; + + public override double GetValue(double t) + { + t = 1d - MathUtils.Clamp(t, 0d, 1d); + return 1d - Math.Pow(t, (_p - 1) * 0.25d) * Math.Cos((_p - 3.5d) * Math.PI * Math.Pow(1d - t, 1.5d)); + } +} \ No newline at end of file diff --git a/UI/Animation/AniType.cs b/UI/Animation/AniType.cs new file mode 100644 index 00000000..c09985a6 --- /dev/null +++ b/UI/Animation/AniType.cs @@ -0,0 +1,62 @@ +namespace PCL.Core.UI.Animation; + +/// +/// 动画基础种类。 +/// +public enum AniType +{ + /// + /// 单个Double的动画,包括位置、长宽、透明度等。这需要附属类型。 + /// + /// + Number, + /// + /// 颜色属性的动画。这需要附属类型。 + /// + /// + Color, + /// + /// 缩放控件大小。比起4个DoubleAnimation来说效率更高。 + /// + /// + Scale, + /// + /// 文字一个个出现。 + /// + /// + TextAppear, + /// + /// 执行代码。 + /// + /// + Code, + /// + /// 以 WPF 方式缩放控件。 + /// + ScaleTransform, + /// + /// 以 WPF 方式旋转控件。 + /// + RotateTransform +} + +/// +/// 动画扩展种类。 +/// +public enum AniTypeExt +{ + X, + Y, + Width, + Height, + Opacity, + Value, + Radius, + BorderThickness, + StrokeThickness, + TranslateX, + TranslateY, + Double, + DoubleParam, + GridLengthWidth +} diff --git a/UI/Logo.cs b/UI/Logo.cs new file mode 100644 index 00000000..e84fba20 --- /dev/null +++ b/UI/Logo.cs @@ -0,0 +1,123 @@ +namespace PCL.Core.UI; + +public static class Logo +{ + /// + /// 图标按钮,心(空心),1.1x + /// + public const string IconButtonLikeLine = "M512 896a42.666667 42.666667 0 0 1-30.293333-12.373333l-331.52-331.946667a224.426667 224.426667 0 0 1 0-315.733333 223.573333 223.573333 0 0 1 315.733333 0L512 282.026667l46.08-46.08a223.573333 223.573333 0 0 1 315.733333 0 224.426667 224.426667 0 0 1 0 315.733333l-331.52 331.946667A42.666667 42.666667 0 0 1 512 896zM308.053333 256a136.533333 136.533333 0 0 0-97.28 40.106667 138.24 138.24 0 0 0 0 194.986666L512 792.746667l301.226667-301.653334a138.24 138.24 0 0 0 0-194.986666 141.653333 141.653333 0 0 0-194.56 0l-76.373334 76.8a42.666667 42.666667 0 0 1-60.586666 0L405.333333 296.106667A136.533333 136.533333 0 0 0 308.053333 256z"; + /// + /// 图标按钮,心(实心),1.1x + /// + public const string IconButtonLikeFill = "M700.856 155.543c-74.769 0-144.295 72.696-190.046 127.26-45.737-54.576-115.247-127.26-190.056-127.26-134.79 0-244.443 105.78-244.443 235.799 0 77.57 39.278 131.988 70.845 175.713C238.908 694.053 469.62 852.094 479.39 858.757c9.41 6.414 20.424 9.629 31.401 9.629 11.006 0 21.998-3.215 31.398-9.63 9.782-6.662 240.514-164.703 332.238-291.701 31.587-43.724 70.874-98.143 70.874-175.713-0.001-130.02-109.656-235.8-244.445-235.8z m0 0"; + /// + /// 图标按钮,垃圾桶,1.1x + /// + public const string IconButtonDelete = "M520.192 0C408.43 0 317.44 82.87 313.563 186.734H52.736c-29.038 0-52.663 21.943-52.663 49.079s23.625 49.152 52.663 49.152h58.075v550.473c0 103.35 75.118 187.757 167.717 187.757h472.43c92.599 0 167.716-83.894 167.716-187.757V285.477h52.59c29.038 0 52.59-21.943 52.663-49.08-0.073-27.135-23.625-49.151-52.663-49.151H726.235C723.237 83.017 631.955 0 520.192 0zM404.846 177.957c3.803-50.03 50.176-89.015 107.447-89.015 57.197 0 103.57 38.985 106.788 89.015H404.92zM284.379 933.669c-33.353 0-69.997-39.351-69.997-95.525v-549.01H833.39v549.522c0 56.247-36.645 95.525-69.998 95.525H284.379v-0.512z M357.23 800.695a48.274 48.274 0 0 0 47.616-49.006V471.7a48.274 48.274 0 0 0-47.543-49.08 48.274 48.274 0 0 0-47.69 49.006V751.69c0 27.282 20.846 49.006 47.617 49.006z m166.62 0a48.274 48.274 0 0 0 47.688-49.006V471.7a48.274 48.274 0 0 0-47.689-49.08 48.274 48.274 0 0 0-47.543 49.006V751.69c0 27.282 21.431 49.006 47.543 49.006z m142.92 0a48.274 48.274 0 0 0 47.543-49.006V471.7a48.274 48.274 0 0 0-47.543-49.08 48.274 48.274 0 0 0-47.616 49.006V751.69c0 27.282 20.773 49.006 47.543 49.006z"; + /// + /// 图标按钮,禁止,1x + /// + public const string IconButtonStop = "M508 990.4c-261.6 0-474.4-212-474.4-474.4S246.4 41.6 508 41.6s474.4 212 474.4 474.4S769.6 990.4 508 990.4zM508 136.8c-209.6 0-379.2 169.6-379.2 379.2 0 209.6 169.6 379.2 379.2 379.2s379.2-169.6 379.2-379.2C887.2 306.4 717.6 136.8 508 136.8zM697.6 563.2 318.4 563.2c-26.4 0-47.2-21.6-47.2-47.2 0-26.4 21.6-47.2 47.2-47.2l379.2 0c26.4 0 47.2 21.6 47.2 47.2C744.8 542.4 724 563.2 697.6 563.2z"; + /// + /// 图标按钮,勾选,1x + /// + public const string IconButtonCheck = "M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m0 921.6a409.6 409.6 0 1 1 409.6-409.6 409.6 409.6 0 0 1-409.6 409.6z M716.8 339.968l-256 253.44L328.192 460.8A51.2 51.2 0 0 0 256 532.992l168.448 168.96a51.2 51.2 0 0 0 72.704 0l289.28-289.792A51.2 51.2 0 0 0 716.8 339.968z"; + /// + /// 图标按钮,笔,1x + /// + public const string IconButtonEdit = "M732.64 64.32C688.576 21.216 613.696 21.216 569.6 64.32L120.128 499.52c-17.6 12.896-26.432 30.144-30.848 51.68L32 870.048c0 25.856 8.8 56 26.432 73.248 17.632 17.216 17.632 48.704 88.64 48.704h13.248l326.08-56c22.016-4.32 39.68-12.928 52.864-30.176l449.472-435.2c22.048-21.536 35.264-47.36 35.264-77.536 0-30.176-13.216-56-35.264-77.568l-256.096-251.2zM139.712 903.776l56-326.912 311.04-295.136 267.104 269.44-310.976 295.168-323.168 57.44zM844.576 467.84l-273.984-260.672 61.856-59.84c8.832-8.512 26.528-8.512 39.776 0l234.24 226.496c4.384 4.288 8.832 12.8 8.832 17.088s-4.416 8.544-8.864 12.8l-61.856 64.128z"; + /// + /// 图标按钮,齿轮,1.1x + /// + public const string IconButtonSetup = "M651.946667 1001.813333c-22.186667 0-42.666667-10.24-61.44-27.306666-23.893333-23.893333-49.493333-35.84-75.093334-35.84-29.013333 0-56.32 11.946667-73.386666 30.72v3.413333c-17.066667 17.066667-42.666667 27.306667-66.56 27.306667h-6.826667c-6.826667 0-11.946667-1.706667-15.36-1.706667l-6.826667-1.706667c-64.853333-20.48-121.173333-54.613333-168.96-98.986666-29.013333-23.893333-37.546667-63.146667-25.6-95.573334 8.533333-23.893333 5.12-51.2-10.24-75.093333-15.36-27.306667-34.133333-40.96-59.733333-47.786667h-1.706667l-5.12-1.706666c-35.84-8.533333-61.44-34.133333-66.56-69.973334C1.706667 575.146667 0 537.6 0 512c0-32.426667 3.413333-63.146667 8.533333-93.866667v-6.826666l3.413334-8.533334c10.24-23.893333 23.893333-40.96 44.373333-51.2 5.12-3.413333 11.946667-6.826667 20.48-8.533333 27.306667-8.533333 51.2-25.6 63.146667-44.373333 13.653333-23.893333 17.066667-52.906667 10.24-81.92-11.946667-34.133333 0-71.68 30.72-93.866667 44.373333-37.546667 97.28-68.266667 158.72-93.866667l3.413333-1.706666c44.373333-13.653333 75.093333 3.413333 92.16 20.48 23.893333 23.893333 49.493333 35.84 75.093333 35.84 30.72 0 56.32-10.24 71.68-30.72l3.413334-3.413334c27.306667-27.306667 63.146667-35.84 93.866666-22.186666 63.146667 22.186667 117.76 54.613333 165.546667 97.28 29.013333 23.893333 37.546667 63.146667 25.6 95.573333-8.533333 23.893333-5.12 51.2 10.24 75.093333 15.36 27.306667 34.133333 40.96 59.733333 47.786667h1.706667l5.12 1.706667c35.84 8.533333 61.44 34.133333 66.56 71.68 6.826667 30.72 10.24 63.146667 11.946667 93.866666v3.413334c0 32.426667-3.413333 63.146667-8.533334 93.866666v6.826667l-3.413333 8.533333c-10.24 23.893333-23.893333 40.96-44.373333 51.2-5.12 3.413333-11.946667 6.826667-20.48 8.533334-27.306667 8.533333-51.2 25.6-63.146667 46.08-13.653333 23.893333-17.066667 52.906667-10.24 81.92 11.946667 35.84-1.706667 75.093333-30.72 95.573333-44.373333 35.84-95.573333 66.56-157.013333 92.16-15.36 3.413333-27.306667 3.413333-35.84 3.413333z m3.413333-83.626666z m1.706667 0zM517.12 853.333333c47.786667 0 93.866667 20.48 134.826667 59.733334 1.706667 1.706667 3.413333 1.706667 3.413333 3.413333 52.906667-22.186667 97.28-49.493333 136.533333-80.213333l1.706667-1.706667v-3.413333c-13.653333-52.906667-8.533333-104.106667 17.066667-148.48 23.893333-39.253333 64.853333-69.973333 114.346666-85.333334 1.706667 0 3.413333-1.706667 6.826667-6.826666 5.12-25.6 8.533333-51.2 8.533333-78.506667-1.706667-29.013333-3.413333-56.32-10.24-81.92v-5.12h-1.706666c-51.2-11.946667-90.453333-39.253333-119.466667-87.04-27.306667-44.373333-34.133333-100.693333-17.066667-148.48l-1.706666-1.706667h-3.413334c-39.253333-35.84-85.333333-63.146667-136.533333-80.213333H648.533333s-1.706667 1.706667-3.413333 1.706667c-32.426667 39.253333-80.213333 59.733333-136.533333 59.733333-47.786667 0-93.866667-20.48-134.826667-59.733333l-1.706667-1.706667h-1.706666c-54.613333 22.186667-98.986667 49.493333-136.533334 80.213333l-1.706666 1.706667v3.413333c13.653333 52.906667 8.533333 104.106667-17.066667 148.48-23.893333 39.253333-64.853333 69.973333-114.346667 85.333334-1.706667 0-3.413333 1.706667-6.826666 6.826666-6.826667 25.6-8.533333 51.2-8.533334 78.506667 0 30.72 3.413333 58.026667 6.826667 76.8l1.706667 5.12h1.706666c51.2 11.946667 90.453333 39.253333 119.466667 87.04 27.306667 44.373333 34.133333 100.693333 17.066667 148.48l1.706666 1.706667 1.706667 1.706666c37.546667 35.84 83.626667 63.146667 134.826667 80.213334 1.706667 0 3.413333 0 3.413333 1.706666h1.706667s1.706667 0 5.12-1.706666c34.133333-37.546667 81.92-59.733333 136.533333-59.733334z m-6.826667-146.773333c-110.933333 0-199.68-85.333333-199.68-196.266667 0-109.226667 87.04-196.266667 199.68-196.266666s199.68 85.333333 199.68 196.266666c-1.706667 109.226667-88.746667 196.266667-199.68 196.266667z m0-307.2c-63.146667 0-114.346667 49.493333-114.346666 110.933333 0 63.146667 49.493333 110.933333 114.346666 110.933334 30.72 0 59.733333-11.946667 80.213334-32.426667 20.48-20.48 32.426667-49.493333 32.426666-78.506667 0-63.146667-49.493333-110.933333-112.64-110.933333z"; + /// + /// 图标按钮,重置,0.9x + /// + public const string IconButtonReset = "M667.6817627 313.65283203l-45.28564454 55.76660156L858.06933594 391.27124023 787.61950684 165.93066406l-56.01379395 69.01611328A354.47387695 354.47387695 0 0 0 520.89892578 165.93066406C324.87536621 165.93066406 165.93066406 324.43041992 165.93066406 519.91015625c0 195.52917481 158.94470215 353.97949219 354.96826172 353.97949219a355.06713867 355.06713867 0 0 0 331.73217774-227.66418458 50.52612305 50.52612305 0 0 0-29.21813966-65.25878905 50.77331543 50.77331543 0 0 0-65.50598144 29.16870117A253.61938477 253.61938477 0 0 1 520.94836426 772.78796387c-140.05920411 0-253.61938477-113.21411133-253.61938477-252.87780762 0-139.61425781 113.56018067-252.82836914 253.61938477-252.82836914 53.59130859 0 104.46350098 16.61132813 146.73339843 46.57104492"; + /// + /// 图标按钮,刷新,0.85x + /// + public const string IconButtonRefresh = "M875.52 148.48C783.36 56.32 655.36 0 512 0 291.84 0 107.52 138.24 30.72 332.8l122.88 46.08C204.8 230.4 348.16 128 512 128c107.52 0 199.68 40.96 271.36 112.64L640 384h384V0L875.52 148.48zM512 896c-107.52 0-199.68-40.96-271.36-112.64L384 640H0v384l148.48-148.48C240.64 967.68 368.64 1024 512 1024c220.16 0 404.48-138.24 481.28-332.8L870.4 645.12C819.2 793.6 675.84 896 512 896z"; + /// + /// 图标按钮,软盘,1x + /// + public const string IconButtonSave = "M819.392 0L1024 202.752v652.16a168.96 168.96 0 0 1-168.832 168.768h-104.192a47.296 47.296 0 0 1-10.752 0H283.776a47.232 47.232 0 0 1-10.752 0H168.832A168.96 168.96 0 0 1 0 854.912V168.768A168.96 168.96 0 0 1 168.832 0h650.56z m110.208 854.912V242.112l-149.12-147.776H168.896c-41.088 0-74.432 33.408-74.432 74.432v686.144c0 41.024 33.344 74.432 74.432 74.432h62.4v-190.528c0-33.408 27.136-60.544 60.544-60.544h440.448c33.408 0 60.544 27.136 60.544 60.544v190.528h62.4c41.088 0 74.432-33.408 74.432-74.432z m-604.032 74.432h372.864v-156.736H325.568v156.736z m403.52-596.48a47.168 47.168 0 1 1 0 94.336H287.872a47.168 47.168 0 1 1 0-94.336h441.216z m0-153.728a47.168 47.168 0 1 1 0 94.4H287.872a47.168 47.168 0 1 1 0-94.4h441.216z"; + /// + /// 图标按钮,信息,1.05x + /// + public const string IconButtonInfo = "M512 917.333333c223.861333 0 405.333333-181.472 405.333333-405.333333S735.861333 106.666667 512 106.666667 106.666667 288.138667 106.666667 512s181.472 405.333333 405.333333 405.333333z m0 106.666667C229.226667 1024 0 794.773333 0 512S229.226667 0 512 0s512 229.226667 512 512-229.226667 512-512 512z m-32-597.333333h64a21.333333 21.333333 0 0 1 21.333333 21.333333v320a21.333333 21.333333 0 0 1-21.333333 21.333333h-64a21.333333 21.333333 0 0 1-21.333333-21.333333V448a21.333333 21.333333 0 0 1 21.333333-21.333333z m0-192h64a21.333333 21.333333 0 0 1 21.333333 21.333333v64a21.333333 21.333333 0 0 1-21.333333 21.333333h-64a21.333333 21.333333 0 0 1-21.333333-21.333333v-64a21.333333 21.333333 0 0 1 21.333333-21.333333z"; + /// + /// 图标按钮,列表,1x + /// + public const string IconButtonList = "M384 128h640v128H384zM160 192m-96 0a96 96 0 1 0 192 0 96 96 0 1 0-192 0ZM384 448h640v128H384zM160 512m-96 0a96 96 0 1 0 192 0 96 96 0 1 0-192 0ZM384 768h640v128H384zM160 832m-96 0a96 96 0 1 0 192 0 96 96 0 1 0-192 0Z"; + /// + /// 图标按钮,文件夹,1.15x + /// + public const string IconButtonOpen = "M889.018182 418.909091H884.363636V316.509091a93.090909 93.090909 0 0 0-99.607272-89.832727h-302.545455l-93.090909-76.334546A46.545455 46.545455 0 0 0 358.865455 139.636364H146.152727A93.090909 93.090909 0 0 0 46.545455 229.469091V837.818182a46.545455 46.545455 0 0 0 46.545454 46.545454 46.545455 46.545455 0 0 0 16.756364-3.258181 109.381818 109.381818 0 0 0 25.134545 3.258181h586.472727a85.178182 85.178182 0 0 0 87.04-63.301818l163.374546-302.545454a46.545455 46.545455 0 0 0 5.585454-21.876364A82.385455 82.385455 0 0 0 889.018182 418.909091z m-744.727273-186.181818h198.283636l93.09091 76.334545a46.545455 46.545455 0 0 0 29.323636 10.705455h319.301818a12.101818 12.101818 0 0 1 6.516364 0V418.909091H302.545455a85.178182 85.178182 0 0 0-87.04 63.301818L139.636364 622.778182V232.727273a19.549091 19.549091 0 0 1 6.516363 0z m578.094546 552.029091a27.461818 27.461818 0 0 0-2.792728 6.516363H154.530909l147.083636-272.290909a27.461818 27.461818 0 0 0 2.792728-6.981818h565.061818z"; + /// + /// 图标按钮,名片,1.1x + /// + public const string IconButtonCard = "M834.5 684.1c-31.2-70.4-98.9-120.9-179.1-127.3 63.5-8.5 112.6-63 112.6-128.8 0-71.8-58.2-130-130-130s-130 58.2-130 130c0 65.9 49 120.3 112.6 128.8-80.2 6.4-148 57-179.1 127.3-8.7 19.7 6 42 27.6 42 12.1 0 22.7-7.5 27.7-18.5 24.3-53.9 78.5-91.5 141.3-91.5s117 37.6 141.3 91.5c5 11.1 15.6 18.5 27.7 18.5 21.4 0 36.1-22.3 27.4-42zM567.9 427.9c0-38.6 31.4-70 70-70s70 31.4 70 70-31.4 70-70 70-70-31.4-70-70zM460.3 347.9H216.9c-16.6 0-30 13.4-30 30s13.4 30 30 30h243.3c16.6 0 30-13.4 30-30 0.1-16.5-13.4-30-29.9-30zM367.4 459.6H216.9c-16.6 0-30 13.4-30 30s13.4 30 30 30h150.4c16.6 0 30-13.4 30-30 0.1-16.6-13.4-30-29.9-30zM297.4 571.2H217c-16.6 0-30 13.4-30 30s13.4 30 30 30h80.4c16.6 0 30-13.4 30-30 0-16.5-13.5-30-30-30zM900 236v552H124V236h776m0-60H124c-33.1 0-60 26.9-60 60v552c0 33.1 26.9 60 60 60h776c33.1 0 60-26.9 60-60V236c0-33.1-26.9-60-60-60z"; + /// + /// 图标按钮,×,0.85x + /// + public const string IconButtonCross = "F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z"; + /// + /// 图标按钮,验证,1.1x + /// + public const string IconButtonAuth = "M511.488256 95.184408c35.310345 22.516742 95.184408 55.78011 167.34033 84.437781 75.738131 29.681159 148.405797 40.93953 191.392304 45.033483v353.615193c0 73.691154-50.662669 164.781609-136.123938 244.101949C649.65917 901.181409 558.568716 942.12094 512 942.12094c-46.568716 0-137.65917-40.93953-222.096952-119.748126C204.441779 742.54073 153.77911 651.450275 153.77911 577.247376v-353.103448c42.474763-4.093953 116.165917-15.352324 191.904048-45.545227 75.226387-30.192904 133.565217-63.456272 165.805098-83.414293M512 0c-4.093953 0-8.187906 1.535232-11.258371 3.582209l-14.84058 10.234882c-1.023488 0.511744-67.550225 47.592204-170.410794 88.531735-100.813593 39.916042-198.556722 41.963018-199.58021 41.963018l-25.075462 0.511744c-10.746627 0.511744-18.934533 8.187906-18.934533 18.422789v414.000999c0 216.97951 286.064968 446.24088 440.09995 446.24088s440.09995-229.261369 440.09995-445.729136V163.758121c0-10.234883-8.69965-18.422789-18.934533-18.422789l-24.563718-0.511744c-1.023488 0-98.766617-2.046977-199.58021-41.963018-103.372314-40.93953-170.410795-88.01999-170.922538-88.531734L523.258371 3.582209c-3.070465-2.558721-7.164418-3.582209-11.258371-3.582209z M743.308346 410.930535l-260.477761 260.477761c-15.864068 15.864068-41.963018 15.864068-57.827087 0l-144.823588-144.823588c-15.864068-15.864068-15.864068-41.963018 0-57.827087 8.187906-8.187906 18.422789-11.770115 29.169415-11.770115 10.234883 0 20.981509 4.093953 29.169416 11.770115l115.654173 115.654173L685.993003 352.591704c15.864068-15.864068 41.963018-15.864068 57.827087 0 15.352324 16.375812 15.352324 42.474763-0.511744 58.338831z"; + /// + /// 图标按钮,第三方 + /// + public const string IconButtonThirdparty = "M865.004 167.069c-10.794-9.687-24.91-15.085-39.579-15.085-1.383 0-2.629 0-4.013 0.139-0.831 0.139-10.102 0.692-24.771 0.692-24.218 0-71.408-1.522-116.107-12.178-57.708-13.7-124.411-77.083-143.785-89.675-9.687-6.227-21.034-9.41-32.244-9.41-11.21 0-22.42 3.182-32.244 9.41-2.353 1.522-72.1 73.484-140.324 89.675-44.699 10.655-92.72 12.178-116.938 12.178-14.53 0-23.941-0.554-24.771-0.692-1.246-0.139-2.629-0.139-3.875-0.139-14.67 0-28.924 5.396-39.717 15.085-11.763 10.655-18.405 25.325-18.405 40.825v140.048c0 517.846 351.089 584.411 366.034 587.040 3.46 0.554 6.782 0.831 10.241 0.831 3.46 0 6.918-0.276 10.241-0.831 14.946-2.629 368.663-69.33 368.663-587.040v-139.911c0.139-15.5-6.642-30.446-18.405-40.962v0zM825.425 348.080c0 476.883-320.783 531.961-320.783 531.961s-318.291-55.078-318.291-531.961v-140.048c0 0 10.933 0.831 28.785 0.831 30.446 0 81.648-2.214 130.777-13.839 80.403-19.098 158.731-97.564 158.731-97.564s81.787 78.466 162.19 97.564c49.129 11.625 99.501 13.839 129.946 13.839 17.714 0 28.785-0.831 28.785-0.831l-0.139 140.048zM463.405 491.173z M349.925 603.958l66.841-15.085c10.102 54.663 40.962 81.925 92.72 81.925 57.43-1.383 87.045-29.476 88.429-84.14 0-50.373-35.289-75.421-105.728-75.421-17.299 0-30.998 0-40.962 0v-51.757c10.102 0 20.757 0 32.382 0 66.149 0 99.916-25.187 101.3-75.421-1.383-45.945-26.571-69.747-75.421-71.132-48.85 0-77.635 26.571-86.215 79.85l-64.766-15.085c18.683-76.252 70.438-114.308 155.27-114.308 87.738 2.906 134.373 40.962 140.187 114.308-1.383 53.279-30.998 87.738-88.429 103.514 63.244 13.008 97.009 49.542 101.3 110.019-4.29 81.925-56.878 124.411-157.486 127.316-87.461 1.246-140.739-36.811-159.422-114.585z"; + /// + /// 图标按钮,用户,0.95x + /// + public const string IconButtonUser = "M660.338 528.065c63.61-46.825 105.131-121.964 105.131-206.83 0-141.7-115.29-256.987-256.997-256.987-141.706 0-256.998 115.288-256.998 256.987 0 85.901 42.52 161.887 107.456 208.562-152.1 59.92-260.185 207.961-260.185 381.077 0 21.276 17.253 38.53 38.53 38.53 21.278 0 38.53-17.254 38.53-38.53 0-183.426 149.232-332.671 332.667-332.671 1.589 0 3.113-0.207 4.694-0.244 0.8 0.056 1.553 0.244 2.362 0.244 183.434 0 332.664 149.245 332.664 332.671 0 21.276 17.255 38.53 38.533 38.53 21.277 0 38.53-17.254 38.53-38.53 0-174.885-110.354-324.13-264.917-382.809z m-331.803-206.83c0-99.22 80.72-179.927 179.935-179.927s179.937 80.708 179.937 179.927c0 99.203-80.721 179.91-179.937 179.91s-179.935-80.708-179.935-179.91z"; + /// + /// 图标按钮,盾牌,1x + /// + public const string IconButtonShield = "M511.488256 95.184408c35.310345 22.516742 95.184408 55.78011 167.34033 84.437781 75.738131 29.681159 148.405797 40.93953 191.392304 45.033483v353.615193c0 73.691154-50.662669 164.781609-136.123938 244.101949C649.65917 901.181409 558.568716 942.12094 512 942.12094c-46.568716 0-137.65917-40.93953-222.096952-119.748126C204.441779 742.54073 153.77911 651.450275 153.77911 577.247376v-353.103448c42.474763-4.093953 116.165917-15.352324 191.904048-45.545227 75.226387-30.192904 133.565217-63.456272 165.805098-83.414293M512 0c-4.093953 0-8.187906 1.535232-11.258371 3.582209l-14.84058 10.234882c-1.023488 0.511744-67.550225 47.592204-170.410794 88.531735-100.813593 39.916042-198.556722 41.963018-199.58021 41.963018l-25.075462 0.511744c-10.746627 0.511744-18.934533 8.187906-18.934533 18.422789v414.000999c0 216.97951 286.064968 446.24088 440.09995 446.24088s440.09995-229.261369 440.09995-445.729136V163.758121c0-10.234883-8.69965-18.422789-18.934533-18.422789l-24.563718-0.511744c-1.023488 0-98.766617-2.046977-199.58021-41.963018-103.372314-40.93953-170.410795-88.01999-170.922538-88.531734L523.258371 3.582209c-3.070465-2.558721-7.164418-3.582209-11.258371-3.582209z M743.308346 410.930535l-260.477761 260.477761c-15.864068 15.864068-41.963018 15.864068-57.827087 0l-144.823588-144.823588c-15.864068-15.864068-15.864068-41.963018 0-57.827087 8.187906-8.187906 18.422789-11.770115 29.169415-11.770115 10.234883 0 20.981509 4.093953 29.169416 11.770115l115.654173 115.654173L685.993003 352.591704c15.864068-15.864068 41.963018-15.864068 57.827087 0 15.352324 16.375812 15.352324 42.474763-0.511744 58.338831z"; + /// + /// 图标按钮,离线,0.85x + /// + public const string IconButtonOffline = "M533.293176 788.841412a60.235294 60.235294 0 1 1 85.202824 85.202823l-42.616471 42.586353c-129.355294 129.385412-339.124706 129.385412-468.510117 0-129.385412-129.385412-129.385412-339.124706 0-468.510117l42.586353-42.616471a60.235294 60.235294 0 1 1 85.202823 85.202824l-42.61647 42.586352a210.823529 210.823529 0 1 0 298.164706 298.164706l42.586352-42.61647z m255.548236-255.548236l42.61647-42.586352a210.823529 210.823529 0 1 0-298.164706-298.164706l-42.586352 42.61647a60.235294 60.235294 0 1 1-85.202824-85.202823l42.616471-42.586353c129.355294-129.385412 339.124706-129.385412 468.510117 0 129.385412 129.385412 129.385412 339.124706 0 468.510117l-42.586353 42.616471a60.235294 60.235294 0 1 1-85.202823-85.202824zM192.542118 192.542118a60.235294 60.235294 0 0 1 85.202823 0l553.712941 553.712941a60.235294 60.235294 0 0 1-85.202823 85.202823L192.542118 277.744941a60.235294 60.235294 0 0 1 0-85.202823z"; + /// + /// 图标,服务端,1x + /// + public const string IconButtonServer = "M224 160a64 64 0 0 0-64 64v576a64 64 0 0 0 64 64h576a64 64 0 0 0 64-64V224a64 64 0 0 0-64-64H224z m0 384h576v256H224v-256z m192 96v64h320v-64H416z m-128 0v64h64v-64H288zM224 224h576v256H224V224z m192 96v64h320v-64H416z m-128 0v64h64v-64H288z"; + /// + /// 图标按钮,复制 + /// + public const string IconButtonCopy = "M394.666667 106.666667h448a74.666667 74.666667 0 0 1 74.666666 74.666666v448a74.666667 74.666667 0 0 1-74.666666 74.666667H394.666667a74.666667 74.666667 0 0 1-74.666667-74.666667V181.333333a74.666667 74.666667 0 0 1 74.666667-74.666666z m0 64a10.666667 10.666667 0 0 0-10.666667 10.666666v448a10.666667 10.666667 0 0 0 10.666667 10.666667h448a10.666667 10.666667 0 0 0 10.666666-10.666667V181.333333a10.666667 10.666667 0 0 0-10.666666-10.666666H394.666667z m245.333333 597.333333a32 32 0 0 1 64 0v74.666667a74.666667 74.666667 0 0 1-74.666667 74.666666H181.333333a74.666667 74.666667 0 0 1-74.666666-74.666666V394.666667a74.666667 74.666667 0 0 1 74.666666-74.666667h74.666667a32 32 0 0 1 0 64h-74.666667a10.666667 10.666667 0 0 0-10.666666 10.666667v448a10.666667 10.666667 0 0 0 10.666666 10.666666h448a10.666667 10.666667 0 0 0 10.666667-10.666666v-74.666667z"; + /// + /// 图标按钮,外链 + /// + public const string IconButtonlink = "M433.230769 74.830769a43.323077 43.323077 0 0 1 0 86.646154l-236.307692 0.157539a35.446154 35.446154 0 0 0-35.446154 35.446153v630.153847a35.446154 35.446154 0 0 0 35.446154 35.446153h630.153846a35.446154 35.446154 0 0 0 35.446154-35.446153V590.769231a43.323077 43.323077 0 1 1 86.646154 0v236.425846a122.092308 122.092308 0 0 1-122.092308 122.092308H196.923077a122.092308 122.092308 0 0 1-122.092308-122.092308v-630.153846a122.092308 122.092308 0 0 1 122.092308-122.092308z m452.923077 0a63.015385 63.015385 0 0 1 63.015385 63.015385V354.461538a43.323077 43.323077 0 0 1-43.323077 43.323077l-4.726154-0.236307A43.323077 43.323077 0 0 1 862.523077 354.461538l-0.039385-131.702153-287.074461 287.15323-90.072616 90.072616a43.323077 43.323077 0 1 1-61.243077-61.243077l90.033231-90.072616 287.113846-287.192615H669.538462a43.323077 43.323077 0 0 1-43.08677-38.596923L626.215385 118.153846A43.323077 43.323077 0 0 1 669.538462 74.830769z"; + /// + /// 图标,音符,1x + /// + public const string IconMusic = "M348.293565 716.53287V254.797913c0-41.672348 28.004174-78.358261 68.919652-90.37913L815.994435 40.826435c62.775652-18.610087 125.907478 26.579478 125.907478 89.933913v539.158261c8.013913 42.25113-8.94887 89.177043-47.014956 127.109565a232.848696 232.848696 0 0 1-170.785392 65.758609c-61.885217-2.938435-111.081739-33.435826-129.113043-80.050087-18.031304-46.614261-2.137043-102.177391 41.672348-145.853218a232.848696 232.848696 0 0 1 170.785391-65.80313c21.014261 1.024 40.514783 5.164522 57.878261 12.065391V233.338435c0-12.109913-10.551652-20.034783-20.569044-20.034783a24.620522 24.620522 0 0 0-5.787826 0.934957L439.785739 338.18713a19.545043 19.545043 0 0 0-14.825739 19.144348v438.984348H423.846957c11.53113 43.987478-5.164522 94.208-45.412174 134.322087a232.848696 232.848696 0 0 1-170.785392 65.758609c-61.885217-2.938435-111.081739-33.435826-129.113043-80.050087-18.031304-46.614261-2.137043-102.177391 41.672348-145.853218a232.848696 232.848696 0 0 1 170.785391-65.80313c20.791652 1.024 40.069565 5.075478 57.299478 11.842783z"; + /// + /// 图标,播放,0.8x + /// + public const string IconPlay = "M803.904 463.936a55.168 55.168 0 0 1 0 96.128l-463.616 264.448C302.848 845.888 256 819.136 256 776.448V247.616c0-42.752 46.848-69.44 84.288-48.064l463.616 264.384z"; + /// + /// 图标,创建,0.9x + /// + public const string IconButtonCreate = "M103.331925 384.978025l25.805736 0L129.137661 161.847132c0-18.313088 14.905478-33.718963 33.718963-33.718963l0.969071 0 253.006318 0c10.82044 0 20.218484 4.797259 26.500561 12.257162l117.579929 126.753869 297.819966 0c18.297738 0 33.736359 15.179724 33.736359 33.977859l0 0.952698 0 82.909292 25.547863 0c18.538215 0 34.187637 15.179724 34.187637 33.977859 0 2.163269-0.469698 3.617387-0.469698 5.539156l-54.437843 432.971086c-1.210571 10.382465-7.007601 19.056008-14.968923 24.352641-6.249331 5.765307-14.680351 9.624195-23.595394 9.624195l-0.969071 0-694.906773 0c-9.155521 0-17.344017-3.858888-23.626094-9.155521-8.67252-5.765307-14.453177-14.939247-15.389502-25.758664L69.597613 423.040922c-2.165316-18.313088 10.868535-35.414581 29.665647-38.062897L103.331925 384.978025 103.331925 384.978025zM196.576609 384.978025 196.576609 384.978025l627.938546 0 0-49.625234L546.461371 335.352791l0 0c-9.400091 0-18.329461-4.117784-25.048489-11.110035L402.363486 196.067514 196.576609 196.067514 196.576609 384.978025 196.576609 384.978025zM879.469767 452.916347 879.469767 452.916347l-20.267603 0-0.469698 0-0.969071 0-694.906773 0-0.984421 0-20.218484 0 45.781696 366.728382 646.218888 0L879.469767 452.916347 879.469767 452.916347z"; + /// + /// 图标,分享,1x + /// + public const string IconButtonShare = "M768.704 703.616c-35.648 0-67.904 14.72-91.136 38.304l-309.152-171.712c9.056-17.568 14.688-37.184 14.688-58.272 0-12.576-2.368-24.48-5.76-35.936l304.608-189.152c22.688 20.416 52.384 33.184 85.216 33.184 70.592 0 128-57.408 128-128s-57.408-128-128-128-128 57.408-128 128c0 14.56 2.976 28.352 7.456 41.408l-301.824 187.392c-23.136-22.784-54.784-36.928-89.728-36.928-70.592 0-128 57.408-128 128 0 70.592 57.408 128 128 128 25.664 0 49.504-7.744 69.568-20.8l321.216 178.4c-3.04 10.944-5.184 22.208-5.184 34.08 0 70.592 57.408 128 128 128s128-57.408 128-128S839.328 703.616 768.704 703.616zM767.2 128.032c35.296 0 64 28.704 64 64s-28.704 64-64 64-64-28.704-64-64S731.904 128.032 767.2 128.032zM191.136 511.936c0-35.296 28.704-64 64-64s64 28.704 64 64c0 35.296-28.704 64-64 64S191.136 547.232 191.136 511.936zM768.704 895.616c-35.296 0-64-28.704-64-64s28.704-64 64-64 64 28.704 64 64S804 895.616 768.704 895.616z"; + /// + /// 图标,添加,1x + /// + public const string IconButtonAdd = "M512.277 954.412c-118.89 0-230.659-46.078-314.73-129.73S67.12 629.666 67.12 511.222s46.327-229.744 130.398-313.427 195.82-129.73 314.73-129.73 230.659 46.078 314.72 129.73S957.397 392.81 957.397 511.183 911.078 740.96 826.97 824.642s-195.8 129.77-314.692 129.77z m0-822.784c-101.972 0-197.809 39.494-269.865 111.222s-111.7 166.997-111.7 268.373 39.653 196.695 111.67 268.335S410.246 890.78 512.248 890.78s197.809-39.484 269.865-111.222 111.7-166.998 111.67-268.374c-0.03-101.375-39.654-196.665-111.67-268.303S614.22 131.628 512.277 131.628z m222.585 347.8H544.073V288.64c-0.76-17.561-15.613-31.18-33.173-30.419-16.495 0.714-29.704 13.924-30.419 30.419v190.787H289.703c-17.56 0.761-31.179 15.614-30.419 33.174 0.715 16.494 13.924 29.703 30.42 30.418H480.48v190.788c0.761 17.56 15.614 31.179 33.174 30.419 16.494-0.715 29.703-13.925 30.418-30.42V543.02h190.788c17.56 0.762 32.413-12.857 33.173-30.418 0.762-17.561-12.858-32.414-30.419-33.174a31.683 31.683 0 0 0-2.753 0z"; + /// + /// 图标,开始游戏,1x + /// + public const string IconPlayGame = "M213.333333 65.386667a85.333333 85.333333 0 0 1 43.904 12.16L859.370667 438.826667a85.333333 85.333333 0 0 1 0 146.346666L257.237333 946.453333A85.333333 85.333333 0 0 1 128 873.28V150.72a85.333333 85.333333 0 0 1 85.333333-85.333333z m0 64a21.333333 21.333333 0 0 0-21.184 18.837333L192 150.72v722.56a21.333333 21.333333 0 0 0 30.101333 19.456l2.197334-1.152L826.453333 530.282667a21.333333 21.333333 0 0 0 2.048-35.178667l-2.048-1.386667L224.298667 132.416A21.333333 21.333333 0 0 0 213.333333 129.386667z"; + public const string IconButtonEnable = "M512 0a512 512 0 1 0 512 512A512 512 0 0 0 512 0z m0 921.6a409.6 409.6 0 1 1 409.6-409.6 409.6 409.6 0 0 1-409.6 409.6z M716.8 339.968l-256 253.44L328.192 460.8A51.2 51.2 0 0 0 256 532.992l168.448 168.96a51.2 51.2 0 0 0 72.704 0l289.28-289.792A51.2 51.2 0 0 0 716.8 339.968z"; + public const string IconButtonDisable = "M508 990.4c-261.6 0-474.4-212-474.4-474.4S246.4 41.6 508 41.6s474.4 212 474.4 474.4S769.6 990.4 508 990.4zM508 136.8c-209.6 0-379.2 169.6-379.2 379.2 0 209.6 169.6 379.2 379.2 379.2s379.2-169.6 379.2-379.2C887.2 306.4 717.6 136.8 508 136.8zM697.6 563.2 318.4 563.2c-26.4 0-47.2-21.6-47.2-47.2 0-26.4 21.6-47.2 47.2-47.2l379.2 0c26.4 0 47.2 21.6 47.2 47.2C744.8 542.4 724 563.2 697.6 563.2z"; +} \ No newline at end of file diff --git a/UI/MyColor.cs b/UI/MyColor.cs new file mode 100644 index 00000000..a9f1e5d2 --- /dev/null +++ b/UI/MyColor.cs @@ -0,0 +1,239 @@ +using Microsoft.VisualBasic; +using PCL.Core.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Media; + +namespace PCL.Core.UI; + +/// +/// 支持小数与常见类型隐式转换的颜色。 +/// +public class MyColor +{ + + public double A = 255d; + public double R = 0d; + public double G = 0d; + public double B = 0d; + + // 类型转换 + public static implicit operator MyColor(string str) + => new(str); + public static implicit operator MyColor(Color col) + => new(col); + public static implicit operator Color(MyColor conv) + => Color.FromArgb(MathUtils.Byte(conv.A), MathUtils.Byte(conv.R), MathUtils.Byte(conv.G), MathUtils.Byte(conv.B)); + public static implicit operator System.Drawing.Color(MyColor conv) + => System.Drawing.Color.FromArgb(MathUtils.Byte(conv.A), MathUtils.Byte(conv.R), MathUtils.Byte(conv.G), MathUtils.Byte(conv.B)); + public static implicit operator MyColor(SolidColorBrush bru) + => new(bru.Color); + public static implicit operator SolidColorBrush(MyColor conv) + => new SolidColorBrush(Color.FromArgb(MathUtils.Byte(conv.A), MathUtils.Byte(conv.R), MathUtils.Byte(conv.G), MathUtils.Byte(conv.B))); + public static implicit operator MyColor(Brush bru) + => new(bru); + public static implicit operator Brush(MyColor conv) + => new SolidColorBrush(Color.FromArgb(MathUtils.Byte(conv.A), MathUtils.Byte(conv.R), MathUtils.Byte(conv.G), MathUtils.Byte(conv.B))); + + // 颜色运算 + public static MyColor operator +(MyColor a, MyColor b) + => new() { A = a.A + b.A, B = a.B + b.B, G = a.G + b.G, R = a.R + b.R }; + public static MyColor operator -(MyColor a, MyColor b) + => new() { A = a.A - b.A, B = a.B - b.B, G = a.G - b.G, R = a.R - b.R }; + public static MyColor operator *(MyColor a, double b) + => new() { A = a.A * b, B = a.B * b, G = a.G * b, R = a.R * b }; + public static MyColor operator /(MyColor a, double b) + => new() { A = a.A / b, B = a.B / b, G = a.G / b, R = a.R / b }; + public static bool operator ==(MyColor? a, MyColor? b) + { + if (a == null && b == null) + return true; + if (a == null || b == null) + return false; + return a.A == b.A && a.R == b.R && a.G == b.G && a.B == b.B; + } + public static bool operator !=(MyColor? a, MyColor? b) + { + if (a == null && b == null) + return false; + if (a == null || b == null) + return true; + return !(a.A == b.A && a.R == b.R && a.G == b.G && a.B == b.B); + } + + // 构造函数 + public MyColor() + { + } + public MyColor(Color col) + { + A = col.A; + R = col.R; + G = col.G; + B = col.B; + } + public MyColor(string hexString) + { + Color stringColor = (Color)ColorConverter.ConvertFromString(hexString); + A = stringColor.A; + R = stringColor.R; + G = stringColor.G; + B = stringColor.B; + } + public MyColor(double newA, MyColor col) + { + A = newA; + R = col.R; + G = col.G; + B = col.B; + } + public MyColor(double newR, double newG, double newB) + { + A = 255d; + R = newR; + G = newG; + B = newB; + } + public MyColor(double newA, double newR, double newG, double newB) + { + A = newA; + R = newR; + G = newG; + B = newB; + } + public MyColor(Brush brush) + { + var color = ((SolidColorBrush)brush).Color; + A = color.A; + R = color.R; + G = color.G; + B = color.B; + } + public MyColor(SolidColorBrush brush) + { + var color = brush.Color; + A = color.A; + R = color.R; + G = color.G; + B = color.B; + } + public MyColor(object obj) + { + if (obj is null) + { + A = 255d; + R = 255d; + G = 255d; + B = 255d; + } + else if (obj is SolidColorBrush) + { + // 避免反复获取 Color 对象造成性能下降 + var color = ((SolidColorBrush)obj).Color; + A = color.A; + R = color.R; + G = color.G; + B = color.B; + } + else + { + A = (double)((dynamic)obj).A; + R = (double)((dynamic)obj).R; + G = (double)((dynamic)obj).G; + B = (double)((dynamic)obj).B; + } + } + + // HSL + public double Hue(double v1, double v2, double vH) + { + if (vH < 0d) + vH += 1d; + if (vH > 1d) + vH -= 1d; + if (vH < 0.16667d) + return v1 + (v2 - v1) * 6d * vH; + if (vH < 0.5d) + return v2; + if (vH < 0.66667d) + return v1 + (v2 - v1) * (4d - vH * 6d); + return v1; + } + public MyColor FromHSL(double sH, double sS, double sL) + { + if (sS == 0d) + { + R = sL * 2.55d; + G = R; + B = R; + } + else + { + double h = sH / 360d; + double s = sS / 100d; + double l = sL / 100d; + s = l < 0.5d ? s * l + l : s * (1.0d - l) + l; + l = 2d * l - s; + R = 255d * Hue(l, s, h + 1d / 3d); + G = 255d * Hue(l, s, h); + B = 255d * Hue(l, s, h - 1d / 3d); + } + A = 255d; + return this; + } + public MyColor FromHSL2(double sH, double sS, double sL) + { + if (sS == 0d) + { + R = sL * 2.55d; + G = R; + B = R; + } + else + { + // 初始化 + sH = (sH + 3600000d) % 360d; + double[] cent = [+0.1d, -0.06d, -0.3d, -0.19d, -0.15d, -0.24d, -0.32d, -0.09d, +0.18d, +0.05d, -0.12d, -0.02d, +0.1d, -0.06d]; // 0, 30, 60 + // 90, 120, 150 + // 180, 210, 240 + // 270, 300, 330 + // 最后两位与前两位一致,加是变亮,减是变暗 + // 计算色调对应的亮度片区 + double center = sH / 30.0d; + int intCenter = (int)Math.Round(Math.Floor(center)); // 亮度片区编号 + center = 50d - ((1d - center + intCenter) * cent[intCenter] + (center - intCenter) * cent[intCenter + 1]) * sS; + // center = 50 + (cent(intCenter) + (center - intCenter) * (cent(intCenter + 1) - cent(intCenter))) * sS + sL = (sL < center ? sL / center : 1d + (sL - center) / (100d - center)) * 50d; + FromHSL(sH, sS, sL); + } + A = 255d; + return this; + } + + public MyColor Alpha(double sA) + { + A = sA; + return this; + } + + public override string ToString() + { + return "(" + A + "," + R + "," + G + "," + B + ")"; + } + + public override bool Equals(object? obj) + { + if (obj == null) + return false; + return this == (MyColor)obj; + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } +} + diff --git a/Utils/EventBus.cs b/Utils/EventBus.cs new file mode 100644 index 00000000..006fed07 --- /dev/null +++ b/Utils/EventBus.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Security; +using System.Reflection; + +namespace PCL.Core.Utils; + +public class EventBus +{ + private static EventBus _instance = new(); + private Dictionary> _eventMap = new(); + + public static ref EventBus GetInstance() + { + return ref _instance; + } + + public EventBus On(string channel, Delegate handler) + { + if (!_eventMap.TryGetValue(channel, out List? value)) + { + value = []; + _eventMap.Add(channel, value); + } + + value.Add(handler); + return this; + } + + [SecuritySafeCritical] + public EventBus Emit(string channel, params object[] args) + { + var impl = typeof(Delegate).GetMethod("DynamicInvoke", BindingFlags.Default); + foreach (var handler in _eventMap[channel]) + { + impl?.Invoke(handler, args); + } + return this; + } + + public EventBus Off(string channel, Delegate handler) + { + _eventMap[channel].Remove(handler); + return this; + } +} + diff --git a/Utils/MathUtils.cs b/Utils/MathUtils.cs new file mode 100644 index 00000000..053b8582 --- /dev/null +++ b/Utils/MathUtils.cs @@ -0,0 +1,117 @@ +using PCL.Core.UI; +using System; +using System.Linq; + +namespace PCL.Core.Utils; + +public static class MathUtils +{ + /// + /// 将数值限定在某个范围内。 + /// + public static double Clamp(double value, double min, double max) + => Math.Max(min, Math.Min(max, value)); + + /// + /// 2~65 进制的转换。 + /// + public static string RadixConvert(string input, int fromRadix, int toRadix) + { + const string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/+="; + // 零与负数的处理 + if (string.IsNullOrEmpty(input)) + return "0"; + bool isNegative = input.StartsWith("-"); + if (isNegative) + input = input.TrimStart('-'); + // 转换为十进制 + long realNum = 0L; + long scale = 1L; + foreach (var digit in input.Reverse().Select(l => digits.IndexOf(l.ToString()))) + { + realNum += digit * scale; + scale *= fromRadix; + } + // 转换为指定进制 + string result = ""; + while (realNum > 0L) + { + int newNum = (int)(realNum % toRadix); + realNum = (long)Math.Round((realNum - newNum) / (double)toRadix); + result = digits[newNum] + result; + } + // 负数的结束处理与返回 + return (isNegative ? "-" : "") + result; + } + + /// + /// 计算二阶贝塞尔曲线。 + /// + public static double Bezier(double x, double x1, double y1, double x2, double y2, double acc = 0.01d) + { + if (x <= 0d || double.IsNaN(x)) + return 0d; + if (x >= 1d) + return 1d; + double a, b; + a = x; + do + { + b = 3 * a * ((0.33333333 + x1 - x2) * a * a + (x2 - 2 * x1) * a + x1); + a += (x - b) * 0.5; + } + while (Math.Abs(b - x) < acc); // 精度 + return 3 * a * ((0.33333333 + y1 - y2) * a * a + (y2 - 2 * y1) * a + y1); + } + + /// + /// 提供 MyColor 类型支持的 Math.Round。 + /// + public static MyColor Round(MyColor col, int w = 0) + { + return new MyColor() { A = Math.Round(col.A, w), R = Math.Round(col.R, w), G = Math.Round(col.G, w), B = Math.Round(col.B, w) }; + } + + /// + /// 将一个数字限制为 0~255 的 Byte 值。 + /// + public static byte Byte(double d) + { + if (d < 0d) + d = 0d; + if (d > 255d) + d = 255d; + return (byte)Math.Round(Math.Round(d)); + } + + /// + /// 获取两数间的百分比。小数点精确到 6 位。 + /// + /// + public static double Percent(double a, double b, double percent) + { + return Math.Round(a * (1d - percent) + b * percent, 6); // 解决 Double 计算错误 + } + + /// + /// 获取两颜色间的百分比,根据 RGB 计算。小数点精确到 6 位。 + /// + public static MyColor Percent(MyColor a, MyColor b, double percent) + { + return Round(a * (1d - percent) + b * percent, 6); // 解决Double计算错误 + } + + /// + /// 符号函数。 + /// + public static int Sgn(double value) + { + if (value == 0d) + return 0; + else if (value > 0d) + return 1; + else + return -1; + } +} + diff --git a/Utils/RandomHelper.cs b/Utils/RandomHelper.cs new file mode 100644 index 00000000..78d943d0 --- /dev/null +++ b/Utils/RandomHelper.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace PCL.Core.Utils; + +public static class RandomHelper +{ + private static readonly System.Random _Random = new(); + + /// + /// 随机选择其一。 + /// + public static T RandomOne(IList objects) + => objects[RandomInt(0, objects.Count - 1)]; + + /// + /// 取随机整数。 + /// + public static int RandomInt(int min, int max) + => (int)Math.Floor((max - min + 1) * _Random.NextDouble()); + + /// + /// 将数组随机打乱。 + /// + public static IList Shuffle(IList array) + { + T[] result = [..array]; + _Random.Shuffle(result); + return [..result]; + } +} +