mirror of
https://gitee.com/chinware/atomui.git
synced 2024-11-30 02:47:45 +08:00
修复 offset 计算错误
This commit is contained in:
parent
396c37f6a8
commit
b37517deb6
@ -171,14 +171,8 @@ public partial class ArrowDecoratedBox : StyledControl, IShadowMaskInfoProvider
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
if (!_initialized) {
|
||||
_customStyle.SetupUi();
|
||||
SetupRelayProperties();
|
||||
}
|
||||
SetupRelayProperties();
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromLogicalTree(e);
|
||||
_compositeDisposable?.Dispose();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Reactive.Disposables;
|
||||
using AtomUI.Controls.Utils;
|
||||
using AtomUI.Data;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.Styling;
|
||||
@ -38,9 +39,10 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
if (IsShowArrow) {
|
||||
BuildGeometry(true);
|
||||
}
|
||||
|
||||
LogicalChildren.Add(_container);
|
||||
VisualChildren.Add(_container);
|
||||
|
||||
|
||||
_controlTokenBinder.AddControlBinding(BackgroundProperty, GlobalResourceKey.ColorBgContainer);
|
||||
_initialized = true;
|
||||
}
|
||||
@ -50,10 +52,14 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
_compositeDisposable = new CompositeDisposable();
|
||||
// 生命周期一样,可以不用管理
|
||||
if (_container is not null) {
|
||||
if (Child?.Parent is not null) {
|
||||
UiStructureUtils.SetLogicalParent(Child, null);
|
||||
UiStructureUtils.SetVisualParent(Child, null);
|
||||
}
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundSizingProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, BackgroundProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, CornerRadiusProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, ChildProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, ChildProperty, _container));
|
||||
_compositeDisposable.Add(BindUtils.RelayBind(this, PaddingProperty, _container));
|
||||
}
|
||||
}
|
||||
@ -75,7 +81,7 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
GetArrowRect(DesiredSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildGeometry(bool force = false)
|
||||
@ -98,7 +104,7 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
if (IsShowArrow) {
|
||||
var direction = GetDirection(ArrowPosition);
|
||||
var matrix = Matrix.CreateTranslation(-_arrowSize / 2, -_arrowSize / 2);
|
||||
|
||||
|
||||
if (direction == Direction.Right) {
|
||||
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(90));
|
||||
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
|
||||
@ -124,7 +130,7 @@ public partial class ArrowDecoratedBox : IControlCustomStyle
|
||||
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);
|
||||
|
@ -278,6 +278,7 @@ public class Flyout : PopupFlyoutBase
|
||||
offsetY += offset.Y;
|
||||
Popup.HorizontalOffset = offsetX;
|
||||
Popup.VerticalOffset = offsetY;
|
||||
Console.WriteLine($"NotifyPositionPopup-{offsetY}");
|
||||
}
|
||||
|
||||
protected override bool ShowAtCore(Control placementTarget, bool showAtPointer = false)
|
||||
@ -295,6 +296,7 @@ public class Flyout : PopupFlyoutBase
|
||||
UiStructureUtils.SetLogicalParent(flyoutPresenter, placementTarget);
|
||||
}
|
||||
var positionInfo = AtomPopup.CalculatePositionInfo(placementTarget, presenter);
|
||||
Console.WriteLine(positionInfo.Offset);
|
||||
PlayShowUpMotion(positionInfo, placementTarget, flyoutPresenter, showAtPointer);
|
||||
result = true;
|
||||
} else {
|
||||
@ -331,7 +333,7 @@ public class Flyout : PopupFlyoutBase
|
||||
UiStructureUtils.ClearVisualParentRecursive(child, null);
|
||||
}
|
||||
_animating = false;
|
||||
// base.ShowAtCore(placementTarget, showAtPointer);
|
||||
base.ShowAtCore(placementTarget, showAtPointer);
|
||||
};
|
||||
|
||||
director?.Schedule(motionActor);
|
||||
|
@ -54,6 +54,9 @@ public class Popup : AbstractPopup
|
||||
private PlacementMode? _originPlacementMode;
|
||||
private PopupAnchor? _originPlacementAnchor;
|
||||
private PopupGravity? _originPlacementGravity;
|
||||
private double _originOffsetX;
|
||||
private double _originOffsetY;
|
||||
private bool _ignoreSyncOriginValues = false;
|
||||
|
||||
static Popup()
|
||||
{
|
||||
@ -68,8 +71,28 @@ public class Popup : AbstractPopup
|
||||
{
|
||||
IsLightDismissEnabled = false;
|
||||
_globalTokenBinder = new GlobalTokenBinder();
|
||||
_originOffsetX = HorizontalOffset;
|
||||
_originOffsetY = VerticalOffset;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (!_ignoreSyncOriginValues) {
|
||||
if (e.Property == HorizontalOffsetProperty) {
|
||||
_originOffsetX = e.GetNewValue<double>();
|
||||
} else if (e.Property == VerticalOffsetProperty) {
|
||||
_originOffsetY = e.GetNewValue<double>();
|
||||
} else 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)
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
@ -278,8 +301,9 @@ public class Popup : AbstractPopup
|
||||
protected internal override void NotifyPopupRootAboutToShow(PopupRoot popupRoot)
|
||||
{
|
||||
base.NotifyPopupRootAboutToShow(popupRoot);
|
||||
var offsetX = HorizontalOffset;
|
||||
var offsetY = VerticalOffset;
|
||||
using var ignoreSyncOriginHandling = IgnoreSyncOriginValueHandling();
|
||||
var offsetX = _originOffsetX;
|
||||
var offsetY = _originOffsetY;
|
||||
var marginToAnchorOffset = CalculateMarginToAnchorOffset(Placement);
|
||||
offsetX += marginToAnchorOffset.X;
|
||||
offsetY += marginToAnchorOffset.Y;
|
||||
@ -362,9 +386,14 @@ public class Popup : AbstractPopup
|
||||
|
||||
internal PopupPositionInfo CalculatePositionInfo(Control placementTarget, Control popupContent)
|
||||
{
|
||||
var offsetX = HorizontalOffset;
|
||||
var offsetY = VerticalOffset;
|
||||
using var ignoreSyncOriginHandling = IgnoreSyncOriginValueHandling();
|
||||
var offsetX = _originOffsetX;
|
||||
var offsetY = _originOffsetY;
|
||||
|
||||
Console.WriteLine(offsetY);
|
||||
|
||||
var marginToAnchorOffset = CalculateMarginToAnchorOffset(Placement);
|
||||
Console.WriteLine(marginToAnchorOffset);
|
||||
offsetX += marginToAnchorOffset.X;
|
||||
offsetY += marginToAnchorOffset.Y;
|
||||
Point offset = default;
|
||||
@ -438,7 +467,7 @@ public class Popup : AbstractPopup
|
||||
positionInfo.IsFlipped = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var rect = PopupUtils.Calculate(
|
||||
parameters.Size * scaling,
|
||||
new Rect(
|
||||
@ -501,6 +530,27 @@ public class Popup : AbstractPopup
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(placement), placement, "Invalid value for PlacementMode")
|
||||
};
|
||||
}
|
||||
|
||||
private protected 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class PopupPositionInfo
|
||||
|
@ -1,17 +1,16 @@
|
||||
using AtomUI.ColorSystem;
|
||||
using AtomUI.Controls.Utils;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.MotionScene;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace AtomUI.Controls.Primitives;
|
||||
|
||||
// TODO 这个类的实例只在动画过程中存在,所以是否需要处理属性变化对重绘的需求需要再评估,暂时先不处理
|
||||
internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
{
|
||||
public static readonly StyledProperty<BoxShadows> ShadowsProperty =
|
||||
@ -21,7 +20,7 @@ internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
Border.CornerRadiusProperty.AddOwner<MotionGhostControl>();
|
||||
|
||||
/// <summary>
|
||||
/// 渲染的阴影值
|
||||
/// 渲染的阴影值,一般在探测失败的时候使用
|
||||
/// </summary>
|
||||
public BoxShadows Shadows
|
||||
{
|
||||
@ -30,21 +29,21 @@ internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// mask 的圆角大小
|
||||
/// mask 的圆角大小,一般在探测失败的时候使用
|
||||
/// </summary>
|
||||
public CornerRadius MaskCornerRadius
|
||||
{
|
||||
get => GetValue(MaskCornerRadiusProperty);
|
||||
set => SetValue(MaskCornerRadiusProperty, value);
|
||||
}
|
||||
|
||||
protected Border? _maskRenderer;
|
||||
protected Border? _contentRenderer;
|
||||
|
||||
protected bool _initialized = false;
|
||||
protected Canvas? _layout;
|
||||
protected Control _motionTarget;
|
||||
protected RenderTargetBitmap? _ghostBitmap;
|
||||
protected Size _motionTargetSize;
|
||||
protected Point _maskOffset;
|
||||
protected Size _maskSize;
|
||||
|
||||
static MotionGhostControl()
|
||||
{
|
||||
@ -52,7 +51,7 @@ internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
AffectsRender<ShadowRenderer>(MaskCornerRadiusProperty);
|
||||
}
|
||||
|
||||
public MotionGhostControl(Control motionTarget, BoxShadows shadows)
|
||||
public MotionGhostControl(Control motionTarget, BoxShadows fallbackShadows)
|
||||
{
|
||||
_motionTarget = motionTarget;
|
||||
if (_motionTarget.DesiredSize == default) {
|
||||
@ -61,35 +60,31 @@ internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
_motionTargetSize = _motionTarget.DesiredSize;
|
||||
}
|
||||
|
||||
foreach (var boxShadow in shadows) {
|
||||
|
||||
_maskOffset = default;
|
||||
_maskSize = _motionTargetSize;
|
||||
|
||||
Shadows = fallbackShadows;
|
||||
DetectMotionTargetInfo();
|
||||
}
|
||||
|
||||
private void DetectMotionTargetInfo()
|
||||
{
|
||||
CornerRadius cornerRadius = default;
|
||||
if (_motionTarget is IShadowMaskInfoProvider shadowMaskInfoProvider) {
|
||||
cornerRadius = shadowMaskInfoProvider.GetMaskCornerRadius();
|
||||
var maskBounds = shadowMaskInfoProvider.GetMaskBounds();
|
||||
_maskOffset = maskBounds.Position;
|
||||
_maskSize = maskBounds.Size;
|
||||
} else if (_motionTarget is BorderedStyleControl bordered) {
|
||||
cornerRadius = bordered.CornerRadius;
|
||||
} else if (_motionTarget is TemplatedControl templatedControl) {
|
||||
cornerRadius = templatedControl.CornerRadius;
|
||||
}
|
||||
|
||||
// 探测出来的是最准确的,优先级高
|
||||
if (cornerRadius != default) {
|
||||
MaskCornerRadius = cornerRadius;
|
||||
}
|
||||
Shadows = new BoxShadows(new BoxShadow
|
||||
{
|
||||
OffsetX = 0,
|
||||
OffsetY = 6 * 2,
|
||||
Blur = 16 * 2,
|
||||
Spread = 0,
|
||||
Color = ColorUtils.FromRgbF(0.10, 0, 0, 0)
|
||||
}, new []
|
||||
{
|
||||
new BoxShadow
|
||||
{
|
||||
OffsetX = 0,
|
||||
OffsetY = 3 * 2,
|
||||
Blur = 6 * 2,
|
||||
Spread = -4 * 2,
|
||||
Color = ColorUtils.FromRgbF(0.12, 0, 0, 0)
|
||||
},
|
||||
new BoxShadow
|
||||
{
|
||||
OffsetX = 0,
|
||||
OffsetY = 9 * 2,
|
||||
Blur = 28 * 2,
|
||||
Spread = 8 * 2,
|
||||
Color = ColorUtils.FromRgbF(0.07, 0, 0, 0)
|
||||
}
|
||||
});;
|
||||
}
|
||||
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
@ -100,27 +95,20 @@ internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
|
||||
_layout = new Canvas();
|
||||
VisualChildren.Add(_layout);
|
||||
LogicalChildren.Add(_layout);
|
||||
LogicalChildren.Add(_layout);
|
||||
|
||||
var shadowThickness = Shadows.Thickness();
|
||||
var offsetX = shadowThickness.Left;
|
||||
var offsetY = shadowThickness.Top;
|
||||
|
||||
var shadowRenderers = BuildShadowRenderers(Shadows);
|
||||
|
||||
foreach (var renderer in shadowRenderers) {
|
||||
_layout.Children.Add(renderer);
|
||||
}
|
||||
|
||||
_maskRenderer = new Border
|
||||
{
|
||||
BorderThickness = new Thickness(0),
|
||||
BoxShadow = Shadows,
|
||||
CornerRadius = MaskCornerRadius,
|
||||
Width = _motionTargetSize.Width,
|
||||
Height = _motionTargetSize.Height
|
||||
};
|
||||
|
||||
_layout.Children.Add(_maskRenderer);
|
||||
_layout.Children.Add(_motionTarget);
|
||||
|
||||
Canvas.SetLeft(_maskRenderer, offsetX);
|
||||
Canvas.SetTop(_maskRenderer, offsetY);
|
||||
|
||||
Canvas.SetLeft(_motionTarget, offsetX);
|
||||
Canvas.SetTop(_motionTarget, offsetY);
|
||||
|
||||
@ -128,6 +116,37 @@ internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 目前的 Avalonia 版本中,当控件渲染到 RenderTargetBitmap 的时候,如果 BoxShadows 的 Count > 1 的时候,如果不是主阴影,后面的阴影如果
|
||||
/// 指定 offset,再 RenderScaling > 1 的情况下是错的。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private List<Control> BuildShadowRenderers(in BoxShadows shadows)
|
||||
{
|
||||
var thickness = Shadows.Thickness();
|
||||
var offsetX = thickness.Left;
|
||||
var offsetY = thickness.Top;
|
||||
|
||||
offsetX += _maskOffset.X;
|
||||
offsetY += _maskOffset.Y;
|
||||
|
||||
var renderers = new List<Control>();
|
||||
for (var i = 0; i < shadows.Count; ++i) {
|
||||
var renderer = new Border
|
||||
{
|
||||
BorderThickness = new Thickness(0),
|
||||
BoxShadow = new BoxShadows(shadows[i]),
|
||||
CornerRadius = MaskCornerRadius,
|
||||
Width = _maskSize.Width,
|
||||
Height = _maskSize.Height
|
||||
};
|
||||
Canvas.SetLeft(renderer, offsetX);
|
||||
Canvas.SetTop(renderer, offsetY);
|
||||
renderers.Add(renderer);
|
||||
}
|
||||
return renderers;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
base.MeasureOverride(availableSize);
|
||||
@ -155,7 +174,7 @@ internal class MotionGhostControl : Control, INotifyCaptureGhostBitmap
|
||||
if (_ghostBitmap is null) {
|
||||
var scaling = TopLevel.GetTopLevel(this)?.RenderScaling ?? 1.0;
|
||||
_ghostBitmap = new RenderTargetBitmap(new PixelSize((int)(DesiredSize.Width * scaling), (int)(DesiredSize.Height * scaling)),
|
||||
new Vector(96, 96));
|
||||
new Vector(96 * scaling, 96 * scaling));
|
||||
_ghostBitmap.Render(this);
|
||||
_layout!.Children.Clear();
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System.Reactive.Disposables;
|
||||
using AtomUI.MotionScene;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace AtomUI.Controls.MotionScene;
|
||||
@ -38,12 +40,11 @@ public class Director : IDirector
|
||||
_compositeDisposable.Add(Disposable.Create((sceneLayer), state =>
|
||||
{
|
||||
if (sceneLayer is not null) {
|
||||
//sceneLayer.Opacity = 0;
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
// await Task.Delay(60);
|
||||
// sceneLayer.Hide();
|
||||
// sceneLayer.Dispose();
|
||||
await Task.Delay(100);
|
||||
sceneLayer.Hide();
|
||||
sceneLayer.Dispose();
|
||||
});
|
||||
}
|
||||
}));
|
||||
@ -51,6 +52,7 @@ public class Director : IDirector
|
||||
_states.Add(actor, state);
|
||||
|
||||
if (actor.DispatchInSceneLayer) {
|
||||
state.SceneLayer = sceneLayer;
|
||||
var ghost = actor.GetAnimatableGhost();
|
||||
sceneLayer!.SetMotionTarget(ghost);
|
||||
actor.NotifyMotionTargetAddedToScene(ghost);
|
||||
@ -136,9 +138,12 @@ public class Director : IDirector
|
||||
|
||||
private void HandleMotionCompleted(MotionActor actor)
|
||||
{
|
||||
actor.NotifyMotionCompleted();
|
||||
MotionCompleted?.Invoke(this, new MotionEventArgs(actor));
|
||||
if (_states.TryGetValue(actor, out var state)) {
|
||||
if (state.SceneLayer is not null) {
|
||||
state.SceneLayer.Opacity = 0;
|
||||
}
|
||||
actor.NotifyMotionCompleted();
|
||||
MotionCompleted?.Invoke(this, new MotionEventArgs(actor));
|
||||
state.Dispose();
|
||||
_states.Remove(actor);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Transformation;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace AtomUI.MotionScene;
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Reflection;
|
||||
using AtomUI.Platform.Windows;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives.PopupPositioning;
|
||||
using Avalonia.Media;
|
||||
@ -152,6 +153,6 @@ public class SceneLayer : WindowBase, IHostedVisualTreeRoot, IDisposable
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
//context.FillRectangle(new SolidColorBrush(Colors.Coral), new Rect(new Point(0, 0), DesiredSize));
|
||||
//context.FillRectangle(new SolidColorBrush(Colors.Coral, 0.2), new Rect(new Point(0, 0), DesiredSize));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user