Skip to content

Commit

Permalink
Merge pull request #10492 from AvaloniaUI/flyout-api
Browse files Browse the repository at this point in the history
Update Flyout and Popup APIs to make gap between them smaller
  • Loading branch information
maxkatz6 authored Mar 7, 2023
2 parents 0e31d4d + 2473bb8 commit f247e89
Show file tree
Hide file tree
Showing 16 changed files with 711 additions and 684 deletions.
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

0 comments on commit f247e89

Please sign in to comment.