From 81918b1cd8faba68b9f44e1a8d2ff4e83f7fef72 Mon Sep 17 00:00:00 2001 From: chinboy Date: Tue, 9 Sep 2025 10:39:33 +0800 Subject: [PATCH] Reconstruct Demension and add four arithmetic operations --- src/AtomUI.Base/Dimension.cs | 187 ++++++++++++++++-- src/AtomUI.Controls/Dialog/Dialog.cs | 35 +--- src/AtomUI.Controls/Dialog/DialogPlacement.cs | 19 -- .../Dialog/DialogPositionRequest.cs | 19 +- .../CustomDialogPlacement.cs | 5 +- .../DialogPositioning/IDialogPositioner.cs | 26 +-- .../ManagedDialogPositioner.cs | 52 +---- .../Dialog/OverlayDialogHost.cs | 18 -- src/AtomUI.Controls/Skeleton/SkeletonLine.cs | 4 +- 9 files changed, 214 insertions(+), 151 deletions(-) delete mode 100644 src/AtomUI.Controls/Dialog/DialogPlacement.cs diff --git a/src/AtomUI.Base/Dimension.cs b/src/AtomUI.Base/Dimension.cs index 6677fe86a..740c968ba 100644 --- a/src/AtomUI.Base/Dimension.cs +++ b/src/AtomUI.Base/Dimension.cs @@ -12,6 +12,14 @@ public enum DimensionUnitType public struct Dimension: IEquatable { + public DimensionUnitType UnitType => _type; + + public bool IsPercentage => _type == DimensionUnitType.Percentage; + + public bool IsAbsolute => _type == DimensionUnitType.Pixel; + + public double Value => _value; + private readonly DimensionUnitType _type; private readonly double _value; @@ -35,17 +43,131 @@ public struct Dimension: IEquatable _type = type; _value = value; } - - public DimensionUnitType UnitType => _type; - - public bool IsAbsolute => _type == DimensionUnitType.Pixel; - - public bool IsPercentage => _type == DimensionUnitType.Percentage; - - public bool IsPixel => _type == DimensionUnitType.Pixel; - - public double Value => _value; - + + public double Resolve(double referenceSize) + { + return _type switch + { + DimensionUnitType.Percentage => _value * referenceSize / 100.0, + DimensionUnitType.Pixel => _value, + _ => _value + }; + } + + private static void ValidateSameUnit(Dimension a, Dimension b) + { + if (a._type != b._type) + { + throw new InvalidOperationException("Cannot perform operation on dimensions with different units"); + } + } + + #region 自定义四则运算符 + + public static Dimension operator +(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return new Dimension(a._value + b._value, a._type); + } + + public static Dimension operator +(Dimension a, double b) + { + return new Dimension(a._value + b, a._type); + } + + public static Dimension operator +(double a, Dimension b) + { + return new Dimension(a + b._value, b._type); + } + + public static Dimension operator -(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return new Dimension(a._value - b._value, a._type); + } + + public static Dimension operator -(Dimension a, double b) + { + return new Dimension(a._value - b, a._type); + } + + public static Dimension operator -(double a, Dimension b) + { + return new Dimension(a - b._value, b._type); + } + + public static Dimension operator *(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return new Dimension(a._value * b._value, a._type); + } + + public static Dimension operator *(Dimension a, double b) + { + return new Dimension(a._value * b, a._type); + } + + public static Dimension operator *(double a, Dimension b) + { + return new Dimension(a * b._value, b._type); + } + + public static Dimension operator /(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + if (Math.Abs(b._value) < double.Epsilon) + { + throw new DivideByZeroException("Division by zero"); + } + return new Dimension(a._value / b._value, a._type); + } + + public static Dimension operator /(Dimension a, double b) + { + if (Math.Abs(b) < double.Epsilon) + { + throw new DivideByZeroException("Division by zero"); + } + return new Dimension(a._value / b, a._type); + } + + public static Dimension operator /(double a, Dimension b) + { + if (Math.Abs(b._value) < double.Epsilon) + { + throw new DivideByZeroException("Division by zero"); + } + return new Dimension(a / b._value, b._type); + } + + public static Dimension operator %(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + if (Math.Abs(b._value) < double.Epsilon) + { + throw new DivideByZeroException("Modulo by zero"); + } + return new Dimension(a._value % b._value, a._type); + } + + public static Dimension operator %(Dimension a, double b) + { + if (Math.Abs(b) < double.Epsilon) + { + throw new DivideByZeroException("Modulo by zero"); + } + return new Dimension(a._value % b, a._type); + } + + public static Dimension operator +(Dimension a) => a; + + public static Dimension operator -(Dimension a) + { + return new Dimension(-a._value, a._type); + } + + #endregion + public static bool operator ==(Dimension a, Dimension b) { return (MathUtils.AreClose(a._value, b._value) && a._type == b._type); @@ -56,6 +178,30 @@ public struct Dimension: IEquatable return !(gl1 == gl2); } + public static bool operator <(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return a._value < b._value; + } + + public static bool operator >(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return a._value > b._value; + } + + public static bool operator <=(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return a._value <= b._value; + } + + public static bool operator >=(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return a._value >= b._value; + } + public override bool Equals(object? o) { if (o == null) @@ -78,7 +224,7 @@ public struct Dimension: IEquatable public override int GetHashCode() { - return _value.GetHashCode() ^ _type.GetHashCode(); + return HashCode.Combine(_value, _type); } public override string ToString() @@ -123,4 +269,21 @@ public struct Dimension: IEquatable return result; } + + public static Dimension Min(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return new Dimension(Math.Min(a._value, b._value), a._type); + } + public static Dimension Max(Dimension a, Dimension b) + { + ValidateSameUnit(a, b); + return new Dimension(Math.Max(a._value, b._value), a._type); + } + public static Dimension Clamp(Dimension value, Dimension min, Dimension max) + { + ValidateSameUnit(value, min); + ValidateSameUnit(value, max); + return new Dimension(Math.Clamp(value._value, min._value, max._value), value._type); + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/Dialog/Dialog.cs b/src/AtomUI.Controls/Dialog/Dialog.cs index d03b5118a..b99bc4975 100644 --- a/src/AtomUI.Controls/Dialog/Dialog.cs +++ b/src/AtomUI.Controls/Dialog/Dialog.cs @@ -58,12 +58,6 @@ public class Dialog : Control, public static readonly StyledProperty IsDragMovableProperty = AvaloniaProperty.Register(nameof (IsDragMovable), false); - public static readonly StyledProperty HorizontalPlacementProperty = - AvaloniaProperty.Register(nameof(HorizontalPlacement)); - - public static readonly StyledProperty VerticalPlacementProperty = - AvaloniaProperty.Register(nameof(VerticalPlacement)); - public static readonly StyledProperty PlacementRectProperty = AvaloniaProperty.Register(nameof(PlacementRect)); @@ -82,11 +76,11 @@ public class Dialog : Control, public static readonly StyledProperty IsLightDismissEnabledProperty = AvaloniaProperty.Register(nameof(IsLightDismissEnabled), false); - public static readonly StyledProperty HorizontalOffsetProperty = - AvaloniaProperty.Register(nameof(HorizontalOffset)); + public static readonly StyledProperty HorizontalOffsetProperty = + AvaloniaProperty.Register(nameof(HorizontalOffset)); - public static readonly StyledProperty VerticalOffsetProperty = - AvaloniaProperty.Register(nameof(VerticalOffset)); + public static readonly StyledProperty VerticalOffsetProperty = + AvaloniaProperty.Register(nameof(VerticalOffset)); public static readonly StyledProperty TopmostProperty = AvaloniaProperty.Register(nameof(Topmost)); @@ -163,18 +157,6 @@ public class Dialog : Control, set => SetValue(IsDragMovableProperty, value); } - public DialogHorizontalPlacement HorizontalPlacement - { - get => GetValue(HorizontalPlacementProperty); - set => SetValue(HorizontalPlacementProperty, value); - } - - public DialogVerticalPlacement VerticalPlacement - { - get => GetValue(VerticalPlacementProperty); - set => SetValue(VerticalPlacementProperty, value); - } - public Rect? PlacementRect { get => GetValue(PlacementRectProperty); @@ -206,13 +188,13 @@ public class Dialog : Control, set => SetValue(OverlayInputPassThroughElementProperty, value); } - public double HorizontalOffset + public Dimension HorizontalOffset { get => GetValue(HorizontalOffsetProperty); set => SetValue(HorizontalOffsetProperty, value); } - public double VerticalOffset + public Dimension VerticalOffset { get => GetValue(VerticalOffsetProperty); set => SetValue(VerticalOffsetProperty, value); @@ -749,9 +731,8 @@ public class Dialog : Control, { dialogHost.ConfigurePosition(new DialogPositionRequest( placementTarget, - HorizontalPlacement, - VerticalPlacement, - new Point(HorizontalOffset, VerticalOffset), + HorizontalOffset, + VerticalOffset, PlacementRect ?? new Rect(default, placementTarget.Bounds.Size), CustomDialogPlacementCallback)); } diff --git a/src/AtomUI.Controls/Dialog/DialogPlacement.cs b/src/AtomUI.Controls/Dialog/DialogPlacement.cs deleted file mode 100644 index d0ccf4a04..000000000 --- a/src/AtomUI.Controls/Dialog/DialogPlacement.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace AtomUI.Controls; - -[Flags] -public enum DialogHorizontalPlacement -{ - Left, - Center, - Right, - Custom -} - -[Flags] -public enum DialogVerticalPlacement -{ - Top, - Center, - Bottom, - Custom -} diff --git a/src/AtomUI.Controls/Dialog/DialogPositionRequest.cs b/src/AtomUI.Controls/Dialog/DialogPositionRequest.cs index 859c719a2..b5269008e 100644 --- a/src/AtomUI.Controls/Dialog/DialogPositionRequest.cs +++ b/src/AtomUI.Controls/Dialog/DialogPositionRequest.cs @@ -6,22 +6,21 @@ namespace AtomUI.Controls; public class DialogPositionRequest { internal DialogPositionRequest(Visual target, - DialogHorizontalPlacement horizontalPlacement, - DialogVerticalPlacement verticalPlacement, - Point offset, + Dimension horizontalOffset, + Dimension verticalOffset, Rect? anchorRect = null, CustomDialogPlacementCallback? placementCallback = null) { - Target = target; - HorizontalPlacement = horizontalPlacement; - VerticalPlacement = verticalPlacement; - Offset = offset; + Target = target; + HorizontalOffset = horizontalOffset; + VerticalOffset = verticalOffset; + AnchorRect = anchorRect; + PlacementCallback = placementCallback; } public Visual Target { get; } - public DialogHorizontalPlacement HorizontalPlacement { get; set; } - public DialogVerticalPlacement VerticalPlacement { get; set; } - public Point Offset { get; } + public Dimension HorizontalOffset { get; set; } + public Dimension VerticalOffset { get; set; } public Rect? AnchorRect { get; } public DialogPositionerConstraintAdjustment ConstraintAdjustment {get;} public CustomDialogPlacementCallback? PlacementCallback { get; } diff --git a/src/AtomUI.Controls/Dialog/DialogPositioning/CustomDialogPlacement.cs b/src/AtomUI.Controls/Dialog/DialogPositioning/CustomDialogPlacement.cs index f63b7936c..c44214ed3 100644 --- a/src/AtomUI.Controls/Dialog/DialogPositioning/CustomDialogPlacement.cs +++ b/src/AtomUI.Controls/Dialog/DialogPositioning/CustomDialogPlacement.cs @@ -4,12 +4,11 @@ namespace AtomUI.Controls.DialogPositioning; public record CustomDialogPlacement { - public DialogHorizontalPlacement HorizontalPlacement { get; set; } = DialogHorizontalPlacement.Center; - public DialogVerticalPlacement VerticalPlacement { get; set; } = DialogVerticalPlacement.Center; + public Dimension HorizontalOffset { get; set; } + public Dimension VerticalOffset { get; set; } public Size DialogSize { get; } public Visual Target { get; } - public Point Offset { get; set; } public DialogPositionerConstraintAdjustment ConstraintAdjustment { get; set; } public Rect AnchorRectangle { get; set; } diff --git a/src/AtomUI.Controls/Dialog/DialogPositioning/IDialogPositioner.cs b/src/AtomUI.Controls/Dialog/DialogPositioning/IDialogPositioner.cs index 1455193b9..3d719ea64 100644 --- a/src/AtomUI.Controls/Dialog/DialogPositioning/IDialogPositioner.cs +++ b/src/AtomUI.Controls/Dialog/DialogPositioning/IDialogPositioner.cs @@ -5,11 +5,10 @@ namespace AtomUI.Controls.DialogPositioning; public record struct DialogPositionerParameters { public Rect AnchorRectangle { get; set; } - public DialogHorizontalPlacement HorizontalPlacement { get; set; } - public DialogVerticalPlacement VerticalPlacement { get; set; } + public Dimension HorizontalOffset { get; set; } + public Dimension VerticalOffset { get; set; } public Size Size { get; set; } public DialogPositionerConstraintAdjustment ConstraintAdjustment { get; set; } - public Point Offset { get; set; } } public interface IDialogPositioner @@ -94,32 +93,27 @@ internal static class DialogPositionerExtensions Size dialogSize) { DialogPositionerParameters positionerParameters = default; - positionerParameters.Offset = positionRequest.Offset; + positionerParameters.HorizontalOffset = positionRequest.HorizontalOffset; + positionerParameters.VerticalOffset = positionRequest.VerticalOffset; positionerParameters.Size = dialogSize; positionerParameters.ConstraintAdjustment = positionRequest.ConstraintAdjustment; - if (positionRequest.HorizontalPlacement == DialogHorizontalPlacement.Custom || - positionRequest.VerticalPlacement == DialogVerticalPlacement.Custom) + + if (positionRequest.PlacementCallback != null) { - if (positionRequest.PlacementCallback is null) - throw new InvalidOperationException( - "CustomDialogPlacementCallback property must be set, when Placement=PlacementMode.Custom"); - var customPlacementParameters = new CustomDialogPlacement( dialogSize, positionRequest.Target) { - HorizontalPlacement = positionerParameters.HorizontalPlacement, - VerticalPlacement = positionerParameters.VerticalPlacement, + HorizontalOffset = positionerParameters.HorizontalOffset, + VerticalOffset = positionerParameters.VerticalOffset, ConstraintAdjustment = positionerParameters.ConstraintAdjustment, - Offset = positionerParameters.Offset }; positionRequest.PlacementCallback.Invoke(customPlacementParameters); - positionerParameters.HorizontalPlacement = customPlacementParameters.HorizontalPlacement; - positionerParameters.VerticalPlacement = customPlacementParameters.VerticalPlacement; + positionerParameters.HorizontalOffset = customPlacementParameters.HorizontalOffset; + positionerParameters.VerticalOffset = customPlacementParameters.VerticalOffset; positionerParameters.ConstraintAdjustment = customPlacementParameters.ConstraintAdjustment; - positionerParameters.Offset = customPlacementParameters.Offset; } return positionerParameters; } diff --git a/src/AtomUI.Controls/Dialog/DialogPositioning/ManagedDialogPositioner.cs b/src/AtomUI.Controls/Dialog/DialogPositioning/ManagedDialogPositioner.cs index 7e12d6086..7e48f8594 100644 --- a/src/AtomUI.Controls/Dialog/DialogPositioning/ManagedDialogPositioner.cs +++ b/src/AtomUI.Controls/Dialog/DialogPositioning/ManagedDialogPositioner.cs @@ -39,10 +39,9 @@ public class ManagedDialogPositioner : IDialogPositioner new Rect( parameters.AnchorRectangle.TopLeft * _dialog.Scaling, parameters.AnchorRectangle.Size * _dialog.Scaling), - parameters.HorizontalPlacement, - parameters.VerticalPlacement, - parameters.ConstraintAdjustment, - parameters.Offset * _dialog.Scaling); + parameters.HorizontalOffset, + parameters.VerticalOffset, + parameters.ConstraintAdjustment); _dialog.MoveAndResize( rect.Position, @@ -51,10 +50,9 @@ public class ManagedDialogPositioner : IDialogPositioner private Rect Calculate(Size translatedSize, Rect anchorRect, - DialogHorizontalPlacement horizontalPlacement, - DialogVerticalPlacement verticalPlacement, - DialogPositionerConstraintAdjustment constraintAdjustment, - Point offset) + Dimension horizontalOffset, + Dimension verticalOffset, + DialogPositionerConstraintAdjustment constraintAdjustment) { var parentGeometry = _dialog.ParentClientAreaScreenGeometry; anchorRect = anchorRect.Translate(parentGeometry.TopLeft); @@ -96,42 +94,8 @@ public class ManagedDialogPositioner : IDialogPositioner static bool IsValid(in Rect rc) => rc.Width > 0 && rc.Height > 0; - var offsetX = 0.0d; - var offsetY = 0.0d; - - if (horizontalPlacement == DialogHorizontalPlacement.Left) - { - offsetX = 0; - } - else if (horizontalPlacement == DialogHorizontalPlacement.Right) - { - offsetX = bounds.Right - translatedSize.Width; - } - else if (horizontalPlacement == DialogHorizontalPlacement.Center) - { - offsetX = (bounds.Width - translatedSize.Width) / 2; - } - else - { - offsetX = offset.X; - } - - if (verticalPlacement == DialogVerticalPlacement.Top) - { - offsetY = 0; - } - else if (verticalPlacement == DialogVerticalPlacement.Bottom) - { - offsetY = bounds.Bottom - translatedSize.Height; - } - else if (verticalPlacement == DialogVerticalPlacement.Center) - { - offsetY = (bounds.Height - translatedSize.Height) / 2; - } - else - { - offsetY = offset.Y; - } + var offsetX = horizontalOffset.Resolve(bounds.Width); + var offsetY = verticalOffset.Resolve(bounds.Height); Rect geo = new Rect(new Point(offsetX, offsetY), translatedSize); diff --git a/src/AtomUI.Controls/Dialog/OverlayDialogHost.cs b/src/AtomUI.Controls/Dialog/OverlayDialogHost.cs index 48391b96c..198105b8e 100644 --- a/src/AtomUI.Controls/Dialog/OverlayDialogHost.cs +++ b/src/AtomUI.Controls/Dialog/OverlayDialogHost.cs @@ -34,12 +34,6 @@ public sealed class OverlayDialogHost : ContentControl, public static readonly StyledProperty IsModalProperty = Dialog.IsModalProperty.AddOwner(); - public static readonly StyledProperty HorizontalPlacementProperty = - Dialog.HorizontalPlacementProperty.AddOwner(); - - public static readonly StyledProperty VerticalPlacementProperty = - Dialog.VerticalPlacementProperty.AddOwner(); - public static readonly StyledProperty TransformProperty = AvaloniaProperty.Register(nameof (Transform)); @@ -76,18 +70,6 @@ public sealed class OverlayDialogHost : ContentControl, set => SetValue(IsModalProperty, value); } - public DialogHorizontalPlacement HorizontalPlacement - { - get => GetValue(HorizontalPlacementProperty); - set => SetValue(HorizontalPlacementProperty, value); - } - - public DialogVerticalPlacement VerticalPlacement - { - get => GetValue(VerticalPlacementProperty); - set => SetValue(VerticalPlacementProperty, value); - } - public Transform? Transform { get => GetValue(TransformProperty); diff --git a/src/AtomUI.Controls/Skeleton/SkeletonLine.cs b/src/AtomUI.Controls/Skeleton/SkeletonLine.cs index c5159f612..7c1a419c1 100644 --- a/src/AtomUI.Controls/Skeleton/SkeletonLine.cs +++ b/src/AtomUI.Controls/Skeleton/SkeletonLine.cs @@ -36,7 +36,7 @@ public class SkeletonLine : AbstractSkeleton protected override Size MeasureOverride(Size availableSize) { var actualWidth = availableSize.Width; - if (LineWidth.IsPixel) + if (LineWidth.IsAbsolute) { actualWidth = LineWidth.Value; } @@ -48,7 +48,7 @@ public class SkeletonLine : AbstractSkeleton { var size = base.ArrangeOverride(finalSize); var actualWidth = finalSize.Width; - if (!LineWidth.IsPixel) + if (!LineWidth.IsAbsolute) { if (!double.IsInfinity(actualWidth)) {