增加 ArrowDecoratedBox 控件

This commit is contained in:
polarboy 2024-06-27 14:18:17 +08:00
parent af3633ac2c
commit 195542e5f9
24 changed files with 703 additions and 85 deletions

View File

@ -0,0 +1,198 @@
using AtomUI.Data;
using AtomUI.TokenSystem;
using Avalonia;
using Avalonia.LogicalTree;
namespace AtomUI.Controls;
public enum ArrowPosition
{
/// <summary>
/// Preferred location is below the target element.
/// </summary>
Bottom,
/// <summary>
/// Preferred location is to the right of the target element.
/// </summary>
Right,
/// <summary>
/// Preferred location is to the left of the target element.
/// </summary>
Left,
/// <summary>
/// Preferred location is above the target element.
/// </summary>
Top,
/// <summary>
/// Preferred location is above the target element, with the left edge of the popup
/// aligned with the left edge of the target element.
/// </summary>
TopEdgeAlignedLeft,
/// <summary>
/// Preferred location is above the target element, with the right edge of popup aligned with right edge of the target element.
/// </summary>
TopEdgeAlignedRight,
/// <summary>
/// Preferred location is below the target element, with the left edge of popup aligned with left edge of the target element.
/// </summary>
BottomEdgeAlignedLeft,
/// <summary>
/// Preferred location is below the target element, with the right edge of popup aligned with right edge of the target element.
/// </summary>
BottomEdgeAlignedRight,
/// <summary>
/// Preferred location is to the left of the target element, with the top edge of popup aligned with top edge of the target element.
/// </summary>
LeftEdgeAlignedTop,
/// <summary>
/// Preferred location is to the left of the target element, with the bottom edge of popup aligned with bottom edge of the target element.
/// </summary>
LeftEdgeAlignedBottom,
/// <summary>
/// Preferred location is to the right of the target element, with the top edge of popup aligned with top edge of the target element.
/// </summary>
RightEdgeAlignedTop,
/// <summary>
/// Preferred location is to the right of the target element, with the bottom edge of popup aligned with bottom edge of the target element.
/// </summary>
RightEdgeAlignedBottom
}
public partial class ArrowDecoratedBox : BorderedStyleControl, ITokenIdProvider
{
string ITokenIdProvider.TokenId => ToolTipToken.ID;
public static readonly StyledProperty<bool> IsShowArrowProperty =
AvaloniaProperty.Register<FlyoutPresenter, bool>(nameof(IsShowArrow), true);
public static readonly StyledProperty<ArrowPosition> ArrowPositionProperty =
AvaloniaProperty.Register<FlyoutHost, ArrowPosition>(
nameof(ArrowPosition), defaultValue: ArrowPosition.Bottom);
public static readonly StyledProperty<bool> IsFlippedProperty =
AvaloniaProperty.Register<FlyoutHost, bool>(nameof(IsFlipped), false);
// 指针最顶点位置
internal (double, double) ArrowVertexPoint { get; private set; }
/// <summary>
/// 是否显示指示箭头
/// </summary>
public bool IsShowArrow
{
get => GetValue(IsShowArrowProperty);
set => SetValue(IsShowArrowProperty, value);
}
/// <summary>
/// 箭头渲染的位置
/// </summary>
public ArrowPosition ArrowPosition
{
get => GetValue(ArrowPositionProperty);
set => SetValue(ArrowPositionProperty, value);
}
public bool IsFlipped
{
get => GetValue(IsFlippedProperty);
set => SetValue(IsFlippedProperty, value);
}
static ArrowDecoratedBox()
{
AffectsArrange<ArrowDecoratedBox>(ArrowPositionProperty,
IsFlippedProperty);
AffectsMeasure<ArrowDecoratedBox>(IsShowArrowProperty);
}
public ArrowDecoratedBox()
{
_customStyle = this;
_controlTokenBinder = new ControlTokenBinder(this);
}
public static Direction GetDirection(ArrowPosition arrowPosition)
{
return arrowPosition switch
{
ArrowPosition.Left => Direction.Left,
ArrowPosition.LeftEdgeAlignedBottom => Direction.Left,
ArrowPosition.LeftEdgeAlignedTop => Direction.Left,
ArrowPosition.Top => Direction.Top,
ArrowPosition.TopEdgeAlignedLeft => Direction.Top,
ArrowPosition.TopEdgeAlignedRight => Direction.Top,
ArrowPosition.Right => Direction.Right,
ArrowPosition.RightEdgeAlignedBottom => Direction.Right,
ArrowPosition.RightEdgeAlignedTop => Direction.Right,
ArrowPosition.Bottom => Direction.Bottom,
ArrowPosition.BottomEdgeAlignedLeft => Direction.Bottom,
ArrowPosition.BottomEdgeAlignedRight => Direction.Bottom,
_ => throw new ArgumentOutOfRangeException(nameof(arrowPosition), arrowPosition,
"Invalid value for ArrowPosition")
};
}
public static ArrowPosition GetFlipArrowPosition(ArrowPosition arrowPosition)
{
return arrowPosition switch
{
ArrowPosition.Left => ArrowPosition.Right,
ArrowPosition.LeftEdgeAlignedTop => ArrowPosition.RightEdgeAlignedTop,
ArrowPosition.LeftEdgeAlignedBottom => ArrowPosition.RightEdgeAlignedBottom,
ArrowPosition.Top => ArrowPosition.Bottom,
ArrowPosition.TopEdgeAlignedLeft => ArrowPosition.BottomEdgeAlignedLeft,
ArrowPosition.TopEdgeAlignedRight => ArrowPosition.BottomEdgeAlignedRight,
ArrowPosition.Right => ArrowPosition.Left,
ArrowPosition.RightEdgeAlignedTop => ArrowPosition.LeftEdgeAlignedTop,
ArrowPosition.RightEdgeAlignedBottom => ArrowPosition.LeftEdgeAlignedBottom,
ArrowPosition.Bottom => ArrowPosition.Top,
ArrowPosition.BottomEdgeAlignedLeft => ArrowPosition.TopEdgeAlignedLeft,
ArrowPosition.BottomEdgeAlignedRight => ArrowPosition.TopEdgeAlignedRight,
_ => throw new ArgumentOutOfRangeException(nameof(arrowPosition), arrowPosition,
"Invalid value for ArrowPosition")
};
}
protected ArrowPosition GetEffectiveArrowPosition()
{
if (IsFlipped) {
return GetFlipArrowPosition(ArrowPosition);
}
return ArrowPosition;
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
if (!_initialized) {
_customStyle.SetupUi();
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
_customStyle.HandlePropertyChangedForStyle(e);
}
protected virtual void NotifyCreateUi() { }
}

View File

@ -0,0 +1,241 @@
using AtomUI.Data;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Layout;
using Avalonia.Media;
namespace AtomUI.Controls;
public partial class ArrowDecoratedBox : IControlCustomStyle
{
private bool _initialized = false;
private IControlCustomStyle _customStyle;
private ControlTokenBinder _controlTokenBinder;
private Geometry? _arrowGeometry;
private Direction? _lastDirection;
private Rect _contentRect;
private Rect _arrowRect;
// 组件的 Token 绑定属性
private double _arrowSize;
private static readonly DirectProperty<ArrowDecoratedBox, double> ArrowSizeTokenProperty
= AvaloniaProperty.RegisterDirect<ArrowDecoratedBox, double>(nameof(_arrowSize),
(o) => o._arrowSize,
(o, v) => o._arrowSize = v);
// 组件的 Token 绑定属性
void IControlCustomStyle.SetupUi()
{
NotifyCreateUi();
_customStyle.ApplyFixedStyleConfig();
BuildGeometry(GetDirection(GetEffectiveArrowPosition()));
_initialized = true;
}
void IControlCustomStyle.ApplyFixedStyleConfig()
{
NotifyApplyFixedStyleConfig();
}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e) { }
private void BuildGeometry(Direction direction)
{
if (_lastDirection != direction) {
var arrowSize = _arrowSize;
_arrowGeometry = CommonShapeBuilder.BuildArrow(arrowSize, 1.5);
_lastDirection = direction;
}
}
protected virtual void NotifyApplyFixedStyleConfig()
{
_controlTokenBinder.AddControlBinding(MinHeightProperty, GlobalResourceKey.ControlHeight);
_controlTokenBinder.AddControlBinding(PaddingProperty, GlobalResourceKey.PaddingXS);
_controlTokenBinder.AddControlBinding(ArrowSizeTokenProperty, ArrowDecoratedBoxResourceKey.ArrowSize);
}
public sealed override void Render(DrawingContext context)
{
if (IsShowArrow) {
var direction = GetDirection(GetEffectiveArrowPosition());
var matrix = Matrix.CreateTranslation(-_arrowSize / 2, -_arrowSize / 2);
if (direction == Direction.Right) {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(-90));
matrix *= Matrix.CreateTranslation(0, _arrowSize / 2);
} else if (direction == Direction.Top) {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(180));
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
} else if (direction == Direction.Left) {
matrix *= Matrix.CreateRotation(MathUtils.Deg2Rad(90));
matrix *= Matrix.CreateTranslation(_arrowSize / 2, _arrowSize / 2);
} else {
matrix *= Matrix.CreateTranslation(_arrowSize / 2, 0);
}
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) {
var realArrowSize = Math.Min(_arrowGeometry!.Bounds.Size.Height, _arrowGeometry!.Bounds.Size.Width);
var direction = GetDirection(GetEffectiveArrowPosition());
if (direction == Direction.Left || direction == Direction.Right) {
targetWidth += realArrowSize;
} else {
targetHeight += realArrowSize;
}
}
return new Size(targetWidth, targetHeight);
}
protected override Size ArrangeOverride(Size finalSize)
{
var visualChildren = VisualChildren;
var visualCount = visualChildren.Count;
_contentRect = GetContentRect(finalSize);
_arrowRect = GetArrowRect(finalSize);
for (int i = 0; i < visualCount; ++i) {
var child = visualChildren[i];
if (child is Layoutable layoutable) {
layoutable.Arrange(_contentRect);
}
}
return finalSize;
}
private 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(GetEffectiveArrowPosition());
if (direction == Direction.Left || direction == Direction.Right) {
targetWidth -= arrowSize;
} else {
targetHeight -= arrowSize;
}
if (direction == Direction.Right) {
offsetX = arrowSize - 0.5;
} else if (direction == Direction.Bottom) {
offsetY = arrowSize - 0.5;
} else if (direction == Direction.Top) {
offsetY = 0.5;
} else {
offsetX = 0.5;
}
}
return new Rect(offsetX, offsetY, targetWidth, targetHeight);
}
private Rect GetArrowRect(Size finalSize)
{
var offsetX = 0d;
var offsetY = 0d;
var size = _arrowGeometry!.Bounds.Size;
var targetWidth = 0d;
var targetHeight = 0d;
var position = GetEffectiveArrowPosition();
if (IsShowArrow) {
var minValue = Math.Min(size.Width, size.Height);
var maxValue = Math.Max(size.Width, size.Height);
if (position == ArrowPosition.Left ||
position == ArrowPosition.LeftEdgeAlignedTop ||
position == ArrowPosition.LeftEdgeAlignedBottom) {
offsetX = finalSize.Width - minValue;
if (position == ArrowPosition.Left) {
offsetY = (finalSize.Height - maxValue) / 2;
} else if (position == ArrowPosition.LeftEdgeAlignedTop) {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = minValue;
} else {
offsetY = maxValue;
}
} else {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = finalSize.Height - minValue - maxValue;
} else {
offsetY = finalSize.Height - maxValue * 2;
}
}
targetWidth = minValue;
targetHeight = maxValue;
} else if (position == ArrowPosition.Top ||
position == ArrowPosition.TopEdgeAlignedLeft ||
position == ArrowPosition.TopEdgeAlignedRight) {
offsetY = finalSize.Height - minValue;
targetWidth = maxValue;
targetHeight = minValue;
if (position == ArrowPosition.TopEdgeAlignedLeft) {
offsetX = maxValue;
} else if (position == ArrowPosition.Top) {
offsetX = (finalSize.Width - maxValue) / 2;
} else {
offsetX = finalSize.Width - maxValue * 2;
}
} else if (position == ArrowPosition.Right ||
position == ArrowPosition.RightEdgeAlignedTop ||
position == ArrowPosition.RightEdgeAlignedBottom) {
targetWidth = minValue;
targetHeight = maxValue;
if (position == ArrowPosition.Right) {
offsetY = (finalSize.Height - maxValue) / 2;
} else if (position == ArrowPosition.RightEdgeAlignedTop) {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = minValue;
} else {
offsetY = maxValue;
}
} else {
if (maxValue * 2 > finalSize.Height / 2) {
offsetY = finalSize.Height - minValue - maxValue;
} else {
offsetY = finalSize.Height - maxValue * 2;
}
}
} else {
if (position == ArrowPosition.BottomEdgeAlignedLeft) {
offsetX = maxValue;
} else if (position == ArrowPosition.Bottom) {
offsetX = (finalSize.Width - maxValue) / 2;
} else {
offsetX = finalSize.Width - maxValue * 2;
}
targetWidth = maxValue;
targetHeight = minValue;
}
}
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);
} else if (direction == Direction.Top || direction == Direction.Bottom) {
ArrowVertexPoint = (center.X, finalSize.Width - center.X);
}
return targetRect;
}
}

View File

@ -0,0 +1,25 @@
using AtomUI.TokenSystem;
namespace AtomUI.Controls;
[ControlDesignToken]
public class ArrowDecoratedBoxToken : AbstractControlDesignToken
{
public const string ID = "ArrowDecoratedBox";
/// <summary>
/// 箭头三角形大小
/// </summary>
public double ArrowSize { get; set; }
public ArrowDecoratedBoxToken()
: base(ID)
{
}
internal override void CalculateFromAlias()
{
base.CalculateFromAlias();
ArrowSize = _globalToken.SeedToken.SizePopupArrow / 1.3;
}
}

View File

@ -78,7 +78,7 @@ public class BorderedStyleControl : StyledControl
/// Gets or sets the decorated control.
/// </summary>
[Content]
protected Control? Child
public Control? Child
{
get => GetValue(ChildProperty);
set => SetValue(ChildProperty, value);

View File

@ -1,11 +1,71 @@
using Avalonia.Controls;
using System.ComponentModel;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Metadata;
using Avalonia.Styling;
namespace AtomUI.Controls;
public class Flyout : PopupFlyoutBase
{
/// <summary>
/// Defines the <see cref="Content"/> property
/// </summary>
public static readonly StyledProperty<object> ContentProperty =
AvaloniaProperty.Register<Flyout, object>(nameof(Content));
private Classes? _classes;
/// <summary>
/// Gets the Classes collection to apply to the FlyoutPresenter this Flyout is hosting
/// </summary>
public Classes FlyoutPresenterClasses => _classes ??= new Classes();
/// <summary>
/// Defines the <see cref="FlyoutPresenterTheme"/> property.
/// </summary>
public static readonly StyledProperty<ControlTheme?> FlyoutPresenterThemeProperty =
AvaloniaProperty.Register<Flyout, ControlTheme?>(nameof(FlyoutPresenterTheme));
/// <summary>
/// Gets or sets the <see cref="ControlTheme"/> that is applied to the container element generated for the flyout presenter.
/// </summary>
public ControlTheme? FlyoutPresenterTheme
{
get => GetValue(FlyoutPresenterThemeProperty);
set => SetValue(FlyoutPresenterThemeProperty, value);
}
/// <summary>
/// Gets or sets the content to display in this flyout
/// </summary>
[Content]
public object Content
{
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
protected override Control CreatePresenter()
{
return default!;
return new FlyoutPresenter
{
[!ContentControl.ContentProperty] = this[!ContentProperty]
};
}
protected override void OnOpening(CancelEventArgs args)
{
if (Popup.Child is { } presenter) {
if (_classes != null) {
SetPresenterClasses(presenter, FlyoutPresenterClasses);
}
if (FlyoutPresenterTheme is { } theme) {
presenter.SetValue(Control.ThemeProperty, theme);
}
}
base.OnOpening(args);
}
}

View File

@ -1,4 +1,5 @@
using AtomUI.Input;
using System.Reactive.Disposables;
using AtomUI.Input;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@ -48,9 +49,9 @@ public class FlyoutHost : Control
/// <summary>
/// Defines the ToolTip.Placement property.
/// </summary>
public static readonly StyledProperty<PlacementType> PlacementProperty =
AvaloniaProperty.Register<FlyoutHost, PlacementType>(
nameof(Placement), defaultValue: PlacementType.Top);
public static readonly StyledProperty<PlacementMode> PlacementProperty =
AvaloniaProperty.Register<FlyoutHost, PlacementMode>(
nameof(Placement), defaultValue: PlacementMode.Top);
/// <summary>
/// 距离 anchor 的边距,根据垂直和水平进行设置
@ -104,7 +105,7 @@ public class FlyoutHost : Control
set => SetValue(IsPointAtCenterProperty, value);
}
public PlacementType Placement
public PlacementMode Placement
{
get => GetValue(PlacementProperty);
set => SetValue(PlacementProperty, value);
@ -135,7 +136,12 @@ public class FlyoutHost : Control
}
private bool _initialized = false;
private IDisposable? _triggerDisposable;
private CompositeDisposable _compositeDisposable;
public FlyoutHost()
{
_compositeDisposable = new CompositeDisposable();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
@ -150,7 +156,6 @@ public class FlyoutHost : Control
((ISetLogicalParent)AnchorTarget).SetParent(this);
VisualChildren.Add(AnchorTarget);
}
_initialized = true;
}
}
@ -159,12 +164,18 @@ public class FlyoutHost : Control
{
base.OnAttachedToVisualTree(e);
SetupTriggerHandler();
SetupFlyoutProperties();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_triggerDisposable?.Dispose();
_compositeDisposable?.Dispose();
}
private void SetupFlyoutProperties()
{
}
private void SetupTriggerHandler()
@ -174,33 +185,46 @@ public class FlyoutHost : Control
}
if (Trigger == FlyoutTriggerType.Hover) {
_triggerDisposable = IsPointAtCenterProperty.Changed.Subscribe(args =>
_compositeDisposable.Add(IsPointAtCenterProperty.Changed.Subscribe(args =>
{
if (args.Sender == AnchorTarget) {
HandleAnchorTargetHover(args);
}
});
}));
} else if (Trigger == FlyoutTriggerType.Focus) {
_triggerDisposable = IsFocusedProperty.Changed.Subscribe(args =>
_compositeDisposable.Add(IsFocusedProperty.Changed.Subscribe(args =>
{
if (args.Sender == AnchorTarget) {
HandleAnchorTargetFocus(args);
}
});
}));
} else if (Trigger == FlyoutTriggerType.Click) {
_triggerDisposable = InputManagerEx.SubscribeRawPointerEvent(type => type == RawPointerEventType.LeftButtonUp,
HandleAnchorTargetClick);
_compositeDisposable.Add(InputManagerEx.SubscribeRawPointerEvent(
type => type == RawPointerEventType.LeftButtonUp,
HandleAnchorTargetClick));
}
}
private void HandleAnchorTargetHover(AvaloniaPropertyChangedEventArgs<bool> e)
{
Console.WriteLine(e.NewValue);
if (Flyout is not null) {
if (e.GetNewValue<bool>()) {
ShowFlyout();
} else {
HideFlyout();
}
}
}
private void HandleAnchorTargetFocus(AvaloniaPropertyChangedEventArgs<bool> e)
{
Console.WriteLine(e.NewValue);
if (Flyout is not null) {
if (e.GetNewValue<bool>()) {
ShowFlyout();
} else {
HideFlyout();
}
}
}
private void HandleAnchorTargetClick(RawPointerEventArgs e)
@ -210,9 +234,27 @@ public class FlyoutHost : Control
if (!pos.HasValue) {
return;
}
var bounds = new Rect(pos.Value, AnchorTarget.Bounds.Size);
if (bounds.Contains(e.Position())) {
if (Flyout is not null) {
if (Flyout.IsOpen) {
HideFlyout();
} else {
ShowFlyout();
}
}
}
}
}
public void ShowFlyout()
{
HideFlyout();
}
public void HideFlyout()
{
}
}

View File

@ -0,0 +1,13 @@
using AtomUI.TokenSystem;
using Avalonia;
namespace AtomUI.Controls;
using AvaloniaFlyoutPresenter = Avalonia.Controls.FlyoutPresenter;
public partial class FlyoutPresenter : AvaloniaFlyoutPresenter, ITokenIdProvider
{
string ITokenIdProvider.TokenId => FlyoutPresenterToken.ID;
}

View File

@ -0,0 +1,22 @@
using AtomUI.Styling;
using Avalonia;
namespace AtomUI.Controls;
public partial class FlyoutPresenter : IControlCustomStyle
{
void IControlCustomStyle.InitOnConstruct() {}
void IControlCustomStyle.SetupUi()
{
}
void IControlCustomStyle.AfterUiStructureReady() {}
void IControlCustomStyle.SetupTransitions() {}
void IControlCustomStyle.CollectStyleState() {}
void IControlCustomStyle.ApplyVariableStyleConfig() {}
void IControlCustomStyle.ApplyFixedStyleConfig() {}
void IControlCustomStyle.ApplyRenderScalingAwareStyleConfig() {}
void IControlCustomStyle.ApplySizeTypeStyleConfig() {}
void IControlCustomStyle.HandlePropertyChangedForStyle(AvaloniaPropertyChangedEventArgs e) {}
}

View File

@ -0,0 +1,15 @@
using AtomUI.TokenSystem;
namespace AtomUI.Controls;
[ControlDesignToken]
internal class FlyoutPresenterToken : AbstractControlDesignToken
{
public const string ID = "FlyoutPresenter";
public FlyoutPresenterToken()
: base(ID)
{
}
}

View File

@ -1,7 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Metadata;
using Avalonia.Controls;
namespace AtomUI.Controls;
@ -9,5 +6,29 @@ using AvaloniaPopupFlyoutBase = Avalonia.Controls.Primitives.PopupFlyoutBase;
public abstract class PopupFlyoutBase : AvaloniaPopupFlyoutBase
{
private PopupShadowDecorator? _popupDecorator;
protected override bool ShowAtCore(Control placementTarget, bool showAtPointer = false)
{
_popupDecorator ??= new PopupShadowDecorator(Popup);
return base.ShowAtCore(placementTarget, showAtPointer);
}
internal static void SetPresenterClasses(Control? presenter, Classes classes)
{
if (presenter is null) {
return;
}
//Remove any classes no longer in use, ignoring pseudo classes
for (int i = presenter.Classes.Count - 1; i >= 0; i--) {
if (!classes.Contains(presenter.Classes[i]) &&
!presenter.Classes[i].Contains(':')) {
presenter.Classes.RemoveAt(i);
}
}
//Add new classes
presenter.Classes.AddRange(classes);
}
}

View File

@ -1,6 +0,0 @@
namespace AtomUI.Controls;
public class PopupFlyoutBaseProperties
{
}

View File

@ -1,6 +0,0 @@
namespace AtomUI.Controls;
public class PopupFlyoutBaseStyle
{
}

View File

@ -1,6 +0,0 @@
namespace AtomUI.Controls;
public class PopupFlyoutBaseToken
{
}

View File

@ -13,6 +13,11 @@
public const string ExtraElementMargin = "ExtraElementMargin";
}
public static class ArrowDecoratedBoxResourceKey
{
public const string ArrowSize = "ArrowSize";
}
public static class ButtonResourceKey
{
public const string FontWeight = "FontWeight";

View File

@ -1,6 +0,0 @@
namespace AtomUI.Controls;
public partial class ArrowPopup
{
}

View File

@ -1,6 +0,0 @@
namespace AtomUI.Controls;
public partial class ArrowPopup
{
}

View File

@ -3,7 +3,7 @@ using Avalonia.Media;
namespace AtomUI.Controls;
public interface IShadowLayer
public interface IShadowDecorator
{
public void AttachToTarget(Control control);
public void SetShadowMaskGeometry(Geometry geometry);

View File

@ -1,14 +0,0 @@
using Avalonia;
using Avalonia.Controls.Primitives;
namespace AtomUI.Controls;
public class PopupDecorator : AvaloniaObject
{
private Popup _popup;
public PopupDecorator(Popup popup)
{
_popup = popup;
}
}

View File

@ -0,0 +1,24 @@
using Avalonia;
using Avalonia.Controls.Primitives;
namespace AtomUI.Controls;
public class PopupShadowDecorator : AvaloniaObject
{
private Popup _popup;
public PopupShadowDecorator(Popup popup)
{
_popup = popup;
}
public virtual void Open()
{
}
public virtual void Close()
{
}
}

View File

@ -4,7 +4,7 @@ using Avalonia.Media;
namespace AtomUI.Controls;
public partial class PopupShadowLayer
public class PopupShadowLayer : AvaloniaObject, IShadowDecorator
{
public void AttachToTarget(Control control)
{

View File

@ -1,6 +0,0 @@
namespace AtomUI.Controls;
public class PopupStyle
{
}

View File

@ -2,7 +2,13 @@
namespace AtomUI.Controls;
internal class PopupToken : AbstractDesignToken
[ControlDesignToken]
internal class PopupToken : AbstractControlDesignToken
{
public const string ID = "Popup";
public PopupToken()
: base(ID)
{
}
}

View File

@ -126,7 +126,6 @@ public partial class ToolTip : BorderedStyleControl, ITokenIdProvider
private Popup? _popup;
private Action<IPopupHost?>? _popupHostChangedHandler;
private CompositeDisposable? _subscriptions;
private AvaloniaWin? _currentAnchorWindow;
/// <summary>
@ -789,7 +788,6 @@ public partial class ToolTip : BorderedStyleControl, ITokenIdProvider
private void Close()
{
if (_popup is not null) {
_popup.IsOpen = false;
SetPopupParent(_popup, null);

View File

@ -60,9 +60,8 @@ public static class ThemeResourceUtils
if (application is null) {
return null;
}
if (themeVariant is null) {
themeVariant = (application as IThemeVariantHost).ActualThemeVariant;
}
themeVariant ??= (application as IThemeVariantHost).ActualThemeVariant;
if (Application.Current!.TryFindResource(resourceKey, themeVariant, out var value)) {
return value;
}
@ -85,9 +84,8 @@ public static class ThemeResourceUtils
if (application is null) {
throw new ApplicationException("The application instance does not exist");
}
if (themeVariant is null) {
themeVariant = (application as IThemeVariantHost).ActualThemeVariant;
}
themeVariant ??= (application as IThemeVariantHost).ActualThemeVariant;
return application.Styles.GetResourceObservable(resourceKey, themeVariant, converter);
}
}