Browse Source

Merge remote-tracking branch 'origin/master' into feature/media-context-4

pull/11552/head
Max Katz 3 years ago
parent
commit
6863354272
  1. 4
      samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
  2. 37
      src/Avalonia.Base/Data/TemplateBinding.cs
  3. 4
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  4. 2
      src/Avalonia.Base/Input/IMainMenu.cs
  5. 2
      src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs
  6. 12
      src/Avalonia.Controls/ContextMenu.cs
  7. 4
      src/Avalonia.Controls/Menu.cs
  8. 28
      src/Avalonia.Controls/MenuBase.cs
  9. 4
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  10. 14
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

4
samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs

@ -19,8 +19,8 @@ namespace ControlCatalog.Pages
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel); customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.Get<Border>("CancellableContextBorder"); var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
cancellableContextBorder.ContextMenu!.ContextMenuClosing += ContextFlyoutPage_Closing; cancellableContextBorder.ContextMenu!.Closing += ContextFlyoutPage_Closing;
cancellableContextBorder.ContextMenu!.ContextMenuOpening += ContextFlyoutPage_Opening; cancellableContextBorder.ContextMenu!.Opening += ContextFlyoutPage_Opening;
} }
private ContextPageViewModel? _model; private ContextPageViewModel? _model;

37
src/Avalonia.Base/Data/TemplateBinding.cs

@ -3,18 +3,21 @@ using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading;
namespace Avalonia.Data namespace Avalonia.Data
{ {
/// <summary> /// <summary>
/// A XAML binding to a property on a control's templated parent. /// A XAML binding to a property on a control's templated parent.
/// </summary> /// </summary>
public class TemplateBinding : SingleSubscriberObservableBase<object?>, public class TemplateBinding : IObservable<object?>,
IBinding, IBinding,
IDescription, IDescription,
IAvaloniaSubject<object?>, IAvaloniaSubject<object?>,
ISetterValue ISetterValue,
IDisposable
{ {
private IObserver<object?>? _observer;
private bool _isSetterValue; private bool _isSetterValue;
private StyledElement? _target; private StyledElement? _target;
private Type? _targetType; private Type? _targetType;
@ -29,6 +32,28 @@ namespace Avalonia.Data
Property = property; Property = property;
} }
public IDisposable Subscribe(IObserver<object?> observer)
{
_ = observer ?? throw new ArgumentNullException(nameof(observer));
Dispatcher.UIThread.VerifyAccess();
if (_observer != null)
{
throw new InvalidOperationException("The observable can only be subscribed once.");
}
_observer = observer;
Subscribed();
return this;
}
public virtual void Dispose()
{
Unsubscribed();
_observer = null;
}
/// <inheritdoc/> /// <inheritdoc/>
public InstancedBinding? Initiate( public InstancedBinding? Initiate(
AvaloniaObject target, AvaloniaObject target,
@ -111,7 +136,7 @@ namespace Avalonia.Data
/// <inheritdoc/> /// <inheritdoc/>
void ISetterValue.Initialize(SetterBase setter) => _isSetterValue = true; void ISetterValue.Initialize(SetterBase setter) => _isSetterValue = true;
protected override void Subscribed() private void Subscribed()
{ {
TemplatedParentChanged(); TemplatedParentChanged();
@ -121,7 +146,7 @@ namespace Avalonia.Data
} }
} }
protected override void Unsubscribed() private void Unsubscribed()
{ {
if (_target?.TemplatedParent is { } templatedParent) if (_target?.TemplatedParent is { } templatedParent)
{ {
@ -147,12 +172,12 @@ namespace Avalonia.Data
value = Converter.Convert(value, _targetType ?? typeof(object), ConverterParameter, CultureInfo.CurrentCulture); value = Converter.Convert(value, _targetType ?? typeof(object), ConverterParameter, CultureInfo.CurrentCulture);
} }
PublishNext(value); _observer?.OnNext(value);
_hasProducedValue = true; _hasProducedValue = true;
} }
else if (_hasProducedValue) else if (_hasProducedValue)
{ {
PublishNext(AvaloniaProperty.UnsetValue); _observer?.OnNext(AvaloniaProperty.UnsetValue);
_hasProducedValue = false; _hasProducedValue = false;
} }
} }

4
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -65,14 +65,14 @@ namespace Avalonia.Input
{ {
if (_mainMenu != null) if (_mainMenu != null)
{ {
_mainMenu.MenuClosed -= MainMenuClosed; _mainMenu.Closed -= MainMenuClosed;
} }
_mainMenu = value; _mainMenu = value;
if (_mainMenu != null) if (_mainMenu != null)
{ {
_mainMenu.MenuClosed += MainMenuClosed; _mainMenu.Closed += MainMenuClosed;
} }
} }
} }

2
src/Avalonia.Base/Input/IMainMenu.cs

@ -26,6 +26,6 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Occurs when the main menu closes. /// Occurs when the main menu closes.
/// </summary> /// </summary>
event EventHandler<RoutedEventArgs>? MenuClosed; event EventHandler<RoutedEventArgs>? Closed;
} }
} }

2
src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs

@ -3,7 +3,7 @@ using Avalonia.Threading;
namespace Avalonia.Reactive namespace Avalonia.Reactive
{ {
public abstract class SingleSubscriberObservableBase<T> : IObservable<T>, IDisposable internal abstract class SingleSubscriberObservableBase<T> : IObservable<T>, IDisposable
{ {
private Exception? _error; private Exception? _error;
private IObserver<T>? _observer; private IObserver<T>? _observer;

12
src/Avalonia.Controls/ContextMenu.cs

@ -196,14 +196,14 @@ namespace Avalonia.Controls
/// <see cref="P:Avalonia.Controls.ContextMenu.IsOpen" /> /// <see cref="P:Avalonia.Controls.ContextMenu.IsOpen" />
/// property is changing from false to true. /// property is changing from false to true.
/// </summary> /// </summary>
public event CancelEventHandler? ContextMenuOpening; public event CancelEventHandler? Opening;
/// <summary> /// <summary>
/// Occurs when the value of the /// Occurs when the value of the
/// <see cref="P:Avalonia.Controls.ContextMenu.IsOpen" /> /// <see cref="P:Avalonia.Controls.ContextMenu.IsOpen" />
/// property is changing from true to false. /// property is changing from true to false.
/// </summary> /// </summary>
public event CancelEventHandler? ContextMenuClosing; public event CancelEventHandler? Closing;
/// <summary> /// <summary>
/// Called when the <see cref="Control.ContextMenu"/> property changes on a control. /// Called when the <see cref="Control.ContextMenu"/> property changes on a control.
@ -353,7 +353,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs RaiseEvent(new RoutedEventArgs
{ {
RoutedEvent = MenuOpenedEvent, RoutedEvent = OpenedEvent,
Source = this, Source = this,
}); });
} }
@ -394,7 +394,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs RaiseEvent(new RoutedEventArgs
{ {
RoutedEvent = MenuClosedEvent, RoutedEvent = ClosedEvent,
Source = this, Source = this,
}); });
@ -446,14 +446,14 @@ namespace Avalonia.Controls
private bool CancelClosing() private bool CancelClosing()
{ {
var eventArgs = new CancelEventArgs(); var eventArgs = new CancelEventArgs();
ContextMenuClosing?.Invoke(this, eventArgs); Closing?.Invoke(this, eventArgs);
return eventArgs.Cancel; return eventArgs.Cancel;
} }
private bool CancelOpening() private bool CancelOpening()
{ {
var eventArgs = new CancelEventArgs(); var eventArgs = new CancelEventArgs();
ContextMenuOpening?.Invoke(this, eventArgs); Opening?.Invoke(this, eventArgs);
return eventArgs.Cancel; return eventArgs.Cancel;
} }
} }

4
src/Avalonia.Controls/Menu.cs

@ -60,7 +60,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs RaiseEvent(new RoutedEventArgs
{ {
RoutedEvent = MenuClosedEvent, RoutedEvent = ClosedEvent,
Source = this, Source = this,
}); });
} }
@ -77,7 +77,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs RaiseEvent(new RoutedEventArgs
{ {
RoutedEvent = MenuOpenedEvent, RoutedEvent = OpenedEvent,
Source = this, Source = this,
}); });
} }

28
src/Avalonia.Controls/MenuBase.cs

@ -25,16 +25,16 @@ namespace Avalonia.Controls
o => o.IsOpen); o => o.IsOpen);
/// <summary> /// <summary>
/// Defines the <see cref="MenuOpened"/> event. /// Defines the <see cref="Opened"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<RoutedEventArgs> MenuOpenedEvent = public static readonly RoutedEvent<RoutedEventArgs> OpenedEvent =
RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuOpened), RoutingStrategies.Bubble); RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(Opened), RoutingStrategies.Bubble);
/// <summary> /// <summary>
/// Defines the <see cref="MenuClosed"/> event. /// Defines the <see cref="Closed"/> event.
/// </summary> /// </summary>
public static readonly RoutedEvent<RoutedEventArgs> MenuClosedEvent = public static readonly RoutedEvent<RoutedEventArgs> ClosedEvent =
RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(MenuClosed), RoutingStrategies.Bubble); RoutedEvent.Register<MenuBase, RoutedEventArgs>(nameof(Closed), RoutingStrategies.Bubble);
private bool _isOpen; private bool _isOpen;
@ -68,8 +68,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public bool IsOpen public bool IsOpen
{ {
get { return _isOpen; } get => _isOpen;
protected set { SetAndRaise(IsOpenProperty, ref _isOpen, value); } protected set => SetAndRaise(IsOpenProperty, ref _isOpen, value);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -105,19 +105,19 @@ namespace Avalonia.Controls
/// <summary> /// <summary>
/// Occurs when a <see cref="Menu"/> is opened. /// Occurs when a <see cref="Menu"/> is opened.
/// </summary> /// </summary>
public event EventHandler<RoutedEventArgs>? MenuOpened public event EventHandler<RoutedEventArgs>? Opened
{ {
add { AddHandler(MenuOpenedEvent, value); } add => AddHandler(OpenedEvent, value);
remove { RemoveHandler(MenuOpenedEvent, value); } remove => RemoveHandler(OpenedEvent, value);
} }
/// <summary> /// <summary>
/// Occurs when a <see cref="Menu"/> is closed. /// Occurs when a <see cref="Menu"/> is closed.
/// </summary> /// </summary>
public event EventHandler<RoutedEventArgs>? MenuClosed public event EventHandler<RoutedEventArgs>? Closed
{ {
add { AddHandler(MenuClosedEvent, value); } add => AddHandler(ClosedEvent, value);
remove { RemoveHandler(MenuClosedEvent, value); } remove => RemoveHandler(ClosedEvent, value);
} }
/// <summary> /// <summary>

4
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -290,7 +290,7 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed += PointerPressed; Menu.PointerPressed += PointerPressed;
Menu.PointerReleased += PointerReleased; Menu.PointerReleased += PointerReleased;
Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.AddHandler(MenuBase.MenuOpenedEvent, MenuOpened); Menu.AddHandler(MenuBase.OpenedEvent, MenuOpened);
Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered); Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited); Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved); Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved);
@ -326,7 +326,7 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed -= PointerPressed; Menu.PointerPressed -= PointerPressed;
Menu.PointerReleased -= PointerReleased; Menu.PointerReleased -= PointerReleased;
Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.RemoveHandler(MenuBase.MenuOpenedEvent, MenuOpened); Menu.RemoveHandler(MenuBase.OpenedEvent, MenuOpened);
Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered); Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited); Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved); Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);

14
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@ -36,7 +36,7 @@ namespace Avalonia.Controls.UnitTests
int openedCount = 0; int openedCount = 0;
sut.MenuOpened += (sender, args) => sut.Opened += (sender, args) =>
{ {
openedCount++; openedCount++;
}; };
@ -139,7 +139,7 @@ namespace Avalonia.Controls.UnitTests
int openedCount = 0; int openedCount = 0;
sut.MenuOpened += (sender, args) => sut.Opened += (sender, args) =>
{ {
openedCount++; openedCount++;
}; };
@ -168,7 +168,7 @@ namespace Avalonia.Controls.UnitTests
bool opened = false; bool opened = false;
sut.MenuOpened += (sender, args) => sut.Opened += (sender, args) =>
{ {
opened = true; opened = true;
}; };
@ -221,7 +221,7 @@ namespace Avalonia.Controls.UnitTests
int closedCount = 0; int closedCount = 0;
sut.MenuClosed += (sender, args) => sut.Closed += (sender, args) =>
{ {
closedCount++; closedCount++;
}; };
@ -259,7 +259,7 @@ namespace Avalonia.Controls.UnitTests
var tracker = 0; var tracker = 0;
var c = new ContextMenu(); var c = new ContextMenu();
c.ContextMenuClosing += (s, e) => c.Closing += (s, e) =>
{ {
tracker++; tracker++;
e.Cancel = true; e.Cancel = true;
@ -431,7 +431,7 @@ namespace Avalonia.Controls.UnitTests
}; };
new Window { Content = target }; new Window { Content = target };
sut.ContextMenuOpening += (c, e) => { eventCalled = true; e.Cancel = true; }; sut.Opening += (c, e) => { eventCalled = true; e.Cancel = true; };
_mouse.Click(target, MouseButton.Right); _mouse.Click(target, MouseButton.Right);
@ -575,7 +575,7 @@ namespace Avalonia.Controls.UnitTests
var window = PreparedWindow(target); var window = PreparedWindow(target);
var overlay = LightDismissOverlayLayer.GetLightDismissOverlayLayer(window); var overlay = LightDismissOverlayLayer.GetLightDismissOverlayLayer(window);
sut.ContextMenuClosing += (c, e) => { eventCalled = true; e.Cancel = true; }; sut.Closing += (c, e) => { eventCalled = true; e.Cancel = true; };
window.Show(); window.Show();

Loading…
Cancel
Save