diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/DropdownButtonShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/DropdownButtonShowCase.axaml
index ee7273b..39a9a88 100644
--- a/samples/AtomUI.Demo.Desktop/ShowCase/DropdownButtonShowCase.axaml
+++ b/samples/AtomUI.Demo.Desktop/ShowCase/DropdownButtonShowCase.axaml
@@ -11,12 +11,19 @@
-
+
Hover me
-
- The most basic example.
-
+
+
+
+
+
+
+
+
+
diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/InfoFlyoutShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/InfoFlyoutShowCase.axaml
index 90a3a48..8ac25e8 100644
--- a/samples/AtomUI.Demo.Desktop/ShowCase/InfoFlyoutShowCase.axaml
+++ b/samples/AtomUI.Demo.Desktop/ShowCase/InfoFlyoutShowCase.axaml
@@ -35,14 +35,6 @@
Hover me
-
-
-
- The most basic example.
-
-
- Focus me
-
diff --git a/src/AtomUI.Controls/DropdownButton/DropdownButton.cs b/src/AtomUI.Controls/DropdownButton/DropdownButton.cs
index d1f78d0..b048402 100644
--- a/src/AtomUI.Controls/DropdownButton/DropdownButton.cs
+++ b/src/AtomUI.Controls/DropdownButton/DropdownButton.cs
@@ -15,14 +15,12 @@ using Avalonia.Threading;
namespace AtomUI.Controls;
-using FlyoutControl = Flyout;
-
public class DropdownButton : Button
{
#region 公共属性定义
- public static readonly StyledProperty DropdownFlyoutProperty =
- AvaloniaProperty.Register(nameof(DropdownFlyout));
+ public static readonly StyledProperty DropdownFlyoutProperty =
+ AvaloniaProperty.Register(nameof(DropdownFlyout));
public static readonly StyledProperty TriggerTypeProperty =
AvaloniaProperty.Register(nameof(TriggerType), FlyoutTriggerType.Click);
@@ -51,7 +49,7 @@ public class DropdownButton : Button
public static readonly StyledProperty MouseLeaveDelayProperty =
AvaloniaProperty.Register(nameof(MouseLeaveDelay), 100);
- public FlyoutControl? DropdownFlyout
+ public MenuFlyout? DropdownFlyout
{
get => GetValue(DropdownFlyoutProperty);
set => SetValue(DropdownFlyoutProperty, value);
@@ -117,6 +115,7 @@ public class DropdownButton : Button
private DispatcherTimer? _mouseLeaveDelayTimer;
private CompositeDisposable? _subscriptions;
private PathIcon? _openIndicatorIcon;
+ private IDisposable? _flyoutCloseDetectDisposable;
static DropdownButton()
{
@@ -136,7 +135,7 @@ public class DropdownButton : Button
base.OnApplyTemplate(e);
TokenResourceBinder.CreateGlobalTokenBinding(this, MarginToAnchorProperty, GlobalTokenResourceKey.MarginXXS);
- SetupTriggerHandler();
+ SetupFlyoutProperties();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
@@ -162,6 +161,26 @@ public class DropdownButton : Button
BindUtils.RelayBind(this, IsShowArrowProperty, DropdownFlyout);
BindUtils.RelayBind(this, IsPointAtCenterProperty, DropdownFlyout);
BindUtils.RelayBind(this, MarginToAnchorProperty, DropdownFlyout);
+
+ DropdownFlyout.Opened += HandleFlyoutOpened;
+ }
+ }
+
+ private void HandleFlyoutOpened(object? sender, EventArgs e)
+ {
+ if (DropdownFlyout is IPopupHostProvider popupHostProvider) {
+ var host = popupHostProvider.PopupHost;
+ if (host is PopupRoot popupRoot) {
+ // 这里 PopupRoot 关闭的时候会被关闭,所以这里的事件处理器是不是不需要删除
+ popupRoot.PointerMoved += (o, args) =>
+ {
+ StopMouseLeaveTimer();
+ if (_flyoutCloseDetectDisposable is null) {
+ var inputManager = AvaloniaLocator.Current.GetService()!;
+ _flyoutCloseDetectDisposable = inputManager.Process.Subscribe(DetectWhenToClosePopup);
+ }
+ };
+ }
}
}
@@ -175,13 +194,6 @@ public class DropdownButton : Button
HandleAnchorTargetHover(args);
}
}));
- } else if (TriggerType == FlyoutTriggerType.Focus) {
- _subscriptions.Add(IsFocusedProperty.Changed.Subscribe(args =>
- {
- if (args.Sender == this) {
- HandleAnchorTargetFocus(args);
- }
- }));
} else if (TriggerType == FlyoutTriggerType.Click) {
var inputManager = AvaloniaLocator.Current.GetService()!;
_subscriptions.Add(inputManager.Process.Subscribe(HandleAnchorTargetClick));
@@ -199,33 +211,46 @@ public class DropdownButton : Button
}
}
- private void HandleAnchorTargetFocus(AvaloniaPropertyChangedEventArgs e)
+ private void DetectWhenToClosePopup(RawInputEventArgs args)
{
- if (DropdownFlyout is not null) {
- if (e.GetNewValue()) {
- if (!DropdownFlyout.IsOpen) {
- ShowFlyout();
+ if (args is RawPointerEventArgs pointerEventArgs) {
+ if (DropdownFlyout is null) {
+ return;
+ }
+ if (DropdownFlyout.IsOpen) {
+ var found = false;
+ if (pointerEventArgs.Root is PopupRoot popupRoot) {
+ var current = popupRoot.Parent;
+ while (current is not null) {
+ if (current == this) {
+ found = true;
+ }
+ current = current.Parent;
+ }
+ } else if (object.Equals(pointerEventArgs.Root, this)) {
+ found = true;
+ }
+ if (!found) {
+ HideFlyout();
}
- } else {
- HideFlyout();
}
}
}
-
+
private void HandleAnchorTargetClick(RawInputEventArgs args)
{
if (args is RawPointerEventArgs pointerEventArgs) {
+ if (DropdownFlyout is null) {
+ return;
+ }
+
if (pointerEventArgs.Type == RawPointerEventType.LeftButtonUp) {
- if (DropdownFlyout is null) {
- return;
- }
-
if (!DropdownFlyout.IsOpen) {
var pos = this.TranslatePoint(new Point(0, 0), TopLevel.GetTopLevel(this)!);
if (!pos.HasValue) {
return;
}
-
+
var bounds = new Rect(pos.Value, Bounds.Size);
if (bounds.Contains(pointerEventArgs.Position)) {
ShowFlyout();
@@ -246,7 +271,8 @@ public class DropdownButton : Button
if (DropdownFlyout is null) {
return;
}
-
+ // 防止干扰打开
+ _flyoutCloseDetectDisposable?.Dispose();
StopMouseEnterTimer();
StopMouseLeaveTimer();
DropdownFlyout.Hide();
@@ -262,7 +288,8 @@ public class DropdownButton : Button
if (DropdownFlyout is null) {
return;
}
-
+ _flyoutCloseDetectDisposable?.Dispose();
+ _flyoutCloseDetectDisposable = null;
StopMouseEnterTimer();
if (MouseLeaveDelay == 0) {
diff --git a/src/AtomUI.Controls/Flyouts/FlyoutHost.cs b/src/AtomUI.Controls/Flyouts/FlyoutHost.cs
index f2a5234..012fa1e 100644
--- a/src/AtomUI.Controls/Flyouts/FlyoutHost.cs
+++ b/src/AtomUI.Controls/Flyouts/FlyoutHost.cs
@@ -18,8 +18,7 @@ using FlyoutControl = Flyout;
public enum FlyoutTriggerType
{
Hover,
- Click,
- Focus
+ Click
}
public class FlyoutHost : Control
@@ -212,13 +211,6 @@ public class FlyoutHost : Control
HandleAnchorTargetHover(args);
}
});
- } else if (Trigger == FlyoutTriggerType.Focus) {
- _subscriptions.Add(IsFocusedProperty.Changed.Subscribe(args =>
- {
- if (args.Sender == AnchorTarget) {
- HandleAnchorTargetFocus(args);
- }
- }));
} else if (Trigger == FlyoutTriggerType.Click) {
var inputManager = AvaloniaLocator.Current.GetService()!;
_subscriptions.Add(inputManager.Process.Subscribe(HandleAnchorTargetClick));
@@ -236,17 +228,6 @@ public class FlyoutHost : Control
}
}
- private void HandleAnchorTargetFocus(AvaloniaPropertyChangedEventArgs e)
- {
- if (Flyout is not null) {
- if (e.GetNewValue()) {
- ShowFlyout();
- } else {
- HideFlyout();
- }
- }
- }
-
private void HandleAnchorTargetClick(RawInputEventArgs args)
{
if (args is RawPointerEventArgs pointerEventArgs) {
diff --git a/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs b/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs
index ef0dded..be0b51b 100644
--- a/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs
+++ b/src/AtomUI.Controls/Flyouts/PopupFlyoutBase.cs
@@ -526,4 +526,19 @@ public abstract class PopupFlyoutBase : FlyoutBase, IPopupHostProvider
//Add new classes
presenter.Classes.AddRange(classes);
}
+
+ internal bool InPopupRootBounds(Point position)
+ {
+ if (!IsOpen) {
+ return false;
+ }
+ // TODO 后期需要加入对 Overlay 的支持
+ if (Popup?.Host is PopupRoot root) {
+ // Get the popup root bounds and convert to screen coordinates
+
+ Console.WriteLine(position);
+ }
+
+ return true;
+ }
}
\ No newline at end of file