Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Button.Flyout not toggling #8448

Merged
merged 8 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions src/Avalonia.Controls/Button.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Windows.Input;
using Avalonia.Automation.Peers;
Expand Down Expand Up @@ -281,24 +282,29 @@ protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs
/// <inheritdoc/>
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Enter)
switch (e.Key)
{
OnClick();
e.Handled = true;
}
else if (e.Key == Key.Space)
{
if (ClickMode == ClickMode.Press)
{
case Key.Enter:
OnClick();
e.Handled = true;
break;

case Key.Space:
{
if (ClickMode == ClickMode.Press)
{
OnClick();
}

IsPressed = true;
e.Handled = true;
break;
}
IsPressed = true;
e.Handled = true;
}
else if (e.Key == Key.Escape && Flyout != null)
{
// If Flyout doesn't have focusable content, close the flyout here
Flyout.Hide();

case Key.Escape when Flyout != null:
// If Flyout doesn't have focusable content, close the flyout here
CloseFlyout();
break;
}

base.OnKeyDown(e);
Expand Down Expand Up @@ -327,7 +333,14 @@ protected virtual void OnClick()
{
if (IsEffectivelyEnabled)
{
OpenFlyout();
if (_isFlyoutOpen)
{
CloseFlyout();
}
else
{
OpenFlyout();
}

var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e);
Expand All @@ -348,6 +361,14 @@ protected virtual void OpenFlyout()
Flyout?.ShowAt(this);
}

/// <summary>
/// Closes the button's flyout.
/// </summary>
protected virtual void CloseFlyout()
{
Flyout?.Hide();
}

/// <summary>
/// Invoked when the button's flyout is opened.
/// </summary>
Expand Down Expand Up @@ -494,8 +515,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang

// If flyout is changed while one is already open, make sure we
// close the old one first
if (oldFlyout != null &&
oldFlyout.IsOpen)
if (oldFlyout != null && oldFlyout.IsOpen)
{
oldFlyout.Hide();
}
Expand Down
57 changes: 41 additions & 16 deletions src/Avalonia.Controls/Flyouts/FlyoutBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,12 @@ namespace Avalonia.Controls.Primitives
{
public abstract class FlyoutBase : AvaloniaObject, IPopupHostProvider
{
static FlyoutBase()
{
Control.ContextFlyoutProperty.Changed.Subscribe(OnContextFlyoutPropertyChanged);
}

/// <summary>
/// Defines the <see cref="IsOpen"/> property
/// </summary>
public static readonly DirectProperty<FlyoutBase, bool> IsOpenProperty =
AvaloniaProperty.RegisterDirect<FlyoutBase, bool>(nameof(IsOpen),
x => x.IsOpen);
AvaloniaProperty.RegisterDirect<FlyoutBase, bool>(nameof(IsOpen),
x => x.IsOpen);

/// <summary>
/// Defines the <see cref="Target"/> property
Expand All @@ -43,6 +38,14 @@ static FlyoutBase()
AvaloniaProperty.RegisterDirect<FlyoutBase, FlyoutShowMode>(nameof(ShowMode),
x => x.ShowMode, (x, v) => x.ShowMode = v);

/// <summary>
/// Defines the <see cref="OverlayInputPassThroughElement"/> property
/// </summary>
public static readonly DirectProperty<FlyoutBase, IInputElement?> OverlayInputPassThroughElementProperty =
Popup.OverlayInputPassThroughElementProperty.AddOwner<FlyoutBase>(
o => o._overlayInputPassThroughElement,
(o, v) => o._overlayInputPassThroughElement = v);

/// <summary>
/// Defines the AttachedFlyout property
/// </summary>
Expand All @@ -57,6 +60,12 @@ static FlyoutBase()
private PixelRect? _enlargePopupRectScreenPixelRect;
private IDisposable? _transientDisposable;
private Action<IPopupHost?>? _popupHostChangedHandler;
private IInputElement? _overlayInputPassThroughElement;

static FlyoutBase()
{
Control.ContextFlyoutProperty.Changed.Subscribe(OnContextFlyoutPropertyChanged);
}

public FlyoutBase()
{
Expand Down Expand Up @@ -101,11 +110,21 @@ public Control? Target
private set => SetAndRaise(TargetProperty, ref _target, value);
}

/// <summary>
/// Gets or sets an element that should receive pointer input events even when underneath
/// the flyout's overlay.
/// </summary>
public IInputElement? OverlayInputPassThroughElement
{
get => _overlayInputPassThroughElement;
set => SetAndRaise(OverlayInputPassThroughElementProperty, ref _overlayInputPassThroughElement, value);
}

IPopupHost? IPopupHostProvider.PopupHost => Popup?.Host;

event Action<IPopupHost?>? IPopupHostProvider.PopupHostChanged
{
add => _popupHostChangedHandler += value;
event Action<IPopupHost?>? IPopupHostProvider.PopupHostChanged
{
add => _popupHostChangedHandler += value;
remove => _popupHostChangedHandler -= value;
}

Expand Down Expand Up @@ -175,8 +194,9 @@ protected virtual bool HideCore(bool canCancel = true)

IsOpen = false;
Popup.IsOpen = false;

((ISetLogicalParent)Popup).SetParent(null);

// Ensure this isn't active
_transientDisposable?.Dispose();
_transientDisposable = null;
Expand Down Expand Up @@ -231,6 +251,8 @@ protected virtual bool ShowAtCore(Control placementTarget, bool showAtPointer =
Popup.Child = CreatePresenter();
}

Popup.OverlayInputPassThroughElement = OverlayInputPassThroughElement;

if (CancelOpening())
{
return false;
Expand Down Expand Up @@ -356,10 +378,13 @@ protected virtual void OnClosed()

private Popup CreatePopup()
{
var popup = new Popup();
popup.WindowManagerAddShadowHint = false;
popup.IsLightDismissEnabled = true;
popup.OverlayDismissEventPassThrough = true;
var popup = new Popup
{
WindowManagerAddShadowHint = false,
IsLightDismissEnabled = true,
//Note: This is required to prevent Button.Flyout from opening the flyout again after dismiss.
OverlayDismissEventPassThrough = false
};

popup.Opened += OnPopupOpened;
popup.Closed += OnPopupClosed;
Expand All @@ -372,7 +397,7 @@ private void OnPopupOpened(object? sender, EventArgs e)
{
IsOpen = true;

_popupHostChangedHandler?.Invoke(Popup!.Host);
_popupHostChangedHandler?.Invoke(Popup.Host);
}

private void OnPopupClosing(object? sender, CancelEventArgs e)
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls/Primitives/Popup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ public void Open()
if (dismissLayer != null)
{
dismissLayer.IsVisible = true;
dismissLayer.InputPassThroughElement = _overlayInputPassThroughElement;
dismissLayer.InputPassThroughElement = OverlayInputPassThroughElement;

Disposable.Create(() =>
{
Expand Down