mirror of
https://gitee.com/chinware/atomui.git
synced 2024-11-30 02:47:45 +08:00
修复 Popup 和 动画指针坐标不正确的问题
This commit is contained in:
parent
f8741a4c90
commit
569b4b18ed
@ -92,7 +92,9 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
||||
Border.CornerRadiusProperty.AddOwner<ArrowDecoratedBox>();
|
||||
|
||||
// 指针最顶点位置
|
||||
internal (double, double) ArrowVertexPoint { get; private set; }
|
||||
// 相对坐标
|
||||
private (double, double) _arrowVertexPoint;
|
||||
internal (double, double) ArrowVertexPoint => GetArrowVertexPoint();
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示指示箭头
|
||||
|
@ -21,6 +21,7 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
private Rect _arrowRect;
|
||||
private Border? _container;
|
||||
private CompositeDisposable? _compositeDisposable;
|
||||
private bool _needGenerateArrowVertexPoint = true;
|
||||
|
||||
// 组件的 Token 绑定属性
|
||||
private double _arrowSize;
|
||||
@ -69,17 +70,30 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
NotifyApplyFixedStyleConfig();
|
||||
}
|
||||
|
||||
private (double, double) GetArrowVertexPoint()
|
||||
{
|
||||
if (_needGenerateArrowVertexPoint) {
|
||||
BuildGeometry(true);
|
||||
_arrowRect = GetArrowRect(DesiredSize);
|
||||
_needGenerateArrowVertexPoint = false;
|
||||
}
|
||||
|
||||
return _arrowVertexPoint;
|
||||
}
|
||||
|
||||
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property == IsShowArrowProperty ||
|
||||
e.Property == ArrowPositionProperty ||
|
||||
e.Property == ArrowSizeTokenProperty ||
|
||||
e.Property == VisualParentProperty) {
|
||||
if (_initialized) {
|
||||
if (IsShowArrow) {
|
||||
BuildGeometry(true);
|
||||
GetArrowRect(DesiredSize);
|
||||
}
|
||||
if (e.Property == IsShowArrowProperty && VisualRoot is null) {
|
||||
// 当开启的时候,但是还没有加入的渲染树,这个时候我们取不到 Token 需要在取值的时候重新生成一下
|
||||
_needGenerateArrowVertexPoint = true;
|
||||
}
|
||||
if (_initialized && VisualRoot is not null) {
|
||||
BuildGeometry(true);
|
||||
_arrowRect = GetArrowRect(DesiredSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,6 +214,7 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
var position = ArrowPosition;
|
||||
if (IsShowArrow) {
|
||||
var size = _arrowGeometry!.Bounds.Size;
|
||||
|
||||
var minValue = Math.Min(size.Width, size.Height);
|
||||
var maxValue = Math.Max(size.Width, size.Height);
|
||||
if (position == ArrowPosition.Left ||
|
||||
@ -270,17 +285,17 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var targetRect = new Rect(offsetX, offsetY, targetWidth, targetHeight);
|
||||
var center = targetRect.Center;
|
||||
|
||||
// 计算中点
|
||||
var direction = GetDirection(position);
|
||||
if (direction == Direction.Left || direction == Direction.Right) {
|
||||
ArrowVertexPoint = (center.Y, finalSize.Height - center.Y);
|
||||
_arrowVertexPoint = (center.Y, finalSize.Height - center.Y);
|
||||
} else if (direction == Direction.Top || direction == Direction.Bottom) {
|
||||
ArrowVertexPoint = (center.X, finalSize.Width - center.X);
|
||||
_arrowVertexPoint = (center.X, finalSize.Width - center.X);
|
||||
}
|
||||
|
||||
return targetRect;
|
||||
}
|
||||
}
|
@ -142,23 +142,18 @@ public class Flyout : PopupFlyoutBase
|
||||
}
|
||||
}
|
||||
|
||||
var placement = popup.Placement;
|
||||
var anchor = popup.PlacementAnchor;
|
||||
var gravity = popup.PlacementGravity;
|
||||
|
||||
if (flyoutPresenter is not null) {
|
||||
var arrowPosition = PopupUtils.CalculateArrowPosition(popup.Placement, popup.PlacementAnchor, popup.PlacementGravity);
|
||||
var arrowPosition = PopupUtils.CalculateArrowPosition(placement, anchor, gravity);
|
||||
if (arrowPosition.HasValue) {
|
||||
flyoutPresenter.ArrowPosition = arrowPosition.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupArrowPosition(FlyoutPresenter flyoutPresenter, PlacementMode placement, PopupAnchor? anchor,
|
||||
PopupGravity? gravity)
|
||||
{
|
||||
var arrowPosition = PopupUtils.CalculateArrowPosition(placement, anchor, gravity);
|
||||
if (arrowPosition.HasValue) {
|
||||
flyoutPresenter.ArrowPosition = arrowPosition.Value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Control CreatePresenter()
|
||||
{
|
||||
var presenter = new FlyoutPresenter
|
||||
@ -176,9 +171,7 @@ public class Flyout : PopupFlyoutBase
|
||||
BindUtils.RelayBind(this, PlacementProperty, popup);
|
||||
BindUtils.RelayBind(this, PlacementAnchorProperty, popup);
|
||||
BindUtils.RelayBind(this, PlacementGravityProperty, popup);
|
||||
BindUtils.RelayBind(this, HorizontalOffsetProperty, popup);
|
||||
BindUtils.RelayBind(this, VerticalOffsetProperty, popup);
|
||||
popup.MaskShadows = MaskShadows;
|
||||
BindUtils.RelayBind(this, MaskShadowsProperty, popup);
|
||||
SetupArrowPosition(popup);
|
||||
}
|
||||
|
||||
@ -207,15 +200,19 @@ public class Flyout : PopupFlyoutBase
|
||||
_compositeDisposable?.Dispose();
|
||||
}
|
||||
|
||||
private Point CalculatePopupPositionDelta(Control anchorTarget, PlacementMode placement, PopupAnchor? anchor = null,
|
||||
private Point CalculatePopupPositionDelta(Control anchorTarget,
|
||||
Control? flyoutPresenter,
|
||||
PlacementMode placement,
|
||||
PopupAnchor? anchor = null,
|
||||
PopupGravity? gravity = null)
|
||||
{
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
if (IsShowArrow && IsPointAtCenter) {
|
||||
if (PopupUtils.CanEnabledArrow(placement, anchor, gravity)) {
|
||||
if (Popup.Child is ArrowDecoratedBox arrowDecoratedBox) {
|
||||
if (flyoutPresenter is ArrowDecoratedBox arrowDecoratedBox) {
|
||||
var arrowVertexPoint = arrowDecoratedBox.ArrowVertexPoint;
|
||||
|
||||
var anchorSize = anchorTarget.Bounds.Size;
|
||||
var centerX = anchorSize.Width / 2;
|
||||
var centerY = anchorSize.Height / 2;
|
||||
@ -262,15 +259,11 @@ public class Flyout : PopupFlyoutBase
|
||||
|
||||
protected internal override void NotifyPositionPopup(bool showAtPointer)
|
||||
{
|
||||
Size sz;
|
||||
// Popup.Child can't be null here, it was set in ShowAtCore.
|
||||
|
||||
if (Popup.Child!.DesiredSize == default) {
|
||||
// Popup may not have been shown yet. Measure content
|
||||
sz = LayoutHelper.MeasureChild(Popup.Child, Size.Infinity, new Thickness());
|
||||
} else {
|
||||
sz = Popup.Child.DesiredSize;
|
||||
LayoutHelper.MeasureChild(Popup.Child, Size.Infinity, new Thickness());
|
||||
}
|
||||
|
||||
|
||||
Popup.PlacementAnchor = PlacementAnchor;
|
||||
Popup.PlacementGravity = PlacementGravity;
|
||||
|
||||
@ -280,13 +273,16 @@ public class Flyout : PopupFlyoutBase
|
||||
Popup.Placement = Placement;
|
||||
Popup.PlacementConstraintAdjustment = PlacementConstraintAdjustment;
|
||||
}
|
||||
|
||||
|
||||
var pointAtCenterOffset = CalculatePopupPositionDelta(Target!, Popup.Child, Popup.Placement, Popup.PlacementAnchor, Popup.PlacementGravity);
|
||||
|
||||
var offsetX = HorizontalOffset;
|
||||
var offsetY = VerticalOffset;
|
||||
|
||||
var offset = CalculatePopupPositionDelta(Target!, Placement, PlacementAnchor, PlacementGravity);
|
||||
offsetX += offset.X;
|
||||
offsetY += offset.Y;
|
||||
if (IsPointAtCenter) {
|
||||
offsetX += pointAtCenterOffset.X;
|
||||
offsetY += pointAtCenterOffset.Y;
|
||||
}
|
||||
// 更新弹出信息是否指向中点
|
||||
Popup.HorizontalOffset = offsetX;
|
||||
Popup.VerticalOffset = offsetY;
|
||||
}
|
||||
@ -307,13 +303,32 @@ public class Flyout : PopupFlyoutBase
|
||||
UiStructureUtils.ClearLogicalParentRecursive(flyoutPresenter, null);
|
||||
UiStructureUtils.ClearVisualParentRecursive(flyoutPresenter, null);
|
||||
UiStructureUtils.SetLogicalParent(flyoutPresenter, placementToplevel);
|
||||
_popupPositionInfo = AtomPopup.CalculatePositionInfo(placementTarget, presenter);
|
||||
|
||||
_popupPositionInfo = AtomPopup.CalculatePositionInfo(placementTarget,
|
||||
presenter,
|
||||
new Point(HorizontalOffset, VerticalOffset),
|
||||
Placement);
|
||||
|
||||
// 重新设置箭头位置
|
||||
SetupArrowPosition(flyoutPresenter,
|
||||
_popupPositionInfo.EffectivePlacement,
|
||||
_popupPositionInfo.EffectivePlacementAnchor,
|
||||
_popupPositionInfo.EffectivePlacementGravity);
|
||||
// 因为可能有 flip 的情况
|
||||
var arrowPosition = PopupUtils.CalculateArrowPosition(_popupPositionInfo.EffectivePlacement,
|
||||
_popupPositionInfo.EffectivePlacementAnchor,
|
||||
_popupPositionInfo.EffectivePlacementGravity);
|
||||
if (arrowPosition.HasValue) {
|
||||
flyoutPresenter.ArrowPosition = arrowPosition.Value;
|
||||
}
|
||||
|
||||
// 获取是否在指向中点
|
||||
var pointAtCenterOffset = CalculatePopupPositionDelta(placementTarget,
|
||||
presenter,
|
||||
_popupPositionInfo.EffectivePlacement,
|
||||
_popupPositionInfo.EffectivePlacementAnchor,
|
||||
_popupPositionInfo.EffectivePlacementGravity);
|
||||
if (IsPointAtCenter) {
|
||||
_popupPositionInfo.Offset = new Point(_popupPositionInfo.Offset.X + pointAtCenterOffset.X * _popupPositionInfo.Scaling,
|
||||
_popupPositionInfo.Offset.Y + pointAtCenterOffset.Y * _popupPositionInfo.Scaling);
|
||||
}
|
||||
|
||||
PlayShowMotion(_popupPositionInfo, placementTarget, flyoutPresenter, showAtPointer);
|
||||
}
|
||||
result = true;
|
||||
@ -355,7 +370,8 @@ public class Flyout : PopupFlyoutBase
|
||||
motion.ConfigureOpacity(_motionDuration);
|
||||
motion.ConfigureRenderTransform(_motionDuration);
|
||||
var topLevel = TopLevel.GetTopLevel(placementTarget);
|
||||
var motionActor = new PopupMotionActor(MaskShadows, positionInfo, flyoutPresenter, motion);
|
||||
|
||||
var motionActor = new PopupMotionActor(MaskShadows, positionInfo.Offset, positionInfo.Scaling, flyoutPresenter, motion);
|
||||
motionActor.DispatchInSceneLayer = true;
|
||||
motionActor.SceneParent = topLevel;
|
||||
motionActor.Completed += (sender, args) =>
|
||||
@ -401,7 +417,7 @@ public class Flyout : PopupFlyoutBase
|
||||
UiStructureUtils.SetVisualParent(popup.Child, null);
|
||||
UiStructureUtils.SetVisualParent(popup.Child, null);
|
||||
|
||||
var motionActor = new PopupMotionActor(MaskShadows, _popupPositionInfo, popup.Child, motion);
|
||||
var motionActor = new PopupMotionActor(MaskShadows, _popupPositionInfo.Offset, _popupPositionInfo.Scaling, popup.Child, motion);
|
||||
motionActor.DispatchInSceneLayer = true;
|
||||
motionActor.SceneParent = placementToplevel;
|
||||
|
||||
|
@ -50,13 +50,9 @@ public class Popup : AbstractPopup
|
||||
private readonly GlobalTokenBinder _globalTokenBinder;
|
||||
private CompositeDisposable? _compositeDisposable;
|
||||
private bool _initialized;
|
||||
|
||||
private PlacementMode? _originPlacementMode;
|
||||
private PopupAnchor? _originPlacementAnchor;
|
||||
private PopupGravity? _originPlacementGravity;
|
||||
private double _originOffsetX;
|
||||
private double _originOffsetY;
|
||||
|
||||
private bool _ignoreSyncOriginValues = false;
|
||||
private Point _pointAtCenterOffset;
|
||||
|
||||
static Popup()
|
||||
{
|
||||
@ -71,28 +67,6 @@ public class Popup : AbstractPopup
|
||||
{
|
||||
IsLightDismissEnabled = false;
|
||||
_globalTokenBinder = new GlobalTokenBinder();
|
||||
_originOffsetX = HorizontalOffset;
|
||||
_originOffsetY = VerticalOffset;
|
||||
}
|
||||
|
||||
internal void UpdateOriginOffset(double offsetX, double offsetY)
|
||||
{
|
||||
_originOffsetX = offsetX;
|
||||
_originOffsetY = offsetY;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (!_ignoreSyncOriginValues) {
|
||||
if (e.Property == PlacementProperty) {
|
||||
_originPlacementMode = e.GetNewValue<PlacementMode>();
|
||||
} else if (e.Property == PlacementAnchorProperty) {
|
||||
_originPlacementAnchor = e.GetNewValue<PopupAnchor>();
|
||||
} else if (e.Property == PlacementGravityProperty) {
|
||||
_originPlacementGravity = e.GetNewValue<PopupGravity>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
@ -303,15 +277,16 @@ public class Popup : AbstractPopup
|
||||
protected internal override void NotifyPopupRootAboutToShow(PopupRoot popupRoot)
|
||||
{
|
||||
base.NotifyPopupRootAboutToShow(popupRoot);
|
||||
using var ignoreSyncOriginHandling = IgnoreSyncOriginValueHandling();
|
||||
var offsetX = _originOffsetX;
|
||||
var offsetY = _originOffsetY;
|
||||
var offsetX = HorizontalOffset;
|
||||
var offsetY = VerticalOffset;
|
||||
var marginToAnchorOffset = CalculateMarginToAnchorOffset(Placement);
|
||||
offsetX += marginToAnchorOffset.X;
|
||||
offsetY += marginToAnchorOffset.Y;
|
||||
HorizontalOffset = offsetX;
|
||||
VerticalOffset = offsetY;
|
||||
|
||||
var direction = GetDirection(Placement);
|
||||
|
||||
if (Placement != PlacementMode.Center &&
|
||||
Placement != PlacementMode.Pointer) {
|
||||
// 计算是否 flip
|
||||
@ -342,7 +317,6 @@ public class Popup : AbstractPopup
|
||||
parameters.AnchorRectangle.Size * scaling);
|
||||
anchorRect = anchorRect.Translate(_managedPopupPositioner.ParentClientAreaScreenGeometry.TopLeft);
|
||||
|
||||
|
||||
var flipInfo = CalculateFlipInfo(popupSize * scaling,
|
||||
anchorRect,
|
||||
parameters.Anchor,
|
||||
@ -353,50 +327,37 @@ public class Popup : AbstractPopup
|
||||
var flipAnchorAndGravity = GetAnchorAndGravity(flipPlacement);
|
||||
var flipOffset = CalculateMarginToAnchorOffset(flipPlacement);
|
||||
|
||||
_originPlacementMode = Placement;
|
||||
_originPlacementAnchor = PlacementAnchor;
|
||||
_originPlacementGravity = PlacementGravity;
|
||||
|
||||
Placement = flipPlacement;
|
||||
PlacementAnchor = flipAnchorAndGravity.Item1;
|
||||
PlacementGravity = flipAnchorAndGravity.Item2;
|
||||
HorizontalOffset = flipOffset.X;
|
||||
VerticalOffset = flipOffset.Y;
|
||||
|
||||
// 这里有个问题,目前需要重新看看,就是 X 轴 和 Y 轴会不会同时被反转呢?
|
||||
|
||||
if (direction == Direction.Top || direction == Direction.Bottom) {
|
||||
VerticalOffset = flipOffset.Y;
|
||||
} else {
|
||||
HorizontalOffset = flipOffset.X;
|
||||
}
|
||||
IsFlipped = true;
|
||||
|
||||
} else {
|
||||
IsFlipped = false;
|
||||
|
||||
if (_originPlacementMode.HasValue) {
|
||||
Placement = _originPlacementMode.Value;
|
||||
}
|
||||
|
||||
if (_originPlacementAnchor.HasValue) {
|
||||
PlacementAnchor = _originPlacementAnchor.Value;
|
||||
}
|
||||
|
||||
if (_originPlacementGravity.HasValue) {
|
||||
PlacementGravity = _originPlacementGravity.Value;
|
||||
}
|
||||
|
||||
_originPlacementMode = null;
|
||||
_originPlacementAnchor = null;
|
||||
_originPlacementGravity = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal PopupPositionInfo CalculatePositionInfo(Control placementTarget, Control popupContent)
|
||||
internal PopupPositionInfo CalculatePositionInfo(Control placementTarget, Control popupContent, Point offset,
|
||||
PlacementMode placement)
|
||||
{
|
||||
using var ignoreSyncOriginHandling = IgnoreSyncOriginValueHandling();
|
||||
var offsetX = _originOffsetX;
|
||||
var offsetY = _originOffsetY + 0.5; // TODO 不知道为什么会出现 0.5 的误差
|
||||
var placement = _originPlacementMode ?? Placement;
|
||||
var offsetX = offset.X;
|
||||
var offsetY = offset.Y + 0.5; // TODO 不知道为什么会出现 0.5 的误差
|
||||
|
||||
var marginToAnchorOffset = CalculateMarginToAnchorOffset(placement);
|
||||
offsetX += marginToAnchorOffset.X;
|
||||
offsetY += marginToAnchorOffset.Y;
|
||||
Point offset = default;
|
||||
|
||||
var direction = GetDirection(placement);
|
||||
|
||||
PopupPositionerParameters parameters = new PopupPositionerParameters();
|
||||
var parentTopLevel = TopLevel.GetTopLevel(placementTarget)!;
|
||||
|
||||
@ -423,8 +384,6 @@ public class Popup : AbstractPopup
|
||||
PlacementRect ?? new Rect(default, placementTarget.Bounds.Size),
|
||||
FlowDirection);
|
||||
|
||||
|
||||
|
||||
var positionInfo = new PopupPositionInfo();
|
||||
positionInfo.EffectivePlacement = placement;
|
||||
positionInfo.EffectivePlacementAnchor = PlacementAnchor;
|
||||
@ -461,7 +420,13 @@ public class Popup : AbstractPopup
|
||||
positionInfo.EffectivePlacement = flipPlacement;
|
||||
positionInfo.EffectivePlacementAnchor = flipAnchorAndGravity.Item1;
|
||||
positionInfo.EffectivePlacementGravity = flipAnchorAndGravity.Item2;
|
||||
positionInfo.Offset = flipOffset;
|
||||
|
||||
if (direction == Direction.Top || direction == Direction.Bottom) {
|
||||
positionInfo.Offset = positionInfo.Offset.WithY(flipOffset.Y);
|
||||
} else {
|
||||
positionInfo.Offset = positionInfo.Offset.WithX(flipOffset.X);
|
||||
}
|
||||
|
||||
positionInfo.IsFlipped = true;
|
||||
} else {
|
||||
positionInfo.IsFlipped = false;
|
||||
@ -530,27 +495,6 @@ public class Popup : AbstractPopup
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(placement), placement, "Invalid value for PlacementMode")
|
||||
};
|
||||
}
|
||||
|
||||
private IDisposable IgnoreSyncOriginValueHandling()
|
||||
{
|
||||
return new IgnoreSyncOriginValueDisposable(this);
|
||||
}
|
||||
|
||||
private readonly struct IgnoreSyncOriginValueDisposable : IDisposable
|
||||
{
|
||||
private readonly Popup _popup;
|
||||
|
||||
public IgnoreSyncOriginValueDisposable(Popup popup)
|
||||
{
|
||||
_popup = popup;
|
||||
_popup._ignoreSyncOriginValues = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_popup._ignoreSyncOriginValues = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void HideShadowLayer()
|
||||
{
|
||||
|
@ -10,23 +10,26 @@ namespace AtomUI.Controls;
|
||||
internal class PopupMotionActor : MotionActor
|
||||
{
|
||||
private BoxShadows _boxShadows;
|
||||
private PopupPositionInfo _popupPositionInfo;
|
||||
private Point _offset;
|
||||
private double _scaling;
|
||||
|
||||
public PopupMotionActor(BoxShadows boxShadows,
|
||||
PopupPositionInfo popupPositionInfo,
|
||||
Point offset,
|
||||
double scaling,
|
||||
Control motionTarget,
|
||||
AbstractMotion motion)
|
||||
: base(motionTarget, motion)
|
||||
{
|
||||
_popupPositionInfo = popupPositionInfo;
|
||||
_offset = offset;
|
||||
_scaling = scaling;
|
||||
_boxShadows = boxShadows;
|
||||
}
|
||||
|
||||
protected override Point CalculateTopLevelGhostPosition()
|
||||
{
|
||||
var boxShadowsThickness = _boxShadows.Thickness();
|
||||
var winPos = _popupPositionInfo.Offset; // TODO 可能需要乘以 scaling
|
||||
var scaledThickness = boxShadowsThickness * _popupPositionInfo.Scaling;
|
||||
var winPos = _offset; // TODO 可能需要乘以 scaling
|
||||
var scaledThickness = boxShadowsThickness * _scaling;
|
||||
return new Point(winPos.X - scaledThickness.Left, winPos.Y - scaledThickness.Top);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user