diff --git a/AtomUI.sln.DotSettings b/AtomUI.sln.DotSettings
index 0a9b71f..e039917 100644
--- a/AtomUI.sln.DotSettings
+++ b/AtomUI.sln.DotSettings
@@ -23,4 +23,5 @@
True
True
True
+ True
True
\ No newline at end of file
diff --git a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBox.cs b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBox.cs
index 10f8e59..01d27af 100644
--- a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBox.cs
+++ b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBox.cs
@@ -117,6 +117,9 @@ public class ArrowDecoratedBox : ContentControl,
internal static readonly StyledProperty ArrowSizeProperty
= AvaloniaProperty.Register(nameof(ArrowSize));
+ internal static readonly StyledProperty ArrowDirectionProperty
+ = AvaloniaProperty.Register(nameof(ArrowDirection));
+
///
/// 箭头的大小
///
@@ -125,7 +128,13 @@ public class ArrowDecoratedBox : ContentControl,
get => GetValue(ArrowSizeProperty);
set => SetValue(ArrowSizeProperty, value);
}
-
+
+ internal Direction ArrowDirection
+ {
+ get => GetValue(ArrowDirectionProperty);
+ set => SetValue(ArrowDirectionProperty, value);
+ }
+
#endregion
// 指针最顶点位置
@@ -133,13 +142,19 @@ public class ArrowDecoratedBox : ContentControl,
private (double, double) _arrowVertexPoint;
internal (double, double) ArrowVertexPoint => GetArrowVertexPoint();
private Geometry? _arrowGeometry;
- private Rect _contentRect;
private Rect _arrowRect;
private bool _needGenerateArrowVertexPoint = true;
+ private Border? _contentDecorator;
static ArrowDecoratedBox()
{
- AffectsMeasure(ArrowPositionProperty, IsShowArrowProperty);
+ AffectsMeasure(IsShowArrowProperty);
+ }
+
+ public ArrowDecoratedBox()
+ {
+ IsShowArrow = true;
+ ArrowPosition = ArrowPosition.BottomEdgeAlignedLeft;
}
public static Direction GetDirection(ArrowPosition arrowPosition)
@@ -179,17 +194,19 @@ public class ArrowDecoratedBox : ContentControl,
public Rect GetMaskBounds()
{
- return GetContentRect(DesiredSize).Deflate(0.5);
+ if (_contentDecorator is not null)
+ {
+ return _contentDecorator.Bounds;
+ }
+
+ return Bounds;
}
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
- HandleTemplateApplied(e.NameScope);
- }
-
- private void HandleTemplateApplied(INameScope scope)
- {
+ _contentDecorator = e.NameScope.Get(ArrowDecoratedBoxTheme.ContentDecoratorPart);
if (IsShowArrow)
{
BuildGeometry(true);
@@ -219,14 +236,22 @@ public class ArrowDecoratedBox : ContentControl,
{
// 当开启的时候,但是还没有加入的渲染树,这个时候我们取不到 Token 需要在取值的时候重新生成一下
_needGenerateArrowVertexPoint = true;
+
}
if (VisualRoot is not null)
{
BuildGeometry(true);
_arrowRect = GetArrowRect(DesiredSize);
+
+ InvalidateArrange();
}
}
+
+ if (e.Property == ArrowPositionProperty)
+ {
+ ArrowDirection = GetDirection(ArrowPosition);
+ }
}
private void BuildGeometry(bool force = false)
@@ -241,9 +266,10 @@ public class ArrowDecoratedBox : ContentControl,
{
if (IsShowArrow)
{
+
var direction = GetDirection(ArrowPosition);
var matrix = Matrix.CreateTranslation(-ArrowSize / 2, -ArrowSize / 2);
-
+
if (direction == Direction.Right)
{
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(90));
@@ -263,97 +289,12 @@ public class ArrowDecoratedBox : ContentControl,
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(180));
matrix *= Matrix.CreateTranslation(ArrowSize / 2, ArrowSize / 2);
}
-
matrix *= Matrix.CreateTranslation(_arrowRect.X, _arrowRect.Y);
_arrowGeometry!.Transform = new MatrixTransform(matrix);
context.DrawGeometry(Background, null, _arrowGeometry);
}
}
-
- protected override Size MeasureOverride(Size availableSize)
- {
- var size = base.MeasureOverride(availableSize);
- var targetWidth = size.Width;
- var targetHeight = size.Height;
- targetHeight = Math.Max(MinHeight, targetHeight);
-
- if (IsShowArrow)
- {
- BuildGeometry();
- var realArrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width);
- var direction = GetDirection(ArrowPosition);
- if (direction == Direction.Left || direction == Direction.Right)
- {
- targetWidth += realArrowSize;
- }
- else
- {
- targetHeight += realArrowSize;
- }
- }
-
- var targetSize = new Size(targetWidth, targetHeight);
- _arrowRect = GetArrowRect(targetSize);
- return targetSize;
- }
-
- protected override Size ArrangeOverride(Size finalSize)
- {
- var visualChildren = VisualChildren;
- var visualCount = visualChildren.Count;
- _contentRect = GetContentRect(finalSize);
- for (var i = 0; i < visualCount; ++i)
- {
- var child = visualChildren[i];
- if (child is Layoutable layoutable)
- {
- layoutable.Arrange(_contentRect);
- }
- }
-
- return finalSize;
- }
-
- internal Rect GetContentRect(Size finalSize)
- {
- var offsetX = 0d;
- var offsetY = 0d;
- var targetWidth = finalSize.Width;
- var targetHeight = finalSize.Height;
- if (IsShowArrow)
- {
- var arrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width) + 0.5;
- var direction = GetDirection(ArrowPosition);
- if (direction == Direction.Left || direction == Direction.Right)
- {
- targetWidth -= arrowSize;
- }
- else
- {
- targetHeight -= arrowSize;
- }
-
- if (direction == Direction.Right)
- {
- offsetX = 0.5;
- }
- else if (direction == Direction.Bottom)
- {
- offsetY = 0.5;
- }
- else if (direction == Direction.Top)
- {
- offsetY = arrowSize - 0.5;
- }
- else
- {
- offsetX = arrowSize - 0.5;
- }
- }
-
- return new Rect(offsetX, offsetY, targetWidth, targetHeight);
- }
-
+
private Rect GetArrowRect(Size finalSize)
{
var offsetX = 0d;
@@ -371,6 +312,7 @@ public class ArrowDecoratedBox : ContentControl,
position == ArrowPosition.LeftEdgeAlignedTop ||
position == ArrowPosition.LeftEdgeAlignedBottom)
{
+ offsetX = 0.5d;
targetWidth = minValue;
targetHeight = maxValue;
if (position == ArrowPosition.Left)
@@ -404,6 +346,7 @@ public class ArrowDecoratedBox : ContentControl,
position == ArrowPosition.TopEdgeAlignedLeft ||
position == ArrowPosition.TopEdgeAlignedRight)
{
+ offsetY = 0.5d;
if (position == ArrowPosition.TopEdgeAlignedLeft)
{
offsetX = maxValue;
@@ -424,7 +367,7 @@ public class ArrowDecoratedBox : ContentControl,
position == ArrowPosition.RightEdgeAlignedTop ||
position == ArrowPosition.RightEdgeAlignedBottom)
{
- offsetX = finalSize.Width - minValue;
+ offsetX = finalSize.Width - minValue - 1.0d;
if (position == ArrowPosition.Right)
{
offsetY = (finalSize.Height - maxValue) / 2;
@@ -457,7 +400,7 @@ public class ArrowDecoratedBox : ContentControl,
}
else
{
- offsetY = finalSize.Height - minValue;
+ offsetY = finalSize.Height - minValue - 1.0d;
targetWidth = maxValue;
targetHeight = minValue;
if (position == ArrowPosition.BottomEdgeAlignedLeft)
diff --git a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs
index 4b7b038..21cc132 100644
--- a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs
+++ b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxTheme.cs
@@ -6,6 +6,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
+using Avalonia.Media;
using Avalonia.Styling;
namespace AtomUI.Controls;
@@ -13,8 +14,10 @@ namespace AtomUI.Controls;
[ControlThemeProvider]
internal class ArrowDecoratedBoxTheme : BaseControlTheme
{
- public const string DecoratorPart = "PART_Decorator";
+ public const string ContentDecoratorPart = "PART_ContentDecorator";
+ public const string ContentLayoutPart = "PART_ContentLayout";
public const string ContentPresenterPart = "PART_ContentPresenter";
+ public const string ArrowContentPart = "PART_ArrowContent";
public ArrowDecoratedBoxTheme() : this(typeof(ArrowDecoratedBox))
{
@@ -28,32 +31,48 @@ internal class ArrowDecoratedBoxTheme : BaseControlTheme
{
return new FuncControlTemplate((box, scope) =>
{
- var decorator = new Border
+ var contentLayout = new DockPanel()
{
- Name = DecoratorPart,
- Margin = new Thickness(0)
+ Name = ContentLayoutPart,
+ LastChildFill = true
};
- decorator.RegisterInNameScope(scope);
-
- decorator.Child = BuildContent(scope);
-
- CreateTemplateParentBinding(decorator, Border.BackgroundSizingProperty,
+ var arrowContent = new Control()
+ {
+ Name = ArrowContentPart
+ };
+ CreateTemplateParentBinding(arrowContent, Border.IsVisibleProperty,
+ ArrowDecoratedBox.IsShowArrowProperty);
+ contentLayout.Children.Add(arrowContent);
+ arrowContent.RegisterInNameScope(scope);
+
+ var content = BuildContent(scope);
+ var contentDecorator = new Border
+ {
+ Name = ContentDecoratorPart,
+ Margin = new Thickness(0)
+ };
+ contentDecorator.RegisterInNameScope(scope);
+; CreateTemplateParentBinding(contentDecorator, Border.BackgroundSizingProperty,
TemplatedControl.BackgroundSizingProperty);
- CreateTemplateParentBinding(decorator, Border.BackgroundProperty, TemplatedControl.BackgroundProperty);
- CreateTemplateParentBinding(decorator, Border.CornerRadiusProperty, TemplatedControl.CornerRadiusProperty);
- CreateTemplateParentBinding(decorator, Decorator.PaddingProperty, TemplatedControl.PaddingProperty);
+ CreateTemplateParentBinding(contentDecorator, Border.BackgroundProperty, TemplatedControl.BackgroundProperty);
+ CreateTemplateParentBinding(contentDecorator, Border.CornerRadiusProperty, TemplatedControl.CornerRadiusProperty);
+ CreateTemplateParentBinding(contentDecorator, Decorator.PaddingProperty, TemplatedControl.PaddingProperty);
- return decorator;
+ contentDecorator.Child = content;
+
+ contentLayout.Children.Add(contentDecorator);
+ return contentLayout;
});
}
-
+
protected virtual Control BuildContent(INameScope scope)
{
var contentPresenter = new ContentPresenter
{
Name = ContentPresenterPart
};
+
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentProperty, ContentControl.ContentProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentTemplateProperty,
ContentControl.ContentTemplateProperty);
@@ -69,6 +88,43 @@ internal class ArrowDecoratedBoxTheme : BaseControlTheme
commonStyle.Add(TemplatedControl.PaddingProperty, ArrowDecoratedBoxTokenResourceKey.Padding);
commonStyle.Add(ArrowDecoratedBox.ArrowSizeProperty, ArrowDecoratedBoxTokenResourceKey.ArrowSize);
commonStyle.Add(TemplatedControl.CornerRadiusProperty, GlobalTokenResourceKey.BorderRadius);
+ BuildArrowDirectionStyle(commonStyle);
Add(commonStyle);
}
+
+ private void BuildArrowDirectionStyle(Style commonStyle)
+ {
+ var topDirectionStyle = new Style(selector => selector.Nesting().PropertyEquals(ArrowDecoratedBox.ArrowDirectionProperty, Direction.Top));
+ {
+ var arrowContentStyle = new Style(selector => selector.Nesting().Template().Name(ArrowContentPart));
+ arrowContentStyle.Add(DockPanel.DockProperty, Dock.Top);
+ arrowContentStyle.Add(Control.HeightProperty, ArrowDecoratedBoxTokenResourceKey.ArrowContentThickness);
+ topDirectionStyle.Add(arrowContentStyle);
+ }
+ commonStyle.Add(topDirectionStyle);
+ var rightDirectionStyle = new Style(selector => selector.Nesting().PropertyEquals(ArrowDecoratedBox.ArrowDirectionProperty, Direction.Right));
+ {
+ var arrowContentStyle = new Style(selector => selector.Nesting().Template().Name(ArrowContentPart));
+ arrowContentStyle.Add(DockPanel.DockProperty, Dock.Right);
+ arrowContentStyle.Add(Control.WidthProperty, ArrowDecoratedBoxTokenResourceKey.ArrowContentThickness);
+ rightDirectionStyle.Add(arrowContentStyle);
+ }
+ commonStyle.Add(rightDirectionStyle);
+ var bottomDirectionStyle = new Style(selector => selector.Nesting().PropertyEquals(ArrowDecoratedBox.ArrowDirectionProperty, Direction.Bottom));
+ {
+ var arrowContentStyle = new Style(selector => selector.Nesting().Template().Name(ArrowContentPart));
+ arrowContentStyle.Add(DockPanel.DockProperty, Dock.Bottom);
+ arrowContentStyle.Add(Control.HeightProperty, ArrowDecoratedBoxTokenResourceKey.ArrowContentThickness);
+ bottomDirectionStyle.Add(arrowContentStyle);
+ }
+ commonStyle.Add(bottomDirectionStyle);
+ var leftDirectionStyle = new Style(selector => selector.Nesting().PropertyEquals(ArrowDecoratedBox.ArrowDirectionProperty, Direction.Left));
+ {
+ var arrowContentStyle = new Style(selector => selector.Nesting().Template().Name(ArrowContentPart));
+ arrowContentStyle.Add(DockPanel.DockProperty, Dock.Left);
+ arrowContentStyle.Add(Control.WidthProperty, ArrowDecoratedBoxTokenResourceKey.ArrowContentThickness);
+ leftDirectionStyle.Add(arrowContentStyle);
+ }
+ commonStyle.Add(leftDirectionStyle);
+ }
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxToken.cs b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxToken.cs
index 03560d5..1189cfc 100644
--- a/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxToken.cs
+++ b/src/AtomUI.Controls/ArrowDecoratedBox/ArrowDecoratedBoxToken.cs
@@ -12,6 +12,11 @@ public class ArrowDecoratedBoxToken : AbstractControlDesignToken
/// 箭头三角形大小
///
public double ArrowSize { get; set; }
+
+ ///
+ /// 绘制箭头的厚度,跟位置有关
+ ///
+ public double ArrowContentThickness { get; set; }
///
/// 默认的内边距
@@ -26,7 +31,8 @@ public class ArrowDecoratedBoxToken : AbstractControlDesignToken
internal override void CalculateFromAlias()
{
base.CalculateFromAlias();
- ArrowSize = _globalToken.SeedToken.SizePopupArrow / 1.3;
- Padding = new Thickness(_globalToken.PaddingXS);
+ ArrowSize = _globalToken.SeedToken.SizePopupArrow / 1.3;
+ ArrowContentThickness = ArrowSize / 2;
+ Padding = new Thickness(_globalToken.PaddingXS);
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/Flyouts/Flyout.cs b/src/AtomUI.Controls/Flyouts/Flyout.cs
index a6efdba..1dfb299 100644
--- a/src/AtomUI.Controls/Flyouts/Flyout.cs
+++ b/src/AtomUI.Controls/Flyouts/Flyout.cs
@@ -6,6 +6,7 @@ using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives.PopupPositioning;
+using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Metadata;
@@ -149,9 +150,9 @@ public class Flyout : PopupFlyoutBase
}
}
- var placement = Placement;
- var anchor = PlacementAnchor;
- var gravity = PlacementGravity;
+ var placement = popup.Placement;
+ var anchor = popup.PlacementAnchor;
+ var gravity = popup.PlacementGravity;
if (flyoutPresenter is not null)
{
@@ -178,11 +179,11 @@ public class Flyout : PopupFlyoutBase
protected internal override void NotifyPopupCreated(Popup popup)
{
base.NotifyPopupCreated(popup);
- BindUtils.RelayBind(this, PlacementProperty, popup, Avalonia.Controls.Primitives.Popup.PlacementProperty);
+ BindUtils.RelayBind(this, PlacementProperty, popup, Popup.PlacementProperty);
BindUtils.RelayBind(this, PlacementAnchorProperty, popup,
- Avalonia.Controls.Primitives.Popup.PlacementAnchorProperty);
+ Popup.PlacementAnchorProperty);
BindUtils.RelayBind(this, PlacementGravityProperty, popup,
- Avalonia.Controls.Primitives.Popup.PlacementGravityProperty);
+ Popup.PlacementGravityProperty);
BindUtils.RelayBind(this, MaskShadowsProperty, popup, Popup.MaskShadowsProperty);
SetupArrowPosition(popup);
}
diff --git a/src/AtomUI.Controls/Flyouts/MenuFlyoutPresenter.cs b/src/AtomUI.Controls/Flyouts/MenuFlyoutPresenter.cs
index cc82f82..dbbf87c 100644
--- a/src/AtomUI.Controls/Flyouts/MenuFlyoutPresenter.cs
+++ b/src/AtomUI.Controls/Flyouts/MenuFlyoutPresenter.cs
@@ -167,7 +167,7 @@ public class MenuFlyoutPresenter : MenuBase, IShadowMaskInfoProvider
{
if (_arrowDecoratedBox is not null)
{
- var contentRect = _arrowDecoratedBox.GetContentRect(Bounds.Size);
+ var contentRect = _arrowDecoratedBox.GetMaskBounds();
var adjustedPos = _arrowDecoratedBox.TranslatePoint(contentRect.Position, this) ?? default;
return new Rect(adjustedPos, contentRect.Size);
}
diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
index 2eac2ef..d9082b6 100644
--- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
+++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
@@ -41,6 +41,7 @@ namespace AtomUI.Theme.Styling
public static class ArrowDecoratedBoxTokenResourceKey
{
public static readonly TokenResourceKey ArrowSize = new TokenResourceKey("ArrowDecoratedBox.ArrowSize", "AtomUI.Token");
+ public static readonly TokenResourceKey ArrowContentThickness = new TokenResourceKey("ArrowDecoratedBox.ArrowContentThickness", "AtomUI.Token");
public static readonly TokenResourceKey Padding = new TokenResourceKey("ArrowDecoratedBox.Padding", "AtomUI.Token");
}
diff --git a/src/AtomUI.Controls/Popup/Popup.cs b/src/AtomUI.Controls/Popup/Popup.cs
index f583de0..049009f 100644
--- a/src/AtomUI.Controls/Popup/Popup.cs
+++ b/src/AtomUI.Controls/Popup/Popup.cs
@@ -67,6 +67,7 @@ public class Popup : AvaloniaPopup
private bool _initialized;
private ManagedPopupPositionerInfo? _managedPopupPositioner;
protected bool _animating;
+ private bool _isNeedFlip = true;
// 当鼠标移走了,但是打开动画还没完成,我们需要记录下来这个信号
internal bool RequestCloseWhereAnimationCompleted { get; set; }
@@ -147,11 +148,14 @@ public class Popup : AvaloniaPopup
"Unable to create shadow layer, top level for PlacementTarget is null.");
}
- if (Placement != PlacementMode.Pointer && Placement != PlacementMode.Center)
+ if (_isNeedFlip)
{
- AdjustPopupHostPosition(placementTarget);
+ if (Placement != PlacementMode.Pointer && Placement != PlacementMode.Center)
+ {
+ AdjustPopupHostPosition(placementTarget!);
+ }
}
-
+
if (!_animating)
{
CreateShadowLayer();
@@ -291,8 +295,6 @@ public class Popup : AvaloniaPopup
///
/// 在这里处理翻转问题
///
- ///
- ///
internal void AdjustPopupHostPosition(Control placementTarget)
{
var offsetX = HorizontalOffset;
@@ -307,8 +309,7 @@ public class Popup : AvaloniaPopup
var direction = PopupUtils.GetDirection(Placement);
var topLevel = TopLevel.GetTopLevel(placementTarget)!;
- if (Placement != PlacementMode.Center &&
- Placement != PlacementMode.Pointer)
+ if (Placement != PlacementMode.Center && Placement != PlacementMode.Pointer)
{
// 计算是否 flip
var parameters = new PopupPositionerParameters();
@@ -422,16 +423,22 @@ public class Popup : AvaloniaPopup
_animating = true;
+ var placementTarget = GetEffectivePlacementTarget();
+
Open();
+ if (Placement != PlacementMode.Pointer && Placement != PlacementMode.Center)
+ {
+ AdjustPopupHostPosition(placementTarget!);
+ _isNeedFlip = false;
+ }
var popupRoot = (Host as PopupRoot)!;
// 获取 popup 的具体位置,这个就是非常准确的位置,还有大小
// TODO 暂时只支持 WindowBase popup
popupRoot.Hide();
- var popupOffset = popupRoot.PlatformImpl!.Position;
- var offset = new Point(popupOffset.X, popupOffset.Y);
- var placementTarget = GetEffectivePlacementTarget();
- var topLevel = TopLevel.GetTopLevel(placementTarget);
- var scaling = topLevel?.RenderScaling ?? 1.0;
+ var popupOffset = popupRoot.PlatformImpl!.Position;
+ var offset = new Point(popupOffset.X, popupOffset.Y);
+ var topLevel = TopLevel.GetTopLevel(placementTarget);
+ var scaling = topLevel?.RenderScaling ?? 1.0;
// 调度动画
var director = Director.Instance;