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

Update Flyout and Popup APIs to make gap between them smaller #10492

Merged
merged 13 commits into from
Mar 7, 2023
5 changes: 3 additions & 2 deletions samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ public ContextFlyoutPage()
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);

var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
cancellableContextBorder.ContextFlyout!.Closing += ContextFlyoutPage_Closing;
cancellableContextBorder.ContextFlyout!.Opening += ContextFlyoutPage_Opening;
var flyout = (Flyout)cancellableContextBorder.ContextFlyout!;
flyout.Closing += ContextFlyoutPage_Closing;
flyout.Opening += ContextFlyoutPage_Opening;
}

private ContextPageViewModel? _model;
Expand Down
16 changes: 15 additions & 1 deletion samples/ControlCatalog/Pages/FlyoutsPage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
<MenuItem Header="Item 3" />
</MenuFlyout>
<Flyout Placement="Bottom" x:Key="BasicFlyout">
<Flyout.FlyoutPresenterTheme>
<ControlTheme TargetType="FlyoutPresenter" BasedOn="{StaticResource {x:Type FlyoutPresenter}}">
<Setter Property="CornerRadius" Value="20" />
</ControlTheme>
</Flyout.FlyoutPresenterTheme>
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
<TextBlock Text="Flyout Content with a custom presenter theme!" TextWrapping="Wrap" />
</Panel>
</Flyout>
</UserControl.Resources>
Expand Down Expand Up @@ -136,6 +141,15 @@
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=Center">
<Button.Flyout>
<Flyout Placement="Center">
<Panel Width="100" Height="100">
<TextBlock Text="Flyout Content!" />
</Panel>
</Flyout>
</Button.Flyout>
</Button>
<Button Content="Placement=TopEdgeAlignedLeft">
<Button.Flyout>
<Flyout Placement="TopEdgeAlignedLeft">
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Base/Input/MouseDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Utilities;
#pragma warning disable CS0618

namespace Avalonia.Input
{
Expand Down
3 changes: 2 additions & 1 deletion src/Avalonia.Base/Input/PenDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Reflection;
using Avalonia.Input.Raw;
using Avalonia.Platform;
#pragma warning disable CS0618

namespace Avalonia.Input
{
Expand Down Expand Up @@ -129,7 +130,7 @@ private bool PenUp(Pointer pointer, ulong timestamp,
var e = new PointerReleasedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers,
_lastMouseDownButton);

source?.RaiseEvent(e);
source.RaiseEvent(e);
pointer.Capture(null);
_lastMouseDownButton = default;
return e.Handled;
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Base/Input/PointerEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ internal PointerEventArgs(RoutedEvent routedEvent,
PointerPointProperties properties,
KeyModifiers modifiers,
Lazy<IReadOnlyList<RawPointerPoint>?>? previousPoints)
#pragma warning disable CS0618
: this(routedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers)
#pragma warning restore CS0618
{
_previousPoints = previousPoints;
}
Expand Down
33 changes: 20 additions & 13 deletions src/Avalonia.Base/Input/PointerOverPreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ namespace Avalonia.Input
internal class PointerOverPreProcessor : IObserver<RawInputEventArgs>
{
private IPointerDevice? _lastActivePointerDevice;
private (IPointer pointer, PixelPoint position)? _lastPointer;
private (IPointer pointer, PixelPoint position)? _currentPointer;
private PixelPoint? _lastKnownPosition;

private readonly IInputRoot _inputRoot;

Expand All @@ -15,7 +16,7 @@ public PointerOverPreProcessor(IInputRoot inputRoot)
_inputRoot = inputRoot ?? throw new ArgumentNullException(nameof(inputRoot));
}

public PixelPoint? LastPosition => _lastPointer?.position;
public PixelPoint? LastPosition => _lastKnownPosition;

public void OnCompleted()
{
Expand All @@ -41,14 +42,14 @@ public void OnNext(RawInputEventArgs value)
}

if (args.Type is RawPointerEventType.LeaveWindow or RawPointerEventType.NonClientLeftButtonDown
&& _lastPointer is (var lastPointer, var lastPosition))
&& _currentPointer is var (lastPointer, lastPosition))
{
_lastPointer = null;
_currentPointer = null;
ClearPointerOver(lastPointer, args.Root, 0, PointToClient(args.Root, lastPosition),
new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()),
args.InputModifiers.ToKeyModifiers());
}
else if (pointerDevice.TryGetPointer(args) is IPointer pointer
else if (pointerDevice.TryGetPointer(args) is { } pointer
&& pointer.Type != PointerType.Touch)
{
var element = pointer.Captured ?? args.InputHitTestResult;
Expand All @@ -62,7 +63,7 @@ public void OnNext(RawInputEventArgs value)

public void SceneInvalidated(Rect dirtyRect)
{
if (_lastPointer is (var pointer, var position))
if (_currentPointer is (var pointer, var position))
{
var clientPoint = PointToClient(_inputRoot, position);

Expand All @@ -80,12 +81,12 @@ public void SceneInvalidated(Rect dirtyRect)

private void ClearPointerOver()
{
if (_lastPointer is (var pointer, var position))
if (_currentPointer is (var pointer, var position))
{
var clientPoint = PointToClient(_inputRoot, position);
ClearPointerOver(pointer, _inputRoot, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
_lastPointer = null;
_currentPointer = null;
_lastActivePointerDevice = null;
}

Expand All @@ -100,9 +101,11 @@ private void ClearPointerOver(IPointer pointer, IInputRoot root,

// Do not pass rootVisual, when we have unknown position,
// so GetPosition won't return invalid values.
#pragma warning disable CS0618
var e = new PointerEventArgs(InputElement.PointerExitedEvent, element, pointer,
position.HasValue ? root as Visual : null, position.HasValue ? position.Value : default,
timestamp, properties, inputModifiers);
#pragma warning restore CS0618

if (element is Visual v && !v.IsAttachedToVisualTree)
{
Expand All @@ -122,18 +125,18 @@ private void ClearPointerOver(IPointer pointer, IInputRoot root,

root.PointerOverElement = null;
_lastActivePointerDevice = null;
_lastPointer = null;
_currentPointer = null;
}

private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element, bool clearRoot)
{
if (element is Visual v)
{
foreach (IInputElement el in v.VisualChildren)
foreach (var el in v.VisualChildren)
{
if (el.IsPointerOver)
if (el is IInputElement { IsPointerOver: true } child)
{
ClearChildrenPointerOver(e, el, true);
ClearChildrenPointerOver(e, child, true);
break;
}
}
Expand All @@ -151,6 +154,8 @@ private void SetPointerOver(IPointer pointer, IInputRoot root, IInputElement? el
ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers)
{
var pointerOverElement = root.PointerOverElement;
var screenPosition = ((Visual)root).PointToScreen(position);
_lastKnownPosition = screenPosition;

if (element != pointerOverElement)
{
Expand All @@ -164,7 +169,7 @@ private void SetPointerOver(IPointer pointer, IInputRoot root, IInputElement? el
}
}

_lastPointer = (pointer, ((Visual)root).PointToScreen(position));
_currentPointer = (pointer, screenPosition);
}

private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element,
Expand All @@ -186,8 +191,10 @@ private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputEl

el = root.PointerOverElement;

#pragma warning disable CS0618
var e = new PointerEventArgs(InputElement.PointerExitedEvent, el, pointer, (Visual)root, position,
timestamp, properties, inputModifiers);
#pragma warning restore CS0618
if (el is Visual v && branch != null && !v.IsAttachedToVisualTree)
{
ClearChildrenPointerOver(e, branch, false);
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Base/Input/TouchDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Reflection;
using Avalonia.Input.Raw;
using Avalonia.Platform;
#pragma warning disable CS0618

namespace Avalonia.Input
{
Expand Down
38 changes: 32 additions & 6 deletions src/Avalonia.Controls/Flyouts/Flyout.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using Avalonia.Controls.Primitives;
using System.ComponentModel;
using Avalonia.Controls.Primitives;
using Avalonia.Metadata;
using Avalonia.Styling;

namespace Avalonia.Controls
{
public class Flyout : FlyoutBase
public class Flyout : PopupFlyoutBase
{
/// <summary>
/// Defines the <see cref="Content"/> property
Expand All @@ -18,6 +20,21 @@ public class Flyout : FlyoutBase

private Classes? _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>
Expand All @@ -36,13 +53,22 @@ protected override Control CreatePresenter()
};
}

protected override void OnOpened()
protected override void OnOpening(CancelEventArgs args)
{
if (_classes != null)
if (Popup.Child is { } presenter)
{
SetPresenterClasses(Popup.Child, FlyoutPresenterClasses);
if (_classes != null)
{
SetPresenterClasses(presenter, FlyoutPresenterClasses);
}

if (FlyoutPresenterTheme is { } theme)
{
presenter.SetValue(Control.ThemeProperty, theme);
}
}
base.OnOpened();

base.OnOpening(args);
}
}
}
Loading